feat(vfx): port retail hidden and teleport presentation

Preserve canonical live-object ownership across Hidden transitions and remote teleport placement so effects, collision, streaming, and targeting remain synchronized.
This commit is contained in:
Erik 2026-07-14 14:59:48 +02:00
parent a51ebc66e9
commit 1e98d81448
46 changed files with 4883 additions and 127 deletions

View file

@ -187,9 +187,17 @@ src/AcDream.App/
│ └── Vfx/ # (already exists)
├── Net/
│ └── LiveSessionController.cs # owns WorldSession lifecycle, login/handshake, reconnect
├── Physics/
│ ├── ProjectileController.cs # canonical live-record projectile orchestration
│ ├── RemotePhysicsUpdater.cs # ordinary/Hidden remote narrow-tick integration
│ ├── RemoteTeleportController.cs # loaded/pending teleport placement ownership
│ ├── RemoteTeleportHook.cs # ordered retail teleport teardown actions
│ └── RemoteTeleportPlacement.cs # collision-seated SetPosition transition commit
├── World/
│ ├── InboundPhysicsStateController.cs # timestamps + accepted spawn snapshots
│ ├── LiveEntityRuntime.cs # shipped: logical lifetime + ServerGuid↔entity.Id translation
│ ├── LiveEntityPresentationController.cs # ordered Hidden/NoDraw/effect/collision side effects
│ ├── LiveEntityTeardown.cs # failure-isolated multi-owner lifecycle drain
│ └── ParentAttachmentState.cs # parent generations + pending ParentEvent relations
├── Interaction/
│ └── SelectionInteractionController.cs # owns WorldPicker, selection state, Use/PickUp dispatch
@ -231,7 +239,11 @@ instead of spread across `GameWindow`.
`InboundPhysicsStateController` for the nine-channel retail timestamp gates and
latest accepted immutable CreateObject snapshot, owns the canonical local ID
and optional runtime components, and separates logical registration from
spatial projection. `ParentAttachmentState` is runtime-owned and keys unresolved
spatial projection. It also retains raw PhysicsState separately from the final
state produced by retail's ordered side effects;
`LiveEntityPresentationController` projects those transitions into draw,
collision, effect, child-NoDraw, and target visibility without becoming a
second lifetime or GUID owner. `ParentAttachmentState` is runtime-owned and keys unresolved
relations by child and parent generation. `Rendering/Vfx/EntityEffectController`
owns the focused mixed F754/F755 pending FIFO, effect profiles, typed-table
resolution, and a readiness set; canonical ServerGuid-to-local-ID translation
@ -241,6 +253,35 @@ allocators fail fast instead of wrapping into another landblock's namespace.
All other non-Parent packet
families still need the future general queue tracked by divergence AD-32.
Remote teleport placement is bounded in `Physics/RemoteTeleportController`,
not `GameWindow`: it retains at most one pending request per materialized
incarnation, scopes it by the live generation and accepted PositionSequence,
and asks `RemoteTeleportPlacement` to collision-seat the current body when the
destination projection is available. `GameWindow` supplies lifecycle and
shadow-sync callbacks only; canonical identity remains in `LiveEntityRuntime`.
Failed hydration restores the captured source and delegates an
incarnation-scoped shadow restore to `LiveEntityPresentationController` while
that source is unloaded, so Hidden/UnHide and teleport never become competing
restore owners. A newer placement transfers that restore into an explicit
generation-scoped active-placement state before its rebucket visibility edge
even while Hidden. All intervening Hidden/UnHide and projection edges defer to
that owner until stable success or rollback completes; only then can it restore,
re-defer the source, or hand a Hidden result back for UnHide. The
`ILiveEntityRemotePlacementRuntime` seam keeps the complete cell/contact
handoff available across same-body runtime-wrapper replacement; replacing the
canonical body or dropping the placement contract within one incarnation is
rejected even after an operational component clear. `RemoteMotion.Body` is
constructor-owned; hydration compares pending/current wrappers directly to the
record body rather than trusting wrapper-to-wrapper equality. Binding reads an
interface Body getter once and reuses that snapshot. `GpuWorldState`
performs remove+place as one spatial rebucket,
then commits and serially drains visibility edges; `LiveEntityRuntime` filters
delayed duplicates. A rollback inside an observer cannot race the outer
destination-visible notification or expose an intermediate false pulse.
`LiveEntityTeardown` executes those independent owner callbacks to completion
and aggregates failures afterwards, so a throwing effect/plugin sink cannot
strand teleport, movement, shadow, light, or GUID-scoped state.
---
## 4. Extraction sequence — safest first