No se pueden recibir invitaciones al centro de juegos.

Estoy tratando de desarrollar un juego multijugador en time real para IOS mediante el uso de cocos2d mediante el tutorial en http://www.raywenderlich.com/3325/how-to-make-a-simple-multiplayer-game-with-game-center -tutorial-parte-22

Todo funciona bien, incluyendo la coincidencia automática con un jugador aleatorio, pero invitar a un amigo no funciona porque otro dispositivo no puede recibir una invitación.

Cuando hice clic en el button Invitar amigos y luego seleccioné a un amigo usando la interfaz estándar del centro de juegos, dice que está esperando (para siempre) y que no pasa nada. Mi amigo no puede recibir una invitación del centro de juegos (sin notifications).

Puedo invitar a un amigo mediante la funcionalidad de amigos cercanos (cuando esta funcionalidad está habilitada en ambos dispositivos) pero no hay notificación de invitación cuando los amigos cercanos están deshabilitados.

Pasé horas y horas buscando en Google, encontré casos similares pero no encontré ninguna solución.

Algunos comentarios iniciales sobre posibles respuestas:

  • Utilizo dos dispositivos (un iPhone y un iPad), ningún simulador
  • Todas las configuraciones en iTunes connect están bien, incluyendo la configuration multijugador
  • Validado que ambos dispositivos están conectados a sandbox mediante el uso de diferentes counts de testing
  • Ya comprobé la configuration de notifications del Centro de juegos en ambos dispositivos.
  • Ya comprobé todos los problemas de proxy / firewall y probé tanto WiFi como Cellular para ambos dispositivos
  • Las invitaciones del juego están habilitadas para ambos dispositivos / counts
  • Ya comprobé las identificaciones del package, las ID de la versión de la aplicación, etc.
  • Ambos dispositivos son IOS 6.x y la versión de destino de la aplicación OS IOS 5.0
  • No tengo otros problemas sobre el centro del juego (tablas de sorting, emparejamientos aleatorios, etc … todo bien)
  • Llamo al método inviteHandler tan pronto como he autenticado un usuario como sea posible como se menciona en la documentation de Apple.

Aquí está el file de cabecera de mi class de ayuda de Game Center:

#import <Foundation/Foundation.h> #import <GameKit/GameKit.h> @protocol GCHelperDelegate - (void)matchStarted; - (void)matchEnded; - (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID; - (void)inviteReceived; @end @interface GCHelper : NSObject <GKMatchmakerViewControllerDelegate, GKMatchDelegate>{ BOOL gameCenterAvailable; BOOL userAuthenticated; UIViewController *presentingViewController; GKMatch *match; BOOL matchStarted; id <GCHelperDelegate> delegate; NSMutableDictionary *playersDict; GKInvite *pendingInvite; NSArray *pendingPlayersToInvite; NSMutableArray *unsentScores; } @property (retain) GKInvite *pendingInvite; @property (retain) NSArray *pendingPlayersToInvite; @property (assign, readonly) BOOL gameCenterAvailable; @property (retain) NSMutableDictionary *playersDict; @property (retain) UIViewController *presentingViewController; @property (retain) GKMatch *match; @property (assign) id <GCHelperDelegate> delegate; - (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers viewController:(UIViewController *)viewController delegate:(id<GCHelperDelegate>)theDelegate; - (BOOL) reportAchievementIdentifier: (NSString*) identifier percentComplete: (float) percent; + (GCHelper *)shanetworkingInstance; - (void)authenticateLocalUser; @end 

Y aquí está la implementación de la class de ayuda del centro de juegos

 #import "GCHelper.h" @implementation GCHelper @synthesize gameCenterAvailable; @synthesize presentingViewController; @synthesize match; @synthesize delegate; @synthesize playersDict; @synthesize pendingInvite; @synthesize pendingPlayersToInvite; #pragma mark Initialization static GCHelper *shanetworkingHelper = nil; + (GCHelper *) shanetworkingInstance { if (!shanetworkingHelper) { shanetworkingHelper = [[GCHelper alloc] init]; } return shanetworkingHelper; } - (BOOL)isGameCenterAvailable { // check for presence of GKLocalPlayer API Class gcClass = (NSClassFromString(@"GKLocalPlayer")); // check if the device is running iOS 4.1 or later NSString *reqSysVer = @"4.1"; NSString *currSysVer = [[UIDevice currentDevice] systemVersion]; BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrdenetworkingAscending); return (gcClass && osVersionSupported); } - (id)init { if ((self = [super init])) { gameCenterAvailable = [self isGameCenterAvailable]; if (gameCenterAvailable) { NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(authenticationChanged) name:GKPlayerAuthenticationDidChangeNotificationName object:nil]; } } return self; } - (void)authenticationChanged { if ([GKLocalPlayer localPlayer].isAuthenticated && !userAuthenticated) { NSLog(@"Authentication changed: player authenticated."); userAuthenticated = TRUE; [GKMatchmaker shanetworkingMatchmaker].inviteHandler = ^(GKInvite *acceptedInvite, NSArray *playersToInvite) { NSLog(@"Received invite"); self.pendingInvite = acceptedInvite; self.pendingPlayersToInvite = playersToInvite; [delegate inviteReceived]; }; } else if (![GKLocalPlayer localPlayer].isAuthenticated && userAuthenticated) { NSLog(@"Authentication changed: player not authenticated"); userAuthenticated = FALSE; } } - (void)lookupPlayers { NSLog(@"Looking up %d players...", match.playerIDs.count); [GKPlayer loadPlayersForIdentifiers:match.playerIDs withCompletionHandler:^(NSArray *players, NSError *error) { if (error != nil) { NSLog(@"Error retrieving player info: %@", error.localizedDescription); matchStarted = NO; [delegate matchEnded]; } else { // Populate players dict self.playersDict = [NSMutableDictionary dictionaryWithCapacity:players.count]; for (GKPlayer *player in players) { NSLog(@"Found player: %@", player.alias); [playersDict setObject:player forKey:player.playerID]; } // Notify delegate match can begin matchStarted = YES; [delegate matchStarted]; } }]; } #pragma mark User functions - (void)authenticateLocalUser { if (!gameCenterAvailable) return; NSLog(@"Authenticating local user..."); if ([GKLocalPlayer localPlayer].authenticated == NO) { [[GKLocalPlayer localPlayer] authenticateWithCompletionHandler:nil]; } else { NSLog(@"Already authenticated!"); } } - (void)findMatchWithMinPlayers:(int)minPlayers maxPlayers:(int)maxPlayers viewController:(UIViewController *)viewController delegate:(id<GCHelperDelegate>)theDelegate { if (!gameCenterAvailable) return; matchStarted = NO; self.match = nil; self.presentingViewController = viewController; delegate = theDelegate; if (pendingInvite != nil) { [presentingViewController dismissModalViewControllerAnimated:NO]; GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithInvite:pendingInvite] autorelease]; mmvc.matchmakerDelegate = self; [presentingViewController presentModalViewController:mmvc animated:YES]; self.pendingInvite = nil; self.pendingPlayersToInvite = nil; } else { [presentingViewController dismissModalViewControllerAnimated:NO]; GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease]; request.minPlayers = minPlayers; request.maxPlayers = maxPlayers; request.playersToInvite = pendingPlayersToInvite; GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease]; mmvc.matchmakerDelegate = self; [presentingViewController presentModalViewController:mmvc animated:YES]; self.pendingInvite = nil; self.pendingPlayersToInvite = nil; } } #pragma mark GKMatchmakerViewControllerDelegate // The user has cancelled matchmaking - (void)matchmakerViewControllerWasCancelled:(GKMatchmakerViewController *)viewController { [presentingViewController dismissModalViewControllerAnimated:YES]; } // Matchmaking has failed with an error - (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFailWithError:(NSError *)error { [presentingViewController dismissModalViewControllerAnimated:YES]; NSLog(@"Error finding match: %@", error.localizedDescription); } // A peer-to-peer match has been found, the game should start - (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFindMatch:(GKMatch *)theMatch { [presentingViewController dismissModalViewControllerAnimated:YES]; self.match = theMatch; match.delegate = self; if (!matchStarted && match.expectedPlayerCount == 0) { NSLog(@"Ready to start match!"); [self lookupPlayers]; } } #pragma mark GKMatchDelegate // The match received data sent from the player. - (void)match:(GKMatch *)theMatch didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID { if (match != theMatch) return; [delegate match:theMatch didReceiveData:data fromPlayer:playerID]; } // The player state changed (eg. connected or disconnected) - (void)match:(GKMatch *)theMatch player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state { if (match != theMatch) return; switch (state) { case GKPlayerStateConnected: // handle a new player connection. NSLog(@"Player connected!"); if (!matchStarted && theMatch.expectedPlayerCount == 0) { NSLog(@"Ready to start match!"); [self lookupPlayers]; } break; case GKPlayerStateDisconnected: // a player just disconnected. NSLog(@"Player disconnected!"); matchStarted = NO; [delegate matchEnded]; break; } } // The match was unable to connect with the player due to an error. - (void)match:(GKMatch *)theMatch connectionWithPlayerFailed:(NSString *)playerID withError:(NSError *)error { if (match != theMatch) return; NSLog(@"Failed to connect to player with error: %@", error.localizedDescription); matchStarted = NO; [delegate matchEnded]; } // The match was unable to be established with any players due to an error. - (void)match:(GKMatch *)theMatch didFailWithError:(NSError *)error { if (match != theMatch) return; NSLog(@"Match failed with error: %@", error.localizedDescription); matchStarted = NO; [delegate matchEnded]; } - (void)reportScore:(int64_t)score forCategory:(NSString *)category { // Only execute if OS supports Game Center & player is logged in if ([self isGameCenterAvailable] && [GKLocalPlayer localPlayer].authenticated == YES) { // Create score object GKScore *scoreReporter = [[[GKScore alloc] initWithCategory:category] autorelease]; // Set the score value scoreReporter.value = score; // Try to send [scoreReporter reportScoreWithCompletionHandler:^(NSError *error) { if (error != nil) { // Handle reporting error here by adding object to a serializable array, to be sent again later [unsentScores addObject:scoreReporter]; } }]; } } - (BOOL) reportAchievementIdentifier: (NSString*) identifier percentComplete: (float) percent { if ([self isGameCenterAvailable] && [GKLocalPlayer localPlayer].authenticated == YES) { GKAchievement *achievement = [[[GKAchievement alloc] initWithIdentifier: identifier] autorelease]; if (achievement) { achievement.percentComplete = percent; [achievement reportAchievementWithCompletionHandler:^(NSError *error) { if (error != nil) { // Retain the achievement object and try again later (not shown). } }]; } return YES; } return NO; } @end 

Y finalmente así es como llamo al centro del juego desde mi capa de juego (probé dos opciones diferentes pero ninguna funcionó)

Opción 1

 [[GCHelper shanetworkingInstance] findMatchWithMinPlayers:2 maxPlayers:2 viewController: [[[UIApplication shanetworkingApplication] keyWindow] rootViewController] delegate: self]; 

opcion 2

 AppController *app = (AppController*) [[UIApplication shanetworkingApplication] delegate]; UINavigationController *viewController = [app navController]; [[GCHelper shanetworkingInstance] findMatchWithMinPlayers:2 maxPlayers:2 viewController:viewController delegate:self]; 

Cualquier ayuda será apreciada. Gracias por adelantado…

    He estado lidiando con el modo multijugador por unos meses y las invitaciones han sido un verdadero problema para mí … y como tú, usé el tutorial de Ray para comenzar. Me di count hoy de que el código de Ray tiene un error en el que las invitaciones no funcionarán si ambos clientes tienen el GKMatchmakerView. Debe descartarlo cuando reciba la invitación por primera vez con algo similar a:

      [gcdelegate.viewController dismissViewControllerAnimated:YES completion:^{ GKMatchmakerViewController *mmvc = [[GKMatchmakerViewController alloc] initWithInvite:pendingInvite]; mmvc.matchmakerDelegate = self; [gcdelegate.viewController presentModalViewController:mmvc animated:YES]; self.pendingInvite = nil; self.pendingPlayersToInvite = nil; boo_invite=true; }]; 

    Ok, parece que funciona de nuevo sin ningún cambio en nuestro código o configuration de networking. Abrí boleto para soporte de Apple, loggings de errores, etc. y parece que algunos de ellos funcionaron …

    Ahora entendemos que fue un error en Game Center sandbox. Por lo que veo, la versión de Sanbox del centro de juegos no es tan estable y la gente de Apple no le presta suficiente atención a este service. Tampoco hay forma de verificar el estado del sistema en Internet.

    Todavía continúo debatiendo con el soporte de Apple para entender el motivo y compartiré toda la conversación aquí cuando esté completa.

    Feliz encoding …