Handling Invitations for Programmatic Turn-Based Game - iphone

Thanks to the updates to GameKit API in iOS 6, I am finally able to implement my turn-based board game the way it should be, complete with turn timeouts and better programmatic creation of matches. However, I am running into an issue that I cannot seem to solve. My desire is to have Game Center running entirely invisible to the end-user, so that everything is programmatic and uses my own custom interfaces.
Therefore, I use my own custom table view to display matches, not the default GKTurnBasedMatchmakerViewController. Right now, I have no problem displaying open matches using the -loadMatchesWithCompletionHandler: method. I also use a custom screen to create a match, with a direct creation for auto-match (not a problem) and a table view that loads Game Center friends of the localPlayer for invitation. Since the playersToInvite attribute can now be filled with playerID's, this is possible in iOS 6.
My main problem is handling the invitation on the recipient's side. Lets say I invite Bob to play my game in a two-player match. Right now I can't seem to find a notification for a new invite on Bob's end. The -handleTurnEvent: only gets called for existing matches or if the banner notification is touched (which I can't guarantee the user will do), and -handleInviteFromGameCenter: does nothing for me in this case.
The only way I have come up with to detect new invites and thus update my custom game view controller is to call the -loadMatchesWithCompletionHandler: method and check for new matches in which lastTurnDate of the invited participant is nil and against an existing array of open matches. I run this check about every 10 seconds in the background since I can't find a notification in GKTurnBasedEventHandler that is called when a new invite is received. Please help!
EDIT: In the end, I have just implemented a pull-to-refresh functionality. There is no way without implementing polling or some other method that would just waste the user's data on their phone, so on demand refreshing is the most ideal solution in my opinion.

Please see this : GKInvite Reference and more specifically inviteHandler.
You just need to register an inviteHandler which will be called after Bob accepts the invite in GK/GC.
T.

Related

How to reliably determine which match got created in response to GKTurnBasedMatchmakerViewController success

On iOS 10 I am trying to create a match using the GKTurnBasedMatchmakerViewController. I can bring up the view so the user can choose matchmaking or an invite. I can't figure out how to reliably determine which match got created in response to that. The problem is that, in iOS 10, the didFindMatch method of the GKTurnBasedMatchmakerViewControllerDelegate has been deprecated. That method used to be called with the match. The old days were easy!
The deprecation message says "use GKTurnBasedEventListener player:receivedTurnEventForMatch:didBecomeActive". The docs and stack overflow threads are filled with reasons why that method gets called. See this thread for a good list: Gamecenter ios 9 GameCenter GKLocalPlayerListener methods not called. If you imagine a user that already has several games going, you can see that this method will be called for a lot of different reasons and it could happen at any time, as far as I know. My question is: how to determine which of these calls is the "I just created a match for you, here it is!" call.
Some examples I think won't work:
Simply assuming the first call to receivedTurnEventForMatch that happens after you bring up the GKTurnBasedMatchmakerViewController seems wrong since receivedTurnEventForMatch could be notifying the user that it is their turn in another game. Unless the system guarantees that, while the view is up, they will only call this method with the match that corresponds to the view. That seems like a pretty big hack, so I'm assuming they don't do it.
A call to receivedTurnEventForMatch with the other player in "Matching" state and didBecomeActive=true seems to be a solution for the first player in a match, but not the second (since both players are done matching).
Looking at the MatchID and seeing if we've "seen it before", and if not, assuming it is the match that just got created seems unreliable since the user might have uninstalled the app and then reinstalled and we've forgotten all about which matches we've "seen before".
I'm stuck, any help is appreciated.
Double check your status when the event handler fires. I believe you should still be in invited state until you accept the match.
edit: Actually, I believe there will be two conditions that indicate a new match when the event handler fires:
You are in the invited state (you joined)
Everyone else is in the invited or automatch state (you started the match)
edit 2:
Checking my old code, it turns out that I looked at the matchData object. Since I knew that player 2 doesn't receive the invite until player1 ended the turn (and so forth), and since I knew that player1 had to save match data in order to end the turn, if matchData.length was greater than zero, I assumed I was joining a game in progress. I didn't rely on the participant status. But that doesn't solve your problem.
The other thing I did was create my own header struct which I inserted at the start of the NSData. In that header, player1 would set status flags for each other player, that would be obvious when those players joined. I totally ignored the participant status in the match object.
Regarding your third bullet, instead of saving the list locally, you could save it remotely using cloudKit. The cloudkit data will persist across installs/uninstalls unless you specifically delete it.
The gist with cloudkit is:
when you enable it in your app, your app gets a "container" with a public database that all users can share plus a private database unique to each app user.
You can create a record in the private database with a "Bytes" field
You can save a NSMutableArray directly into the aforementioned Bytes field
You can read back a NSMutableArray directly from the Bytes field

Handle invitation to GKTurnBasedMatch without game center view controllers

I am working on a simple turn-based word game, and I'm having a difficult time figuring out how invitations are working. When user A invites user B to a game, I want user B to be able to see this game in my custom match interface (ie a screen where all of user B's games are listed). Things are fine if user B clicks on the notification when he is invited to a game, but I still want the game to be visible through my own interface if user B just navigates to the app on his own.
Whenever this match interface screen appears, I am using [GKTurnBasedMatch loadMatchesWithCompletionHandler:] to get all of the local player's matches. With this, I would assume that user B would be able to see the match he has been invited to, but this new match doesn't show up. If I accept the invitation through the GKTurnBasedMatchmakerViewController, the user enters the game as expected, but what makes this even more puzzling is that if I open up my GKTurnBasedMatchmakerViewController and then cancel without accepting the invitation, a new match now shows up in my custom match interface, but it lacks the match data that was sent by user A.
To sum up, I am really just wondering how to properly display and accept GKTurnBasedMatch invitations with a custom interface (rather than having to use GKTurnBasedMatchmakerViewController). Thanks!
This question seems to be of interest to a couple people, so here's a rough approximation of what I ended up doing.
First of all, the reason new matches weren't showing up at all was just that I wasn't calling [GKTurnBasedMatch loadMatchesWithCompletionHandler:] every time I wanted the view to refresh like I though I was. So no real issue there as I recall.
The real issue was the case where I was receiving and displaying the new GKTurnBasedMatch, but none of the data (i.e. the opposing player's first move) was available. Essentially, [GKTurnBasedMatch loadMatchesWithCompletionHandler:] doesn't seem to guarantee providing you with the most up-to-date GKTurnBasedMatch objects available. To make sure the matches are up-to-date, I had to also call [match loadMatchDataWithCompletionHandler:], on each match returned by [GKTurnBasedMatch loadMatchesWithCompletionHandler:] (where match is one of those GKTurnBasedMatches). This returns the most current match data associated with that match as an NSData object. I was then able to use this NSData to make sure all the matches in my match table were refreshed to reflect the most recent changes in game center.
In short, use loadMatchDataWithCompletionHandler on your GKTurnBasedMatch objects to make sure their data is up to date.
UPDATE: now includes the actual answer for receiving invitations.
I have struggled with programmatically handling invites for turn-based matches for close to a week. I finally found the answer. I'm gonna super-highlight it because it took me so long to find:
Game Center treats turn-based invitations as turn events. They are not handled like other invitations.
Turn-based events are handled in this function in the GKLocalPlayerListener protocol:
player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch,
didBecomeActive: Bool)
When you recieve the match, check if you're invited to it, and presto. You've received an invitation.
BUT:
Through frustrating trial and error I have found some caveats, which hopefully can save you some serious time:
It just plain doesn't always work. Believe it or not, Game Center is unreliable. This means you need a back-up system that reviews your local player's open matches and searches them for new invitations. That itself pertains to caveat #2.
A player that is invited to a match (for instance say playerJake is invited to matchFoo) will not actually get that invitation nor see that match in the matches returned by loadMatches until it is their turn. Apparently Game Center does not actually involve any player on a match's invitation list in any way until it is their turn.
If you can identify a match you're invited to, but haven't responded to, you must call acceptInvite(...) directly on that match. So if playerJake inspected the matches retrieved by loadMatches, and was able to detect that matchFoo still had an open invitation, playerJake has to call matchFoo.acceptInvite( /* ...completion handler stuff here... */), and then happy playerJake is off and running.
From this you should be able to get your own programmatic matching system to work. Best of luck, and I mean it!

iPhone GameCenter invitation with playerGroup

I developed an iPhone App that uses the GameCenter. To support different levels of my game I set the playerGroup within the GKMatchRequest class. If the match is started by an automatic match making process both users knows the playerGroup, but if the invitation process is used, the invitee does not know the playerGroup.
I tried to read the playerGroup within the method
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFindMatch:(GKMatch *)match
by using
[viewController matchRequest].playerGroup, but the property playerGroup always return 0, instead the right player group.
Does anyone have an idea how to solve this problem. I need to know the playerGroup to load the right level.
Thank you very much for your help
As far as I know, this is a built in limitation of Game Center. I have been struggling with it too, and asked on several forums, including Apples devforums, without any response. I've written a bug report to Apple and I suggest you do the same.
As a workaround in my own game, where I have several different rulesets that players choose from and where players have to share the same rules for multiplayer matches to work, I put in a "double check" after the game actually starts to make sure everyone is on the same rules: Just in case someone was invited and is using the wrong rules.
What I did is, as soon as the game starts (which you should detect for in both the didFindMatch method of the GKMatchMakerViewControllerDelegate and in match:player:didChangeState: in GKMatchDelegate) I let the players enter the game and then send out packages containing the rules they use to all the other players. When they receive this data, they match it with their own rules and if anyone is using the wrong rules, the game will put up an alert and disconnect the match.
It's a bit ugly, and it's very clear that Apple ought to implement a way to set a GKMatchRequest for a GKMatchMakerViewController when initialized by an invite. But at least it works.
I stumpled upon the same problem. As a workaround I am sending a handshake with all relevant game customization info through the GKMatch object. I send the data with GKMatchSendDataReliable DataMode so the handshake doesn't fail.
After all information is collected by the clients the actual game logic is created and started on its basis.
You can check the Player Group of a match request like this.
func player(player: GKPlayer, didAcceptInvite inviteToAccept: GKInvite) {
if inviteToAccept.playerGroup == 0 || inviteToAccept.playerGroup == myPlayerGroup {
inviteAccepted()
}
else {
// TO SOMETHING
myPlayerGroup = inviteToAccept.playerGroup
inviteAccepted()
}

How would I get SMS/Phone events, and act on them before they're sent to the system apps?

I'm trying to develop an application for a jailbroken iPhone similar to Mcleaner or iBlacklist.
What I want my app to be able to do is intercept SMS or Phone events and handle them before they're sent to the appropriate receiver. So for example, I want the application to get an SMS event, compare the sender to an array of numbers, and either process the data or ignore it and pass it off to the SMS app.
In MCleaner, you can define a blacklist of numbers, and if a text message is received that matches the blacklist the user won't get an alert from the phone and the data is instead handled by MCleaner.
How would I go about getting these events, and further, how would I get these before the appropriate apps receive them? From what I understand, I'd need to become a first responder for these things, but I really have no clue where to start. I can't seem to find any hints on how I'd go about this as this app will not be calling apple classes to get the data..
Thanks.
I can only guide in with this, sorry, the code isn't mine to give.
In memory, all the action calls are all just pointers. What you have to do is over ride the pointer to make it point to your own function. Once you do that, you do w/e you want in your function, and then call the default function call. This ensures the integrity of the action calls and allows you to intercept the actions.
You need to use the CKMessage class.
Check this out: http://hexorcist.com/private_frameworks/html/interface_c_k_message.html
and this: http://www.ifans.com/forums/showthread.php?t=232745

OpenFeint achievements performance

I've decided to integrate OpenFeint into my new game to have achievements and leaderboards.
The game is dynamic and I would like user to be rewarded immediately for some successful results, but as it seems for me, OpenFeint's achievements are a bit sluggish and it shows visual notification only when it receives confirmation from the server.
Is it possible to change something in settings or hack it a little bit to show notification immediately as soon as it checks only local database if the achievement has not been unlocked it?
Not sure if this relates to the Android version of the SDK (which seems even slower), but we couldn't figure out how to make it faster. It was so unacceptably slow that we started developing our own framework that fixes most of open feint's shortcomings and then some. Check out Swarm, it might fit your needs better.
There are several things you can do to more tightly control the timing of these notifications. I'll explain one approach and you can use this as a starting point to explore further on your own. These suggestions apply specifically to iOS apps. One caveat is that these suggestions refer to internal APIs in OFSDK 2.8 for iOS and not ordinarily recommended for high level use and subject to change in future versions.
The first thing I recommend is that you build the sample app with your own product key. Use the standard sample app to experiment before applying the result to your own code.
You are going to get the snappiest response by separating the notification pop-up UI from the process of submitting the achievement. This way you don't have to worry about getting wrapped up in the logic for deciding whether the submission is going just to the local db or is doing the full confirmation on an async network transaction.
See the declaration of "showAchievementNotice" in "OFNotification.h". Performing a search in the sample app, you will see that this is the internal API used for displaying the achievement pop-up when an achievement is earned. It does not actually submit the achievement. You can call this method directly as it is called from "OFAchievementService.mm" to directly control when the message appears. You can then use the following article to disable the pop-up from being called when the actual submission occurs:
http://support.openfeint.com/dev/notification-pop-ups-in-ios/
This gives you complete freedom to call the submission at a later time provided you keep track of the need to do so. For example, you could locally serialize a flag to take care of the actual submission either after the level is done or the next time the app starts up. Don't forget that the user could quit out of a game without cleanly finishing a level.