Player movement uses a hybrid transport model. Clients sendDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/kenzz55/ue5-iocp-mmo-server/llms.txt
Use this file to discover all available pages before exploring further.
C_MoveReq over UDP—connectionless, low-latency, drop-tolerant—and the server responds with authoritative position updates batched into S_MoveBatchNotify datagrams delivered over the same UDP socket. TCP carries only the reliable subset of game events (combat acknowledgements, inventory operations, chat). This split keeps the TCP stream free from the high-frequency movement traffic that would otherwise head-of-line block more important packets.
MovementReplication Class
MovementReplication runs a dedicated background thread that wakes every MovementNotifyIntervalMs = 100 ms and dispatches all pending snapshots in a single pass.
Publish() inserts or replaces the latest snapshot for sessionId in latestSnapshots_. If a snapshot already exists for that session it is overwritten—old positions are never queued, only the most recent state matters. This replacement strategy is what keeps replication traffic flat under high player density: each session contributes at most one entry per dispatch cycle regardless of how many movement inputs arrived since the last cycle.
Every 100 ms the dispatch thread swaps latestSnapshots_ under the mutex, then invokes DispatchCallback with the collected map. IOCPServer::DispatchMovementReplicationBatch is the registered callback; it iterates snapshots, resolves AoI peers for each mover, and packs entries into UDP datagrams.
MovementSnapshot Fields
Session::StepMovement() during the PlayerMovement tick phase and published to MovementReplication immediately after the step completes.
Datagram Batching
Snapshots destined for the same AoI peer are packed into a single UDP datagram up toMovementNotifyDatagramBytes = 1200 bytes. This stays well under the typical Ethernet MTU of 1,500 bytes to avoid IP fragmentation.
| Constant | Value | Role |
|---|---|---|
MovementNotifyIntervalMs | 100 ms | Dispatch cycle period |
MovementNotifyDatagramBytes | 1,200 B | Maximum UDP payload per datagram |
MaxMovementReplicationPendingSnapshots | 65,536 | Snapshots dropped beyond this high-water mark |
PacketHeader + 12-byte MoveBatchNotifyHeader + 40 bytes per MoveBatchNotifyEntry:
Client Prediction and Reconciliation
The server implements a standard predict-and-reconcile loop compatible with Unreal Engine’s built-in Character Movement Component prediction model. Client side:- Apply input locally without waiting for a server response.
- Stamp the input with a monotonically increasing
input_sequence. - Send
C_MoveReqover UDP. - Buffer the unacknowledged input locally.
HandleUdpMove()receivesC_MoveReq, enqueues anApplyMoveCommandinto the game simulation viaEnqueueMoveCommand().- During the PlayerMovement tick phase,
StepMovement()integrates the input and produces a newMovementSnapshot. lastProcessedInputSequencein the snapshot is set to the sequence number of the input that was just applied.- The snapshot is published to
MovementReplicationand also echoed directly to the mover asS_MoveNotifyover TCP (as a reliable authoritative correction).
last_processed_input_sequence in S_MoveNotify or S_MoveBatchNotify, it discards all locally buffered inputs with sequence ≤ that value and re-simulates from the authoritative position using the remaining unacknowledged inputs. This eliminates rubber-banding caused by latency while keeping the server fully authoritative.
Server-Side Movement Physics
StepMovement() applies the same acceleration/friction model used by Unreal’s UCharacterMovementComponent, ensuring the server and client simulate identical trajectories for the same input sequence.
Config.h:
| Constant | Value | Meaning |
|---|---|---|
MovementMaxWalkSpeed | 500 UU/s | Maximum ground speed |
MovementMaxAcceleration | 800 UU/s² | Acceleration applied when input is non-zero |
MovementBrakingDecelerationWalking | 750 UU/s² | Deceleration applied when no input |
MovementGroundFriction | 5.0 | Friction scale factor applied each tick |
MovementBrakingFrictionFactor | 1.0 | Additional multiplier on braking deceleration |
MovementMinAnalogWalkSpeed | 150 UU/s | Minimum speed for partial analog stick input |
MovementInputDeadZone | 0.01 | Input magnitudes below this are treated as zero |
MovementInputTimeoutMs | 250 ms | After this many ms without a C_MoveReq, input clears |
MaxMovementDeltaSeconds | 0.05 s | Clamps delta to prevent large steps from network jitter |
C_MoveReq arrives for more than MovementInputTimeoutMs = 250 ms, the session’s movement input is cleared and the character decelerates to a stop under MovementBrakingDecelerationWalking.