fix(motion): #39 — wire ApplyServerControlledVelocityCycle into player-remote path
Visual verify with the proper Shift-toggle scenario revealed that fix #1's
ApplyPlayerLocomotionRefinement was UNREACHABLE for player remotes — the
L.3 M2 routing at line 3626+ returns at line 3755, BEFORE the call site
at line 3879. The legacy NPC-only block that compute server velocity +
calls ApplyServerControlledVelocityCycle never runs for players.
[UPCYCLE_PLAYER] count = 0 in launch-39-fix2.log and launch-39-diag2.log
proved the velocity-fallback path was completely dead code for players.
Wire-level evidence (launch-39-diag2.log):
- [FWD_WIRE] for retail actor 0x50000001 over a clean Hold-W-press-Shift-
release-Shift-release-W test shows ONLY Ready→Run and Run→Ready
transitions. NO Walk wire transitions for the Shift toggle. So retail's
outbound MoveToState logic does NOT emit a fresh packet on HoldKey-only
changes (refutes the launch-39-fix2 hypothesis that both directions
emit; the earlier fix2 log's many Walk↔Run transitions came from
W press/release cycles WITH Shift held continuously, not from Shift
toggling alone).
- [VEL_DIAG] over the same test shows clear walk-pace (~2.5 m/s) and
run-pace (~11.5 m/s) periods, so the actor's actual physical speed
IS changing despite the wire silence.
Fix: in OnLivePositionUpdated's L.3 M2 player-remote block, after the
near-enqueue / far-snap routing and before the early `return`, compute
synth velocity from PrevServerPos / LastServerPos and call into
ApplyServerControlledVelocityCycle. The function's internal routing
(commit 8fa04af) sends player remotes through ApplyPlayerLocomotionRefinement
which has the 500 ms UM grace + forward-direction + hysteresis logic
to flip Run↔Walk only when no fresh UM is authoritative.
Build clean. Diagnostics: [UPCYCLE_SRC] now prints `src=synth-player`
when the player-remote path fires (distinct from `src=synth`/`src=wire`
in the legacy NPC path).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bb026b7991
commit
2653b307c7
1 changed files with 45 additions and 0 deletions
|
|
@ -3745,6 +3745,51 @@ public sealed class GameWindow : IDisposable
|
|||
isMovingTo: false,
|
||||
currentBodyPosition: rmState.Body.Position);
|
||||
}
|
||||
// #39 fix-3 (2026-05-06): velocity-fallback cycle refinement
|
||||
// for player remotes. Wire-level evidence (`launch-39-diag2.log`):
|
||||
// when retail's actor toggles Shift while a direction key
|
||||
// is held, retail's outbound MoveToState logic does NOT
|
||||
// emit a fresh packet (only Ready ↔ Run UMs visible in
|
||||
// `[FWD_WIRE]`, despite a clear walk-pace ↔ run-pace
|
||||
// velocity transition in `[VEL_DIAG]`). ACE has nothing
|
||||
// to broadcast → no UM arrives at the observer → cycle
|
||||
// sticks at whatever the last UM set. Compute the
|
||||
// synth-velocity here in the player-remote path AND
|
||||
// call into ApplyServerControlledVelocityCycle, which
|
||||
// routes through the direction-preserving + UM-grace
|
||||
// ApplyPlayerLocomotionRefinement helper (added in
|
||||
// commit 8fa04af).
|
||||
//
|
||||
// The legacy non-player block below (3759+) covers NPCs
|
||||
// and is gated `!IsPlayerGuid`; this block fills the
|
||||
// matching gap for players.
|
||||
if (rmState.PrevServerPosTime > 0.0)
|
||||
{
|
||||
double nowSecVel = rmState.LastServerPosTime;
|
||||
double dtPos = nowSecVel - rmState.PrevServerPosTime;
|
||||
if (dtPos > 0.001)
|
||||
{
|
||||
var synthVel = (worldPos - rmState.PrevServerPos) / (float)dtPos;
|
||||
rmState.ServerVelocity = synthVel;
|
||||
rmState.HasServerVelocity = true;
|
||||
|
||||
if (_animatedEntities.TryGetValue(entity.Id, out var aeForVel)
|
||||
&& aeForVel.Sequencer is not null)
|
||||
{
|
||||
if (System.Environment.GetEnvironmentVariable("ACDREAM_REMOTE_VEL_DIAG") == "1")
|
||||
{
|
||||
System.Console.WriteLine(
|
||||
$"[UPCYCLE_SRC] guid={update.Guid:X8} src=synth-player");
|
||||
}
|
||||
ApplyServerControlledVelocityCycle(
|
||||
update.Guid,
|
||||
aeForVel,
|
||||
rmState,
|
||||
synthVel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sync the visible entity to the body — overrides the unconditional
|
||||
// entity.Position = worldPos snap at the top of this function.
|
||||
// For the far-snap branch this is a no-op (body == worldPos); for
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue