🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Combining server reconciliation and client side prediction

Started by
9 comments, last by saad9 4 years, 3 months ago

Hi!, I'm currently developing a deterministic racing game and I have encountered a problem I can't seem to figure out the best solution for it.
In the client side every fixed time tick I perform client side prediction and store the result in history buffer,
than whenever the client receives an update from the server it perform reconciliation.
Reconciliation pseudo code:

  • Discard old inputs
  • Check if the diff between client state and server state is too big
  • If true rerun client inputs on the received server state
  • Than snap player to the received state


My problem is sometimes the diff is too big (Different computers usually causes this) and then the snapping is too harsh, I tried to interpolate the client state towards the server state but I don't have a lot of time because the next client tick is coming and client side prediction is run, so even if I interpolate it looks harsh and if I just interpolate until the next tick happens and then continue from there although the divergence is still there(only smaller),
Is there a better way to perform this? (Client and server run at 60 fps)

I tried to snap the player to the server position even if the diff is small in order to prevent the divergence from getting too big, I also tried to separate the collider and the visual representation, run prediction and snapping on the collider and always interpolate the visual to the collider but it gave the player a laggy feel
Thanks a lot

Advertisement

The first thing I see is that the pseudo-code is wrong - once you re-run the client inputs from the received server state there is (usually) no more work to do - you certainly don't want to snap any values because you don't know what to snap to. e.g. If your local tick is 10 and you just received the server's correction of your position at tick 8, you replay the 2 past ticks of input to take you from 8 to 10 and then you're finished - you can't snap to tick 10's position because the server hasn't sent it yet!

The other thing to consider is - do you know why the difference is too big? If this is down to collisions with other players, then that makes sense, but big changes of velocity are always going to be hard to make look good to a local player, and you'll have to play with 'rubber banding' and blending to make that look reasonable. But on the other hand, if you're getting small discrepancies most of the time, despite no interactions with other players, this suggests your simulation is not as deterministic as you would like, and that's probably a bug you need to fix.


I fixed the code according to what you said, I also had a mistake in the pseudo code I posted
(apparently it was the same end result but it was indeed wrong to do it this way),
I tried to find when its consistently happen and it is when player crashes into another player and a few times when a player crashes into a wall
So I should leave it that way without any smoothing or interpolation?
(I saw in overwatch they are using some sort of smoothing)

Thank you for the help


I see that all I read here is very similar to the networking of the Source Engine from Valve Software. The Counter Strike game uses this engine.

Sleepybearer said:

I fixed the code according to what you said, I also had a mistake in the pseudo code I posted
(apparently it was the same end result but it was indeed wrong to do it this way),
I tried to find when its consistently happen and it is when player crashes into another player and a few times when a player crashes into a wall
So I should leave it that way without any smoothing or interpolation?
(I saw in overwatch they are using some sort of smoothing)

Thank you for the help

If your simulation is deterministic, then the results of crashing into a wall should be 100% the same on the client as they are on the server, so you would expect no divergence and nothing to correct. If this is not what you observe, then you have a bug that needs fixing. :)

You will expect diverging simulations in the case where players crash into other players, because each player will have 'predicted' their own vehicle into a position that doesn't match the one on other clients (yet).

I've not done smoothed corrections myself but in theory, once you replay inputs and discover that you come to a different position/velocity/etc, you can decide not to apply the whole correction but could apply it over time, a bit per frame. Obviously this means that you'll find yourself diverging from the server position on subsequent frames too so you'll need to consider that. Perhaps someone else can suggest something specific and robust here.


Another option is to smooth visual display for corrections, but update the physical simulation state directly.

enum Bool { True, False, FileNotFound };

Seconding what hplus said. Interpolate your visuals toward the correct value, using whatever method you want. If the delta is small, snap, otherwise just take a percentage of the delta capped to some reasonable max.

As someone said above, if the simulation is deterministic you should only have deviation when another player is involved. Otherwise, you likely have more non-determinism than you think. One thing to look out for is compounding sources of error. Many times, non determinism just results in 1 frame differences. Only certain systems are subject to accumulating error, and those are the ones to watch out for.

https://fouramgames.com/blog/fast-paced-multiplayer-implementation-smooth-server-reconciliation

Checkout the demo here and explanation.

The author said that the two implementations he has isn't perfect, and often I've seen many people have some variation of the error correction

This topic is closed to new replies.

Advertisement