Most games, including all the shooters I know of, use repeated unreliable sends for player updates.
Repeating unreliable sends:
1. Send position, orientation, health
2. [50 ms later]
3. Go to 1
The advantage of this technique is it is tolerant of packetloss and ping spikes. If a packet is lost, it doesn’t matter, because another is coming in 50 ms later. So you never see the player falling to move, then all of the sudden warping somewhere.
Sequenced Repeating unreliable sends:
1. Send position, orientation, health, timestamp
2. [50 ms later]
3. Go to 1
On the recipient:
if (timestamp in message > last timestamp we got) then process the message and set the last timestamp to the timestamp in the message
else ignore the message.
This way you don’t get out of order or old messages messing up the stream.
So this technique is good for removing the effect of latency spikes and packet loss, but is also inefficient. If the player isn’t moving you still send these messages. If the player is just running in a straight line, you still send these messages, although it should be possible for the recipient to get a “run forward” message and just keep doing that unless it hears otherwise. You can fix this with event based sends
Reliable event based sends:
If [player presses a key] then send this key, and the current player status.
That’s very efficient, but the problem is if the message is lost then the player may have run away a long time ago, yet you still see him standing there.
You can send this message reliably but then you are subject to ping spikes. By the time the system can assume that message was lost and retransmit, it may be half a second later, and you see a pop. This won’t happen very often (maybe 2% of the time) but with 1 command a second and 30 players on the screen, you would see 1 pop every second. It would look awful.
For an MMOG, I need to use the absolute minimum bandwidth, but also not have pops.
Event based networking with staggered unreliable datagrams
1. (Constructor) Set sendStaggerTime = [large value]; Set nextSendTime=0;
2. (On user action update)
If (nextSendTime+sendStaggerTime < currentTime) then
Set nextSendTime = currentTime;
3. (Update loop) If (sendStaggerTime <= 200 && nextSendTime <= currentTime) then
What this means is that:
1. Updates will never be sent faster than every 50 ms.
2. Actions will be sent 0 ms later, 100 ms later, 300 ms later. It will then stop sending.
I send using RakNet’s unreliable sequenced capability, which means that I can be assured that I don’t get messages out of order.
The advantages of this technique:
1. If a ping spike affects one message so that it arrives late, in all likelyhood the next message will arrive within 100 or 200 ms, thus minimizing the effect of the spike.
2. For a message to completely not arrive, all 3 messages over a total period of 300 or 350 ms would have to be lost. This is unlikely enough that that if it does happen it won’t ruin the game.
3. Simply performing one action, and continuing to perform that action (or no actions at all) result in 3 sends, and then no further sends.
4. Messages are sent unreliably, but arrive reliability without incurring the memory overhead of tracking messages for resending.
5. The client doesn’t send needless duplicate updates while the server relays updates immediately.
6. If I get 3 updates for every action, I can use the update with the lowest timestamp value, thus resulting in the most correctness for interpolation.
7. Errors are minimized. If on the server I’m stuck on a corner, but I barely went around it on the client, enough time elapsed by the 3rd message that I can fix this and know the client actually went around.
The most common case is that a message will arrive on-time and without loss (97% of the time or so). Yet I’m sending each message 3X, which means I waste two sends most of the time.
The reason for the numbers I picked:
50 ms – players won’t notice granularity less than 50 ms. Also, hitting 2 keys at once will probably occur within 50 ms, so only one send occurs, rather than 2.
100 ms – If the first message is lost to due ping spikes or packetloss, the second will arrive soon enough that it won’t harm the game.
200 ms – If the first two messages are lost, this is likely due to a prolonged period of spike or packetloss. 200 ms is probably long enough to get past that temporary spike, with modern internet routers.