docs(runtime): plan simulation ownership

Inventory the present selection, interaction, combat, movement, physics, and projectile owners; pin named-retail ordering; and define independently reversible J5.1-J5.7 gates without moving presentation into Runtime.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Erik 2026-07-26 10:25:46 +02:00
parent 2c67a2c3ba
commit bb45afef33
3 changed files with 588 additions and 0 deletions

View file

@ -280,6 +280,9 @@ J4 must borrow it rather than create another inventory model.
**Active 2026-07-26.** Detailed execution begins with a source/owner inventory **Active 2026-07-26.** Detailed execution begins with a source/owner inventory
and a named-retail ordering matrix. Each coherent move must preserve the and a named-retail ordering matrix. Each coherent move must preserve the
accepted connected interaction, facing, projectile, and collision behavior. accepted connected interaction, facing, projectile, and collision behavior.
The executable sub-slice ledger, presentation boundary, acceptance gates, and
rollback discipline live in
[`2026-07-26-modern-runtime-slice-j5.md`](2026-07-26-modern-runtime-slice-j5.md).
- Move presentation-free movement interpretation, authoritative/predicted - Move presentation-free movement interpretation, authoritative/predicted
physics state, selection identity, approach/use transactions, combat intent, physics state, selection identity, approach/use transactions, combat intent,

View file

@ -0,0 +1,378 @@
# Modern runtime Slice J5 — movement, physics, interaction, and combat
**Status:** ACTIVE — J5.1 next
**Parent:** `2026-07-25-modern-runtime-slice-j.md`, J5
**Production base:** `2c67a2c3`
**Authorization:** the user approved Slices FL, including the Slice J
gameplay-owner gates, on 2026-07-24
## Objective
Move the complete presentation-independent action and simulation graph into
`AcDream.Runtime` while preserving retail behavior and the accepted graphical
feel.
At completion:
- Runtime owns selection, interaction mode/transactions, combat and spell-cast
intent, local/remote movement, outbound cadence, per-session physics, and
projectile simulation;
- App converts physical input into typed commands and projects committed state
into cameras, retained UI, animation, sound, particles, markers, and meshes;
- graphical and future no-window hosts borrow the same owners and observe the
same ordered state;
- no render callback, UI element, or camera object is gameplay authority.
## Presentation boundary
### Moves to Runtime
- selected/previous object identity;
- temporary use/examine/use-item-on-target mode;
- appraisal/use/pickup/approach transaction identity;
- combat mode, attack build/request/repeat intent, target identity;
- spell-cast request intent;
- `PlayerMovementController` state, movement interpretation, canonical local
body, object clock, movement managers, and outbound cadence;
- per-session `PhysicsEngine`, transition scratch, shadow/collision gameplay
state;
- remote canonical body/movement state;
- projectile component state and simulation;
- typed views, commands, ordered events, reset, disposal, and ownership
ledgers for all of the above.
### Remains in App
- Silk input sources and configurable key/chord mapping;
- camera orbit, mouse cursor capture, and device-delta filtering;
- world ray construction/picking;
- selection lighting pulse, vivid markers, and selected-object HUD;
- combat/spell bars and retained target-mode cursor images;
- animated part/root pose composition;
- meshes, terrain/EnvCell streaming publication, sounds, particles, lights,
translucency, and debug drawing;
- UI text, toasts, drag/drop visuals, pending-slot mesh, and appraisals'
retained window.
The camera's instant mouse-look mode still emits typed retail turn/sidestep
commands to Runtime. The camera may not write the player heading directly.
## Fixed invariants
1. Retail algorithms and branch/order semantics do not change during the move.
2. One mutable owner exists for every state class.
3. Runtime has no App/UI/backend dependency.
4. `RuntimeEntityRecord` remains canonical entity identity and physics
authority; App `LiveEntityRecord` remains a projection sidecar.
5. `RuntimeInventoryState` remains the only busy/inventory-transaction owner.
6. Core flat collision data is immutable/shareable. Each runtime/session owns
its `PhysicsEngine`, transition scratch, bodies, shadows, interpolation, and
projectile state.
7. All mutation stays on the existing update thread. Network workers enqueue
immutable messages only.
8. App input-to-Runtime commands are synchronous; no extra frame, polling
mirror, or background queue is introduced.
9. App presentation callbacks may re-enter/delete/replace entities. Every
continuation revalidates exact record, incarnation, authority version, and
object-clock epoch before commit.
10. Reset/disposal executes every suffix after an observer failure and refuses
a new session until the complete ownership ledger converges.
11. Every sub-slice is independently buildable, tested, connected-gated where
behavior can change, documented, committed, and given an exact rollback.
## Execution ledger
| Sub-slice | Status | Result/evidence |
|---|---|---|
| J5.0 owner/retail inventory | complete | `docs/research/2026-07-26-slice-j5-owner-and-retail-order-inventory.md` |
| J5.1 canonical action state | next | selection, combat state, target-mode owner |
| J5.2 interaction transactions | pending | use/appraisal/pickup/approach state and transport ordering |
| J5.3 combat and magic intent | pending | attack/mode/target/cast owners and typed input |
| J5.4 local movement and outbound cadence | pending | controller, body, movement managers, MTS/AP/jump cadence |
| J5.5 per-session physics and remote simulation | pending | engine/scratch/shadows/remote canonical body |
| J5.6 projectile runtime | pending | canonical component/simulation plus App projection acknowledgements |
| J5.7 combined cleanup and closeout | pending | no mirrors, no-window parity, teardown, connected and visual gates |
## J5.1 — canonical action state
Create one Runtime owner for:
- `SelectionState`;
- `CombatState`;
- presentation-free interaction/target-mode state.
Actions:
1. Move interaction-mode types/state from App UI into Runtime gameplay.
2. Make Runtime construct all three exact owners.
3. Remove Program's selection construction and GameWindow's direct combat
construction.
4. Make App composition, session routing, retained UI, plugins, and current
Runtime commands/views borrow the exact Runtime instances.
5. Remove `ItemInteractionController`'s optional private
`InteractionState` fallback; production and tests must pass the exact owner.
6. Add a combined ownership/revision snapshot and failure-safe reset/disposal.
7. Add Runtime views for selection, combat mode/known health, and interaction
mode without exposing mutable collections.
Gate:
- named-retail selection order and deduplication;
- combat Begin/reset defaults;
- target-mode enter/acquire/cancel/reset;
- UI/plugin observer failure isolation;
- two Runtime instances in one process;
- exact graphical/direct owner identity;
- source guard proving App cannot construct a production owner;
- Runtime dependency and assembly-load closure;
- focused Runtime/App tests, Release build, full Release suite, connected
lifecycle/reconnect route.
## J5.2 — interaction transactions
Split App orchestration into:
- App `WorldSelectionQuery`/picker/lighting/drag presentation;
- Runtime interaction transaction owner;
- typed query, movement, inventory-policy, transport, and presentation
acknowledgement seams.
Runtime owns:
- use throttle and source/target identity;
- appraisal request/current identity;
- ordinary use reservation transfer to authoritative UseDone;
- pickup request and exact post-arrival identity/token;
- ordered outbound interaction queue;
- entity-hidden/deleted cancellation;
- reset/disposal convergence.
App retains:
- cursor position and world ray;
- lighting pulse;
- toasts/text rendering;
- retained drag/drop and pending-slot drawing.
Ordering:
- ordinary Use sends immediately, then transfers its busy reservation to
UseDone;
- targeted Use enters mode until a compatible target is acquired;
- pickup's same-frame send drains after local movement output and before
inbound dispatch;
- post-arrival pickup sends once only on natural completion;
- stale/replaced object identities never execute queued work.
Gate:
- current connected near/far door/NPC/corpse use;
- item-on-target, appraisal, pickup, pending-slot accept/failure;
- same-frame W-release plus R;
- delete/GUID reuse, hidden target, cancellation, transport failure, callback
reentrancy, reset/disposal;
- normalized graphical/direct ordered traces and no duplicate busy owner.
## J5.3 — combat and magic intent
Move:
- `CombatAttackController`'s presentation-free build/request/repeat state;
- combat-mode selection/default policy;
- target death/auto-target identity transitions;
- `SpellCastingController` intent and last-request state.
Replace `InputAction` dependencies with Runtime-specific typed commands.
App maps configured actions and bar/button presses onto those commands and
projects Runtime snapshots.
Keep App-only:
- combat/spell bar widgets;
- camera target tracking;
- animation, sound, particles, and response text layout;
- DAT catalog/icon/formula presentation.
Ordering:
- movement aborts repeat attack at the existing retail input boundary;
- targeted attack/cast turns through the shared movement owner before
release;
- cast stops movement, emits exactly one targeted/untargeted request, then
increments the shared busy reference;
- server attack/cast completion remains authoritative.
Gate:
- power-bar cadence and height selection;
- repeat start/abort/movement cancellation;
- combat-mode equipment defaults;
- 180-degree bow/spell target facing;
- hostile-only auto-target after death;
- self/untargeted/targeted spell requests, component failure, busy completion;
- graphical bar parity and direct typed-command parity.
## J5.4 — local movement and outbound cadence
Move `PlayerMovementController`, its data types, and
`LocalPlayerOutboundController` into Runtime. Replace test-era environment
reads with typed construction options and the Runtime character movement-skill
projection.
App provides:
- current typed movement snapshot from the input dispatcher;
- filtered mouse-look turn adjustment;
- animation root-motion frame and completion acknowledgements;
- optional diagnostic sink.
Runtime owns:
- canonical local `PhysicsBody`, `MotionInterpreter`, `MovementManager`,
`MoveToManager`, `PositionManager`, and object clock;
- local input edge interpretation;
- run lock, jump charge/launch, movement/contact state;
- target-facing movement;
- pre-inbound MTS/jump and post-inbound autonomous-position send cadence;
- complete canonical `Position`, contact-plane, timestamps, and sent baselines.
Gate:
- bit-exact `RawMotionState`, MTS, jump, and autonomous-position fixtures;
- strict cadence/equality boundaries;
- complete quaternion/cell-local frame;
- short-tap animation-owned start;
- run/walk/back/strafe/turn/mouse-look/jump/land;
- portal/focus/reset teardown;
- target-facing and movement-aborts-repeat ordering;
- zero added per-frame allocations after warmup.
## J5.5 — per-session physics and remote simulation
Add a Runtime physics owner that constructs one `PhysicsEngine` per runtime
instance. App streaming publishes immutable prepared collision assets through
typed admission/withdrawal acknowledgements.
Move canonical remote simulation state and component indexes from App sidecars
to `RuntimeEntityRecord`/Runtime owners:
- remote body/interpolation/movement-manager state;
- canonical cell/rebucket writes;
- physics-host lookup;
- shadow/collision gameplay membership;
- ordinary-object update workset.
App consumes committed snapshots/events to:
- update `WorldEntity`;
- rebucket render projections;
- publish current root/part/equipped-child poses;
- refresh graphical collision/debug views.
Gate:
- complete flat collision conformance corpus remains bit-identical;
- fresh/reused engine parity and zero-allocation resolve profiles;
- player and remote position/cell/orientation parity;
- crowd de-overlap, doors, stairs, dungeon/cell seams, login placement;
- hidden/pending/rehydration, teleport correction, delete/GUID reuse;
- two concurrent Runtime instances prove scratch/shadow isolation.
## J5.6 — projectile runtime
Move projectile component identity, prediction versions, and simulation into
Runtime. Store the canonical component on `RuntimeEntityRecord`, not the App
sidecar.
Runtime uses:
- existing Core `ProjectilePhysicsStepper`;
- canonical body/cell/state/timestamp authority;
- strict prepared collision shapes;
- existing split Begin/Complete prediction with exact-version revalidation.
App acknowledges successful commits by:
- updating/rebucketing the render projection;
- synchronizing graphical shadow representation;
- publishing effect root/pose.
App does not decide collision outcome, impact, damage, effect, or deletion.
Gate:
- existing clamp/gravity/AlignPath/angular/collision/thin-wall fixtures;
- designated-target exclusion semantics;
- loaded/pending/cross-landblock/correction/delete/GUID-reuse;
- 96-owner lifetime/effect stress;
- no duplicated mesh, effect, shadow, body, or script owner;
- arrow, bolt, and representative spell projectile connected route.
## J5.7 — combined cleanup and closeout
Delete:
- temporary App owner fields and construction fallbacks;
- App simulation component dictionaries;
- current-runtime adapters that reconstruct moved snapshots;
- reset/disposal branches replaced by Runtime;
- compatibility aliases after all consumers borrow Runtime.
Add:
- one J5 ownership ledger;
- failure injection at every construction, subscription, command, projection
acknowledgement, reset, and disposal boundary;
- graphical/direct normalized traces;
- Runtime-only movement/use/combat/cast/projectile fixture host;
- source/dependency guards against App/backend leakage and duplicate owners.
Run:
1. focused Core physics/selection/combat tests;
2. Runtime state/command/event/teardown tests;
3. App input/interaction/combat/magic/physics/projectile tests;
4. Release build and complete Release suite;
5. exact-binary lifecycle/reconnect route;
6. canonical nine-stop movement/combat/jump route;
7. connected interaction, casting, projectile, portal, and reconnect route;
8. user graphical feel gate for movement, facing, use, combat/cast bars, and
visible missiles;
9. architecture, divergence, issue, roadmap, AGENTS/CLAUDE, and durable-memory
reconciliation.
## Commit and rollback discipline
Each sub-slice lands as one independently reversible production commit followed
by a documentation/evidence checkpoint where needed.
Planned production commit sequence:
1. `refactor(runtime): own canonical action state`
2. `refactor(runtime): own interaction transactions`
3. `refactor(runtime): own combat and magic intent`
4. `refactor(runtime): own local movement and outbound cadence`
5. `refactor(runtime): own per-session physics simulation`
6. `refactor(runtime): own projectile simulation`
7. `refactor(runtime): close simulation ownership`
Record each full SHA and exact `git revert <sha>` command in this file before
running its connected gate. Revert J5 groups in reverse order. Do not revert
J4 or the flat-collision Slice I cutover to mask a J5 ownership failure.
## Acceptance
J5 is complete only when:
- every listed presentation-free owner is constructed by Runtime;
- App owns presentation only and borrows exact Runtime state;
- graphical and no-window commands produce the same ordered state and packets;
- collision and trajectory fixtures remain bit-identical;
- reset, reconnect, same-process multiple-instance, GUID reuse, and failure
injection converge every owner;
- no extra frame or per-frame allocation regression exists;
- complete Release and connected gates pass;
- the user accepts graphical movement, facing, interaction, combat/casting,
and projectile behavior;
- architecture, roadmap, milestones, issues/divergences, AGENTS/CLAUDE,
evidence, memory, and rollback instructions are synchronized.

View file

@ -0,0 +1,207 @@
# Slice J5 owner and retail-order inventory
**Date:** 2026-07-26
**Scope:** presentation-free selection, interaction, combat/magic intent,
movement/outbound cadence, physics, and projectile ownership
**Parent plan:** `docs/plans/2026-07-26-modern-runtime-slice-j5.md`
## Purpose
Slice J5 moves gameplay simulation and intent out of the graphical App and into
`AcDream.Runtime`. This note pins the current production owners and the retail
ordering that cannot change while those owners move.
This is an ownership refactor, not a new movement, combat, interaction, or
projectile implementation. Existing retail ports remain the behavioral oracle.
## Current source ownership
| Concern | Current canonical state | Construction/root | Presentation coupling to split |
|---|---|---|---|
| Selection identity | `AcDream.Core.Selection.SelectionState` | `Program` constructs; `GameWindow` retains | world ray picking, lighting pulse, vivid markers, status/UI projections |
| Combat notifications/mode | `AcDream.Core.Combat.CombatState` | `GameWindow` constructs | combat/spell bars, camera target tracking, feedback |
| Pointer target mode | `AcDream.App.UI.InteractionState` | optional private construction inside `ItemInteractionController` | cursor image only; state itself is presentation-free |
| Item-use/appraisal/pickup intent | `ItemInteractionController`, `SelectionInteractionController`, `InventoryTransactionState` | App retained-UI and live-presentation composition | world picking, retained drag/drop, toasts, pending-slot mesh |
| Combat attack intent | `CombatAttackController`, `LiveCombatModeCommandController` | App retained-UI/session composition | input action mapping and combat-bar projection |
| Spell-cast intent | `SpellCastingController` inside App `MagicRuntime` | App content/UI composition | DAT catalog loading, component icons/text, spell bar |
| Local movement | `PlayerMovementController` plus Core `PhysicsBody`, `MotionInterpreter`, `MovementManager` | App player-mode composition | Silk/input sampling, camera/mouse-look device state, animated pose publication |
| Outbound movement cadence | `LocalPlayerOutboundController` | `GameWindow` constructs | App diagnostic sink only |
| Per-session collision | Core `PhysicsEngine`, `ShadowObjectRegistry`, transition scratch | `GameWindow` constructs | streaming publishers and debug visualization |
| Remote movement/physics | App `RemotePhysicsUpdater`, `RemoteInboundMotionDispatcher`, live sidecars | App composition | `WorldEntity`, render projection, animation pose/effect publication |
| Projectile simulation | Core `ProjectilePhysicsStepper` plus App `ProjectileController.Runtime` | App live-presentation composition | `WorldEntity`, effect pose, shadow publication |
`RuntimeEntityRecord` already owns canonical accepted identity, timestamps,
full cell, raw/final physics state, object clock, `PhysicsBody`, and
`IPhysicsObjHost`. The App `LiveEntityRecord` is a projection sidecar. J5 must
extend the canonical record rather than add another entity or physics model.
`RuntimeInventoryState` already owns the exact inventory transaction/busy/use
reservation state. J5 interaction work must borrow that owner and must not
recreate a busy counter or pending inventory request.
## Current reset ordering
`LiveSessionResetManifest` currently performs the relevant stages in this
order:
1. mouse/input capture;
2. player presentation and teleport transit;
3. dialogs/chat/settings;
4. equipped children and external container;
5. interaction plus selection;
6. inventory transactions;
7. selection presentation and object table;
8. spellbook/magic;
9. combat attack and combat state;
10. local-player and remaining session state;
11. live entity teardown;
12. render projection drain and session identity;
13. network effects/presentation tails.
Runtime ownership may combine internal stages, but App acknowledgements must
retain the same dependency order: presentation releases references before the
canonical owner is destroyed, while old session identity remains available
until exact-incarnation teardown converges.
## Named-retail ordering matrix
### Selection
`ACCWeenieObject::SetSelectedObject @ 0x0058C2E0`
1. If forced or changed, clear the old object's selected flag.
2. Commit the new selected ID.
3. Set the new object's selected flag.
4. If the ID changed, commit previous and previous-valid IDs.
5. Publish the UI selection notice.
6. Publish the shared item-holder selection change.
7. Notify the plugin API.
The state commit precedes every observer. Deduplicated selection does not
publish the changed notice.
### Session combat initialization
`ClientCombatSystem::Begin @ 0x0056A460`
1. Commit NonCombat and clear pending mode.
2. Clear jump, build, response, attack, repeat, and tracking transients.
3. Restore medium attack height and UI requested power `0.5`.
4. Register the global notice handler.
Runtime combat reset must clear the complete intent transaction before App
projects the peace-mode UI.
### Ordinary use and target mode
`ItemHolder::UseObject @ 0x00588A80`
1. Enforce the strict 0.2-second local use gate.
2. Verify inventory-request readiness and vendor restrictions.
3. Resolve automatic wield/use policy and targeted-use policy.
4. For ordinary use, emit `CM_Inventory::Event_UseEvent`.
5. Increment the shared UI busy reference.
6. Invoke `CPlayerSystem::UsingItem` and publish local text.
7. For a targeted item without an acquired target, store the source object and
enter `TARGET_MODE_USE_TARGET`; do not emit the use-with-target packet yet.
8. Once a compatible target is acquired, emit
`Event_UseWithTargetEvent`, increment busy, then invoke `UsingItem`.
`ClientUISystem::Handle_Item__UseDone @ 0x00564900`
1. Decrement exactly one busy reference.
2. Refresh cursor state only when the count reaches zero.
3. Present a nonzero server error afterward.
The current production correction remains authoritative for local ACE:
ordinary Use is sent immediately and ACE owns spatial approach. Pickup retains
the existing ordered local approach transaction and post-arrival send.
### Local movement output
`CommandInterpreter::SendMovementEvent @ 0x006B4680`
1. Require player, SmartBox, raw motion state, and nonzero autonomy.
2. Read the player's current raw state, canonical `Position`, contact state,
long-jump state, and four retail timestamps.
3. build/send `MoveToStatePack`.
4. Stamp `last_sent_position_time` only after send.
`CommandInterpreter::SendPositionEvent @ 0x006B4770`
1. Require SmartBox, player, active/in-world transient state, and valid
canonical `Position`.
2. Read current contact and timestamps.
3. build/send `AutonomousPositionPack`.
4. After send, stamp time and copy the complete sent `Position` and contact
plane as the next comparison baseline.
The existing pre-inbound movement/jump and post-inbound autonomous-position
slots remain unchanged.
### Object physics
`CPhysicsObj::UpdatePositionInternal @ 0x00512C30`,
`CPhysicsObj::transition @ 0x00512DC0`,
`CPhysicsObj::SetPositionInternal(CTransition const*) @ 0x00515330`,
and the ordered object tail recorded in
`docs/research/2026-07-19-r6-update-object-order-pseudocode.md` remain the
simulation oracle.
Runtime owns the canonical body and simulation. App may publish the final
accepted root/part poses only after that simulation result commits. A render
callback must never become position, cell, velocity, or collision authority.
### Projectile collision exclusion
`OBJECTINFO::missile_ignore @ 0x0050CEB0`
The existing Core `ProjectilePhysicsStepper` port remains unchanged:
- ignore other missiles;
- ignore ethereal weenies unless they are the designated target;
- with a nonzero designated target, ignore other creatures;
- do not infer a designated target from UI selection.
## Fixed dependency direction
```text
Silk / retained UI / camera
|
v
App typed input + presentation adapters
|
v
AcDream.Runtime intent/simulation owners
|
v
Core algorithms + Core.Net transport/content seams
```
Runtime never references App, Silk, OpenGL, OpenAL, ImGui, Arch, or retained
UI. App may synchronously acknowledge a committed runtime mutation and project
it; it may not keep a second mutable copy.
## Source hazards
1. `ItemInteractionController` can currently create a private
`InteractionState`; this is a duplicate-owner seam and must be removed when
J5.1 lands.
2. `Program` currently constructs selection while `GameWindow` constructs
combat. Both must move under one Runtime owner before higher-level
controllers move.
3. `PlayerMovementController` contains environment reads for test-era default
skills. A Runtime move must use typed construction options and the existing
Runtime character skill projection.
4. `LocalPlayerOutboundController` directly accepts `WorldSession` and an App
diagnostic sink. Runtime may use Core.Net, but diagnostics must cross a
narrow optional interface.
5. App `LiveEntityRecord` still stores remote/projectile component interfaces.
Simulation components must migrate to `RuntimeEntityRecord`; only
animation/effect/render projection belongs in the App sidecar.
6. Remote/projectile callbacks can synchronously delete or replace a GUID.
Every split-phase commit must retain the existing exact-record,
incarnation, authority-version, and object-clock-epoch revalidation.
7. `PhysicsEngine` transition scratch is per engine and same-thread LIFO.
Immutable prepared collision content may be shared; the engine and scratch
may not be shared by concurrent sessions.