GodsVictory

Properly syncing multiplayer movement

10 posts in this topic

Currently, when a client moves, the inputs are processed on both the client and the server which use the same calculation to process the movement. When the client receives an update from the server, the servers position will override the clients position. I would hope that the client and the server would calculate the same positions, but that is not the case. I believe timing is the culprit. What is the best practice when syncing movement?

 

Current flow:

chrome_2017-06-19_13-28-20.png.214ffc861682d71d74ce331ad59be36a.png

 

Source: https://github.com/GodsVictory/SuperOnRoad

ivan.popelyshev likes this

Share this post


Link to post
Share on other sites

It depends on application. In general case, the best way is to send every server calculated position and lerp the client position to it , not override it, but move it smoothly. 

For strategy games with a huge number of units you can use determinism, but that means you have to be sure that all operations, and especially, all floating-point operations are synchronous both on server and client. To check synchronization, you can compare hashes of client gamestate and server gamestate, its not difficult to send it every frame.

I could post some books here but my dropbox library is already blocked by publishing houses. Really, I hate publishing houses. You can at least read some articles from https://0fps.net/

Share this post


Link to post
Share on other sites

What you implemented is called Client-side prediction. Gabriel Gambetta wrote a nice article about it: http://www.gabrielgambetta.com/fpm2.html

I also wrote about how I implemented in my in-development multiplayer platformer game: https://antriel.com/post/online-platformer-3/

You also want to implement what Ivan mentioned:

22 minutes ago, ivan.popelyshev said:

lerp the client position to it , not override it, but move it smoothly

Basically, on the client, once you do the reconciliation, you calculate the error, i.e. the difference between predicted and actual position. But you don't apply this difference right away, but instead you apply it over a few frames. You can find some pseudo code for one way of implementing that in an article by Glenn Fiedler: http://gafferongames.com/game-physics/networked-physics/

Share this post


Link to post
Share on other sites
1 hour ago, ivan.popelyshev said:

It depends on application. In general case, the best way is to send every server calculated position and lerp the client position to it , not override it, but move it smoothly. 

For strategy games with a huge number of units you can use determinism, but that means you have to be sure that all operations, and especially, all floating-point operations are synchronous both on server and client. To check synchronization, you can compare hashes of client gamestate and server gamestate, its not difficult to send it every frame.

I could post some books here but my dropbox library is already blocked by publishing houses. Really, I hate publishing houses. You can at least read some articles from https://0fps.net/

 

1 hour ago, Antriel said:

What you implemented is called Client-side prediction. Gabriel Gambetta wrote a nice article about it: http://www.gabrielgambetta.com/fpm2.html

I also wrote about how I implemented in my in-development multiplayer platformer game: https://antriel.com/post/online-platformer-3/

You also want to implement what Ivan mentioned:

Basically, on the client, once you do the reconciliation, you calculate the error, i.e. the difference between predicted and actual position. But you don't apply this difference right away, but instead you apply it over a few frames. You can find some pseudo code for one way of implementing that in an article by Glenn Fiedler: http://gafferongames.com/game-physics/networked-physics/

 

I am lerping instead of simply overriding. I was just wanting the calculations to be more accurate between the server and client. The more you lerp, the less responsive the controls feel, but the less you lerp, the more choppy movement is.

Share this post


Link to post
Share on other sites

You could take a look at how Lance-gg does it:

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

By how much is the position 'off' compared to the predicted state of the client?

I'm using pretty much the same code as yours for a project of mine(currently on hold). I'm sending the deltas to the server, but since both loops run (optimally) at 60Hz, this shouldn't be the issue, and both deltas should constantly be the same.

You usually never really see the correction in multiplayer games. Only the occasional rubber-banding.

Share this post


Link to post
Share on other sites
8 hours ago, GodsVictory said:

I was just wanting the calculations to be more accurate between the server and client.

They should be pretty accurate most of the time. As far as I can see the errors come from either:

  • Floating point operation errors. Those shouldn't even be visible unless they resulted in widely different results, like getting stuck on an edge vs moving alongside it. Not much you can do about this, but it should be extremely rare.
  • Too active physics environment where the world state is too different between client and the server. Not much you can do about that, other than increasing tick rate.

From what you said in your first post you shouldn't have issues with either of those, so I maybe you implemented the prediction wrong. Are you correctly reapplying unconfirmed inputs on the client (i.e. properly calculating reconciliation result). Are you using the same timings on both client and the server? Usually you should use fixed time step, not really the time it took for the packet to travel (i.e. not the time since last input). Although that depends on the game and could work with high enough tick rate (small time step).

Share this post


Link to post
Share on other sites

Having different result on the client and the server is a bad sign, you are right to dig deep and demand perfect accuracy. 

Some of the things I did to make sure I had the same behaviour between the client and the server : 

  • Synchronize the clock between both ends, use a fixed timestep, and make sure they do the same calculations at the same time, over the same timestep
  • Share the logic code between the client and the server (if they are both written in the same language). You'll avoid duplication and greatly increase accuracy
  • Make sure the client has all the important state variables to run complete calculation. Don't forget to send velocity, gravity etc. if they are to change and needed in the physics calculation
  • Round your results. In JS you'll have crazy-long floating points results, round them (two-decimal numbers are enough in my opinion, you could even go with one)

If your game is deterministic enough you shouldn't even have to lerp. Client should have the same results as the server and thus almost never need correction (unless lag happens).

Share this post


Link to post
Share on other sites

1. The flow is more like this chart, with a time gap between when the client and server calculate the new position. To make it like your flow chart you would need to make the client wait 25 ms to apply the input if it takes 25 ms for the server to receive and apply it. If you want to keep the client-side predicting accurate then you need to predict and account for this delay.

fllow.png.30e907803462e0abdaad1d1d933a257d.png

2. Sometimes you can make it feel more responsive by triggering non-movement animation(s) right away, like rotating the avatar or part of it (head or eyes for instance) to face the new direction. Another thing you can try is to have the client change velocity more slowly - that way you can respond right away but the position won't get as out of sync..

3. Are you maybe doing this sequence client-side: set to new position right away, LERP back towards the old position for awhile, LERP to new position?

Share this post


Link to post
Share on other sites

I ended up fixing the desync issues. I wasn't reapplying inputs that were made after the last server update.

 

A new issue has arose out of this, all normal driving is perfectly smooth, but boosting seems to cause desync.

 

you can see the boost desync here: https://superonroad-wip.herokuapp.com/

At the end of every boost, you'll get rubber banding (WASD to move, space to boost)

code: https://github.com/GodsVictory/SuperOnRoad/tree/wip

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.