Move camera pointer, framebuffer resize, and retained/devtools input edges behind focused reversible owners. Preserve input priority while making shutdown deactivate callbacks before live-session retirement and retry physical detach without stranding transport teardown.
8.4 KiB
GameWindow decomposition — current truth
Current state
The behavior-preserving App ownership campaign is complete through Slice 7 and
Slice 8 checkpoints A–E. GameWindow.cs moved from the 2026-07-21 baseline of
15,723 lines / 278 fields / 205 methods to 4,266 lines / 194 fields / 65
methods: 11,457 lines (72.9%) were removed without changing accepted gameplay
or rendering behavior.
| Slice | Ownership moved out | Closeout size |
|---|---|---|
| 1 | world selection and interaction intent | 14,912 / 278 / 191 |
| 2 | live animation presentation | 14,546 / 277 / 190 |
| 3 | complete live-session lifecycle and routing | 14,310 / 274 / 190 |
| 4 | live-entity hydration, teardown, and network presentation | 10,301 / 267 / 163 |
| 5 | landblock build, publication, retirement, and shared-origin lifetime | 8,811 / 247 / 153 |
| 6 | complete update-frame orchestration | 7,026 / 241 / 108 |
| 7 | complete render-frame orchestration and failure recovery | 4,666 / 196 / 70 |
| 8 A–E | shell freeze, native/session/environment composition, reversible pointer/input/framebuffer ownership | 4,266 / 194 / 65 |
Slice 8 is an ordered checkpoint campaign rather than another feature-body
move. A froze/pruned the shell boundary; B gave all nine native window callback
edges one reversible owner plus host quiescence; C added LiveSessionHost for
reset/selection/entry/route composition while keeping LiveSessionController
the sole session owner. C also replaced the embedded ACE shortcut with the
named-retail SkillFormula::Calculate @ 0x00591960 port. Partial route
construction and individual detach edges are retained and retried; reset and a
new generation cannot pass an incompletely detached route. D added
WorldEnvironmentController as the one-shot owner of WorldTime, loaded sky,
day-group selection, Weather, server synchronization, AdminEnvirons, and debug
cycles. GameWindow retains only ABI-compatible readonly aliases and typed
composition edges. The named-oracle correction is
SkyDesc::CalcPresentDayGroup @ 0x00500E10; the carried AdminEnvirons audio and
full fog/ambient/radar gaps are TS-54/TS-55.
E adds CameraPointerInputController, FramebufferResizeController, terminal
publish-before-attach Silk/dispatcher/retained input owners, and a quiescent
non-owning input-context view for optional devtools. Mouse-only hosts still
receive raw camera/cursor behavior. Logical callback deactivation runs before
the graceful session close; physical removal and the local mouse-look release
run only after session retirement. This prevents both copied callback reentry
and a failed event accessor from blocking F653/transport teardown. The carried
non-retail post-filter camera scalars are explicitly registered as TS-56.
Slice 6 implementation commits are 99a3e819, 4e4aac2c, 0bc9fda9,
c5570383, eeb0f6b4, 947c61d2, and production cutover e91f3102.
Slice 7 implementation commits are 7e4cfb37, 733126a2, bc6f09f9,
6d6e5b5f, 85239fb3, 28e1cf80, and production cutover 9d7df1bf.
The detailed ledgers live under docs/plans/2026-07-2*-gamewindow-slice-*.md and
docs/architecture/code-structure.md.
Frozen update graph
GameWindow.OnUpdate starts the profiler scope and calls one
UpdateFrameOrchestrator.Tick. The accepted production graph is:
- retry retained live-entity teardown;
- normalize/publish the update and PhysicsScript clocks;
- converge streaming origin, residence, readiness, and rescued projections;
- sample semantic input, raw mouse, and combat intent;
- advance live objects and deferred presentation hooks;
- drain inbound network traffic inside one world mutation batch;
- run the local post-network position tail;
- reconcile root/child/emitter/light presentation without advancing time;
- expire liveness;
- advance local teleport/reveal/tunnel presentation;
- evaluate one-shot player-mode entry;
- publish fly/chase/player camera presentation.
LiveEntityRuntime remains the canonical identity/incarnation owner.
GpuWorldState remains the spatial bucket owner. LiveSessionController owns
the session generation. Extracted phase owners query these typed runtimes and
must not create replacement dictionaries, service locators, or callback
facades into substantial GameWindow methods.
Frozen render graph
GameWindow.OnRender creates one value-only RenderFrameInput and calls
RenderFrameOrchestrator.Render. The accepted healthy order is:
- begin the GPU-flight transaction;
- begin/reset frame resources, clear, and publish live resources;
- begin the optional ImGui frame;
- advance weather display preparation;
- draw the complete world/PView/shared-alpha/particle/debug scene;
- draw the portal/private viewport using the pre-private foundation snapshot;
- refresh/draw paperdoll, retained gameplay UI, then submit ImGui;
- capture a requested screenshot;
- publish title/resource/frame diagnostics;
- close the GPU-flight transaction.
Recovery is one reverse-lifetime transaction. An active ImGui frame closes
through the owning Silk controller, and text rendering restores exact borrowed
GL state on success or failure. The orchestrator and its immediate phase owners
retain no direct GameWindow or anonymous callback bag. Recursive reachability
through canonical UI/input owners is not frame ownership; the known paths and
review rule live in docs/architecture/code-structure.md.
Accepted verification
- Slice 8 Checkpoint E Release suite: 7,477 passed / 5 fixture or environment skips; App 3,108/3 skips, UI Abstractions 533/0, and all three independent corrected-diff reviews are clean.
- Slice 8 Checkpoint D Release suite: 7,419 passed / 5 fixture or environment skips; App 3,059/3 skips. Focused environment/boundary tests and all three independent corrected-diff reviews are clean.
- Slice 8 Checkpoint C Release suite: 7,408 passed / 5 fixture or environment skips; App 3,048/3 skips and Core.Net 548/0. All three independent corrected- diff reviews are clean.
- Release suite: 7,341 passed / 5 fixture or environment skips.
- Connected lifecycle/reconnect: 315.6 seconds, graceful capped close and fresh process uncapped reconnect passed; only 25 expected world-edge empty landblocks.
- The six lifecycle PNGs preserve Slice 6 world geometry, depth, UI/paperdoll, private viewport, and presentation. Differences are live weather/particles, authoritative vitals, and small scripted camera settling.
- Synchronized nine-stop resource soak: 395.2 seconds, nine materializations, all movement/jump/combat exercises, and graceful exit passed; update p95 was at or below 0.8 ms. Caul return → plateau working/private delta was +85.7/+59.3 MiB, inside the unchanged gate.
- Two immediately preceding identical-binary runs exceeded the coarse process- residency threshold while deterministic ownership/lifecycle/timing checks passed. #232 adds canonical resource-owner snapshots; do not solve this by loosening the process-memory threshold or treating residency alone as an identified owner leak.
- The 18–33 missing VFX table/emitter diagnostics are known DAT-driven records, not a Slice 7 regression.
- TS-50, TS-51, and TS-53 remain registered; Slice 7 introduced no new retail divergence.
Next work
Slice 8 checkpoints F–L group the remaining action routing, settings, resource
ownership, startup, and shutdown wiring and reduce Silk callbacks to
narrow calls into input, update, render, resize, focus, and shutdown owners.
It removes leftover feature state only where Slices 1–7 already established a
canonical owner; it does not redesign gameplay, rendering, or resource lifetime.
The reviewed A–L execution ledger is
docs/plans/2026-07-22-gamewindow-slice-8-composition-lifecycle.md.
Do not retry
- Do not reintroduce delegate facades into
GameWindow; an ordering wrapper is not an extraction until the state and behavior body move. - Do not reorder the frozen update or render graphs during structural work. TS-53 records the known retail host-order difference for a separate evidence- backed port.
- Do not add a second GUID, session, spatial, or resource owner.
- Do not fold full
GameEntityaggregation into this campaign; evaluate it after Slice 8. - Do not loosen the soak process-memory threshold to hide #232. Add canonical owner snapshots and preserve process residency as a secondary guard.
- Small value/policy callbacks and diagnostics are acceptable only when they do not call a substantial window body or hide mutable ownership.