gamecenter turnbased save match data out of turn - ios

Any chance I can save/update matchdata even when it is not my turn?
[currentMatch saveCurrentTurnWithMatchData:data completionHandler:^(NSError *error) {
if (error)
{ }];
The above code can be used if it is still this user's turn, but what if it is not this user's turn? How do I send data between two players?

As of iOS 6.0, you cannot. :(
You can save match data without advancing the turn (assuming you are
the current player). see - saveCurrentTurnWithMatchData:completionHandler:
You can end a game out of turn. see - participantQuitOutOfTurnWithOutcome:withCompletionHandler:
However, you cannot update match data out of turn.
GKTurnBasedMatch Reference

Try this
- (void) advanceTurn
{
NSData *updatedMatchData = [this.gameData encodeMatchData];
NSArray *sortedPlayerOrder = [this.gameData encodePlayerOrder];
this.MyMatch.message = [this.gameData matchAppropriateMessage];
[this.myMatch endTurnWithNextParticipants: sortedPlayerOrder turnTimeOut: GKTurnTimeoutDefault
matchData: updatedMatchData completionHandler ^(NSError *error) {
if (error)
{
// Handle the error.
}
}];
}

Related

Realm-cocoa: How to add notification inside other notification block

In my app, I have registered a notification block that itself has to register an other notifiaction block.
notificationToken = [self.appState.currentProject addNotificationBlock:^(BOOL deleted, NSArray<RLMPropertyChange *> *changes, NSError *error) {
if (deleted) {
NSLog(#"The object was deleted.");
} else if (error) {
NSLog(#"An error occurred: %#", error);
} else {
bool filterChanged = false;
for (RLMPropertyChange * change in changes){
if([change.name isEqualToString:#"filters"])
filterChanged = true;
}
if(filterChanged){
// compute predicate from filters
// predicate = [NSPredicate ...
styles = [Style objectsWithPredicate:predicate]
notificationTokenStyles = [styles addNotificationBlock:^(RLMResults<Style *> * _Nullable results, RLMCollectionChange * _Nullable change, NSError * _Nullable error) {
// react on changed styles.
}]; // <- RLMException here
}
}];
}
}];
I know that this is technically not possible, but for me it is necessary. Can I somehow dispatch or defer the nested addNotificationBlock to another thread or so? I know I could wait 500 ms, but that would result in a race condition.
Now, my problem is that as soon as I perform a wirte transaction that leads to the first notification block to execute, I encounter RLMException: Cannot register notification blocks from within write transactions. just at the nested addNotificationBlock.
Thanks in advance!
This is a known problem (see this Realm issue on GitHub).
You can either wrap the creation of the new NotificationToken like this (Swift code):
DispatchQueue.main.async {
// register observation blocks here
}
Or you can call realm.refresh() before starting the write transaction.

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.

End a turn in Game Center turn based match.

I´m having troubles with ending turns in my turn based game app.
The method I´m using is
GKTurnBasedMatch *currentMatch = [[GCTurnBasedMatchHelper sharedInstance] currentMatch];
[currentMatch endTurnWithNextParticipants:p turnTimeout:1000 matchData:data completionHandler:^(NSError *error) {
if (error) {
NSLog(#"%#", error);
}
}];
Here p is my NSArray for nextParticipants and this is my declaration for it:
and this is the declaration and assignment for
NSArray *p = [[currentMatch.participants reverseObjectEnumerator] allObjects];
I´m reversing the participants array to get the turn order of the players. (Only 2)
This all compile and runs without an error, but the turn never actually passes to the other player!
Thinking my p-array is the problem I´ve tried passing it without reversing it which produced the same result.
Does anyone know the correct way to handle this?
Replace your code with
GKTurnBasedMatch *currentMatch = [[GCTurnBasedMatchHelper sharedInstance] currentMatch];
GKTurnBasedParticipant *nextPerson = [currentMatch.participants objectAtIndex:((currentIndex + 1) % [currentMatch.participants count])];
[currentMatch endTurnWithNextParticipants:[NSArray arrayWithObject:nextPerson] turnTimeout:1000 matchData:matchData completionHandler:^(NSError *error) {
if (error) {
NSLog(#"%#", error);
}
}];

iOS: Retrieve Game Center friends who are online

I can retrieve all Game Center friends with this code...
GKLocalPlayer *lp = [GKLocalPlayer localPlayer];
if (lp.authenticated)
{
[lp loadFriendsWithCompletionHandler:^(NSArray *friends, NSError *error)
{
NSLog(#"MY FRIENDS: %#",friends);
if (friends != nil)
{
[GKPlayer loadPlayersForIdentifiers:friends withCompletionHandler:^(NSArray *players, NSError *error)
{
if (error != nil)
{
// Handle the error.
NSLog(#"PLAYERLIST ERROR: %#",[error localizedDescription]);
}
if (players != nil)
{
// Process the array of GKPlayer objects.
NSLog(#"PLAYERS: %#",players);
}
}];
}
}];
}
... however, is there a way to retrieve only the friends with GameKit who are online?
It does not look like you are able to. Since GKPlayer does not offer any way to view if a player is online or not.
Also since technically once a person logs on to Game Center they are "Online" till they log off. Meaning they could be online for days while using their phone. When they are logged on if you send them an invite they will get the trumpet noise.
http://developer.apple.com/library/IOS/#documentation/GameKit/Reference/GKPlayer_Ref/Reference/Reference.html#//apple_ref/doc/uid/TP40009599

Game Center GKTurnBasedMatch problems with matchData property

I'm creating a Game Center game with GKTurnBasedMatch matches. I'm having a problem where the readonly matchData property on a GKTurnBasedMatch does not seem to be properly stored on Game Center servers.
I'm using this StackOverflow answer to generate an md5 checksum on the matchData NSData, both when being sent and received to and from the Game Center servers.
I note the checksum of my NSData game data object when I send the matchData using the GKTurnBasedMatch instance method endTurnWithNextParticipants:turnTimeout:matchData:completionHandler:.
The opponent then retrieves the matches using GKTurnBasedMatch's class method loadMatchesWithCompletionHandler:, and when the matches arrive (no errors), I note the checksum again.
The two checksums do not match, and the resulting data are clearly not identical based on the reconstructed game. I have checked in the two accounts that the matchID property on my GKTurnBasedMatch objects are identical.
I've also performed the following test:
NSLog(#"matchID: %# matchData checksum: %#",
match.matchID,
[Utilities md5StringFromData:match.matchData]);
// match is a valid `GKTurnBasedMatch` object.
[match endTurnWithNextParticipants: #[ opponent ] // My `GKTurnBasedParticipant` opponent
turnTimeout:600
matchData:data // This is a valid NSData object
completionHandler:^(NSError *error) {
if (nil != error) {
NSLog(#"%#", error);
} else {
NSLog(#"Successfully ended turn.");
[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray *matches, NSError *error) {
if (nil != error) {
NSLog(#"Error getting matches: %#", [error localizedDescription]);
} else {
for (GKTurnBasedMatch *match in matches) {
NSLog(#"matchID: %# matchData checksum: %#",
match.matchID,
[Utilities md5StringFromData:match.matchData]);
}
}
}];
}
}];
In this sample, where I end the turn with data and immediately retrieve the matches from Game Center, the data match. However, when I access the matchData from the opponent's Game Center account and device, they differ.
Anyone encountered anything like this?
I discovered the solution on Apple's Dev Forums.
It turns out that loadMatchesWithCompletionHandler: doesn't always grab the most up-to-date matchData. To make sure you have the most recent version, make sure you call the loadMatchDataWithCompletionHandler: method on your GKTurnBasedMatch object.

Resources