Record the measured GameWindow reduction, final review and test gates, durable selection/inventory ownership rules, and live animation presentation as Slice 2. Co-authored-by: OpenAI Codex <codex@openai.com>
352 lines
15 KiB
Markdown
352 lines
15 KiB
Markdown
# GameWindow Slice 1 — selection and interaction ownership
|
|
|
|
**Status:** COMPLETE — landed 2026-07-21.
|
|
**Parent program:** [`docs/architecture/code-structure.md`](../architecture/code-structure.md), Slice 1.
|
|
**Baseline:** `cf50ee3d`; `GameWindow.cs` is 15,723 lines before this slice.
|
|
**Behavior rule:** Existing retail behavior moves unchanged. Any defect found by
|
|
the extraction audit is fixed in a separately identified commit before the
|
|
mechanical cutover.
|
|
|
|
## Landing record
|
|
|
|
- Plan/oracle: `c2713837`.
|
|
- Exact-incarnation lifetime hardening: `047a4c83`.
|
|
- Retail pending keyboard-pickup presentation: `52dbb574`.
|
|
- Read-only query extraction: `e74f2ca9`.
|
|
- Stateful interaction owner: `fa8d5232`.
|
|
- `GameWindow` cutover: `d2bb5af4`.
|
|
- Three-review correction pass: `5acc3f01`.
|
|
|
|
The correction pass unified retail's one-inventory-request-at-a-time owner
|
|
across every mutation surface, separated optimistic/rollback/authoritative
|
|
placement notices, made response completion atomic and reentrancy-safe, and
|
|
bound pending work to exact object identities and placement tokens.
|
|
|
|
Final metrics: `GameWindow.cs` 15,723 → 14,912 lines and 205 → 191 methods;
|
|
fields remained 278. Release build passed, the focused selection/inventory
|
|
gate passed, the full suite passed 6,558 tests with five intentional skips,
|
|
and the retail-conformance, architecture/integration, and adversarial reviews
|
|
all finished with no actionable findings.
|
|
|
|
## 1. Outcome
|
|
|
|
`GameWindow` stops owning world selection and selection-driven interaction.
|
|
After this slice it retains only composition and narrow lifecycle calls:
|
|
|
|
- construct the query/controller;
|
|
- feed the completed render-selection frame;
|
|
- drain outbound interactions at the existing retail frame boundary;
|
|
- forward input actions and MoveTo completion;
|
|
- forward entity hidden/removal and session-reset edges.
|
|
|
|
Two App owners replace the current body:
|
|
|
|
1. `WorldSelectionQuery` owns read-only world selection, eligibility,
|
|
classification, naming, closest-hostile lookup, target bounds, and range
|
|
queries over canonical live state.
|
|
2. `SelectionInteractionController` owns selection intents, click routing,
|
|
Use/PickUp ordering, speculative facing/movement, deferred close-range
|
|
requests, and the input-action boundary.
|
|
|
|
Core `SelectionState` remains the sole selected-object truth.
|
|
`ItemInteractionController` remains the sole retail ItemHolder policy owner.
|
|
`LiveEntityRuntime` remains the sole GUID/incarnation/visibility owner.
|
|
|
|
## 2. Retail and project oracles
|
|
|
|
No AC algorithm is invented in this slice. Move the existing ports with these
|
|
references attached:
|
|
|
|
- [`2026-07-17-retail-world-selection-pseudocode.md`](../research/2026-07-17-retail-world-selection-pseudocode.md)
|
|
— render-coupled part selection, selection sphere, vivid target, click pulse.
|
|
- [`2026-07-10-retail-toolbar-interaction-pseudocode.md`](../research/2026-07-10-retail-toolbar-interaction-pseudocode.md)
|
|
— target-mode precedence, ItemHolder Use, selection notices.
|
|
- [`2026-07-17-retail-external-container-looting-pseudocode.md`](../research/2026-07-17-retail-external-container-looting-pseudocode.md)
|
|
— corpse/container open, range, pending loot placement.
|
|
- [`2026-07-12-death-and-auto-target-pseudocode.md`](../research/2026-07-12-death-and-auto-target-pseudocode.md)
|
|
— synchronous selection clear and reentrant Auto Target.
|
|
- `Render::GfxObjUnderSelectionRay @ 0x0054C740`.
|
|
- `ACCWeenieObject::SetSelectedObject @ 0x0058C2E0`.
|
|
- `CPlayerSystem::OnAction @ 0x00561890`.
|
|
- `CPlayerSystem::PlaceInBackpack @ 0x0055D8C0`.
|
|
- `ItemHolder::UseObject @ 0x00588A80`.
|
|
- `UIElement_SmartBoxWrapper::RecvNotice_SmartBoxObjectFound @ 0x004E5AD0`.
|
|
|
|
Existing registered adaptations remain adaptations; this structural slice does
|
|
not silently “improve” them:
|
|
|
|
- IA-19: automatic acquisition is narrowed to hostile non-player monsters.
|
|
- AD-11: ACE null-useability fallback.
|
|
- AD-27: natural `MoveToComplete` is the client completion seam.
|
|
- AP-23: speculative local Use movement and 3/2/0.6-unit radius buckets.
|
|
|
|
Their file pointers move in the same commit as their implementation bodies.
|
|
|
|
## 3. Invariants
|
|
|
|
### 3.1 Render selection
|
|
|
|
- The renderer builds a private list, then publishes it only at
|
|
`CompleteFrame`; input reads the previous complete frame.
|
|
- A hit carries server GUID **and local entity ID**. The query accepts it only
|
|
if `LiveEntityRuntime` still resolves the same current, visible top-level
|
|
incarnation.
|
|
- Plain selection excludes self. Active target mode includes self.
|
|
- A successful hit starts the HIGH click-light pulse before target-mode or
|
|
selection routing.
|
|
- Target-mode success or rejection consumes the click and never falls through
|
|
to ordinary selection/use.
|
|
- A miss does not clear selection. A miss during target mode leaves target mode
|
|
active.
|
|
- Drag release may pulse the target, but does not select it.
|
|
- Vivid target markers deliberately query materialized state so selected
|
|
objects remain indicated through walls and long distances. Picking and new
|
|
combat acquisition use visible interaction state only.
|
|
|
|
### 3.2 Input and outbound ordering
|
|
|
|
- Keep the one `InputDispatcher.Fired` subscription in `GameWindow`.
|
|
- Combat attack handling, Press/DoubleClick filtering, and retained-UI capture
|
|
occur before delegation exactly as today.
|
|
- The controller handles only the currently implemented semantic actions:
|
|
previous selection, closest monster, left selection, double-left selection,
|
|
Use selected, and PickUp selected.
|
|
- Input callbacks enqueue Use/PickUp. The queue drains after local movement
|
|
output and before inbound packet dispatch.
|
|
- The queue drains only its boundary snapshot; reentrant enqueue waits until
|
|
the next frame.
|
|
|
|
### 3.3 Use/PickUp and movement
|
|
|
|
- `ItemInteractionController` decides Use/open/target mode. The new controller
|
|
does not copy ItemHolder policy.
|
|
- Far target: install speculative MoveTo and send exactly one wire request
|
|
immediately. ACE owns its arrival callback.
|
|
- Close target: install TurnToObject and defer the **first** request until
|
|
natural `MoveToComplete(None)`.
|
|
- Cancel/error completion never sends.
|
|
- The movement intent preserves target cylinder radius/height, the AP-23 use
|
|
radius, and the 7.5-unit CanCharge prediction.
|
|
- Every queued/deferred request is bound to the captured live incarnation and
|
|
revalidated before sending.
|
|
- Pickup preserves destination container and placement. Retail pending-slot
|
|
presentation occurs before the request.
|
|
|
|
### 3.4 Lifecycle
|
|
|
|
- Hidden, logical removal, session reset, and GUID replacement invalidate old
|
|
hits and pending actions immediately.
|
|
- Token-specific pending cleanup is allowed even after GUID replacement;
|
|
GUID-only selection cleanup retains the existing incarnation guard so old
|
|
teardown cannot clear a newly selected replacement.
|
|
- Session reset atomically clears selection history, outbound queue, deferred
|
|
action, item target mode/busy state, published selection frame, and click
|
|
pulse.
|
|
- `CombatTargetController` must not auto-acquire during `SessionReset`.
|
|
- No new GUID map, selection store, visibility store, or retry timer is added.
|
|
|
|
## 4. Target interfaces
|
|
|
|
### 4.1 Render hit identity
|
|
|
|
`RetailSelectionPart` and `RetailSelectionHit` carry `LocalEntityId` alongside
|
|
`ServerGuid`. `RetailSelectionScene.Pick` returns the complete hit. Its lighting
|
|
source is also keyed by both identities so a reused GUID cannot inherit an old
|
|
click pulse.
|
|
|
|
`RetailSelectionScene.Reset` clears building/published lists, frame keys,
|
|
frustum state, and pulse state. Geometry resolution receives a narrow internal
|
|
source interface so publication behavior is testable without installed DAT.
|
|
|
|
### 4.2 `WorldSelectionQuery`
|
|
|
|
The query receives:
|
|
|
|
- canonical `LiveEntityRuntime`;
|
|
- `ClientObjectTable`;
|
|
- the shared `RetailSelectionScene`;
|
|
- player GUID and current player position providers;
|
|
- camera/viewport and cursor providers;
|
|
- a DAT selection-sphere resolver;
|
|
- the existing Setup-cylinder resolver.
|
|
|
|
It exposes read-only operations for:
|
|
|
|
- pick at cursor/screen point with exact-incarnation validation;
|
|
- current interaction record/entity lookup;
|
|
- item type, name, and description;
|
|
- creature/hostile/health eligibility;
|
|
- closest visible hostile result without mutating selection;
|
|
- combat camera target;
|
|
- vivid target information and selection sphere;
|
|
- useability/pickupability;
|
|
- AP-23 use radius, close-range, CanCharge, and target movement data;
|
|
- external-container cylinder range.
|
|
|
|
The query never mutates selection, queues work, sends packets, or starts
|
|
movement.
|
|
|
|
### 4.3 `SelectionInteractionController`
|
|
|
|
The controller receives:
|
|
|
|
- `SelectionState` and `WorldSelectionQuery`;
|
|
- `ItemInteractionController` provider;
|
|
- a typed Use/PickUp transport;
|
|
- a narrow movement-intent sink;
|
|
- toast/system diagnostic sinks;
|
|
- Auto Target setting provider.
|
|
|
|
It owns `OutboundInteractionQueue` and an incarnation-bound
|
|
`PendingPostArrivalAction`. Public methods form the complete host seam:
|
|
|
|
```text
|
|
HandleInputAction
|
|
PickAtCursor / PlaceDraggedItem
|
|
SelectClosestCombatTarget
|
|
GetSelectedOrClosestCombatTarget
|
|
SendUse / SendPickUp
|
|
DrainOutbound
|
|
OnNaturalMoveToComplete
|
|
OnEntityHidden / OnEntityRemoved
|
|
ResetSession
|
|
```
|
|
|
|
The controller does not subscribe directly to Silk input and does not own
|
|
rendering, combat state, the session, or item policy.
|
|
|
|
## 5. Execution sequence
|
|
|
|
### Commit 0 — plan and defect registration
|
|
|
|
- Land this plan.
|
|
- Register #230 for GUID-incarnation/session-reset leakage.
|
|
- Register #231 for F-key pickup missing the pending destination reservation.
|
|
- No runtime behavior change.
|
|
|
|
### Commit 1 — lifecycle identity hardening (#230)
|
|
|
|
- Add local-entity identity to render parts/hits and selection lighting.
|
|
- Add scene reset and deterministic publication tests.
|
|
- Validate published hits against current visible incarnation.
|
|
- Bind pending post-arrival actions to a live record/local identity.
|
|
- Clear only the matching pending token on teardown, including GUID reuse.
|
|
- Make session reset clear scene and item interaction state.
|
|
- Prevent combat Auto Target during `SessionReset`.
|
|
- Keep this root-cause correction independently bisectable.
|
|
|
|
### Commit 2 — retail pending pickup presentation (#231)
|
|
|
|
- Add one ItemInteractionController entry point for a validated world pickup.
|
|
- Publish `PendingBackpackPlacementRequested` before calling the existing
|
|
placement delegate.
|
|
- Route F-key pickup through it with destination/placement unchanged.
|
|
- Do not optimistically move ownership; server confirmation/failure remains
|
|
authoritative.
|
|
|
|
### Commit 3 — extract `WorldSelectionQuery`
|
|
|
|
- Move every read-only selection/classification/range body from `GameWindow`.
|
|
- Preserve materialized-versus-visible semantics.
|
|
- Extend the live animation read seam with current motion so dead eligibility
|
|
does not depend on `GameWindow.AnimatedEntity`.
|
|
- Rewire cursor, vivid target, toolbar health, combat camera, external-container
|
|
range, drag target, and door-name reads through the query.
|
|
- Delete the corresponding `GameWindow` methods.
|
|
|
|
### Commit 4 — extract `SelectionInteractionController`
|
|
|
|
- Move world click routing, selection mutation, closest-target selection,
|
|
outbound queue, Use/PickUp, movement intent, deferred completion, and
|
|
hidden/removal/reset handling.
|
|
- Compose `ItemInteractionController`; do not duplicate its policies.
|
|
- Update IA-19/AD-11/AD-27/AP-23 file pointers.
|
|
- Add focused controller tests for every invariant in §3.
|
|
|
|
### Commit 5 — input cutover and host cleanup
|
|
|
|
- Replace the six selection/interaction switch cases with one controller
|
|
delegation call at the same ordered position.
|
|
- Forward render-frame, update-frame, MoveTo completion, hidden/removal,
|
|
session-reset, cursor, and drag hooks.
|
|
- Delete old fields/records/wrappers from `GameWindow`.
|
|
- Prove `GameWindow` no longer owns a selection algorithm or interaction
|
|
request body.
|
|
|
|
### Commit 6 — review, gate, and ledger close
|
|
|
|
- Run retail-conformance, architecture/integration, and adversarial read-only
|
|
reviews against the complete slice diff.
|
|
- Fix confirmed findings and repeat review until clean.
|
|
- Run focused Core/App tests, Release solution build, and full Release suite.
|
|
- Update code-structure ownership status, roadmap/current queue if necessary,
|
|
both session instruction files, divergence pointers, and durable interaction
|
|
memory.
|
|
- Record the new `GameWindow` line/field/method counts.
|
|
|
|
## 6. Automated acceptance matrix
|
|
|
|
### Render/query
|
|
|
|
- Building frame is invisible until `CompleteFrame`.
|
|
- Last complete frame remains queryable while the next frame builds.
|
|
- Empty completion clears prior hits.
|
|
- Hidden, pending, deleted, and replaced incarnations reject stale hits.
|
|
- A replacement GUID is not pickable until its own part is published.
|
|
- Pulse is identity-bound and cannot color a replacement.
|
|
- Plain pick excludes self; target-mode pick includes self.
|
|
- Vivid target can use materialized state while combat candidates cannot.
|
|
- Closest hostile excludes self, players, pets, friendly NPCs, dead, Hidden,
|
|
pending, and deleted objects.
|
|
|
|
### Interaction
|
|
|
|
- Pulse precedes target-mode and selection routing.
|
|
- Target-mode success/rejection consumes the click without selection drift.
|
|
- Single click selects; double click captures the clicked GUID and queues one
|
|
activation.
|
|
- Miss preserves selection and target mode.
|
|
- Previous selection preserves current SelectionState semantics.
|
|
- Queue drains after movement and only once per captured boundary.
|
|
- Far Use/PickUp sends once; close sends only on natural completion.
|
|
- Cancel/error/Hidden/delete/reset sends zero.
|
|
- Delete then same-GUID create sends zero to the replacement.
|
|
- Useability and pickupability fallbacks remain byte-for-byte behaviorally
|
|
equivalent.
|
|
- Creature and Stuck pickup rejection messages remain exact.
|
|
- Corpse double-click/Use opens it and never attempts to pick up the corpse.
|
|
- F pickup reserves the first destination slot before wire send.
|
|
- Pickup destination/placement survive the deferred path.
|
|
|
|
### Lifecycle/input
|
|
|
|
- Session reset clears previous selection, target mode, busy state, queue,
|
|
pending completion, published picks, and pulse; repeated reset is safe.
|
|
- SessionReset cannot trigger combat auto-target.
|
|
- Modal/retained UI capture still prevents controller invocation upstream.
|
|
- Escape target-mode cancellation remains ahead of window-close behavior.
|
|
|
|
## 7. Connected gate
|
|
|
|
Run one normal capped Release client and verify:
|
|
|
|
1. select a nearby door/NPC and see the click pulse and target corners;
|
|
2. double-click/Use the door or NPC;
|
|
3. select a distant corpse, press R, approach, and open it;
|
|
4. double-click one loot item and use F on another; both reserve the expected
|
|
backpack slot and settle after ACE confirmation;
|
|
5. enter combat, select a hostile, use closest-target and previous-selection;
|
|
6. kill the selected target with Auto Target enabled and observe only a hostile
|
|
replacement;
|
|
7. portal or relog and confirm no old selection/pulse/action survives.
|
|
|
|
Any failure returns to the owning commit. No retry, grace period, or
|
|
GUID-only suppression is acceptable.
|
|
|
|
## 8. Subagent policy for this slice
|
|
|
|
The implementation is tightly coupled across `GameWindow`, live lifetime,
|
|
render publication, input order, and player MoveTo. One primary agent performs
|
|
all edits. Independent subagents are useful for read-only retail,
|
|
architecture, and adversarial reviews; they do not edit the shared slice
|
|
concurrently.
|