Как правильно выполнить аутентификацию GameCenter?

Я видел в сообщениях о переполнении стека, которые показывают фрагменты обработки аутентификации GameCenter. Однако ни одно из этих решений не решает ни одной из проблем, которые решаются в реальных случаях использования. А именно, [GKLocalPlayer localPlayer].authenticateHandler - это просто обратный вызов состояния, и не более того. Он предоставляет контроллер представления, но существуют серьезные несоответствия в состояниях.authenticated и error.

Есть несколько вещей, которые я пытаюсь сделать: 1. Не открывать вход в игровой центр до тех пор, пока какая-либо функция не использует его. 2. Попробуйте выполнить автоматическую аутентификацию при запуске приложения. 3. Предоставьте пользователю некоторую информацию о том, почему функции GameCenter не работают. 4. Обеспечить механизм восстановления

А именно, если есть сообщение об ошибке, как я могу показать диалог входа?

Я получаю эту ошибку без viewController:

Случай 1:

Ошибка в GameCenterManager::authenticateLocalPlayer [Интернет-соединение отключено.]

Несмотря на сообщение об ошибке, устройство полностью подключено к сети, так как safari прекрасно загружает cnn.com.

Случай 2:

Кто-то закрывает экран входа в систему, потому что он не готов, и в этом случае.authenticated возвращается как истина, viewController остается равным нулю, но все вызовы игрового центра не будут выполнены. Почему [GKLocalPlayer localPlayer].authenticated имеет значение true, если это не так?

Случай 3:

Ошибка в GameCenterManager::authenticateLocalPlayer [Операция не может быть завершена. (NSURLErrorDomain ошибка -1009.)]

Это происходит, но приложение ничего не может сделать для пользователя. В таком случае каким должен быть обмен сообщениями? Переключить приложения в Game Center и войти в систему?

Дело 4:

Ошибка в GameCenterManager::authenticateLocalPlayer [Запрошенная операция была отменена или отключена пользователем.]

Это происходит, если пользователь отменяет viewController, который приложение было предложено представить Apple. Тем не менее, также нет восстановления или обнаружения этого состояния.

Дело 5:

Ошибка в GameCenterManager::createMatch [Запрошенная операция не может быть завершена, поскольку локальный игрок не прошел проверку подлинности.]

Это происходит, если пользователь вошел в систему, но по какой-либо причине выходит из GameCenter, а затем возвращается в приложение. Приложению будет сообщено, что пользователь все еще аутентифицирован, когда его явно нет, но я не могу позвонить, чтобы вызвать еще один логин.

По сути, если GameCenter не просто работает тихо, что мы должны делать как дизайнеры приложений? Просматривать оповещения и предлагать им войти в систему с помощью приложения игрового центра и перезапустить приложение?

Вот мой код аутентификации:

//******************************************************
// Authenticate
//******************************************************
-(void)authenticateLocalPlayer:(bool)showLogin
{
    if( showLogin && self.loginScreen != nil )
    { [[WordlingsViewController instance] presentViewController:self.loginScreen animated:YES completion:nil]; }

    if( [GKLocalPlayer localPlayer].isAuthenticated )
    {
        NSDLog(NSDLOG_GAME_CENTER,@"GameCenterManager::authenticateLocalPlayer LocalPlayer authenticated");
    }
    __weak GameCenterManager* weakSelf = self;
    [GKLocalPlayer localPlayer].authenticateHandler = ^(UIViewController *viewController, NSError *error)
    {
        if (error != nil)
        {
            NSDLog(NSDLOG_GAME_CENTER,@"Error in GameCenterManager::authenticateLocalPlayer [%@]", [error localizedDescription]);
        }
        else
        {
            if (viewController != nil)
            {
                NSDLog(NSDLOG_GAME_CENTER,@"GameCenter: No authentication error, but we need to login");
                weakSelf.loginScreen = viewController;
            }
            else
            {
                if ( [GKLocalPlayer localPlayer].authenticated )
                {
                    NSDLog(NSDLOG_GAME_CENTER,@"GameCenter localPlayer authenticated");
                    weakSelf.gameCenterAvailable = YES;
                    weakSelf.localPlayer = [GKLocalPlayer localPlayer];
                    [weakSelf retrieveFriends];
                    [weakSelf loadPlayerPhoto:weakSelf.localPlayer];

                    for ( id<GameCenterDelegate> listener in weakSelf.listeners )
                    { [listener onPlayerAuthenticated]; }
                }
                else
                {
                    weakSelf.gameCenterAvailable = NO;
                }
            }
        }
    };
}

Эта функция вызывается дважды: один раз при запуске приложения, чтобы надеяться создать действительное состояние входа, и 2-й, если пользователь не прошел проверку подлинности и пытается использовать функцию приложения, для которой требуется игровой центр. В этом приложении создается пошаговый матч или просмотр друзей.

1 ответ

Решение

Вы сталкиваетесь со многими из тех же жалоб, что и у меня по поводу API Game Center. Я пытался достичь того же, что и ты. Версия TL;DR: Game Center просто не поддерживает ее. ><Но есть некоторые вещи, которые вы можете сделать, чтобы уменьшить боль.

Одна общая вещь, которая помогла мне: не забудьте проверить оба NSError а также это .underlyingError имущество. Я видел несколько случаев, когда NSError слишком расплывчато, чтобы быть полезным, но основная ошибка имеет более конкретные детали.

Случай 1: Можете ли вы поделиться доменом ошибок и кодом ошибки как для NSError, так и для basicError?

Случай 2: у меня есть открытая ошибка с Apple в течение долгого времени. Есть несколько случаев, в том числе в режиме "В самолете", когда аутентификация не выполняется, но .authenticated возвращает истину. Когда я написал баг об этом, Apple закрыла его, заявив, что это "специально", чтобы игроки могли продолжать играть в игру, используя любые ранее кэшированные данные. Итак, я добавил ошибку к нескольким сценариям, где кэшированные данные вызывают значительные проблемы. Моя ошибка была вновь открыта и с тех пор там сидела. Философия дизайна выглядит так: "ну, просто продолжай, и, возможно, все получится в конце". Но в итоге это не сработало, пользователь застревает, не может играть, и они обвиняют мою игру, а не Apple.

Единственное смягчение, которое я нашел, это то, что вы уже делаете: всегда всегда проверяйте NSError сначала в обработчике аутентификации. Контроллер вида и .authenticated абсолютно ненадежны, если была установлена ​​ошибка.

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

Случай 3: я также получил -1009. Из того, что я могу различить, это происходит, когда у меня есть сетевое соединение, но Game Center никогда не отвечал. Это может произойти из-за любого сбоя между моим маршрутизатором, включая серверы Game Center, которые не отвечают. Я часто видел это при использовании GC Test Servers. Не так много сейчас, когда тестовые серверы были объединены в среду prod.

Случай 4: Вы на 100% правы. в игре нет восстановления. Если пользователь отменяет аутентификацию, это конец строки. Единственный способ восстановиться - это убить игру (не просто выйти и снова войти) и перезапустить ее. Тогда, и только тогда, вы можете представить другой контроллер вида входа в систему.

Есть некоторые вещи, которые вы можете сделать, чтобы смягчить это, хотя. Это напрямую нарушит вашу цель № 1 - отложить вход в систему до тех пор, пока это не потребуется, но я не нашел ничего лучшего:

  1. Отключите кнопку "начать игру" (или что-то еще, что есть в вашей игре), пока вы не подтвердите, что вход в систему прошел без ошибок И вы не можете успешно загрузить пример списка лидеров из GC. Это доказывает сквозную связь.
  2. Если пользователь отменяет регистрацию, ваш обработчик аутентификации получит NSError домена = GKErrorDomain и код = GKErrorCanceled, Когда я вижу это комбо, я предупреждаю пользователя о том, что он не может играть в сетевые игры до тех пор, пока он не войдет в систему и для входа в систему ему придется остановить и перезапустить игру.
  3. Пользователи были сбиты с толку, почему кнопка "Пуск" была отключена, поэтому я тоже добавил туда предупреждение. Я показываю кнопку, которая выглядит отключенной, но действительно включена. И когда они пытаются щелкнуть по нему, я снова представляю предупреждение о том, что им нужно войти в игровой центр, чтобы играть в сетевую игру.

Это отстой, но, по крайней мере, пользователь не застрял.

Случай 5: Это один из примеров, которые я привел в своей ошибке, упомянутой в случае 2. Позволяя пользователю думать, что он вошел в систему, когда на самом деле это не так, он пытается сделать то, чего он действительно не может сделать, и в конце концов случится что-то плохое.

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

Фактически, выполняя поиск по всем моим базам кода, я никогда не использую.authenticated для каких-либо решений.

Сказав все это, вот мой обработчик аутентификации. Я не скажу, что это красиво, но пока пользователи не застревают в неисправимых ситуациях. Я предполагаю, что это случай отчаянных времен (работа с дерьмовым API) требует отчаянных мер (грязные обходные пути).

[localPlayer setAuthenticateHandler:^(UIViewController *loginViewController, NSError *error)
 {
    //this handler is called once when you call setAuthenticated, and again when the user completes the login screen (if necessary)
     VLOGS (LOWLOG, SYMBOL_FUNC_START, @"setAuthenticateHandler completion handler");

     //did we get an error? Could be the result of either the initial call, or the result of the login attempt
     if (error)
     {
         //Here's a fun fact... even if you're in airplane mode and can't communicate to the server,
         //when this call back fires with an error code, localPlayer.authenticated is set to YES despite the total failure. ><
         //error.code == -1009 -> authenticated = YES
         //error.code == 2 -> authenticated = NO
         //error.code == 3 -> authenticated = YES

         if ([GKLocalPlayer localPlayer].authenticated == YES)
         {
             //Game center blatantly lies!
             VLOGS(LOWLOG, SYMBOL_ERROR, @"error.code = %ld but localPlayer.authenticated = %d", (long)error.code, [GKLocalPlayer localPlayer].authenticated);
         }

         //show the user an appropriate alert
         [self processError:error file:__FILE__ func:__func__ line:__LINE__];

         //disable the start button, if it's not already disabled
         [[NSNotificationCenter defaultCenter] postNotificationName:EVENT_ENABLEBUTTONS_NONETWORK object:self ];
         return;
     }

     //if we received a loginViewContoller, then the user needs to log in.
     if (loginViewController)
     {
         //the user isn't logged in, so show the login screen.
         [appDelegate presentViewController:loginViewController animated:NO completion:^
          {
              VLOGS(LOWLOG, SYMBOL_FUNC_START, @"presentViewController completion handler");

              //was the login successful?
              if ([GKLocalPlayer localPlayer].authenticated)
              {
                  //Possibly. Can't trust .authenticated alone. Let's validate that the player actually has some meaningful data in it, instead.
                  NSString *alias = [GKLocalPlayer localPlayer].alias;
                  NSString *name = [GKLocalPlayer localPlayer].displayName;
                  if (alias && name)
                  {
                      //Load our matches from the server. If this succeeds, it will enable the network game button
                      [gameKitHelper loadMatches];
                  }
              }
          }];
     }

     //if there was not loginViewController and no error, then the user is already logged in
     else
     {
         //the user is already logged in, so load matches and enable the network game button
         [gameKitHelper loadMatches];
     }

 }];
Другие вопросы по тегам