Two README known issues share one root cause:
Landing from a jump can occasionally leave the player gliding across the floor.
The player may drift while standing still.
The district acknowledges every client move as good and never corrects one. There is no server-side authoritative movement simulation, so client prediction error is never reined in — and residual velocity through a landing, or slow integration drift while idle, is exactly the error class that a correction would absorb.
Verified on main (84cbb97).
The ack is unconditional
DistrictServer/DistrictServer.cpp:10744-10767:
const bool shouldAck =
movementAckEnabled &&
movement.HasTimeStamp;
bool ackSent = false;
if (shouldAck)
{
std::vector<std::uint8_t> ack =
ApbUdp::BuildUnreliableActorFloatFieldPacket(
account->AllocateServerPacketId(),
kControllerChannel,
kFieldClientAckGoodMove,
kPlayerControllerFieldMax,
movement.TimeStamp);
...
The only conditions are a config flag (APB_ENABLE_MOVEMENT_ACK, default true) and the presence of a timestamp. The move's content — position, acceleration, flags — is never evaluated before it is accepted.
There is no correction RPC anywhere in the tree
Repo-wide grep for AdjustPosition|ClientSetLocation|ClientSetRotation returns zero matches. In stock UE3 PlayerController.ServerMove the server simulates the submitted move against its own authoritative pawn and then either calls ClientAckGoodMove(TimeStamp) or ClientAdjustPosition(...). Only the first half of that pair exists here (kFieldClientAckGoodMove = 59, :2631).
So this is not "the correction is computed wrong" — the correction path does not exist. The client is authoritative over its own position in practice.
The server has no authoritative position; it differences the client's
The one place the server derives motion state, it does so from client-reported values (:10797-10821):
const std::int32_t currentZ = movement.ClientLocationZ;
if (state.HasMotionSample)
{
const float deltaTime = movement.TimeStamp - state.LastTimeStamp;
if (deltaTime > 0.001f && deltaTime < 0.500f)
{
const std::int32_t deltaZ = currentZ - state.LastLocationZ;
const float estimatedVelocityZ =
static_cast<float>(deltaZ) / deltaTime;
Both the position and the clock are the client's. This is fine for its actual purpose (reconstructing the hard-landing winded trigger) but it confirms there is no independently simulated pawn to compare a move against, which is the prerequisite for issuing a correction.
Consistent with the README, which already lists "Movement correction and prediction timing" under Experimental.
Why this produces the two reported symptoms
- Gliding after landing: the client's predicted horizontal velocity survives the landing transition; with no
ClientAdjustPosition to snap it to a server-simulated resting position, the slide plays out to completion.
- Drift while standing still: small per-frame prediction/physics residue accumulates monotonically because nothing ever resets the client to an authoritative position. A stock server corrects this invisibly at the first threshold breach.
Both are consequences of the same missing half of the ServerMove contract, so they are filed together — a correction path would likely close both.
Suggested direction
This is a large piece of work; a staged approach that stays honest about authority:
- Track a server-side pawn position per account, seeded at spawn from the selected spawn zone, advanced by the accepted moves (the decoder already yields position, acceleration and flags —
ApbUdp.h:467-521).
- Compare the client's reported position against that track; when the divergence exceeds a tolerance, emit
ClientAdjustPosition instead of ClientAckGoodMove. Field number is not yet established for this build and will need a probe.
- Keep it behind a config flag alongside
APB_ENABLE_MOVEMENT_ACK so it can be disabled during regression testing.
- Only then consider ground/collision awareness, which is the part that needs real level geometry.
Steps 1-3 need no map geometry and may be enough to damp idle drift on their own.
Line numbers are from 84cbb97. No live-client session was run for this report; the symptoms are quoted from the README rather than independently reproduced, and the causal link between the missing correction and each symptom is reasoned from the code, not measured.
Two README known issues share one root cause:
The district acknowledges every client move as good and never corrects one. There is no server-side authoritative movement simulation, so client prediction error is never reined in — and residual velocity through a landing, or slow integration drift while idle, is exactly the error class that a correction would absorb.
Verified on
main(84cbb97).The ack is unconditional
DistrictServer/DistrictServer.cpp:10744-10767:The only conditions are a config flag (
APB_ENABLE_MOVEMENT_ACK, defaulttrue) and the presence of a timestamp. The move's content — position, acceleration, flags — is never evaluated before it is accepted.There is no correction RPC anywhere in the tree
Repo-wide grep for
AdjustPosition|ClientSetLocation|ClientSetRotationreturns zero matches. In stock UE3PlayerController.ServerMovethe server simulates the submitted move against its own authoritative pawn and then either callsClientAckGoodMove(TimeStamp)orClientAdjustPosition(...). Only the first half of that pair exists here (kFieldClientAckGoodMove = 59,:2631).So this is not "the correction is computed wrong" — the correction path does not exist. The client is authoritative over its own position in practice.
The server has no authoritative position; it differences the client's
The one place the server derives motion state, it does so from client-reported values (
:10797-10821):Both the position and the clock are the client's. This is fine for its actual purpose (reconstructing the hard-landing winded trigger) but it confirms there is no independently simulated pawn to compare a move against, which is the prerequisite for issuing a correction.
Consistent with the README, which already lists "Movement correction and prediction timing" under Experimental.
Why this produces the two reported symptoms
ClientAdjustPositionto snap it to a server-simulated resting position, the slide plays out to completion.Both are consequences of the same missing half of the ServerMove contract, so they are filed together — a correction path would likely close both.
Suggested direction
This is a large piece of work; a staged approach that stays honest about authority:
ApbUdp.h:467-521).ClientAdjustPositioninstead ofClientAckGoodMove. Field number is not yet established for this build and will need a probe.APB_ENABLE_MOVEMENT_ACKso it can be disabled during regression testing.Steps 1-3 need no map geometry and may be enough to damp idle drift on their own.
Line numbers are from
84cbb97. No live-client session was run for this report; the symptoms are quoted from the README rather than independently reproduced, and the causal link between the missing correction and each symptom is reasoned from the code, not measured.