6
\$\begingroup\$

I started a new project, a fast-paced online game (some sort of 2D MOBA). I encountered a problem with the part of netcode about client prediction & server reconciliation.

Game Mechanics: 2D - Platformer (MOBA)
Movement: WASD (jump / left / down / right)
Abilites & Autoattack: J/K/L
Architecture: Client/server (authoritative)

My netcode is using these techniques:

  • Client-side prediction & server reconciliation
    • Server is sending position & data to the player, the player is replaying the last unprocessed inputs
    • Client is sending input each frame at a fixed timestep (60 times/sec) which are processed on the server-side
  • Other entities are interpolated
  • Bullets/abilities are extrapolated (Dead reckoning)

So far, this is working good on clients. My question is, how to handle some sort of forced movement?

For example, another player stuns or slows down you, on the server, you will be slowed down or stunned, but you will not know it before the packet arrives, since you are predicting the next state, it will teleport you back because your speed was changed to 0 or lower (in case of slow), it will immediately turn you back (teleport).

What is a good way to handle this technique? I was thinking about interpolating between the current position and the position on the server, but it is not working well for me.

\$\endgroup\$
4
\$\begingroup\$

For example, another player stuns or slows down you, on the server, you will be slowed down or stunned

There is a delay there, from when the player initiates the stun until it reaches the server. For the player initiating the stun, it does not happen right away. If we do not deal with that lag, it will bite us later.

Let us follow the general approach: Send message to the server, and initiate animation (not a telegraphing animation, this is at the stage where there is not cancellation), and wait for server response.

Thus, when the player initiates the stun, it will play an animation, which has some duration. We, hopefully get the response from the server just when the animation ends. How much time must pass in the server so that the client receives the response form the server when the animation ends? That is animation duration minus round trip time for that player.

Thus, ClientA:

  • Send message to Server
  • Initiate animation (duration = D)
  • Wait for server response ("stun happens")

Server:

  • Receives message from the CliantA
  • Wait D - ClientA.RTT
  • Apply stun
  • Send message to ClientA ("stun happens")

Notes:

  • Do not take Wait here as sleeping. It is more like "continue this process after this much time has elapsed", or "remember to do the rest of the steps after this much time has passed".
  • By apply stun means that from that moment on, the server will take the stun into consideration in the simulation

but you will not know it before the packet arrives, since you are predicting the next state

Right, we must deal with the other player. The issue is that it can't predict being stun. If only the server could tell the player before the stun should happen… Wait, we can, because we dealt with the entry lag!

Server:

  • Receives message from the ClientA
  • Send message to ClientB ("you will be stun in X")
  • Wait D - ClientA.RTT
  • Apply stun
  • Send message to ClientA ("stun happens")

Hmm… So we need this feature where the server tells the client something will happen ahead of time (so that the client can take into account on its prediction).

If we can tell the client something will happen in the future, we can get rid of the wait on the server. Also how much time is X? We can try to compute something like D - ClientA.RTT/2 - ClientB.RTT/2, However, better talk in frames:

Server:

  • Receives message from the ClientA
  • Target_Frame = Current_Frame + (D - ClientA.RTT) / Frame_Duration
  • Send message to ClientB ("stunt will happen at Target_Frame")
  • Send message to ClientA ("stunt will happen at Target_Frame")
  • Wait until Target_Frame
  • Apply stun

And there you go. If all clients know on what frame they are, this works, they can predict the stun.


but you will not know it before the packet arrives, since you are predicting the next state, it will teleport you back because your speed was changed to 0 or lower (in case of slow), it will immediately turn you back (teleport).

This should not happen anymore. Because the client was able to predict the stun, because the server told it about it before hand. However…

You are still going to need interpolation. In some situations D - ClientA.RTT will be negative. And that just does not work. In others ClientB is too far behind.

You can, of course, tweak the animation so that D of reasonable length (keeping in mind that making it longer can be in detriment of user experience). But you can't control the round trip time. If it is long enough then interpolation is the way.

\$\endgroup\$
3
  • \$\begingroup\$ Thanks mate! Gold answer and nice explanation. I wanna ask, RTT is time when packet was send to the server and returned back (+time to process). In first case, we press input which will invoke to cast stun f.e and starts animation, so server should substract only 1/2 of RTT or am i missing something? \$\endgroup\$
    – saqirmdev
    May 18 at 12:41
  • 1
    \$\begingroup\$ @saqirmdev Yes, that is RTT. Now, let us say that the server waits wait_time before sending the packet back. So the client receives the response after RTT + wait_time. If we want the client to receive the response when the animation ends, then we want the duration of the animation to match, i.e D = RTT + wait_time. Solve for wait_time. wait_time = D - RTT. \$\endgroup\$
    – Theraot
    May 18 at 13:10
  • \$\begingroup\$ Oh thanks you are right. this is the best explanation for this case so far on the internet. Thanks alot. You also explained the case when animation is instant \$\endgroup\$
    – saqirmdev
    May 18 at 13:17
0
\$\begingroup\$

I found a workaround after lot of testing & calculating.

I tried to slowly adjust client position to match server position by adjusting client predicted moves

If server and client does not agree with position, player will continue his client prediction (movement) but it is slowly adjusted to the server position. I do not think this is actually best way to do it, but it hides lags (jittering/teleporting) pretty well.

I will set this answer as solution, however feel free to share your ideas

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Out of curiosity, what is the mean time to converge between client and server using this approach? And does it not lead to more bad than good? \$\endgroup\$
    – Sacha
    May 17 at 20:27
  • \$\begingroup\$ @sacha it is not best approach. I am just smoothing out the correction to hide lags. It works well atleast for now, it needs more testing. It is more like hack approach than real one. I do not know how to really deal with it. It combines futuristic movement prediction with past. For exampls, if you are moving at speed 10px / sec and latency is 1000ms. Lets say you gets slowed by enemy's ability, but your client wont notice that slow until packet arrives. So server says hey client you are too far away, you moved 10px but you were slowed, so move back by 5px. It suddenly teleports you back. \$\endgroup\$
    – saqirmdev
    May 18 at 4:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.