35
\$\begingroup\$

I've read Valve + Gafferon and hundreds of pages from Google, but for whatever reason I can't get my head around client prediction.

To my understanding, the basic problem is:

  • Client A sends input at T0
  • Server receives input at T1
  • All clients receive the change at T2

At T2 however, using client prediction, Client A is now at a position appropriate to T4.

How do you ensure that the Client A, when predicting that the server will accept the movement request, won't be ahead of the server? Obviously all the time they are ahead, this results in snapping back to where the server last saw them. With all corrections I've tried, this is still noticeable when you stop, because the server stops behind you

\$\endgroup\$

4 Answers 4

38
\$\begingroup\$

I wrote a series of articles on this. It is based on the same ideas you've read elsewhere, but explained in a very detailed and (I hope) accessible way.

In particular, the article about the client-side prediction is this one.

\$\endgroup\$
9
  • \$\begingroup\$ Excellent articles :-) I'd love to see the fourth part of the series. As a small suggestion, a link to the next part at the end of each of the articles would certainly improve navigation. \$\endgroup\$ Commented Jul 11, 2013 at 19:09
  • 5
    \$\begingroup\$ @O.R.Mapper - I finally wrote the 4th article! gabrielgambetta.com/fpm4.html \$\endgroup\$
    – ggambetta
    Commented Jul 30, 2013 at 16:15
  • \$\begingroup\$ Kudos for your article series :-) Very helpful, thanks :-) \$\endgroup\$ Commented Jul 31, 2013 at 20:33
  • \$\begingroup\$ All articles (I could find) that talk about reconstructing past using stored snapshots take shooting as example. Does this apply for movement as well? I can imagine that movement resimulation can lead to some big differences for other players if they can collide with each other. Let's say two players move against each other and one of them stops moving few "steps" away from would be collision point. This stop commands arrives late because of lag so if we resimulate world, the two players would be in very different positions \$\endgroup\$
    – Lope
    Commented Nov 6, 2013 at 22:10
  • 1
    \$\begingroup\$ Thanks for the articles, really helped me understand the concepts. I would like to ask a question about MOBA and RTS games. When you play them multiplayer you can "always" feel some lag, unlike multiplayer FPS games where your game runs as smooth as single player. They also do not have problems caused by interpolation, such as correcting position of a wrongly-placed unit or visual effects of an ability/skill that showed to client, but actually did not effect the game state because server decided skill was used too late. What is your opinion about those games? Do they use any interpolation? \$\endgroup\$
    – unlut
    Commented Jul 19, 2019 at 19:12
4
\$\begingroup\$

I haven't actually implemented this (so there might be some problems I'm not immediately seeing), but I thought I'd try to help.

Here's what you said is happening:

Client A sends input at T0

Server receives input at T1

All clients receive the change at T2

At T2 however, using client prediction, Client A is now at a position appropriate to T4.

It probably would be useful to think in terms of server time. Its (probably) very similar to how interpolation works.

Every command is sent up with a server time. This server time is figured out at the start of a match by querying for the server tick, compensating for ping time. On the client you have your own local tick count, and each command you send up is converted to server ticks (it's a simple subtraction operation)

Also, the client is always rendering "in the past". So you assume that the world the client sees is, say, 100ms behind what the server's time really is.

So let's rephrase your example with server time (designated by S).

Client sends input at T0 with server time S0 (which I'm guessing is really "client representation of server time minus interpolation time"). Client doesn't wait for response from server and moves immediately.

Server receives input at T1. Server figures out client's authoritative position at server time S0 given by the client. Sends that to the client.

Client receives the authoritative position at T2 (still with designation of server time S0). The client keeps track of some past amount of time worth of previous events (probably just a queue of all unconfirmed predictions).

If the predicted position/velocity/whatever that the server sends back at S0 is different than what the client has stored at S0, the client handles this somehow. Either by snapping the player back to their past position, or resimulating the previous input, or maybe something else I haven't thought of.

\$\endgroup\$
1
  • 3
    \$\begingroup\$ That's all correct except the bit about the client rendering in the past. Relative to the server, the client is actually rendering in the future! The server knows that the information it has from each client is old and that each client will have already changed since then. \$\endgroup\$
    – Kylotan
    Commented Jan 16, 2012 at 0:37
2
\$\begingroup\$

Actually there is an open-source implementation in github which shows how this is done. Check out Lance.gg

github repo: https://github.com/lance-gg/lance

The client prediction code is implemented in the module called src/syncStrategies/ExtrapolateStrategy.js

Besides extrapolation, there are two concepts which I didn't see mentioned above:

  1. Incremental Bending. Basically rather than applying the server correction all at once, you let the delta apply in small increments. That way remote objects will gradually adjust their positions to match the server positions. There is position bending, velocity bending, angle bending, and angular velocity bending. Also you may want different bending factors for different objects.
  2. Step Re-Enactment. The fact that the data is in the past means you can rollback time to the server data time, and restart from that point. Of course you will still need to bend towards the new-found position, rather than jump to it.
\$\endgroup\$
1
\$\begingroup\$

Client A is always ahead of the server - but it doesn't matter. You only have to snap the client back if the server says there was a problem with the reported position, at which point the client re-runs all the changes it has made since the error with the corrected values, to bring it to a state compatible with the server.

To do this, the client needs to remember some of its past state and past updates. This may only be a few simple values such as position, velocity, orientation, that sort of thing. The server will periodically send an acknowledgement that various client updates were legit, meaning they can now be forgotten from the client. If the server however reports that an update was invalid, the client state rolls back to that point and the future changes are applied to that modified state.

There are some extra links at the bottom of the Valve article that are worth reading - this is one of them: https://developer.valvesoftware.com/wiki/Prediction

\$\endgroup\$
4
  • \$\begingroup\$ So, am I right in thinking that client (at t=4) receives info about t=2, so it resets state to t=2 then re-runs updates to bring objects from t=2 to t=4? \$\endgroup\$ Commented Jan 15, 2012 at 13:15
  • \$\begingroup\$ I'm still not grasping it for some reason. The server isn't being told the position of the player, only the inputs. So the player is moving from the last position that the server said it was at. Input is applied. Server is informed. Server confirms input to everyone. Assuming all commands are accepted, the server will still be behind Client A - so when Client A stops, its character will stop immediately then slide back to the servers location when it receives the stop confirmation. \$\endgroup\$ Commented Jan 15, 2012 at 13:42
  • \$\begingroup\$ @GeorgeDuckett: yes (although it doesn't have to be t=4, it could be whenever a discrepancy is detected, and there could be any number of re-applied updates.) \$\endgroup\$
    – Kylotan
    Commented Jan 15, 2012 at 16:35
  • \$\begingroup\$ @ChrisEvans: known state + changes based on input are equivalent to sending state anyway. As for the stopping example, that in itself is an input, and the server is still simulating movement until it receives that input. Assuming constant latency, the server will stop the player moving at precisely the same position that the client saw when he stopped moving, because the client was ahead of the server. (In the real world, the latency varies, so you interpolate a bit to smooth it out.) \$\endgroup\$
    – Kylotan
    Commented Jan 15, 2012 at 16:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .