Record the accepted flat-authoritative collision cutover, production parsed-graph removal, exact rollbacks, zero-residency connected gates, and RDP measurement boundary. Reconcile architecture, milestones, roadmap, issue ledger, agent instructions, and the active Slice J handoff. Validated by the existing zero-warning Release build, 8,413 passing Release tests with five pre-existing skips, 201 focused closeout tests, and both exact-binary connected routes. Co-authored-by: Codex <codex@openai.com>
18 KiB
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
-
Retail collision math is frozen. This slice changes storage and scratch ownership only.
-
Every float read from DAT is copied bit-for-bit. The bake performs no normalization, quantization, coordinate conversion,
doubleround-trip, or plane reconstruction. -
Positive/negative child order, polygon order within every leaf, early returns, epsilon comparisons, and contact-selection tie behavior remain exact.
-
The current object-graph traversal remains an executable oracle until the flat traversal passes mass differential and connected gates.
-
Physics
Transitionreuse lands only after reset-completeness, cross-mover-identity, failure, and reentrancy tests prove that no state can leak between resolves. -
No GL/App dependency enters Core. Flat collision records live in
AcDream.Core; serialization and package access live inAcDream.Content; the bake tool produces them offline. -
The active G4 render cutover is independent. Its exact visual rollback remains:
git revert ef1d263337997bb030eadb7b8e71d73dc659907a
2. Target data architecture
The runtime collision representation is immutable, array-backed, and addressed by integer index:
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
- Grep named retail before touching traversal:
BSPTREE::find_collisionsBSPNODE::find_walkableBSPLEAF::find_walkableBSPNODE::sphere_intersects_*BSPNODE::point_inside_cell_bspCTransition::find_transitional_positionCTransition::init_pathCPhysicsObj::transition
- Cross-reference ACE and the existing acdream pseudocode/trajectory notes.
- Write one collision-layout pseudocode note that separates: source storage, traversal order, mutable query scratch, and returned side-effects.
- Inventory every production call into
BSPQuery, every field read fromPhysicsBSPNode,CellBSPNode,ResolvedPolygon,CellPhysics,GfxObjPhysics, andSetupPhysics, and every raw DAT graph retained byLandblockBuild/PhysicsDatBundle. - 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.
- 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
- Add explicit
ResetForReusemethods toObjectInfo,CollisionInfo,SpherePath, andTransition. - 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.
- Give each
PhysicsEngineone owned transition lease. The normal update-thread path rents, initializes, resolves, snapshots the value-only result, and returns infinally. - Make reentrancy explicit and tested. Never hand one mutable transition to two concurrent/nested resolves and never silently share session scratch.
- 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.
- Convert short-lived
BSPQuery.CollisionSpherereference objects to value/ref scratch while preserving each mutation/copy boundary. - 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.
- 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.
I2 — immutable flat collision schema and flattener
- Add Core-only flat record types with explicit index/range contracts.
- Flatten physics and containment BSPs iteratively during preparation while emitting deterministic pre-order node arrays.
- Copy planes, spheres, vertices, setup dimensions, and collision metadata
verbatim. Tests compare every float via
SingleToInt32Bits. - Resolve each leaf polygon ID to a direct polygon index at preparation time. Missing IDs are corruption, not a runtime fallback.
- 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.
- 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.
I3 — package serialization, bake, and prepared collision source
- Append new
PakAssetTypevalues; never renumber the existing mesh values. - Bump
CurrentBakeToolVersionbecause a complete production package now includes collision assets. - Add strict little-endian serializers with checked counts/ranges, exact float bits, cancellation, CRC coverage, corruption rejection, and trailing-byte rejection.
- Add a typed
IPreparedCollisionSourceover the existing memory-mapped pak. It shares package/catalog lifetime but does not overload the render-onlyObjectMeshDataAPI. - Bake:
- GfxObj physics BSP + polygons + visual bounds;
- Setup cylinder/sphere/dimension records;
- CellStruct physics BSP + containment BSP + physics/portal polygons;
- EnvCell topology records.
- Alias only byte-identical immutable collision payloads. Cell-specific topology and world placement cannot be aliased merely because geometry is.
- 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.
I4 — flat traversal shadow implementation
- 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.
- Share only polygon-level math that is storage-independent. Do not “simplify” recursion, branch ordering, or early returns.
- Preserve mutation semantics for valid-position spheres, hit polygon, path, contact plane, slide normal, walk interpolation, and transition state.
- 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.
- Sweep a large installed-DAT sample with randomized points, spheres, movement vectors, insert modes, orientations, scales, and boundary values.
- 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.
I5 — dual publication and connected shadow gate
- Extend collision preparation so a
LandblockBuildcarries immutable flat assets and small placement/topology records rather than reparsing them on the update thread. - During the shadow phase, publish both current and flat views under one landblock generation/receipt.
- Keep the current graph path authoritative. Run sampled flat queries from cloned inputs and emit a deterministic mismatch artifact without affecting gameplay.
- Verify load, pending-to-loaded, demotion, rehydrate, same-location revisit, removal, cancellation, and session reset release both views exactly once.
- 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.
I6 — production cutover and graph removal
- Flip canonical
PhysicsDataCacherecords and everyBSPQueryproduction call to flat assets in one bisectable commit. - 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.
- After automated and user correctness acceptance, remove:
- production
PhysicsBSPTree/CellBSPTreeretention; - production polygon dictionaries and vertex DBObj graphs;
- raw collision graphs from streaming build/publication payloads;
- obsolete graph adapters and referee code.
- production
- 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:
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.
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:
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
- Measure capped and uncapped resolve allocation separately for player, remotes, projectiles, and camera.
- Run the canonical connected lifecycle/reconnect route and the nine-stop R6 portal soak.
- Repeat indoor door/ramp/cellar, jump/fall, combat/projectile, and camera-wall gates.
- Under RDP, accept correctness and lifecycle evidence only; defer absolute GPU/frame-time acceptance to a physical-display run.
- 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.
4. Commit order
Each unit is independently buildable and reversible:
docs(physics): pin Slice I collision oracle and fixturesperf(physics): reuse reset-complete transition scratchfeat(physics): define deterministic flat collision assetsfeat(content): bake and read flat collision assetsfeat(physics): add differential flat BSP traversalfeat(streaming): shadow-publish flat collision assetsperf(physics): cut production traversal to flat assetsrefactor(physics): remove parsed collision graphsdocs(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.