From 1d04b94da2d6e23ab4261e13ec16c833b443e54b Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 22 Jul 2026 14:58:08 +0200 Subject: [PATCH] docs(architecture): plan ordered GameWindow composition --- ...lice-8-checkpoint-i-ordered-composition.md | 314 ++++++++++++++++++ ...amewindow-slice-8-composition-lifecycle.md | 3 +- 2 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 docs/plans/2026-07-22-gamewindow-slice-8-checkpoint-i-ordered-composition.md diff --git a/docs/plans/2026-07-22-gamewindow-slice-8-checkpoint-i-ordered-composition.md b/docs/plans/2026-07-22-gamewindow-slice-8-checkpoint-i-ordered-composition.md new file mode 100644 index 00000000..e117fb8b --- /dev/null +++ b/docs/plans/2026-07-22-gamewindow-slice-8-checkpoint-i-ordered-composition.md @@ -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 5–6:** 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 8–9:** 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 1–8 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. diff --git a/docs/plans/2026-07-22-gamewindow-slice-8-composition-lifecycle.md b/docs/plans/2026-07-22-gamewindow-slice-8-composition-lifecycle.md index bf9c5000..6db74fff 100644 --- a/docs/plans/2026-07-22-gamewindow-slice-8-composition-lifecycle.md +++ b/docs/plans/2026-07-22-gamewindow-slice-8-composition-lifecycle.md @@ -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