Move the live-session reset and routing graph, combat and diagnostic command targets, and the sole gameplay input subscriber into Phase 7 before frame publication. Add exact retryable ownership for late bindings so partial startup cannot strand session or component teardown edges. Co-authored-by: Codex <codex@openai.com>
745 lines
40 KiB
Markdown
745 lines
40 KiB
Markdown
# GameWindow Slice 8 — composition and lifecycle shell
|
||
|
||
**Status:** Active 2026-07-22.
|
||
**Parent program:** [`docs/architecture/code-structure.md`](../architecture/code-structure.md), Slice 8.
|
||
**Baseline:** `96f8bfcf`; `GameWindow.cs` is 4,666 raw lines, 196 fields, and
|
||
70 methods. The Release suite passes 7,341 tests / 5 fixture or environment
|
||
skips. The connected lifecycle/reconnect and synchronized nine-stop soak gates
|
||
pass.
|
||
**Behavior rule:** This is the final behavior-preserving ownership slice. It
|
||
must not change accepted input priority, session/reset behavior, update/render
|
||
order, world presentation, quality settings, or retail gameplay behavior. It
|
||
may fix resource and callback lifetime defects proven by this slice's ownership
|
||
audit.
|
||
|
||
## Progress ledger
|
||
|
||
- [x] A — freeze construction/callback/shutdown order and delete proven dead or
|
||
test-facade residue.
|
||
- [x] B — make native-window callback intake an explicit reversible owner and
|
||
define host-quiescence failure semantics.
|
||
- [x] C — extract live-session host/reset/binding callbacks and verify the
|
||
embedded skill formula against named retail.
|
||
- [x] D — extract retail world-environment/day/weather behavior.
|
||
- [x] E — extract two-phase raw pointer/camera input, focus, and framebuffer
|
||
resize behind symmetric named subscriptions.
|
||
- [x] F — extract the sole gameplay input-action router plus focused combat and
|
||
diagnostic command owners.
|
||
- [x] G — extract two-phase persisted settings/display/quality ownership.
|
||
- [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. I.1–I.7 and I.8a are complete;
|
||
I.8b atomic frame-root publication is 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
|
||
checkpoint without weakening the process-memory guard.
|
||
- [ ] L — corrected-diff reviews, focused and full Release gates, connected
|
||
lifecycle/soak, framebuffer comparison, documentation, memory, and final
|
||
visual handoff.
|
||
|
||
Each checked checkpoint lands as a bisectable commit. A checkpoint is not done
|
||
while a new class delegates a substantial body back into `GameWindow`, stores
|
||
`GameWindow`, or replaces 196 fields with one runtime/service-locator bag.
|
||
|
||
## 1. Outcome and non-goals
|
||
|
||
At slice exit `GameWindow` is the native construction shell:
|
||
|
||
```text
|
||
GameWindow
|
||
├── creates Window + GL + input context
|
||
├── invokes ordered composition functions
|
||
├── forwards tiny Silk callbacks to typed owners
|
||
├── starts LiveSessionController last
|
||
└── asks GameWindowLifetime to close synchronously
|
||
```
|
||
|
||
The shell may retain injected Core/UI game-state roots whose replacement would
|
||
be the separately deferred `GameEntity`/client-state aggregation migration. It
|
||
must not retain AC algorithms, entity scans, session binding/reset bodies,
|
||
settings algorithms, the input switch, anonymous device subscriptions, or the
|
||
shutdown operation manifest.
|
||
|
||
This slice does **not**:
|
||
|
||
- alter the frozen twelve-phase update graph or ten-phase render graph;
|
||
- port a new retail host loop or retire TS-53;
|
||
- change input bindings, mouse feel, focus/reset semantics, camera formulas,
|
||
resize/DPI behavior, frame pacing, VSync, MSAA, quality, draw distance, or
|
||
resource budgets;
|
||
- change live-session authority, packet parsing, reset order, the 35-second
|
||
graceful F653 confirmation, reconnect, or transport teardown;
|
||
- change UI layout, magic, portal presentation, selection, world rendering, or
|
||
gameplay behavior;
|
||
- fold full `GameEntity` aggregation, headless mode, Linux support, or GPU
|
||
particle work into this campaign;
|
||
- loosen the soak's working/private-memory threshold.
|
||
|
||
## 2. Frozen host behavior
|
||
|
||
No fresh retail research is required for the host boundaries. One audited
|
||
exception exists: the embedded skill-formula helper currently cites ACE only,
|
||
so Checkpoint C must complete the named-retail pseudocode/cross-reference/test
|
||
workflow before moving or claiming that formula.
|
||
|
||
### 2.1 Startup and callbacks
|
||
|
||
Preserve this exact order:
|
||
|
||
1. before `Window.Create`, load persisted Display and resolve VSync/MSAA because
|
||
these are native-context attributes;
|
||
2. create the window and bind `DisplayFramePacingController` immediately;
|
||
3. register Load, Update, main Render, pacing Render, Closing, FocusChanged,
|
||
Move pacing, StateChanged pacing, and FramebufferResize in that order;
|
||
4. in `OnLoad`, acquire GL and input from the native window;
|
||
5. compose camera/DAT/effects/audio, persisted settings, optional devtools,
|
||
world/render resources, retained UI, live presentation, streaming, session,
|
||
teleport, then update/render roots;
|
||
6. call `LiveSessionController.Start` last, including when live mode or
|
||
credentials are absent, because its disabled path still performs the
|
||
canonical reset.
|
||
|
||
Main Render remains registered before pacing so the wait occurs after all frame
|
||
work and before Silk's swap. Direct raw MouseMove remains ordered after
|
||
SilkKeyboard/MouseSource + InputDispatcher subscriptions and before retained
|
||
`UiHost` device wiring.
|
||
|
||
### 2.2 Input, focus, and resize
|
||
|
||
Preserve the exact input-action priority:
|
||
|
||
1. pointer press/release transitions;
|
||
2. scroll;
|
||
3. combat press/release transitions;
|
||
4. Press/DoubleClick gate;
|
||
5. retained UI semantic actions;
|
||
6. selection interactions;
|
||
7. pressed movement/autorun;
|
||
8. the remaining UI/debug/player/combat/escape commands.
|
||
|
||
Mouse capture remains three distinct operations:
|
||
|
||
- focus loss and camera-mode exit use lifecycle end and may publish a final
|
||
movement update;
|
||
- session reset uses reset semantics and cannot send into the ending session;
|
||
- process close retires the session first, then releases presentation without
|
||
an active session.
|
||
|
||
FramebufferResize continues to ignore non-positive sizes, then updates GL
|
||
viewport, `ViewportAspectState`, camera aspect, and the forced devtools layout
|
||
reset. It must not mutate retained `UiRoot` size; the next UI draw does that.
|
||
|
||
### 2.3 Session and reset
|
||
|
||
`LiveSessionController` remains the only session lifecycle owner. The extracted
|
||
host adapter may hold typed binding/reset collaborators but no second session,
|
||
generation, identity, GUID, routing, or command map.
|
||
|
||
Preserve `LiveSessionResetManifest` exactly: projection/capture/UI first;
|
||
equipped projection before the canonical live runtime; live-runtime convergence
|
||
before identity reset; pending effects/hooks/presentation last. The AC skill
|
||
formula currently embedded in the window moves only after Checkpoint C adds its
|
||
missing named-retail citation and conformance tests (or records the proven
|
||
remaining adaptation).
|
||
|
||
### 2.4 Update and render
|
||
|
||
`OnUpdate` remains profiler scope plus one immutable
|
||
`UpdateFrameOrchestrator.Tick`. `OnRender` takes the existing one window-size
|
||
snapshot and calls one `RenderFrameOrchestrator.Render`. PView's
|
||
`FramebufferSize` source remains distinct from the logical Window.Size used by
|
||
the frame/UI/portal/screenshot path. TS-33 and TS-53 remain registered.
|
||
|
||
### 2.5 Shutdown
|
||
|
||
Closing synchronously completes the retryable transaction while the GL context
|
||
is current. Direct `Dispose` is the constructed-never-run, Run-failure, and
|
||
retry fallback. The native window is disposed only after successful completion
|
||
or the explicitly reported last-resort incomplete fallback described below.
|
||
|
||
The frozen dependency order is:
|
||
|
||
1. set a no-throw host-quiescence gate and converge live-session shutdown as
|
||
independent all-attempted **hard** operations; this guarantees F653 and
|
||
transport teardown before dependents;
|
||
2. attempt physical detachment of all window/Silk/UI ingress as explicitly
|
||
reportable **soft** operations: retry them, record any persistent failure,
|
||
but do not block now-safe owner/GPU teardown after quiescence;
|
||
3. withdraw **both** update and render frame graphs through their shared slot;
|
||
4. retire session/UI dependents;
|
||
5. clear the live runtime while teardown callbacks remain alive;
|
||
6. retire live dependents;
|
||
7. wait for submitted GPU work;
|
||
8. retire private/render frontends;
|
||
9. retire shared textures;
|
||
10. retire the mesh adapter;
|
||
11. retire remaining render borrowers, including terrain renderer;
|
||
12. retire separately owned borrowed assets, including terrain atlas and sky
|
||
shader, only after their respective renderer stage converges;
|
||
13. retire remaining render owners and frame-flight ownership;
|
||
14. close DAT mappings;
|
||
15. dispose the already-detached input context;
|
||
16. dispose GL last;
|
||
17. return to `GameWindow.Dispose`, which disposes the native window.
|
||
|
||
The logical quiescence gate makes callbacks inert even when a physical event
|
||
unsubscribe reports failure. `ResourceShutdownTransaction` therefore gains an
|
||
explicit non-blocking/reportable operation policy for physical detach only.
|
||
Those failures remain in the final result and are never swallowed, but they do
|
||
not strand unrelated resources. Session convergence and GPU drain remain hard
|
||
barriers. Operations within a stage remain independent and all-attempted. Later
|
||
stages remain protected until the current stage converges. Successful operations are
|
||
never replayed on retry or reentrant completion.
|
||
|
||
Silk's `Closing` event is not cancellable. `OnClosing` therefore performs the
|
||
first synchronous completion attempt and reports an incomplete transaction
|
||
without pretending success. The outer `Dispose` retries the same retained
|
||
transaction. If a persistent native/driver failure still prevents convergence,
|
||
the native window/context is disposed as the explicit last-resort safety net
|
||
and the incomplete result is logged with the blocked stage; it is never
|
||
reported as a clean shutdown. Tests pin this fallback instead of assuming a
|
||
close-cancellation mechanism Silk does not provide.
|
||
|
||
The lifetime state is explicit: `Active`, `RetryableIncomplete`, clean
|
||
`Complete`, `CompleteWithCleanupFailures`, or terminal `AbandonedIncomplete`.
|
||
Persistent soft-detach failure followed by otherwise complete teardown produces
|
||
`CompleteWithCleanupFailures`; repeated Dispose is inert and returns the same
|
||
immutable non-clean report. Hard-barrier retry is allowed only while the native
|
||
context is retained. Last-resort native disposal moves to
|
||
`AbandonedIncomplete`; every later Dispose is inert and reports the retained
|
||
blocked-stage result without touching GL, callbacks, or the already-destroyed
|
||
window.
|
||
|
||
## 3. Architecture and interfaces
|
||
|
||
### 3.1 Native window intake
|
||
|
||
Add a focused reversible binding equivalent to:
|
||
|
||
```csharp
|
||
internal sealed class SilkWindowCallbackBinding : IDisposable
|
||
{
|
||
public static SilkWindowCallbackBinding Create(
|
||
IWindow window,
|
||
WindowCallbackTargets targets,
|
||
DisplayFramePacingController pacing);
|
||
|
||
public void Attach();
|
||
}
|
||
```
|
||
|
||
`WindowCallbackTargets` is a fixed typed set for Load, Update, Render, Closing,
|
||
FocusChanged, and FramebufferResize. It is not a general callback list. The
|
||
binding is published into its lifetime slot after `Create` and before `Attach`
|
||
begins, so even an attach failure plus rollback failure retains a cleanup owner.
|
||
Attach treats the current edge as possibly acquired before calling a custom
|
||
event add accessor and rolls back in reverse order if registration fails.
|
||
The explicit lifecycle state monitor is released around every external event
|
||
accessor; external cleanup waits for Attaching/Detaching to converge, while a
|
||
gate-owned reentrant cleanup reports typed deferred completion instead of
|
||
deadlocking or falsely succeeding. Dispose detaches in reverse registration
|
||
order, joins concurrent callers, is idempotent/reentrant-safe, and makes later
|
||
window events inert. `GameWindow` clears the retained slot only after terminal
|
||
disposal. The separate pacing Render callback remains ordered after main Render.
|
||
Every callback enters the shared no-throw host-quiescence monitor, so external
|
||
shutdown drains an admitted callback, Closing can stop reentrantly, and a failed
|
||
physical unsubscribe cannot re-enter a retired owner.
|
||
|
||
### 3.2 Input owners
|
||
|
||
Use two focused owners:
|
||
|
||
- `CameraPointerInputController` owns named raw MouseMove subscriptions,
|
||
pointer position, camera-mode cursor transitions, focus loss, typed scroll
|
||
operations, and the existing mode-specific sensitivities. Construction is
|
||
deliberately two-phase: `AttachRaw` runs after dispatcher/source wiring and
|
||
before retained `UiHost` wiring; `BindGameplayFrame` fills a focused deferred
|
||
slot after `GameplayInputFrameController` exists. It consumes canonical
|
||
`LocalPlayerModeState`, `ChaseCameraInputState`, input capture, and camera
|
||
owners.
|
||
- `GameplayInputActionRouter` is the **only** gameplay subscriber to
|
||
`InputDispatcher.Fired` and owns the frozen priority graph above. Its pointer
|
||
and scroll edges call typed operations on `CameraPointerInputController`; the
|
||
pointer owner does not independently subscribe to `Fired`. The router calls existing typed
|
||
retained-UI, selection, movement, player-mode, interaction, combat-command,
|
||
and diagnostic-command owners.
|
||
|
||
The window binding, router Fired subscription, pointer raw MouseMove,
|
||
Combat/Camera events, dispatcher-to-source links, source-to-Silk links, and
|
||
retained-UI-to-Silk links must all be named and reversible. Shutdown's first
|
||
stage deactivates them without disposing the input context or the UI owners
|
||
needed by session reset. `UiHost` therefore exposes a separate idempotent input
|
||
deactivation seam; its later full Dispose owns windows and rendering. Do not
|
||
replace these edges with anonymous lambdas retained by a generic subscription
|
||
bag.
|
||
|
||
Every wrapper on those edges—including Silk sources, dispatcher, raw pointer,
|
||
retained UiHost, and optional devtools input—checks the same quiescence gate (or
|
||
performs its no-throw logical deactivate in stage 1). No device callback can
|
||
re-enter during the potentially 35-second live-session close while physical
|
||
event removal waits for the following soft-detach stage.
|
||
|
||
Every multi-event Attach is transactional: if the Nth subscription fails, the
|
||
already-attached prefix is removed in reverse order before the exception
|
||
escapes. This applies to native window, dispatcher/source/Silk, raw pointer,
|
||
Combat/Camera, retained `UiHost`, and optional devtools input bindings.
|
||
|
||
### 3.3 Session and world environment
|
||
|
||
- A focused App session host owns the current selection/entered/reset/binding
|
||
factories around `LiveSessionLifecycleHost`, `LiveSessionEventRouter`, and
|
||
`LiveSessionResetManifest`. It resolves the current session through
|
||
`LiveSessionController`; it does not mirror it.
|
||
- `WorldEnvironmentController` owns current loaded sky/day state,
|
||
`AdminEnvirons`, provider refresh, WorldTime synchronization, and Weather
|
||
changes. The existing retail day-group/weather algorithm moves verbatim.
|
||
- A named skill-credit resolver owns the formula formerly nested in the session
|
||
binding only after Checkpoint C verifies it against named retail; until then
|
||
the current ACE-only interpretation is not overclaimed as retail-faithful.
|
||
|
||
### 3.4 Settings and diagnostics
|
||
|
||
`RuntimeSettingsController` owns SettingsStore, active toon key, persisted
|
||
display/audio/gameplay/chat/character values, quality reapply, and UI
|
||
lock/FPS/combat preferences. It is constructed before `Window.Create` and is
|
||
the single source for one immutable startup snapshot and Display/VSync/MSAA.
|
||
After GL/input sources/input dispatcher/camera/audio exist—but still before
|
||
devtools, the world render dispatcher, terrain, retained UI, or streaming—it
|
||
applies window/pacing/audio values and resolves the final quality snapshot.
|
||
Later factories consume that resolved snapshot. A fixed
|
||
typed `RuntimeSettingsTargets` implementation is late-bound only to support
|
||
future runtime changes; binding it does not replay startup display or quality
|
||
transitions. The controller exposes typed state/commands to existing consumers;
|
||
it does not own those renderers, UI trees, or session state.
|
||
|
||
`RuntimeDiagnosticCommandController` owns command routing for collision wires,
|
||
time/weather, sensitivity, and nearby-world dumps. State remains solely in its
|
||
canonical owner: time/weather operations call `WorldEnvironmentController`,
|
||
sensitivity calls `CameraPointerInputController.AdjustSensitivity`, and world
|
||
queries use canonical runtime views. It creates no duplicate state or entity
|
||
cache. Diagnostic algorithms do not remain in the window merely because their
|
||
input actions originate there.
|
||
|
||
`FramebufferResizeController` is a separate GL-aware typed target. It owns the
|
||
frozen viewport → `ViewportAspectState` → camera aspect → devtools layout-reset
|
||
sequence and is not folded into the pointer owner.
|
||
|
||
### 3.5 Frame-root slots
|
||
|
||
A focused `GameFrameGraphSlot` is the sole publication point for the current
|
||
`UpdateFrameOrchestrator` and `RenderFrameOrchestrator`. The two Silk stubs
|
||
resolve through this slot; `GameWindowLifetime` withdraws it after session
|
||
convergence and before borrowed owners retire. Clearing a lifetime snapshot is
|
||
not withdrawal while a direct window field can still invoke the graph.
|
||
|
||
### 3.6 Ordered composition
|
||
|
||
`OnLoad` invokes the narrow production platform phase that owns
|
||
`GL.GetApi(_window)` and `_window.CreateInput()`, then invokes small composition
|
||
functions at these existing boundaries:
|
||
|
||
1. host input/camera;
|
||
2. content/effects/audio;
|
||
3. persisted settings/devtools;
|
||
4. world/render resources;
|
||
5. interactions/retained UI;
|
||
6. live presentation/landblock publishers;
|
||
7. streaming/session/local player/teleport;
|
||
8. update/render roots;
|
||
9. session start.
|
||
|
||
Each function receives only typed dependencies and may return a small immutable
|
||
result needed by the next function. There is no stored mega-context. Stable
|
||
owners are copied to locals before retained delegates are constructed so those
|
||
delegates cannot capture `GameWindow` accidentally. Construction cycles use
|
||
the existing focused deferred slots; they do not use callbacks into the window.
|
||
|
||
The production ordering lives in a fixed `GameWindowCompositionPipeline` with
|
||
typed phase interfaces/factories. Tests invoke that same pipeline with fake
|
||
platform/acquisition phases; no test-only clone or source-text assertion stands
|
||
in for partial-load execution. A narrow production
|
||
`GameWindowPlatformAcquisition` phase is invoked by `OnLoad` and alone calls
|
||
`GL.GetApi(window)` and `window.CreateInput()` through injectable factories. It
|
||
publishes GL to the lifetime before attempting input, then publishes input
|
||
before later phases. Tests execute this exact method with fake factories,
|
||
including GL success followed by input failure. Each concrete phase uses a
|
||
transactional acquisition scope so failure after any internal acquisition
|
||
unwinds the exact prefix.
|
||
|
||
Every acquired disposable is published to the lifetime owner immediately. A
|
||
composer that cannot publish incrementally must unwind its own partial prefix
|
||
before throwing. The portal-tunnel transfer remains explicit: publish fallback,
|
||
prepare GL resources, transfer to `LocalPlayerTeleportController`, then clear
|
||
the fallback before frame graphs are published.
|
||
|
||
Retained UI uses one `RetailUiRuntimeLease` retained by `GameWindowLifetime`
|
||
from Host construction through final teardown. The lease initially owns
|
||
`UiHost`, retains a partially constructed `RetailUiRuntime` before Initialize,
|
||
and exposes the runtime as a borrower only after Mount succeeds. Constructor
|
||
failure leaves Host in the lease; Initialize/cleanup failure leaves the exact
|
||
partial runtime in the lease for later retry; success makes lease disposal call
|
||
runtime disposal. There is no fallback-to-runtime ownership gap and never two
|
||
independent host disposers.
|
||
|
||
### 3.7 Lifetime owner
|
||
|
||
`GameWindowLifetime` owns the one `ResourceShutdownTransaction` and typed
|
||
teardown-only roots. It does not expose runtime service lookup or gameplay
|
||
operations. It exists before `Window.Create`, so window/bootstrap acquisitions
|
||
can publish their ownership immediately. Its transaction preserves §2.5 and
|
||
fixes these audited gaps:
|
||
|
||
- update and render orchestrators are both withdrawn;
|
||
- `TerrainAtlas` residency/textures are explicitly released after submitted
|
||
work and before GL;
|
||
- the dedicated sky `Shader` is explicitly disposed;
|
||
- a published `UiHost` is disposed even if `RetailUiRuntime.Mount` fails;
|
||
- window, dispatcher, pointer, combat, camera, and UI-root callbacks detach
|
||
before the owners they target.
|
||
|
||
## 4. Detailed checkpoint execution
|
||
|
||
### A — freeze and prune
|
||
|
||
- Add structural freeze tests for the current startup/session-start, input,
|
||
resize, update/render, and shutdown boundaries; later checkpoints replace
|
||
these with functional owner tests rather than treating source checks as final
|
||
acceptance.
|
||
- Extend `ResourceShutdownTransactionTests` for multiple same-stage failures,
|
||
failure then explicit retry, empty stages, and reentrant completion.
|
||
- Delete only proven dead/test-facade residue: `_capturedMouse`, obsolete
|
||
`_streamingRadius`, unused snap constants, `IsPlayerGuid`, `IsDoorName`, and
|
||
test-only forwarding helpers. Tests call canonical owners directly.
|
||
|
||
Result: the exact native attributes/callbacks, input subscription priority,
|
||
frame-root/session-start boundary, framebuffer behavior, shutdown stages, and
|
||
native-window-last edge are frozen. Dead duplicate state and two test-only
|
||
window facades are removed. Three corrected-diff reviews are clean; 56 focused
|
||
tests and the complete App suite (2,991 pass / 3 intentional skips) pass.
|
||
|
||
### B — native window binding and host quiescence
|
||
|
||
- Implement exact attach/reverse-detach/rollback and the no-throw quiescence
|
||
gate.
|
||
- Replace direct `Run` event wiring with one owned binding while preserving the
|
||
two Render handlers and their order.
|
||
- Add post-detach silence, failure-after-Nth-attach, repeated/reentrant Dispose,
|
||
callback-during-detach, and persistent physical-detach tests.
|
||
|
||
Result: one fixed typed binding now owns the exact nine Silk edges, including
|
||
the two ordered Render callbacks. Create/publish/Attach and the explicit
|
||
lifecycle state preserve cleanup ownership across partial event-accessor
|
||
failure, rollback failure, concurrent or reentrant shutdown, and condition
|
||
wakeups. The shared host gate drains admitted callbacks and makes failed
|
||
physical detach logically inert. Three corrected-diff review loops are clean;
|
||
54 focused tests, the App suite (3,027 pass / 3 intentional skips), and the full
|
||
Release suite (7,386 pass / 5 intentional skips) pass. No connected gate was
|
||
required because this checkpoint changes ownership only and preserves the
|
||
frozen callback behavior.
|
||
|
||
### C — live-session host and skill formula
|
||
|
||
- Extract selection/entry/reset/binding factories around the existing canonical
|
||
session, router, command, and reset owners.
|
||
- Before moving the formula, write pseudocode for
|
||
`SkillFormula::Calculate @ 0x00591960`, cross-check the DAT field semantics
|
||
against ACE plus a second reference, then add conformance tests. If current
|
||
behavior differs, register it rather than silently calling the ACE-only
|
||
formula retail-faithful inside a structural commit.
|
||
- Keep `LiveSessionController.Start` last; both disabled-live and
|
||
missing-credential starts must execute the reset path.
|
||
|
||
Result: `LiveSessionHost` now owns reset-plan construction, exact selection and
|
||
entered-world ordering, and create→attach route factories while resolving all
|
||
session/command/in-world state through the sole `LiveSessionController`.
|
||
Partially attached routes and every individual subscription edge retain failed
|
||
cleanup work; successful edges are never replayed, and reset/new generations
|
||
remain blocked until teardown converges. Named-retail research corrected the
|
||
former ACE-only skill shortcut to exact unsigned `SkillFormula::Calculate @
|
||
0x00591960` semantics. Three corrected-diff review loops are clean; focused
|
||
session/formula/ledger tests, the App suite (3,048 pass / 3 intentional skips),
|
||
Core.Net (548 pass), the Release build, and the full suite (7,408 pass / 5
|
||
intentional skips) pass. No connected gate was required for this ownership
|
||
checkpoint; the final connected lifecycle gate remains Checkpoint L.
|
||
|
||
### D — world environment
|
||
|
||
- Move loaded sky/day state, `RefreshSkyForCurrentDay`, `AdminEnvirons`,
|
||
WorldTime synchronization, provider swaps, and Weather changes verbatim into
|
||
`WorldEnvironmentController`.
|
||
- Preserve existing named-retail citations and registered sound adaptation;
|
||
diagnostics call typed commands rather than owning time/weather state.
|
||
|
||
Result: `WorldEnvironmentController` is now the sole owner of the clock,
|
||
loaded sky descriptor, selected day group, Weather state, server time sync,
|
||
AdminEnvirons bridge, and time/weather debug cycles. `GameWindow` preserves its
|
||
public readonly clock/weather aliases but only composes the owner into render
|
||
and session seams. Initialization is explicitly one-shot, and missing GameTime
|
||
restores the documented fallback origin instead of inheriting process-global
|
||
state. Named-oracle review corrected the day picker to
|
||
`SkyDesc::CalcPresentDayGroup @ 0x00500E10` and AdminEnvirons to
|
||
`CPlayerSystem::Handle_Admin__Environs @ 0x0055DE20`; TS-54/TS-55 now register
|
||
the carried centered-audio and complete fog/ambient/radar gaps. Three
|
||
corrected-diff reviews are clean; 17 focused tests, the App suite (3,059 pass /
|
||
3 intentional skips), the warning-free Release build, and the full suite
|
||
(7,419 pass / 5 intentional skips) pass. No connected gate was required for
|
||
this behavior-preserving ownership checkpoint.
|
||
|
||
### E — pointer, focus, and framebuffer resize
|
||
|
||
- Make SilkKeyboard/Mouse sources and dispatcher links reversible, then attach
|
||
the raw pointer owner in the existing pre-UiHost order.
|
||
- Late-bind the gameplay-frame slot without resubscribing or reordering input.
|
||
- Extract camera mode, lifecycle focus loss, scroll, and sensitivity state.
|
||
- Extract the independent framebuffer resize target and pin logical Window.Size
|
||
versus FramebufferSize behavior.
|
||
- Require rollback after every Nth event add and silence between every shutdown
|
||
stage for raw/retained/devtools device events.
|
||
|
||
Result: `CameraPointerInputController` now owns raw move, camera-mode cursor,
|
||
focus-loss, scroll, and per-mode sensitivity policy; its gameplay-frame edge is
|
||
late-bound without resubscription. Silk keyboard/mouse sources,
|
||
`InputDispatcher`, retained UI bindings, and the devtools
|
||
`QuiescentInputContext` are publish-before-attach, terminal after disposal,
|
||
transactional on every side-effecting event add, and retry only the physical
|
||
edges that remain pending. Mouse-only hosts retain camera/cursor behavior.
|
||
`FramebufferResizeController` is the sole physical framebuffer publisher and
|
||
seeds viewport/aspect/camera state from `FramebufferSize`, while logical UI and
|
||
frame dimensions remain `Window.Size`. Shutdown now performs no-throw logical
|
||
input cutoff, retires the live session, then removes physical callbacks; the
|
||
post-session mouse-look release therefore cannot send a final movement packet,
|
||
and a bad Silk remove accessor cannot block F653/transport teardown. TS-56
|
||
records the carried non-retail camera input scalars. Three corrected-diff review
|
||
loops are clean; 66 focused App tests, 9 dispatcher-lifetime tests, 5 Core input
|
||
integration tests, the warning-free production Release build, and the complete
|
||
Release suite (7,477 pass / 5 intentional skips) pass. `GameWindow` is 4,266
|
||
raw lines / 194 fields / 65 methods. No connected gate was required for this
|
||
behavior-preserving ownership checkpoint; Checkpoint L retains the final
|
||
connected lifecycle and visual gates.
|
||
|
||
### F — gameplay action and command routing
|
||
|
||
- Move the frozen action-priority graph into the sole Fired subscriber.
|
||
- Extract combat mode command behavior and diagnostic command routing to typed
|
||
owners; retained UI, selection, movement, player mode, and item interaction
|
||
remain canonical.
|
||
- Make Combat, Camera, dispatcher, and retained-root subscriptions named,
|
||
transactional, and symmetrically detached.
|
||
|
||
Result: `GameplayInputActionRouter` is the sole gameplay subscriber to
|
||
`InputDispatcher.Fired` and preserves the frozen pointer → scroll → combat →
|
||
Press/DoubleClick gate → retained UI → selection → movement → command
|
||
priority. Focused command owners now route combat, diagnostics, window/player
|
||
mode, item-target mode, and Escape without calling feature bodies on
|
||
`GameWindow`. `RetainedUiGameplayBinding` owns the outside-UI item-drop edge;
|
||
the combat/diagnostic deferred slots release their targets before session
|
||
retirement, and the retained binding detaches transactionally afterward.
|
||
Named-retail review
|
||
also corrected `ToggleCombatMode @ 0x0056C8C0`: active combat short-circuits to
|
||
peace without an equipment lookup, while an incompatible Held object prints
|
||
the exact retail notice and sends no mode request. Three corrected-diff review
|
||
loops are clean; 54 focused App tests, 20 Core combat tests, the App suite
|
||
(3,155 pass / 3 intentional skips), the Release build, and the complete suite
|
||
(7,524 pass / 5 intentional skips) pass. `GameWindow` is 4,057 raw lines / 198
|
||
fields / 54 methods.
|
||
|
||
### G — two-phase runtime settings
|
||
|
||
- Construct `RuntimeSettingsController` before Window.Create and source startup
|
||
Display/VSync/MSAA from it.
|
||
- After GL/camera/audio and before devtools/world, apply startup
|
||
display/pacing/audio and resolve the immutable quality snapshot consumed by
|
||
downstream factories.
|
||
- Late-bind typed runtime targets without replaying startup transitions, then
|
||
move runtime display/quality changes, frame-rate/UI-lock/combat preferences,
|
||
and toon-scoped state.
|
||
- Preserve restart-required quality behavior and test transitions/previews
|
||
without requiring GL.
|
||
|
||
**Implementation result:** one pre-window `RuntimeSettingsController` now owns
|
||
the concrete settings store, the immutable startup snapshot, current Display /
|
||
Audio / Gameplay / Chat / Character values, the active toon, resolved quality,
|
||
and the optional Settings draft. Startup applies pacing, window state, saved
|
||
FOV, and audio in order exactly once; a failed later substep retries without
|
||
replaying a successful earlier substep. Complete runtime targets bind only
|
||
after their borrowers exist and binding performs no replay. Display saves keep
|
||
the persistence → live window → snapshot → quality order; quality applies
|
||
alpha-to-coverage → anisotropy → render range → streaming → completion budget.
|
||
Combat preference changes merge only their three fields into a live draft, so
|
||
unrelated unsaved Gameplay edits survive. Saved FOV now applies without
|
||
devtools, correcting the old accidental SettingsVM gate. `/framerate` retains
|
||
the named-retail live toggle/notice and keeps acdream's shipped cross-launch
|
||
persistence as documented AP-121. UI-lock convergence requires target,
|
||
persistence, and Settings baseline publication; a failed substep keeps the
|
||
same-value command retryable. `GameWindow` is 3,663 raw lines / 162 fields / 37
|
||
methods. Thirty-five focused App settings/boundary tests, all 43
|
||
`SettingsVMTests`, the UI Abstractions Release suite (534 pass), and the App
|
||
Release suite (3,183 pass / 3 intentional skips) pass. All three independent
|
||
corrected-diff reviews are clean; the production App Release build has zero
|
||
warnings/errors, and the complete Release suite passes 7,553 tests / 5
|
||
intentional skips (17 existing test-project warnings remain tracked by #228).
|
||
|
||
### H — explicit single resource ownership and frame slots
|
||
|
||
- Publish TerrainAtlas and the dedicated sky Shader as separate lifetime roots;
|
||
renderers remain borrowers and retire before the sole atlas/shader disposal.
|
||
- Publish `RetailUiRuntimeLease` before Host/Mount acquisition; add separate
|
||
idempotent input deactivation and test constructor failure, Initialize failure
|
||
with successful cleanup, transient cleanup followed by lifetime retry,
|
||
persistent cleanup followed by terminal abandonment, and exactly one Host
|
||
disposal/no unowned Host on every path.
|
||
- Publish update/render roots only through `GameFrameGraphSlot` and withdraw the
|
||
slot after session convergence.
|
||
- Pin portal fallback/transfer unchanged and test every normal/partial prefix
|
||
for no leak or double delete.
|
||
- Make `Shader` construction and `TerrainAtlas.Build` internally exception-safe;
|
||
phase scopes cannot recover GL names created inside a factory that throws.
|
||
Inject GL compile/link/texture-allocation failure after every internal name
|
||
and prove exact rollback/residency release.
|
||
|
||
Checkpoint H is complete. `GameRenderResourceLifetime` is the sole terrain-
|
||
atlas and dedicated-sky-shader owner; renderers borrow and retire before those
|
||
roots. `RetailUiRuntimeLease` retains the Host/runtime chain through partial
|
||
construction, input cutoff, retryable disposal, and explicit terminal
|
||
abandonment. `GameFrameGraphSlot` publishes update/render roots atomically, and
|
||
the portal tunnel now uses one prepare-aware fallback/transfer transaction.
|
||
Shader, terrain, text-renderer, buffer/VAO, bindless, and texture-binding
|
||
construction/mutation paths use always-on checked GL commit boundaries. A
|
||
failed cleanup retains the exact name/handle/binding obligation for retry, and
|
||
successful substeps never replay. `GameWindow` is 3,689 raw lines / 162 fields
|
||
/ 37 methods. The focused ownership gate passes 61 tests, the App Release suite
|
||
passes 3,236 tests / 3 intentional skips, and all three independent final
|
||
corrected-diff reviews are clean. The complete Release suite passes 7,606 tests
|
||
/ 5 intentional skips; the 17 existing test-project warnings remain tracked by
|
||
#228.
|
||
|
||
### I — ordered production composition
|
||
|
||
- Split the 2,289-line `OnLoad` body at §3.6 boundaries using the fixed typed
|
||
production pipeline, small immutable phase results, and no stored mega-context.
|
||
- Remove retained GameWindow captures via stable locals, typed adapters, and
|
||
existing deferred slots.
|
||
- Run the same pipeline with fake acquisition phases and inject failure after
|
||
platform, GL, input, DAT/effects, render resources, retained UI, live/session,
|
||
teleport transfer, and frame-root publication. Each concrete phase also
|
||
proves transactional rollback of its internal acquisition prefix.
|
||
|
||
### J — lifetime cutover and shutdown failure policy
|
||
|
||
- Move the teardown-only stage records and shutdown manifest out of GameWindow.
|
||
- Co-stage no-throw quiescence and live-session convergence as hard operations;
|
||
retry/report physical detach as non-blocking operations, withdraw both frame
|
||
graphs next, then preserve the remaining frozen order.
|
||
- Cover constructed-never-run, Load-never-fired, normal WM_CLOSE, WM_CLOSE then
|
||
Dispose, direct/repeated/reentrant Dispose, transient/persistent failures,
|
||
session deferred disposal, optional-devtools absence, GPU-drain failure, and
|
||
every partial-load prefix.
|
||
- Verify acquired owners dispose exactly once, unacquired owners never dispose,
|
||
completed operations never replay, and native-window fallback is last and
|
||
explicitly reported incomplete when a non-cancellable close cannot converge.
|
||
- Verify persistent physical-detach failure still retires downstream owners and
|
||
is reported, while persistent session/GPU barriers still protect dependents;
|
||
after native fallback the terminal abandoned state makes repeated Dispose
|
||
inert.
|
||
|
||
### K — canonical soak checkpoints (#232, separate commit)
|
||
|
||
- Extract `WorldLifecycleResourceSnapshotSource` from the window and reuse the
|
||
existing render diagnostics source where fields overlap.
|
||
- Change `checkpoint <name>` from an immediate retained-UI-time capture to a
|
||
FIFO request with an acknowledgement token. The script runner treats that
|
||
token as a command barrier: it executes no later command until the token is
|
||
resolved. After private presentation returns and
|
||
`RenderFrameDiagnosticsController.Publish` has published the current frame,
|
||
a post-diagnostics checkpoint phase drains requests using that same immutable
|
||
`RenderFrameOutcome` plus one sample from every canonical source. This avoids
|
||
previous-frame diagnostics mixed with current-frame owner counts.
|
||
- Mark the token Succeeded only after serialization/write completes. Deferred
|
||
capture/write failure marks it Failed, and the runner stops with that exact
|
||
error on its next tick. Preserve request sequence/name across the deferred
|
||
edge, drain independent producers in FIFO order, and cancel pending tokens
|
||
with an explicit shutdown result.
|
||
- Store one token against the active script-command index. Repeated pending
|
||
ticks poll that token and never enqueue again—even when a render failed before
|
||
the post-diagnostics drain. Clear it only after Success, Failure, or
|
||
Cancellation is consumed; one command therefore produces one request, write,
|
||
and sequence increment.
|
||
- Add exactly nine ordered named `checkpoint` commands, parse the JSONL into the
|
||
soak report, and assert checkpoint identity/order.
|
||
- Gate pending teardown, retirement, staging, and warmup at zero; compare exact
|
||
canonical owner/cache counts and bytes at Caul return → Caul plateau; label
|
||
authoritative entity/animation population changes as workload warnings.
|
||
- Preserve process working/private memory as a separately labelled secondary
|
||
residency guard with unchanged thresholds. Require two fresh-process clean
|
||
route runs before closing #232.
|
||
|
||
### L — final closeout
|
||
|
||
- Run three independent corrected-diff reviews: retail conformance,
|
||
architecture/integration, and adversarial lifecycle/failure analysis.
|
||
- Resolve every confirmed finding and re-review until clean.
|
||
- Run focused App/Core/UI tests, Release build, and the complete Release suite.
|
||
- Run connected lifecycle/reconnect and two fresh-process synchronized
|
||
nine-stop soaks; compare stable framebuffer checkpoints with Slice 7.
|
||
- Audit every active divergence row that still points at `GameWindow`; retarget
|
||
ownership paths only. Keep TS-53 and other unresolved mechanisms open.
|
||
- Update architecture, milestones, roadmap, issues, AGENTS/CLAUDE, and durable
|
||
memory with final metrics and exact gates.
|
||
|
||
## 5. Automated acceptance
|
||
|
||
- exact window callback attach order, reverse detach, rollback, reentrancy, and
|
||
post-detach silence;
|
||
- transactional rollback after every Nth add for all multi-event bindings, plus
|
||
retained/devtools/raw device silence between every shutdown stage;
|
||
- exact input subscription/priority order and focus/reset/close semantics;
|
||
- exact resize order and non-positive-size rejection;
|
||
- disabled-live and missing-credentials startup still perform reset; live
|
||
startup remains last;
|
||
- PView still reads FramebufferSize while RenderFrameInput, retained UI,
|
||
portal/private presentation, and screenshots share one Window.Size snapshot;
|
||
- exact session reset trace and current-session borrowing;
|
||
- partial-load rollback at every composition checkpoint;
|
||
- TerrainAtlas, sky Shader, UiHost, frame graphs, GPU-flight, DAT, input, GL,
|
||
and native window have explicit tested lifetimes; terrain/sky renderers remain
|
||
borrowers and cannot also dispose the separately owned atlas/shader;
|
||
- throwing Shader/TerrainAtlas leaf factories release every internal GL name
|
||
and bindless-residency prefix;
|
||
- shutdown retry/all-attempted/no-replay semantics remain intact;
|
||
- persistent non-cancellable close fallback names the blocked stage and never
|
||
reports a clean shutdown;
|
||
- completed teardown with soft-detach failures reports immutable
|
||
`CompleteWithCleanupFailures`, and repeated Dispose is inert;
|
||
- no anonymous MouseMove subscription and no substantial Silk callback body;
|
||
- no stored `GameWindow`, broad service locator, or feature-owner callback
|
||
facade into the window; the fixed native `WindowCallbackTargets` binding is
|
||
the intentional host-boundary exception;
|
||
- `OnLoad` is ordered composition, `OnUpdate`/`OnRender` remain one handoff,
|
||
and `OnClosing`/`Dispose` delegate to one lifetime owner;
|
||
- all canonical soak snapshots are post-diagnostics same-frame atomic,
|
||
round-trip, preserve request/name order across the deferred edge and nine-name
|
||
order across two fresh processes, and name the exact owner/property on growth;
|
||
- checkpoint acknowledgement blocks following script commands, propagates
|
||
deferred write failure, and reports shutdown cancellation;
|
||
- repeated pending ticks enqueue exactly once and cannot duplicate a checkpoint
|
||
after a failed/delayed render;
|
||
- complete Release suite, lifecycle gate, soak gate, and framebuffer comparison
|
||
pass.
|
||
|
||
## 6. Final visual handoff
|
||
|
||
The only user pause is one connected visual smoke gate after all automation is
|
||
green:
|
||
|
||
- first login world bootstrap and radar;
|
||
- movement, mouse look/orbit, resize, focus loss/return, combat toggle;
|
||
- inventory/skills/spellbook shared panel and retained UI input;
|
||
- outdoor, building, dungeon, portal/recall, paperdoll, particles, alpha;
|
||
- graceful close and fresh-process reconnect.
|
||
|
||
After that pass the GameWindow structural campaign is complete. The roadmap
|
||
returns to the carried M3 magic/spell-bar/spellbook/component-book and final
|
||
two-client portal-out/materialization visual gates before new M4 feature work.
|