docs(architecture): plan GameWindow lifecycle shell
Freeze host ordering, resource ownership, callback quiescence, partial-startup rollback, and canonical soak evidence as twelve reviewed, bisectable Slice 8 checkpoints. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
96f8bfcfa8
commit
20ebf217e2
3 changed files with 615 additions and 0 deletions
|
|
@ -676,6 +676,9 @@ residency without weakening leak detection.
|
|||
|
||||
#### Slice 8 — composition and shutdown cleanup — ACTIVE
|
||||
|
||||
Detailed execution ledger:
|
||||
[`docs/plans/2026-07-22-gamewindow-slice-8-composition-lifecycle.md`](../plans/2026-07-22-gamewindow-slice-8-composition-lifecycle.md).
|
||||
|
||||
Keep GL/window construction in `GameWindow.OnLoad`, but group creation into
|
||||
small composition functions and delete feature state left behind by prior
|
||||
slices. `OnClosing` delegates to the existing retryable shutdown transaction.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,610 @@
|
|||
# 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
|
||||
|
||||
- [ ] A — freeze construction/callback/shutdown order and delete proven dead or
|
||||
test-facade residue.
|
||||
- [ ] B — make native-window callback intake an explicit reversible owner and
|
||||
define host-quiescence failure semantics.
|
||||
- [ ] C — extract live-session host/reset/binding callbacks and verify the
|
||||
embedded skill formula against named retail.
|
||||
- [ ] D — extract retail world-environment/day/weather behavior.
|
||||
- [ ] E — extract two-phase raw pointer/camera input, focus, and framebuffer
|
||||
resize behind symmetric named subscriptions.
|
||||
- [ ] F — extract the sole gameplay input-action router plus focused combat and
|
||||
diagnostic command owners.
|
||||
- [ ] G — extract two-phase persisted settings/display/quality ownership.
|
||||
- [ ] 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.
|
||||
- [ ] 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 Attach(
|
||||
IWindow window,
|
||||
WindowCallbackTargets targets,
|
||||
DisplayFramePacingController pacing);
|
||||
}
|
||||
```
|
||||
|
||||
`WindowCallbackTargets` is a fixed typed set for Load, Update, Render, Closing,
|
||||
FocusChanged, and FramebufferResize. It is not a general callback list. Attach
|
||||
rolls back in reverse order if any registration fails; Dispose detaches in
|
||||
reverse registration order, is idempotent/reentrant-safe, and makes later
|
||||
window events inert. The separate pacing Render callback remains ordered after
|
||||
main Render. Every callback first checks the shared no-throw host-quiescence
|
||||
gate, so 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue