26 KiB
GameWindow Slice 6 — update-frame orchestration
Status: Active 2026-07-21.
Parent program: docs/architecture/code-structure.md, Slice 6.
Baseline: d4ecac1d; GameWindow.cs is 8,811 raw lines, 247 fields, and
153 methods. Release baseline: 7,054 passed / 5 skipped. The connected
lifecycle gate and synchronized nine-stop resource soak both pass.
Behavior rule: This is an ownership extraction. Preserve the accepted
retail object/network order, asynchronous streaming-readiness adaptation,
portal transit, input semantics, and camera presentation. A defect exposed by
the boundary receives its own deterministic oracle and bisectable fix; it is
never hidden behind a retry, delay, suppression flag, or reordered callback.
Progress ledger
- A — freeze the complete production phase graph and introduce the typed orchestration contract plus structural/order guards.
- B — extract the pre-network live-object presentation phase and the
non-advancing post-network spatial reconciler; remove callbacks into
GameWindowfromRetailLiveFrameCoordinator. - C — extract streaming-origin convergence, observer selection, dungeon collapse, streaming tick, and rescued-entity rebucketing.
- D — extract dispatcher/raw-mouse/combat input sampling without changing movement-command or UI-capture semantics.
- E — extract the complete local-player teleport owner and wire it between the existing post-network liveness and player-mode auto-entry phases.
- F — extract the shared player-mode lifecycle plus fly/chase/player
camera presentation and cut production over to one
UpdateFrameOrchestrator. - G — delete the old
OnUpdatebodies, callback facades, and obsolete frame state; run focused corrected-diff reviews after each ownership edge. - H — full Release suite, connected lifecycle/reconnect gate, synchronized resource soak, documentation, durable memory, and line/field/method closeout.
Every checked checkpoint is committed as one bisectable architectural unit. Documentation records the exact commit and accepted test count as work lands.
Checkpoint A introduced the internal AcDream.App.Update contract, one typed
UpdateFrameClock/IPhysicsScriptTimeSource, the twelve-phase outer-order
oracle, exact teardown and invalid-delta tests, and guards against a transitive
GameWindow clock capture. Production now publishes PES time once per update;
18 focused Release tests and the full 2,709-test App project pass, and all
three corrected-diff reviews are clean.
Checkpoint B moved the complete local/ordinary/static/effect body into
LiveObjectFrameController, made RetailLiveFrameCoordinator's
object → inbound batch → command → spatial-reconcile barrier fully typed,
and removed animation/runtime/projectile back-references to GameWindow.
GameWindow.cs is now 8,716 raw lines. The corrected diff passes 239 focused
Release tests, the 2,732-test App suite, and the full 7,090-test Release suite
(5 fixture/environment skips); all three independent reviews are clean.
Checkpoint C moved retained origin convergence, readiness, observer selection,
sealed-dungeon collapse, streaming publication, and persistent live-projection
rescue into StreamingFrameController. Production keeps streaming publication
ahead of input and the live object/network barrier, so a same-frame
CreateObject can enter the newly resident bucket. The focused 146-test Release
selection, 2,754-test App suite, and complete 7,112-test Release suite (5
fixture/environment skips) pass. Login-before-position, portal hold, outdoor
and sealed/SeenOutside EnvCells, pending recenter, exact rescue rebucketing,
GUID deletion, and session reset are pinned; all three corrected-diff reviews
are clean. GameWindow.cs is now 8,569 raw lines.
Checkpoint D moved semantic hold delivery, movement sampling, autorun,
instant mouse-look, cursor capture, raw filtering/idle handling, and combat
intent into GameplayInputFrameController and focused typed owners. Devtools
keyboard capture pauses all gameplay movement without clearing the autorun
latch; retained chat suppresses held keys while autorun continues. Every
focus, teleport, camera/player-mode, reset, and shutdown boundary now releases
mouse-look through one lifecycle path and clears stale RMB orbit state.
Production combat requests, automatic hostile selection, UI-capture probes,
and movement-truth diagnostics no longer retain callbacks into GameWindow.
The 2,787-test App suite and complete 7,145-test Release suite pass (5
fixture/environment skips); all three corrected-diff reviews are clean.
GameWindow.cs is now 8,205 raw lines.
1. Outcome and non-goals
At slice exit, GameWindow.OnUpdate starts the profiler scope and delegates one
host delta to UpdateFrameOrchestrator. GameWindow composes concrete owners;
it no longer selects a streaming observer, advances animations/effects,
samples raw mouse input, sequences inbound network traffic, advances portal
transit, or updates gameplay cameras.
The owner graph is explicit rather than a list of anonymous callbacks:
UpdateFrameOrchestrator
├── live teardown convergence retry retained tombstones
├── UpdateFrameClock normalized host time / PES clock
├── StreamingFrameController residence/readiness pre-pass
├── GameplayInputFrameController semantic input + mouse/combat intent
├── RetailLiveFrameCoordinator retail SmartBox object barrier
│ ├── LiveObjectFrameController local/ordinary/static/effect tails
│ ├── LiveSessionController inbound network drain
│ ├── RetailLocalPlayerFrame CommandInterpreter position tail
│ └── LiveSpatialReconciler non-advancing authoritative refresh
├── LiveEntityLivenessController expiry after accepted inbound state
├── LocalPlayerTeleportController local transit/reveal/tunnel state
├── PlayerModeAutoEntry one-shot ready-world entry
├── PlayerModeController enter/exit/current-player lifetime
└── CameraFrameController fly/chase/player presentation
Concrete names may be narrowed during checkpoint A's source audit, but the
ownership boundaries and order do not change. The orchestrator receives a
small immutable per-frame input and concrete phase owners. It does not receive
a hundred delegates, one mega IUpdateFrameServices context, or a service
locator, and no transitive update collaborator calls a substantial method back
on GameWindow.
This slice does not change simulation rate, object quantum rules, movement speed, interpolation, streaming radii, reveal readiness, portal visuals, particle range, combat cadence, key bindings, camera formulas, render order, or shutdown order. It adds no GL work and does not move retained-UI timers out of their render seam. Slice 7 owns the draw graph; Slice 8 owns final composition and Silk callback cleanup.
2. Retail oracle and accepted adaptation
The object/network barrier is pinned by
2026-07-19-r6-update-object-order-pseudocode.md:
CPhysicsObj::update_object @ 0x00515D10applies the strict object quantum;CPhysicsObj::UpdatePositionInternal @ 0x00512C30advances the PartArray, position/physics candidate, then processes semantic hooks;CPhysicsObj::UpdateObjectInternal @ 0x005156B0runs Detection → Target → Movement →CPartArray::HandleMovement→ Position → Particle → Script;SmartBox::UseTime @ 0x00455410advances object/physics time before the inbound queue and then callsCommandInterpreter::UseTime;gmSmartBoxUI::UseTime @ 0x004D6E30owns UI/viewport time outside that object barrier.
At the retail host level, Client::UseTime @ 0x00411C40 calls
UIElementManager::UseTime before SmartBox::UseTime; the UI time broadcast
reaches gmSmartBoxUI::UseTime. Retail also publishes player-camera work from
the physics/player callback rather than from one post-network camera tail.
acdream currently advances teleport/UI-camera presentation after its
object/network/command barrier. Slice 6 preserves that existing order as
registered TS-53; it does not cite gmSmartBoxUI as proof that the current
post-network placement is retail-exact.
acdream preserves two documented presentation adaptations:
- only semantic
AnimationDoneexecutes at retail's in-object hook slot; every other captured animation hook retains the current deferred path after final root, part, and equipped-child poses are published, including particles, audio, lights, translucency,CallPES, and default-script chaining (TS-50); - shared particle simulation and PhysicsScript queues tick once per host update after both worksets rather than once per object's admitted quantum. This also means the current shared Particle → Script order does not reproduce the static workset's retail Script → Particle → hooks order (TS-51).
Asynchronous landblock streaming has no one-call retail equivalent. Its
convergence pre-pass remains before inbound CreateObject dispatch so a newly
accepted projection can find a resident bucket in the same frame. This
pre-pass does not advance an object's retail clock. WorldRevealCoordinator
remains the single login/portal readiness owner.
3. Fixed production phase order
The complete host-update graph is frozen as follows:
- retry canonical live-entity teardown tombstones; an
AggregateExceptionis logged and later phases continue, while any other exception propagates; - normalize the host delta, advance the shared PES clock once, and publish it;
- advance pending origin retirement/recenter, select the observer, tick streaming, and rebucket rescued persistent entities;
- tick
InputDispatcher, consume one raw mouse sample, and advance combat attack intent; - enter the non-reentrant inbound-event envelope and advance the pre-network
live-object phase:
- local object and pre-network movement/jump output;
- queued selection/use output after movement edges;
- ordinary live objects;
- the distinct static-animation workset;
- equipped children and static hook completion;
- fades, every deferred non-
AnimationDonehook, and current effect/light anchors; - particle visibility and particle simulation;
- PhysicsScript queues;
- drain inbound session traffic inside one
GpuWorldState.MutationBatch; - run the local player's post-network
CommandInterpreterposition check; - perform the ordinary post-network non-advancing root/child/emitter/light reconcile;
- advance liveness expiry from the absolute client timer;
- activate/tick local teleport transit. A Place edge commits the player and live root, immediately performs its own non-advancing reconcile, then notifies world reveal and continues tunnel state;
- evaluate one-shot player-mode auto-entry;
- update fly or chase/player camera presentation. If the local player was first created by this frame's inbound pass, publish its non-advancing projection, immediately reconcile a second time, then update the camera.
Those three reconcile edges—ordinary post-network, teleport Place, and inbound-created-player projection—are separate accepted production edges. Any future consolidation is a behavior change with its own oracle, not part of this extraction.
An invalid/non-positive host delta becomes zero for the PES/object/particle/ tunnel delta but never blocks inbound traffic, absolute-time liveness, recenter convergence, or camera-state publication. Raw mouse idle uses its independent monotonic input clock. A missing gameplay camera/input owner never suppresses outer phases; the streaming controller may skip observer-driven streaming when it has no valid live or offline observer, while origin retirement still advances. Devtools keyboard capture pauses player/camera control including autorun; retained chat capture suppresses held WASD while autorun continues; mouse capture ends mouse-look but does not suppress keyboard, combat, network, streaming, teleport, or teardown.
4. Architecture and interfaces
4.1 Per-frame input
Use an immutable value equivalent to:
public readonly record struct UpdateFrameInput(double HostDeltaSeconds);
Derived facts are read by their owning controller from stable runtime owners
or from a narrowly typed state source. Do not pass GameWindow, expose its
private fields wholesale, or create a service locator. Mutable player/session
owners that legitimately change during login are represented by one focused
state/source interface per domain, not anonymous closures into window methods.
UpdateFrameClock is constructed before any materializer/network owner that
needs the current PES time. It owns the normalized host/PES clock and exposes a
narrow CurrentScriptTime source; _physicsScriptGameTime does not remain a
GameWindow field captured by later owners. Absolute liveness time and raw
mouse idle time remain distinct monotonic sources and are not derived from the
normalized simulation delta.
4.2 Object phase
LiveObjectFrameController owns the body currently in
AdvanceLiveObjectRuntimeCore. It composes existing focused owners and keeps
their retail order; it does not absorb their algorithms or identity maps.
LiveSpatialPresentationReconciler owns the current no-time refresh body.
RetailLiveFrameCoordinator depends on those typed owners plus the existing
session and local-player owners. Its public Tick remains the one explicit
object → network → command → reconcile barrier.
RetailInboundEventDispatcher continues wrapping the complete object phase.
Reentrant packets queue until the current object's manager tail completes.
The hot path remains allocation-free after warmup.
The extraction inventory includes the complete transitive update graph, not
only RetailLiveFrameCoordinator: RetailLocalPlayerFrameController loses
substantial window captures in favor of a focused local-player slot, movement
input source, outbound owner, and projection owner;
LiveEntityAnimationPresenter receives a concrete animation-presentation
context instead of retaining GameWindow as
ILiveAnimationPresentationContext; scheduler clock/player/projectile sources
are similarly narrow. Existing small value/policy callbacks are acceptable
only when they do not call a substantial body back on the window or create a
second identity/state owner.
4.3 Streaming phase
StreamingFrameController owns observer selection and the sealed-dungeon
gate. It receives the existing StreamingOriginRecenterCoordinator,
StreamingController, LiveWorldOriginState/center state, camera/player
state, physics current cell, GpuWorldState, and LiveEntityRuntime through
typed seams. It never builds, publishes, or retires a landblock; those remain
Slice 5 owners.
The controller preserves:
- no pre-login hardcoded-Holtburg streaming;
- real-player-origin readiness before first streaming;
- destination-origin pinning while in portal space;
- authoritative player landblock during the login hold;
- offline fly-camera observation only outside live in-world mode;
- sealed EnvCell collapse using the cell's own landblock identity;
- teleport-hold suppression of the stale source-cell dungeon gate;
- rescued-entity rebucketing after the accepted streaming tick.
4.4 Input and camera phases
GameplayInputFrameController owns per-frame dispatcher hold delivery,
UI-capture transitions, raw mouse sample/filter/idle handling, combat attack
ticking, and the complete mouse-look/cursor-capture lifetime. It is the single
owner of mouse-look state, saved cursor mode, begin/end/hide/restore, and
movement teardown across press/release, capture change, focus loss, teleport,
session reset, player-mode exit, and shutdown. Action press/release callbacks
remain on InputDispatcher and movement stays owned by
PlayerMovementController.
PlayerModeController owns enter/exit/toggle construction and the current
player-controller, physics-host, chase-camera, and chase-mode slot. Manual
input, PlayerModeAutoEntry, and local teleport activation all enter through
that one owner. Streaming, camera, local-frame, and teleport owners receive
narrow read sources from it; no path calls the substantial current
EnterPlayerModeNow/exit bodies back on GameWindow or duplicates player-mode
state.
CameraFrameController owns held fly-camera controls, retail chase offsets,
post-network player presentation sampling, and both chase-camera updates. It
does not own physics, target selection, or camera formulas. The local player
created by the inbound pass gets a non-advancing projection before its first
draw exactly as today, followed by the current second spatial reconcile before
the camera samples that projection.
4.5 Teleport phase
LocalPlayerTeleportController owns only the current local-player transit
state machine and presentation edges. It composes
TeleportTransitCoordinator,
TeleportViewPlaneController, PortalTunnelPresentation,
WorldRevealCoordinator, streaming priority, and the session LoginComplete
send. Checkpoint E first extracts the currently window-owned local aim,
destination, hold/forced, activation, placement, reset, and session-clear
state into this owner plus a concrete local placement collaborator where
needed. It does not reuse RemoteTeleportPlacement, and network/session sinks
do not retain AimTeleportDestination or reset callbacks into GameWindow.
LiveEntityLivenessController, tombstone retry, and PlayerModeAutoEntry
remain separate focused phases; teleport does not absorb them.
4.6 Composition, thread, and lifetime
The clock and focused dynamic sources are constructed before their first
consumer. The final orchestrator is composed exactly once at the end of a
successful OnLoad; OnUpdate fails fast if that invariant is broken rather
than running a nullable partial graph. Every phase mutates state only on the
existing update/render thread. Streaming worker completion remains behind
StreamingController.Tick; the extraction adds no lock, task, or async entry.
Phase controllers borrow their input, session, streaming, portal, camera, and
render-presentation owners; they do not dispose shared dependencies. Any new
event subscription has explicit symmetric detach. During shutdown the update
collaborators remain alive through the first-stage LiveSessionController
reset, then retire before their underlying input/camera/portal/streaming
resources. Existing retryable shutdown ordering remains authoritative.
5. Checkpoints and acceptance
A — contract and guards
- record the exact production phase graph and every mutable dependency;
- correct and pin the retail oracle for physics-hook retention/reentrant append, inactive object behavior, and static Script → Particle → hooks; pin the existing TS-50/TS-51 divergences without reordering runtime behavior in this ownership-only slice;
- add an explicit orchestrator order test covering all twelve phases and the three conditional reconcile edges;
- pin normalized simulation/PES/particle/tunnel time separately from absolute
liveness and raw-mouse idle clocks for NaN, both infinities, non-positive
values, and finite values above
float.MaxValue; - pin missing live/offline camera behavior and the separate devtools keyboard, retained-chat keyboard, and mouse-capture contracts;
- pin teardown retry success,
AggregateExceptionlog-and-continue, other exception propagation, and next-frame retry; - add source guards that prevent
OnUpdateor the coordinator from regaining substantial callback bodies, and prevent any transitive update owner from retainingGameWindowor one mega service context; - verify current production traces before cutover.
B — object/network barrier
- move the pre-network animation/effect body into
LiveObjectFrameController; - move no-time root/child/effect refresh into
LiveSpatialPresentationReconciler; - replace coordinator callbacks with typed collaborators;
- preserve semantic/visual hook separation, owner-incarnation revalidation, static-after-ordinary order, ordinary Particle-before-Script, the current shared-tail TS-51 adaptation, and once-per-host tails;
- delete
AdvanceLiveObjectRuntimeCoreandReconcileLiveObjectSpatialPresentationfromGameWindow.
Focused tests cover RetailObjectActivityGate and
RetailObjectQuantumClock: parented/cell-less/Frozen suspension, inactive
Particle/Script-only time, Hidden+Active adjustment/hooks/manager time, and
separate ordinary epsilon/Min/Max/Huge plus static epsilon/Huge boundaries.
They also cover incomplete physics-hook
retention, same-drain animation-hook append, deferred CallPES/default-script/
translucency routing, physics-hook append deferred until the next drain,
completed physics-hook removal preserving the snapshotted successor, static
hooks, callback GUID reuse, and reentrant inbound packets. Particle then Script
must run exactly once for an empty world,
multiple ordinary/static owners, invalid delta, and reentrant inbound work;
the one published PES clock remains visible from hook capture through the
script tick.
Checkpoint B removes the coordinator, animation presenter/scheduler, runtime
view, static-root, and projectile callbacks into GameWindow. The callback
composition still retained by RetailLocalPlayerFrameController is explicitly
deferred: checkpoint D owns its input/capture sources and checkpoint F owns its
mutable player-mode/controller/session sources. A structural test permits that
one named legacy owner only; D/F must remove the exception before final cutover.
C — streaming frame
- extract readiness/recenter advancement, observer selection, dungeon gate, streaming tick, and rescued projection rebucketing;
- test login-before-player-spawn, outdoor/dungeon/portal-hold/offline observer cases, missing live/offline camera sources, and cell-landblock identity;
- prove same-frame streaming completion precedes CreateObject projection into the resident bucket, unknown pre-login origin enqueues nothing, and recenter retirement continues while ordinary streaming is gated;
- verify no build/publication/retirement ownership leaks back from Slice 5.
D — input frame
- extract dispatcher tick, UI mouse-capture edge, raw sample/filter/idle path, combat intent tick, and the one cross-lifetime mouse-look/cursor owner;
- test capture entry while mouse-look is active, zero-idle drift stop, production action ordering, devtools-vs-chat autorun semantics, mouse-only capture, and focus/teleport teardown;
- prove every non-input phase still runs under each capture mode;
- retain diagnostic semantic input as the unattended connected gate seam.
E — local teleport and post-network world frame
- extract the currently window-owned local-player teleport state and every aim/destination/activation/place/reset/session port before moving its tick;
- retain liveness and auto-entry as separate orchestrator calls around pending transit activation, readiness/hold, tunnel events, materialization, and reveal completion;
- test loaded and pending destinations, forced-readiness boundary, same-cell respawn, LoginComplete ordering, delete/reset, and no second reveal owner;
- pin Place → immediate reconcile → reveal and old-GUID liveness candidate → inbound delete/recreate generation safety;
- preserve the completed portal visuals and world-reveal barrier unchanged.
F/G — camera, cutover, and deletion
- extract player-mode enter/exit/toggle and current-owner state before camera
or teleport cutover; retain
PlayerModeAutoEntryonly as its one-shot readiness guard; - extract fly camera, retail chase adjustment, local post-network projection, combat target tracking, and chase-camera publication;
- pin inbound-created projection → immediate second reconcile → camera, with exact root/child/emitter/light refresh counts and no duplicate time advance;
- compose
UpdateFrameOrchestratoronly after all dependencies exist; - replace
OnUpdatewith profiler scope plus oneTickcall; - remove obsolete frame fields/facades and prove there is no back-reference to
GameWindowfrom an extracted owner; - measure line/field/method count. The target is below 8,000 lines, but the ownership/ordering exit criteria outrank the count.
H — release and connected gates
After all three independent corrected-diff reviews are clean:
- run every focused App/Core/Net/UI test project;
- run
dotnet build AcDream.slnx -c Release; - run the full Release suite;
- run the connected lifecycle/reconnect gate;
- run the synchronized nine-stop resource soak with movement, turn, jump, combat, portal materialization waits, fatal-log scan, and graceful close;
- compare lifecycle, outbound movement, materialization, entity/animation, frame-profile, resource, and teardown traces with the Slice 5 baseline;
- audit the divergence register, architecture, milestones, roadmap, issues,
AGENTS.md,CLAUDE.md, and durable memory.
The slice requires no new visual behavior. If connected screenshots or traces change, return to the responsible checkpoint; do not defer the difference to Slice 7.
6. Review gate for every checkpoint
After each implementation checkpoint:
- run focused tests and inspect the complete diff;
- retail-conformance review checks named functions, exact order, Hidden/ quantum semantics, and adaptation boundaries;
- architecture review checks dependency direction, absence of a replacement god object/service locator, same-thread mutation, and symmetric teardown;
- adversarial review checks independent clocks, callback reentrancy, owner/GUID reuse, login/portal transitions, capture modes, missing observer sources, teardown failures, and early-return phase suppression;
- fix every confirmed root cause and repeat review until clean;
- build/test Release and commit the checkpoint before starting the next one.
7. Divergence and documentation bookkeeping
TS-50, TS-51, and the newly documented existing host-order TS-53 remain until
their exact adaptations are ported; this slice must not overclaim retail
parity. IA-14/AD-22 and the lifetime/resource rows are audited against the
final owner graph but changed only if their mechanism actually changes. Any
newly discovered unavoidable adaptation gets a register row in the same
commit. The Slice 6 closeout updates the code-structure table,
roadmap, milestones, session docs, and
project_gamewindow_decomposition.md with exact commits, test counts, gates,
and final metrics.