Game Center - Sending and receiving data
Asked Answered
H

1

6

EDIT: I have made a clean, new project, but still can't get it working. Please download it, there is a little code to look at and probably easy for a professional or anyone remotely experience to see whats I am doing wrong. Just trying to send that integer.

http://www.2shared.com/file/fPOCLlg5/gkTest.html

Hi

I am trying to implement Game Center multiplayer in my iphone game and having trouble understanding the samples I have at hand in the Apple Docs and from third parties concerning sending and receiving data.

Could someone please explain the code samples in the Official Apple docs here please: http://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/GameKit_Guide/MatchesandVoice/MatchesandVoice.html#//apple_ref/doc/uid/TP40008304-CH10-SW4

Or help me figure out this sample code I was supplied with. Its a prebuilt class, made to handle all the game center tasks and a sample from it for sending and receiving data would be this:

- (void) sendPosition
{
    NSError *error;
    PositionPacket msg;
    msg.messageKind = PositionMessage;
    msg.x = currentPosition.x;
    msg.y = currentPosition.y;
    NSData *packet = [NSData dataWithBytes:&msg length:sizeof(PositionPacket)];
    [match sendDataToAllPlayers: packet withDataMode: GKMatchSendDataUnreliable error:&error];
    if (error != nil)
    {
        // handle the error
    }
}

And receiving:

- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID
{
    Packet *p = (Packet*)[data bytes];
    if (p.messageKind == PositionMessage)
        // handle a position message.
}

My big question about this code form the official docs is:

Where does PositionPacket/Packet come from? And assuming when you want to send/receive data you call them like so:

[self sendPosition];

or

[self match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID];

What do I enter as the match, data and playerID?

E.g. I have an int named 'score' but is there not a special key I need to use something?

Hypercritical answered 1/1, 2011 at 12:58 Comment(0)
G
18

In this example, the PositionPacket is just a struct. The following line then puts that struct into an NSData which is just a "byte bucket" object.

NSData *packet = [NSData dataWithBytes: &msg length: sizeof(PositionPacket)];

So if you just wanted to send an int score, you could have a sendScore method that looked like this:

- (void) sendScore
{
    NSError *error;
    int myScore = self.score;
    NSData *packet = [NSData dataWithBytes:&myScore length:sizeof(myScore)];
    [match sendDataToAllPlayers: packet withDataMode: GKMatchSendDataUnreliable error: &error];
    if (error != nil)
    {
        // handle the error
    }
}

Typically, you'll want a struct so that there's some additional information that lets the receivers know what kind of data it is. In the example, that would have been the purpose of this line:

msg.messageKind = PositionMessage;

In general, you can send anything you want encapsulated in an NSData object, since it's just a byte bucket. You can send primitive types like ints, or structs as in the example, or even NSObjects (as long as they implement NSCoding). You should read up on NSKeyedArchiver, NSCoding, and NSData for more information on sending and receiving NSObjects in this way. Here is Apple's reference document on Archving.

As for receiving, YOU don't call the method, it gets called ON you by the Kit. It's what's called a "delegate method" (in Cocoa parlance) or a "callback method." You can think of it like a "phone call" that your app can receive asynchronously. By implementing a method with the signature:

- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID;

...you are saying "I can receive this kind of phone call." So when the GameKit receives data on your behalf from another player, it will see that you want to receive callbacks of that kind and will then call that method -- then it's up to you to update your internal application state based on what is received.

To continue with this example, if you had sent the simple "nothing but an integer" message described above, you might implement that method like this:

- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID
{
    int* receivedScorePtr = (int*)[data bytes];
    int receivedScore = *receivedScorePtr;
    [self updateScore: received forPlayer: playerID];
}

That is, of course, assuming that you have a method called updateScore:forPlayer: that would update a table of scores.

You can find a more general discussion/explanation of how delegates and delegate methods work at this blog entry: http://mohrt.blogspot.com/2010/01/cocoa-and-delegates.html

ADDED: Using code the asker posted, I made a few modifications and produced a version that "works" for this bare bones use case. Working Version of Test App To Send One Integer Through GameCenter I make no claims about the quality of the code, or its suitability for, well, anything at all. I didn't write 99.9% of it - please don't take my posting of it here as an endorsement of anything that appears in it.

One lesson learned (that I didn't know, so I'm putting here in hopes that it helps others) is that you can't use the Matchmaking service with the simulator. This means that you need two development-provisioned iOS devices in order to test this scenario, and likely, for non-trivial programs, two development machines to debug both devices simultaneously. This problem cost me the most time while figuring this out.

Grof answered 1/1, 2011 at 17:9 Comment(11)
This is great, thanks a lot! Makes much more sense, although as for the match is this just a new GKMatch I can declare or does it need to be from a specific place?Hypercritical
In the case of this method, the match gets passed in to you. I suspect that in the simple case (i.e. only one game going on at a time) that you don't need to worry about it/do anything with it in these methods. If you're looking for more info on matches in general, they come from the Matchmaking service. Details here: developer.apple.com/library/ios/documentation/…Grof
I understand that the receive data method gives us the GKMatch match but where does match come from in the sendScore method?Hypercritical
I have gone into a fresh project and setup game center just to test this and still can't get it to work. I really need help. Could someone please setup a clean project showing the ability to match make send a single integer from one device to another. I think I and other would be forever grateful!Hypercritical
I will give a try this evening after my "real" job. :)Grof
Thanks! I have made an edit of the original question. I have uploaded the little tester I made.Hypercritical
I've edited my answer to include a version of your project that worked for me. Main issues with the code were: 1) When getting the match you also need to set myMatch.delegate = self; 2) You needed to add an inviteHandler to the sharedMatchmaker -- See [bit.ly/fBsfOS]. I added it in viewDidLoad (prolly the wrong place, but...) 3) Make a habit of initting pointers to nil like: NSError *error = nil; A garbage value was causing sendScore to think there was an error when there wasn't. After those changes, it worked for me. Let me know if you have trouble.Grof
do you know if Game Center caps the size of the NSData packet? Could you send a large bitmap through?Irreplaceable
Since you're using GKMatchSendDataUnreliable (good choice!) you may have to re-send the packet. Drops happen about 2% of the time.Regressive
Is that really an example from GameKit? If so, the handling of the error parameter is completely wrong.Ophthalmic
am also trying to send poistion of player via gamecenter. but not able to send the data from gamecenter through cocos2d-xFalco

© 2022 - 2024 — McMap. All rights reserved.