diff --git a/AGENTS.md b/AGENTS.md index 30fb71a3..209851cb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -115,7 +115,7 @@ and the final two-client portal-out/materialization observer flow are user-gated. Deterministic world-lifecycle automation protects fresh login, outdoor/world-edge/dungeon travel, same-location revisit, exact graceful disconnect, and uncapped fresh-process reconnect with JSON/PNG artifacts. -Release builds; 7,054 tests pass / 5 skip. +Release builds; 7,182 tests pass / 5 skip. **Active prerequisite before new M4 subsystem work:** continue the behavior-preserving `GameWindow` decomposition reconciled on 2026-07-21. @@ -123,18 +123,21 @@ Selection/interaction Slice 1, live-animation-presentation Slice 2, complete live-session ownership Slice 3, and live-entity App integration Slice 4 are landed. Focused hydration, teardown, inbound-authority, network-update, and motion-runtime owners now sit over canonical `LiveEntityRuntime` without a -second GUID dictionary. Slice 5 is complete: the complete DAT -build, render/physics/static publication, retryable retirement, and shared- -origin lifetime barrier are out of `GameWindow`; it is now 8,811 raw lines / -247 fields / 153 methods, down 44% from the 15,723-line campaign baseline. -The lifecycle/reconnect and synchronized nine-stop soak gates pass. Slice 6 -update-frame orchestration is active; later slices finish render orchestration -and composition cleanup. +second GUID dictionary. Slice 5 landblock presentation and Slice 6 update-frame +orchestration are complete. Streaming, input, the object/network barrier, +liveness, teleport, player mode, and camera presentation have typed phase +owners; `GameWindow.OnUpdate` is one orchestrator handoff with no substantial +transitive callback facade. `GameWindow` is 7,026 raw lines / 241 fields / 108 +methods, down 8,697 lines (55.3%) from the 15,723-line campaign baseline. The +7,182-test Release suite, 314-second lifecycle/reconnect gate, and 394-second +synchronized nine-stop soak pass. Slice 7 draw-frame orchestration is active; +Slice 8 finishes composition and Silk callback cleanup. See `docs/architecture/code-structure.md`. **Carried:** #153, #116, remaining -R6 ownership cleanup, TS-50/TS-51, Modern Pipeline MP1b+, and #225's +R6 ownership cleanup, TS-50/TS-51/TS-53, Modern Pipeline MP1b+, and #225's lifestone/particle alpha visual gate. -Start structural work at `docs/architecture/code-structure.md`; start +Start structural work at `memory/project_gamewindow_decomposition.md` and +`docs/architecture/code-structure.md`; start magic follow-up at `claude-memory/project_magic_ui_and_casting.md`; start render/streaming work at `claude-memory/project_render_pipeline_digest.md`. diff --git a/CLAUDE.md b/CLAUDE.md index ce3e9c7f..e2cad234 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -113,7 +113,7 @@ and the final two-client portal-out/materialization observer flow are user-gated. Deterministic world-lifecycle automation protects fresh login, outdoor/world-edge/dungeon travel, same-location revisit, exact graceful disconnect, and uncapped fresh-process reconnect with JSON/PNG artifacts. -Release builds; 7,054 tests pass / 5 skip. +Release builds; 7,182 tests pass / 5 skip. **Active prerequisite before new M4 subsystem work:** continue the behavior-preserving `GameWindow` decomposition reconciled on 2026-07-21. @@ -121,18 +121,21 @@ Selection/interaction Slice 1, live-animation-presentation Slice 2, complete live-session ownership Slice 3, and live-entity App integration Slice 4 are landed. Focused hydration, teardown, inbound-authority, network-update, and motion-runtime owners now sit over canonical `LiveEntityRuntime` without a -second GUID dictionary. Slice 5 is complete: the complete DAT -build, render/physics/static publication, retryable retirement, and shared- -origin lifetime barrier are out of `GameWindow`; it is now 8,811 raw lines / -247 fields / 153 methods, down 44% from the 15,723-line campaign baseline. -The lifecycle/reconnect and synchronized nine-stop soak gates pass. Slice 6 -update-frame orchestration is active; later slices finish render orchestration -and composition cleanup. +second GUID dictionary. Slice 5 landblock presentation and Slice 6 update-frame +orchestration are complete. Streaming, input, the object/network barrier, +liveness, teleport, player mode, and camera presentation have typed phase +owners; `GameWindow.OnUpdate` is one orchestrator handoff with no substantial +transitive callback facade. `GameWindow` is 7,026 raw lines / 241 fields / 108 +methods, down 8,697 lines (55.3%) from the 15,723-line campaign baseline. The +7,182-test Release suite, 314-second lifecycle/reconnect gate, and 394-second +synchronized nine-stop soak pass. Slice 7 draw-frame orchestration is active; +Slice 8 finishes composition and Silk callback cleanup. See `docs/architecture/code-structure.md`. **Carried:** #153, #116, remaining -R6 ownership cleanup, TS-50/TS-51, Modern Pipeline MP1b+, and #225's +R6 ownership cleanup, TS-50/TS-51/TS-53, Modern Pipeline MP1b+, and #225's lifestone/particle alpha visual gate. -Start structural work at `docs/architecture/code-structure.md`; start +Start structural work at `memory/project_gamewindow_decomposition.md` and +`docs/architecture/code-structure.md`; start magic follow-up at `claude-memory/project_magic_ui_and_casting.md`; start render/streaming work at `claude-memory/project_render_pipeline_digest.md`. diff --git a/docs/ISSUES.md b/docs/ISSUES.md index e4ba7c64..194fcefb 100644 --- a/docs/ISSUES.md +++ b/docs/ISSUES.md @@ -24,17 +24,19 @@ What does NOT go here: - Every session: scan OPEN issues at start; promote/close anything we touched during the session before ending. - Promoting to a Phase: mark as `DONE (promoted to Phase X)` + commit SHA where the Phase entry landed. -## Current queue — 2026-07-21 +## Current queue — 2026-07-22 -- **Active structural work:** `GameWindow` decomposition. Selection/interaction - ownership, live animation presentation, and complete live-session ownership - are landed; Slice 4 live-entity App integration is next in +- **Active structural work:** `GameWindow` decomposition. Slices 1–6 are + complete; `GameWindow.OnUpdate` is one typed orchestration handoff and the + class is 7,026 lines / 241 fields / 108 methods. Slice 7 draw-frame + orchestration is active in [`docs/architecture/code-structure.md`](architecture/code-structure.md). This is the behavior-preserving prerequisite before new M4 feature bodies. - **Separate rendering gate:** `#225`, lifestone/particle alpha ordering. Its connected performance, lifetime, and unattended portal routes pass. - **Carried behavior debt:** `#153` far-teleport unstreamed-edge arrival and - `#116` slide response. TS-50/TS-51 are tracked in the divergence register. + `#116` slide response. TS-50/TS-51/TS-53 are tracked in the divergence + register. - **Deferred visual fidelity:** `#226` retail landscape detail overlay. - **Build hygiene:** `#228` records 17 clean-Release test-project warnings; production compilation and all tests pass. diff --git a/docs/architecture/code-structure.md b/docs/architecture/code-structure.md index 9fc6cd54..f27abb03 100644 --- a/docs/architecture/code-structure.md +++ b/docs/architecture/code-structure.md @@ -1,8 +1,9 @@ # acdream — code structure & extraction sequence **Status:** Living document. Created 2026-05-16; implementation reconciliation -completed 2026-07-21; Slices 1–5 landed the same day. This is the active -structural program before new M4 subsystems enter the App layer. +completed 2026-07-21; Slices 1–6 landed by 2026-07-22. Slice 7 draw-frame +orchestration is the active structural program before new M4 subsystems enter +the App layer. **Purpose:** Describe the desired structural state of the App layer, explain the rules we've adopted, and lay out the safe extraction sequence from today's reality (one 15,723-line `GameWindow.cs` at the @@ -28,6 +29,7 @@ after Slice 2 14,546 lines / 277 fields / 190 method after Slice 3 14,310 lines / 274 fields / 190 methods after Slice 4 10,301 lines / 267 fields / 163 methods after Slice 5 closeout 8,811 lines / 247 fields / 153 methods +after Slice 6 closeout 7,026 lines / 241 fields / 108 methods ``` `GameWindow` is the single object that: @@ -396,7 +398,7 @@ useful ordering seam, but its ownership status is **partial**. | Startup options | **Complete** | `RuntimeOptions` owns startup configuration (`eda936dc`). Remaining direct environment reads are legacy runtime diagnostics, not startup configuration. | | Network session | **Complete** | `LiveSessionController` owns resolve/create/bind/Connect/selection/EnterWorld/Tick/stop/reconnect/disposal; `LiveSessionLifecycleHost` is the narrow App composition boundary; `LiveSessionEventRouter` and `LiveSessionCommandRouter` own exact inbound/outbound lifetimes. `GameWindow` retains one controller field and focused domain sink factories only (`d9ccf8a6`, `6a5d9e2e`). | | Live identity/lifetime | **Complete App integration** | `LiveEntityRuntime` owns incarnation identity, accepted snapshots/timestamps, runtime components, and logical/spatial lifetime. `LiveEntityHydrationController`, `LiveEntityRuntimeTeardownController`, and `LiveEntityNetworkUpdateController` own DAT-backed hydration, retryable cleanup, and accepted Position/Vector/State/Movement presentation without a second GUID owner (`aa90c646`). | -| Inbound/object-frame order | **Partial** | `RetailInboundEventDispatcher`, `RetailLiveFrameCoordinator`, `LiveEntityAnimationScheduler`, `LiveEntityAnimationPresenter`, static scheduler, remote/projectile updaters, and teleport owners are shipped. `AdvanceLiveObjectRuntimeCore` retains the visible cross-owner order until Slice 6. | +| Inbound/object-frame order | **Complete App orchestration** | `UpdateFrameOrchestrator` owns the complete typed host phase graph; `RetailInboundEventDispatcher`, `RetailLiveFrameCoordinator`, `LiveObjectFrameController`, `LiveSpatialPresentationReconciler`, streaming/input/teleport/player-mode/camera owners preserve the accepted order. `GameWindow.OnUpdate` is one profiler-scoped handoff (`e91f3102`). | | World reveal | **Complete** | `WorldRevealCoordinator` owns login/portal readiness and reveal lifetime (`a4ef5788`). The accepted deterministic lifecycle trace did not change after extraction. | | Retained gameplay UI | **Mostly complete feature ownership** | `RetailUiRuntime` and focused panel/controllers own layout and behavior. `GameWindow.OnLoad` still performs substantial service composition, which is allowed until the final composition cleanup. | | Selection/interaction | **Complete** | `WorldSelectionQuery` owns read-only picking/classification/range queries; `SelectionInteractionController` owns selection intent, Use/PickUp transport, exact-incarnation queues, and auto-walk deferral; `ItemInteractionController` owns ItemHolder policy plus the shared retail inventory-request transaction. `GameWindow` retains construction and narrow lifecycle forwarding only. | @@ -572,11 +574,11 @@ resource pin/release balance, collision footprints across landblock seams, and first-login bootstrap replacement. The deterministic world-lifecycle gate and nine-destination resource soak pass. -#### Slice 6 — extract update-frame orchestration +#### Slice 6 — extract update-frame orchestration — COMPLETE 2026-07-22 Detailed execution ledger: [`docs/plans/2026-07-21-gamewindow-slice-6-update-frame-orchestration.md`](../plans/2026-07-21-gamewindow-slice-6-update-frame-orchestration.md) -(active). +(complete). After the stateful bodies above have owners, make the update path a real orchestrator instead of delegates back into the window. It preserves the @@ -590,6 +592,18 @@ graph and registered TS-53 host-order adaptation live in the detailed ledger. Frame-order tests and the existing R6 gate must produce the same lifecycle and movement traces before and after extraction. +The production cutover is complete in `e91f3102`. `GameWindow.OnUpdate` now +contains the profiler scope and one `UpdateFrameOrchestrator.Tick` handoff; +streaming, input, the object/network barrier, liveness, teleport, player-mode, +and camera phases have typed owners with no substantial callback facade into +the window. The complete Release suite passes 7,182 tests with five intentional +skips. The 314-second lifecycle/reconnect gate and 394-second synchronized +nine-stop resource soak both pass with graceful exits, stable live-owner +counts, and a 0.8 ms update-frame p95 at the final Caul checkpoint. +`GameWindow` is 7,026 raw lines / 241 fields / 108 methods: 8,697 lines (55.3%) +smaller than the campaign baseline. TS-50, TS-51, and TS-53 remain accurately +registered; this behavior-preserving extraction introduced no new divergence. + #### Slice 7 — extract `RenderFrameOrchestrator` Move the complete draw graph and its reusable frame-local scratch state into a diff --git a/docs/architecture/retail-divergence-register.md b/docs/architecture/retail-divergence-register.md index b2f3dd95..516f9f12 100644 --- a/docs/architecture/retail-divergence-register.md +++ b/docs/architecture/retail-divergence-register.md @@ -257,10 +257,10 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps. | TS-47 | **NARROWED 2026-07-13** — typed routing now ports the named-retail recall/house/PK travel, age/birth, local display/location, UI persistence, AFK/consent, emote, friends, squelch/filter, and fill-components families. Retail-owned verbs outside the researched family set still fall through to ACE until individually verified. | `src/AcDream.UI.Abstractions/Panels/Chat/RetailClientCommandCatalog.cs`; `src/AcDream.App/UI/ClientCommandController.cs`; `src/AcDream.Core.Net/Messages/ClientCommandRequests.cs` | The high-use researched families have decomp pseudocode, typed actions, and conformance tests; unresearched registry entries must follow the same evidence-first path | An unported retail-owned verb can still produce ACE unknown-command output or server-specific behavior instead of its client action | `ClientCommunicationSystem` command-table construction around `0x00581A40..0x005850A0`; `docs/research/2026-07-13-retail-client-command-routing-pseudocode.md`; `docs/research/2026-07-13-retail-client-command-families-pseudocode.md` | | TS-48 | Dragging an item onto another player honors the authoritative `DragItemOnPlayerOpensSecureTrade` option, but the option's default-true branch stops at the existing unavailable toast because the secure-trade transaction and UI are not ported. Direct player giving through `GiveObjectRequest 0x00CD` works when the option is disabled; NPC giving is complete. | `src/AcDream.App/UI/ItemInteractionController.cs` (`PlaceIn3D`, `PolicyActionMessage`); `src/AcDream.Core/Items/ItemInteractionPolicy.cs` | The player/NPC distinction and character preference are now faithful; inventing a direct gift while the option requests secure trade would be a worse behavioral divergence. Secure trade is a separate multi-party state machine beyond the starter-dungeon NPC-give slice. | With retail's default character options, an item dragged onto another player cannot be exchanged until the secure-trade subsystem lands. | `ItemHolder::AttemptPlaceIn3D @ 0x00588600`; `PlayerModule::DragItemOnPlayerOpensSecureTrade @ 0x005D31B0`; `ClientTradeSystem`; `docs/research/2026-07-13-retail-give-item-pseudocode.md` | | TS-49 | Hidden-object availability is bridged through `TargetManager.NotifyVoyeurOfEventAndClear(ExitWorld)` because acdream has not ported retail's DetectionManager. Retail `CObjCell::hide_object` sends `LeftDetection` to detection voyeurs; acdream instead withholds Hidden hosts from ordinary `GetObjectA` relationship creation and uses the existing non-Ok target update to tear down MoveTo/Sticky consumers and clear watched-role subscriptions while preserving the hidden object's own watcher role. | `src/AcDream.App/Physics/EntityPhysicsHost.cs` (`NotifyHidden`); `src/AcDream.App/Physics/LiveEntityMotionRuntimeController.cs` (`ResolvePhysicsHost`); `src/AcDream.Core/Physics/Motion/TargetManager.cs` (`NotifyVoyeurOfEventAndClear`) | The current movement consumers already share TargetManager's status fan-out; the bridge prevents pursuit of an unavailable object without inventing a second partial detection database. | Plugins or future systems listening specifically for retail detection enter/leave events receive no `LeftDetection`; only movement/sticky target consumers observe the equivalent availability loss. | `CObjCell::hide_object @ 0x0052BE30`; retire by porting DetectionManager/CObjCell detection-voyeur delivery and routing Hidden through `LeftDetection` | -| TS-50 | `AnimationDone` executes semantically at each owner's retail `CPhysicsObj::process_hooks` boundary, but all other animation hooks are retained in `AnimationHookFrameQueue` until final root/part/equipped-child pose publication. Retail executes the complete hook stream before transition and the Target/Movement/PartArray/Position manager tail because its current CPartArray pose already exists in-place. Static owners correctly reach `process_hooks` only after their root, parts, and children are current. | `src/AcDream.App/Rendering/Vfx/AnimationHookFrameQueue.cs`; `src/AcDream.App/Rendering/RetailStaticAnimatingObjectScheduler.cs`; frame drain in `src/AcDream.App/Rendering/GameWindow.cs` | The modern renderer publishes immutable effect-pose snapshots after all root/child composition; deferred visual sinks avoid attaching particles/lights/audio to the previous pose. Semantic `AnimationDone` is split out and exact, so motion completion and manager behavior are not delayed. Pose-owner lifetime tokens prevent deferred hooks from crossing delete/local-ID reuse. | A non-AnimationDone hook with same-quantum semantic consequences (notably `CallPES`, default-script chaining, audio/particle creation relative to a transition) runs later than retail and can observe post-tail state or start one render frame late. | `CPhysicsObj::process_hooks @ 0x00511550`; `CPhysicsObj::UpdatePositionInternal @ 0x00512C30`; `CPhysicsObj::animate_static_object @ 0x00513DF0`; retire by publishing the current per-object/child pose before hook routing or splitting semantic and presentation sinks without changing authored hook order | -| TS-51 | Particle and PhysicsScript tails advance once per render frame after the complete ordinary/static object worksets. Retail advances each ordinary object's ParticleManager then ScriptManager inside every admitted `UpdateObjectInternal` quantum; `animate_static_object` instead advances that static owner's ScriptManager then ParticleManager and only then `process_hooks`, using its whole admitted elapsed interval. acdream's shared tail is Particle → Script after static hook capture. | `src/AcDream.App/Rendering/GameWindow.cs` (`AdvanceLiveObjectRuntime` final `_particleSystem.Tick(dt)` / `_scriptRunner.Tick(...)`); `src/AcDream.App/Rendering/RetailStaticAnimatingObjectScheduler.cs` | The current managers are shared presentation/runtime owners rather than per-object manager instances. R6 makes root motion, animation, object clocks, workset membership, and ordinary manager order faithful without pretending the shared tails have per-owner timing or static-tail order. Splitting ownership safely requires a later effect-lifetime slice. | A render fragment below retail's minimum object quantum can advance an effect while its owner waits; a catch-up frame advances an owner's root through several quanta but its effect tail only once; static hooks can route before their script/particle managers and static default scripts/particles use render elapsed in Particle → Script order rather than `animate_static_object` elapsed/discard and Script → Particle → hooks timing. | `CPhysicsObj::UpdateObjectInternal @ 0x005156B0`; `CPhysicsObj::animate_static_object @ 0x00513DF0`; retire by giving live/static owners incarnation-bound particle/script managers and ticking each manager in the owning object quantum/order | +| TS-50 | `AnimationDone` executes semantically at each owner's retail `CPhysicsObj::process_hooks` boundary, but all other animation hooks are retained in `AnimationHookFrameQueue` until final root/part/equipped-child pose publication. Retail executes the complete hook stream before transition and the Target/Movement/PartArray/Position manager tail because its current CPartArray pose already exists in-place. Static owners correctly reach `process_hooks` only after their root, parts, and children are current. | `src/AcDream.App/Rendering/Vfx/AnimationHookFrameQueue.cs`; `src/AcDream.App/Rendering/RetailStaticAnimatingObjectScheduler.cs`; shared frame drain in `src/AcDream.App/Update/LiveObjectFrameController.cs` (`LiveEffectFrameController`) | The modern renderer publishes immutable effect-pose snapshots after all root/child composition; deferred visual sinks avoid attaching particles/lights/audio to the previous pose. Semantic `AnimationDone` is split out and exact, so motion completion and manager behavior are not delayed. Pose-owner lifetime tokens prevent deferred hooks from crossing delete/local-ID reuse. | A non-AnimationDone hook with same-quantum semantic consequences (notably `CallPES`, default-script chaining, audio/particle creation relative to a transition) runs later than retail and can observe post-tail state or start one render frame late. | `CPhysicsObj::process_hooks @ 0x00511550`; `CPhysicsObj::UpdatePositionInternal @ 0x00512C30`; `CPhysicsObj::animate_static_object @ 0x00513DF0`; retire by publishing the current per-object/child pose before hook routing or splitting semantic and presentation sinks without changing authored hook order | +| TS-51 | Particle and PhysicsScript tails advance once per render frame after the complete ordinary/static object worksets. Retail advances each ordinary object's ParticleManager then ScriptManager inside every admitted `UpdateObjectInternal` quantum; `animate_static_object` instead advances that static owner's ScriptManager then ParticleManager and only then `process_hooks`, using its whole admitted elapsed interval. acdream's shared tail is Particle → Script after static hook capture. | `src/AcDream.App/Update/LiveObjectFrameController.cs` (`LiveObjectFrameController` + `LiveEffectFrameController` shared `_particles.Tick` / `_scripts.Tick` tail); `src/AcDream.App/Rendering/RetailStaticAnimatingObjectScheduler.cs` | The current managers are shared presentation/runtime owners rather than per-object manager instances. R6 makes root motion, animation, object clocks, workset membership, and ordinary manager order faithful without pretending the shared tails have per-owner timing or static-tail order. Splitting ownership safely requires a later effect-lifetime slice. | A render fragment below retail's minimum object quantum can advance an effect while its owner waits; a catch-up frame advances an owner's root through several quanta but its effect tail only once; static hooks can route before their script/particle managers and static default scripts/particles use render elapsed in Particle → Script order rather than `animate_static_object` elapsed/discard and Script → Particle → hooks timing. | `CPhysicsObj::UpdateObjectInternal @ 0x005156B0`; `CPhysicsObj::animate_static_object @ 0x00513DF0`; retire by giving live/static owners incarnation-bound particle/script managers and ticking each manager in the owning object quantum/order | | TS-52 | The terrain shader applies retail-authored base/overlay/road `TerrainTex.TexTiling` but omits the separate Environment Detail Textures pass and its viewer-distance fade (**#226**). | `src/AcDream.App/Rendering/TerrainAtlas.cs`; `src/AcDream.App/Rendering/TerrainModernRenderer.cs`; `src/AcDream.App/Rendering/Shaders/terrain_modern.frag` | `bb5acab9` fixed the user-visible stretched/blurry regression by porting the distinct base-tiling contract. An earlier experimental detail array darkened the whole ground because its source/neutral blend contract was wrong, so it was correctly reverted rather than guessed into production. | With retail's Environment Detail Textures preference enabled, close terrain lacks the extra high-frequency modulation/fade even though authored base texture scale is correct. | `LScape::GenerateDetailSurfaces` / `SetDetailTexturing @ 0x00506B40`; `ACRender::landPolyDraw @ 0x006B6450..0x006B6525`; issue #226 | -| TS-53 | acdream advances retained UI time on the draw seam and local teleport/UI-camera presentation after its SmartBox-shaped object → inbound network → CommandInterpreter barrier. Retail `Client::UseTime` calls `UIElementManager::UseTime` first, whose global time message reaches `gmSmartBoxUI::UseTime`, and publishes player-camera work from the physics/player callback rather than one post-network camera tail. Slice 6 preserves the accepted host order as an ownership-only extraction. | `src/AcDream.App/Rendering/GameWindow.cs` (`OnUpdate` teleport/camera tails and `_retailUiRuntime.Tick` in `OnRender`); `docs/plans/2026-07-21-gamewindow-slice-6-update-frame-orchestration.md` | Current retained UI, portal transit, reveal, camera, and connected movement traces are accepted; changing cross-subsystem host order while extracting ownership would combine a behavior change with the structural cutover. | Retained UI, teleport, and camera presentation can observe same-frame object/inbound/player state one host update earlier or later than retail at transition boundaries; a future exact host-order port must prove UI, input, reveal, and camera consequences together. | `Client::UseTime @ 0x00411C40`; `UIElementManager::UseTime`; `gmSmartBoxUI::UseTime @ 0x004D6E30`; `CPhysics::UseTime @ 0x00509950`; retire only with a focused host-order port and connected portal/camera comparison | +| TS-53 | acdream advances retained UI time on the draw seam and local teleport/UI-camera presentation after its SmartBox-shaped object → inbound network → CommandInterpreter barrier. Retail `Client::UseTime` calls `UIElementManager::UseTime` first, whose global time message reaches `gmSmartBoxUI::UseTime`, and publishes player-camera work from the physics/player callback rather than one post-network camera tail. Slice 6 preserves the accepted host order as an ownership-only extraction. | `src/AcDream.App/Update/UpdateFrameOrchestrator.cs` (post-live-frame teleport/camera phases); `src/AcDream.App/Rendering/GameWindow.cs` (`_retailUiRuntime.Tick` in `OnRender`); `docs/plans/2026-07-21-gamewindow-slice-6-update-frame-orchestration.md` | Current retained UI, portal transit, reveal, camera, and connected movement traces are accepted; changing cross-subsystem host order while extracting ownership would combine a behavior change with the structural cutover. | Retained UI, teleport, and camera presentation can observe same-frame object/inbound/player state one host update earlier or later than retail at transition boundaries; a future exact host-order port must prove UI, input, reveal, and camera consequences together. | `Client::UseTime @ 0x00411C40`; `UIElementManager::UseTime`; `gmSmartBoxUI::UseTime @ 0x004D6E30`; `CPhysics::UseTime @ 0x00509950`; retire only with a focused host-order port and connected portal/camera comparison | --- diff --git a/docs/plans/2026-04-11-roadmap.md b/docs/plans/2026-04-11-roadmap.md index f2023076..46c10bca 100644 --- a/docs/plans/2026-04-11-roadmap.md +++ b/docs/plans/2026-04-11-roadmap.md @@ -1,6 +1,6 @@ # acdream — strategic roadmap -**Status:** Living document. Updated 2026-07-21. **M3 landed; M4 is next after the active `GameWindow` structural prerequisite.** M3's retail casting/UI, R6 locomotion/collision/projectile/teleport/radar rebaseline, deterministic fresh-login/portal world lifecycle, and final two-client portal observer flow are user-gated. The current program is the behavior-preserving ownership campaign in [`docs/architecture/code-structure.md`](../architecture/code-structure.md); new M4 quest/emote/character-creation feature bodies wait until that campaign is complete. Issue #225's lifestone/particle alpha comparison remains a separate rendering visual gate. +**Status:** Living document. Updated 2026-07-22. **M3 landed; M4 is next after the active `GameWindow` structural prerequisite.** M3's retail casting/UI, R6 locomotion/collision/projectile/teleport/radar rebaseline, deterministic fresh-login/portal world lifecycle, and final two-client portal observer flow are user-gated. The current program is the behavior-preserving ownership campaign in [`docs/architecture/code-structure.md`](../architecture/code-structure.md); Slices 1–6 are complete and Slice 7 draw-frame orchestration is active. New M4 quest/emote/character-creation feature bodies wait until that campaign is complete. Issue #225's lifestone/particle alpha comparison remains a separate rendering visual gate. **Purpose:** One source of truth for where the project is and where it's going. Every observed defect or missing feature has a named phase that owns it; when something looks wrong in-game, look here to find the phase that'll address it. Implementation details live in per-phase specs under `docs/superpowers/specs/`, not in this file. --- @@ -56,9 +56,19 @@ session replacement, including failure/retry and fast relogin. Shutdown keeps the reset graph alive until disposal converges. `GameWindow` is 8,811 raw lines / 247 fields / 153 methods (44% below the campaign baseline); 7,054 Release tests, the capped/reconnect lifecycle gate, and the synchronized -nine-stop resource soak pass. Slice 6 update-frame orchestration is active; -its detailed checkpoint ledger is +nine-stop resource soak pass. + +Slice 6 update-frame orchestration is complete. Streaming, input, the retail- +shaped object/network barrier, liveness, local teleport, player mode, and +camera presentation now have typed phase owners. `GameWindow.OnUpdate` is one +orchestrator handoff, with no substantial transitive callback facade back into +the window. `GameWindow` is 7,026 raw lines / 241 fields / 108 methods, down +8,697 lines (55.3%) from the campaign baseline. The complete Release suite +passes 7,182 tests / 5 skips; the 314-second lifecycle/reconnect gate and +394-second synchronized nine-stop resource soak pass with graceful exits and +stable owner counts. The completed ledger is [`2026-07-21-gamewindow-slice-6-update-frame-orchestration.md`](2026-07-21-gamewindow-slice-6-update-frame-orchestration.md). +Slice 7 `RenderFrameOrchestrator` extraction is now active. This is a behavior-preserving structural program. Severe regressions still get root-cause fixes in separate commits; ordinary feature work resumes with M4 diff --git a/docs/plans/2026-05-12-milestones.md b/docs/plans/2026-05-12-milestones.md index ff932553..c398945d 100644 --- a/docs/plans/2026-05-12-milestones.md +++ b/docs/plans/2026-05-12-milestones.md @@ -23,16 +23,20 @@ complete. Slice 5 landblock presentation is also complete: the worker DAT transaction, render/physics/static publishers, and retryable Near/full retirement now have focused owners. The direct production cutover also serializes complete old-window retirement with teleport and session -origin lifetimes. `GameWindow` is 8,811 raw lines / 247 fields / 153 methods, -down 1,490 lines from the Slice-5 baseline and 6,912 (44%) from the campaign -baseline. The full Release suite passes 7,054 tests / 5 skips; the 311-second -capped/reconnect lifecycle gate and synchronized 394-second nine-destination -resource soak both pass. Slice 6 update-frame orchestration is active under +origin lifetimes. Slice 6 update-frame orchestration is complete: streaming, +input, the object/network barrier, liveness, teleport, player mode, and camera +presentation have typed phase owners, and `GameWindow.OnUpdate` is one +orchestrator handoff. `GameWindow` is 7,026 raw lines / 241 fields / 108 +methods, down 8,697 lines (55.3%) from the campaign baseline. The full Release +suite passes 7,182 tests / 5 skips; the 314-second capped/reconnect lifecycle +gate and synchronized 394-second nine-destination resource soak both pass with +graceful exits and stable owner counts. The completed Slice 6 ledger is [`2026-07-21-gamewindow-slice-6-update-frame-orchestration.md`](2026-07-21-gamewindow-slice-6-update-frame-orchestration.md). +Slice 7 draw-frame orchestration is active. Carried: #153 far-teleport residual, #116 slide-response, the remaining R6 ownership cleanup plus -registered TS-50/TS-51 timing residuals (the complete-root-Frame/object-workset +registered TS-50/TS-51/TS-53 timing residuals (the complete-root-Frame/object-workset cutover is automated- and visual-gate complete; Release builds with 17 known test-project warnings tracked by #228, and 6,575 tests pass / 5 skip), and the deferred Modern Pipeline track (MP1b+). The separate #225 shared-alpha diff --git a/docs/plans/2026-07-21-gamewindow-slice-6-update-frame-orchestration.md b/docs/plans/2026-07-21-gamewindow-slice-6-update-frame-orchestration.md index 832c15ec..344e1444 100644 --- a/docs/plans/2026-07-21-gamewindow-slice-6-update-frame-orchestration.md +++ b/docs/plans/2026-07-21-gamewindow-slice-6-update-frame-orchestration.md @@ -1,6 +1,6 @@ # GameWindow Slice 6 — update-frame orchestration -**Status:** Active 2026-07-21. +**Status:** Complete 2026-07-22. **Parent program:** [`docs/architecture/code-structure.md`](../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 @@ -29,7 +29,7 @@ never hidden behind a retry, delay, suppression flag, or reordered callback. `UpdateFrameOrchestrator`. - [x] G — delete the old `OnUpdate` bodies, callback facades, and obsolete frame state; run focused corrected-diff reviews after each ownership edge. -- [ ] H — full Release suite, connected lifecycle/reconnect gate, synchronized +- [x] 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. @@ -437,7 +437,7 @@ Focused App/Core tests cover destination readiness, forced holds, same-cell placement, LoginComplete order, reset/disposal, GUID reuse, reentrancy, host rollback, auto-entry idempotence, and completion-mailbox ordering. The full App suite is green at 2,814 passed / 3 skipped. `GameWindow.cs` is 7,351 -lines at this checkpoint, down from the slice baseline of 15,723. +lines at this checkpoint, down from the 15,723-line campaign baseline. ### F/G — camera, cutover, and deletion @@ -467,7 +467,7 @@ retains no duplicate lifecycle state. Focused Release tests are green at 37/37, the App suite at 2,820 passed / 3 skipped, and the full suite at 7,179 passed / 5 skipped. All three corrected-diff reviews are clean. `GameWindow.cs` is 7,160 lines, down 8,563 lines (54.5%) from the 15,723-line -slice baseline. +campaign baseline. **Checkpoint G completed 2026-07-22.** `GameWindow.OnUpdate` now owns only the update profiler scope and one `UpdateFrameOrchestrator.Tick` call. Teardown, @@ -501,6 +501,22 @@ 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. +**Completed 2026-07-22.** The complete Release suite is green at 7,182 passed / +5 fixture or environment skips. The deterministic lifecycle/reconnect gate +passed in 314.195 seconds across capped login/travel checkpoints, graceful close, +and a fresh-process reconnect. Its only warning was the expected set of 25 +world-edge empty landblocks. The synchronized nine-stop resource soak passed in +393.581 seconds with movement, turn, jump, combat, teleport waits, fatal-log scan, +and graceful exit. The final Caul plateau held the same 21,025 entity and 13 +animation-owner counts as the earlier Caul return, with update-frame p95 at +0.8 ms; working/private memory increased by 112.2/111.0 MiB across that retained +route, inside the existing deterministic gate. The soak's only diagnostics were +35 known DAT-driven missing VFX table/emitter records and the same 25 expected +world-edge misses. The divergence audit keeps TS-50, TS-51, and TS-53 unchanged +and found no new behavior adaptation. Final `GameWindow` size is 7,026 raw +lines / 241 fields / 108 methods, versus 8,811 / 247 / 153 at Slice 6 entry and +15,723 / 278 / 205 at the campaign baseline. + ## 6. Review gate for every checkpoint After each implementation checkpoint: diff --git a/memory/project_gamewindow_decomposition.md b/memory/project_gamewindow_decomposition.md new file mode 100644 index 00000000..9acbe0b1 --- /dev/null +++ b/memory/project_gamewindow_decomposition.md @@ -0,0 +1,84 @@ +# GameWindow decomposition — current truth + +## Current state + +The behavior-preserving App ownership campaign is complete through Slice 6. +`GameWindow.cs` moved from the 2026-07-21 baseline of 15,723 lines / 278 fields / +205 methods to 7,026 lines / 241 fields / 108 methods: 8,697 lines (55.3%) were +removed without changing accepted gameplay or rendering behavior. + +| Slice | Ownership moved out | Closeout size | +|---|---|---:| +| 1 | world selection and interaction intent | 14,912 / 278 / 191 | +| 2 | live animation presentation | 14,546 / 277 / 190 | +| 3 | complete live-session lifecycle and routing | 14,310 / 274 / 190 | +| 4 | live-entity hydration, teardown, and network presentation | 10,301 / 267 / 163 | +| 5 | landblock build, publication, retirement, and shared-origin lifetime | 8,811 / 247 / 153 | +| 6 | complete update-frame orchestration | 7,026 / 241 / 108 | + +Slice 6 implementation commits are `99a3e819`, `4e4aac2c`, `0bc9fda9`, +`c5570383`, `eeb0f6b4`, `947c61d2`, and production cutover `e91f3102`. +The detailed ledgers and earlier commit sets live under `docs/plans/2026-07-21- +gamewindow-slice-*.md` and `docs/architecture/code-structure.md`. + +## Frozen update graph + +`GameWindow.OnUpdate` starts the profiler scope and calls one +`UpdateFrameOrchestrator.Tick`. The accepted production graph is: + +1. retry retained live-entity teardown; +2. normalize/publish the update and PhysicsScript clocks; +3. converge streaming origin, residence, readiness, and rescued projections; +4. sample semantic input, raw mouse, and combat intent; +5. advance live objects and deferred presentation hooks; +6. drain inbound network traffic inside one world mutation batch; +7. run the local post-network position tail; +8. reconcile root/child/emitter/light presentation without advancing time; +9. expire liveness; +10. advance local teleport/reveal/tunnel presentation; +11. evaluate one-shot player-mode entry; +12. publish fly/chase/player camera presentation. + +`LiveEntityRuntime` remains the canonical identity/incarnation owner. +`GpuWorldState` remains the spatial bucket owner. `LiveSessionController` owns +the session generation. Extracted phase owners query these typed runtimes and +must not create replacement dictionaries, service locators, or callback +facades into substantial `GameWindow` methods. + +## Accepted verification + +- Release suite: 7,182 passed / 5 fixture or environment skips. +- Connected lifecycle/reconnect: 314.195 seconds, graceful close and fresh + process reconnect passed; only 25 expected world-edge empty landblocks. +- Synchronized nine-stop resource soak: 393.581 seconds, graceful exit; stable + 21,025 entity and 13 animation-owner counts at the Caul return and Caul + plateau; update p95 0.8 ms. Working/private memory delta was +112.2/+111.0 + MiB, inside the gate. +- The 35 missing VFX table/emitter diagnostics are known DAT-driven records, + not a Slice 6 regression. +- TS-50, TS-51, and TS-53 remain registered; Slice 6 introduced no new retail + divergence. + +## Next work + +Slice 7 extracts `RenderFrameOrchestrator`: the modern draw graph, portal/PView +visibility routing, landscape/opaque/shared-alpha flushes, particles, debug +draw, paperdoll, retained UI, frame fences, and reusable frame-local scratch. +It must keep the exact accepted order and use a small immutable service set plus +explicit per-frame input. It may not become a GL service locator or call +substantial draw bodies back on `GameWindow`. + +Slice 8 then groups composition and shutdown wiring and reduces Silk callbacks +to narrow calls into input, update, render, resize, focus, and shutdown owners. + +## Do not retry + +- Do not reintroduce delegate facades into `GameWindow`; an ordering wrapper is + not an extraction until the state and behavior body move. +- Do not reorder the frozen update graph during structural work. TS-53 records + the known retail host-order difference for a separate evidence-backed port. +- Do not add a second GUID, session, spatial, or resource owner. +- Do not fold full `GameEntity` aggregation into this campaign; evaluate it + after Slice 8. +- Small value/policy callbacks and diagnostics are acceptable only when they do + not call a substantial window body or hide mutable ownership.