371 lines
19 KiB
Markdown
371 lines
19 KiB
Markdown
# Modern runtime Slice I — flat collision assets and zero-allocation physics
|
||
|
||
**Status:** I0–I7 COMPLETE — retail oracle, zero-allocation transition
|
||
scratch, deterministic immutable collision records, complete prepared
|
||
package, exact flat traversal, dual publication, flat-authoritative production
|
||
cutover, parsed-graph removal, connected correctness/lifetime acceptance, and
|
||
documentation closeout
|
||
**Parent:** `2026-07-24-modern-runtime-architecture.md`, Slice I
|
||
**Purpose:** remove parsed DAT object graphs and steady collision allocations
|
||
without changing one retail collision decision, float, comparison, or traversal
|
||
order.
|
||
|
||
## 1. Non-negotiable boundaries
|
||
|
||
1. Retail collision math is frozen. This slice changes storage and scratch
|
||
ownership only.
|
||
2. Every float read from DAT is copied bit-for-bit. The bake performs no
|
||
normalization, quantization, coordinate conversion, `double` round-trip, or
|
||
plane reconstruction.
|
||
3. Positive/negative child order, polygon order within every leaf, early
|
||
returns, epsilon comparisons, and contact-selection tie behavior remain
|
||
exact.
|
||
4. The current object-graph traversal remains an executable oracle until the
|
||
flat traversal passes mass differential and connected gates.
|
||
5. Physics `Transition` reuse lands only after reset-completeness,
|
||
cross-mover-identity, failure, and reentrancy tests prove that no state can
|
||
leak between resolves.
|
||
6. No GL/App dependency enters Core. Flat collision records live in
|
||
`AcDream.Core`; serialization and package access live in `AcDream.Content`;
|
||
the bake tool produces them offline.
|
||
7. The active G4 render cutover is independent. Its exact visual rollback
|
||
remains:
|
||
|
||
```text
|
||
git revert ef1d263337997bb030eadb7b8e71d73dc659907a
|
||
```
|
||
|
||
## 2. Target data architecture
|
||
|
||
The runtime collision representation is immutable, array-backed, and addressed
|
||
by integer index:
|
||
|
||
```text
|
||
FlatCollisionAsset
|
||
├── FlatPhysicsBsp
|
||
│ ├── FlatBspNode[] child indices; -1 = no child
|
||
│ ├── int[] leaf polygon-index stream
|
||
│ ├── FlatPolygon[] plane, cull mode, id, vertex range
|
||
│ └── Vector3[] verbatim polygon vertices
|
||
├── FlatCellContainmentBsp split planes + positive/negative indices
|
||
├── FlatSetupCollision cyl/sphere records + verbatim dimensions
|
||
└── EnvCellTopology portals, visible cells, seen-outside
|
||
```
|
||
|
||
Node order is deterministic pre-order. The flattener records the original
|
||
positive child before the negative child and retains each source leaf's
|
||
polygon list order. Traversal follows indices using the same recursive
|
||
control flow as the current retail port; “flat” does not mean a new collision
|
||
algorithm.
|
||
|
||
Per-EnvCell world transform remains publication state, not baked geometry.
|
||
CellStruct collision geometry can therefore alias by exact source identity;
|
||
cell-specific portals, visibility, `seen_outside`, and placement remain a
|
||
separate small payload/publication record.
|
||
|
||
## 3. Execution slices
|
||
|
||
### I0 — oracle inventory and fixed evidence
|
||
|
||
1. Grep named retail before touching traversal:
|
||
- `BSPTREE::find_collisions`
|
||
- `BSPNODE::find_walkable`
|
||
- `BSPLEAF::find_walkable`
|
||
- `BSPNODE::sphere_intersects_*`
|
||
- `BSPNODE::point_inside_cell_bsp`
|
||
- `CTransition::find_transitional_position`
|
||
- `CTransition::init_path`
|
||
- `CPhysicsObj::transition`
|
||
2. Cross-reference ACE and the existing acdream pseudocode/trajectory notes.
|
||
3. Write one collision-layout pseudocode note that separates:
|
||
source storage, traversal order, mutable query scratch, and returned
|
||
side-effects.
|
||
4. Inventory every production call into `BSPQuery`, every field read from
|
||
`PhysicsBSPNode`, `CellBSPNode`, `ResolvedPolygon`, `CellPhysics`,
|
||
`GfxObjPhysics`, and `SetupPhysics`, and every raw DAT graph retained by
|
||
`LandblockBuild`/`PhysicsDatBundle`.
|
||
5. Capture representative installed-DAT fixtures:
|
||
- outdoor/no-BSP;
|
||
- Facility Hub and cottage indoor cells;
|
||
- staircase/ramp and cellar-lip cases;
|
||
- a multipart door/building shell;
|
||
- projectile/thin-wall geometry;
|
||
- cells with asymmetric/null BSP children;
|
||
- leaves with multiple polygons and equal-distance candidates.
|
||
6. Record allocation baselines for player, remote, projectile, and camera
|
||
resolve loops.
|
||
|
||
Gate: source inventory is complete, fixtures reproduce through the current
|
||
graph path, and no behavior code changed.
|
||
|
||
### I1 — reusable transition and query scratch
|
||
|
||
1. Add explicit `ResetForReuse` methods to `ObjectInfo`, `CollisionInfo`,
|
||
`SpherePath`, and `Transition`.
|
||
2. Reset every scalar, nullable, property backing field, diagnostic counter,
|
||
collision GUID list, sphere element, walkable reference, backup value, and
|
||
transient flag. Retain only storage identity whose contents are completely
|
||
reset.
|
||
3. Give each `PhysicsEngine` one owned transition lease. The normal
|
||
update-thread path rents, initializes, resolves, snapshots the value-only
|
||
result, and returns in `finally`.
|
||
4. Make reentrancy explicit and tested. Never hand one mutable transition to
|
||
two concurrent/nested resolves and never silently share session scratch.
|
||
5. Reuse walkable vertex arrays only when their exact logical length matches;
|
||
allocate only on a true size change. No larger-capacity array may leak stale
|
||
vertices into polygon math.
|
||
6. Convert short-lived `BSPQuery.CollisionSphere` reference objects to
|
||
value/ref scratch while preserving each mutation/copy boundary.
|
||
7. Add structural reflection tests that poison every resettable member, reset,
|
||
and compare its complete value graph with a fresh instance while asserting
|
||
retained buffer identities.
|
||
8. Differential-run fresh-transition and reused-transition engines across
|
||
success, collision, failure, placement, grounded, airborne, sliding,
|
||
step-up/down, two-sphere, projectile, and camera cases.
|
||
|
||
Gate: bit-identical `ResolveResult` and body side effects, no state leakage
|
||
when alternating hostile fixtures between different mover IDs, and zero
|
||
steady transition/query-scratch allocation. **PASSED 2026-07-25:** the expanded
|
||
118-test graph oracle, structural poison/reset suite, hostile fresh-vs-reused
|
||
differential, Core/solution Release gates, and all five measured profiles pass.
|
||
Issue #237 is closed. Evidence:
|
||
[`../research/2026-07-25-slice-i1-transition-scratch.md`](../research/2026-07-25-slice-i1-transition-scratch.md).
|
||
|
||
### I2 — immutable flat collision schema and flattener
|
||
|
||
1. Add Core-only flat record types with explicit index/range contracts.
|
||
2. Flatten physics and containment BSPs iteratively during preparation while
|
||
emitting deterministic pre-order node arrays.
|
||
3. Copy planes, spheres, vertices, setup dimensions, and collision metadata
|
||
verbatim. Tests compare every float via `SingleToInt32Bits`.
|
||
4. Resolve each leaf polygon ID to a direct polygon index at preparation time.
|
||
Missing IDs are corruption, not a runtime fallback.
|
||
5. Validate:
|
||
- child indices/ranges;
|
||
- acyclic/reachable tree shape;
|
||
- polygon and vertex ranges;
|
||
- source-vs-flat node, child, polygon, and float identity;
|
||
- deterministic output independent of dictionary enumeration.
|
||
6. Keep source graph and flat asset together only in test/shadow fixtures.
|
||
|
||
Gate: round-trip structural equality over synthetic edge cases and installed
|
||
DAT samples; no traversal cutover yet. **PASSED 2026-07-25:** 13 focused
|
||
structural/installed-DAT tests, the 131-test expanded Slice I oracle, complete
|
||
Core/solution Release gates, exact float-bit comparisons, and corruption
|
||
tripwires pass. The graph route remains the only executable traversal.
|
||
Evidence:
|
||
[`../research/2026-07-25-slice-i2-flat-collision-schema.md`](../research/2026-07-25-slice-i2-flat-collision-schema.md).
|
||
|
||
### I3 — package serialization, bake, and prepared collision source
|
||
|
||
1. Append new `PakAssetType` values; never renumber the existing mesh values.
|
||
2. Bump `CurrentBakeToolVersion` because a complete production package now
|
||
includes collision assets.
|
||
3. Add strict little-endian serializers with checked counts/ranges, exact float
|
||
bits, cancellation, CRC coverage, corruption rejection, and trailing-byte
|
||
rejection.
|
||
4. Add a typed `IPreparedCollisionSource` over the existing memory-mapped pak.
|
||
It shares package/catalog lifetime but does not overload the render-only
|
||
`ObjectMeshData` API.
|
||
5. Bake:
|
||
- GfxObj physics BSP + polygons + visual bounds;
|
||
- Setup cylinder/sphere/dimension records;
|
||
- CellStruct physics BSP + containment BSP + physics/portal polygons;
|
||
- EnvCell topology records.
|
||
6. Alias only byte-identical immutable collision payloads. Cell-specific
|
||
topology and world placement cannot be aliased merely because geometry is.
|
||
7. Extend full-bake determinism, two-thread equivalence, corruption,
|
||
cancellation, publish-transaction, and catalog-completeness tests.
|
||
|
||
Gate: two independent bakes are byte-identical; every collision key required
|
||
by the canonical route is present; a failed/cancelled bake cannot replace the
|
||
last good pak. **PASSED 2026-07-25:** two complete 29,908,271,024-byte bakes
|
||
with different worker counts produced the same SHA-256 and 2,232,170 typed
|
||
keys with zero failures. Strict serialization/corruption/cancellation gates,
|
||
representative installed-package reads, Release build, and 8,371 solution
|
||
tests / 5 skips pass. Production traversal remains graph-only. Evidence:
|
||
[`../research/2026-07-25-slice-i3-prepared-collision-package.md`](../research/2026-07-25-slice-i3-prepared-collision-package.md).
|
||
|
||
### I4 — flat traversal shadow implementation
|
||
|
||
1. Port each current entry point line-for-line against integer-index nodes:
|
||
- point-in-cell;
|
||
- sphere/cell overlap;
|
||
- walkable search;
|
||
- six-path moving collision dispatcher;
|
||
- static sphere/polygon overlap;
|
||
- time-of-impact overlap.
|
||
2. Share only polygon-level math that is storage-independent. Do not
|
||
“simplify” recursion, branch ordering, or early returns.
|
||
3. Preserve mutation semantics for valid-position spheres, hit polygon, path,
|
||
contact plane, slide normal, walk interpolation, and transition state.
|
||
4. Add a differential harness that executes old and flat queries from cloned
|
||
transition/body inputs and compares:
|
||
- bool/enum result;
|
||
- all output vectors/planes by float bits;
|
||
- selected polygon ID;
|
||
- path/collision/object side effects;
|
||
- cell membership and final `ResolveResult`.
|
||
5. Sweep a large installed-DAT sample with randomized points, spheres,
|
||
movement vectors, insert modes, orientations, scales, and boundary values.
|
||
6. Require explicit fixtures for null children, on-plane equality, radius
|
||
equality, equal candidate distances, multi-polygon leaf order, and deep
|
||
trees.
|
||
|
||
Gate: zero differential mismatch. Any mismatch blocks cutover; no tolerance
|
||
band is permitted for deterministic scalar results.
|
||
|
||
**PASSED 2026-07-25:** the integer-indexed shadow ports every current
|
||
containment, overlap, walkable, and six-path moving-collision entry point.
|
||
The 13-test referee completed 10,000 randomized static/swept comparisons,
|
||
5,000 walkable comparisons, 7,500 installed-DAT comparisons across three
|
||
representative cells, all six dispatcher paths, and 1,200 complete resolver
|
||
frames with exact state and float-bit equality. Ten thousand warmed flat
|
||
iterations allocated zero managed bytes. Production remains graph-authoritative
|
||
until I5/I6. Release build and 8,384 solution tests / 5 skips pass. Evidence:
|
||
[`../research/2026-07-25-slice-i4-flat-bsp-differential.md`](../research/2026-07-25-slice-i4-flat-bsp-differential.md).
|
||
|
||
### I5 — dual publication and connected shadow gate
|
||
|
||
1. Extend collision preparation so a `LandblockBuild` carries immutable flat
|
||
assets and small placement/topology records rather than reparsing them on
|
||
the update thread.
|
||
2. During the shadow phase, publish both current and flat views under one
|
||
landblock generation/receipt.
|
||
3. Keep the current graph path authoritative. Run sampled flat queries from
|
||
cloned inputs and emit a deterministic mismatch artifact without affecting
|
||
gameplay.
|
||
4. Verify load, pending-to-loaded, demotion, rehydrate, same-location revisit,
|
||
removal, cancellation, and session reset release both views exactly once.
|
||
5. Run connected login, indoor/outdoor portal churn, doors, ramps, jumping,
|
||
projectiles, camera collision, and graceful reconnect.
|
||
|
||
Gate: zero shadow mismatch and no retained collision owner after landblock or
|
||
session teardown.
|
||
|
||
**Complete 2026-07-25.** Near-tier builds carry one immutable prepared
|
||
collision closure; streamed and live objects publish graph and flat assets
|
||
strictly, with no graph-only fallback. Cancellation, publication, demotion,
|
||
rehydration, revisit, adjacent-landblock isolation, removal, and session
|
||
teardown gates pass. The complete Release solution passes 8,396 tests / 5
|
||
skips. The connected lifecycle/reconnect and nine-stop routes sampled 14,064
|
||
queries with exact state/float-bit equality, zero mismatch, zero flat fault,
|
||
zero mismatch artifact, equal graph/flat cell residency at every stable
|
||
checkpoint, and graceful exits. Evidence:
|
||
[`../research/2026-07-25-slice-i5-dual-collision-shadow.md`](../research/2026-07-25-slice-i5-dual-collision-shadow.md).
|
||
|
||
### I6 — production cutover and graph removal
|
||
|
||
1. Flip canonical `PhysicsDataCache` records and every `BSPQuery` production
|
||
call to flat assets in one bisectable commit.
|
||
2. Keep the graph route as an automated referee for the first cutover gate,
|
||
with an exact revert command recorded in this plan and project memory.
|
||
3. After automated and user correctness acceptance, remove:
|
||
- production `PhysicsBSPTree`/`CellBSPTree` retention;
|
||
- production polygon dictionaries and vertex DBObj graphs;
|
||
- raw collision graphs from streaming build/publication payloads;
|
||
- obsolete graph adapters and referee code.
|
||
4. Live-DAT tooling may parse source graphs only to produce/compare flat
|
||
assets; gameplay production never falls back from a missing/corrupt prepared
|
||
collision asset.
|
||
|
||
Gate: trajectories and collision fixtures remain bit-identical, package-only
|
||
startup succeeds, and retained-memory accounting shows no parsed collision
|
||
DBObj graph after stable world reveal.
|
||
|
||
**Production cutover landed 2026-07-25 at
|
||
`068a06518dd710ca5e7f166754cbb7789ca1ed0f`.** Gameplay is now
|
||
flat-authoritative, package publication is strict, and the parsed graph runs
|
||
only as a sampled referee. The exact rollback for this cutover is:
|
||
|
||
```text
|
||
git revert 068a06518dd710ca5e7f166754cbb7789ca1ed0f
|
||
```
|
||
|
||
Do not revert I3–I5 package/publication work. The pre-visual automated gate
|
||
passes 8,402 Release tests / 5 skips and a strict dense-Arwic connected run
|
||
with 46,309/46,309 exact graph-referee matches, zero mismatch/fault/artifact,
|
||
and graceful shutdown. Production-like uncapped dense Arwic (15,530 entities,
|
||
no automation oracle) sustained roughly 190–200 FPS with stable CPU p50
|
||
~5.0 ms / p95 ~5.9–7.3 ms and GPU p95 ~2.1 ms. Evidence:
|
||
[`../research/2026-07-25-slice-i6-flat-production-cutover.md`](../research/2026-07-25-slice-i6-flat-production-cutover.md).
|
||
|
||
The user accepted the connected collision gate on 2026-07-25. Production
|
||
parsed-graph removal then landed at
|
||
`82f8d4f82e24ad85042ac3e25c4f6464bebce758`. The production
|
||
`PhysicsDataCache`, stable `GpuWorldState`, near-build payloads, and live
|
||
publication path now retain only immutable flat assets. Graph construction
|
||
survives solely in explicit tests, bake/equivalence tools, and source-oracle
|
||
constructors. The exact rollback for graph removal is:
|
||
|
||
```text
|
||
git revert 82f8d4f82e24ad85042ac3e25c4f6464bebce758
|
||
```
|
||
|
||
Do not revert I3–I6 unless evidence implicates the prepared package or
|
||
flat-authoritative traversal itself.
|
||
|
||
### I7 — allocation, soak, and documentation closeout
|
||
|
||
1. Measure capped and uncapped resolve allocation separately for player,
|
||
remotes, projectiles, and camera.
|
||
2. Run the canonical connected lifecycle/reconnect route and the nine-stop R6
|
||
portal soak.
|
||
3. Repeat indoor door/ramp/cellar, jump/fall, combat/projectile, and camera-wall
|
||
gates.
|
||
4. Under RDP, accept correctness and lifecycle evidence only; defer absolute
|
||
GPU/frame-time acceptance to a physical-display run.
|
||
5. Update architecture, WorldBuilder inventory, milestones, roadmap, issue
|
||
ledger, divergence register, and both physics/render memory digests.
|
||
|
||
Final gate:
|
||
|
||
- full Release build and solution tests;
|
||
- zero deterministic old-vs-flat mismatch;
|
||
- zero steady transition/query-scratch allocation;
|
||
- no raw collision graph in production stable state;
|
||
- connected portal/reconnect/interaction routes pass;
|
||
- exact cutover rollback recorded before user verification.
|
||
|
||
**Completed 2026-07-25.** Release build and the complete solution pass
|
||
8,413 tests / 5 skips. The focused collision/door/doorway/cellar/ramp/jump/
|
||
projectile/allocation gate passes 154 Core tests plus 47 App camera/projectile
|
||
tests; player, grounded publication, remote, projectile, and camera resolves
|
||
remain 0 B/resolve.
|
||
|
||
The exact-binary connected lifecycle/reconnect route and canonical nine-stop
|
||
R6 soak both pass with graceful exits. Every stable checkpoint reports
|
||
`0/0/0` retained parsed GfxObj/Setup/cell graphs while flat GfxObj/Setup/cell
|
||
residency remains populated. Movement, jump, combat, projectile, portal,
|
||
dungeon, revisit, reconnect, publication, retirement, and teardown checks
|
||
pass. The user accepted collision behavior over RDP. Absolute post-removal
|
||
GPU/frame-time and ordinary-process memory comparison remains a physical-
|
||
display measurement, not a Slice-I correctness blocker. Evidence:
|
||
[`../research/2026-07-25-slice-i7-closeout.md`](../research/2026-07-25-slice-i7-closeout.md).
|
||
|
||
**Corrective gate 2026-07-26.** Later connected house/dungeon testing exposed
|
||
one I7 consumer omission: `CEnvCell::find_transit_cells` still required the
|
||
deleted parsed portal-polygon dictionary, even though the prepared topology
|
||
and polygon table retained the exact same plane. The production consumer now
|
||
uses that direct prepared index. Synthetic graph-free tests and the captured
|
||
Holtburg cottage camera path prove identical graph/prepared results; no
|
||
package rebake or parsed-graph restoration is needed. Evidence:
|
||
[`../research/2026-07-26-prepared-indoor-transit-regression.md`](../research/2026-07-26-prepared-indoor-transit-regression.md).
|
||
|
||
## 4. Commit order
|
||
|
||
Each unit is independently buildable and reversible:
|
||
|
||
1. `docs(physics): pin Slice I collision oracle and fixtures`
|
||
2. `perf(physics): reuse reset-complete transition scratch`
|
||
3. `feat(physics): define deterministic flat collision assets`
|
||
4. `feat(content): bake and read flat collision assets`
|
||
5. `feat(physics): add differential flat BSP traversal`
|
||
6. `feat(streaming): shadow-publish flat collision assets`
|
||
7. `perf(physics): cut production traversal to flat assets`
|
||
8. `refactor(physics): remove parsed collision graphs`
|
||
9. `docs(physics): close Slice I evidence and roadmap`
|
||
|
||
The exact production-cutover commit and its `git revert <sha>` command are
|
||
added to this plan and `claude-memory/project_physics_collision_digest.md`
|
||
immediately when step 7 lands, before any visual/correctness gate.
|