docs: handoff for inbound animation+position verbatim retail port

Fresh-session handoff for the INBOUND (remote-entity) motion arc. Captures the
symptom (walk<->run transition without stopping = interpreter reacts too slowly
-> animation+position desync compounding through turns; plus sliding + stop
position errors), the root-cause hypothesis (acdream lacks retail's CMotionInterp/
CSequence motion-transition state machine: pending_motions/MotionDone), the
pre-paid research (2026-06-04 sequencer deep-dive, 2026-06-26 audit D7-D12), the
decomp anchors, the inbound code map, the /investigate-then-port plan, the
live-capture setup, and the discipline reminders (report-only, surgical
sequencer integration, decomp-verbatim). Prior outbound/local arc shipped clean
through d34721fa.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-07-02 13:49:01 +02:00
parent d34721fa94
commit b60b9b4a21

View file

@ -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.