Game Center Authenticate Local Player - ios

I am trying to authenticate the local player using the following code:
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
if(!localPlayer.authenticated) {
localPlayer.authenticateHandler = ^(UIViewController* controller, NSError *error) {
if(controller != nil)
[self presentModalViewController:controller animated:YES];
};
}
But the problem is that even though [GKLocalPlayer localPlayer].authenticated is false, the controller that is returned in the authenticationHandler block is always nil. Therefore gamer center always seems to be disabled.

The problem was caused by the authenticateHandler being set twice. The handler should be set once for the duration of the application's life cycle. Subsequent authenticateHandler property being set after the first time will yield unexpected results like the problem I described in the question.

I found this authentication code works fine in iOS 8.
- (void)authenticateLocalPlayer
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
if (localPlayer.isAuthenticated) {
[[NSNotificationCenter defaultCenter] postNotificationName:LocalPlayerIsAuthenticated object:nil];
return;
}
localPlayer.authenticateHandler =
^(UIViewController *viewController, NSError *error) {
[self setLastError:error];
if(viewController != nil){
[self setAuthenticationViewController:viewController];
} else if([GKLocalPlayer localPlayer].isAuthenticated) {
NSLog(#"connected to gk");
_GKConnected = YES;
[[NSNotificationCenter defaultCenter] postNotificationName:LocalPlayerIsAuthenticated object:nil];
}
else {
_GKConnected = NO;
}
};
// REGISTER LISTENER FOR INVITES
[localPlayer registerListener:self];
}
The notification strings can be any constant string (it is good practice to prefix a reverse DNS for use with NotificationCenter org.myapp.notificationname).
In the original OP question was an error in retrieving the getter for the authenticated property in GKPlayer instance: the property name is authenticated but the getter has to be isAuthenticated.
Can be trivial to catch out those semantic typos, whilst the compiler doesn't warn you because it could be more code behind a simple property accessed with dot notation and by default setters begins with is.
By the way I can add a link to a well known tutorial with working sample tutorial:
http://www.raywenderlich.com/60998/game-center-tutorial-how-to-make-a-simple-multiplayer-game-with-sprite-kit-part-2

Related

GKMatchmaker findMatchForRequest invite never received

I'm trying to invite nearby players to a match, but the invite is either never sent or never received.
GKMatchMaker startBrowsingForNearbyPlayersWithHandler works and returns nearby players that are on same wifi, but then I use findMatchForRequest and it returns a match without any players, and the players I try to invite never receive an invite notification. Here is my code.
I start by authenticating the local player:
GKLocalPlayer.localPlayer.authenticateHandler= ^(UIViewController *controller, NSError *error)
{
if (error)
{
NSLog(#"%s:: Error authenticating: %#", __PRETTY_FUNCTION__, error.localizedDescription);
return;
}
if(controller)
{
// User has not yet authenticated
[pViewController presentViewController:controller animated:YES completion:^(void)
{
[self lookForNearbyPlayers];
}];
return;
}
[self lookForNearbyPlayers];
};
-(void)lookForNearbyPlayers
{
if(!GKLocalPlayer.localPlayer.authenticated)
{
NSLog(#"%s:: User not authenticated", __PRETTY_FUNCTION__);
return;
}
I register my view controller as a delegate of GKLocalPlayerListener:
[GKLocalPlayer.localPlayer registerListener:self]; // self is a view controller.
// This works. My test local player which is a second device and appleID I setup shows up when this handler is called.
[GKMatchmaker.sharedMatchmaker startBrowsingForNearbyPlayersWithHandler:^(GKPlayer *player, BOOL reachable)
{
NSArray * paPlayers= [NSArray arrayWithObject:player];
_pMatchRequest= [[GKMatchRequest alloc] init];
_pMatchRequest.minPlayers= 2;
_pMatchRequest.maxPlayers= 4;
_pMatchRequest.recipients = paPlayers;
_pMatchRequest.inviteMessage = #"Join our match!";
_pMatchRequest.recipientResponseHandler = ^(GKPlayer *player, GKInviteeResponse response)
{
// This is never called.
NSLog((response == GKInviteeResponseAccepted) ? #"Player %# Accepted" : #"Player %# Declined", player.alias);
};
// This returns with a match without any players.
[GKMatchmaker.sharedMatchmaker findMatchForRequest:_pMatchRequest withCompletionHandler:^(GKMatch *match, NSError *error)
{
if(error)
{
NSLog(#"%s:: %#", __PRETTY_FUNCTION__, error.localizedDescription);
return;
}
else if(match != nil)
{
_pMatch= match;
match.delegate = self;
NSLog(#"players count= %lu", (unsigned long)_pMatch.players.count); // Always returns 0
}
}];
}
}
I have delegate methods for GKLocalPlayerListener setup, but they are never called:
- (void)player:(GKPlayer *)player didRequestMatchWithRecipients:(NSArray<GKPlayer *> *)recipientPlayers
{
NSLog(#"%s", __PRETTY_FUNCTION__);
}
- (void)player:(GKPlayer *)player didAcceptInvite:(GKInvite *)invite
{
NSLog(#"%s", __PRETTY_FUNCTION__);
}
Does anyone know how to get this to work without GKMatchmakerViewController and for iOS9? The only examples I can find have the deprecated -inviteHandler method.
This code is working in Swift if you know how you can convert it to Objective-C and to try it.
GKMatchmaker.sharedMatchmaker().findMatchForRequest(
request,
withCompletionHandler: {(match : GKMatch!, error: NSError!) -> Void in
NSLog("This works")
})
Based on multiple questions here on SO, Game Center seems to be getting stuck from time to time. In the best case, it returns "Game not recognized" errors. In the worst case, it just cheerfully returns nil to GC calls. Sometimes it resumes working on it's own, sometimes it doesn't. But it seems you can kickstart it again by logging into iTunesConnect and do any of the following:
Add a leaderboard
Change the default leaderboard
Add an achievement
I've added this to my debugging routine. If some aspect of GC stops working, or returns nil, I try making one of the above changes in iTunesConnect before proceeding. In my case, I get the "game not recognized" several times per week, but several others have noted the "nil return values."
I know this an older post, but I ran across it when trying to establish a connection between several app instances over the internet. I believe the part you're missing is that after registering for the listener, you need to receive the connected status with
- (void)match:(GKMatch *)match
player:(GKPlayer *)player
didChangeConnectionState:(GKPlayerConnectionState)state
{
NSLog(#">>> did change state");
if (state == GKPlayerStateConnected)
{
NSLog(#">>>> match:%# did change to Connected for player %# ",match, player.displayName);
}
else if (state == GKPlayerStateDisconnected)
{
NSLog(#">>>> match:%# disconnected for player %# ",match, player.displayName);
}
I find the match has 0 players when the completionHandler is called from findMatchForRequest:, but that I can successfully use the GKMatch and GKPlayer as returned in didChangeConnectionState:
Hope that helps someone who reads this long after the OP.

why GKLocalPlayer from game center in ios return nill when authenticate succeeded

I used demo in ios 9 for testing game center, [GKLocalPlayer localPlayer] returns the right information, but when i used in my app, it returns nil. I cannot find why.The demo and app use the same bundle identifier and signature.
Anyone knows the answer, please help the despairing me!
sorry for poor English, wish you can understand them.
Can it caused by complex thread?
Please answer me!!!!!
Beg your answer!!!!!
SOS!!
See a code sample
//here is some of codes i use;
- (void)authenticateLocalUser : (bool)bLoginGame{
if(!gameCenterAvailable)
{
return;
}
NSLog(#"Authenticating local user...");
if ([GKLocalPlayer localPlayer].authenticated == NO)
{
[GKLocalPlayer localPlayer].authenticateHandler=^(UIViewController* viewcontroller, NSError *error) {
if(viewcontroller != nil)
{
if(bLoginGame)
{
[g_sharedInstanceAppDelegate.viewController presentViewController:viewcontroller animated:YES completion:nil];
}
}
else if(error == nil && [GKLocalPlayer localPlayer].authenticated)
{
if(bLoginGame)
{
NSString* uid = [GKLocalPlayer localPlayer].playerID;
NSString* name = [GKLocalPlayer localPlayer].displayName;
GameCenterLoginCallback(uid, name, nil, false);
}
}
else
{
NSLog(#"LOGIN FAILED!");
if(bLoginGame)
{
GameCenterLoginCallback(nil, nil, error, true);
}
}
};
}
else{
NSLog(#"Already authenticated!");
if(bLoginGame)
{
NSString* uid = [GKLocalPlayer localPlayer].playerID;
NSString* name = [GKLocalPlayer localPlayer].displayName;
GameCenterLoginCallback([GKLocalPlayer localPlayer].playerID, name, nil, false);
}
}
sad for no one give me the right answer...
but today i suddenly found the reason why [GKLocalPlayer localPlayer] information is nil.....so i write here, for some one may need it...
Edit scheme->Run->Diagnostics->unselect [enable address sanitizer].
This will fix the question.But donot know why caused that.

game center login form when player not authenticated sprite kit

I created a class of NSObject and I have this authenticate function in it:
-(void)authenticateLocalUser {
if(!gameCenterAvailable) { return; }
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
__weak GKLocalPlayer *blockLocalPlayer = localPlayer;
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error){
NSLog(#"authenticateHandler");
if (viewController != nil)
{
NSLog(#"viewController != nil");
}
else if (blockLocalPlayer.isAuthenticated)
{
NSLog(#"localPlayer already authenticated");
//do some stuff
}
else
{
NSLog(#"local player not authenticated");
// not logged in
}
};
}
Its the new version i looked up , but its not displaying the Game Center login form.
Im calling this method in my AppDelegate.m if you need this information.
I think I have to put something in there when the player is not logged in like get the login form. But I don't know how.
Thanks for any help!
There are two things you need. First, you need to check error. If it is set, the viewController will usually be nil and your code will incorrectly assume that you're logged in and communicating happily Game Center. You may appear logged in based on cached credentials, but in most cases, you won't actually be seeing current data from the server, you'll just have access to whatever data was last cached on the device.
Second, you have to present the viewController to the user if they're not logged in. Try this:
localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error)
{
NSLog(#"authenticateHandler");
if (error)
{
//Examine the error. IOS may show you as "authenticated" based on
//cached credentials, but you probably can't really communicate with
//game center in any meaningful manner
}
else if (viewController != nil)
{
//You're not logged in, show the login viewController
NSLog(#"viewController != nil");
UIViewController *mainVC = [[[[UIApplication sharedApplication]delegate] window] rootViewController];
[mainVC presentViewController:viewController animated:NO completion:^
{
//the login view controller has been dismissed. Did it work?
if ([GKLocalPlayer localPlayer].authenticated)
{
//Yes, Login worked. enable whatever comes next after successful login
}
else
{
//No, login failed or was canceled.
}
}];
}
else if (blockLocalPlayer.isAuthenticated)
{
NSLog(#"localPlayer already authenticated");
//do some stuff
}
else
{
NSLog(#"local player not authenticated");
// not logged in
}
};

IOS Game Center: How to know when default sign in form for game center is finished?

I was wondering how to know when Game Center sign in form is finished by the user. I am doing Facebook sign in automatically but I need to wait for Game Center login to finish. Is there any way of knowing that?
-(void) authenticateLocalPlayer {
GKLocalPlayer* localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler =
^(UIViewController *viewController,
NSError *error) {
[self setLastError:error];
if (localPlayer.authenticated) {
_gameCenterFeaturesEnabled = YES;
NSLog(#"local Player Info: %#",localPlayer);
[[UserManager sharedInstance] setGameCenterId:localPlayer.playerID];
[[UserManager sharedInstance] setUserName:localPlayer.alias];
[self retrieveFriends];
} else if(viewController) {
[self presentViewController:viewController];
} else {
_gameCenterFeaturesEnabled = NO;
}
};
}
-(void) setLastError:(NSError*)error {
_lastError = [error copy];
if (_lastError) {
NSLog(#"GameKitHelper ERROR: %#", [[_lastError userInfo]
description]);
}
}
-(UIViewController*) getRootViewController {
return [UIApplication
sharedApplication].keyWindow.rootViewController;
}
-(void)presentViewController:(UIViewController*)vc {
UIViewController* rootVC = [self getRootViewController];
[rootVC presentViewController:vc animated:YES completion:nil];
}
Here is a quote from authenticateHandler documentation
The authenticate handler will be called whenever the authentication
process finishes or needs to show UI. The handler may be called
multiple times. Authentication will happen automatically when the
handler is first set and whenever the app returns to the foreground.
If the authentication process needs to display UI the viewController
property will be non-nil. Your application should present this view
controller and continue to wait for another call of the
authenticateHandler. The view controller will be dismissed
automatically.
Possible reasons for error:
1. Communications problem
2. User credentials invalid
3. User cancelled
So the block will be called multiple times,
For example if user is not logged in, a view-controller will be passed to this block, after you present it and user submits the form, he block will be executed once again.
Here is code that handles authentication:
- (void)authenticateLocalPlayer
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
__weak typeof(localPlayer) weakLocalPlayer = localPlayer;
localPlayer.authenticateHandler =
^(UIViewController *viewController, NSError *error)
{
if (error) {
// Something happened, handle it...
return;
}
__strong typeof(weakLocalPlayer) strongLocalPlayer = weakLocalPlayer;
if (viewController) {
// Just show it, user needs to submit the form
return;
}
if (strongLocalPlayer.isAuthenticated) {
// User completed login, do FB login
} else {
// GameKit is disabled, show guide to enable it
}
};
}

iOS GameCenter Integration Error

In DidFinishLaunchingWithOptions I am trying to authenticate the player with this code
// Authenticate Player with Game Center
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
// Handle the call back from Game Center Authentication
[localPlayer localPlayer.authenticateHandler:^(NSError *error)
{
if (localPlayer.isAuthenticated)
{
// Player was successfully authenticated.
// Perform additional tasks for the authenticated player.
}
else if (error != nil)
{
NSLog(#"error : %#", [error description]);
}
}];
return YES;
}
But I get the error on this line [localPlayer localPlayer.authenticateHandler:^(NSError *error) saying missing a square bracket ']' and is pointing at the full stop '.'
I can't get this working
Thanks
You're not sending a message to the local player instance, so this is plain grammatically incorrect.
Please read.
You should be doing something like:
[localPlayer setAuthenticateHandler:
^(UIViewController *viewController, NSError *error) {
// ...
}
];

Resources