The following article is an adaptation of the XNA GameFest 2008 session on "Networking, Traffic Jams & Shcrodinger's Cat" by Shawn Hargreaves and Mitch Walker. It will introduce you to some of the headaches suffered by networking in XNA Community Games and propose potential solutions.
This article assumes you have an interest in the XNA Framework
and a basic grasp of general networking principles
. This session is complimented by the session GDC 2008: Netwoking with the XNA Framework
The Inconvenient Truth
It is incredibly difficult to syncronise games over a computer network. It will never be exactly precise. Only if you assume that there is no time, then and only then can networked programs be deterministic.
The speed of light is not fast enough to be irrelevent. The speed of light is only a mere 186,282 miles per second, which is actually pretty slow. In order to travel 1000 miles, approximately 5 milliseconds are required. In order to traverse the Atlantic Ocean, approximately 4,800 miles, a mighty 26 milliseconds is required. Each router along the will, on average, add approximately 5 milliseconds to the total. To complicate things further, we don't even get the speed of light. Signals within fiber or copper will slow to approximately 60% of the speed of light! We can't really do much about this latency either.
Even bandwidth is far from infinite. Event on the advent of broadband network connections and high-speed internet, the reccomended limit is still 8kb per second!
The Five Stages of Networking
As a network programmer, you will quickly become familiar the five stages of network development. These include:
Let us examine each phase in more detail:
With the heavy networking constraints place on todays computer games, the first approach the network engineer will attempt is The Ostrich Appoach. Hence the game will be:
- Small and Simple;
- Each frame send only:
- Position (Vector3);
- Velocity (Vector3);
- IsFiring (Boolean);
- 8 Players;
- 30 FPS.
Okay. This sounds reasonable. Let's calculate how much bandwidth we are using:
7 * ((30 * (12 + 12 + 1 + 51)) + 500) = ~19kHuh? What the heck? That can't be right! Where did all those numbers come from? Well, let's have a look:
- Number of players to send data to: 7 (Don't send anything to yourself!)
- Send an update every frame: 30
- Position: 12
- Velocity: 12
- Boolean: 1
- Packet Header: 51
- Voice Data: 500
What is this packet header I have to pay for? Well, data is sent as a UDP packet with the following structure:
- IP Headers: 20 bytes
- UDP Header: 8 bytes
- Microsoft LIVE Encryption: 16 bytes
- XNA Framework Overhead: 7 bytes
As a result, every packet sent from the computer will have a 51 byte overhead.
Wow! What is with voice? I havn't implemented any voice in my game! Well, we always have voice data because it is automatically implemented and run. By default, all players can talk and listen to everyone. This consumes ~500 bytes per player per second.
Essentially, the game itself is not the majority of the data. In fact, using the osterich approach isn't feasible. Sending empty packets containing absolutely no game data results in ~13kb of data being sent.
At this point, developers get angry. WTF? Why are the bandwidth constraints so low? Why should I stick to 8kb? Why not 56kb? Thats dial up and everyone is faster than dial up!
Internet Service Providers provide way more and other applications report way more! Well...ISP's tend to only report their best-case when marketing internet speeds. Furthermore, the Internet is highly variable in speed and a glitch for a couple of seconds is unlikely to hurt a web page but is unforgiving in games.
Okay, okay! Where's the bottleneck? Explain why I have to limit myself to 8kb!
Well, according to the data collected by Bungie Studios during the Halo 3 beta in 2007, only 1% of consoles on Xbox LIVE have less than an 8kb downstream and 8kb upstream. Meanwhile, 50% have less than 42kb downstream and 44kb upstream! So half of the consumer base of Xbox LIVE has a bandwidth less than dial-up! Explain that to your publisher!
Hold on...why is the downstream and upstream very similar? Surely the downstream should be much higher than the upstream! Well, alot of XBLA games are peer-to-peer and therefore use pretty similar upstream and downstream values.
So, 8kb stays unless you plan to sacriface more than 1% of your market base.
Bandwidth is not the only problem that plagues networkers! Although the UDP protocol can guarentee that the contents of a packet will be the same on delivery, it cannot actually guarentee that the packet will be delivered, or that it will be delivered in the correct order!
The XNA packets can be explicitly manipulated to help, however:
You can set both options if you choose, however it is a trade-off with bandwidth and latency.
SendDataOptions.None // Packets may jumble / never arrive
SendDataOptions.InOrder // Cheap
SendDataOptions.Reliable // Expensive
In order to accept the constraints, you must know your limitations! These are:
- Bandwidth: 8kb - 46kb
- Latency (One-Way): 25 - 250 milliseconds
- Packet Loss: 2% (Typical) - 10% (Worst Average)
In order to test your networking, you can set and access several debugging properties in NetworkSession:
Just remember to remove latency and packet loss when you publish your XNA project!
To conclude, there are serveral approaches to improving network performance under such tight restrictions:
- Send Fewer Packets
- Send Smaller Packets
- Talk Less
- Know What Matters
- Distribute Your Workload with Hybrid Topologies
- Live With Latency - Make Predictions
Below I will briefly explain each potential improvement:
Send Fewer Packets
Despite the earlier emphasis on the 8kb reccomendation, in several situations it is neccessary to trade bandwidth for latency. Hence, a small number of large packets is preferable to many smaller ones; usually 10 - 20 packets a second. This also provides the advantage of reducing packet overheads.
XNA also provides the facility to automatically merge packets at runtime. When multiple calls are made to send a packet prior to updating the NetworkSession, the packets will usually be merged.
On a similar note, only call NetworkSession.Update when you plan on sending packets.
Send Smaller Packets
Often, generalised compression algorithms are not much use when attempting to make packets smaller. However there are some simple casts you can perform to help improve performance. These include:
- Int -> Byte
- Use Bitfields
- Vector3 -> PackedVector
- Matrix -> Quaternion + PackedVector
Futhermore, avoid sending location vectors for things the clients should already know. For example, spawn points should be sent to the client before the game starts. Consequently, there is no need to send a position vector, instead an index for a lookup table can be used.
Strings are the enemy! If you must send strings, use the same lookup table approach explained above. Also, force strings to use ASCII where appropriate. The default is UNICODE which is double the size!
Know What Matters
There are things that are important and things that are not important. You don't need to syncronise everything since each player being in their own parallel world isn't going to break the game!
Important: Am I dead? Who picked up the item? Who won?
Less Important: Where am I? What direction am I moving?
Not Important At All: Which way did the particle bounce? Which frame of animation is being run?
It makes sense to talk less if you need to save on bandwidth! Only talk to people near you, or people on your team! However, beware of hidden costs associated with changing the people you are talking to too often!
Distribute Your Workload with Hybrid Topologies
Most business applications that operate over the Internet are proberbly the victims of defensive computing! Luckily for us, the LIVE network and built-in encryption techniques are strong enough to merit its omission from a simple game. Peer-to-peer client-server hybrid topologies are also a viable option.
In hybrid topologies, the host will make all the important decisions about the game. Peers can control object movement and behavior. Additionally, specialised authorities can control things like powerups and score (ie. a single non-host is responsible for the score, another machine tracks powerups, etc.). This can help save on round trips.
Live With Latency - Make Predictions
Embrace quantum uncertainty! Most players can't tell the difference between real players and error correcting on predictions!
Also bear in mind that you have to consider relative parallel universes. A player may not be in a deterministic position, so it is better to send data such as "Shot at Player0, missed 10 degrees to left" then "fired at (x, y, z)". This makes the system robust even if player positions differ!
Questions & Comments?