Networks are not reliable. When you send a data packet, many things can happen:
- It might arrive at the other end.
- It might never arrive.
- Packets might arrive, but in a different order to how you sent them.
- Packets might arrive, but their contents could be corrupted. Someone may have altered them along the way, trying to cheat or hack your machine.
That fourth problem is not an issue for XNA Framework games. Thanks to the underlying LIVE functionality, which automatically encrypts all network traffic, it is impossible to ever receive corrupted or tampered data.
The XNA Framework also provides optional solutions for problems 2 and 3, via the SendDataOptions.Reliable and SendDataOptions.InOrder flags. Why are these optional? Why wouldn’t you always choose SendDataOptions.ReliableInOrder? While we’re here, why don’t games just use reliable TCP networking like everyone else?
The reason is that games are constantly updating a realtime simulation. Think about what happens behind the scenes when you send packets over a TCP channel, or using SendDataOptions.ReliableInOrder:
- The sender transmits packets over the network.
- Sender also stores a copy of the packet data in an internal queue.
- When packets arrive, the recipient sends an acknowledgement back to the sender.
- When sender receives this acknowledgement, they remove the packet from their pending queue.
- If the sender does not receive an acknowledgement after a certain period of time, they resend the packet.
- If packets are received out of order (for instance B and C arrive before A) the recipient must store B and C in a queue.
- To preserve packet ordering, they must wait until A arrives before B and C can be passed on to the game.
This works well for things like downloading a webpage. After all, there isn’t much point delivering packet C, which contains the page footer, before you have packet A, which contains the HTML header and stylesheet reference: that just wouldn’t make any sense.
But consider a game which is sending updated player positions at regular intervals:
- Send player position at time A.
- Send player position at time B.
- Send player position at time C.
- Positions A and B get dropped.
- Position C is delivered successfully.
- The recipient does not yet get to see position C, because the network stack is waiting for A and B to be resent first.
How silly is that! If the game knows where the player was at time C, it is utterly irrelevant where they used to be at times A and B. It would be better to just skip over that old data and jump straight to the latest position.
When you combine SendDataOptions.ReliableInOrder with packet loss, you get increased latency. Without that flag, if a single packet gets dropped, there will be a minor glitch, after which the game can continue as normal. But with that flag, even a single dropped packet causes all subsequent packets to be delayed until the first one can be resent.
Note that when used separately, neither SendDataOptions.Reliable nor SendDataOptions.InOrder has this problem. It is only when you combine them both (asking for a TCP style behavior) that latency can be affected.
If you ask for reliable but unordered delivery, and packet A is dropped, then B delivered, the game gets to see B straight away. Packet A must be resent, but this need not delay the delivery of subsequent packets.
If you ask for ordered but unreliable delivery, a version number is included with every packet. If older packets arrive after a more recent one has already been received, the outdated ones are simply discarded. This is extremely cheap.
- Use SendDataOptions.InOrder for most game data.
- Use SendDataOptions.Reliable where necessary.
- Use SendDataOptions.ReliableInOrder as little as possible.