Game Creation with XNA/Networking/Network and Peer-to-peer
Network and Peer-to-Peer
well, I will do that. - project79
One very important point is: decide for or against networking support of your game right from the beginning. Due to some horrible stuff like bandwith, latency, packet loss and also all these together, you can't send just your input over the wire. There will be failures you have to hide before the player. Why, which failures and how will be mentioned later. First there is some theory on games and networks.
Most people think on illegal stuff hearing peer-to-peer - or short p2p. But it isn't illegal. It's only a way of organizing a network.
Here every machine (called peer) knows every other or at least every other peer it wants to communicate with. Thus one peer can directly communicate with another which is faster than the client/server approach. Also you don't have one machine with the need of a lot of bandwith. But temper the wind to the shorn lamb. The peer with the poorest bandwith determines the speed for all other peers (for the connections with him). (Everybody is connected to everyone. Thus the download of B is the same as the upload of A)
If every peer is equal, nobody has more than the others, also the logic. So everybody is calculating his part of the world and therefore cheating is easy - nobody (can) controls you. Another disadvantage is the way of finding other peers. The player can't type in 10 IP-Addresses and you can't scan the whole net.
further reading at Wikipedia
This is the typical network architecture in the net. Here every machine (client) is connected to one machine (the server) in the middle. Normally the flow of information is that the clients are sending there input to the server, which computes the results and send them back. So the logic is on one central point and can't be manipulated by a player. Therefore cheating is hard. There is also the advantage of creating a gamesession, because the clients only need the address of one machine.
But there are also cons. Due to everybody is communicating via the server, the bandwithload is much higher on this machine. Also this machine should be more powerful. Another point is latency. Every message has to be passed by the server, which leads to a longer distance and more time for delivery.
further reading at Wikipedia
Like hybrids always are, there are somehow both. Here client/server and p2p. Following two interesting kinds are mentioned.
The first deals with the problem of finding other peers. Somehow you need a central point like the server. So why not using one? One player starts a multiplayer session and in background a little server. The others only joins a session. Maybe they type in the IP of the first player or in a LAN the game looks for the server. Having found the server and set a flag for being ready, the IPs of all clients and some initializing stuff (the map, rules for this session) are shared. Now ervery client could address every other client. Thus the server is stopped, the network becomes p2p and the game starts.
The second deals with the problem of cheating. Due to every peer having the whole logic (and using it) there is no authority machine which could say that the conclusion of A is wrong. But having one big authority machine controlling the logic, would be client/server, which maybe is impossible for bandwith reasons. So split up the logic (for important parts) in small independent parts and distribute them on the peers. Thus A controls the teamflag, while B controls something else and so on.
There are typical 12 to 250 kilobytes per second available. But the recommendation is to use only 8 kilobytes per second. That is the bandwith 99% of the user have (data collection during Halo 3 beta (2007) (median for up is 44 and downstream 42)
So why only that little?
Services providers always want to sell their products. Even on good moments you wont have the full power which is written on your contract. The bandwith is influenced by the numbers of people online, your distance to the router, the activity of the others and so on. And maybe your teammate hasn't a good connection. But his upload is your download and vice versa. That is also the reason for the upstream being similar to the downstream.
Latency (one way)
We all know the speed of light is 299,792,458 metres per second in vacuum. We also know that packets are send via light or electricity. But is there vacuum in these cords? No, it is fiber or copper, slowing our packets down to 194,865,097 m/s (65%). So here are some distances from Berlin to other locations and the time each packet took (theoretic and round-trip; the website-hoster are only in the near of the cities)
|City||Distance (km)||calculated time (ms)||measured time (ms) (ping 32byte)|
|Mexico City||9,710||49.79||671 (mexicocity.gob.mx)|
So why is the measured time always that much higher? Because you don't have a direct (physical) connection to these computers. The packets have to be routed and each router needs some time to put your packet on the right line (5 - 50 ms). Also your modem adds 10 ms.
So a good number for latency in games is 270 ms.
This issue (which is also known as "bad latency") comes down to a question of which protocol you use for your packets. For Games, UDP is the primary protocol used, but this isn't always reliable. UDP Protocol fires the packets down the wire and forgets them. Thus if a packet gets lost, it can't be found again. In real-time tests, 2% packet loss is the average latency, but for games, be ready to account for up to 10% packet loss. The possible corruption of each packet is also a distinct possibilty, along with the correct order of the stream being jumbled up and incorrect. Another protocol, TCP, fixes most of these issues, but is too slow to make a big difference. A good use of TCP in multiplayer gaming in in a turn-based game or another environment where latency is not a big issue.
What to do
Whatever you are programming, one point you should think about: use the right (smaller) data type. A byte is only a quarter of an int. And matricies can be represented as a quaternion and a vector, which leads to 7 values instead of 16. Due to their overhead strings should be avoided. You could also put several booleans in one bitfield (ok, that isn't really smaller), but you putting several numbers (which aren't 32 bits long) in one 32 bit Integer (via bitshifting).
Another way is not to send the whole world everytime (or the specific state of your character), just send the changes.
Don't be too exact! Think on images, there you don't send the real value of every pixel. Besides of grouping pixels the values are quantized. So instead of sending the exact angle in radians (or degrees) turn in into degrees and round the values to natural numbers. Or you turn them into a byte if 256 different values are still enough.
Others and more XNA/C# specific possibilities are:
- if an float-value can't be greater than 1 turn it into an Alpa8 (75% less)
- if a float-value can be greater than 1 turn it into a Halfsingle (50% less)
- Vector2 ⇒ HalfVector2
- Vector4 ⇒ HalfVector4
- if a Vector3 is normalized ⇒ Normalized101010
- if not ⇒ 3 HalfSingle
- Quaternion ⇒ NormalizedByte4
Just a simple example. You have a small game with 8 players which runs at 30 fps. Each player has a position, a direction where he look at and a bool as some kind of status. Every frame you send your data to all other players.
But aren't we sending over the internet? So we need at least three things more. The IP header is 20 bytes, UDP 8. That's the transport, but somehow the other computer have to know, how to deal with the package. So there is still the header of our framework missing. Live needs 16 bytes and something around 7 bytes is needed for XNA. Thus we have 51 additional bytes not storing any data.
Ok. 15.6 kB that is twice our limit of 8. If you want to send your voice via XNA you need to add 500 bytes per second for each teammate.
In the example we have 25 Bytes of (for the game) useful data and 51 Bytes overhead, which means that 67% of the used bandwith we have filled with “waste”. This waste we can not shrink absolutely, but relatively. If we reduce the number of sendings to every third frame (10 times per second, typical is 10 to 20 times). Let's assume we would still send our data of this frame. Hence we would send only 5.2 kB of data, but we still have had an overhead of 67% and fewer useful data. So we combine the last 2 frames with our “sending” frame. To make it easier we send only the whole position and direction of us in every of these frames – better would be to send the position of the first frame and for the second and third in smaller datatypes (the same for the direction).
75 bytes useful data vs. 51 bytes overhead means only 40% waste with exactly the same data. Sounds great and we are still sending our whole world to everyone uncompressed. Just a comparison of roundtrips. First I ping my website with a packet size of 5250 Bytes, afterwards I use 15960 Bytes. See the results:
I think I haven't to explain, that more data also mean more latency.
Don't send unneeded data
Think about these questions:
- To whom I have to talk during the game?
- Does I have to know what is behind me?
- In which direction bounce the dust particles?
- Where are which star?
- Need Linus to know which boots I wear or where I look at?
Ok. About some thinks you will have lost some thoughts and on others maybe only a „What the ….“ The last type of question you can forget, if you still have in mind, that not every client have to have the same world, but one which is similar enough to the other ones. The rest should be prioritizes.
Prediction & Smoothing
This is not that easy in a lot of cases. But the underlying thought is. Playing your game, it already has some data of your teammates. Even if they are from the past, you can combine them to compute their way of moving. This is prediction, smoothing now deals with the failures which has been made. Because showing the original position instead of the prediction when there are a new packet isn't that nice (the avatar would jump). That's why it is so difficult.
But you can make it a little bit easier by separating the physic state of an object from the rest of its states and put it (with all stuff the physicsimulation needs) in a nested helper structure. Also send more data for the simulation. More (and the right) data leads to a better prediction. Thus the data has to be send less frequently.
Due to the fact, that the position of an enemy is only a prediction or a smoothing of it and "reality" the player can't really rely that his enemy is where he sees him – remember we live in parallel worlds where some things are known to be different and others are only guessed. So when you throw a book to a specific position, the other guy could be a little bit to the left in his world and so you miss him. Therefore it is better throw at this guy and let your computer compute whether you hit him or not - and how you missed him.
You also have to hide the roundtriptime to the server (or other peers) responding on the players behaviour. That is easy, hide it with an animation. Let the character say something or let him look around like searching the path. You can also use it to be in sync with other players.
- some parts can be found here on slide 18 and 20 Networking, Traffic Jams, and Schrödinger's Cat
- Chad Carter - Microsoft XNA Game Studio 3.0 unleashed - p.561