fix(world): AP-48 client-side visibility cull (FPS sink across portal-hops)
acdream accumulated every CreateObject from every town visited and never pruned by distance/time (only on server DeleteObject / respawn de-dup), so the entity tables + the O(N^2) TickAnimations scan grew with each hop and sank FPS (confirmed in Release). Faithful port of holtburger liveness.rs (ACE_DESTRUCTION_TIMEOUT_SECS=25, CONSERVATIVE_VISIBILITY_DISTANCE_M=384): a world entity is evicted only after being >384m AND outside the 3x3 landblock neighborhood for 25s continuous (arm-on-leave / clear-on-return). Logic in a pure, unit-tested EntityVisibilityCuller; GameWindow wires a 1Hz tick that snapshots the world entities + player and tears each evicted guid down through the existing pruner. Player + held/equipped/contained items are excluded (player by guid; inventory items never carry a world position so they never enter the culled map). A re-created object starts fresh (deadline cleared on remove). Skipped during a teleport hold (frozen player position). AD-32 registered. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
76c7b1594b
commit
e5b2d15b63
4 changed files with 268 additions and 1 deletions
|
|
@ -61,7 +61,7 @@ accepted-divergence entries (#96, #49, #50).
|
|||
|
||||
---
|
||||
|
||||
## 2. Adaptation (AD) — 31 rows
|
||||
## 2. Adaptation (AD) — 32 rows
|
||||
|
||||
| # | Divergence | Where (file:line) | Why it is safe / justified | Risk if assumption breaks | Retail oracle |
|
||||
|---|---|---|---|---|---|
|
||||
|
|
@ -96,6 +96,7 @@ accepted-divergence entries (#96, #49, #50).
|
|||
| AD-29 | `ClientObjectTable` fires global `ObjectAdded`/`ObjectUpdated`/`ObjectRemoved` events; consumers filter by guid on their end. Retail dispatches per-object via `NoticeRegistrar` observer dispatch — each UI cell observes only its specific object guid | `src/AcDream.Core/Items/ClientObjectTable.cs:48` (events); `src/AcDream.App/UI/Layout/ToolbarController.cs:115` (guid filter) | `NoticeRegistrar` is inside keystone.dll with no PDB/decomp; global broadcast + consumer-side filter is functionally equivalent for the current panel count and object volumes seen in practice | At high object counts (>1 000 objects), every `ObjectUpdated` wakes every subscribed consumer — O(n·m) notification cost instead of retail's O(1) per-observer dispatch; a consumer that forgets the guid filter processes all objects (a latent correctness bug) | `NoticeRegistrar` (keystone.dll, no PDB); retail per-object observer registration in `CObjectMaint` |
|
||||
| AD-30 | Cell-march preserves seed landblock id when `TryGetTerrainOrigin` returns false for an outdoor seed (#145 D, 2026-06-22): `BuildCellSetAndPickContaining` returns `currentCellId` verbatim rather than marching via `blockOrigin=(0,0,0)`; retail never encounters this state (cells stored block-local, no streaming-gap concept) | `src/AcDream.Core/Physics/CellTransit.cs:765` | Equivalence argument: "preserve-verbatim when unregistered" is the same contract as `PhysicsEngine.Resolve`'s NO-LANDBLOCK branch; the player's cell stays the last known-correct cell until the landblock's terrain registers — no march, no lbX=0 wire | A body whose seed landblock is genuinely absent for >1 physics tick holds its last-known cell rather than discovering the true containing cell; transient only — corrects the instant terrain registers; an indoor seed is explicitly excluded from the guard (outdoor low < 0x100 gate) | `CObjCell::find_cell_list` + block-local storage (retail has no streaming gap); `TryGetTerrainOrigin` pc path |
|
||||
| AD-31 | Teleport transit covered by a full-screen black FADE (`FadeOverlay` + `TeleportAnimSequencer`) instead of retail's 3D portal-tunnel swirl (2026-06-22, spec C). Opaque black holds through the tunnel states; the world ramps back in on `WorldFadeIn`. | `src/AcDream.App/Rendering/FadeOverlay.cs` + `GameWindow.cs` (TAS transit tick; `_teleportFadeAlpha = ShowTunnel ? 1 : FadeAlpha`) | The fade is a functional cover that hides the (now-fast) destination load + the post-materialization object flood; the TAS state machine + golden timing constants are retail-verbatim — only the tunnel *graphic* is approximated. Sibling to AP-49 (fade-curve). | Visual-only: the transit shows a black cover, not the animated swirl. Retire by porting the `gmSmartBoxUI` 3D tunnel render. | `gmSmartBoxUI::UseTime` 0x004d6e30 (tunnel render, unported); `TELEPORT_ANIM_*` golden constants (spec §2.1) |
|
||||
| AD-32 | Client-side visibility cull (AP-48 fix, 2026-06-22): world entities >384m AND outside the 3×3 landblock neighborhood for 25s continuous are evicted (arm-on-leave/clear-on-return), so the entity tables don't accumulate every town's objects across portal-hops. Adaptations vs holtburger: the nearby test uses each entity's `ParentCellId`-derived landblock (vs holtburger's `scene` landblock_map), the cull runs at **1 Hz** client-side (vs per-world-tick), and the time base is wall-clock (`DateTime.UtcNow`) not server time. | `src/AcDream.App/World/EntityVisibilityCuller.cs` + `GameWindow.TickEntityCull` (wiring) + `RemoveLiveEntityByServerGuid` (Forget on remove) | Faithful port of holtburger's `liveness.rs` (`ACE_DESTRUCTION_TIMEOUT_SECS=25`, `CONSERVATIVE_VISIBILITY_DISTANCE_M=384`); the player + held/equipped/contained items are excluded (player by guid; inventory items never carry a world position so they never enter the culled map); a re-created object is rescued (deadline cleared on remove + on the next visible tick) | An object the server still tracks but that goes silent >25s while >384m away is evicted locally and re-materializes only on the next server CreateObject (matches holtburger); a coordinate-frame edge where a genuinely-near object reads >384m is covered by the landblock-neighborhood OR-term | holtburger `crates/holtburger-world/src/state/liveness.rs:11-12,145-189,294-308`; ACE `ObjectMaint`/`Player_Tracking` never clears KnownObjects on teleport |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue