docs(architecture): plan ordered GameWindow composition

This commit is contained in:
Erik 2026-07-22 14:58:08 +02:00
parent c791f13853
commit 1d04b94da2
2 changed files with 316 additions and 1 deletions

View file

@ -0,0 +1,314 @@
# GameWindow Slice 8 Checkpoint I — ordered production composition
**Status:** Active 2026-07-22.
**Parent:**
[`2026-07-22-gamewindow-slice-8-composition-lifecycle.md`](2026-07-22-gamewindow-slice-8-composition-lifecycle.md),
Checkpoint I.
**Integrated baseline:** `c791f138`; the reviewed Checkpoint-H tree plus the
complete-world-reveal history is merged. `GameWindow.cs` is 3,689 raw lines,
162 fields, and 37 methods. The Release build succeeds with the 17 warnings
tracked by #228 and all 7,606 tests pass / 5 intentionally skip.
**Behavior rule:** startup ownership and failure behavior may become explicit;
accepted input priority, settings, session/reset, update/render order, world
presentation, and retail gameplay behavior do not change.
## 1. Outcome
Replace the 2,149-line `GameWindow.OnLoad` body with one fixed production
composition pipeline. `GameWindow` remains the native host and write-only
publication shell. It does not become a service locator, and no extracted
phase stores or calls back into `GameWindow`.
The final startup shape is:
```text
GameWindow.OnLoad
-> GameWindowPlatformAcquisition.Acquire
-> GameWindowCompositionPipeline.Run
1 Host input / camera
2 Content / effects / audio
3 Settings / optional devtools
4 World / render resources
5 Interaction / retained UI
6 Live presentation / landblock publication
7 Streaming / session / local player / teleport
8 Atomic update + render roots
9 LiveSessionHost.Start, and nothing after it
```
Every phase consumes only the exact immutable prior results it needs and
returns a cohesive typed result. Results are borrowers. Newly acquired owners
move immediately into the existing lifetime slots, where shutdown remains the
sole release authority.
## 2. Fixed architecture
### 2.1 Platform prelude
`GameWindowPlatformAcquisition` is the exact production method used by
`OnLoad`. Its order is fixed:
1. create GL;
2. publish GL to the lifetime shell;
3. create the input context;
4. publish input;
5. return borrowed platform handles.
GL publication must happen before the input factory runs. An input-factory
failure leaves GL lifetime-owned for normal shutdown; local rollback never
double-disposes it. Viewport, diagnostics, camera, and GPU-frame resources are
not part of this narrow prelude.
### 2.2 Pipeline contract
Use nine explicit phase interfaces and immutable result records. The pipeline
holds no cumulative context and stores no final runtime graph. Locals pass only
the required sub-results forward.
Representative result grouping:
```csharp
HostInputCameraResult(InputGraph, CameraGraph, HostRenderGraph)
ContentEffectsAudioResult(ContentCatalogs, EffectsGraph, AudioGraph?)
SettingsDevToolsResult(ResolvedQuality, DevToolsGraph?)
WorldRenderResult(TerrainBuildContext, RenderFoundation)
InteractionUiResult(InteractionGraph, RetainedUiGraph?)
LivePresentationResult(LiveEntityGraph, WorldPresentationGraph)
SessionPlayerResult(StreamingGraph, SessionGraph, PlayerGraph, FrameParts)
FrameRootResult(LiveSessionHost)
SessionStartResult
```
Nested records are cohesive typed graphs, not dictionaries, `object` bags,
general service providers, or wrappers around the window.
### 2.3 Publication seam
`GameWindow` implements narrow write-only phase publishers. Publication methods
only validate single assignment and assign the existing shell fields. They do
not construct resources, invoke gameplay behavior, or provide runtime lookup.
The pipeline and phases release the publisher reference when `Run` returns.
Platform publication is narrower still so GL can publish before input
creation. Checkpoint J will move the exact shutdown manifest into the lifetime
owner; Checkpoint I does not duplicate or reorder that manifest.
### 2.4 Acquisition and rollback
Every concrete phase uses `CompositionAcquisitionScope` and named typed leases:
- unpublished acquisitions roll back in strict reverse dependency order;
- publication or transfer removes the resource from local rollback
immediately;
- lifetime-owned resources are never released locally;
- cleanup continues after an independent cleanup failure;
- the original construction failure and cleanup failures are retained;
- retry invokes only unfinished cleanup actions;
- successful cleanup never replays;
- duplicate publication and duplicate transfer are rejected.
This is not a generic shutdown bag. Long-lived owners remain in their focused
H-era slots and are retired in dependency order by the existing shutdown
transaction.
## 3. Frozen phase order and dependencies
### Phase 1 — host input and camera
Acquire viewport target, GPU-frame owner, world render diagnostics, keyboard
and mouse sources, input dispatcher, movement/camera bindings, mouse-look
cursor, orbit/fly camera, framebuffer camera target, and raw pointer owner.
Preserve:
- keyboard/mouse source attach before dispatcher attach;
- dispatcher attach before raw `MouseMove` attach;
- raw pointer attach before retained-UI device wiring;
- physical framebuffer size remains distinct from logical window size.
### Phase 2 — content, effects, and audio
Open the sole `DatCollection`; load magic, animation, collision, emitter,
particle, hook-frame, physics-script, lighting, and translucency owners. Audio
is optional, but failure can be swallowed only after its entire unpublished
prefix has converged cleanup. Hook-router registrations are reversible leases.
The physics-script advancement gate resolves the later entity-effect authority
through one typed deferred source; it must not capture the window or a null
snapshot.
### Phase 3 — settings and optional devtools
Apply the immutable startup settings snapshot after camera/audio exist and
before world/render/UI factories consume it. Compose optional devtools with a
transactional input/bootstrap/backend/framebuffer-target lease. Disabled
devtools acquire nothing. A devtools construction failure disables the feature
only after cleanup converges; cleanup failure fails startup rather than
stranding callbacks or native state.
Devtools live facts use focused sources for render diagnostics, canonical
world state, player mode, and player controller. No callback captures the host.
### Phase 4 — world and render resources
Load Region/environment/height/blend data, validate bindless support, then
construct terrain atlas, terrain shader, scene lighting, debug/text resources,
terrain renderer, WB mesh/render infrastructure, texture/sampler caches, and
the render foundation.
Required order includes bindless before atlas/terrain, and the final immutable
height/blend/surface build inputs are captured directly for worker use.
Constructor prefixes for debug lines, bitmap font, OpenGL graphics device,
particle batcher, global mesh buffers, portal depth, particles, samplers, and
terrain renderer must have a strong transactional guarantee.
### Phase 5 — interaction and retained UI
Compose combat target/attack, external-container and item interaction,
character/magic controllers, radar/chat/cursor assets, `UiHost`, and the
retained runtime lease. Retained bindings to later session, reveal, player,
diagnostic, and live-entity authorities use focused deferred sources. Host,
partial runtime, mounted runtime, and input bindings retain exact ownership.
### Phase 6 — live presentation and landblock publication
Compose WB/spawn adapters, canonical `GpuWorldState`, `LiveEntityRuntime`, live
motion/projectile/animation/effect/presentation owners, selection/paperdoll,
EnvCell/portal/sky/particle renderers, landblock publishers, and render
diagnostics. Bind the canonical live-runtime slot immediately after runtime
creation. Anonymous retained event handlers become named reversible bindings.
Portal tunnel ownership remains acquire -> prepare -> transfer. The fallback
slot and final controller never co-own the same resource.
### Phase 7 — streaming, session, local player, and teleport
Compose and publish the streamer before `Start`, then streaming/recenter/reveal,
session/hydration/network/liveness, player-mode/local-animation/shadow,
gameplay-input, commands, action-router, live-session host, and teleport
owners. All UI and devtool late sources bind here.
The current premature frame-root publication defect is removed: command
bindings and gameplay-action attachment complete in Phase 7, before Phase 8.
### Phase 8 — atomic frame roots
Construct all update/render leaves locally, then both roots, and publish the
pair once through `GameFrameGraphSlot` as the final phase action. A failure
before publication leaves the slot empty. A failure after publication leaves
the lifetime owner responsible for exactly one paired graph. No callback can
observe a half-pair.
### Phase 9 — terminal session start
Invoke only `LiveSessionHost.Start`. It is the absolute last startup operation.
There is no publication, callback attachment, command binding, or diagnostics
work afterward.
## 4. Focused live sources and reversible edges
Replace current host captures with focused authorities:
- `ILocalPlayerIdentitySource` for the server GUID;
- `ILocalPlayerModeSource` for player/fly state;
- a player-controller slot for current movement facts;
- `RenderRangeState` for near radius;
- `LiveWorldOriginState` for center coordinates;
- external-container state for current container;
- focused deferred session command/host authority;
- a canonical world-state source that cannot freeze the pre-runtime
placeholder;
- typed item/use/pick/selection/debug-fact operations;
- immutable phase-4 terrain worker inputs.
Add expected-owner unbind tokens for one-shot deferred composition slots that
can be bound before a later fallible edge. A failed phase must be disposable
and retryable without stale bindings or “already bound” failures.
Named reversible bindings replace anonymous subscriptions for projection
visibility, projection pose, entity-ready, appearance-applied, hook sinks, and
streamer start/stop.
## 5. Implementation checkpoints
1. **I.1 — contracts and executable oracle:** pipeline, platform prelude,
acquisition scope, fake phases, prefix/failure tests.
2. **I.2 — platform + Phase 1:** exact GL/input publication and host
input/camera cutover.
3. **I.3 — Phase 2:** content/effects/audio, reversible hooks, transactional
OpenAL construction.
4. **I.4 — Phase 3:** settings/devtools, complete optional rollback.
5. **I.5 — Phase 4:** world/render foundation and strong constructor
guarantees.
6. **I.6 — Phases 56:** retained UI, live presentation, landblock publishers,
typed late sources and event leases.
7. **I.7 — Phase 7:** streaming/session/player/teleport and all pre-frame
bindings.
8. **I.8 — Phases 89:** atomic roots, terminal start, delete old `OnLoad`
body, structural gates.
9. **I.9 — corrected-diff review, full gates, docs/memory, one bisectable
Checkpoint-I closeout commit.**
Small implementation commits may be used while the checkpoint is active, but
each must build and preserve production startup. The final I commit closes the
ledger only after the complete pipeline is live.
## 6. Automated acceptance
### Platform and pipeline
- Success trace is exactly `gl.factory -> gl.publish -> input.factory ->
input.publish`.
- GL-factory failure never calls input; input-factory failure retains GL in the
lifetime owner.
- Failure at each of nine phases runs exactly the preceding prefix and no
suffix.
- Every phase receives the exact immutable prior result instance.
- Success trace is phases 18 followed by terminal session start.
- Failure after frame publication withdraws the pair exactly once.
### Acquisition and concrete phases
- A/B/C prefix tests prove reverse rollback, transfer, aggregation, retry, and
no replay.
- Every concrete production phase has injected fault points at each
acquisition/attach/bind/publication edge.
- Optional audio/devtools continue only after successful rollback.
- Hook and event registrations unregister exactly once.
- Deferred slots unbind only the exact owner and reject stale release.
- Streamer start is reversed on later Phase-7 failure.
- Portal fallback/controller never co-own.
- Phase 8 never exposes one frame root without the other.
### Structural and repository gates
- `OnLoad` is a small platform/pipeline forwarder.
- No phase stores `GameWindow`, accepts a callback facade for substantial
host methods, or uses a service locator / `object` dictionary.
- `GameWindow` contains no construction body larger than one paragraph.
- `AcDream.Core` gains no App/GL dependency.
- App production build has zero warnings/errors; solution warnings remain only
the 17 tracked by #228.
- Focused tests, all App tests, and full Release suite pass.
- Connected lifecycle/reconnect and synchronized soak gates remain green when
the local ACE/visual environment is available.
## 7. Review gate
After the complete production cutover, run three independent read-only reviews:
1. retail/behavior-order conformance;
2. architecture, dependency direction, ownership, and shutdown symmetry;
3. adversarial failure, partial construction, reentrancy, and stale-binding
analysis.
Fix every confirmed finding at its root and repeat review until all three are
clean. No review finding is waived merely because the happy-path tests pass.
## 8. Documentation and commit exit
Checkpoint I closes only when the code-structure ledger, architecture, roadmap,
milestones, issues, `memory/project_gamewindow_decomposition.md`, `AGENTS.md`,
and `CLAUDE.md` agree on the same production pipeline and measured class size.
No retail-divergence row is added for a behavior-preserving ownership move; any
discovered behavioral adaptation is recorded in the same commit that keeps it.

View file

@ -29,7 +29,8 @@ audit.
- [x] H — give terrain atlas, sky shader, retained `UiHost`, and both frame roots
explicit single ownership and transfer seams.
- [ ] I — group `OnLoad` into small ordered, fakeable composition phases with
transactional partial-acquisition rollback.
transactional partial-acquisition rollback. Active detailed plan:
[`2026-07-22-gamewindow-slice-8-checkpoint-i-ordered-composition.md`](2026-07-22-gamewindow-slice-8-checkpoint-i-ordered-composition.md).
- [ ] J — move the exact retryable shutdown manifest to a focused lifetime
owner and prove all partial-load/reentrant/retry paths.
- [ ] K — in a separate #232 commit, add canonical owner snapshots to every soak