I\'m trying to implement a real-time multiplayer game with a custom UI (no GKMatchMakerViewController). I\'m using startBrowsingForNearbyPlayersWithReachableHandler: ^(NSString
I just got this working on my game tonight. There is more negotiation you need to do to get the communication channel setup. The initial match returned to the inviter is waiting for the invitee to respond... Here is my process with only two players. Here are all the steps my communication spin-up is performing. Obviously, no real error handling included here:
First, Authenticate your player
Second, right after authentication set inviteHandler. Something like this:
[GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite* acceptedInvite, NSArray *playersToInvite)
{
if(acceptedInvite != nil)
{
// Get a match for the invite we obtained...
[[GKMatchmaker sharedMatchmaker] matchForInvite:acceptedInvite completionHandler:^(GKMatch *match, NSError *error)
{
if(match != nil)
{
[self disconnectMatch];
// Record the new match...
self.MM_gameCenterCurrentMatch = match;
self.MM_gameCenterCurrentMatch.delegate = self;
}
else if(error != nil)
{
NSLog(@"ERROR: From matchForInvite: %@", [error description]);
}
else
{
NSLog(@"ERROR: Unexpected return from matchForInvite...");
}
}];
}
};
Third, Get your list of friend playerIds (not alias).
Fourth, Setup your GKMatchRequest something like this... I am only inviting one friend:
// Initialize the match request - Just targeting iOS 6 for now...
GKMatchRequest* request = [[GKMatchRequest alloc] init];
request.minPlayers = 2;
request.maxPlayers = 2;
request.playersToInvite = [NSArray arrayWithObject:player.playerID];
request.inviteMessage = @"Let's play!";
// This gets called when somebody accepts
request.inviteeResponseHandler = ^(NSString *playerID, GKInviteeResponse response)
{
if (response == GKInviteeResponseAccepted)
{
//NSLog(@"DEBUG: Player Accepted: %@", playerID);
// Tell the infrastructure we are don matching and will start using the match
[[GKMatchmaker sharedMatchmaker] finishMatchmakingForMatch:self.MM_gameCenterCurrentMatch];
}
};
Fifth, Use the request to call findMatchForRequest:withCompletionHandler: something like this...
[[GKMatchmaker sharedMatchmaker] findMatchForRequest:request withCompletionHandler:^(GKMatch* match, NSError *error) {
if (error)
{
NSLog(@"ERROR: Error makeMatch: %@", [error description] );
[self disconnectMatch];
}
else if (match != nil)
{
// Record the new match and set me up as the delegate...
self.MM_gameCenterCurrentMatch = match;
self.MM_gameCenterCurrentMatch.delegate = self;
// There will be no players until the players accept...
}
}];
Sixth, this sends the request to the other player and if they accept the "inviteHandler" from the second step gets called.
Seventh, the "inviteHandler" from the second step gets the match for the GKInvite!
Eighth, the "inviteeResponseHandler" from the fourth step gets called which finished the match!
Ninth, create a didChangeState from GKMatchDelegate to handle the finalization of the match. Something like this:
- (void)match:(GKMatch *)match player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state{
switch (state)
{
case GKPlayerStateConnected:
// Handle a new player connection.
break;
case GKPlayerStateDisconnected:
// A player just disconnected.
break;
}
if (!self.matchStarted && match.expectedPlayerCount == 0)
{
self.matchStarted = YES;
// Handle initial match negotiation.
if (self.iAmHost && !self.sentInitialResponse)
{
self.sentInitialResponse = true;
// Send a hello log entry
[self sendMessage: [NSString stringWithFormat:@"Message from friend, 'Hello, thanks for accepting, you have connected with %@'", self.MM_gameCenterLocalPlayer.alias] toPlayersInMatch: [NSArray arrayWithObject:playerID]];
}
}}
Tenth, here is my sendMessage:
- (void) sendMessage:(NSString*)action toPlayersInMatch:(NSArray*) playerIds{
NSError* err = nil;
if (![self.MM_gameCenterCurrentMatch sendData:[action dataUsingEncoding:NSUTF8StringEncoding] toPlayers:playerIds withDataMode:GKMatchSendDataReliable error:&err])
{
if (err != nil)
{
NSLog(@"ERROR: Could not send action to players (%@): %@ (%d) - '%@'" ,[playersInMatch componentsJoinedByString:@","],[err localizedDescription],[err code], action);
}
else
{
NSLog(@"ERROR: Could not send action to players (%@): null error - '%@'",[playersInMatch componentsJoinedByString:@","], action);
}
}
else
{
NSLog(@"DEBUG: Message sent to players (%@) - '%@'",[playersInMatch componentsJoinedByString:@","], action);
}}
Eleventh, create a didReceiveData from GKMatchDelegate something like this:
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID{
NSString* actionString = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
// Send the initial response after we got the initial send from the
// invitee...
if (!self.iAmHost &&!self.sentInitialResponse)
{
self.sentInitialResponse = true;
// Send a hello log entry
[self sendMessage: [NSString stringWithFormat:@"Message from friend, 'Hello, thanks for inviting, you have connected with %@'", self.MM_gameCenterLocalPlayer.alias] toPlayersInMatch: [NSArray arrayWithObject:playerID]];
}
// Execute the action we were sent...
NSLog(actionString);}
Twelfth... Well now you have the communication channels up and running... do whatever you want...