acdream/docs/research/2026-07-02-inbound-motion-verbatim-port-handoff.md
Erik b60b9b4a21 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>
2026-07-02 13:49:01 +02:00

8.5 KiB

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.