refactor(runtime): own local movement and outbound cadence

Move the canonical local movement controller, body/motion managers, object clock, movement wire data, and MTS/jump/AP sender into AcDream.Runtime. Replace process skill defaults with typed Runtime character options, make graphical and direct commands borrow one autorun owner, retain the construction-time PartArray seam, and include movement in terminal ownership convergence.

Preserve the accepted pre-inbound movement/jump and post-inbound autonomous-position order while moving the exact packet/cadence fixtures into Runtime tests. Add graphical/direct parity, two-instance isolation, teardown, allocation, architecture, and divergence-path coverage.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Erik 2026-07-26 12:33:53 +02:00
parent 3456dff038
commit aa3f4a60f8
36 changed files with 878 additions and 276 deletions

View file

@ -0,0 +1,146 @@
# Slice J5.4 — canonical local movement ownership
**Date:** 2026-07-26
**Parent:** `docs/plans/2026-07-26-modern-runtime-slice-j5.md`
**Scope:** ownership relocation only; no movement, collision, animation, or
wire behavior changes
## Retail oracle
The named September 2013 client remains the oracle:
| Mechanism | Named symbol | Address / named pseudo-C |
|---|---|---|
| take local control | `CommandInterpreter::TakeControlFromServer` | `0x006B32D0`, lines 699046699145 |
| apply current raw movement | `CommandInterpreter::ApplyCurrentMovement` | `0x006B3430`, lines 699146699530 |
| stop decision | `CommandInterpreter::MaybeStopCompletely` | `0x006B3B90`, lines 699531699563 |
| post-inbound position slot | `CommandInterpreter::UseTime` | `0x006B3BF0`, lines 699564699645 |
| input command boundary | `CommandInterpreter::MovePlayer` | `0x006B3F40`, lines 699803700232 |
| AP predicate | `CommandInterpreter::ShouldSendPositionEvent` | `0x006B45E0`, lines 700233700273 |
| MTS serialization | `CommandInterpreter::SendMovementEvent` | `0x006B4680`, lines 700274700315 |
| AP serialization | `CommandInterpreter::SendPositionEvent` | `0x006B4770`, lines 700316 onward |
| object/root-motion step | `CPhysicsObj::UpdatePositionInternal` | `0x00512C30`, lines 280817 onward |
| object update envelope | `CPhysicsObj::update_object` | `0x00515D10`, lines 283950 onward |
The detailed wire unpack/pack interpretation and prior ACE/Holtburger
cross-checks are retained in:
- `docs/research/2026-05-04-l3-port/02-um-handling.md`;
- `docs/research/2026-05-04-l3-port/14-local-player-audit.md`;
- `docs/research/2026-05-01-retail-motion-trace/findings.md`;
- `docs/research/2026-07-26-slice-j5-owner-and-retail-order-inventory.md`.
The reference source trees are not present in this worktree, so J5.4 reuses
those already-recorded cross-checks rather than inventing a new interpretation.
No behavioral algorithm is changed by this slice.
## Consolidated retail pseudocode
### Input / object phase
```text
MovePlayer(command, speed, hold, ...)
if server currently controls movement
TakeControlFromServer()
mutate the player's one RawMotionState / MotionInterpreter
apply the current movement to the same CPhysicsObj
if the movement boundary requires a report
SendMovementEvent()
SendMovementEvent()
require player + SmartBox + raw motion state + autonomous movement
snapshot complete raw motion state
snapshot the player's canonical cell-local Position and contact
snapshot instance/server-control/teleport/force-position sequences
serialize and send MoveToState
stamp only last_sent_position_time after successful send
```
Jump uses the same pre-inbound input/object phase. The charged extent and
body-local launch velocity are read from the canonical local controller and
serialized exactly once on release.
### Object simulation
```text
UpdatePositionInternal(dt, rootFrame)
apply the animation-authored root frame to the canonical physics body
integrate/transition through the existing retail physics pipeline
commit complete Position, velocity, contact, and cell membership
update_object()
advance the retail object clock in fixed quanta
update movement and physics in retail order
leave one committed canonical body for every later borrower
```
App animation may supply the authored root frame and consume the committed
pose. It does not own a second body, movement state, or object clock.
### Post-inbound autonomous position phase
```text
UseTime()
after inbound messages may have corrected the canonical body:
if ShouldSendPositionEvent()
SendPositionEvent()
ShouldSendPositionEvent()
compare current cell/contact against the last AP baseline
after the retail interval, compare the complete current Frame
SendPositionEvent()
snapshot canonical cell-local Position, orientation, contact, timestamps
serialize and send AutonomousPosition
after successful send:
stamp last_sent_position_time
copy complete Position and contact plane into the AP baseline
```
This preserves the accepted acdream frame order:
1. graphical or no-window input becomes a typed `MovementInput`;
2. Runtime mutates the canonical controller/body;
3. Runtime emits MTS/jump before inbound dispatch;
4. inbound corrections mutate that same body;
5. Runtime evaluates/emits AP after inbound dispatch;
6. App projects the final committed body to animation, camera, and UI.
## Ownership result
- `PlayerMovementController`, its movement data types, and
`LocalPlayerOutboundController` now live in `AcDream.Runtime.Gameplay`.
- `RuntimeLocalPlayerMovementState` is the sole controller, construction
motion seam, autorun latch, typed view, and terminal ownership ledger.
- `DispatcherMovementInputSource` samples physical held state only and maps
configured edges onto the Runtime owner.
- direct Runtime movement commands and graphical input mutate that same
autorun state.
- `PlayerMovementConstructionOptions` replaces process environment reads and
is populated from `RuntimeMovementSkillState`.
- the App diagnostic implementation crosses only
`IMovementTruthDiagnosticSink`.
- shutdown retires graphical/session borrowers before disposing the Runtime
movement owner.
## Preserved boundaries
- Silk input, mouse filtering, camera, CSequence/PartArray presentation,
retained jump bar, sounds, and effects remain in App.
- `PhysicsEngine` remains App-owned until J5.5.
- remote movement and projectiles remain App-owned until J5.5/J5.6.
- TS-33's already-recorded possible same-update MTS/AP ordering difference is
preserved; this ownership move neither widens nor hides it.
## Automated evidence required before closeout
- Runtime state/view identity, two-instance isolation, construction lease,
terminal convergence, typed movement skills, and zero-allocation snapshots;
- graphical/direct autorun parity and one-production-owner source guards;
- existing bit-exact RawMotionState, MTS, jump, AP, quaternion, contact, and
strict cadence tests;
- existing run/walk/back/strafe/turn/mouse-look/jump/land, short-tap,
portal/reset, target-facing, and repeat-abort tests;
- Release build, complete solution tests, exact-binary connected lifecycle,
and canonical movement route.