GKSession和sendDataToAllPeers的问题:withDataMode:错误:

Ray*_*onK 11 objective-c gamekit ios

好吧,这涉及到多人教程这一部分的大量网络编码.

基本上,我正在尝试按照上面链接的教程使用GameKit实现多人游戏.我放入了所有必要的网络编码,或多或少地理解它,但是我在方法调用的某个方面遇到了麻烦.基本上,我的设置是一个设备充当主机,其余设备充当客户端.我有两个独立的UIViewcontrollers分别用于建立连接的主机和客户端.

现在的问题是,连接已建立,但它只是识别连接的主机,而不是客户端.问题出在这里:

- (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:&error])
    {
        NSLog(@"Error sending data to clients: %@", error);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是在实施GameMultiplayer实际游戏的地方实施的.该方法应该做的是向每个客户端发送数据包,说明主机收到连接请求并能够与它们连接.在[_session sendDataToAllPeers:data withDataMode:dataMode error:&error]调用之后(if语句中的方法),应该触发此方法:

- (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];
}
Run Code Online (Sandbox Code Playgroud)

这个方法在我上面链接的教程的下一部分(在这里),并且应该接收主机发送给所有客户端的数据包并实现此网络链中的下一个方法.但是,该方法永远不会被调用.没有触发调试断点,我在控制台中什么也得不到.

我知道如果我需要提供更多的源材料,但已经实现了很多网络编码,所以我想把它留给人们需要看到的东西.另外,[_session setDataReceiveHandler:self withContext:nil]并且_session.delegate = self是用另一个被调用的方法编写的GameMultiplayer,所以这不是问题.有谁知道我需要修理什么?

编辑:根据要求,这里是GKSession初始化的地方:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

会话初始化MatchmakingServer,用于主机视图控制器.然后会话传递给应用程序的主视图控制器,然后初始化GameMultiplayer并将GKSession发送给它.这是主机视图控制器将其发送到主视图控制器的位置:

- (IBAction)startAction:(id)sender
{
    if (_matchmakingServer != nil && [_matchmakingServer connectedClientCount] > 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];
    }
}
Run Code Online (Sandbox Code Playgroud)

然后主视图控制器在这里处理该方法调用:

- (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];
          }];
     }];
}
Run Code Online (Sandbox Code Playgroud)

最后,这是在以下方法中实现该方法调用的地方GameMultiplayer:

- (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 < [_players count]; i++) {
//        NSLog([NSString stringWithFormat:@"%@", [clients objectAtIndex:i]]);
//    }
}
Run Code Online (Sandbox Code Playgroud)

Grz*_*ski 0

我认为您正在呼叫“快速发送”。当服务器意识到连接时,它会向客户端发送确认以真正建立连接 - 这样客户端就知道连接成功了。

如果您在此之前发送数据包 - 它将丢失。

只需这样做:

[self performSelector:@selector(sendPacketToAllClients) withObject:nil afterDelay:1.0];
Run Code Online (Sandbox Code Playgroud)

代替:

[self sendPacketToAllClients];
Run Code Online (Sandbox Code Playgroud)

我遇到了同样的问题,即连接是在不同时刻建立的,客户端上的延迟很小。最好的方法是从客户端发送第一个数据包,表明他已准备好从服务器接收数据包 - 然后从那里正常继续。

还可以尝试调试:

- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state
Run Code Online (Sandbox Code Playgroud)

在两个设备上(服务器和客户端)。