I've been trying to implement a way to notice when my app gets disconnected from Game Center since ending a turn while disconnected breaks the app. I just started testing on my iPad with the WiFi off and noticed that even when I completely quit the app, it automatically authenticates my player and signs me into game center. It even loads game data from recent games. I then tried going on the game center app itself and the only thing that caused a network error was clicking on the "Games" tab. Obviously much of the data is being cached so I don't know what to check to see if I'm disconnected.
So how can I test if the device is connected to Game Center? Neither Match data, participants, nor any of their properties are nil, and the player is always authenticated.
Try to manage this with two things :
Network connection : with something like FXReachability and then [FXReachability isReachable]
Game Center authentification :
In your app test authentification with :
GKLocalPlayer.localPlayer.isAuthenticated
More generally, always test completion handlers errors and array existence like this :
[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray *matches, NSError *error) {
if (error) {
// what kind of error ?
}
if (matches) {
// do what you need to do
}}];
Hope this will help you a little bit...
Related
Is a valid, even in case of an authentication error?
GKLocalPlayer.localPlayer.authenticateHandler =
^(UIViewController *viewController, NSError *error)
{
if (error)
{
bool a = GKLocalPlayer.localPlayer.authenticated;
}
else
{
This happens for instance when I have an authenticated player, moves the app to the background, disables the WiFi, and then move the app to foreground again. My hope is that GameCenter just continues with a cached account?
I find the manual a bit ambiguous.
From https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/GameKit_Guide/Users/Users.html#//apple_ref/doc/uid/TP40008304-CH8-SW11:
"As soon as your game moves to the background, the value of the local player object’s authenticated property becomes and remains invalid until your game moves back to the foreground. You cannot read the value to determine if the player is still authenticated until Game Kit reauthenticates the player and calls your authentication handler. Your game must act as though there is not an authenticated player until your completion handler is called. Once your handler is called, value stored in the authenticated property is valid again."
Is the value valid even though the authentication failed?
I have a long-running Bug with Apple on this. It's been closed and re-opened during the ongoing dialog. The question of whether or not .authenticated is valid seems to depend on your perspective
Apple views this as working-as-intended as you have cached information which Apple believes lets you go ahead and play your game, you can display leaderboards, etc. Apple says that .authenticated is indeed valid in this state. I've seen some developers on this forum agree with that perspective, although I don't have links handy to their posts.
In practice, though, if you attempt to do any subsequent game center operation while in this state, it will fail because you're not really authenticated. You can't save games, load matches, etc. Any leaderboard you display will be stale, cached data.
It appears to me that Apple loathes for players to ever see a problem resulting from their infrastructure. Thus, with this mechanism you attempt to drive forward by faking the state, hoping the problem works itself out later. In my games, that strategy never pans out and eventually reaches an unrecoverable situation after users have invested time/effort into the game. So, like your code above, I rely on what the NSError says. If is says "error" then I treat the player as unathenticated, providing UI prompts to correct the situation.
I've documented more details on my approach here: https://stackoverflow.com/a/37216566/1641444
I have an turn-based game using GameKit in iOS. Generally, my authentication with Game Center works. My game works and I can for periods of time send moves back and forth. However, relatively frequently but not constantly, when I try to perform an action (e.g., finding a new match) over Game Center it fails with the error:
Error Domain=GKErrorDomain Code=6 "The requested operation could not be completed because local player has not been authenticated." UserInfo={NSLocalizedDescription=The requested operation could not be completed because local player has not been authenticated.}
Yet, the GKLocalPlayer.localPlayer().authenticated = true, both immediately before getting the error and after. After getting this error, the authentication viewController does not get presented and GameKit methods that rely on authentication cease to function (they don't come back to life ). Then, if I send the app to the background and then bring it back to the foreground, the error does not return when I retry the action (without requiring new login and password entry).
According to this similar observation:
GKLocalPlayer authentication not working, but isAuthenticated returns YES (Game Center sandbox)
"1.Game Center fails to complete authentication if your device has incorrect dates. So, go ahead and check the current date.
You might have done this. I trust you - iOS Simulator >> Reset Content and Settings"
I am getting this problem on the device, not the simulator, ruling out #2. Could someone help me with #1? I may be naive here, but my iPhone and iPad have the right time and date. Is there something or somewhere else I should be checking and setting?
Then, if this is not the issue, what else could be the problem? I am running iOS9.
I've seen this too, and have an open bug with Apple. In my case, I'm testing with multiple physical devices, and only one of those devices ever encounters this. On that particular device, I'm logged into iTunes using a personal account, but logged into game center using a test account.
From what I can see, when the device is using the same login for iTunes, iCloud and game Center, there are no problems. But, when I try to mix-and-match the accounts for whatever testing I need to do, then I randomly get error 6.
There doesn't seem to be anyway to recover from this, except to have the user re-login into game center. Of course, there's no way to present the login view controller again, so the only option is for the user to leave the game, kill it off, and star up again (which re-starts the game center authentication process). I trap the error now, give the user a message saying Game Center has logged them out, and explain what they need to do about it.
But as I said, when I stopped mixing up credentials on the device, the problem went away.
BTW, on a slightly related topic, I've come to view the .authenticated property as completely unreliable. It will be TRUE in conditions where you do not actually have connection to Game Center. I opened a bug on this, too. Apple closed it saying this was working as intended by using "cached" data. Thus, it will report authenticated when it's not authenticated and give you access only to outdated match and leaderboard data which had previously been saved locally.
I have made an iOS multiplayer GameCenter game, but right before publishing found an issue I don't know how to solve. In coding process I used Ray Wenderlich tutorial http://www.raywenderlich.com/3276/how-to-make-a-simple-multiplayer-game-with-game-center-tutorial-part-12
GameCenter view controller is shown, connection creates and game can be played until both devices are on the same Wifi network.
If I turn off Wifi on my phone and use 3G network, then try to start new game - in that case connection isn't made anymore. Both devices find each other, but hangs on "Connecting..." screen. It looks like that
- (void)match:(GKMatch *)theMatch player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state
is not called. Any ideas how to solve it or at least understand, where exactly is the problem?
I think that in your particular case the problem is that your 3G ISP restricts connections from necessary ports. The Apple docs say:
To use Game Center ... port forwarding must be enabled for ports 443 (TCP), 3478-3497 (UDP), 4398 (UDP), 5223 (TCP), 16384-16387 (UDP), and 16393-16472 (UDP)
I faced this issue too when trying to play on iPad connected via bluetooth to iPhone: there was "Connecting..." screen on each device.
But when I use built-in iPad 3G (with a different tariff plan) everything goes fine.
Just remind, in a normal match-making scenario match:player:didChangeState: may not be called. You should also check match.expectedPlayerCount:
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFindMatch:(GKMatch *)theMatch {
//...
if (theMatch.expectedPlayerCount == 0) {
NSLog(#"Ready to start match!");
}
}
Also I expected a similar problem with "Connecting..." screen, but on Wifi network.
And it was reproduced only on iOS6 and after I tried rematch via -[GKMatch rematchWithCompletionHandler:^(GKMatch *match, NSError *error) {}] before.
One device hanged on "Connecting..." screen but on the other matchmakerViewController:didFindMatch: was successfully called, but what is interesting is that match.expectedPlayerCount was 0 and match.playerIDs array was empty at the same time.
I think such error occurred because I tried to find a new match while previous match tried to reconnect on background thread in the same time. And because of that new match was obtained corrupted.
The decision is to wait for rematchCompletion being called and only then try to find new match. There is no interface in GKMatch to cancel rematch, so I use [[GKMatchmaker sharedMatchmaker] cancel] and after several seconds rematchCompletion is called with error and we are ready to start finding new match.
Also I figured out that old unsued GKMatch instances are not deallocated and continue to live somewhere in GameKit framework. And they may probably cause problems if the work with them is not finished correctly (i.e. not disconnected, or rematch is not canceled in my case ). So do not forget to call -[GKMatch disconnect] and finish any other kind of work before removing the last strong reference to the match object .
I am using iOS 6 Game Center API for turn based games.
When the device is disconnected from internet
In the completion handler of the method
[currentMatch endTurnWithNextParticipant:nextParticipant matchData:data completionHandler:^(NSError *error) {
if (error) {
NSLog(#"%#", error);
} else {
//save the new state of the game
}
I get an error. But then, game center standard UI that displays matches list, says "Their turn". when connected again it changes to "Your turn".
The code from famous tutorial at http://www.raywenderlich.com/5509/beginning-turn-based-gaming-with-ios-5-part-2 has the same exact problem.
How I should handle this problem?
If you are using iOS 6 Game Center API then you will have to use
-endTurnWithNextParticipants:turnTimeout:matchData:completionHandler:
because...
–endTurnWithNextParticipant:matchData:completionHandler: Deprecated in iOS 6.0
http://developer.apple.com/library/ios/#documentation/GameKit/Reference/GKTurnBasedMatch_Ref/Reference/Reference.html
The thing is, that when you use GC methods that change status of the match (matchData and synchronization info in this case), data is uploaded to the GC server so that other player(s) get the update. If you're disconnected and ignore the error, your local GKTurnBasedMatch and its matchData change, as well as your synchronization info (which is used to determine if it is your turn to act among other things).
However, since you are diconnected, only your local instance of GCTurnBasedMatch is updated (you get error so that you app is aware of that). When you're reconnected, your app authenticates the user and updates match state (if you're following the tutorial code). Updating match data reverts the sync data (so it's still your turn).
At this point, you should either submit the turn again (provided that you cached gameData that was passed to GC while you were disconnected) and/or call updateMatchData so that your local GKTurnBasedMatch and its matchData get in sync with what's on the server. You should also re-layout your game board with previous turn's data if you didn't re-submit turn after reconnection.
The following code is called once upon applicationDidFinishLaunching:; however, it runs each time my app re-enters the foreground again.
[localPlayer authenticateWithCompletionHandler:^(NSError *error) {
if (localPlayer.isAuthenticated)
{
// Some implementation
}
}];
This makes sense, according to the Game Kit Programming Guide:
... it also retains your completion handler for later use. Each time your application is moved from the background to the foreground, Game Kit automatically authenticates the local player again on your behalf and calls your completion handler to provide updated information about the state of the authenticated player.
Is there any way to delay this authentication until Game Center is actually needed? The reason I ask is that I would like to avoid showing the "Welcome back, userX!" banner each and every time the app is brought to the foreground.
No, you can't, at least not with public APIs.