Game Center Matchmaking GKTurnBasedMatch has signi

2019-04-28 05:54发布

问题:

I'm implementing a turn-based game with multiplayer mode through gamecenter. I have 2 devices (1 ipad, 1 iphone) to test in sandbox mode which were working fine but lately it has started to struggle in auto matchmaking process. After I send the first turn from one user, the other device doesn't immediately recognize that game but opens up its own fresh game. Before it was able to immediately spot the game started in the other device and matchmaking was fairly straightforward. And I don't remember changing any parts relevant to matchmaking (NSCoding, GKTurnBasedEventHandler, GKTurnBasedMatchmakerViewControllerDelegate delegate methods etc).

Now I send the first turn from one device and need to wait around 1 min so the other device can successfully connect to that game. After connection occurs endTurnWithMatchData calls work without any problems, it can send and receive data within 1-2 secs. But it won't be a good UX if users start a fresh game and had to wait 1 min so another user can connect to his game. Has anyone been experiencing significant lag in auto matchmaking process? I didn't implement invitations yet, so I cannot check it. The matchdata I archive with NSKeyedArchiver seemed quite big, 3396 bytes, even for a fresh game with almost no data. And here are relevant parts of my code:

GameOptionsViewController:

- (void)turnBasedMatchmakerViewControllerWasCancelled:(GKTurnBasedMatchmakerViewController *)viewController
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFailWithError:(NSError *)error
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFindMatch:(GKTurnBasedMatch *)match
{
    [self dismissViewControllerAnimated:NO completion:nil];
    self.gcMatch = match;
    [self performSegueWithIdentifier:@"GameMultiplayer" sender:self];
}

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([segue.identifier isEqualToString:@"GameMultiplayer"])
    {
        GameViewController *GameVC = (GameViewController *)segue.destinationViewController;

        [GameVC setGameMode:GAMEMODE_MULTIPLAYER_SAMEDEVICE];

        //Multiplayer game it is
        if(self.gcMatch != nil)
        {
            [GameVC setGameMode:GAMEMODE_MULTIPLAYER_GAMECENTER];
            GameVC.gcMatchDelegate = self;
            GameVC.gcMatch = self.gcMatch;
            NSLog(@"Game OVC Segue: Match ID | %@", self.gcMatch.matchID);

        }
    }
    else
    {
       ...
    }
}

GameViewController:

//This method is called according to user actions
//It's the only method I use to send data to other participant
-(void) sendCurrentGameDataWithNewTurn:(BOOL) newTurn
{
    NSLog(@"Sending game data current participant : %@", gcMatch.currentParticipant.playerID);

    //Update match data if it is corrupted anyhow
    if (gcMatch.currentParticipant == nil)
    {
    [GKTurnBasedMatch loadMatchWithID:gcMatch.matchID withCompletionHandler:^(GKTurnBasedMatch *match, NSError *error)
     {
         if (error != nil)
         {
             NSLog(@"Error :%@", error);
             return ;
         }
         [self sendCurrentGameDataWithNewTurn:newTurn];
     }];
}
else
{
    NSData *matchData = [NSKeyedArchiver archivedDataWithRootObject:game];

    //Game advances to new player, buttons are disabled
    if(newTurn)
    {
        NSLog(@"SENDING NEW TURN");

        NSUInteger currentIndex = [gcMatch.participants
                                   indexOfObject:gcMatch.currentParticipant];

        GKTurnBasedParticipant *nextParticipant;
        nextParticipant = [gcMatch.participants objectAtIndex:
                           ((currentIndex + 1) % [gcMatch.participants count])];

        [gcMatch endTurnWithNextParticipants:[NSArray arrayWithObject:nextParticipant] turnTimeout:GC_TURN_TIMEOUT matchData:matchData completionHandler:^(NSError *error) {
            NSLog(@"Sent");
            if (error) {
                NSLog(@"SNT - %@", error);
            }
        }];
    }
    else
    {
        NSLog(@"ONLY UPDATING DATA");
        [gcMatch saveCurrentTurnWithMatchData:matchData completionHandler:^(NSError *error) {
            NSLog(@"Sent");
            if (error) {
                NSLog(@"OUD - %@", error);
            }
        }];
    }
}

}

-(void) updateGameDataWithGCMatch
{
    //Update whole game data
    self.game = [NSKeyedUnarchiver unarchiveObjectWithData:self.gcMatch.matchData];

    //Update game ui
    ...
}

-(void) handleTurnEventForMatch:(GKTurnBasedMatch *)match didBecomeActive:(BOOL)didBecomeActive
{
    //Check if I got data for the currently active match that options vc forwarded me here, if not do some debug print and return
    if(![self.gcMatch.matchID isEqual:match.matchID])
    {
        //For debugging reasons I skip if i get info for any previous match (other player quit etc)
        NSLog(@"GCMatch matchID: %@ match matchID:  %@",self.gcMatch.matchID,match.matchID);
        return;
    }

    NSLog(@"Turn event handle");

    self.gcMatch = match;

    if([match.currentParticipant.playerID isEqualToString: [GKLocalPlayer localPlayer].playerID ])
    {
        //Disable field buttons
        [self setFieldButtonsEnabled:TRUE];
        [self turnChangeAnimationFromLeftToRight:FALSE];
    }

    [self updateGameDataWithGCMatch];
}

回答1:

As for your question:

I myself tempered with matchmaking over Game Center quite a bit and also experienced lags quite frequently, which have been proven to not have been caused by my site but by apples game center servers.

As for additional guidance:

As far as I can see your current approach to matchmaking on a device is:

look if there is a match I can connect to --> If YES request gamedata and connect to the match ELSE start your own match and broadcast the matchdata

From my experience it is better practice to start with matchrequestbroadcasts, wait until you find a second player, define the server device (e.g. by lower checksum of game-center names) and then start the game on that device.