fix(streaming): preserve portal destination ownership
Detach old-world spatial ownership atomically, prioritize destination retirement dependencies, and reveal the viewport at the retail transition edge. Give private paperdoll views independent mesh ownership and retain dormant ACE entities so portal revisits preserve server objects without extending active GPU lifetimes.
This commit is contained in:
parent
2c848d4167
commit
823936ec31
57 changed files with 2551 additions and 324 deletions
|
|
@ -452,6 +452,13 @@ script creation. Its canonical materialized view remains stable across pending
|
|||
landblocks, while a separate visible-only view feeds radar, picking, status, and
|
||||
targeting. Raw server PhysicsState and the final state produced by retail's
|
||||
ordered Lighting/NoDraw/Hidden side effects are stored separately.
|
||||
When retail's 25-second leave-visibility deadline expires, active ownership
|
||||
still ends completely. The ACE adapter retains only the accepted
|
||||
`EntitySpawn` in `DormantLiveEntityStore`, because ACE can keep that GUID in
|
||||
`KnownObjects` and omit a later CreateObject on revisit. A matching landblock
|
||||
load re-enters the ordinary timestamp- and generation-gated hydration path;
|
||||
F747, a newer incarnation, or session reset removes the cold snapshot. Dormant
|
||||
records own no frame-time, physics, render, effect, audio, or GPU resource.
|
||||
`LiveEntityPresentationController` drains those accepted transitions only after
|
||||
the renderer/effect owner is ready: Hidden suppresses the retained root's mesh,
|
||||
collision, interaction, radar, and target eligibility while preserving the
|
||||
|
|
@ -735,9 +742,14 @@ liveness, and spatial reconciliation stop; world-space audio is silenced; UI,
|
|||
network/event/command delivery, portal presentation, destination streaming, and
|
||||
readiness continue. Canonical live records remain retained until physical
|
||||
teardown converges, so quiescence neither reconstructs server identity nor
|
||||
invents Hidden/UnHide transitions. Full-window and shared-origin retirement
|
||||
advance only from `StreamingController.Tick` through stable resident and
|
||||
per-owner entity/stage cursors on the frame's one meter.
|
||||
invents Hidden/UnHide transitions. A shared-origin recenter atomically removes
|
||||
the complete old spatial generation and captures one exact receipt per
|
||||
landblock before the origin changes. Expensive script, physics, render, and GL
|
||||
release then advances only from `StreamingController.Tick` through stable
|
||||
per-owner entity/stage cursors on the frame's one meter. Publication remains
|
||||
fenced only by an older receipt for the same canonical landblock; the active
|
||||
destination's exact receipt may advance ahead of unrelated cleanup without
|
||||
reordering the remaining FIFO.
|
||||
|
||||
Accepted publication is likewise retained at its exact priority-queue head.
|
||||
`LandblockPresentationPipeline` meters render, physics, static, building, and
|
||||
|
|
@ -767,9 +779,14 @@ The Slice E connected closeout is recorded in
|
|||
`WorldRevealCoordinator` is the single App-layer owner of each login or portal
|
||||
reveal lifetime. It composes the canonical `WorldRevealReadinessBarrier` with
|
||||
generation-scoped lifecycle observation and the streaming destination
|
||||
reservation, so begin, preparation, readiness, materialization, world
|
||||
visibility, completion, cancellation, and reserved capacity cannot be wired
|
||||
independently. The barrier joins `StreamingController`/`GpuWorldState`
|
||||
reservation plus a generation-scoped render-resource profile, so begin,
|
||||
preparation, readiness, materialization, viewport release, protocol completion,
|
||||
cancellation, and reserved capacity cannot be wired independently. While the
|
||||
normal world viewport is withheld, mesh and composite upload owners may admit
|
||||
more small destination items per frame, but retain the ordinary 8 MiB byte,
|
||||
array, buffer, and mipmap ceilings. The exact profile ends at the retail
|
||||
portal-to-world viewport swap; it does not reduce the readiness radius or
|
||||
reveal incomplete content. The barrier joins `StreamingController`/`GpuWorldState`
|
||||
static-mesh publication, `WbDrawDispatcher` composite-texture warmup, and
|
||||
`PhysicsEngine` destination residency. Login's sky-only gate and portal-space
|
||||
transit consume the same predicate; they differ only in presentation. There is
|
||||
|
|
@ -822,6 +839,12 @@ and before retained UI. The shared dispatcher is reset to no world-cell clips
|
|||
and no world point lights for this pass. Chat, gameplay windows, toolbar,
|
||||
cursor, and input continue normally above it. The synthetic object never
|
||||
enters `LiveEntityRuntime`, collision, picking, radar, or server GUID state.
|
||||
Portal, paperdoll, and creature-appraisal viewports share
|
||||
`SyntheticEntityMeshReferenceOwner` but own independent leases. World
|
||||
landblock retirement therefore cannot evict a mesh still used by retained UI.
|
||||
The paperdoll keeps its last successful private object while the SmartBox
|
||||
player is temporarily unavailable and clears it only at the character-session
|
||||
boundary, matching `gmPaperDollUI::RedressCreature`.
|
||||
|
||||
`TeleportViewPlaneController` is the App-layer projection adapter for retail
|
||||
`SmartBox::SetOverrideFovDistance` / `Render::set_vdst`. It captures the active
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ AP-94..AP-112 for the confirmed retail-UI completion gaps.
|
|||
| AP-65 | **PickupEvent (0xF74A) no longer evicts the weenie from `ClientObjectTable`** — only `DeleteObject (0xF747)` evicts, matching the retail `object_table`-vs-`weenie_object_table` split. An item another player picks up near you (only ever gets `PickupEvent`, never `DeleteObject`) lingers as a data-only entry (`ContainerId 0`, not in any view) until teleport/relog `Clear()`. | `src/AcDream.Core.Net/ObjectTableWiring.cs` (EntityDeleted handler); `src/AcDream.Core.Net/WorldSession.cs` (PickupEvent branch) | The weenie entry for nearby pickups is a harmless data ghost — no UI shows it (no container, not wielded). Retail evicts it from `weenie_object_table` when the object fully leaves interest range via `DeleteObject`; acdream defers to teleport/relog clear. | Slight memory growth in long sessions if many world items are picked up by other players near you; item data ghosts cannot cause functional issues because no UI queries `ContainerId 0`. | `CACObjectMaint::DeleteObject` / `SmartBox::HandleDeleteObject`; ACE `Player_Inventory.cs TryDequipObjectWithNetworking` |
|
||||
| ~~AP-66~~ | **RETIRED 2026-07-13 — authored paperdoll empty-slot presentation.** The earlier “no silhouettes” conclusion inspected the ItemList elements' own media but missed `UIElement_ItemList::InternalCreateItem`, which clones a distinct `UIElement_UIItem` catalog prototype for each location. All 21 supported jewelry, weapon, ammo, shield, clothing, cloak, trinket, and armor lists now resolve their exact `ItemSlot_Empty` surface from live DAT; `PostInit` confirms non-armor lists remain visible while the nine armor lists toggle with Slots. | `src/AcDream.App/UI/Layout/PaperdollSlotBackgrounds.cs`; `ItemListCellTemplate.cs`; `PaperdollController.cs` | — | — | `gmPaperDollUI::GetLocationInfoFromElementID @ 0x004A37F0`; `PostInit @ 0x004A5360`; `UIElement_ItemList::InternalCreateItem @ 0x004E3570`; `LayoutDesc 0x21000037` |
|
||||
| AP-68 | acdream keeps the 128 nearest-to-CAMERA point lights live (`MaxGlobalLights=128`, `BuildPointLightSnapshot`) and selects per cell CAMERA-INDEPENDENTLY (by the cell's own bounds), so a building interior stays lit at any distance within a town; retail keeps only the 40 nearest-to-PLAYER static lights (`Render::max_static_lights=0x28`, distance-sorted replace-farthest `insert_light`) and re-bakes a cell when its live light set changes, so distant interiors are baked dark and "light up" only as the player approaches and their torches enter the live 40. INTENTIONAL — acdream's always-lit interiors are the preferred behavior (no 1999-era light-budget pop-in); user-confirmed 2026-06-20. | `src/AcDream.Core/Lighting/LightManager.cs` (`MaxGlobalLights=128`, `BuildPointLightSnapshot`, `SelectForObject`) | The retail pop-in is a fixed-function light-budget artifact, not an intended aesthetic; revert to retail by clamping the global set to 40 + distance-to-player sort if ever desired | Distant town interiors are lit in acdream where retail's are dark until approached — a deliberate, user-preferred divergence | `Render::max_static_lights` 0x28; `insert_light` 0x0054d1b0 (distance-sorted, replace-farthest); bake re-trigger `SetStaticLightingVertexColors` cache `burnedInStaticLights != num_static_lights` |
|
||||
| AP-69 | acdream now preserves one accepted live record across rebucketing and ports retail's 25-second leave-visibility destruction lifecycle. Spatially resident records cancel expiry; otherwise the ACE compatibility boundary uses holtburger's conservative 384-unit distance envelope and retains attached/container/wielder/parent-owned objects. Expiry routes through the exact generation-safe F747 teardown. DIVERGENCE: the fallback does not yet derive visibility from retail/ACE ObjCell PVS (`SeenOutside` plus `VisibleCells`), and trade/container preview retention has no separate lifecycle flag. | `src/AcDream.App/World/LiveEntityRuntime.cs`; `src/AcDream.App/World/LiveEntityLivenessController.cs`; `src/AcDream.App/Streaming/GpuWorldState.cs`; `LiveEntityHydrationController.OnPrune` | Prevents stale portal destinations from accumulating animation/effect/render owners while retaining loaded long-view objects and canonical identity | A nonresident object outside 384 units that remains visible through an unusual long EnvCell PVS could expire early; a future preview-only object with no parent/container ownership could also expire. Replace the compatibility predicate when exact ObjCell PVS and preview lifetimes are available | `CPhysicsObj::prepare_to_leave_visibility` 0x00511F40; `CPhysicsObj::prepare_to_enter_world` 0x00511FA0; `CObjectMaint::AddObjectToBeDestroyed` 0x00508F70; `CObjectMaint::UseTime` 0x005089B0; `docs/research/2026-07-18-retail-object-liveness-and-mesh-reclamation-pseudocode.md` |
|
||||
| AP-69 | acdream preserves one accepted active record across rebucketing and ports retail's 25-second leave-visibility destruction lifecycle. Spatially resident records cancel expiry; otherwise the ACE compatibility boundary uses holtburger's conservative 384-unit distance envelope and retains attached/container/wielder/parent-owned objects. Expiry uses the exact generation-safe active teardown, then retains only a cold `EntitySpawn` because ACE can keep the GUID in `KnownObjects` and omit CreateObject on revisit; explicit F747/new generation/session reset removes it. DIVERGENCE: retail can delete the complete object under its visibility protocol; the fallback does not yet derive visibility from retail/ACE ObjCell PVS (`SeenOutside` plus `VisibleCells`), and trade/container preview retention has no separate lifecycle flag. | `src/AcDream.App/World/LiveEntityRuntime.cs`; `src/AcDream.App/World/LiveEntityLivenessController.cs`; `src/AcDream.App/World/DormantLiveEntityStore.cs`; `LiveEntityHydrationController.OnPrune` | Prevents stale portal destinations from accumulating animation/effect/render owners while still allowing doors, signs, portals, and other ACE-known objects to rematerialize when the server does not resend them | Dormant data-only snapshots can grow with every unique ACE destination until F747 or session reset; a nonresident object outside 384 units that remains visible through an unusual long EnvCell PVS could expire early; a future preview-only object with no parent/container ownership could also expire. Replace the compatibility predicate when exact ObjCell PVS and preview lifetimes are available | `CPhysicsObj::prepare_to_leave_visibility` 0x00511F40; `CPhysicsObj::prepare_to_enter_world` 0x00511FA0; `CObjectMaint::AddObjectToBeDestroyed` 0x00508F70; `CObjectMaint::UseTime` 0x005089B0; ACE `KnownObjects`; `docs/research/2026-07-18-retail-object-liveness-and-mesh-reclamation-pseudocode.md` |
|
||||
| AP-71 | **`CEnvCell::find_env_collisions` omits the `CObjCell::check_entry_restrictions` gate** — retail's `CEnvCell::find_env_collisions` (pc:309576) calls `CObjCell::check_entry_restrictions` (pc:309576) FIRST and returns `COLLIDED` when an access-locked cell's `restriction_obj` entity rejects the mover (`CanMoveInto` fails AND `CanBypassMoveRestrictions` is false). acdream's `FindEnvCollisions` (`src/AcDream.Core/Physics/TransitionTypes.cs:2088`) goes straight to BSP collision. | `src/AcDream.Core/Physics/TransitionTypes.cs:2088` | **No access-restriction cell data exists in acdream.** `CellPhysics` (`src/AcDream.Core/Physics/PhysicsDataCache.cs:540`) has no `restriction_obj` field; `DatReaderWriter` does not model per-cell access locks; no weenie-object-table exists on the client for the gate's `CanMoveInto` / `CanBypassMoveRestrictions` dispatch. The gap is **inert** in all dev content (ACE starter area has no access-locked env cells). | If access-locked dungeon cells are ever modeled (dat + wire), the player will walk through the restriction barrier without being blocked — wrong PK/housing gating. | `CObjCell::check_entry_restrictions` pc:309576; `CEnvCell::find_env_collisions` pc:309573–309597 |
|
||||
| AP-72 | **Cursor art falls back to OS standard cursors when dat resolution fails** — retail always renders MediaDescCursor / EnumIDMap-resolved dat cursor art; acdream's `RetailCursorManager.Apply` falls back to Silk `StandardCursor` (IBeam/crosshair/not-allowed/…) when the EnumIDMap chain or RenderSurface decode fails, and `RetailCursorResolver`/`RetailCursorManager` permanently negative-cache the failed enum/surface id for the session. | `src/AcDream.App/Rendering/RetailCursorManager.cs:47` (`ApplyStandard`), `RetailCursorResolver.cs:47` (negative cache) | Fallback triggers only when the dat lacks the asset — nominal EoR dats always resolve the 0x27/0x28/0x29 chain; an OS cursor keeps the UI usable rather than showing nothing. | A dat-read or decode regression silently shows OS-native cursors instead of surfacing an error — masked failure class; check the `[D.2b]` cursor log lines before suspecting art. | `ClientUISystem::UpdateCursorState` 0x00564630 |
|
||||
| AP-74 | **UseDone WeenieError text comes from a hardcoded subset map, not the portal String tables** — retail resolves the 0x01C7 UseDone error code through the client String tables into the canonical line ("You are not trained in healing!"); acdream's `WeenieErrorText.For` hardcodes the handful of codes the current use/heal flows produce (0x001D/0x04EB/0x04FC/0x04FE, texts phrased after the ACE enum names) with a generic code-carrying fallback. | `src/AcDream.Core.Net/Messages/WeenieErrorText.cs` | Every refusal is now visible; only unmapped wording deviates, and those lines retain the raw code. Retire by porting the String-table lookup (#202). | An unmapped WeenieError shows a generic line instead of retail's exact sentence | retail String-table error lookup; ACE `WeenieError.cs` values |
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue