处理Game Center身份验证

sol*_*eil 5 authentication gamekit ios game-center

根据Apple文档,我们应该做这样的事情来处理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");

            }
        })];

    }
}
Run Code Online (Sandbox Code Playgroud)

我们获得了这些信息:

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

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

显然我需要知道身份验证是否有效,我需要知道何时恢复游戏,如果我不得不暂停它,因为提出了神奇的GC视图控制器.

Kai*_*zay 13

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

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

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

-(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");
}
Run Code Online (Sandbox Code Playgroud)

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

how do we know when this view controller gets dismissed,
Run Code Online (Sandbox Code Playgroud)

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

我需要知道何时恢复游戏,如果我不得不暂停它,因为提出了神奇的GC视图控制器.

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

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

如果验证成功,则视图控制器为零,并且localPlayer.isAuthenticated为true.

或不 ?

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

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

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


Dav*_*ood 5

由于某些原因,Game Center身份验证视图控制器是一个实例,GKHostedAuthenticateViewController该实例是我们不允许使用或引用的私有类。它并没有提供任何方法来干净地检测何时将其关闭(与之不同的是,它的实例GKGameCenterViewController允许我们通过GKGameCenterControllerDelegate协议进行操作)。

此解决方案(读取解决方法)通过每隔四分之一秒在后台测试一次视图控制器被关闭的时间来工作。它不漂亮,但是可以用。

下面的代码应该是presentingViewController的一部分,它应该符合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
}
Run Code Online (Sandbox Code Playgroud)

注意: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
}
Run Code Online (Sandbox Code Playgroud)