fix(R4): #161 remote landing stuck in falling pose - apply-pass params decode
Retail's apply_interpreted_movement (0x00528600) does NOT dispatch with ctor-default MovementParameters: the BN decomp smears the bitfield store into the mush expression at raw 305778 - (word & 0x37ff) | cancelMoveTo<<15 | disableJump<<17 - which CLEARS SetHoldKey/ModifyInterpretedState/ CancelMoveTo. ACE MotionInterp.cs:444-449 confirms independently. Three legs fixed, all retail decode, no adaptations added: 1. ApplyInterpretedMovement now builds the pass params retail actually uses (ModifyInterpretedState=false is load-bearing): no dispatch in the pass writes InterpretedState, so the airborne Falling substitution PRESERVES the wire's forward command and HitGround's re-apply dispatches it - the motion table plays the Falling->X landing link. The W6 entry-cache (built on the wrong "retail self-heals via hoisted registers" theory) is deleted; live reads are retail semantics. Raw arg3 decoded as DisableJumpDuringLink -> every (N,0) caller means allowJump=true; all 9 caller polarities fixed. copy_movement_from's current_style copy (raw 0051e757) added to the UM flat-copy. 2. Both GameWindow landing blocks cleared the Gravity state bit BEFORE Motion.HitGround(), whose verbatim state&0x400 gate then no-opped the whole retail re-apply; the UP-driven landing block never called HitGround at all. Both now run HitGround (minterp then moveto, MovementManager::HitGround 0x00524300 order) with Gravity still set, then do the DR bookkeeping clear (register row AP-81 added for the remaining flag dance, retire in R6). 3. K-fix17's forced SetCycle (both copies) deleted: it executed every landing but read the leg-1-clobbered ForwardCommand (0x40000015) and re-set the very Falling cycle it meant to clear. Tests: new HitGround_AfterFall_RedispatchesPreservedForward_ExitsFalling lifecycle pin; AirborneBody state assertion flipped (it had pinned the bug value). Suite 3,964 green incl. the 183-case retail-observer trace. Filed #164 (action-replay Autonomous bit, no current consumer). Awaiting live verify: stand-still landing must exit the falling pose with zero wire input. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
790938392f
commit
b1cf01029a
6 changed files with 280 additions and 199 deletions
|
|
@ -73,31 +73,60 @@ actually steps at (Player_Move.cs / Creature GetRunRate usage).
|
|||
|
||||
## #161 — Remote jump landing holds the falling pose (no landing anim)
|
||||
|
||||
**Status:** OPEN (2026-07-03; retested after TS-40/`41006e79` — still
|
||||
broken; user: stuck in falling until the char moves or the window loses
|
||||
focus).
|
||||
**Evidence (launch.log, session bklydthrr, guid 0x50000001):** `VU.land`
|
||||
fires; Falling completions (0x40000015) flow; but the FIRST post-landing
|
||||
UM still reports `seq now motion=0x40000015` — the landing block's reset
|
||||
(rm.Motion.HitGround() apply + K-fix17 SetCycle) did NOT move the cycle
|
||||
off Falling. Recovery happened only via SUBSEQUENT wire UMs (the mover
|
||||
pressed keys); a stand-still landing gets none → stuck forever. The
|
||||
focus-change unstick hints a frame-advance/big-dt interaction as well.
|
||||
**Prime suspect:** the funnel's airborne branch dispatches Falling via
|
||||
DoInterpretedMotion with ctor-default ModifyInterpretedState=true →
|
||||
InterpretedState.ForwardCommand becomes 0x40000015; HitGround's
|
||||
apply_current_movement (interpreted branch for remotes) then re-applies
|
||||
the interpreted state — i.e. re-dispatches FALLING — instead of the
|
||||
pre-fall wire command. Check retail: does apply_interpreted_movement's
|
||||
falling dispatch (raw ~305730, fresh local var_2c params) really write
|
||||
interpreted fwd (bit 0x4000 in the local's 0x1EE0F default) — and if so,
|
||||
what exits Falling on a retail REMOTE landing (HitGround raw @305949
|
||||
apply_current_movement(this, 1, ...) — the arg2=1 semantics)? Also
|
||||
verify K-fix17's SetCycle actually executes there (add a probe) and
|
||||
whether it lost a fight with the same-frame HitGround apply ordering.
|
||||
**Where:** GameWindow remote landing block (`VU.land` region);
|
||||
MotionInterpreter.ApplyInterpretedMovement falling branch;
|
||||
HitGround/apply_current_movement.
|
||||
**Status:** FIX LANDED (2026-07-03) — awaiting user live verify.
|
||||
**Root cause (three legs, all retail-decode):**
|
||||
1. **Apply-pass params (the big one):** retail's `apply_interpreted_movement`
|
||||
runs ALL its dispatches with `ModifyInterpretedState=false` — the ctor
|
||||
default 0x1EE0F is REWRITTEN by a bitfield store the BN decomp smears
|
||||
into the mush expression at raw 305778 (`(word & 0x37ff) |
|
||||
cancelMoveTo<<15 | disableJump<<17`; 0x37ff clears SetHoldKey/
|
||||
ModifyInterpretedState/CancelMoveTo). ACE MotionInterp.cs:444-449
|
||||
confirms. Our pass dispatched with ctor defaults (modify=true), so the
|
||||
airborne Falling substitution CLOBBERED `InterpretedState.ForwardCommand`
|
||||
→ HitGround's re-apply re-dispatched Falling instead of the preserved
|
||||
pre-fall wire command. (The handoff's "HitGround raw @305949" was a
|
||||
mislabel — 305949 is inside `move_to_interpreted_state`; the real
|
||||
HitGround is 0x00528ac0 → `apply_current_movement(0,0)`.) Bonus decode:
|
||||
raw arg3 = DisableJumpDuringLink, so every `(N, 0)` caller means
|
||||
allowJump=TRUE — all caller polarities fixed. The W6 entry-cache (whose
|
||||
"retail self-heals via hoisted registers" theory was this same
|
||||
misreading) is deleted; live field reads are retail semantics now that
|
||||
the pass cannot write state.
|
||||
2. **Landing-order:** both GameWindow landing blocks cleared the Gravity
|
||||
state bit BEFORE `rm.Motion.HitGround()`, whose verbatim `state & 0x400`
|
||||
gate then no-opped the entire retail re-apply (retail never clears
|
||||
GRAVITY on landing). The UP-driven landing block never called HitGround
|
||||
at all. Both now: transients → `Motion.HitGround()` → `MoveTo.HitGround()`
|
||||
→ THEN the DR bookkeeping gravity-clear.
|
||||
3. **K-fix17 answered (user Q3):** its SetCycle DID execute — and re-set
|
||||
the Falling cycle, because it read the leg-1-clobbered ForwardCommand
|
||||
(0x40000015 ≠ 0, so the Ready fallback never fired). Both SetCycle
|
||||
adaptation blocks deleted — superseded by the retail HitGround path,
|
||||
which also plays the proper Falling→X landing LINK animation SetCycle
|
||||
never did. Also fixed en route: `copy_movement_from` omitted
|
||||
`current_style` (raw 0051e757 copies it first) — the landing re-apply's
|
||||
style dispatch read a stale style.
|
||||
**Tests:** `MotionInterpreterFunnelTests.HitGround_AfterFall_
|
||||
RedispatchesPreservedForward_ExitsFalling` (full lifecycle) + the flipped
|
||||
`AirborneBody_NoCycleDispatches_OnlyTurnStop` state assertion (it had
|
||||
PINNED the bug value 0x40000015 — noted in-file). Suite 3,964 green.
|
||||
**Verify:** retail mover jumps in place near acdream observer → lands →
|
||||
legs exit falling pose into idle WITHOUT pressing any key; jump while
|
||||
running → lands into run cycle.
|
||||
|
||||
## #164 — UM action-replay dispatches drop the per-action Autonomous bit
|
||||
|
||||
**Status:** OPEN (2026-07-03, filed during #161)
|
||||
**Finding:** retail's `move_to_interpreted_state` action loop sets the
|
||||
dispatch params' Autonomous bit (0x1000) from each action's autonomy flag
|
||||
(raw 305982: `var_28 ^= ((autonomous << 0xc) ^ var_28) & 0x1000`), which
|
||||
flows into `InterpretedMotionState::AddAction`'s stored node. Our
|
||||
`MoveToInterpretedState` action loop dispatches with `new
|
||||
MovementParameters { Speed }` — Autonomous stays false. No observed
|
||||
symptom today (stored-node autonomy has no current consumer); fix when
|
||||
R5/R6 touches the action list. **Where:**
|
||||
`MotionInterpreter.MoveToInterpretedState` action loop /
|
||||
`DispatchInterpretedMotion` (doc comment marks the gap).
|
||||
|
||||
## #162 — Observer-side moveto cancelled by ACE's autonomous MTS reflections (the "glide" class)
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ accepted-divergence entries (#96, #49, #50).
|
|||
|
||||
---
|
||||
|
||||
## 3. Documented approximation (AP) — 72 rows
|
||||
## 3. Documented approximation (AP) — 73 rows
|
||||
|
||||
| # | Divergence | Where (file:line) | Why it is safe / justified | Risk if assumption breaks | Retail oracle |
|
||||
|---|---|---|---|---|---|
|
||||
|
|
@ -179,6 +179,7 @@ accepted-divergence entries (#96, #49, #50).
|
|||
| AP-77 | **`apply_current_movement`'s interpreted-branch tail (`ApplyCurrentMovementInterpreted`) writes body velocity DIRECTLY via `get_state_velocity`/`set_local_velocity` when grounded, instead of dispatching through an `IInterpretedMotionSink`** — retail's real `apply_interpreted_movement` (0x00528600) drives velocity indirectly through `DoInterpretedMotion`'s animation-table backend (the SAME function the funnel's `ApplyInterpretedMovement` already uses WITH a sink); this dual-dispatch call site (`HitGround`/`LeaveGround`/`ReportExhaustion`/hold-key toggles/`SetWeenieObject`/`SetPhysicsObject`) has no sink threaded through it | `src/AcDream.Core/Physics/MotionInterpreter.cs` (`ApplyCurrentMovementInterpreted`) | Direct continuation of the pre-R3-W4 `apply_current_movement` approximation (R3-W4 plan explicitly keeps this shape rather than relocating it — "the direct grounded-velocity write MOVES to the controller-side call site unchanged"); correct for the grounded, no-animation-table-yet state acdream is in today | A MotionTable whose locomotion cycle bakes a DIFFERENT velocity than `get_state_velocity`'s constants would silently diverge from what the animation actually plays, since this path never touches the animation backend at all | `CMotionInterp::apply_interpreted_movement` 0x00528600; retire when a sink is threaded through `apply_current_movement`'s callers (R6 root motion) |
|
||||
| AP-79 | **P4 TargetTracker minimal adapter**: MoveToManager's setTarget/clearTarget seams store the tracked guid on RemoteMotion (remotes, R4-V4) or the `_playerMoveToTarget*` GameWindow fields (local player, R4-V5); GameWindow's per-tick block (remotes) / pre-Update feed (player) delivers `HandleUpdateTarget(Ok)` from the live entity table whenever the target moved beyond the set_target radius (and `ExitWorld` when the entity despawns) — retail's TargetManager is a full subscription system with per-target quantums and callback scheduling (R4-V4 + R4-V5, 2026-07-03) | `src/AcDream.App/Rendering/GameWindow.cs` (the tracker block in TickAnimations + the setTarget seam in EnsureRemoteMotionBindings; the player twin: `_playerMoveToTarget*` fields, the EnterPlayerModeNow setTarget seam, and the pre-Update feed in OnUpdateFrame) | The manager's deferred-start/retarget lifecycle only needs position deltas at the tracked radius; the adapter delivers exactly that from data the client already holds | Update cadence is frame-quantized rather than quantum-scheduled; a target moving sub-radius never re-fires (retail same); multi-listener semantics absent until R5's TargetManager port | `TargetManager::ReceiveUpdate` chain (r4-moveto-decomp.md); retire in R5 |
|
||||
| AP-80 | **PlanFromVelocity survives for velocity-only NPC cycles** (M16): UpdatePosition-derived speed picks Ready/Walk/Run cycles for server-controlled creatures whose UMs never arrive (scripted-path NPCs); retail derives every cycle from motion messages through the motion tables (R4-V4 note; pre-existing mechanism, row added per the V4 plan) | `src/AcDream.Core/Physics/ServerControlledLocomotion.cs` (`PlanFromVelocity`); consumer `GameWindow.ApplyServerControlledVelocityCycle` | Some ACE entities move by position updates alone — without this, they slide in T-pose; constants (StopSpeed 0.2, RunThreshold 1.25) tuned against live ACE traffic | Cycle-pick thresholds are acdream inventions — a creature intended to walk fast may show run legs near the threshold | retire in R6 (root motion + full per-tick order) |
|
||||
| AP-81 | **Remote-DR gravity toggled via the Gravity STATE bit**: the jump handler sets `Body.State \|= Gravity` at VectorUpdate and both landing blocks clear it after `HitGround()`; retail keeps GRAVITY set for the object's whole life and gates gravity ACCELERATION on the Contact transient (`calc_acceleration`) (pre-existing K-fix9/K-fix15 mechanism, row added during #161 — which also fixed the ordering so `Motion.HitGround()`'s verbatim `state&0x400` gate runs BEFORE the clear) | `src/AcDream.App/Rendering/GameWindow.cs` (VectorUpdate jump handler + the two landing blocks) | The DR tick integrates gravity only for airborne remotes; the flag dance delivers exactly that without porting the full contact-gated `calc_acceleration` chain; the #161 ordering fix keeps the retail HitGround contract satisfied | Any NEW call into `Motion.HitGround`/`LeaveGround` placed after the clear silently no-ops on the gravity gate (the #161 leg-2 class); grounded remotes carry a non-retail state word (probes comparing state bits vs retail mislead) | `CPhysicsObj::calc_acceleration` (contact-gated); `set_on_walkable` 0x00511310; retire in R6 (contact-gated accel + persistent GRAVITY) |
|
||||
|
||||
## 4. Temporary stopgap (TS) — 36 rows (TS-37 is a retired-row historical note, not an active count)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue