4
\$\begingroup\$

I'm trying to build a simple networked game and I'm having trouble keeping a jump in-sync on the client and server.

I've read the free gaffer on games articles and it's helped somewhat but I'm still not completing grasping the theory behind how to keep physics in sync.

Right now I have a character that can move right and left. This is kept in sync perfectly by sending all of the inputs and then relaying them to the clients.

For the jump command..

- I send a packet indicating that i'm jumping. 
- The client then predicts the jump
- The server begins to simulate the jump and tells the other clients that client X is jumping
- that then gets simulated on their clients

The issue is that if I keep on jumping on the spot eventually the jumps will begin to overlap. I came up with some makeshift solutions to that, but realized that if I started moving and jumping at the same time it wasn't simulating everything perfectly and sometimes the player would land on the tip of a platform on one client and miss it on the server/other client.

Right now everything is being done locally so there's virtually no lag. I figured the reason it's becoming out of sync is because of jitter.

I'm just a little confused in figuring out how to fix this. How do I get it so the client side prediction is 100% perfect in a local environment when incorporating physics?

\$\endgroup\$
6
  • \$\begingroup\$ afaik the only way to make sure is to have the same piece of code in both client and server. (preferably same language) and use same seed for randomizations. \$\endgroup\$
    – Bizhan
    Jan 22, 2017 at 13:31
  • \$\begingroup\$ @Bizz , then the server will become client , and client need server \$\endgroup\$ Jan 22, 2017 at 14:09
  • \$\begingroup\$ What randomizations are you talking about? (I'm using NodeJS) \$\endgroup\$ Jan 22, 2017 at 14:10
  • \$\begingroup\$ I don't know anything about your game but if you want 100% accuracy you may wanna forget about prediction and make it so that everything is deterministic and happens with a delay. then don't let anything happen on the client side unless the server tells it to. all mmo games are doing this \$\endgroup\$
    – Bizhan
    Jan 22, 2017 at 14:50
  • \$\begingroup\$ my first comment was a good suggestion for a turn based game, but if your game is realtime then you must consider the ping \$\endgroup\$
    – Bizhan
    Jan 22, 2017 at 14:53

2 Answers 2

7
\$\begingroup\$

Client-side prediction depends very heavily on a deterministic physics model that exactly replicates the way the game object behaves on both the client and the server. Even small floating point errors, differing random seeds, or slight differences in time if your time step is not fixed can cause very different results. These results can then rapidly diverge or cause game-breaking bugs (such as the player landing on the platform on one client and not others).

I suspect that is occurring here. While I can't give you an exact solution without seeing the way you've written your code I can offer some advice based on my own experience writing movement and physics using client-side prediction.

First, our research:

I also used Gaffer on Games as a source as well as the Source Multiplayer Networking article and Gabriel Gambetta's Fast-Paced Multiplayer series. While Gaffer on Games covers these concepts very well I found Gambetta's explanation simpler and easier to follow when I was first learning the concepts and I found Valve's article great to help connect the concepts to a live game application. They may help you similarly as well.

Second, your comments about your own simulation:

For the jump command..

  • I send a packet indicating that i'm jumping.
  • The client then predicts the jump
  • The server begins to simulate the jump and tells the other clients that client X is jumping
  • that then gets simulated on their clients

You did not mention it in that quote, so just in case: you should be doing client-side reconciliation when the results of the server return back to that game object's controlling client. This is described very well in the Gabriel Gambetta series however a quick rundown of this approach is as follows:

  1. What you listed above occurs with two differences: 1) each input is sent to the server with some unique id; common ones include the time at the moment or an incrementing integer id. 2) the inputs are stored along with that id in a queue to be used later.
  2. When the server updates the controlling client on the game state, the id of the last input processed server-side is sent back with it.
  3. The client sets the new game state (anything the server says is absolute).
  4. The client then looks at the queue of inputs and the 'last input id' sent from the server and drops any inputs with an id less than or equal to that returned by the server.
  5. All the inputs left in the queue are re-simulated all at once back to the present to ensure that the client-side prediction is an accurate as possible.

It should be noted, this process only happens on the controlling client because the other clients do not have the past input information.

The issue is that if I keep on jumping on the spot eventually the jumps will begin to overlap. I came up with some makeshift solutions to that, but realized that if I started moving and jumping at the same time it wasn't simulating everything perfectly and sometimes the player would land on the tip of a platform on one client and miss it on the server/other client.

Rather than sending a packet indicating you're moving, a packet indicating you're jumping, and so on, a common approach to sending input to the server is to bundle up all the input for all used buttons into one single packet. This is sending the state of all game relevant keys to the server. The server then has a packet containing all the inputs values as they were seen at the point of update/prediction on the client which it should be able to use to re-simulate the frame exactly as it was on the client.

Right now everything is being done locally so there's virtually no lag. I figured the reason it's becoming out of sync is because of jitter.

I'm just a little confused in figuring out how to fix this. How do I get it so the client side prediction is 100% perfect in a local environment when incorporating physics?

Some latency & jitter alone should not cause the issues that you're seeing with sync from my experience. The client-side prediction model is designed to make the simulation appear smooth and non-laggy regardless of fluctuating network conditions. A part of that is keeping the client and server in sync despite these conditions.

If I'm understanding correctly, then in your current model, It's possible out of sync errors could happen in this case for example:

  1. Client starts moving and jumps at the same time.
  2. Packet containing the movement input is sent to the server.
  3. Packet containing the jump command is sent to the server but experiences a delay.
  4. Server starts processing the movement but because the jump packet did not arrive on time, some movement has already occurred before the server computes the jump putting the client and server out of sync.

So, bundling up all of your input into one packet to send to the server rather than sending a packet for jumping separate from movement should solve the largest part of your out of sync issue since the server will be able to simulate an entire frame from exactly the same input state the client would have simulated it.

Next would be to look into implementing client-side reconciliation to reduce potential errors in the prediction code and thus warping of your character. This will be important once you are running your server on a remote box and latency begins to come into play since some inputs will occur before the server gets back to you, hence why you want to replay the ones the server hasn't processed.

Also, reviewing your code and ensuring that the update loops are happening exactly the same on client and server may help you identify some issues.

For example, in my own project I ran into a situation once I implemented collision where I was handling outstanding packets slightly differently on client and server (all at once in one tick, as opposed to one per tick) which worked perfectly for movement but caused my collision to rapidly become out of sync since the detection occurred after all movement was applied.

\$\endgroup\$
2
  • \$\begingroup\$ Thanks for such a detailed response. I added a delay buffer which helped a lot. I still need to do a bunch of tests to figure out how accurate things are right now as well as time to digest everything you spoke about. Thanks for the help :) \$\endgroup\$ Jan 23, 2017 at 10:55
  • \$\begingroup\$ "prediction depends very heavily on a deterministic physics model" Only if you're not doing rollback and reconciliation! This is a critical point. Client side prediction is super forgiving so long as you do rollback/reconciliation. If not, then you're building a deterministic networking solution, and you're in for a totally different world of pain. IMO it's the reconciliation part that's hard because you have to be able to re-simulate the world, which means you can't rely on built-in "tick" methods like Update() in Unity or _process() in Godot. \$\endgroup\$
    – Clonkex
    Jun 14 at 11:04
0
\$\begingroup\$

in my opinion

- I send a packet indicating that i'm jumping and wait for next packet .
-
- The server begins to simulate the jump and tells all clients ( including me ) that 
  client X is jumping with current state , coordinate , and direction 
  ( example 25% of jump with coordinate Y = 10 direction south , or maybe  
  50% of jump with coordinate Y = 20 direction south , ect.. )
-
- that then gets simulated at my device & other client device based at jumping state
-
- The state is increased each time new packet is send (0.1% , 0.5% , 1% , 5% .... 100% ) 

-
- if other client / me is lagging , the player will stop ( in mid air) and wait for 
  the next packet 
-
- If after a few second not receive packet , the device start showing signal picture
  ( or anything like that ) 
- 
- If other client / me simulated that current jump state is 10% but received packet from
  server that the jumping state is 90% , the device will position the player at 90% state     
\$\endgroup\$

You must log in to answer this question.

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