diff --git a/docs/research/2026-07-02-inbound-motion-verbatim-port-handoff.md b/docs/research/2026-07-02-inbound-motion-verbatim-port-handoff.md new file mode 100644 index 00000000..3780585a --- /dev/null +++ b/docs/research/2026-07-02-inbound-motion-verbatim-port-handoff.md @@ -0,0 +1,156 @@ +# Handoff: inbound animation + position — verbatim retail port + +Date: 2026-07-02 +Branch: `claude/vigorous-joliot-f0c3ad` (clean; prior arc all committed through `d34721fa`) +Mode for the new session: **/investigate first (report-only), then port on approval.** + +## What just shipped (context — the OUTBOUND / LOCAL half is done) + +This session made acdream's *outbound* wire and *local-player* motion +retail-faithful, verified against live ACE + user/retail-observer sign-off: + +- **L.1b** `2c8620ea` — dual `IMotionCommandCatalog` (AceModern runtime / + Retail2013 conformance, full `command_ids[0x198]` extraction). +- **L.2b** `78e163a4` + sign-off `f271a49e` — outbound wire parity (D1 + default-difference `RawMotionState::Pack`, D3 contact/longjump byte, D4 + `JumpPack`). +- **D6** `4ed27836` (pseudocode) + `0f099bb6` (D6.2a) + `47407506` (sign-off) + + `d34721fa` (D6.2b) — ported `CMotionInterp::adjust_motion` / + `apply_run_to_command` / `apply_raw_movement` / `get_state_velocity` and + unified the local player's velocity + turn + jump + wire onto one + input-built `RawMotionState` at `forward_speed=1.0`. Backward/strafe-left no + longer zero; strafe retail-exact; turn omega from interpreted `turn_speed`. +- **Key wire finding:** ACE **recomputes** the broadcast run speed from run + skill (echo-test confirmed) and auto-upgrades `WalkForward+HoldKey.Run → + RunForward`. The wire sends raw `forward_speed=1.0`. (Crib: + `claude-memory/project_retail_motion_outbound.md`.) + +**That was OUTBOUND + LOCAL. This handoff is the INBOUND / REMOTE half.** + +## The goal + +Port retail's **incoming (inbound) handling of animation + position VERBATIM** +into acdream — but **surgically**, targeted at where acdream deviates (not a +blind wholesale sequencer rewrite; a rushed sequencer swap has "broken +everything" in a past session — integrate carefully). Strict standard: no +approximations; retail decomp / ACE / DAT as oracles; parity tests before +behavior changes. + +## The symptom (the acceptance oracle — user's live observation) + +Watching a **remote player** (inbound motion) in acdream: + +- Overall "works OK" but **not polished**. Some **sliding** and some + **position errors once the entity stops**. +- **The worst offender:** transitioning **walk↔run WITHOUT stopping** — the + motion **interpreter reacts too slowly**, and this **throws all animation + + position off afterward**, compounding as the entity keeps **running around + + turning**. The error accumulates through subsequent motion. + +Fixing the walk↔run-transition responsiveness (and the resulting animation + +position desync) IS the acceptance test. + +## Root-cause hypothesis (UNCONFIRMED — verify in the investigation first) + +acdream's motion sequencer does not implement retail's motion-**transition** +state machine. Retail's `CMotionInterp` doesn't just swap the animation cycle +on a motion change — it **appends** the new motion to a `CSequence` and +transitions through it with `pending_motions` / `MotionDone` bookkeeping. If +acdream applies a walk↔run change abruptly/late (snap `SetCycle`) instead of +sequencing it, the animation cycle and the dead-reckoned position drift apart +and the error accumulates. The 2026-06-04 deep-dive already ranked missing +`pending_motions` / `MotionDone` as **HIGH**. + +## Pre-paid research to leverage (READ THESE) + +1. **`docs/research/2026-06-04-animation-sequencer-deep-dive.md`** — START + HERE. Retail `CMotionInterp` + `CSequence` + hook-dispatch state graph + 8 + ranked divergences (missing `pending_motions`/`MotionDone` HIGH; frame-swap + class; link→cycle boundary). **Verify its findings against the CURRENT code + — the L.1 animation work may have closed some gaps since 2026-06-04; do not + trust its line numbers.** +2. `docs/research/2026-06-26-movement-animation-retail-parity-audit.md` — the + inbound divergences: **D7** (animation split from retail motion flow), **D8** + (force-walk / `MoveToManager` approximate), **D9** (inbound `TurnToObject`/ + `TurnToHeading` types 8/9 dropped — CONFIRMED live: `mt=0x09` arrives and + just `SetCycle(Ready)` instead of turning), **D10** (spawn-time MoveTo weak), + **D11** (sequence/autonomy parsed then discarded), **D12** (jump/contact + gates simplified). +3. `docs/research/2026-07-01-d6-motion-interp-pseudocode.md` — the OUTBOUND/local + `CMotionInterp` port already done; inbound uses the SAME machinery + (`get_state_velocity`, `adjust_motion`, `apply_raw_movement` now exist in + `src/AcDream.Core/Physics/MotionInterpreter.cs`). + +## Decomp anchors (the oracle; grep `docs/research/named-retail/acclient_2013_pseudo_c.txt`) + +- `CMotionInterp::apply_current_movement` (~line 305713 — sequences style / + forward-falling / sidestep / turn / actions), `DoInterpretedMotion` (~305575), + `CMotionTable::GetObjectSequence` (~298636). +- `CSequence` append/advance/update (~lines 301622, 301777, 301839, 302425). +- The 2026-06-04 deep-dive has the full CMotionInterp/CSequence address list. +- `acclient.h` for the structs; Ghidra HTTP bridge at `http://127.0.0.1:8081` + (decompile-by-address) if it's up. + +## Where the inbound code lives (starting points to map) + +- `src/AcDream.App/Rendering/GameWindow.cs` — inbound remote-entity motion: + `OnLiveMotionUpdated`, the remote `SetCycle` / `ObservedOmega` seeding + (~4815-4856), dead-reckoned position + omega application (~9750-9865), the + `[SETCYCLE]`/`[SCFAST]`/`[SCFULL]`/`[OMEGA_DIAG]`/`[VEL_DIAG]`/`[SEQSTATE]`/ + `[UPCYCLE]` diagnostics. +- `src/AcDream.Core/Physics/AnimationSequencer.cs` — the cycle/action sequencer. +- `src/AcDream.Core/Physics/MotionInterpreter.cs` — interpreted state (now has + the D6 `adjust_motion`/`apply_raw_movement`/`get_state_velocity`). +- `src/AcDream.Core/Physics/RemoteMoveToDriver.cs` — remote MoveTo + `TurnRateFor`. +- `src/AcDream.Core.Net/Messages/UpdateMotion.cs`, `UpdatePosition.cs`, + `MoveToState.cs` — inbound parsing. + +## The plan for the new session + +1. **/investigate (report-only):** produce a ranked **deviation map** — + where acdream's inbound animation + position handling deviates from retail's + `CMotionInterp`/`CSequence`, **anchored on the walk↔run transition-lag + symptom** (not a whole-system rewrite). Parallel research agents are a good + fit. Optionally corroborate with a live capture (below). Deliver the report + + recommended verbatim-port slices; get explicit approval before edits. +2. **Port (after approval):** verbatim-port the deviating retail mechanisms — + most likely the `CSequence` motion-queue / transition + `pending_motions` / + `MotionDone`, and the inbound position/dead-reckoning where it deviates. + Tests-first (parity tests before behavior). **Integrate the sequencer + SURGICALLY** — change the minimum; do not replace the whole transform/cycle + pipeline at once. +3. **Verify:** the walk↔run transition is smooth and no cascading + animation/position desync — user visual + retail-observer side-by-side. + +## Live-capture setup (structured testing) + +- acdream = **OBSERVER** (login as `+Acdream`); the **other player driven from a + retail client** (retail packets = ground truth, so any render artifact is a + pure acdream inbound bug). +- Launch env (PowerShell): the standard live vars + + `ACDREAM_DUMP_MOTION=1` + `ACDREAM_REMOTE_VEL_DIAG=1` (heavy; dumps + `[UM_RAW]`/`[SETCYCLE]`/`[OMEGA_DIAG]`/`[VEL_DIAG]`/`[SEQSTATE]`/`[UPCYCLE]` so + you can correlate each inbound packet with what acdream renders). +- Structured protocol, one motion at a time: walk-straight (start→stop), + run-straight, turn-in-place, curved path, idle, **walk↔run toggle + (the key one)**, abrupt-stop-from-run. +- NOTE: `+Acdream` had `run=15225` (GM-buffed) this session → absolute speeds + unrepresentative; judge **relative** behavior + transitions. + +## Housekeeping + +- **Close the running observer acdream client before the new session rebuilds** + (it locks `AcDream.App.exe`; `dotnet build` fails with the DLL-copy lock + otherwise — the user closes it, we do NOT kill it). +- A background research agent (mapping acdream's inbound animation handling) was + in-flight when this session stopped — orphaned; the new session re-does the + mapping cleanly. + +## Discipline reminders + +- The user's side-by-side retail observations are **axioms** (retail-oracle); + once a mechanism plan exists, artifacts become acceptance tests, not new + investigations (no whack-a-mole). +- Report-only until fixes approved. Decomp-verbatim. Tests-first. +- Every deviation the port introduces/retires updates + `docs/architecture/retail-divergence-register.md` in the same commit.