Skip to main content
GameDev.net gamedev.net
🔒 Locked

"The Quake3 Networking Model" Question

Started by Barabashka Dec 30, 2004 at 4:53 AM 29 replies 12k views
Original Post
Barabashka
Barabashka
Hello, I just read this article at http://www.bookofhook.com/Article/GameDevelopment/TheQuake3NetworkingModel.html and I'am trying to understand why: "server sends sequenced game state updates that are delta compressed from the last acknowledged game state the client received". If "first person" side does client side prediction, what does it matter, how other third person players had moved till I got new game-state update? OK, I missed few updates, made prediction for them (possible got some of them wrong) but now with new update packet I can fix it all. So why would I need all that game-state updates I missed? If "Dropped packets are handled implicitly..." why it is so important to send them? Thanks.
hplus0603
hplus0603
You seem to be asking whether the Quake 3 model re-sends dropped packets.

The answer is that it does not.

For each packet that DOES get through, the other end acknowledges in their next packet. This way, the two can share a common baseline for delta compression.

Delta compression is used to minimize the amount of data sent; movement as a delta from where you were half a second ago can be sent with fewer bits than movement in an absolute (world-space) coordinate frame.
enum Bool { True, False, FileNotFound };
Barabashka
Barabashka
Now everything on their places, THANK you hplus0603 & Happy New Year to everybody.
Basiror
Basiror
just wondering but why doesn t he include visiblility calculations to the networking system?

do you really have to know whats going on 4 blocks behind you?
http://www.8ung.at/basiror/theironcross.html
hplus0603
hplus0603
In Quake, it's possible to turn 180 degrees with a flick of the wrist (skilled players do this all the time). The levels are usually fairly small, too. And there are ranged weapons (such as rocket launcher) that you absolutely need to know about wherever they are on the map. Thus, typical FPS-es don't do visibility filtering much.
enum Bool { True, False, FileNotFound };
Barabashka
Barabashka
What is still a mystery for me is if server generates one update packet and send it to all clients, so if just one client does not get some packet - server now have to increase delta range.

For instance:
Server sent delta(Pos(1) - Pos(0)).
Got ack from all clients.

Server sent delta(Pos(2) - Pos(1)).
Did not get ack from only one client-A.

Now Server sends to all delta(Pos(3) - Pos(1)).
...

So the delta range can grow is'n it ?
eloso
eloso
Well, i may be wrong but as far as i understand it, you have to store the last ack for every client independently. So in your case you would send delta(Pos(3)-Pos(2)) to all clients except client A and delta(Pos(3)-Pos(1)) to client A only.

Quote:

So the delta range can grow is'n it ?

Yes, that's right.

Now i have another question: How about more data consuming things like chat messages? Would you send them again and again in every packet until you receive an ack for at least one of those packets containing the chat message? Or is it better to send those only something like every 10th frame? But wouldn't that break the delta compression mechanism?
Barabashka
Barabashka
Please correct me if I'm wrong but I was sure that if server

1. Prepare data for client.
2. Add CRC
3. Encript packet

for each client that would be some waste of time/performance(more latency).
I thought server could send the same data packet for everyone.
Promit
Promit
Quote:
Original post by Basiror
just wondering but why doesn t he include visiblility calculations to the networking system?

do you really have to know whats going on 4 blocks behind you?


I don't know about Q3, but Half Life (and probably HL2) does do a PVS check. This is mostly useful for preventing wallhacks.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Tim Cowley
Tim Cowley
Quote:
Now i have another question: How about more data consuming things like chat messages? Would you send them again and again in every packet until you receive an ack for at least one of those packets containing the chat message? Or is it better to send those only something like every 10th frame? But wouldn't that break the delta compression mechanism?

I think the easiest way round that one would be to keep a TCP socket open for non-time-critical (but still important) data. That way you can blame the service provider for any missing chat packets [grin]
Morbo
Morbo
Barabashka: The server has to send data to each client independently (at least in the client/server model) because each client needs a different, small part of the overall game state. To build a packet that you could simple fire off to each client would require a whole heck of a lot more data, which is a huge waste of bandwidth. Since server power is easier to scale than bandwidth, we pass the hit onto the server instead.

Also note that, as currently implemented, the Q3 network model has a nasty flaw: one laggy/poor client connection can quickly slow the whole server down. This is because each time a client lags, it forces the server to send more data on the next pass. If this continues for too long, eventually the server is trying to send a whole heck of a lot of data to that client in order to catch it up. This ends up lagging every other connection, which creates a very nasty vicious cycle. They could've implemented a per-socket throttle, but for whatever reason didn't.

A far more interesting network model (IMHO), is the one they used in Tribes 2. I can't seem to find the bookmark, but google should provide the answers.
hplus0603
hplus0603
Quote:
for each client that would be some waste of time/performance


As long as your connection can fit on a 56k modem, or even most actual broadband connections, the encryption of the data won't be a major CPU hog on the server. The amount of data (5 kilobytes per second per player, tops?) is miniscule compared to the actual throughput of modern CPUs (5 GIGAbytes per second of memory throughput -- that's a factor of a million!).

You don't need to add your own CRC, because UDP already does that for you. You might want to sign the packet, though -- typically, you'll want to use an encryption mechanism that can both sign and encrypt in the same go, although re-signing when the packet is already in the cache is really cheap.

Now, that being said, I don't recommend encrypting your data sent from the server. Signing is more important than encryption. Encryption doesn't actually give you any more security, because anything that the client can present to the user, the user can (by definition) figure out how to decrypt.

Quote:
How about more data consuming things like chat messages? Would you send them again and again in every packet until you receive an ack for at least one of those packets containing the chat message? Or is it better to send those only something like every 10th frame? But wouldn't that break the delta compression mechanism?


I'm assuming the delta is not a raw, per-byte delta compression, but instead a data structure aware, variable-bit-length delta compression mechanism. Thus, it would delta-compress each individual position, heading, and other datum within the packet. Tacking on chat, or not, in the same packet does not affect that compression.

If a server gets laggy when a single player is falling behind, then that server is running with too many players for the size connection it has. That's hardly a fault of the game, rather of the operator. You have to design in enough headroom in your system (hardware/hosting) to deal with hiccups, or it's never going to stay stable.

enum Bool { True, False, FileNotFound };
Morbo
Morbo
Quote:
Original post by hplus0603
If a server gets laggy when a single player is falling behind, then that server is running with too many players for the size connection it has. That's hardly a fault of the game, rather of the operator. You have to design in enough headroom in your system (hardware/hosting) to deal with hiccups, or it's never going to stay stable.


Unless, of course the lag is caused by the client connection, which is not in the operators control. One can even picture a DOS attack based on this: a custom client designed to connect to a server and simulate lost packets and high latency, slowing down the game for everyone. From this perspective, it's the game's responsibility to minimize the impact of a single bad client on everyone else.
hplus0603
hplus0603
Quote:
Unless, of course the lag is caused by the client connection


A single player lagging should not affect the other players, even if that player needs more bandwidth (i e, the benefits of delta compression shrink). There is an upper bound on the size of a state update, which is one-over-delta-compression-ratio. Typically, you might get 50% of delta compression, so one laggy client would be the same as two non-laggy clients. If you don't have space for at least one more non-laggy client on your server, I believe you're running with too little headroom.
enum Bool { True, False, FileNotFound };
bani
bani
Quote:
Original post by Morbo
Also note that, as currently implemented, the Q3 network model has a nasty flaw: one laggy/poor client connection can quickly slow the whole server down. This is because each time a client lags, it forces the server to send more data on the next pass. If this continues for too long, eventually the server is trying to send a whole heck of a lot of data to that client in order to catch it up. This ends up lagging every other connection, which creates a very nasty vicious cycle. They could've implemented a per-socket throttle, but for whatever reason didn't.


this is wrong.

one client's sucky connection cannot lag anyone else. ever. period.

per socket throttling (rather per client) is implemented. indisputable fact. all packets to each individual client obey rate settings, always. if the data to be sent would exceed the rate, their snaps are delayed so they will always stay under the client rate setting.

client lost packets, etc also do not generate any more work for the server. snaps are generated fresh every frame, regardless of whether a client acknowledged them or not. large snaps are typical when clients enter new clusters or when a bunch of new entities are spawned, so theres no difference really for snaps from dropped frames vs snaps from "normal" gameplay.
[=^_^=]http://bani.anime.net/etpro/ - ETPro websitehttp://bani.anime.net/banimod/forums/ - ETPro discussion forums
Basiror
Basiror
just to make sure if i got this right

instead of sending the client the coordinates ... of a player you send the curpos-laspos so your numbers get much smaller and you spare a few bits

analog for the rest of the gamestate data


what about values that didn t change considerably? e.g. a angle differs only 1 degree or if a player is 72 units large and the positio differs only a few units 0-4?

you wouldn t update in this case right?


i would implement a 16 oder 32 integer at the top of the gamestate where i set flags whether this variable has changed or not

how often do you update gamestates per second 6 8 12 times?
http://www.8ung.at/basiror/theironcross.html
Prefect
Prefect
That's not what delta means, at least not for the Quake 2 and Half-Life protocols. Quake 3 might be different, but I really doubt it, because it only makes things more complicated.

Delta simply means that of any structure (e.g. entity data) only the fields that have changed since the last acknowledge state are sent. The fields that did change are sent in their entirety. So the data that updates a structure has a small header bitfield indicating which fields are resent, followed by the data itself.

Fields are always updated if changed, even if the changes are minor. However, at least Half-Life uses bit packing and is rather aggressive about how many bits per field are used. IIRC game updates are sent at a fixed rate, while the client sends commands at the client frame rate.

cu,
Prefect
Widelands - laid back, free software strategy
hplus0603
hplus0603
In addition to bit packing, you may want to use a variable-length encoding of certain fields. Especially if the fields themselves are sent as a differential to the last-acked value, most changes will be small.
enum Bool { True, False, FileNotFound };
bani
bani
Quote:
Original post by Basiror
how often do you update gamestates per second 6 8 12 times?


sv_fps determines the number of updates per second the server will try to send. its usually 20 in quake3.
[=^_^=]http://bani.anime.net/etpro/ - ETPro websitehttp://bani.anime.net/banimod/forums/ - ETPro discussion forums
Barabashka
Barabashka
Quote:
Original post by Prefect
That's not what delta means, at least not for the Quake 2 and Half-Life protocols. Quake 3 might be different, but I really doubt it, because it only makes things more complicated.


I thought that delta compression algorithm is all about sending differences and not all data. Using delta u can pack position (x,y,z data) with very good accuracy, but still bit packing is very useful.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.