我在帖子中看到了堆栈溢出,它显示了处理GameCenter身份验证的片段。但是,这些解决方案都没有解决现实世界用例所涵盖的任何问题。也就是说,[GKLocalPlayer localPlayer] .authenticateHandler只是状态的回调,而不是其他。它提供了一个视图控制器,但在.authenticated和错误状态方面存在大量的不一致。
我正在尝试做一些事情: 1.在功能使用之前,不会弹出游戏中心登录 2.尝试在应用启动时静默验证 3.向用户提供一些信息,说明为什么GameCenter功能不起作用 4.提供恢复机制
即如果报告了错误,我怎么能显示登录对话框?
我没有viewController得到这个错误:
案例1:
GameCenterManager中的错误:: authenticateLocalPlayer [Internet连接似乎处于脱机状态。]
尽管有错误消息,但设备完全在线,因为safari加载cnn.com就好了。
案例2:
有人关闭了登录界面,因为他们还没准备好,在这种情况下.authenticated回复为true,viewController保持为零,但所有游戏中心调用都将失败。为什么[GKLocalPlayer localPlayer] .authenticated设置为true?
案例3:
GameCenterManager :: authenticateLocalPlayer出错[操作 无法完成。 (NSURLErrorDomain错误-1009。)]
这种情况一直在发生,但应用程序无法为用户执行任何操作。在这种情况下,消息应该是什么?将应用切换到Game Center并登录?
案例4:
GameCenterManager :: authenticateLocalPlayer出错[请求的 操作已被用户取消或禁用。]
如果用户取消了应用程序被Apple告知应用的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;
}
}
}
};
}
此功能被调用两次:一次在应用启动时希望创建一个有效的登录状态,如果用户未经过身份验证,他们会尝试使用需要游戏中心的应用功能。在这个应用程序中,它正在创建一个回合制的匹配或查看朋友
答案 0 :(得分:3)
你遇到了很多关于Game Center API的抱怨。我一直在努力实现同样的4件事。 TL; DR版本:Game Center根本不支持它。 &GT;&LT;但是你可以采取一些措施来减轻痛苦。
帮助我的一般性事项:确保同时检查NSError
及其.underlyingError
属性。我见过几种情况NSError
过于模糊而无法提供帮助,但潜在的错误有更具体的细节。
案例1:你能分享NSError和底层错误的错误域和错误代码吗?
案例2:在这个问题上,我对苹果公司有一个开放的错误。有几种情况,包括处于飞行模式,其中身份验证失败但.authenticated
返回true。当我写一个关于此的错误时,Apple关闭它说这是“按设计”,所以玩家可以继续使用任何以前缓存的数据玩游戏。因此,我在缓存数据导致重大问题的几种情况下附加了该错误。我的虫子重新开放,从那时起就一直坐在那里。设计理念似乎是:“好吧,继续前进,也许它最终都会成功。”但它最终没有成功,用户卡住,无法玩,他们责怪我的游戏,而不是苹果。
我发现的唯一缓解正是您已经在做的事情:始终始终先在身份验证处理程序中检查NSError
。如果设置了错误,则视图控制器和.authenticated
完全不可靠。
如果出现错误,我会将其传递给一个专门的错误处理程序,该处理程序向用户显示警报并告诉他们需要做些什么来恢复。
案例3:我也打了-1009。从我可以看出它发生在我有网络连接时,但Game Center从未回复过。这可能来自我的路由器最新和包括游戏中心服务器之间的任何中断没有响应。使用GC测试服务器时,我常常看到这一点。现在测试服务器已经合并到prod环境中了。
案例4:你100%正确。没有游戏中的恢复。如果用户取消身份验证,那就是该行的结尾。恢复的唯一方法是杀死游戏(不只是离开并重新进入)并重新启动它。然后,只有这样,您才能呈现另一个登录视图控制器。
但是,您可以采取一些措施来缓解这种情况。它将直接打破你的第一个目标,即延迟登录直到需要,但我还没有找到更好的东西:
NSError
的域= GKErrorDomain
和代码= GKErrorCanceled
。当我看到这个组合时,我向用户发出一个警告,即他们无法玩网络游戏,直到他们成功登录并登录他们将不得不停止并重新启动游戏。 很糟糕,但至少用户没有卡住。
案例5:这是我在案例2中提到的错误中引用的示例之一。通过让用户认为他们真的没有登录,他们会尝试做他们真的无法做到的事情做,并最终会发生坏事。
我发现的最佳缓解方法与案例4相同:不要让用户启动会话,直到您看到验证处理程序激活而没有错误并且您可以成功下载示例排行榜以证明网络连接
事实上,在搜索我的所有代码库时,我从不使用.authenticated进行任何决策。
说完所有这些,这是我的身份验证处理程序。我不会说它很漂亮,但到目前为止,用户不会陷入无法恢复的状态。我想这是一个绝望的时期(使用废话API)需要绝望的措施(kludgy works)。
[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];
}
}];