25 KiB
GameWindow Slice 5 — landblock presentation
Status: Complete 2026-07-21 (4a205a3e).
Parent program: docs/architecture/code-structure.md, Slice 5.
Baseline: 2b10e91e; GameWindow.cs is 10,301 lines, 267 fields, and
163 methods. Release baseline: 6,940 passed / 5 skipped. The connected R6
baseline passed seven destinations/revisits, three movement/jump/combat
exercises, and graceful close in 310 seconds.
Behavior rule: Preserve the accepted two-tier streaming quality, immutable
per-job EnvCell transaction, render-readiness barrier, collision/light/script/
plugin lifetime, and retryable detach-first retirement. This is an ownership
move. Any proven defect needed to make that boundary safe gets its own oracle,
test, and bisectable commit rather than being hidden in a mechanical move.
Progress ledger
- A — freeze production order and introduce explicit build/publication
seams plus structural guards.
- A1 landed in
4f965d06: accepted results now enter one stagedLandblockPresentationPipelinetransaction;GpuWorldStatereturns an exact spatial-publication receipt before retryable render-pin/script activation; retries preserve FIFO suffixes, pending-live merges, and promotion identity across policy/generation changes. Three corrected-diff reviews are clean. Release gate: 6,955 passed / 5 skipped. - A2 landed in
090b0354: every production load now captures a specified immutable origin (including valid map origin(0,0)) and carries it through worker construction, priority/supersession, completion, Far normalization, and publication. Origin-less or mismatched request-aware work fails before it can publish. Three corrected-diff reviews are clean. Release gate: 6,965 passed / 5 skipped.
- A1 landed in
- B — capture one immutable world-origin snapshot per load and extract the
serialized DAT-backed
LandblockBuildFactory.- Landed in
5af101b2:LandblockBuildFactorynow owns the complete serialized worker-side DAT transaction, immutable height-table snapshot, stab/scenery/interior hydration, complete EnvCell build, and physics-DAT bundle.GameWindowonly constructs the owner and passes itsBuildoperation to the streamer; it no longer contains any build helper body. Hermetic tests pin Far/Near/Promote shape, zero-submesh visibility cells, missing-record behavior, the exact supplied DAT gate, non-interleaved full Near transactions, exception recovery, and immutable inputs. The installed Holtburg gate independently derives visibility completeness fromLandBlockInfo.NumCells. Three corrected-diff reviews are clean. Release gate: 6,979 passed / 5 skipped;GameWindow.csis 9,573 lines.
- Landed in
- C — extract terrain, visibility, EnvCell, building, and render-pinning
publication.
- Landed in
1029f837:LandblockRenderPublisherowns captured-origin terrain publication, complete visibility-cell transactions, AABBs, building registries, EnvCell commit/preparation, and matching retirement operations. Invalid EnvCell identities fail before any presentation mutation; visibility-only cells and zero-submesh transactions remain first-class. Tests pin building-before-EnvCell order, duplicate and failed completion behavior, foreign receipts, post-pin preparation, real publish-to-retire balance, and hidden AABB ownership. Three corrected-diff reviews are clean. Release gate: 6,988 passed / 5 skipped;GameWindow.csis 9,490 lines.
- Landed in
- D — extract physics/cache/collision publication and cross-landblock
reflood ownership.
- Landed in
3613d393:LandblockPhysicsPublishernow owns captured-origin terrain/cell/portal/building physics, cached GfxObj shapes, mutually exclusive multipart BSP or Setup fallback collision, exact static-owner replacement, final reflood, demotion, and full removal. Every Setup shape shares the retail logical entity ID; building shells remain exclusive to the per-landcell building channel. Reapply retires stale cell/building/UCG state and omitted static owners by landblock prefix, rebindsCurrCellto the replacement object, and preserves adjacent owners even when collision footprints cross the seam. Three corrected-diff reviews are clean. Release gate: 7,008 passed / 5 skipped; zero-warning build;GameWindow.csis 9,043 lines.
- Landed in
- E — extract static lighting, translucency, default-script, and plugin
presentation; compose the small
LandblockPresentationPipeline.- Landed in
ea0da8c8: render, physics, and DAT-static publishers now preflight immutable receipts before mutation and retain exact per-stage progress through retry.LandblockStaticPresentationPublisherowns static lights, translucency lifetime, and plugin projection;GpuWorldStateandEntityScriptActivatorexplicitly rebind same-ID static snapshots without replaying Setup defaults or resetting animation/light/translucency state, while omitted IDs take full teardown. Source changes and duplicate IDs fail before presentation mutation, missing DefaultAnimation never claims an owner, and fallible reconciliation precedes pending-map consumption. Three corrected-diff reviews are clean. Release gate: 7,026 passed / 5 skipped;GameWindow.csis temporarily 9,073 lines because production wiring lands before checkpoint G deletes the old callback body.
- Landed in
- F — move retryable demotion/full-retirement presentation under the
pipeline without changing
LandblockRetirementCoordinator's ledger.- Landed in
ea7ffbc1: the pipeline now owns a focusedLandblockPresentationRetirementOwnerand its exact detach-first ledger;GameWindowno longer owns a coordinator or retirement callback body. Full and Near retirements preserve their distinct terrain/physics/live owner matrices, successful stages and entity cursors never replay, and reentrant Begin/Advance requests serialize without mutating an active ticket enumeration. A committed visibility-observer failure retains the detach receipt, completes presentation teardown, drains deferred work, then rethrows. Constructor identity guards enforce one world-state, publisher, lighting, and translucency graph, while explicit concrete and legacy controller overloads make ignored presentation callbacks impossible. Three corrected-diff reviews are clean. Release gate: 7,039 passed / 5 skipped; build green with 17 pre-existing test-project warnings;GameWindow.csis 9,011 lines.
- Landed in
- G — cut
StreamingControllerandGameWindowdirectly to the pipeline; delete old bodies, scratch state, and callback facades. - H — three-agent corrected-diff review, full Release suite, deterministic
lifecycle gate, nine-destination resource soak, documentation, and durable
memory closeout.
- Landed in
4a205a3e: native shutdown now keeps the live-session reset dependencies alive until deferred/reentrant disposal actually converges; source and behavioral tests pin that completion barrier. Both connected harnesses scan the final post-close logs for shutdown/disposed-resource failures. The resource gate uses an already-warm Caul → Sawato → Caul plateau instead of comparing a cold first load to a warmed cache, and every route command blocks on its exact portal-materialization count. - All three final reviews are clean. Release gate: 7,054 passed / 5 skipped.
The capped/reconnect lifecycle gate passed in 311.4 seconds. The
synchronized nine-stop soak passed in 394.1 seconds; Caul return → plateau
changed working/private memory by only +56.4/+48.4 MiB and update p95 by
0.8 → 0.7 ms.
GameWindow.cscloses Slice 5 at 8,811 raw lines, 247 fields, and 153 methods—1,490 lines below the Slice-5 baseline and 6,912 lines (44%) below the campaign baseline.
- Landed in
Every checked checkpoint is committed as a bisectable architectural unit. Documentation records the exact commit and accepted test count as work lands.
1. Outcome and non-goals
At slice exit, GameWindow only composes and retains the landblock presentation
owner. It no longer builds a landblock from DAT records, hydrates scenery or
interiors, constructs a physics DAT bundle, publishes terrain/cells/collision/
lights/plugins, or advances presentation retirement.
The final owner graph is deliberately not a replacement god object:
StreamingController (tier, region, generation, priority, budget)
│ accepted Loaded / Promoted / Unloaded result
▼
LandblockPresentationPipeline (transaction order only)
├── LandblockBuildFactory worker-only DAT transaction
├── LandblockRenderPublisher terrain/cells/EnvCell/building/render pins
├── LandblockPhysicsPublisher terrain/cell/portal/static collision
├── LandblockStaticPresentation light/translucency/script/plugin projection
└── LandblockRetirementCoordinator detach-first retry ledger
│
▼
GpuWorldState (spatial buckets, pending live projections, tier, AABB)
StreamingController remains the sole desired-tier and stale-generation
authority. GpuWorldState remains the sole spatial-bucket and retained-live-
projection owner. LandblockStreamer remains the sole worker queue. Existing
renderers, physics/cache registries, lighting, scripts, plugin state, and
WorldRevealCoordinator remain independent owners.
This slice does not change Near/Far radii, mesh or texture quality, world view
distance, portal visibility, GPU frame fences, the draw graph, live-entity
identity, or the update/render phase order. It does not re-port algorithms
already inventoried under Rendering/Wb or introduce another DAT reader.
2. Retail, WorldBuilder, and shipped oracles
The extraction preserves the following retail order and already-shipped adaptations:
LScape::grab_visible_cells @ 0x00504EC0: initialize buildings and gather visible cells before static/dynamic activation;CLandBlock::init_buildings @ 0x0052FD80;CLandBlock::grab_visible_cells @ 0x0052F460;CLandBlock::init_static_objs @ 0x00530A40andCLandBlock::init_dyn_objs @ 0x0052F3B0;CLandBlock::destroy_static_objects @ 0x0052FA50,CLandBlock::Destroy @ 0x0052FAA0, andCLandBlock::release_all @ 0x0052FCF0for static/building/cell teardown;CObjCell::init_objects @ 0x0052B420→CPhysicsObj::recalc_cross_cells @ 0x00515A30; acdream's per-landblockRefloodLandblockremains the registered asynchronous adaptation;PView::InitCell @ 0x005A4B70andCEnvCell::find_visible_child_cell @ 0x0052DC50;CBuildingObj::find_building_collisions @ 0x006B5300;CPhysicsObj::add_shadows_to_cells @ 0x00514AE0;SmartBox::UseTime @ 0x00455410; acdream's shared reveal coordinator is the asynchronous equivalent of retail's synchronous cell readiness;worldbuilder-inventory.md, especially the one-job/one-complete-EnvCell-transaction and sole-DAT-reader rules;- the current render and two-tier streaming digests, including logical withdrawal before fence-delayed GPU reuse and the prohibition on reducing view distance as an optimization;
2026-07-13-envcell-landblock-transaction.mdand2026-07-18-retail-texture-resource-lifetime-pseudocode.md.
The named retail decomp is the oracle for lifecycle ordering. The extracted WorldBuilder code is the oracle for already-ported terrain, scenery, EnvCell, mesh, and texture mechanics. ACE and the retained WorldBuilder reference tree are cross-checks, not new dependencies.
3. Load-bearing invariants
3.1 Worker build
- One
Loadcaptures landblock ID, job kind, generation, and the accepted world-origin center on the update thread. The worker never reads the update-thread-confinedLiveWorldOriginState. - All DAT reads for one build execute under the existing single-reader gate. The worker performs CPU work only: no GL call, renderer/physics publication, registry mutation, or shared pending cell bag.
- Far reads the heightmap only and emits no static entities, EnvCells, or
physics bundle. Near/Promote emits hydrated stabs, procedural scenery,
interiors, a complete
PhysicsDatBundle, and one immutableEnvCellLandblockBuild. - Every valid EnvCell is present in the visibility transaction even when it has zero drawable shell submeshes. Geometry IDs retain their current deterministic arithmetic.
- Missing records keep the current skip/null semantics. A failed build has no externally visible partial publication.
3.2 Publication
For an accepted Near load or promotion, the transaction order remains:
- publish terrain and rebase it to the captured origin;
- commit the complete cell-visibility snapshot and AABB;
- populate physics data and publish terrain/cells/portals/buildings;
- commit building registries and the EnvCell renderer transaction;
- publish static BSP/cylinder collision, lights, translucency, and DAT-static plugin snapshots; reflood the landblock;
- publish/merge the canonical spatial bucket in
GpuWorldState, acquiring ordinary and synthetic EnvCell render IDs and activating static defaults; - only after the pins exist, replay schema-aware EnvCell mesh preparation;
- only after spatial publication, restore retained live projections.
Render readiness means Near tier plus real uploaded ownership for every
required ordinary and synthetic EnvCell render ID. Dictionary publication is
not readiness. Duplicate Near completion is a no-op; stale Far cannot erase a
Near layer. Promotion-before-base and the accepted Near-completion-while-Far-
desired synthesis remain StreamingController policy.
3.3 Demotion and retirement
GpuWorldState.DetachNearLayerorDetachLandblockcommits first. No presentation cleanup failure can resurrect spatial membership.- Near→Far keeps terrain and all live projections, but removes DAT statics, static scripts/classification, lights/translucency/plugins, indoor physics, cell visibility, building registries, and EnvCells.
- Full retirement additionally releases terrain and full landblock physics. Persistent player projection is rescued and still-live server projections are parked for rebucketing with the same identity.
- The retryable order is exactly: mesh references → static scripts → classification → entity lights → entity translucency → DAT-static plugin projection → terrain for Full only → physics remove/demote → cell visibility → building registry → EnvCell renderer.
- Successful stages and entity cursors never replay. Same-ID replacement is fenced until every required stage converges, including across controller or radius reconfiguration.
- GPU range/layer reuse remains fence-delayed by
GpuFrameFlightController.
4. Architecture and interfaces
4.1 Immutable build request
Introduce a request equivalent to:
public readonly record struct LandblockBuildRequest(
uint LandblockId,
LandblockStreamJobKind Kind,
ulong Generation,
int OriginCenterX,
int OriginCenterY);
The generation remains for matching and diagnostics; the builder never uses it to decide residency. Results carry the same origin snapshot so apply cannot combine worker geometry from one center with render-thread placement from a later center.
4.2 DAT build factory
LandblockBuildFactory receives IDatReaderWriter, the one shared DAT gate,
the immutable height table, and worker-safe mesh/physics-cache collaborators.
Its public surface is one operation:
LandblockBuild? Build(in LandblockBuildRequest request);
The extracted BuildPhysicsDatBundle, stab hydration, scenery generation,
interior/EnvCell construction, and terrain sampling remain private focused
helpers. No method accepts GameWindow, LiveWorldOriginState, GL objects, or
presentation registries.
4.3 Focused publishers
LandblockRenderPublisher owns terrain slot publication, AABB, cell
visibility, building registries, EnvCell commit, static render pin/preparation,
and a typed immutable diagnostics snapshot.
LandblockPhysicsPublisher owns cache population, terrain/cell/portal/building
physics, static multipart BSP and exclusive Setup-cylinder/sphere fallback,
demotion/full removal, and final cross-cell reflood.
LandblockStaticPresentationPublisher owns idempotent static light and
translucency replacement, static script activation/removal at the existing
spatial registration boundary, and DAT-static plugin spawn/removal projection.
The pipeline calls concrete publishers in the fixed order. Publish/retire
interfaces expose no DAT reader. Diagnostics move with the owner and the Debug
UI reads a snapshot rather than GameWindow scratch counters.
4.4 Policy cutover
StreamingController keeps result validation and calls the pipeline only after
generation/tier/duplicate/retirement-fence checks. It must not gain DAT, GL,
physics, plugin, light, or live-GUID state. GpuWorldState keeps its buckets,
pending Near/render-ID parking, and live rescue. Existing hidden presentation
side effects may be made explicit only when tests prove identical ordering and
balance.
GameWindow retains construction/wiring plus one pipeline field. Structural
tests forbid these methods from returning:
BuildLandblockForStreaming
BuildLandblockForStreamingLocked
BuildPhysicsDatBundle
BuildSceneryEntitiesForStreaming
BuildInteriorEntitiesForStreaming
EnsureEnvCellMeshesAfterPin
ApplyLoadedTerrain
ApplyLoadedTerrainLocked
AdvanceLandblockPresentationRetirement
5. Checkpoint execution
A — characterization and seams
- Add owner-call trace tests around the current production order and payload multiplicity.
- Introduce immutable build/publication request/result seams without changing the live call graph. The typed apply-diagnostics snapshot moves with its concrete render owner in C instead of creating a temporary A-stage owner.
- Add dependency/reflection guards: no origin-state read from worker, no DAT in publish/retire interfaces, no residency/generation map in the pipeline.
B — origin snapshot and build factory
- Capture origin with every enqueued load; preserve it through priority queue, completion, promotion, and Far synthesis.
- Move the complete serialized DAT build verbatim into
LandblockBuildFactory; keepDatCollectionaccess behindIDatReaderWriterand the shared gate. - Test Far/Near payload shape, repeat-build determinism, EnvCell completeness, physics-DAT closure, missing records, lock serialization, and zero DAT reads during publication.
C — render publication
- Extract terrain upload/rebase, CellVisibility, AABB, building registry, EnvCell renderer commit, render-ID pin/preparation, and diagnostics.
- Test visibility-only cells, zero-submesh EnvCells, promotion-before-base, real render readiness, reapply idempotency, and release balance.
D — physics publication
- Extract physics cache commit, terrain/cell/portal/building setup, static multipart BSP/cylinder registration, reflood, demote, and full removal.
- Use two adjacent landblocks to prove one retirement cannot erase a neighbor's seam collision. Pin Far terrain-only, Near reapply, promotion, demotion, and full unload symmetry.
E — static presentation and transaction coordinator
- Extract static lights/translucency/plugin projection and make existing static script ownership explicit without replaying defaults.
- Compose the small
LandblockPresentationPipeline; add exact order, multiplicity, rollback/blocked-publication, and owner-balance tests.
F — retirement
- Move production retirement callbacks under the pipeline while keeping
LandblockRetirementCoordinatoras the exact retry ledger. - Inject failure before/after each boundary; prove only unfinished suffixes and exact entity cursors resume. Test Near and Full requirements separately.
G — direct cutover and cleanup
- Route
LandblockStreamerbuild andStreamingControllerapply/retire calls directly to the new owners. - Delete the old GameWindow bodies, callback facades, building-registry and apply-diagnostic scratch state that now have focused owners.
- Add structural source tests and remeasure GameWindow lines/fields/methods.
Complete in c79d0a49. Production constructs one concrete pipeline;
legacy callback constructors are internal test seams and structural tests keep
them out of the composition root. The old GameWindow apply methods and their
building/diagnostic scratch state are deleted. A retained
StreamingOriginRecenterCoordinator now joins complete old-window
presentation retirement to teleport recenter and session cancellation before
the shared origin may be reused. Reviews closed full-window overlap, radius
reconfiguration, sealed-dungeon bootstrap, committed-callback retry,
reentrancy, fast-relogin, and ordinary-logout gaps. Three final reviews are
clean; the zero-warning Release build and 7,052 tests pass with five skips.
GameWindow.cs was 8,793 raw lines at checkpoint G, 1,508 below the Slice-5
baseline and 6,930 (44%) below the campaign baseline.
H — closeout
Complete in 4a205a3e. H proved the landed graph under deterministic and
connected lifecycle churn, corrected shutdown-stage ordering so session reset
converges before its streaming dependencies are disposed, and strengthened the
resource oracle to compare already-warm steady-state revisits.
- Run focused App/Core/Net tests after each checkpoint.
- After each code checkpoint, run three independent read-only reviews: retail/ WorldBuilder conformance, architecture/integration, and adversarial failure analysis. Fix confirmed findings and repeat review until clean.
- Run
dotnet build AcDream.slnx -c Releaseand the full Release test suite. - Run deterministic first-login/portal lifecycle automation and the connected nine-destination resource soak. Verify retirement/preparation queues drain, residence matches the region, resource counts remain bounded, and graceful close succeeds.
- Update the divergence register pointers in IA-14, AD-2, AD-22, and any other row whose cited ownership moved. Keep AD-6, AD-24, AP-31, and TS-52 unless a separate retail mechanism actually lands.
- Update architecture, milestones, roadmap, issues if needed, synchronized
AGENTS.md/CLAUDE.md, and durable decomposition/render memory.
The final IA-14/AD-2/AD-22 audit found no new retail divergence. IA-14 and AD-22 still point at the correct WB/mesh owners; AD-2 now names the extracted origin-recenter/presentation owners. AD-6, AD-24, AP-31, and TS-52 remain open as required.
6. Acceptance matrix
Automated acceptance includes:
- Far heightmap-only and complete immutable Near/Promote payloads;
- deterministic static/scenery/interior IDs, bounds, offsets, cells, and shell geometry IDs across repeated builds;
- single-reader DAT gate and no publish-time DAT access;
- exact owner-call sequence for load, promotion, Far synthesis, demotion, full unload, replacement, and failure retry;
- stale/duplicate completions invoke no presentation participant;
- complete render pins and real readiness before reveal;
- static lights/translucency/scripts/plugins and mesh references remain balanced through load → reapply → demote → promote → full unload;
- adjacent-landblock collision survives one neighbor's retirement;
- first login with unknown origin starts no build, and stale placeholder/old- center completions cannot publish or satisfy reveal;
- reflection/source gates keep moved methods and scratch ownership out of
GameWindowand keep policy/state out of the pipeline.
Connected acceptance includes fresh login, outdoor travel, world-edge empty blocks, dungeon travel, same-location revisit, turn sampling at every stop, movement/jump/combat exercises, portal reveal, and graceful close. No view- distance or quality reduction is an acceptable way to pass the resource gate.
7. Known risks and explicit traps
- A worker must not read a later live origin than the job it is building.
- A Near promotion may complete before its Far base; parking behavior and required render IDs must survive unchanged.
- Duplicate Near/ForceReload must not stack lights, scripts, plugin events, meshes, collision, or EnvCells.
- Demotion must never retire live entities or terrain.
- Publication must not be mistaken for uploaded render readiness.
- Failed retirement must survive controller/radius replacement.
- Do not move
WbDrawDispatcheror any draw-graph body; that is Slice 7. - Do not reintroduce worker-side
RegisterCell, global pending renderer lists, legacy rendering, or another DAT reader. - Do not reduce Near/Far radii, texture detail, or mesh quality.