fix(world): complete recall before teleport hide
Match retail's update ordering so object animation, particles, and scripts advance before inbound teleport state is applied. Separate input-originated movement from post-network autonomous position output, and reconcile presentation without a second physics tick so recall cannot resume after arrival. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
dded9e6b17
commit
75acae02d6
13 changed files with 1068 additions and 332 deletions
|
|
@ -206,6 +206,7 @@ src/
|
|||
LiveEntityRuntime.cs -> canonical identity/state/body/projectile/spatial ownership
|
||||
LiveEntityPresentationController.cs -> Hidden/NoDraw/effect/collision presentation
|
||||
LiveEntityTeardown.cs -> failure-isolated multi-owner lifecycle drain
|
||||
RetailLiveFrameCoordinator.cs -> object/physics-before-network frame barrier
|
||||
Rendering/
|
||||
GameWindow.cs -> still owns too much runtime wiring
|
||||
TerrainModernRenderer.cs -> mandatory bindless+MDI terrain path
|
||||
|
|
@ -642,7 +643,19 @@ canonical local physics controller and chase-camera handoff.
|
|||
The destination residency gate supplies retail's `EndTeleportAnimation` edge
|
||||
once asynchronous streaming is ready. Player placement remains authoritative
|
||||
and separate from presentation; arrival resets camera viewer state but does
|
||||
not reset the character animation sequence. See
|
||||
not reset the character animation sequence. `RetailLiveFrameCoordinator`
|
||||
matches `SmartBox::UseTime @ 0x00455410`: it advances local and remote object/
|
||||
projectile runtime, composes final animated/equipped poses, drains animation
|
||||
hooks, then advances particles before owner scripts and inbound network dispatch.
|
||||
`RetailLocalPlayerFrameController` owns the player's exactly-once tick and sends
|
||||
input-originated movement/jump output on the object side of that barrier. After
|
||||
inbound dispatch, the retail command-interpreter phase evaluates periodic
|
||||
AutonomousPosition from current authoritative state, then a non-advancing spatial
|
||||
reconciliation updates equipped children and live effect/light anchors. Consequently a
|
||||
teleport Hidden transition cannot freeze the last fraction of recall and replay
|
||||
it after UnHide, and ForcePosition/F751 cannot combine old one-shot input with a new cell.
|
||||
The draw pass consumes that completed update snapshot and never advances live
|
||||
animation independently. See
|
||||
`docs/research/2026-07-15-retail-portal-space-pseudocode.md`.
|
||||
|
||||
## Roadmap Model
|
||||
|
|
|
|||
|
|
@ -593,7 +593,7 @@ Research: R9 + R12 + R13.
|
|||
- **✓ SHIPPED — G.1 — Sky + weather + day-night.** Deterministic client-side from Portal Year time. Sky dome geometry + keyframe gradients + rain/snow particles. See `r12-weather-daynight.md`. Full data + visual stack shipped: Region dat loader, keyframe interp, WeatherSystem with 5-kind PDF + transitions + storm flashes, WorldSession→WorldTimeService sync via ConnectRequest+TimeSync, SkyRenderer with sky-object arcs + UV scroll, rain/snow billboard renderer, F7/F10 debug cycle keys.
|
||||
- **✓ SHIPPED — G.2 — Dynamic lighting.** 8-light D3D-style fixed pipeline. Hard-cutoff at Range, no attenuation inside. Cell ambient. Shader UBO per frame. See `r13-dynamic-lighting.md`. SceneLightingUbo std140 at binding=1 feeds terrain + mesh + mesh_instanced + sky shaders. LightingHookSink auto-registers Setup.Lights at entity stream-in, flips IsLit on SetLightHook, unregisters on landblock unload.
|
||||
- **Indoor portal-based cell tracking (follow-up to Indoor walking Phase 1 / issue #87).** Replace `PhysicsDataCache.TryFindContainingCell` AABB containment with retail's `CObjMaint::HandleObjectEnterCell` portal traversal. When the player crosses a cell portal boundary, `CellId` propagates through the `CEnvCell` portal connectivity graph. Prerequisite for wall collision from outside (#85) and the remaining #84 threshold symptom. PDB symbols and `acclient.h` `CCellStructure` refs are in place (see #87). **Unblocks G.3.**
|
||||
- **✓ SHIPPED — G.3 — Dungeon streaming + portal space.** `EnvCellStreamer`, portal-visibility BFS, `PlayerTeleport (0xF751)` handling with post-transition `LoginComplete`, and the retail DAT-authored portal-space CreatureMode viewport (Setup `0x02000306`, animation `0x030005AC`, exact camera/light/timing/view-plane transitions, forward-axis roll, direct portal→world viewport switch, projection warp, and destination viewer reset; corrective visual gate pending 2026-07-15). Dungeons render, stream, teleport-in, collide, light, and their doors work — see the shipped-table rows (G.3, G.3a, #137 collision, A7.L1 lighting) below and the M1.5 section of `docs/plans/2026-05-12-milestones.md` for current gate status (one recorded end-to-end round-trip user gate outstanding; live residual = far-town teleport-OUT arrival cascade, #145-residual REOPENED). See `r09-dungeon-portal-space.md` and `docs/research/2026-07-15-retail-portal-space-pseudocode.md`.
|
||||
- **✓ SHIPPED — G.3 — Dungeon streaming + portal space.** `EnvCellStreamer`, portal-visibility BFS, `PlayerTeleport (0xF751)` handling with post-transition `LoginComplete`, and the retail DAT-authored portal-space CreatureMode viewport (Setup `0x02000306`, animation `0x030005AC`, exact camera/light/timing/view-plane transitions, forward-axis roll, direct portal→world viewport switch, projection warp, destination viewer reset, object/physics-before-network frame barrier, pre-network input movement/jump output, post-inbound AutonomousPosition, and non-advancing spatial reconciliation; recall-tail corrective visual gate pending 2026-07-16). Dungeons render, stream, teleport-in, collide, light, and their doors work — see the shipped-table rows (G.3, G.3a, #137 collision, A7.L1 lighting) below and the M1.5 section of `docs/plans/2026-05-12-milestones.md` for current gate status (one recorded end-to-end dungeon round-trip user gate outstanding; live residual = far-town teleport-OUT arrival cascade, #145-residual REOPENED). See `r09-dungeon-portal-space.md` and `docs/research/2026-07-15-retail-portal-space-pseudocode.md`.
|
||||
|
||||
**Acceptance:** walk outside at dusk, see the sky gradient + sun moving; enter a torch-lit dungeon via portal; leave back to daylight.
|
||||
|
||||
|
|
|
|||
|
|
@ -312,6 +312,16 @@ a black alpha fade; both compose below retained UI, so
|
|||
chat, panels, toolbar, cursor, and input remain live throughout travel. See
|
||||
`docs/research/2026-07-15-retail-portal-space-pseudocode.md`.
|
||||
|
||||
The live frame barrier now also matches `SmartBox::UseTime`: local/remote
|
||||
object, projectile, animation, final-pose, animation-hook, particle, and
|
||||
owner-script work completes before the inbound network queue is dispatched.
|
||||
Local movement/jump output is committed on that same pre-network side, while a
|
||||
non-advancing post-network pass reconciles children and attached effect/light
|
||||
anchors after authoritative root moves. This prevents ACE's teleport Hidden state
|
||||
from freezing the final fraction of recall and replaying it after materialize,
|
||||
without an arrival reset or Hidden-time animation exception. Automated recall
|
||||
DAT conformance is green; destination-side visual confirmation remains.
|
||||
|
||||
---
|
||||
|
||||
#### (historical M1.5 working notes below)
|
||||
|
|
@ -513,9 +523,9 @@ include dungeons.
|
|||
zero-time recursive chains fail diagnostically while valid timed weather loops
|
||||
continue. The
|
||||
live-pose step now publishes exact root/indexed rigid-part transforms after
|
||||
animation and equipped-child composition, then drains animation hooks,
|
||||
PhysicsScripts, attached emitters/lights, and particle simulation in fixed
|
||||
frame order. World-released particles remain at their birth positions while
|
||||
animation and equipped-child composition, advances existing FP hooks, drains
|
||||
animation hooks, refreshes attached emitters/lights, advances particles, then
|
||||
ticks PhysicsScripts in fixed retail frame order. World-released particles remain at their birth positions while
|
||||
parent-local particles follow; normal/blocking/anonymous logical emitter
|
||||
semantics are exact; pending projections skip particle updates/draw while
|
||||
retaining absolute creation times, the same emitter, and its logical ID, so
|
||||
|
|
|
|||
|
|
@ -844,8 +844,17 @@ Repository links:
|
|||
4. No client-side impact, explosion, damage, or deletion is synthesized.
|
||||
5. Presentation frame ordering is fixed: advance animation/root motion,
|
||||
publish indexed parts, compose equipped children, drain animation hooks,
|
||||
tick owner PhysicsScripts, refresh attached emitters/lights, then advance
|
||||
particle simulation and draw.
|
||||
refresh attached emitters/lights, advance particle simulation, tick owner
|
||||
PhysicsScripts, dispatch inbound network events, run the post-inbound
|
||||
command-interpreter position check, reconcile spatial derivatives without
|
||||
advancing time, then draw. The explicit network boundary ports
|
||||
`SmartBox::UseTime @ 0x00455410`: `CPhysics::UseTime` updates every object
|
||||
(including its particle and script managers) before SmartBox drains its
|
||||
inbound queue, after which `CommandInterpreter::UseTime @ 0x006B3BF0`
|
||||
evaluates AutonomousPosition. Within each object,
|
||||
`CPhysicsObj::UpdateObjectInternal @ 0x005156B0` updates
|
||||
`ParticleManager` before `ScriptManager`, so a particle created by a script
|
||||
begins simulation on the following object frame.
|
||||
|
||||
Retail stages animation hooks inside `CSequence::update`, then executes them
|
||||
from `CPhysicsObj::process_hooks` before the new root is committed. The same
|
||||
|
|
|
|||
|
|
@ -276,10 +276,45 @@ The viewer reset copies the player's complete position into both `viewer` and
|
|||
boom outward through the same damped and swept path used after camera
|
||||
collision. Reusing the source-world viewer is not retail behavior.
|
||||
|
||||
## Recall completion versus Hidden
|
||||
|
||||
The retail teleport hook does not clear the character's animation sequence.
|
||||
The arrival presentation is instead hidden by the portal scene and its final
|
||||
world view-plane transition while the normal motion/update stream settles. Therefore acdream
|
||||
must not add an arrival-only animation reset.
|
||||
Recall completion is instead guaranteed by the enclosing frame order:
|
||||
|
||||
```text
|
||||
Client.UseTime:
|
||||
Timer.update_time()
|
||||
ClientNet.UseTime()
|
||||
process packet controller/cache/UI work
|
||||
SmartBox.UseTime()
|
||||
SmartBox.Draw()
|
||||
|
||||
SmartBox.UseTime:
|
||||
CObjectMaint.UseTime()
|
||||
CPhysics.UseTime() // advances PartArray, particles, scripts
|
||||
GameTime/LScape/Ambient.UseTime()
|
||||
while inbound queue not empty:
|
||||
DispatchSmartBoxEvent() // SetState/Hidden lands here
|
||||
CommandInterpreter.UseTime() // ShouldSendPositionEvent -> AP
|
||||
```
|
||||
|
||||
The exact call sites are `Client::UseTime` `0x00411C40` and
|
||||
`SmartBox::UseTime` `0x00455410`, and `CommandInterpreter::UseTime`
|
||||
`0x006B3BF0`. `SmartBox::DoSetState` `0x004520D0`
|
||||
applies Hidden directly and does not advance or reset animation. Separately,
|
||||
`CPhysicsObj::UpdatePositionInternal` `0x00512C30` skips `CPartArray::Update`
|
||||
while Hidden. Therefore the ordering boundary is load-bearing: the current
|
||||
frame's animation must advance before an inbound Hidden transition can freeze
|
||||
it.
|
||||
|
||||
ACE schedules lifestone recall teleport after the MotionTable-reported action
|
||||
length. With the installed human DATs, the boundary is about 15.06024 seconds;
|
||||
floating-point conversion can leave acdream at frame 149.999998 of the
|
||||
0..149 recall node if it dispatches Hidden before the current object tick. On
|
||||
UnHide that microscopic remainder becomes a visible destination-side recall
|
||||
tail. Retail's object-before-network order retires the node first. The faithful
|
||||
fix is that frame barrier, never an arrival-only Ready/reset command and never
|
||||
advancing PartArray while Hidden.
|
||||
|
||||
## acdream integration translation
|
||||
|
||||
|
|
@ -303,6 +338,23 @@ must not add an arrival-only animation reset.
|
|||
strand the transit, while canonical physics retains its own retail gates.
|
||||
- Destination placement resets the retail chase camera's published and sought
|
||||
positions to the player before the normal update path resumes.
|
||||
- `RetailLiveFrameCoordinator` owns the `SmartBox::UseTime` phase barrier:
|
||||
local/remote object and projectile advancement, final pose/hook publication,
|
||||
attached emitters/lights, particle simulation, and PhysicsScripts all precede
|
||||
inbound session dispatch in that retail order; rendering consumes the resulting
|
||||
coherent snapshot.
|
||||
- `RetailLocalPlayerFrameController` owns the local player's exactly-once
|
||||
object tick. Input-originated movement and jump packets are serialized from
|
||||
its pre-network result; F751 or ForcePosition received later in the frame
|
||||
cannot relocate or replay those one-shot commands. After inbound dispatch,
|
||||
`CommandInterpreter::UseTime @ 0x006B3BF0` evaluates AutonomousPosition from
|
||||
the current controller frame, matching its exact retail slot.
|
||||
- After inbound dispatch, a non-time-advancing reconciliation composes equipped
|
||||
children and refreshes emitter/light anchors from the authoritative root. It
|
||||
does not rerun animation, scripts, particles, or fades. A player first created
|
||||
by that inbound pass is likewise projected without receiving a late physics
|
||||
tick; its first normal object tick occurs on the next update.
|
||||
This is update-loop ordering, not portal-specific animation logic.
|
||||
- Destination residency remains acdream's asynchronous adaptation. It supplies
|
||||
the same `EndTeleportAnimation` edge retail receives when its blocking cell
|
||||
load completes; it does not alter presentation ordering.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue