Проблемы с GKSession и sendDataToAllPeers: withDataMode: ошибка:

Хорошо, это связано с большим количеством сетевого кодирования от этой части многопользовательского учебника.

В принципе, я пытаюсь реализовать многопользовательскую игру с использованием GameKit в соответствии с приведенным выше учебным пособием. Я включил все необходимое сетевое кодирование и более или менее понял его, однако я попал в ловушку где-то вдоль линии вызовов методов. В принципе, настройка, которая у меня есть, заключается в том, что одно устройство действует как хост, а остальные действуют как клиенты. У меня есть два отдельных UIViewcontroller для хоста и клиентов, соответственно, где установлено соединение.

Теперь дело в том, что соединение установлено, но это только хост, который распознает соединение, а не клиент. Проблема здесь:

- (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);
    }
}

Это реализовано в 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];
}

Этот метод находится в следующей части руководства, которое я связал выше (это здесь) и должен получать пакеты, которые хозяин отправляет всем клиентам и реализует следующие методы в этой сетевой цепочке. Однако этот метод никогда не вызван. Никакие отладочные точки останова не срабатывают, и я ничего не получаю в консоли.

Я понимаю, если мне нужно предоставить больше исходных материалов, но уже есть много сетевого кодирования, поэтому я хочу, чтобы это не доходило до того, что нужно видеть людям. Кроме того, [_session setDataReceiveHandler:self withContext:nil] и _session.delegate = self записываются в другом методе, который вызывается в GameMultiplayer, так что не проблема. Кто-нибудь знает, что мне нужно исправить?

EDIT: в соответствии с запросом здесь, где инициализируется 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;
    }
}

Сеанс инициализируется в 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];
    }
}

а затем главный контроллер представления обрабатывает вызов этого метода здесь:

- (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];
          }];
     }];
}

и, наконец, это где вызов метода реализован в 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]]);
//    }
}

Ответ 1

Я думаю, что вы звоните по почте быстро. Когда сервер поймет о соединении, он отправит подтверждение клиенту, чтобы действительно установить соединение - поэтому клиент знает об этом успешно.

Если вы отправляете пакеты до этого, это будет потеряно.

Просто сделайте следующее:

[self performSelector:@selector(sendPacketToAllClients) withObject:nil afterDelay:1.0];

вместо:

[self sendPacketToAllClients];

У меня была та же проблема, что соединение установлено в другой момент с небольшой задержкой на клиенте. Лучше всего отправить первый пакет от клиента, чтобы он был готов принимать пакеты с сервера, и, как правило, оттуда.

Также попробуйте отладки:

- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state

На обоих устройствах (сервер и клиент).

Ответ 2

У меня также были проблемы с GKSession. Мне было интересно узнать (на этом сайте) сегодня, что GKSession устарел в пользу использования Multiper Connectivity Framework. С удачей, Wenderlich et al. будет делать учебник с использованием новой технологии.:)

Система имеет некоторое сходство с GKSession, поэтому не слишком сложно обернуть голову.

Ссылка на документ Apple.