Zen of Networked Character Physics

Started by
20 comments, last by DirectXFreak 19 years, 3 months ago
Hey all, I've just released the presentation and demo used in my AGDC 2004 talk "Zen of Networked Character Physics" including full source code for the demo. The talk shows how to apply standard fps netcode techniques such as client side prediction and important moves to any physics simulation, provided that it is deterministic, driven by input and each client has strong ownership over one object on the server. See www.gaffer.org for details and a download cheers all
Advertisement
Thanks for posting that presentation!

One question: we've found that packet loss is typically "bursty"; i e, with 20% packet loss, we don't lose 1 packet of every 5; instead, we'll get 2 seconds of packet loss out of every 10 seconds of traffic. This is likely due to how routers on the internet work. Do you have any data on how double-sending user input copes with those situations?
enum Bool { True, False, FileNotFound };
Quote:Original post by hplus0603
Thanks for posting that presentation!

One question: we've found that packet loss is typically "bursty"; i e, with 20% packet loss, we don't lose 1 packet of every 5; instead, we'll get 2 seconds of packet loss out of every 10 seconds of traffic. This is likely due to how routers on the internet work. Do you have any data on how double-sending user input copes with those situations?


sure, ok. firstly because the server runs on client time, this bunching up doesnt affect this type of netcode in the slightest - the server just catches up perfectly when it receives the latest packet from the client with time say 20 steps ahead, it does 20steps in a row immediately to catchup.

because the important moves are sent redundantly with every input rpc sent from client to server, even under 99% packet loss, as soon as the server gets an input packet, it has complete information to advance ahead without desyncing the client -- eg. the important moves are a perfect summary of the client input since the last server ack. this redundancy is what allows the system to handle high packet loss perfectly

important moves are is easy if you have binary input, because the important moves are simply the deltas. in the source code here, the important moves are whenever the boolean inputs change (ie. left is released, or right is pressed etc.)

what if your inputs are floats? say if you have an analogue controller, surely everymove for the last 2 seconds is important, and it quickly become impractical. in this case, you need to use splines and control points to approximate the analogue inputs instead of just sending important moves. this is tricky, and i havent done it yet, but its the best you can do in the situation. i dont know of any fps that currently does this, but in theory it works, and the amount of error in the approximation if done well, should be small enough that snapping is not noticeable, and your smoothing will handle any tiny snaps nicely

cheers
Quote:
even under 99% packet loss, as soon as the server gets an input packet, it has complete information


So you just keep tacking on more and more input data with each packet until you somehow gets acknowledge that the data made it through? This works until the packets get too big :-) Luckily, movement data typically RLE compressed really well, so this is probably still OK. If you allow very long times of drop-out, though, you may get some of the Counterstrike-type cheats where people would install on/off switches on ports on their network hubs :-)

Quote:floats as inputs


I've found that most "analog" inputs have, at best, 256 levels, i e 8 bits of precision. Thus, you can quantize your analog input to 256 discrete levels, and nobody will know the difference. These will likely still compress well, if you use a variable-bit-width encoding that favors small deltas.

The play-forward of received data gives rise to a bit more curiosity: what happens to other players at the point where you catch up? Are they re-stepped in sync, or do they remain wherever they were at the last step (that the catcher-upper is stepping up to)? Do you use a log of positions on the server for accelerating this case? Will you send correction ("snap") packets "in the past" to clients if they end up colliding?
enum Bool { True, False, FileNotFound };
no actually the server acks in the ping round trip time, so under normal conditions you only ever need to send the important moves (input deltas) for the last 250ms or so.

how often can the typical user change left/right/forward/back input values in a 1/4 of a second? even if every move was important, you're only sending a max of 25*5bits in the example i've presented :)

its quite a practical technique, and it works perfectly even on low bandwidth connections - read the article/source and you'll see that the server ack is the 'synchronize' correction send back with a timestamp so the maximum size of the redundant input is bounded nicely

if this still makes you nervous, just clamp the maximum number of important moves to some number (say 5?) and you should cover most cases perfectly for real-world input. i just wanted to present an implementation that was perfect (never snapped, ever) to show that the technique is flawless.

cheers
regarding other clients, this is what i call a "proxy" in the article and source code. you can think of proxy as being the other clients' view of your character remotely, or alternatively, the 'round trip' of your input being sent to the physics and corrections arriving back to your client.

while the server is in lock-step with the client, the proxy runs on its own concept of time. as you pointed out, if you dont do this, the proxy will lock up while packet loss starves it of input packets, which is unacceptable.

so effectly, the proxy is just a physics simulation extrapolating continuously from the last snapped input/state sent from the server, and everytime a snap comes from the server, it just snaps to it, then extrapolates again -- smoothing is used to make it all nice and continuous so the snapping cant be seen

remember, the proxy is just an approximation so its ok, but the client/server require a much greater coupling of physics, which is why they run lockstep

cheers
I see how this works right

except, how would I deal with multiple clients colliding? neither client is guarnteed to see the other client at the same spot so the collisions can't really be assured to be deterministic

any ideas?

all I can think of is some kind of hack where somebody is the authority and over some period of time all cubes are put in some path this authority specifies [to get back to being able to use that the phyiscs is deterministic]. Still, this is a hack and will not likely work well if the bodies remain touching [like, a stack]
this is the fundamental cost of client side prediction. yes the collision will be deterministic, but each client will have a different result from the server, which will cause a snap for both clients.

my take on the future of networking: as multiplayer games become more focused on player-player interaction, they will be forced to trade back latency by dropping client side prediction to eliminate the snapping when players interact. you'll also need to drop the client-server lockstep and make sure the entire scene physics advances ahead in lockstep - effectively forcing all players to run at the latency of the most lagged player.

obviously this isnt practical in the near future, we need regular packet delivery, low latency, and low packet loss for this to be practical - but over the next 5 years i think we'll start seeing a swing towards this for interaction heavy type network games

cheers
erk. silly anonymous postings - anyway as i was saying during my talk, consider a physics simulation with a stack of blocks and n clients connected to a server. each client can interact with any of the blocks, so no one block is clearly owned by a single client.

consider what needs to be done to ensure that the blocks stack correctly and dont snap, firstly the entire physics block simulation needs to advance ahead in time in sync (each block cant go ahead itself as it recieves input from the client), which makes it different from the technique i present in zen of networked physics. there are two things you can do, first you could just completly decouple client and server time, which can cause problems under packet loss or packets bunching up (you dont normally receive packets sent at 100fps at nice 0.01 intervals for example...), OR the halfway option, clients still have their own time concept, but the server locksteps ahead only when ALL client input is received. this effectively makes everybody run with the latency of the most lagged client, so clearly this second option is only good if everybody has around the same ping, but it does fix up the issues of timeshifting of packets over the network.

next, each client needs to stop performing client side prediction - which means they need to accept the round trip latency when interacting with the simulation. otherwise if two players are interacting with the same block (or two players blocks are interacting) snapping will always occur. there is simply no way to resolve this short of eliminating the erroneous client side prediction, so you have to wait for that round trip.

hope this clears things up, cheerio
Why is it necessary to completely eliminate the client side prediction? As in most cases, you still have some possibilities between using and not using csp. Lots of players influencing a stack of boxes is an extreme example, IMHO. In games, how often does it happen that two players directly influence the same object at the same time? You can always use client-side prediction, but instead of just predicting the player itself, you predict the whole world. If another player is moving in the clients direction, it knows that they will collide, unless the collision is caused (or avoided) by simultaneous user input on both sides. So it does (and will) depend largely on the type of game if csp can be performed.

You could even go one step further and use csp on an object while it is under direct control of the client (think HL2's gravity gun) and as soon as the object is released, you turn it off, smooth the object into a non-predicted state. If you incorporate all the game's knowledge into the network system, you can minimize the snapping by just using prediction where it is safe, and waiting for the roundtrip where it is not safe (e.g. when the game knows already that two players are influencing the same object). The only problem I see here are the possible side-effects of turning csp on and off all the time, which will result in little snaps in itself.

RFC, I have never tested anything like this.
Regards

Jörg RüppelZoidcom - Game Networking System

This topic is closed to new replies.

Advertisement