进行GameCenter身份验证的正确方法是什么? [英] What is the proper way to do GameCenter authentication?

查看:305
本文介绍了进行GameCenter身份验证的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在堆栈溢出周围的帖子中看到过,这些帖子显示了处理GameCenter身份验证的摘要.但是,这些解决方案都无法解决现实使用案例所涵盖的任何问题.即,[GKLocalPlayer localPlayer] .authenticateHandler只是状态的回叫,仅此而已.它提供了一个视图控制器,但是在.authenticated和错误状态中存在大量不一致之处.

I have seen in posts around stack overflow that shows snippets of handling GameCenter authentication. However, none of these solutions address any of the problems that real world use cases cover. Namely, the [GKLocalPlayer localPlayer].authenticateHandler is just a call back of the status, and not much else. It provides a view controller, but there are massive inconsistencies in .authenticated, and error states.

我正在尝试做一些事情: 1.在功能使用之前,不要弹出游戏中心登录名 2.尝试在应用启动时进行静默身份验证 3.向用户提供一些信息,说明GameCenter功能为何不起作用 4.提供恢复机制

There are a few things I am trying to do: 1. Not pop up the game center login until a feature uses it 2. Try to authenticate silently on app launch 3. Provide some info to the user why GameCenter features are not working 4. Provide a recovery mechanism

即,如果报告了错误,则无论如何我如何显示登录对话框?

Namely if there is an error reported how can I show the login dialog anyways?

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

I get this error with no viewController:

情况1:

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

尽管出现错误消息,但该设备完全在线,因为safari可以很好地加载cnn.com.

Despite its error message, the device is completely online, as safari loads cnn.com just fine.

情况2:

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

Someone closes the login screen because they are not ready, in which case .authenticated comes back as true, viewController remains at nil, yet all game center calls will fail. Why is the [GKLocalPlayer localPlayer].authenticated set to true when it isn't?

情况3:

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

Error in GameCenterManager::authenticateLocalPlayer [The operation couldn’t be completed. (NSURLErrorDomain error -1009.)]

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

This keeps occurring yet there is nothing the app can do for the user. In this case what should the messaging be? Switch apps to Game Center and login there?

情况4:

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

Error in GameCenterManager::authenticateLocalPlayer [The requested operation has been canceled or disabled by the user.]

如果用户取消了Apple告知应用显示的viewController,则会发生这种情况.但是,也没有恢复或检测到此状态.

This happens if the user cancels the viewController the app was told to present by apple. Yet, there is also no recovery or detecting this state.

案例5:

GameCenterManager :: createMatch中的错误[请求的操作可能 尚未完成,因为尚未验证本地播放器.]

Error in GameCenterManager::createMatch [The requested operation could not be completed because local player has not been authenticated.]

如果用户已登录,则会发生这种情况,但是无论出于何种原因退出GameCenter,然后都会返回到应用程序.显然不会通过该应用程序时,该应用程序将被告知用户仍在通过身份验证,但我无法拨打电话以再次发起登录.

This happens if the user was logged in, but for whatever reason logs out of GameCenter then returns to the app. The app will be told the user is still authenticated when it is clearly not, yet there are no calls I can make to bring up another login.

因此,从本质上讲,如果GameCenter不能只是默默地工作,那么作为应用程序设计师,我们将做什么?警报视图并告诉他们使用Game Center应用程序登录并重新启动该应用程序?

So essentially, if GameCenter doesn't just silently work, what are we to do as app designers? Alert view and tell them to go login using the game center app and restart the app?

这是我的验证码:

//******************************************************
// 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;
                }
            }
        }
    };
}

该函数被调用了两次:一次在应用启动时希望创建有效的登录状态,第二次在用户未通过身份验证并且他们尝试使用需要游戏中心的应用功能时进行.在此应用中,它可以创建基于回合的比赛或查看朋友

This function is called twice: once at app startup to hopefully create a valid login state, and 2nd if the user is not authenticated and they try to use an app feature that requires game center. In this app, it is creating a turn based match or viewing friends

推荐答案

您也遇到了许多有关Game Center API的投诉.我一直在尝试实现与您相同的4件事. TL; DR版本:Game Center不支持它. ><但是您可以采取一些措施来减轻痛苦.

You're encountering many of the same complaints I have about the Game Center API. I've been trying to achieve the same 4 things you are. The TL;DR version: Game Center simply doesn't support it. >< But there are some things you can do to reduce the pain.

对我有帮助的一件事:确保同时检查NSError.underlyingError属性.我已经见过几种情况,其中NSError太模糊而无济于事,但是潜在的错误有更具体的细节.

One general thing that helped me: make sure to check both the NSError as well as it's .underlyingError property. I've seen several cases where the NSError is too vague to be helpful, but the underlying error has more specific details.

案例1:可以同时为NSError和underlyingError共享错误域和错误代码吗?

Case 1: Can you share the error domain and error code for the both the NSError and the underlyingError?

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

Case 2: I have an open bug with Apple on this, for a looooong time. There are several cases, including being in Airplane mode, where the authentication fails but .authenticated returns true. When I wrote a bug on this, Apple closed it saying this was "by design" so players could continue to play the game using any previously cached data. So, I appended the bug with several scenarios where cached data causes significant problems. My bug was re-opened and has sat there ever since. The design philosophy seems to be: "well, just keep going and maybe it will all work out in the end." But it doesn't work out in the end, the user gets stuck, unable to play and they blame my game, not Apple.

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

The only mitigation I have found is exactly what you're already doing: Always always always check the NSError first in the authentication handler. The view controller and .authenticated are totally unreliable if an error has been set.

如果有错误,我会将其传递给一个专用的错误处理程序,该处理程序向用户显示警报,并告诉他们他们需要采取什么措施来进行恢复.

If there is an error, I pass it to one dedicated error handler that displays alerts to users and tells them what they need to do to recover.

情况3:我也击中了-1009.据我所知,当我有网络连接时会发生这种情况,但是Game Center从未答复.这可能是由于我的路由器与最新的Game Center服务器之间没有任何响应而导致的任何中断.在使用GC测试服务器时,我经常看到这一点.现在测试服务器已合并到产品环境中,现在还不算什么.

Case 3: I have hit -1009 as well. From what I can discern it happens when I have network connection, but Game Center never replied. That could come from any disruption anywhere between my router up-to-and-including Game Center servers not responding. I used to see this a lot when using the GC Test Servers. Not so much now that the test servers were merged into the prod environment.

情况4:您是100%正确的.游戏中没有恢复.如果用户取消身份验证,则该行的结尾.恢复的唯一方法是杀死游戏(不仅仅是离开并重新进入游戏),然后重新启动游戏.然后,只有那时,您才能提供另一个登录视图控制器.

Case 4: You are 100% correct. there is no in-game recovery. If the user cancels the authentication, that's the end of the line. The only way to recover is to kill the game (not just leave and re-enter) and restart it. Then, and only then, you can present another login view controller.

不过,您可以采取一些措施来减轻这种情况.这将直接打破您将登录延迟到需要的第一目标,但我没有发现更好的方法:

There are some things you can do to mitigate this, though. It will directly break your #1 goal of delaying login until needed, but I haven't found anything better:

  1. 禁用开始游戏"按钮(或游戏中的任何按钮),直到确认登录成功且没有错误,并且可以从GC成功下载示例排行榜.这证明了端到端的连接性.
  2. 如果用户取消登录,则您的身份验证处理程序将收到域= GKErrorDomain和代码= GKErrorCanceledNSError.当我看到该组合时,我向用户发出警告,告知他们只有成功登录才能玩网络游戏,而要登录必须停止并重新启动游戏.
  3. 用户感到困惑,为什么禁用了开始"按钮,因此我也在那里添加了警报.我显示了一个看起来已禁用但实际上已启用的按钮.当他们尝试单击它时,我再次显示警报,告诉他们必须登录游戏中心才能玩网络游戏.
  1. Disable the "start game" button (or whatever you have in your game) until you've confirmed the login succeeded with no errors AND you can successfully download a sample leaderboard from GC. This proves end-to-end connectivity.
  2. If the user cancels the login, your authentication handler will receive an NSError of domain = GKErrorDomain and code = GKErrorCanceled. WHen I see that combo, I put up a warning to the user that they cannot play network games until they've successfully logged in and to login they will have to stop and restart the game.
  3. Users were confused why the "start" button was disabled, so I added an alert there too. I show a button that looks disabled but is really enabled. And when they try to click it, I again present an alert telling them they have to login in to game center to play a network game.

很烂,但至少不会卡住用户.

It sucks, but at least the user isn't stuck.

案例5:这是我在案例2中提到的错误中引用的示例之一.通过让用户认为他们确实不在登录状态,他们尝试执行他们实际上无法做到的事情这样做,最终会发生不好的事情.

Case 5: This is one of the examples I cited in my bug referred to in case 2. By letting the user think they're logged in when they really aren't, they try to do things they really can't do, and eventually something bad will happen.

为此,我找到的最佳缓解方法与情况4相同:在看到身份验证处理程序无错误启动并且可以成功下载示例排行榜以证明网络连接之前,不要让用户开始会话

The best mitigation I have found for this is the same as Case 4: don't let the user start a session until you see the authentication handler fire with no errors AND you can successfully download a sample leaderboard to prove the network connection.

事实上,在我所有的代码库中进行搜索时,我再也不会使用.authenticated进行任何决策了.

In fact, doing a search through all of my code bases, I never use .authenticated for any decisions anymore.

说了这么多,这是我的身份验证处理程序.我不会说它很漂亮,但是到目前为止,用户不会陷入无法恢复的境地.我猜这是一个拼命的情况(使用废话API)需要拼命的措施(麻烦的解决方法).

Having said all of that, here's my authentication handler. I won't say it's pretty, but thus far, users don't get stuck in unrecoverable situations. I guess it's a case of desperate times (working with a crap API) requires desperate measures (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];
     }

 }];

这篇关于进行GameCenter身份验证的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆