Using GameKit for Leader to many Follower type connections

I need to exchange small amounts of data in my app near realtime between users who could be anywhere with only an internet connection. One user is a Leader and needs to send data (just a few bytes) to all Followers - up to 30 - about each second. Then each Follower needs to send data (again, just a few bytes) back to the Leader about each second. The requirement is similar to the example app from last year's WWDC networking part 2 video called TicTacToe, except the users may not be local.


I've tried using Game Center but can't get it right. I've been able to achieve most of my objectives with this GameKit implementation:


Follower starts:

[[GKLocalPlayer localPlayer] registerListener:self];
request = [[GKMatchRequest alloc] init];
              request.minPlayers = 2;
              request.maxPlayers = 2;
       
request.playerAttributes = 0x0000000F;
request.playerGroup = groupName;
   
[[GKMatchmaker sharedMatchmaker] findMatchForRequest:request...

Leader starts:

[[GKLocalPlayer localPlayer] registerListener:self];
request = [[GKMatchRequest alloc] init];
request.minPlayers = 2;
request.maxPlayers = 2;
request.playerAttributes = 0xFFFFFFF0;
request.playerGroup = groupName;
      
[[GKMatchmaker sharedMatchmaker] findMatchForRequest:request...

You can update UI with the completion handlers, but don't count on a real match until:

- (void)match:(GKMatch *)match
       player:(GKPlayer *)player
didChangeConnectionState:(GKPlayerConnectionState)state
{
        use the match / player combo to note the connection for both  leader and follower. Add to array of followers
        for leader. If Leader, call your Leader starts code again
}

Send data to either with:

[match sendData:encoder.encodedData toPlayers:playerArray dataMode:GKMatchSendData(Un)reliableerror:&error];


and receive in:


- (void)match:(GKMatch *)match
didReceiveData:(NSData *)data
forRecipient:(GKPlayer *)recipient
fromRemotePlayer:(GKPlayer *)player
{ // you've got mail from player!
}


So far, so good. Here's the bad news: depending on network strength, connections disconnect regularly. Even though I've built in dependable reconnect code, it can take up to 15 seconds to reconnect so it really isn't a good "real-time" experience.


I'm currently testing with various combinations of data size (sometimes pad it so it's heft may keep the connection) or frequency (sometimes just send a "poke me" message) or tweaking the reliable vs unreliable flag in the send message calls.


I can get about 10 followers per leader on a good day, but other times, more than 3 followers cycle between connect, disconnect, reconnect in an unusable fashion.


I'm still trying to make usable, but wanted to document my success/failures so far in hopes that I can help many and perhaps get help from a few.


Any ideas would be welcomed and any tweaks will be tested and reported.

Replies

(interestingly - I thought I posted this last night...here it goes again...)


Another approach is to use a daisy chain.


User's A, B, C and D create two Matches. One amongst each other and one with 3 other users:


Matches:

ABCD

AEFG

BHIJ

CKLM

DNOP


Whenever users A, B, C or D receive data in one of their matches they immediately transmit teh data to their other match. Whenever A, B, C or D wish to transmit data, they send it out to both matches.


In your case, to get to 30, you would need users EFG and HI to do the same thing.

Great idea that I'll explore if I can't get this current method off the ground. I found a post from a few years ago that implies a 1 second 'poke' message with the GKMatchSendDataReliable will keep sessions active and will test that in the coming days. Will update this post no matter.

I've added a small 'poke' message from each follower to the leader every 0.9 seconds using the GKMatchSendDataReliable message type. The connections may be a bit more reliable, but with more than 7 or so followers, there is still a constant disconnect, reconnect dance that seems to happen.


Has anyone successfully kept more than 6 active GKMatch connection pairs on a device for more than a few minutes?


If not, are there any data transmission services that anyone would recommend? I wanted to stay in the safety of Apple's services but am willing to look beyond if necessary.


PBK, although your daisy chain idea is clever, I fear the reliability might be even worse, as one lost connection would mean multiple lost connections.

This:

https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/GameKit_Guide/MatchmakingwithGameCenter/MatchmakingwithGameCenter.html#//apple_ref/doc/uid/TP40008304-CH12-SW7


says that a peer-to-peer GKMatch is limited to 4 players so you are trying to do something that is not supported.


Regarding multiple GKMatches - a GKMatch never breaks if you limit it to 4 players. Therefore I suspect a daisy chain will work fine.

PBK, thanks for the reminder of 4 max GKPlayers in a GKMatch, but I'm using a seperate GKMatch for each follower, so a "lawyer" defense is that I never have more than 2 players in each GKMatch... but I'm trying to let one player (the leader) be in many matches at a time. I couldn't find any documentation on that scenario. My tests seem to indicate a cutoff around 7 but at times have had 10 active GKMatches.


Your input is very appreciated and leads me to consider having multiple GKMatches of 4. _IF_ the leader could be in 6 concurrent GKMatches and each GKMatch could be with 3 followers, then I'm making progress.

If anyone can offer success/failure experience with multiple GKMatches I'd appreciate hearing it.

And again, PBK, I'm very grateful for your quick and helpful replies!

So it sounds like you are already one step ahead of me - you already have the system in which data is transfered from one GKMatch to another. So now go from 2 players to 4 players per GKMatch. Once you have done that, increase the number of GKMatches until the system starts to be unstable. At that point there are two paths - 1) eliminate that instability with tweaks to prevent losing a GKMatch or 2) push the structure down a level by having a 'follower' in one 4 player GKMatch become their own 'leader' and create a second layer of multiple GKMatches. I'd definitely vote for #2.

Thanks for the updated information! Having 16 in one peer to peer GKMatch will certainly be better than the 8 - 10 individual GKMatch(es) with 2 in each that I've been able to get working so far. 

Could you offer an example or pseudocode for best practice to programmatically create such? I imagine it goes like this:

Follower starts:
Code Block
[[GKLocalPlayer localPlayer] registerListener:self];
request = [[GKMatchRequest alloc] init];
request.minPlayers = 2;
request.maxPlayers = 16;
request.playerAttributes = 0x0000000F;
request.playerGroup = groupName;
[[GKMatchmaker sharedMatchmaker] findMatchForRequest:request...

Leader starts:


Code Block
[[GKLocalPlayer localPlayer] registerListener:self];
request = [[GKMatchRequest alloc] init];
request.minPlayers = 2;
request.maxPlayers = 16;
request.playerAttributes = 0xFFFFFFF0;
request.playerGroup = groupName;
[[GKMatchmaker sharedMatchmaker] findMatchForRequest:request request... //(should this completion handler finish matching and call addPlayersToMatch or will that happen automatically?)

Update UI with the completion handlers, but don't count on a real match until:

Code Block
- (void)match:(GKMatch *)match
player:(GKPlayer *)player
didChangeConnectionState:(GKPlayerConnectionState)state
{
//add player to array of followers
//(should I finish match and call addPlayersToMatch here or is it not necessary?)
}

Send data to either with:

Code Block
[match sendData:encoder.encodedData toPlayers:playerArray dataMode:GKMatchSendData(Un)reliableerror:&error];


and receive in:

Code Block
- (void)match:(GKMatch *)match
didReceiveData:(NSData *)data
forRecipient:(GKPlayer *)recipient
fromRemotePlayer:(GKPlayer *)player
{ // you've got mail from player!
}


I've tried a few ways of ordering the connections but haven't figured it out yet. I'm not sure if I need to call finishMatchmaking in the leader code after each new peer then addPlayersToMatch, and if so should it be in the findMatchForRequest completion handler or in didConnect? Any additional insight or example would be appreciated.

For anyone watching this thread, I'm interested in trying the nostr protocol for a better solution. No matter what I tried, I wasn't able to get the GameKit implementation to be consistent for more than 8 peers. At times, up to 32 would be great, but other times more than 8 would cause dropped connections or wildly delayed packets. I couldn't identify any correlated items like peer broadband quality or location (or how much caffeine they'd had that morning   ) Success seemed totally random.  Which has led to my curiosity to try the nostr protocol but I don't see any iOS examples using nostr for peer to peer messaging.  Can anyone offer a link or advice?