docs(plan): define GameWindow slice 5
This commit is contained in:
parent
2b10e91e07
commit
4001251064
2 changed files with 366 additions and 0 deletions
|
|
@ -546,6 +546,10 @@ Slice 5 is active next.
|
||||||
|
|
||||||
#### Slice 5 — extract landblock presentation
|
#### Slice 5 — extract landblock presentation
|
||||||
|
|
||||||
|
Detailed execution ledger:
|
||||||
|
[`docs/plans/2026-07-21-gamewindow-slice-5-landblock-presentation.md`](../plans/2026-07-21-gamewindow-slice-5-landblock-presentation.md)
|
||||||
|
(active).
|
||||||
|
|
||||||
Create `LandblockPresentationPipeline` around the existing immutable
|
Create `LandblockPresentationPipeline` around the existing immutable
|
||||||
`LandblockBuild` transaction. It owns DAT build context, scenery/EnvCell
|
`LandblockBuild` transaction. It owns DAT build context, scenery/EnvCell
|
||||||
construction, mesh/collision/light publication, demotion/full-retirement, and
|
construction, mesh/collision/light publication, demotion/full-retirement, and
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,362 @@
|
||||||
|
# GameWindow Slice 5 — landblock presentation
|
||||||
|
|
||||||
|
**Status:** Active 2026-07-21.
|
||||||
|
**Parent program:** [`docs/architecture/code-structure.md`](../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.
|
||||||
|
- [ ] B — capture one immutable world-origin snapshot per load and extract the
|
||||||
|
serialized DAT-backed `LandblockBuildFactory`.
|
||||||
|
- [ ] C — extract terrain, visibility, EnvCell, building, and render-pinning
|
||||||
|
publication.
|
||||||
|
- [ ] D — extract physics/cache/collision publication and cross-landblock
|
||||||
|
reflood ownership.
|
||||||
|
- [ ] E — extract static lighting, translucency, default-script, and plugin
|
||||||
|
presentation; compose the small `LandblockPresentationPipeline`.
|
||||||
|
- [ ] F — move retryable demotion/full-retirement presentation under the
|
||||||
|
pipeline without changing `LandblockRetirementCoordinator`'s ledger.
|
||||||
|
- [ ] G — cut `StreamingController` and `GameWindow` directly to the pipeline;
|
||||||
|
delete old bodies, scratch state, and callback facades.
|
||||||
|
- [ ] H — three-agent corrected-diff review, full Release suite, deterministic
|
||||||
|
lifecycle gate, seven-destination resource soak, documentation, and durable
|
||||||
|
memory closeout.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```text
|
||||||
|
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 @ 0x00530A40` and
|
||||||
|
`CLandBlock::init_dyn_objs @ 0x0052F3B0`;
|
||||||
|
- `CLandBlock::destroy_static_objects @ 0x0052FA50`,
|
||||||
|
`CLandBlock::Destroy @ 0x0052FAA0`, and
|
||||||
|
`CLandBlock::release_all @ 0x0052FCF0` for static/building/cell teardown;
|
||||||
|
- `CObjCell::init_objects @ 0x0052B420` →
|
||||||
|
`CPhysicsObj::recalc_cross_cells @ 0x00515A30`; acdream's per-landblock
|
||||||
|
`RefloodLandblock` remains the registered asynchronous adaptation;
|
||||||
|
- `PView::InitCell @ 0x005A4B70` and
|
||||||
|
`CEnvCell::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`](../architecture/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.md`](../research/2026-07-13-envcell-landblock-transaction.md)
|
||||||
|
and
|
||||||
|
[`2026-07-18-retail-texture-resource-lifetime-pseudocode.md`](../research/2026-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
|
||||||
|
|
||||||
|
1. One `Load` captures landblock ID, job kind, generation, and the accepted
|
||||||
|
world-origin center on the update thread. The worker never reads the
|
||||||
|
update-thread-confined `LiveWorldOriginState`.
|
||||||
|
2. 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.
|
||||||
|
3. 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 immutable
|
||||||
|
`EnvCellLandblockBuild`.
|
||||||
|
4. Every valid EnvCell is present in the visibility transaction even when it
|
||||||
|
has zero drawable shell submeshes. Geometry IDs retain their current
|
||||||
|
deterministic arithmetic.
|
||||||
|
5. 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:
|
||||||
|
|
||||||
|
1. publish terrain and rebase it to the captured origin;
|
||||||
|
2. commit the complete cell-visibility snapshot and AABB;
|
||||||
|
3. populate physics data and publish terrain/cells/portals/buildings;
|
||||||
|
4. commit building registries and the EnvCell renderer transaction;
|
||||||
|
5. publish static BSP/cylinder collision, lights, translucency, and DAT-static
|
||||||
|
plugin snapshots; reflood the landblock;
|
||||||
|
6. publish/merge the canonical spatial bucket in `GpuWorldState`, acquiring
|
||||||
|
ordinary and synthetic EnvCell render IDs and activating static defaults;
|
||||||
|
7. only after the pins exist, replay schema-aware EnvCell mesh preparation;
|
||||||
|
8. 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
|
||||||
|
|
||||||
|
1. `GpuWorldState.DetachNearLayer` or `DetachLandblock` commits first. No
|
||||||
|
presentation cleanup failure can resurrect spatial membership.
|
||||||
|
2. 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.
|
||||||
|
3. 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.
|
||||||
|
4. 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.
|
||||||
|
5. Successful stages and entity cursors never replay. Same-ID replacement is
|
||||||
|
fenced until every required stage converges, including across controller or
|
||||||
|
radius reconfiguration.
|
||||||
|
6. GPU range/layer reuse remains fence-delayed by `GpuFrameFlightController`.
|
||||||
|
|
||||||
|
## 4. Architecture and interfaces
|
||||||
|
|
||||||
|
### 4.1 Immutable build request
|
||||||
|
|
||||||
|
Introduce a request equivalent to:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
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:
|
||||||
|
|
||||||
|
```text
|
||||||
|
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 and diagnostics
|
||||||
|
snapshots without changing the live call graph.
|
||||||
|
- 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`; keep `DatCollection` access behind
|
||||||
|
`IDatReaderWriter` and 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
|
||||||
|
`LandblockRetirementCoordinator` as 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 `LandblockStreamer` build and `StreamingController` apply/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.
|
||||||
|
|
||||||
|
### H — closeout
|
||||||
|
|
||||||
|
- 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 Release` and the full Release test suite.
|
||||||
|
- Run deterministic first-login/portal lifecycle automation and the connected
|
||||||
|
seven-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.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
`GameWindow` and 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 `WbDrawDispatcher` or 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.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue