In UE4’s default movement, it basically sends hard updates of both position and velocity to the client every-time the object is moved on the server. Believe it or not the default implementation has a few problems of it’s own. The primary one being that if an object is completely still, then receives a collision that occurs ONLY on the client side - it will move on the client and not on the server. It’s not until the server simulation makes the object move again that the client comes back into sync via the replicated position / velocity data. This of course only occurs in physics-simulating actors, which can receive collision and move as a result. Now, you could force replication updates to go out every frame to fix the sync problems - but then the client won’t be able to move, because it will be receiving updates from the server, then trying to move, then receiving old updates etc.
Anyway… In a latent situation (aka any online connection) - the position / velocity updates are received on the client after they have happened. Although Velocity is also updated on the client for smoother transitions between updates, the simulation can still vary enough even over the course of a few frames to result in a noticeable position update. Unfortunately since UE4 isn’t deterministic you can’t sync time-stamps, simulations or anything, so we’re forced really to have these updates regardless of the networking solution you use.
To get around the noticeable position update (aka snapping), you can take the approach that character movement does and ONLY hard-update the position of the collision capsule. At that point, you calculate the offset from the old to the new position, and apply that as a relative position to the actual rendered mesh (and of course it’s children, which also usually has the camera). You then work out roughly how long you have until the next packet arrives, and over that time you then interpolate the relative transform of the mesh back to the collision capsule. If the time between updates is long enough and there’s a huge difference, you get the ‘rubber banding’ effect. Unfortunately there’s not really a way around that so it’s a ‘pick your poison’ kind of deal.
Going back a bit - I mentioned about the simulation differences. So in order to keep rubber banding and snapping to a minimum, you have to send the input data from the client to the server, and run the same data on the server. You sync them up with a rough time-stamp (which is hard enough as it is), and the server calculates whether it’s local simulation difference from the clients one. If it does, then it sends the hard position update and the same timestamp back to the client. The reason you have to re-simulate on the client is because by the time it receives this update, it’s already progressed in the game. If you applied the position update directly at the current client time then they would be jumping back to where they were previously, the client would always be behind and would be snapping constantly. This is where prediction comes in.
To get around that therefore, you have to replay all the moves from the received time-stamp back up to the most-recent move, then figure out where you think you will be on the server at time now. You re-simulate the input and calculate the positions again (but don’t send the recalculations to the server), and it’s rinse-and-repeat. Essentially, because you don’t know what kind of input data you need or what the simulation for movement actually is - there’s no one-size-fits-all solution. Character Movement Component and it’s prediction stuff is written veryspecifically for that style of movement. Since everything in the character can be inferred from it’s current velocity (like it’s rotation etc.) - they don’t actually send input data, they send the ‘acceleration’ vector instead to save a bit of bandwidth (which they can do, since the properties that govern the movement are either constant or replicated themselves). The acceleration vector is also modified by the navigation stuff for AI etc, so they get all that replicated for free essentially.
But yeah… anything more complex and it becomes a minefield. Went a bit overboard on this explanation but hopefully this explains the complexity a little
I tried for months and months to synchronize a physics-simulating pawn, but it just never would get close enough. I’m eager to see Rama’s latest solution which I believe is server-authoritative - but in the meantime I’m rewriting mine to use a similar system to Character Movement, calculating all collision impulses and movement in the movement itself and not relying on the physics engine at all.
EDIT: Just quickly adding, this was (and still is) my attempt to get a server-authoritative networked physics component working with history states and prediction etc. I still can’t even get my timestamps to sync correctly, which is a minefield in itself.