General UDP questions (as it applies to an action game)

Started by
3 comments, last by hplus0603 11 years, 4 months ago
I've been working on a little top down fighter (XNA, C#) in what spare time I've had over the last month and a half. If you've ever played Bloodline Champions, its like a really dumb downed version of that. Y'know, just something me and my friends can mess around with and maybe it'll double as some decent résumé fodder before I graduate. I plan on using the "one player hosts, other players join" type of architecture.

I've spent literally all day trying to wrap my head around networking and I'm just hoping to clarify a few things before I start to dive in and code. I know this is probably a premature post and I should probably do a little bit more research before I start asking for help, but I have like 13 different tabs open all discussing different specific aspects of networking and I'm getting a little overwhelmed. smile.png

Just let me know if I'm on the right track. Here's what I -think- I know:

1) UDP is generally faster than TCP. This is part in due to the overhead packet checking/management of TCP, and partly (and most detrimentally) due to the fact that TCP halts everything in order to resend and re-receive (is that even a word) any packets that fail. Since my game is a fighting game, and latency will be very very bad, I want to use a managed UDP system. Past this, I start getting confused, so bear with me.

2) So if I want UDP, I have to manage it a little. First, I have to know if the clients are staying "connected" with the server. So I have to have a two way connection abstraction in the packet header to check against. Just to see how long its been since the client sent a packet.

3) Second, packets can't arrive out of order. If you input "W" exclusively on one frame, and then "A" exclusively on the next, you don't want the server to receive "A" and then "W". Likewise, you don't want the server sending you the game world and then sending you the game world from 4 seconds ago (exaggeration, of course). So I have to keep track of and manually order my packets in the packet header.

4) Past this, is packet loss a huge deal? As long as I keep giving both sides the most recent packet, does it matter if one slips by?

5) The standard algorithm is just taking input exclusively from the clients, using these to update the actual game world on the server, and then drawing them back to the clients' screens. Is this right? Or is that somehow inefficient?

Thanks in advance!
Advertisement
Hello,

In my opinion, if you have the time to do it in UDP go for it. The problem is that it will require a lot of time and effort from you to achieve what you want instead of just using TCP, unless you have experience in this field or you just want to learn.
1) That is true, UDP has got less overhead and it is faster, but you have to manage it well for your game.
2) To check if the client is still connected, just do the typical ping (ie: send an empty packet to be answered, if any error occurs the client is probably disconnected).
3) For that, you can use sequence numbers. If any packet is lost, you have to ask for it.
4) Well, in a fighting game it can be a big deal. It is not the same just kicking or jumping and kicking, provided that the jumping packet was lost.
5) Normally, clients don't send input to the server. They should be smart enough to suppose that the packet reached the server and therefore the client should start its animation even before the packet arrived.

In my opinion, your requirements can justify using TCP over UDP, as you require all packets to arrive and also to arrive in order.
I'm actually working on my own network code right now and perhaps not able to give great answers, but here's my 2 cents:

1) Yeah that's true, though I would guess that the sorting etc in the hardware could be ignored because its done so much faster than the "network" part(?).

2) To handle the connection abstraction for the game (and other stuff) I have a Connection class that contains all connection specific information, like received sequence numbers and send sequence number, along with a timestamp for the last time it was active (received a packet). I use this timestamp to every frame check if the time since this timestamp exceeds some time I've decided is the time out time. If this is the case I just close the connection.

I don't know if having this information in the packet itself brings any advantages, but it might work.

3) I haven't actually encountered any case where my packet order makes a difference so I've completely ignored this (apart from discarding old update packets, since they're old). Do you have any case in mind? Like what packets must be ordered in your situation? If packet order is required I would assume it's a pain to handle this in software for UDP. TCP could be a good choice in this case.

4) I've heard some numbers that generally there's a less than 5% packet loss when on a line connection, which probably varies a lot too. I have some packets that needs to arrive, like console commands (chat messages, name changes) so I made it possible to send "reliable" packets with UDP with a simple ack/resend system (all packets contain 33 ack numbers in 8 bytes like described by Gaffer here). I've heard of some other ways to try to minimize impact of packet loss, like sending the same important packet several times and hope for the best.

You are basically the one deciding if it matters or not if packets are lost. If you are worried only about object updates it doesn't really matter. Resending old updates because it was lost, doesn't help since it would make that update packet outdated anyway.

5) Well it depends on the game, but the standard server client architecture nowadays is basically what you're describing, with some interpolation/prediction of objects' positions etc.


I like UDP since it has no "unnecessary" features for my project (like packet ordering and blocking for lost packets) and it's basically what I needed, except the resending of lost packets that I made the game handle.
UDP vs TCP comes down to the requirements of the program really. If you need packet ordering then TCP might be the best choice. If you are only sending real time updates, and no resending of lost packets nor packet ordering is needed, the UDP seems like a good solution.
Since the game your talking about is a birds eye game and not a shooter I think you should just use TCP. You'll probably end up reimplementing much of TCP on top of UDP if you choose the UDP route anyway.

TCP is also much easier to manage and considering your new to networking I think TCP would be a better choice because of this. If your worried about latency there are things you can do in TCP to lower the latency (i.e. enable NO_DELAY link).

TCP has the following benefits:
1. Packet loss wont be a problem
2. All messages are ordered correctly (TCP is stream based)
3. I love .net's TcpClient/TcpListener/NetworkStream classes lol.

While the disadvantages are:
1. Higher latency - a stable connection wont have much higher latency at all though.
2. Less flexibility?

Many combat/pvp oriented games have been made using TCP aswell. This includes WoW and Terraria.

I'm not saying USE TCP DAMMIT lol, I'm saying use TCP if you don't want to deal with the problems that UDP can create (out of order packets/missing packets).
1) No. Yes. Faster is the wrong word. UDP as such has less latency because it does not implement a stream with guaranteed in-order delivery. It also has fewer bytes in the header, but this does not matter much. It may take a microsecond less to go through the DSL modem, but on the internet it doesn't really matter. Routers forward packets-per-second, not bytes-per-second. Their queue depth is measured in packets, too. Insofar, all packets, TCP or UDP, large or small, are generally the same and equally fast.
However, as Orwell taught us, all animals are equal, but some are more equal. In our perfect world, there exists QoS to make the world even more perfect. Which means that any router on the internet may (and usually will) favour one or the other packet type in a more or less fair or unfair manner. It may delay TCP packets in order to deliver the "usually realtime" UDP packets faster. The router in my office does that. When I pick up the phone while a download is running, the router drops TCP packets in favour of VoIP (and congestion control kicks in). I don't even notice that this is happening, other than by the fact that it "just works". Normally, given the fact that the download already fills the cable, doing a phonecall should be troublesome.
Of course it may as well work the other way around. A router may give UDP a very short queue depth and drop your packets under the assumption that if they're queueing up, then they're not delivered fast enough anyway. Or, something else. You don't know. A router might even drop both TCP and UDP based on your sender address because you've exceeded your bandwidth guarantee. Many hosting plans have something like "10Mbit/s guaranteed, burst up to 100Mbit/s". Which means no more and no less than you have a plain normal 100Mbit/s uplink, and the router will usually just forward your packets, and at some more or less unpredictable time (at its own discretion, based on some secret, obscure metrics) start dropping your packets!

2) UDP gives you exactly one thing. It sends out complete, discrete, independent "messages". The only guarantees that you have is that a complete "chunk" of data up to a specific maximum size will go out, and in case it arrives at the other end, the same complete "chunk" of data will be received (never less, either all or nothing). That's all the guarantees you have.
Everything else you need, you must do yourself. This includes acknowleging (or negatively acknowledging) and maintaining a connection. The easiest way of maintaining a connection is to remember when you've last sent and last received a packet. If you've not sent a packet for, say, a second, send a keepalive packet. This usually won't happen because you usually send some data once or several times every second (but you want to be sure that you're not dropped only because you don't have anything to send for a moment!). If you've not received a packet for, say, 2 seconds, then the other end is "dead". You know this because even if they have nothing to send, they should have sent a keepalive. Consider the connection dropped. More fine grained control is of course possible, but that's the basic thing.

3) No. Yes. That's exactly the problem. Packets can arrive out of order. And not only that, the same packet can in theory arrive twice (or more often), or not at all. You just got shot. Twice. You've died and lose reputation. Twice. Charming.
If this is a problem (usually it is!) you need something like a sequence number to detect both missing and duplicate packets. Note that by implementing packet ordering, you are losing most of UDP's "extra speed" advantage.

4) Packet loss is relatively rare (around 0.5% here), but it happens. To everyone, all the time. Packet loss is a normal condition, not an error. You must be able to deal with it (or it must be OK to ignore it) because it will happen.
Packet loss not only happens because someone uses a wireless connection or because of noise in a cheap copper cable. Packets are dropped both unintentionally (because a router queue is full) and intentionally as a means of congestion control. For example, TCP uses a rather complicated congestion control algorithm that uses packet loss as the indicator of how much it can push onto the wire at a given time.

5) Making the server authorative is the only safe way of ensuring that people can't trivially cheat. Otherwise, I could just send to you "I've knocked out your guy, I win" when I'm in fact 15 meters away and could not possibly reach him.
Doing some correctness checks on the client first can greatly reduce your server load. If you rule out every impossible move before sending it to the server, the server does not need to process what's not possible anyway. Regardless, everything that comes from a client machine must be validated and must not be trusted.

1) UDP is generally faster than TCP.


There is no speed difference. There is only a difference in when you get to see the in the case of packet loss. In the case of TCP, you get to see the data (and any data after it) a big later. In the case of UDP, you get to see the data never.

2) ... So I have to have a two way connection abstraction in the packet header to check against. Just to see how long its been since the client sent a packet.
[/quote]

The actual client is already in the UDP header, in the form of client IP address and port number. This will be unique for a session. Timing out sessions when you haven't seen anything come in from that source IP / port for X seconds is common, for values of X between 5 and 60 or so.

3) ... So I have to keep track of and manually order my packets in the packet header.
[/quote]

At that point you have no advantage with UDP over TCP. You would have to delay packets to wait for re-transmitted earlier packets, just like TCP. If you want to keep the reduced latency of UDP data delivery, you pretty much have to drop packets that arrive out of order. To be able to accept occasional packet drop, you can duplicate older control data into each packet. With RLE encoding, that might not take a lot of extra space.

4) Past this, is packet loss a huge deal? As long as I keep giving both sides the most recent packet, does it matter if one slips by?
[/quote]

That depends on you. Does it matter?

5) The standard algorithm is just taking input exclusively from the clients, using these to update the actual game world on the server, and then drawing them back to the clients' screens. Is this right? Or is that somehow inefficient?
[/quote]

That's common. You often also want to run the client "ahead of time" so that input is shown with immediate feedback on the client. There are then various ways to deal with hiding latency of remote players, dealing with possible visual inconsistencies between what the client knows and the server knows, etc. Those are the areas where game networking code really gets hairy.
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement