Rubberbanding On Client due to future state on client?

I am beginning to work with the Unreal Engine 5.3 netcode. My character on the Client moves smoothly on the Server. My character on the Server moves smoothly on the Client, except when he stops. Then he rubberbands back a bit.

I put some prints in the scripts. One is when I am moving. It presents the milliseconds and true/false indicating that there is any velocity applied to the character. The other is in the Tick event, where I am printing the milliseconds and the position of the characters.

You can see that the Client’s proxy of the Server’s character is running ahead. That is, it is about one frame ahead of the character on the Server. Then when the character stops moving on the Server, the Client’s proxy is a little too far, then the next frame he jumps back to where the character settled on the Server.

My understanding is that the UE netcode keeps the Clients in the Server’s future, that the Clients are always processing the world in the Server’s future. So this makes sense.

But what I don’t understand is how to avoid the rubberbanding.

Here is the part of the log of interest.


[BP_Alex_C_0] Server: 238 true
[BP_Alex_C_0] Server: 239 X=-489.756 Y=-147.679 Z=91.15
[BP_Alex_C_1] Client 0: 241 X=-494.442 Y=-140.849 Z=91.15

[BP_Alex_C_0] Server: 270 true
[BP_Alex_C_0] Server: 271 X=-494.277 Y=-140.926 Z=91.15
[BP_Alex_C_1] Client 0: 274 X=-498.801 Y=-134.164 Z=91.15

[BP_Alex_C_0] Server: 303 true
[BP_Alex_C_0] Server: 303 X=-498.603 Y=-133.992 Z=91.15
[BP_Alex_C_1] Client 0: 306 X=-502.917 Y=-127.063 Z=91.15

[BP_Alex_C_0] Server: 335 false
[BP_Alex_C_0] Server: 335 X=-500.002 Y=-131.75 Z=91.15
[BP_Alex_C_1] Client 0: 338 X=-507.251 Y=-120.11 Z=91.15

(no movement event fired)
[BP_Alex_C_0] Server: 368 X=-500.002 Y=-131.75 Z=91.15
[BP_Alex_C_1] Client 0: 370 X=-500 Y=-131.75 Z=91.15

So if the Client is going to move the proxy character to a position ahead of what it is told by the Server, how is it possible to avoid the rubberbanding? How can the Server tell the Client that the proxy character stopped moving without the Client overshooting the stop location?

(edited for clarity)

The only character setup (class) that has any “netcode” written is the base character class. This class uses character movement component (CMC).

CMC uses client-side prediction so the Autonomous Proxy is ahead of the Authoritative Proxy. The autonomous sends “end results” along with the inputs to the server. Server takes the inputs and calcs its own end result, then compares. If the autonomous’ doesn’t match the server, the server sends a correction. The correction moves the autonomous to the servers corrected position/rotation etc.

Full walkthrough…

My problem is the opposite of what you described.

The Character is on the Server (possessed by a player on the Server), and the Client has a Simulated Proxy of that Character. The Simulated Proxy on the Client is running ahead of the Character on the Server.

When the Character on the Server stops, it tells the Simulated Proxy on the Client it stopped and where it stopped at. But the Simulated Proxy on the Client, being ahead of the Character on the Server already, has to back up to match the location of the Character on the Server when it stopped.

BTW, I read through those docs and more already. I am just curious how this was supposed to work with the Client running in the Server’s future like this. Or do I have a misconception of how this is functioning???

edit:

I think the terms are Actor, Autonomous Proxy, and Simulated Proxy. If I understand the docs correctly, the Server has no proxies. All the proxies are on the Clients that relate to the Actors on the Server.

So I was thinking of this problem more and decided I needed to prove that the Client’s Simulated Proxy was in fact running ahead of the Server’s Character. So I went back to the beginning of the movement to see what it looked like, and I found that it didn’t do any such thing. It was initially in sync, but then slowly diverged. The Client actually predicted more than it should, a little bit at a time. This means that the rubberbanding is actually the proper response to the error on the Client.

But why is there an error at all? And how do I fix this?

Please also note that this is tested on one Windows box running two instances of the game from the editor (pretty neat feature, really).

The following is the log for the start of the movement. You see that the positions are nearly identical, but they slowly diverge.


 [BP_Alex_C_0] Server: 599 X=-476.768 Y=-72.897 Z=91.15
 [BP_Alex_C_1] Client 0: 601 X=-476.77 Y=-72.9 Z=91.15
 
 [BP_Alex_C_0] Server: 682 X=-476.768 Y=-72.897 Z=91.15
 [BP_Alex_C_1] Client 0: 684 X=-476.77 Y=-72.9 Z=91.15
 
 [BP_Alex_C_0] Server: 747 true
 [BP_Alex_C_0] Server: 748 X=-477.589 Y=-73.968 Z=91.15
 [BP_Alex_C_1] Client 0: 763 X=-478.714 Y=-75.394 Z=91.15
 
 [BP_Alex_C_0] Server: 784 true
 [BP_Alex_C_0] Server: 785 X=-480.521 Y=-77.792 Z=91.15
 [BP_Alex_C_1] Client 0: 787 X=-479.905 Y=-76.902 Z=91.15
 
 [BP_Alex_C_0] Server: 808 true
 [BP_Alex_C_0] Server: 808 X=-481.826 Y=-79.494 Z=91.15
 [BP_Alex_C_1] Client 0: 811 X=-483.147 Y=-81.197 Z=91.15
  
 [BP_Alex_C_0] Server: 833 true
 [BP_Alex_C_0] Server: 834 X=-483.307 Y=-81.426 Z=91.15
 [BP_Alex_C_1] Client 0: 837 X=-484.782 Y=-83.352 Z=91.15
 
 [BP_Alex_C_0] Server: 858 true
 [BP_Alex_C_0] Server: 858 X=-485.163 Y=-83.847 Z=91.15
 [BP_Alex_C_1] Client 0: 861 X=-486.384 Y=-85.446 Z=91.15
 
 [BP_Alex_C_0] Server: 881 true
 [BP_Alex_C_0] Server: 881 X=-487.002 Y=-86.245 Z=91.15
 [BP_Alex_C_1] Client 0: 884 X=-488.835 Y=-88.659 Z=91.15
 
 [BP_Alex_C_0] Server: 908 true
 [BP_Alex_C_0] Server: 909 X=-489.487 Y=-89.486 Z=91.15
 [BP_Alex_C_1] Client 0: 911 X=-491.969 Y=-92.74 Z=91.15
 
 [BP_Alex_C_0] Server: 940 true
 [BP_Alex_C_0] Server: 940 X=-492.721 Y=-93.705 Z=91.15
 [BP_Alex_C_1] Client 0: 943 X=-494.827 Y=-96.487 Z=91.15
  
 [BP_Alex_C_0] Server: 972 true
 [BP_Alex_C_0] Server: 973 X=-496.19 Y=-98.229 Z=91.15
 [BP_Alex_C_1] Client 0: 975 X=-499.669 Y=-102.74 Z=91.15
 
 [BP_Alex_C_0] Server: 5 true
 [BP_Alex_C_0] Server: 6 X=-499.66 Y=-102.755 Z=91.15
 [BP_Alex_C_1] Client 0: 8 X=-503.141 Y=-107.262 Z=91.15
 
 [BP_Alex_C_0] Server: 37 true
 [BP_Alex_C_0] Server: 38 X=-503.158 Y=-107.318 Z=91.15
 [BP_Alex_C_1] Client 0: 40 X=-506.669 Y=-111.869 Z=91.15
 
 [BP_Alex_C_0] Server: 70 true
 [BP_Alex_C_0] Server: 70 X=-506.652 Y=-111.875 Z=91.15
 [BP_Alex_C_1] Client 0: 73 X=-510.155 Y=-116.423 Z=91.15
 
 [BP_Alex_C_0] Server: 102 true
 [BP_Alex_C_0] Server: 102 X=-509.961 Y=-116.19 Z=91.15
 [BP_Alex_C_1] Client 0: 104 X=-513.603 Y=-120.894 Z=91.15
 
 [BP_Alex_C_0] Server: 134 true
 [BP_Alex_C_0] Server: 135 X=-513.393 Y=-120.668 Z=91.15
 [BP_Alex_C_1] Client 0: 137 X=-516.834 Y=-125.153 Z=91.15

edit: the following snip shows the function I am using. The Scale parameters are the magnitude of the velocity along the forward axis and the lateral axis. This is ran when an input event occurs (when you see the true or false entries in the logs above).

Honestly, this is a fascinating problem (netcode fascinates me). Any help would be very much appreciated on how to work more accurately with Unreal Engine’s built in solution here.

How are you handling client movement input? Is the client RPC’ing input to the server to handle and then multicast back out?

Are you using character movement component?

Your replay assumes that the Autonomous Proxy on the Client is the one being moved. This would be incorrect.

It is the Character on the Server that is being moved. The Player attached to the Server is moving his Character, and the Simulated Proxy on the Client is the one that is getting ahead of the Character on the Server.

The input mechanism is the Add Movement Input node (see the pic in my last post above).

I am relying on the CMC.

Also, for completeness of discussion, when the Player connected to the Client is moving the Autonomous Proxy on the Client, the Authority Proxy on the Server replicates the moves silky smooth with no issues at all.

There seems to be some confusion all around.

My character on the Client moves smoothly on the Server. My character on the Server moves smoothly on the Client, except when he stops.

This doesn’t make any sense in regards to CMC.

You are the client (autonomous). The servers pawn is the authority. Representative pawn of you on another client is a simulated proxy.

Autonomous: You.
Authoritative: Servers version of you.
Simulated: Some other players version of you.

For further clarity regarding CMC.
You, the connected client, do not move the servers pawn. You only move the autonomous pawn which is possessed by your controller. CMC replicates your inputs to the server and its auth proxy for your character mirrors the inputs given. It then compares results. If its position/state etc differs from yours it corrects, otherwise issues an ack. It then multicasts its results to all other connected clients. Their clients in turn move the simulated proxy of you on their simulation.

Therefore when using CMC the owning client is always ahead of the server and the representing sim. This is do to client-side prediction, the simple act of your client executing movement locally as inputs are generated.

My terminology wasn’t precise in my first post. So let me try once again.

When I have my controller attached to the Client window (the second instance of the game launched by the editor), I am driving the Autonomous Proxy on the Client and the Authoritive Proxy on the Server follows silky smooth. No problem.

When I have my controller attached to the Server window (the first instance of the game launched by the editor), I am driving the Authoritive Character on the Server and the Simulated Proxy on the Client predicts slightly too much and gets out of sync, resulting in the rubberbanding.

Also, when running a Server and two Clients, when I have my controller attached to Client #1 window, I am driving the Autonomous Proxy on Client #1, and the matching Simulated Proxy on Client #2 predicts slightly too much and gets out of sync, resulting in rubberbanding on Client #2.

The problem appears that Clients receiving replication of movement and position from the Server are predicting a bit too much.

But why does this happen at all? It’s literally the same code in a single Windows process. (And I have tried running them in separate processes, that didn’t help.)

When I have my controller attached to the Client window (the second instance of the game launched by the editor), I am driving the Autonomous Proxy on the Client and the Authoritive Proxy on the Server follows silky smooth. No problem.

This is the correct flow and should work 100% unless there’s severe packet loss (>5%). High latency can causes issues here as well. You can’t correct for either.

When I have my controller attached to the Server window (the first instance of the game launched by the editor), I am driving the Authoritive Character on the Server and the Simulated Proxy on the Client predicts slightly too much and gets out of sync, resulting in the rubberbanding.

The issue here is you’re directly driving the server, which never happens in any setup. The reason for the rubberbanding is the client #1 isn’t inputting anything, thus its always reporting to the server no moves. So when the server does its comparison there’s always a correction.

CMC relies on the client inputting a move and moving locally. Then the server moves and compares the end results. It only corrects (rubberbands) when the server outcome differs from the clients.

Also, when running a Server and two Clients, when I have my controller attached to Client #1 window, I am driving the Autonomous Proxy on Client #1, and the matching Simulated Proxy on Client #2 predicts slightly too much and gets out of sync, resulting in rubberbanding on Client #2.

Sims do not predict. They are literally sent end locations/rotations/states etc that the server calculated. So the servers end result is what’s used. Yet, also sims will use a networked smoother interpolation.

This is all documented in the CMC docs.

Post a video showing the desync in your 3rd scenario. Sims should always match the server…eventually (connection issues aside).

It isn’t clear to me why you said driving the Server never happens in any setup. How else do you possess Pawns on a Listening Server?

I will try to create a video, but I am not sure how to do that at the moment. Also, if Client #2 Simulated Proxy isn’t predicting, that is fine, but why is it rubberbanding when it comes to a stop?

The pattern of this problem seems to be that only Simulated Proxies are rubberbanding, not Autonomous Proxies or Authority.

In this video, the Server is in the top left corner, Client #1 is in the top right corner, and Client #2 is along the bottom. I am possessing the Autonomous Proxy in Client #1. Notice it doesn’t rubberband in the Server, but it rubberbands in Client #2 when it stops.

EDIT:

I created a blank project from scratch. I added only the movement joystick on my controller in the enhanced input system, nothing else. The following is the only script I added to my character blueprint. The character blueprint has literally only the mesh skeleton in it with a camera, and the CMC, but no animations added yet. It exhibits the same rubberbanding on the Simulated Proxy only.

A little out of pocket… Poll your primary references.

Use the controller reference when initializing IMC.

Use Control Rotation for movement input.

It’s quite possible how you’re handling movement input is the issue.

I made changes along what you showed, no difference.

There was a statement in the CMC docs about Simulated Proxies predicting movement and it seemed to be related to when the Simulated Proxy on a Client did not get a net update from the Server. I disabled this feature and the rubberbanding ceased, but the movement by the Simulated Proxies was randomly ever so slightly stuttering.

From the section “Replicating Movement to Simulated Proxies”, under the sub section “Network Smoothing”:

Network smoothing is a process that smooths out this motion, interpolating the character gradually from the source location towards a target location instead of snapping it to the target instantly. The source location is given by the character’s current position, while the target is given by the client prediction data.

I think that this is saying that the Simulated Proxies have a predicted location that they will move toward until told otherwise at the next net update. And from the material in the preceding sections, it would appear that this client prediction data is coming from the Server. But it really isn’t clear and I would have to spend a lot of time in the code to figure out what it is actually doing.

The bottom line is why is the Simulated Proxies off so much that they rubberband on stop movement out of box like this?

I spent some time looking at videos that showed the problem but they were not as pronounced due to the legs animating and the smoothing time was longer.

So I added leg animation and I lengthened the smoothing from 0.1s to 0.5s. I can see the problem still, but it is well hidden in the animations now.

Based upon what I have seen in other tutorial videos, I think this is an artifact of UE5 CMC replication framework.

ue5.1 third person project

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.