The movement synchronization processing flow is give blew.
Step 1: Client receives player’s input.
Step 2: Client executes PerformMovement(in Tick) to simulate player input.
Step 3: Client caches NewMove in array SavedMoves of FNetworkPredictionData_Client_Character. Each move data save as struct FSavedMove_Character, including speed, rotation…
Step 4: Client caches NewMove to PendingMove if sending delta time is too small.
Step 5: Client finds the oldest unacknowledged important move (OldMove) and call ServerMoveOld to send OldMove to server if OldMove is exist.
Step 6: Client calls ServerMoveDual to send two moves simultaneously if PendingMove is exist.
Step 7: Client calls ServerMove to send one NewMove if PendingMove is not exist.
Step 8: Server receives client moves data.
Step 9: Server calls ClientAckGoodMove to tell client NewMove is valid and replicates movement to all simulated clients.
Step 10: Server calls ClientAdjustPosition to tell client NewMove is invalid and send correct position to client.
Step 11: Client receives ClientAckGoodMove and saves last correct move to LastAckedMove.
Step 12: Client will remove all moves in SaveMoves list before LastAckedMove.
Step 13: Client receives ClientAdjustPosition and adjusts character’s position from server data.
Step 14: Client will remove all moves in SaveMoves list before this uncorrect move. Then, client will replay all unacknowledged moves to make character perform smoothly as possible (ClientUpdatePositionAfterServerUpdate).
For simulated character, while receiving replicated movement (OnRep_ReplicatedMovement), client will call SmoothCorrection to adjust client position smoothly to server position.
[1] Gameplay Framework Quick Reference