Movement Modifiers and ServerMove

Are you testing and seeing this correction-heavy behavior under significant latency/packet loss situations, or do you get this with no simulated latency? I’d like to get an idea of how severe your issue is (large corrections? tiny? do you have extremely strict position correction in GameNetworkManager settings?). How much this is influenced by latency/packet loss vs. base logic/framerate difference will change scope of the dive/investigation necessary.

You should definitely investigate move combining. On FSavedMove_Character there’s a bForceNoCombine bool. I would suggest project-specific code setting that to true whenever you’re in a “movement altered” state. If you don’t do this, moves can end up getting combined where you had one character movement speed originally for the first move and a different speed for the second, and now it’ll get resimulated as having the most recent speed for both time steps. This would affect both “start of sprint” and “end of sprint” moves.

At a high-level view, if this is is really only visible during latency/packet loss/out-of-order packets you’re running into a fundamental problem of logically coupling separate streams from client to server. Your “ActivateSprint” RPC has no dependencies or connection to “ServerMove #123456 time delta 0.2 seconds” RPC. Those can arrive out of order. Even if those arrive in order, you can run into issues. If the ServerMove RPC sent before you activated sprint got dropped or hasn’t arrived yet, the server will simulate the full time from the last ServerMove received to the current ServerMove, meaning if you swapped “sprint speed” between those, the server will simulate two moves worth of speed boosting, putting the player ahead of where they thought they were (and farther than what they should be allowed).

The easiest way to deal with this and related issues is to design the game in a way where we know what’s going to happen to player movement ahead of time. If you can add even a 0.2 second delay to sprint, you can get the communication of “in 200 milliseconds you’ll be moving faster” out of the way and now the client who is always ahead of time vs. the server can properly predict that.

Failing solving things to not have the problem in the first place, and not knowing exactly where your problem lies, I’ll just offer up some misc points:

  • Paragon has abilities and sprints and fast travel modes etc. Building into the design tiny/near-imperceptible delays saves a lot of headache and mis-predictions/corrections. This isn’t applicable to all games and situations. We are very lenient with our corrections to cover most of the remaining issues. The danger is opening yourself up to cheating, but right now we couple leniency with precise analytics so that even if we allow you to fudge things a tiny bit, we remember and can process the data to find players who are doing that more than usual.

  • If you don’t want to be lenient all the time, you could include logic on the server correction functionality to at least be a little forgiving around the starts and stops of different modes/changes.

  • Instead of tracking sprint mode or whatever you’re working on as bools (if you are), instead store these as client movement time ranges. So instead of a bIsSprinting, it would be a float range “sprinting is active starting at client timestamp 17.23”. The benefit of this means that when you DO get server corrections, when those saved moves end up being replayed you save into your moves/CharacterMovement state a history of when sprinting was enabled, so that you can resimulate those with the right modifications.

  • Even with our lenient corrections/client-trust we have on Paragon, we still can run into significant issues with very sudden/significant movement abilities coupled with significant latency/packet loss. Player A activates a dash move ability that sends them 300 units up in a single tick. We send a “ability x activated” RPC, and then the next tick the ServerMove RPC goes to the server as well. If they arrive in a reverse order, the server will simulate the ServerMove and conclude “there’s no way you would have gone straight up 300 units under normal movement conditions, CORRECTION”, THEN receive the dash move ability activation RPC, and then next ServerMove it receives it’ll simulate doing the dash and cause ANOTHER correction. So the client sees two significant corrections. This is why I mentioned it as a fundamental problem - you have certain RPCs being sent to the server (moves) that only make sense assuming that other RPCs (ability activation) have already occurred. One way to work to alleviate this is to encode some data in the ServerMove RPC - say maybe a “movement sync key” - every alteration to movement capabilities done from outside movement code itself changes a counter/key value on the movement component, and those are sent with every ServerMove RPC. When the server receives a ServerMove RPC that is ahead of what it knows about (abilities/modes activating), it delays processing that ServerMove for up to maybe a few frames to give a good chance at receiving the other related info. Basically providing a time window on the server to receive all the client commands before the server processes and considers sending corrections back.