【问题标题】:What is the proper way to do GameCenter authentication?进行 GameCenter 身份验证的正确方法是什么?
【发布时间】:2016-05-13 15:55:52
【问题描述】:

我在有关堆栈溢出的帖子中看到了处理 GameCenter 身份验证的 sn-ps。但是,这些解决方案都没有解决现实世界用例涵盖的任何问题。即 [GKLocalPlayer localPlayer].authenticateHandler 只是一个状态的回调,并没有什么其他的。它提供了一个视图控制器,但 .authenticated 和错误状态存在大量不一致。

我正在尝试做一些事情: 1. 游戏中心登录不弹出,直到某个功能使用它 2. 尝试在应用启动时静默进行身份验证 3. 向用户提供一些 GameCenter 功能无法正常工作的信息 4. 提供恢复机制

也就是说,如果报告了错误,我该如何显示登录对话框?

我在没有 viewController 的情况下收到此错误:

案例一:

GameCenterManager::authenticateLocalPlayer 中的错误 [Internet 连接似乎处于脱机状态。]

尽管有错误消息,但设备完全在线,因为 safari 可以正常加载 cnn.com。

案例 2:

有人因为没有准备好而关闭了登录屏幕,在这种情况下 .authenticated 返回为真,viewController 保持为零,但所有游戏中心调用都将失败。为什么 [GKLocalPlayer localPlayer].authenticated 设置为 true 而不是?

案例3:

GameCenterManager::authenticateLocalPlayer 中的错误 [操作 无法完成。 (NSURLErrorDomain 错误 -1009.)]

这种情况一直在发生,但应用程序无法为用户做任何事情。在这种情况下,消息应该是什么?将应用切换到 Game Center 并在那里登录?

案例 4:

GameCenterManager::authenticateLocalPlayer 中的错误 [请求的 操作已被用户取消或禁用。]

如果用户取消了苹果告诉应用程序呈现的 viewController,就会发生这种情况。然而,也没有恢复或检测到这种状态。

案例 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;
                }
            }
        }
    };
}

这个函数被调用了两次:一次是在应用程序启动时创建一个有效的登录状态,第二次如果用户没有通过身份验证并且他们尝试使用需要游戏中心的应用程序功能。在这个应用程序中,它正在创建回合制比赛或查看朋友

【问题讨论】:

    标签: game-center


    【解决方案1】:

    您遇到了许多与我对 Game Center API 相同的投诉。我一直在努力实现与您相同的 4 件事。 TL;DR 版本:Game Center 根本不支持它。 >

    对我有帮助的一件事是:确保同时检查 NSError.underlyingError 属性。我见过几个例子,NSError 太模糊而无济于事,但潜在的错误有更具体的细节。

    案例1:能否分享NSError和underlyingError的错误域和错误代码?

    案例 2:我在这方面与 Apple 有一个未解决的错误,持续了很长时间。有几种情况,包括在飞行模式下,身份验证失败但.authenticated 返回 true。当我为此写了一个错误时,Apple 关闭了它,说这是“设计使然”,因此玩家可以使用任何以前缓存的数据继续玩游戏。因此,我在缓存数据导致严重问题的几种情况下附加了该错误。我的虫子被重新打开并一直坐在那里。设计理念似乎是:“好吧,继续前进,也许最终一切都会成功。”但最终还是不行,用户卡住了,无法玩,他们责怪我的游戏,而不是苹果。

    我发现的唯一缓解措施正是您已经在做的事情:始终始终始终首先在身份验证处理程序中检查 NSError。如果设置了错误,视图控制器和.authenticated 将完全不可靠。

    如果出现错误,我会将其传递给一个专用的错误处理程序,该处理程序会向用户显示警报并告诉他们需要做什么才能恢复。

    案例 3:我也达到了 -1009。据我所知,当我有网络连接时会发生这种情况,但游戏中心从未回复过。这可能来自我的路由器之间的任何中断,包括游戏中心服务器没有响应。我在使用 GC 测试服务器时经常看到这种情况。现在测试服务器已合并到 prod 环境中并没有那么多。

    案例 4:你是 100% 正确的。没有游戏内恢复。如果用户取消身份验证,则该行结束。恢复的唯一方法是终止游戏(不仅仅是离开并重新进入)并重新启动它。然后,也只有这样,你才能呈现另一个登录视图控制器。

    不过,您可以采取一些措施来缓解这种情况。它会直接破坏您延迟登录直到需要时的第一目标,但我没有找到更好的方法:

    1. 禁用“开始游戏”按钮(或您游戏中的任何按钮),直到您确认登录成功且没有错误,并且您可以从 GC 成功下载示例排行榜。这证明了端到端的连接性。
    2. 如果用户取消登录,您的身份验证处理程序将收到域 = GKErrorDomain 和代码 = GKErrorCanceledNSError。当我看到该组合时,我向用户发出警告,他们在成功登录之前无法玩网络游戏,并且要登录,他们将不得不停止并重新启动游戏。
    3. 用户对为什么禁用“开始”按钮感到困惑,所以我也在那里添加了警报。我展示了一个看起来禁用但实际上已启用的按钮。当他们尝试点击它时,我会再次显示一条提醒,告诉他们必须登录游戏中心才能玩网络游戏。

    这很糟糕,但至少用户没有被卡住。

    案例 5:这是我在案例 2 中提到的错误中引用的示例之一。通过让用户认为他们实际上没有登录,他们会尝试做他们确实不能做的事情做,最终会发生一些不好的事情。

    我发现的最佳缓解方法与案例 4 相同:在您看到身份验证处理程序触发且没有错误并且您可以成功下载示例排行榜以证明网络连接之前,不要让用户启​​动会话.

    事实上,通过搜索我的所有代码库,我再也不会使用 .authenticated 来做任何决定。

    说了这么多,这是我的身份验证处理程序。我不会说它很漂亮,但到目前为止,用户并没有陷入无法恢复的境地。我想这是一个绝望的时代(使用垃圾 API)需要绝望的措施(kludgy work arounds)。

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

    【讨论】:

    • 正如预期的那样,我想我将不得不弹出一个警报视图,其中包含在设置了错误的后视图控制器的情况下杀死应用程序的指令。感谢您的反馈,我们的处理方式似乎非常相似,所以我没有那么糟糕。
    猜你喜欢
    • 2020-02-07
    • 1970-01-01
    • 2019-04-23
    • 2014-10-10
    • 2020-06-27
    • 1970-01-01
    • 2016-06-29
    • 2021-08-01
    • 2018-03-10
    相关资源
    最近更新 更多