iPhone GameCenter invitation with playerGroup - iphone

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()
}

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

Game Center Simultaneous Turns

I'm new to iOS (although I do have a lot of C++ experience) and I am working on a turn-based card game using gamecenter. Here's the catch - The player who's turn it is is the judge and waits for all OTHER players to play a card before the turn is over. Is there a way to do this in Game Center, so all players chose what to do simultaneously and once all have done so, the judge is notified, makes a decision, and then passes priority to the next player? For example, say we start turn 1. I ask the question, #"What is your favorite color?" to all players passing them a gamestate which has that as the question and them not having answered it. Then you respond, #"Green", and our friend Steve responds #"Blue". I then decide which is better, give one of you a point, and then whoever I gave the point becomes the new judge. My question is, how do I allow all players to respond simultaneously, rather than sequentially. I know that, worst case, I could sequentially loop around through players until it comes back to me, then judge it, but this would slow down my game and make it less fun. Is there a way to do this simultaneously?
I agree with NSSplendid about the API for turnbased games requiring sequential turns. The only truly simultaneous method would be using the real-time matches from GKMatch, and that isn't really an option for games with more than a few players.
However, the sequential version could be improved slightly by using a programmatic approach to game center instead of the default view controller.
Ending a turn fires off a push notification through Game Center, and by using GKTurnBasedEventHandler's method handleTurnEventForMatch:didBecomeActive:, you can receive that in your app. When the judge asks the question, have the users display that as part of the game, and have their responses stored locally until their turn. Once it becomes a given player's turn, they receive the notification, even with the app in the background.
In the method, it can check the locally stored answer and end the turn immediately, if they've answered. If they haven't, send the turn once the answer is complete. It's not truly simultaneous, but the judge gets the answers as soon as everyone has responded, without the players having to wait for one player to finish before they can enter their own answers.
The players won't get the notification that the judge has ended their turn until they open the app, but they can't see the question anyways without doing that. Another approach to this, though slightly wasteful, is after the judge ends their turn with their question, is to do a runthrough of all the players ending their turn as soon as they get the "Your turn" notification, so everyone knows a question has been asked, then doing the steps from the previous two paragraphs.
The iOS API is built around the model of sequential turns. While the workaround you mentioned will work, there is no way to get GC to do real concurrent turns. Sorry ):

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!

Handling Invitations for Programmatic Turn-Based Game

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.

Online real time multiplayer game for iOS 5/6

I know there are already some posts about this topic, but I cannot find the answer I need and I do not know where to start.
I would like to create an online multiplayer game for the iPhone where players can play together against each other over the Internet. For example a 2 player racing game where once the 2players are matched and connected they can both control their cars in real time against each other. E.g like mario cart.
I know that the game kit does thus but only over Bluetooth or over the same wifi network. I would like this to be over the Internet (wifi only) where players can play against each other around the world.
I know there are already some frameworks to do this. But they are costly and depend on the number of connections. Is there a cheap or dare I say, free way of doing this? Like have game kit do the matching and then the connections and sending data is done some other way? Like having the iPhones host the games? Rather than having a dedicated server. I do not have the budget nor the knowledge and experience to create a dedicated server.
The matching is simple. There are 30 levels and any 2 players wanting to play the same level are matched.
Any links or book recommendations are welcome. I have very limited networking knowledge and do not know where to start.
I can read and learn the techniques even if they are technical but I will need the right resouces to allow me to make a start
Thanks in advance.
You actually can do exactly what you are looking for using the Game Kit API's. Basically, you create a match using the GKMatchMakerViewController. To make the match you use the GKMatchMakerViewController to start looking for other players, once it finds other players it notifies the GKMatchMakerViewControllerDelegate that a match has been found and will pass the GKMatch object. Then you need to have an object which implements the GKMatchDelegate protocol to handle the actual data. You set your delegate object to be the delegate of the GKMatch you are passed, and then use methods from the GKMatchDelegate protocol such as – match:didReceiveData:fromPlayer: and methods from GKMatch to send data.
Heres some example code to help with that explanation. This is just the bare essentials, you of course need to implement your gameplay stuff, and some error handling.
Also, you can find the documentation you need at these four links
GKMatchMakerViewController GKMatchMakerViewControllerDelegate GKMatch GKMatchDelegate
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID{
if(matchStarted){
Packet *msg = (Packet *)[data bytes];
//do whatever you want with the data received from other people
}
}
-(void)sendPosition{
//call this to update the other players devices (should be self explanatory)
NSError *error;
Packet msg;
//Here the msg object is actually a typedefed struct name Packet. I use this to send and receive data
NSData *packet = [NSData dataWithBytes:&msg length:sizeof(msg)];
[myMatch sendDataToAllPlayers: packet withDataMode: GKMatchSendDataUnreliable error:&error];
if (error != nil)
{
// handle the error
}
}
#pragma mark MatchSetup
- (void)matchmakerViewController:(GKMatchmakerViewController *)viewController didFindMatch:(GKMatch *)match{
[self dismissModalViewControllerAnimated:YES];
self.myMatch = match; // Use a retaining property to retain the match.
self.myMatch.delegate = self;
if (!matchStarted)
{
// Insert application-specific code to begin the match.
}
}
What you're looking for is a GameKit "hosted match" wrapper, which possibly does exist. The caveat is that Apple doesn't host these types of matches, your own server does. So, you'd need a webhost, and those aren't usually free. Amazon's AWS is free for a micro-instance, but depending on scale you would probably top out with enough synchronous play.
I'm more interested in learning about potential GameKit wrappers now that you've brought it up. Can you comment on what you've found, you mentioned that they're expensive and have limits on connections.
PS - Apple's limit is up to 16 players
You should check out real-time peer-to-peer networking service PubNub as it sounds like a perfect match for your project.
I found PubNub very easy to implement even with my modest programming knowledge. Integrating different platforms was also easy as there are SDK's for most common languages. I had a PHP based web page that served as a scoreboard for my iOS based app.