I am developing a platform game trying to make it as well for multiplayer using the IOS Multipeer connectivity. I am stuck/confused/do not know what is the best way to send messages between peers (mostly like 4 peers in the game). The game style is like fun run and I have 4 hero running and firing each other and stuff like that. The thing is that keeping the positions of the other players is pushing me crazy!!
Sometimes other players are positioned in the wrong position (keeping in mind all devices are with the same screen size). Is that because of the lag of connectivity. How much information I can send per seconds and what is the best way to manage data transferring?
Any idea/thoughts are appreciated.
The throughput of MPC depends mostly upon the connection. What I've seen as the biggest performance problem is when one of the peers has WiFi disabled. This forces MPC to communicate over bluetooth which is incredibly slow. I've seen it run 20x slower than MPC over Wifi.
You don't need to be connected to a WiFi network. Just enable WiFi on all of the devices and iOS will leverage shared WiFi networks or create its own adhoc network.
The second thing you want to ensure is that your peers don't invite each other. You can only have one inviter in your network. When multiple peers invite and accept connections the MPC network become unstable.
Third thing is sendData:(NSData *)data toPeers:(NSArray *)peerIDs withMode:(MCSessionSendDataMode)mode error:(NSError **)error. If you are broadcasting your packets to all peers in the toPeers: array AND mode is MCSessionSendDataReliable then MPC waits until all of the connected peers ACK the message before moving on to the next packet.
UPDATE: I did some testing with my own app and over WiFi and two devices I can put about 100kbps. I'm using an iPhone 6 Plus and an iPhone 5S.
UPDATE 2: Thinking more about your question, there are a couple things to keep in mind with MPC communications:
Do all of your sendData and didReceiveData calls on a background thread and have that thread update your position data in your model. Tell your viewController that updates are available with a delegate method or notification.
Keep your data packets small, but design them so an update received represents the current state of your player. This allows you to miss an update and not be completely out of sync -- "send player 1 moved to (10, 10)" instead of "player 1 moved by (1, -1)"
Number your packets and use MCSessionSendDataUnreliable. If you get a packet with an earlier number than the last one you processed, throw it away. If you follow the second guideline above, you won't need this packet.
If you follow Dan Loughney's suggestions, I think your best bet in debugging is to log the received coordinates from the other players. That should help you identify whether the problem is due to timing issues, misinterpreted data or something else.
Related
We've created an iOS app that implements a CBCentralManager to connect to a device that we've created, that transmits data at 10Hz. It's vitally important that this data comes through and displays quickly, so we have built tight latency checks around this, if too many points are missed or if the local clock detects that incoming values are slowed, we will fault and break the connection.
The client has asked us top implement a second iOS app that will observe the first one. We implemented a CBPeripheralManager in the original app which advertises, can be connected to, and will periodically publish its data to a few outgoing characteristics.
What we are finding is that we cannot seem to connect the observer iOS app to the original iOS app (i.e., the original iOS app has both a CBCentral connection to the device and CBPeripheral connection to the observer app active at the same time), without tripping up our latency checks on the incoming data from the device.
I've tried everything I can think of, I've used separate queues for both CBPeripheralManager and CBCentralManager, as follows:
q = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
ptr_CBPeriphMgr = [[CBPeripheralManager alloc] initWithDelegate:self queue:q];
Also,
I logged and timestamped everything, verified none of my code is taking too long
I moved almost all of my code out of BLE handlers to make them very light and not blocking,
I tried the separate queues (example shown above), with low priorities
I have tried slowing my CBPeripheralManager data rates to a trickle, a few updates a second
I have tried suspending the latency checks for three seconds after a CBPeripheralManager connection is established (which is very not ideal), but the problem seems to kick in randomly, not just after a connection.
It seems like no matter what I try, after 4-5 minutes of both peripheral and central connections being active (we have a loop where the second App repeatedly connects and disconnects every five seconds, to challenge the device connection) my incoming value updates from the device to the central slows to about 1/4 or 1/5 speed, or they stop for a full second and then three or four updates come in nearly simultaneously -- both of which trip our latency checks. It's like some queue is getting filled up and performance flatlines, but as I mentioned above I think I'm using separate queues.
I'm at my wits end... does anybody have any thoughts about how to prioritize my central functions over my peripheral functions in the iOS app, or to somehow improve performance to prevent this from being an issue and keep my app responsive to 10Hz updates from the device, even when being observed as a peripheral?
(Edited to state that we are connecting/disconnecting the second App repeatedly... perhaps I'm not cleaning up after the disconnection properly, and the garbage piles up and screws up BLE? That would explain why the problem seems to occur after 4-5 minutes regardless of the frequency of data updates over the second connection.)
Here are some suggestions:
Try creating queues using QOS_CLASS_USER_INITIATED or higher instead of QOS_CLASS_UTILITY.
Make sure that you call -[CBCentralManager stopScan] whenever you do not need to be scanning for peripherals, and -[CBPeripheralManager stopAdvertising] whenever you do not need to be prepared for incoming connections (probably whenever you are connected).
Call -[CBPeripheralManager setDesiredConnectionLatency:forCentral:] to reserve more resources.
However, if you can target iOS 11+, what I would recommend to decrease latency and increase speed is using a L2CAP channel. Although CoreBluetooth's L2CAP support is not documented well, here is a list of the available APIs.
The requirement is that the synchronization should happen between 2-4 iPad's over a Wi-Fi network (router), that could go online and offline at anytime (i.e. something like https://www.youtube.com/watch?v=LNRDPDjJyfU)
I'm currently trying out this library: https://github.com/robbiehanson/CocoaAsyncSocket
But it doesn't seem to be very reliable when the distance between 2 iPads are stretched to room at a size of 150 m2. We noticed that multiple times, the packets aren't received reliably, or if received, we're getting a duplicates.
Thoughts?
Original question:
The iOS Developer Library mentions in the Game Kit Programming Guide that
The maximum size of a client-server game is 16 players.
Does this limitation also apply to P2P GKsessions?
This was already kind-of asked here (but from a different angle).
Updated question:Is GKSession reliable when more than 4 peers are connected?
note:The question was updated based on the response by saulobrito below.
Reference: One example of an app that uses a large group of peers connected is Seedio. You will notie that they are not using GKSession as bluetooth is not an option for connectivity (with GKSession, you can't even chose bluetooth or wi-fi.. GKSession does that for you.) Check out their FAQ for some interesting information about why they chose Wi-fi rather than bluetooth.
What I can assure you is that GKSession is very unstable and you should not trust these docs. In fact, Apple recently decided to remove the documentation altogether.
I did a lot of testing and I would suggest that the practical limit is 4 connected devices (one acting as a server and 3 clients). Of course it's better that you do your own testing scenario.
I also couldn't find any game that allows more than 4 players. The only that I knew was Apple own Texas Hold'em - which allowed 8 players, but they decided to remove it.
And last, but not least, Game Center imposes a 4 player limit to peer-to-peer games.
Yes I'm developing a game that should support 10 players but, in our tests, it became unstable/unusable when there were more than 4 devices. By unstable I mean: sometimes you can't find a peer and connection drops in less than a minute. To make matters worse, updating to iOS 6 brought some strange behaviors like freezing (no error, no stack trace, no nothing) while trying to send a message. Other odd thing: when a player loses connection, all other players get disconnected.
EDIT: did a lot of testing since that response and have more information to share:
Using iOS 6 I was able to play reliably with 9 devices using either wi-fi or bluetooth. There's stil a huge issue although: You can't connect devices using iOS 6 with devices using iOS 5 cause you will face unexplainable freezing-with-no-stack-trace time to time if any of the devices is with its wi-fi enabled. You either set iOS 6 as the minimum supported version for your App or you will have to ask users to disable their wi-fi and use bluetooth.
On iOS 6.0 I can confirm that GKSession allows at least 6 players. I have found that you must make sure not to send messages to a peer until the state changes to connected though.
Overall, I think GKSession is a wonderful and simple API. It will use bluetooth or WiFi, meaning that you don't even have to think about whether the phone is connected to a WiFi network. The latency is nice and low ( I measured it at 4ms for unreliable and around 10ms for reliable networking ).
My main caveats are:
Use unreliable messaging if you can.
Make sure you set .available to NO on application exit ( if you're terminated while advertising, you may leave phantom sessions open ).
Do not try and send messages to a peer until the peer in question changes to the connected state.
Don't try to re-use a gksession once you've been disconnected.
You may need to retry a connection attempt ( I automatically retry 3 times if a connection attempt fails ). My timeout is 10 seconds.
Throttle your reliable messaging. I haven't quite figured out what the limit is, but eventually it seems like you will blow a transmission buffer and then your latency will balloon. Again, this just isn't a problem when you use unreliable messaging.
I've encountered a pretty large issue and have been trying to find a solution for 2 months now with no luck. I've submitted it as a bug, ( https://bugzilla.xamarin.com/show_bug.cgi?id=4910 ) but was hoping maybe someone here could shed some light on the cause of the problem, or suggest a work-around.
In a nutshell, to encounter the error:
Create a basic .Net socket connection between two devices
Create and initialize a GameKit.GKSession object on a least one device.
What occurs is the transfer of the data on the .NET socket becomes erratic and too slow to be usable. I've performed many tests across different devices (see link below) and it affects all of them (iPad 3 affected the least). I've tested it between an iPhone and a Windows PC and it still occurs. MonoTouch's GameKit code is somehow affecting the Socket code.
As you can see from the spreadsheet, speed drop from a few milliseconds to send 1 MB to several minutes to forever.
As soon as the GameKit.GKSession is set to null, any backedlogged data on the socket flows freely again and the sockets act normally once more.
Sample Windows and iOS/MonoTouch Apps demonstrating problem: https://dl.dropbox.com/u/8617393/SocketBug/SocketBug.zip
Test results across different devices (PDF Spreadsheet):
https://dl.dropbox.com/u/8617393/SocketBug/SocketBugTestResults.pdf
This issue seems so hard even Apple decided to circumvent it: http://developer.apple.com/library/ios/#qa/qa1753/_index.html#//apple_ref/doc/uid/DTS40011315
The revealing sentence is this: "This change was made to reduce interference with Wi-Fi."
To assist with anyone that comes across this problem, the issue only manifests itself when the GKSession.Available is set to true. This is required for the device to be discoverable, but not to maintain a connection. With this in mind, a temporary work-around can be used where the GKSession.Available is only set to true when required, and set to false as soon as the connection has been made.
In my scenario, I have a socket connection to the server on both devices. I use this connection to send a message from device A to device B, asking it to become discoverable via bluetooth for the next 10 seconds. As soon as the message is sent, device A begins looking for device B and connects as soon as it finds it. Once the connection is established (or 10 seconds elapses without a connection), device B turns off discoverability and the socket behaves as normal.
In my circumstances, this is an acceptable (pending no real fix) workaround to the problem.
I have the following problem with GKSessions:
Sometimes there is a huge delay when sending data (3-4 seconds sometimes up to 10 seconds) in a client/server application.
If the server sends let's say 10 packets during the delay, those 10 packets are received all at once on the client. The weird thing is that the server still receives packets from the clients during the delay.
This delay seems to be worse in WiFi networks but also happens in bluetooth networks.
Did anyone else encounter such delay spikes when using gamekit?
What could be the source of this issue?
I've been working a lot with gamekit and gksession. I always use the Peer2Peer mode even though some people discourage it. I never experience any kind of delay like the one you describe.
Do you send with GKSendDataReliable or GKSendDataUnreliable? Try switching to the other and see if it changes anything. If you're using GKSendDataReliable then the sending device will wait for a "Received" message from the receiver before sending the next message. This might be the problem.
On your testing devices: Is anything running which might flood the network?
I was testing a multiplayer gamekit based game, and if I had several active devices then suddenly some would stall completely, and I needed to hard reboot (shut down completely and restart) them to make them work again.
If you keep getting in trouble you might want to try a much more low level api: dns-sd https://developer.apple.com/library/mac/#documentation/Networking/Conceptual/dns_discovery_api/Introduction.html
I hope you make it work, good luck!