acdream/docs/research/2026-07-26-slice-j5-owner-and-retail-order-inventory.md
Erik bb45afef33 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>
2026-07-26 10:25:46 +02:00

207 lines
9.4 KiB
Markdown

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