处理Game Center身份验证

时间:2013-11-29 18:59:36

标签: ios authentication game-center gamekit

根据Apple docs我们应该做这样的事情来处理GC身份验证:

- (void) authenticateLocalUser
{
    GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];

    if(localPlayer.authenticated == NO)
    {
        [localPlayer setAuthenticateHandler:(^(UIViewController* viewcontroller, NSError *error) {
            if (!error && viewcontroller)
            {
                DLog(@"Need to log in");
                AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
                [appDelegate.window.rootViewController presentViewController:viewcontroller animated:YES completion:nil];

            }
            else
            {
                DLog(@"Success");

            }
        })];

    }
}

我们获得了这些信息:

如果设备没有经过身份验证的播放器,Game Kit会将视图控制器传递给您的身份验证处理程序。显示时,此视图控制器显示身份验证用户界面。您的游戏应该暂停需要用户交互的其他活动(例如您的游戏循环),呈现此视图控制器然后返回。当播放器完成与其交互时,视图控制器将自动关闭。

我的问题是,我们如何知道此视图控制器何时被解雇,以及我们如何知道身份验证是否成功?

显然我需要知道身份验证是否有效,如果由于魔术GC视图控制器出现而需要暂停,我需要知道何时恢复游戏。

4 个答案:

答案 0 :(得分:13)

您的代码存在问题:首先,您应该在应用加载后立即设置身份验证处理程序。这意味着无论localPlayer是否经过身份验证,您都可以设置处理程序,以便在播放器注销并重新登录时自动调用它。如果您的播放器从您的应用程序切换到游戏中心应用程序并注销,那么您的应用程序中的处理程序将不会被调用(如果他在应用程序首次启动时已经过身份验证)。设置处理程序的目的是每次进行auth更改(输入/输出)时,您的应用程序都可以做正确的事情。

其次,你不应该依赖于任何错误。即使返回错误,游戏工具包仍可能有足够的缓存信息,以便为游戏提供经过身份验证的播放器。这些错误只是为了帮助您进行调试。

要回答您的问题,请先查看下面的代码示例。

-(void)authenticateLocalPlayer
{
    GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];

    //Block is called each time GameKit automatically authenticates
    localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error)
    {
        [self setLastError:error];
        if (viewController)
        {
            self.authenticationViewController = viewController;
            [self disableGameCenter];
        }
        else if (localPlayer.isAuthenticated)
        {
            [self authenticatedPlayer];
        }
        else
        {
            [self disableGameCenter];
        }
    };
}

-(void)authenticatedPlayer
{
     GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
    [[NSNotificationCenter defaultCenter]postNotificationName:AUTHENTICATED_NOTIFICATION object:nil];
    NSLog(@"Local player:%@ authenticated into game center",localPlayer.playerID);
}

-(void)disableGameCenter
{
    //A notification so that every observer responds appropriately to disable game center features
    [[NSNotificationCenter defaultCenter]postNotificationName:UNAUTHENTICATED_NOTIFICATION object:nil];
    NSLog(@"Disabled game center");
}

在我的应用中,当应用启动时,只会拨打authenticateLocalPlayer一次。这是因为在此之后会自动调用处理程序。

how do we know when this view controller gets dismissed,

您不知道此视图控制器何时被解除。 文档中的代码示例说明在适当的时候显示视图控制器。这意味着您不一定要显示游戏中心无法登录的视图控制器时间。事实上,您可能不应立即在处理程序中显示它。只有当玩家需要继续执行手头的任务时,才应显示视图控制器。它不应该在一个奇怪的时间弹出。这就是我保存视图控制器的原因,所以我可以在以后显示它时显示。

  

如果我不得不暂停比赛,我需要知道何时恢复比赛   提出了神奇的GC视图控制器。

如果您设置身份验证处理程序以根据状态更改发布通知,您可以侦听事件并显示“暂停菜单”或其他内容,直到用户选择恢复为止。

  

我们如何知道身份验证是否成功

如果验证成功,则视图控制器为零,localPlayer.isAuthenticated为真。

  是不是?

如果身份验证失败,则localPlayer.isAuthenticated为false,视图控制器为零。认证失败可能由于多种原因(网络等)而发生,在这种情况下您不应该呈现视图控制器,这就是视图控制器将为零的原因。在这种情况下,您应该禁用游戏中心功能,直到用户下次登录。由于自动调用了身份验证处理程序,因此大多数情况下您不需要执行任何操作。如果您想提示用户在游戏中心做一些事情,您可以随时提供从您的应用启动游戏中心应用程序的方法,而您无法通过代码自动执行此操作。

编辑:使用像self.isAuthenticated这样的标志(如上所述)来跟踪你是否登录并不是一个好主意(我不想引起任何混淆,所以我没有不要删除它。最好始终检查[GKLocalPlayer localPlayer].isAuthenticated

编辑:稍微清理一下代码 - 删除了不必要的self.isAuthenticated,并阻止了不需要的变量。

答案 1 :(得分:4)

由于某种原因,Game Center身份验证视图控制器是GKHostedAuthenticateViewController的一个实例,它是我们不允许使用或引用的私有类。它没有给我们任何方法来干净地检测何时被解雇(与允许我们通过GKGameCenterViewController协议的GKGameCenterControllerDelegate实例不同。

此解决方案(读取变通方法)的工作原理是每隔一刻钟在后台进行测试,以查看视图控制器何时被解除。它不漂亮,但它有效。

下面的代码应该是presentationViewController的一部分,它应该符合GKGameCenterControllerDelegate协议。

提供Swift和Objective-C。

// Swift
func authenticateLocalUser() {
    if GKLocalPlayer.localPlayer().authenticateHandler == nil {
        GKLocalPlayer.localPlayer().authenticateHandler = { (gameCenterViewController: UIViewController?, gameCenterError: NSError?) in
            if let gameCenterError = gameCenterError {
                log.error("Game Center Error: \(gameCenterError.localizedDescription)")
            }

            if let gameCenterViewControllerToPresent = gameCenterViewController {
                self.presentGameCenterController(gameCenterViewControllerToPresent)
            }
            else if GKLocalPlayer.localPlayer().authenticated {
                // Enable GameKit features
                log.debug("Player already authenticated")
            }
            else {
                // Disable GameKit features
                log.debug("Player not authenticated")
            }
        }
    }
    else {
        log.debug("Authentication Handler already set")
    }
}

func testForGameCenterDismissal() {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.25 * Double(NSEC_PER_SEC))), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
        if let presentedViewController = self.presentedViewController {
            log.debug("Still presenting game center login")
            self.testForGameCenterDismissal()
        }
        else {
            log.debug("Done presenting, clean up")
            self.gameCenterViewControllerCleanUp()
        }
    }
}

func presentGameCenterController(viewController: UIViewController) {
    var testForGameCenterDismissalInBackground = true

    if let gameCenterViewController = viewController as? GKGameCenterViewController {
        gameCenterViewController.gameCenterDelegate = self
        testForGameCenterDismissalInBackground = false
    }

    presentViewController(viewController, animated: true) { () -> Void in
        if testForGameCenterDismissalInBackground {
            self.testForGameCenterDismissal()
        }
    }
}

func gameCenterViewControllerDidFinish(gameCenterViewController: GKGameCenterViewController!) {
    gameCenterViewControllerCleanUp()
}

func gameCenterViewControllerCleanUp() {
    // Do whatever needs to be done here, resume game etc
}

注意:log.error和log.debug调用引用了XCGLogger:https://github.com/DaveWoodCom/XCGLogger

// Objective-C
- (void)authenticateLocalUser
{
    GKLocalPlayer* localPlayer = [GKLocalPlayer localPlayer];

    __weak __typeof__(self) weakSelf = self;
    if (!localPlayer.authenticateHandler) {
        [localPlayer setAuthenticateHandler:(^(UIViewController* viewcontroller, NSError* error) {
            if (error) {
                DLog(@"Game Center Error: %@", [error localizedDescription]);
            }

            if (viewcontroller) {
                [weakSelf presentGameCenterController:viewcontroller];
            }
            else if ([[GKLocalPlayer localPlayer] isAuthenticated]) {
                // Enable GameKit features
                DLog(@"Player already authenticated");
            }
            else {
                // Disable GameKit features
                DLog(@"Player not authenticated");
            }
        })];
    }
    else {
        DLog(@"Authentication Handler already set");
    }
}

- (void)testForGameCenterDismissal
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        if (self.presentedViewController) {
            DLog(@"Still presenting game center login");
            [self testForGameCenterDismissal];
        }
        else {
            DLog(@"Done presenting, clean up");
            [self gameCenterViewControllerCleanUp];
        }
    });
}

- (void)presentGameCenterController:(UIViewController*)viewController
{
    BOOL testForGameCenterDismissalInBackground = YES;
    if ([viewController isKindOfClass:[GKGameCenterViewController class]]) {
        [(GKGameCenterViewController*)viewController setGameCenterDelegate:self];
        testForGameCenterDismissalInBackground = NO;
    }

    [self presentViewController:viewController animated:YES completion:^{
        if (testForGameCenterDismissalInBackground) {
            [self testForGameCenterDismissal];
        }
    }];
}

- (void)gameCenterViewControllerDidFinish:(GKGameCenterViewController*)gameCenterViewController
{
    [self gameCenterViewControllerCleanUp];
}

- (void)gameCenterViewControllerCleanUp
{
    // Do whatever needs to be done here, resume game etc
}

答案 2 :(得分:1)

我可能错了,但我认为实际上有一种方法可以知道身份验证视图控制器什么时候被解雇。我相信当用户解除身份验证视图控制器时,将调用您设置的初始身份验证处理程序,但这次处理程序的viewController参数将为nil。

我的应用程序的工作方式是:在应用程序的开头设置身份验证处理程序,但只有在用户要求查看排行榜时才会显示身份验证视图控制器。然后,当解除此身份验证视图控制器时,初始身份验证处理程序将显示排行榜(如果用户已通过身份验证)或不显示(如果不是)。

答案 3 :(得分:-1)

Game Center DELEGATE方法:当Game Center viewController为'Done'时,会自动调用'gameCenterViewControllerDidFinish'。 (这是代表的必修方法。)

您可以在此方法中添加应用所需的任何内容。