fix(R3-W6b): entry-cache the apply pass's axis reads — the 'press W and stop instantly' regression
User live report (the R3 visual pass): pressing W stopped the local player instantly (retail observers saw run + rubber-band back — our AP never moved); S/strafe appeared to work. The edge tracer + a harness-with-echo repro converged on the funnel: ApplyInterpretedMovement live-read InterpretedState.ForwardCommand AFTER the style dispatch. The style dispatch SUCCEEDS against the real MotionTableDispatchSink (GetObjectSequence Branch 1 style==target → success — verified in the raw @298636), which gates the ModifyInterpretedState write → InterpretedMotionState::ApplyMotion's style branch resets forward_command to Ready UNCONDITIONALLY (raw 0051ea6c — our port is verbatim). Retail SELF-HEALS: its compiled apply pass reads the axis fields into registers BEFORE the style call, so the fwd dispatch re-applies the pre-reset command and the field recovers within the pass — proven by our own 183-case live-retail observer trace (the fwd dispatch carries the wire's RunForward after the style dispatch on the same UM). The BN pseudo-C's apparent post-style field reads at 0x528687 are decompiler rendering of hoisted registers — the same artifact class as the A1 polarity inversion. Under the live-field read, every apply pass (the ~10Hz ACE UM echo via ApplyServerRunRate included) dispatched Ready and left the field permanently Ready; the controller's per-frame get_state_velocity then zeroed the body. The 183-case suite could not catch it: its RecordingSink's return value doesn't mirror the real sink's style-success, so the resetting state-write never ran under test. Fix: entry-cache fwd/sidestep/turn commands+speeds (and the my_run_rate read) before the style dispatch — restoring the S2a funnel's original semantics through the W5 merge. 3 new regression tests bind the REAL sink over a real sequencer (the masked condition): a full apply pass self-heals forward; 2 seconds of held-W with echo cadence covers meters not centimeters; Shift walk↔run toggling survives mid-transition echoes. Full suite: 3,734 passed. Temp diagnostics removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
778a2b5385
commit
30115d96aa
2 changed files with 208 additions and 11 deletions
|
|
@ -2729,12 +2729,33 @@ public sealed class MotionInterpreter : IMotionDoneSink
|
|||
_ = cancelMoveTo;
|
||||
_ = allowJump;
|
||||
|
||||
if (InterpretedState.ForwardCommand == MotionCommand.RunForward)
|
||||
MyRunRate = InterpretedState.ForwardSpeed;
|
||||
// ENTRY-CACHE all axis values BEFORE the style dispatch (W6 stop-bug
|
||||
// fix, 2026-07-03). The style dispatch's success path runs
|
||||
// InterpretedMotionState::ApplyMotion(style), whose style branch
|
||||
// resets forward_command to Ready UNCONDITIONALLY (raw 0051ea6c —
|
||||
// verbatim). Retail SELF-HEALS because its compiled apply pass reads
|
||||
// the axis fields into registers before the style call, so the fwd
|
||||
// dispatch re-applies the pre-reset command (proven by our own
|
||||
// 183-case live-retail observer trace: the fwd dispatch carries the
|
||||
// wire's RunForward after the style dispatch on the same UM — the
|
||||
// BN pseudo-C's apparent post-style field reads at 0x528687 are
|
||||
// decompiler rendering of hoisted registers, the same artifact
|
||||
// class as the A1 polarity). A live-field read here dispatched
|
||||
// READY after every style apply, leaving the field permanently
|
||||
// Ready — the "press W and stop instantly" W6 regression.
|
||||
uint entryFwdCmd = InterpretedState.ForwardCommand;
|
||||
float entryFwdSpeed = InterpretedState.ForwardSpeed;
|
||||
uint entrySideCmd = InterpretedState.SideStepCommand;
|
||||
float entrySideSpeed = InterpretedState.SideStepSpeed;
|
||||
uint entryTurnCmd = InterpretedState.TurnCommand;
|
||||
float entryTurnSpeed = InterpretedState.TurnSpeed;
|
||||
|
||||
if (entryFwdCmd == MotionCommand.RunForward)
|
||||
MyRunRate = entryFwdSpeed;
|
||||
|
||||
DispatchInterpretedMotion(currentStyle, 1.0f, sink);
|
||||
|
||||
if (!contact_allows_move(InterpretedState.ForwardCommand))
|
||||
if (!contact_allows_move(entryFwdCmd))
|
||||
{
|
||||
DispatchInterpretedMotion(MotionCommand.Falling, 1.0f, sink);
|
||||
}
|
||||
|
|
@ -2745,19 +2766,16 @@ public sealed class MotionInterpreter : IMotionDoneSink
|
|||
}
|
||||
else
|
||||
{
|
||||
DispatchInterpretedMotion(
|
||||
InterpretedState.ForwardCommand, InterpretedState.ForwardSpeed, sink);
|
||||
if (InterpretedState.SideStepCommand == 0)
|
||||
DispatchInterpretedMotion(entryFwdCmd, entryFwdSpeed, sink);
|
||||
if (entrySideCmd == 0)
|
||||
DispatchStopInterpretedMotion(MotionCommand.SideStepRight, sink);
|
||||
else
|
||||
DispatchInterpretedMotion(
|
||||
InterpretedState.SideStepCommand, InterpretedState.SideStepSpeed, sink);
|
||||
DispatchInterpretedMotion(entrySideCmd, entrySideSpeed, sink);
|
||||
}
|
||||
|
||||
if (InterpretedState.TurnCommand != 0)
|
||||
if (entryTurnCmd != 0)
|
||||
{
|
||||
DispatchInterpretedMotion(
|
||||
InterpretedState.TurnCommand, InterpretedState.TurnSpeed, sink);
|
||||
DispatchInterpretedMotion(entryTurnCmd, entryTurnSpeed, sink);
|
||||
return; // retail early return — no idle-stop this call
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue