Note that there are some explanatory texts on larger screens.

plurals
  1. POProblems with GKSession and sendDataToAllPeers:withDataMode:error:
    text
    copied!<p>Alright, this involves a lot of network coding from <a href="http://www.raywenderlich.com/12910/how-to-make-a-simple-playing-card-game-with-multiplayer-and-bluetooth-part-3" rel="noreferrer">this part of a multiplayer tutorial</a>.</p> <p>Basically, I'm trying to implement a multiplayer game using GameKit as per the tutorial linked above. I put in all of the necessary network coding and more or less understand it, however I've hit a snag somewhere along the line of method calls. Basically, the setup that I have is that one device acts as the host and the rest act as the clients. I have two separate <code>UIViewcontroller</code>s for the host and clients respectively where the connection is established.</p> <p>Now the thing is, the connection gets established, but it's only the host that recognizes the connection, not the client. The problem is here:</p> <pre><code>- (void)sendPacketToAllClients:(Packet *)packet { [_players enumerateKeysAndObjectsUsingBlock:^(id key, Player *obj, BOOL *stop) { obj.receivedResponse = [_session.peerID isEqualToString:obj.peerID]; }]; GKSendDataMode dataMode = GKSendDataReliable; NSData *data = [packet data]; NSError *error; if (![_session sendDataToAllPeers:data withDataMode:dataMode error:&amp;error]) { NSLog(@"Error sending data to clients: %@", error); } } </code></pre> <p>This is implemented in <code>GameMultiplayer</code>, where the actual game will be implemented. What this method is supposed to be doing is sending data packets to each of the clients saying that the host received the connection request and is able to connect with them. After <code>[_session sendDataToAllPeers:data withDataMode:dataMode error:&amp;error]</code> is called (the method in the <code>if</code> statement), this method is supposed to be triggered:</p> <pre><code>- (void)receiveData:(NSData *)data fromPeer:(NSString *)peerID inSession:(GKSession *)session context:(void *)context { #ifdef DEBUG NSLog(@"Game: receive data from peer: %@, data: %@, length: %d", peerID, data, [data length]); #endif Packet *packet = [Packet packetWithData:data]; if (packet == nil) { NSLog(@"Invalid packet: %@", data); return; } Player *player = [self playerWithPeerID:peerID]; if (player != nil) { player.receivedResponse = YES; // this is the new bit } if (self.isServer) [self serverReceivedPacket:packet fromPlayer:player]; else [self clientReceivedPacket:packet]; } </code></pre> <p>This method is in the next part of the tutorial I linked above (which is <a href="http://www.raywenderlich.com/12930/how-to-make-a-simple-playing-card-game-with-multiplayer-and-bluetooth-part-4" rel="noreferrer">here</a>) and is supposed to receive the packets that the host sends to all clients and implement the next methods in this networking chain. However, the method never gets called. No debug breakpoints are triggered and I get nothing in the console.</p> <p>I understand if I need to provide more source material, but there is a lot of network coding already implemented, so I want to keep it down to what people need to see. Also, <code>[_session setDataReceiveHandler:self withContext:nil]</code> and <code>_session.delegate = self</code> are written in another method that is called in <code>GameMultiplayer</code>, so that's not the problem. Does anyone know what I need to fix?</p> <p>EDIT: As requested, here's where GKSession is initialized:</p> <pre><code>@property (nonatomic, strong, readonly) GKSession *session; //This is done in the header file @synthesize session = _session; //This is done in the main file - (void)startAcceptingConnectionsForSessionID:(NSString *)sessionID { if (_serverState == ServerStateIdle) { _serverState = ServerStateAcceptingConnections; _connectedClients = [NSMutableArray arrayWithCapacity:self.maxClients]; _session = [[GKSession alloc] initWithSessionID:sessionID displayName:nil sessionMode:GKSessionModeServer]; _session.delegate = self; _session.available = YES; } } </code></pre> <p>The session is initialized in <code>MatchmakingServer</code>, which is used in the host view controller. The session is then passed on to the main view controller of the app, which then initializes <code>GameMultiplayer</code> and sends the GKSession to it. Here's where the host view controller sends it to the main view controller:</p> <pre><code>- (IBAction)startAction:(id)sender { if (_matchmakingServer != nil &amp;&amp; [_matchmakingServer connectedClientCount] &gt; 0) { NSString *name = [self.nameTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; if ([name length] == 0) name = _matchmakingServer.session.displayName; [_matchmakingServer stopAcceptingConnections]; [self.delegate hostViewController:self startGameWithSession:_matchmakingServer.session playerName:name clients:_matchmakingServer.connectedClients]; } } </code></pre> <p>and then the main view controller handles that method call here:</p> <pre><code>- (void)hostViewController:(MatchmakerHost *)controller startGameWithSession:(GKSession *)session playerName:(NSString *)name clients:(NSArray *)clients { [self dismissViewControllerAnimated:NO completion:^ { [self startGameWithBlock:^(GameMultiplayer *aGame) { [aGame startServerGameWithSession:session playerName:name clients:clients]; }]; }]; } </code></pre> <p>and finally, this is where that method call is implemented in <code>GameMultiplayer</code>:</p> <pre><code>- (void)startServerGameWithSession:(GKSession *)session playerName:(NSString *)name clients:(NSArray *)clients { _clients = clients; const char* className = class_getName([[_clients objectAtIndex:0] class]); NSLog(@"yourObject is a: %s", className); self.isServer = YES; _session = session; _session.available = NO; _session.delegate = self; [_session setDataReceiveHandler:self withContext:nil]; _state = GameStateWaitingForSignIn; [self.delegate gameWaitingForClientsReady:self]; // Create the Player object for the server. Player *player = [[Player alloc] init]; player.name = name; player.peerID = _session.peerID; player.position = PlayerPositionBottom; [_players setObject:player forKey:player.peerID]; // Add a Player object for each client. int index = 0; for (NSString *peerID in clients) { Player *player = [[Player alloc] init]; player.peerID = peerID; [_players setObject:player forKey:player.peerID]; if (index == 0) player.position = ([clients count] == 1) ? PlayerPositionTop : PlayerPositionLeft; else if (index == 1) player.position = PlayerPositionTop; else player.position = PlayerPositionRight; index++; } NSLog(@"Players:"); Packet *packet = [Packet packetWithType:PacketTypeSignInRequest]; [self sendPacketToAllClients:packet]; // for (int i = 0; i &lt; [_players count]; i++) { // NSLog([NSString stringWithFormat:@"%@", [clients objectAtIndex:i]]); // } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload