fix(world): restore portal and distant use lifecycles
This commit is contained in:
parent
c730632075
commit
1a14812c44
18 changed files with 401 additions and 196 deletions
|
|
@ -3,8 +3,8 @@
|
|||
Date: 2026-07-23
|
||||
|
||||
Scope: the retained UI busy cursor and the
|
||||
`You can only move or use one item at a time` gate while an object/NPC Use
|
||||
crosses acdream's close-range turn/approach boundary.
|
||||
`You can only move or use one item at a time` gate from an accepted object/NPC
|
||||
Use through its authoritative completion.
|
||||
|
||||
## Retail oracle
|
||||
|
||||
|
|
@ -44,10 +44,26 @@ There is no retail timeout and movement cancellation is not a substitute for
|
|||
- holtburger `inventory/actions.rs` and `inventory/events.rs` independently
|
||||
confirm the one Use action / one UseDone event protocol pair.
|
||||
|
||||
The existing close-range compatibility seam is recorded as divergence AD-27:
|
||||
acdream may finish its local turn before emitting Use. That seam creates a
|
||||
real pre-wire interval which retail does not have, so it needs explicit
|
||||
ownership rather than pretending a server completion is already owed.
|
||||
The connected 2026-07-25 regression trace proved why the old AD-27 Use branch
|
||||
was incorrect: a pending local Ready animation could starve the speculative
|
||||
TurnToObject, so no Use packet ever reached ACE. Retail has no such dependency.
|
||||
`ItemHolder::UseObject` emits `CM_Inventory::Event_UseEvent` first; the event
|
||||
packs and sends opcode `0x36` in `CM_Inventory::Event_UseEvent @ 0x006AC3B0`.
|
||||
Only then does retail call `CPlayerSystem::UsingItem`. ACE owns any required
|
||||
authoritative `CreateMoveToChain`. The Use half of AD-27 is therefore retired;
|
||||
its remaining client completion adaptation applies only to pickup.
|
||||
|
||||
The immediate-send rerun then proved a distinct presentation-owner defect:
|
||||
ACE sent the correct MoveTo and acdream resolved its target, but a login Ready
|
||||
node remained at the head of the local `MotionInterpreter`. During login
|
||||
hydration the player's logical animation runtime remained current while its
|
||||
spatial projection was temporarily withdrawn. The presenter consumed the
|
||||
zero-tick PartArray motion but its spatial-only guard withheld the
|
||||
`MotionDoneTarget`, so the interpreter never popped that node. The correction
|
||||
binds completion to the exact logical live-record animation owner and drains
|
||||
the pre-controller PartArray queue before publishing a candidate interpreter.
|
||||
The connected distant-NPC gate then turned, walked, sent no duplicate Use, and
|
||||
completed through authoritative UseDone.
|
||||
|
||||
## Ported ownership pseudocode
|
||||
|
||||
|
|
@ -62,37 +78,15 @@ RequestUse(object, reservation):
|
|||
reservation.CancelBeforeDispatch()
|
||||
return
|
||||
|
||||
if object is carried or equipped by the player:
|
||||
send Use immediately
|
||||
transfer reservation to UseDone
|
||||
return
|
||||
|
||||
if world identity/approach is invalid:
|
||||
if a non-owned world object is not usable:
|
||||
reservation.CancelBeforeDispatch()
|
||||
return
|
||||
|
||||
if a close-range turn must finish first:
|
||||
pending = (exact object incarnation, approach token, reservation)
|
||||
if movement installation fails:
|
||||
pending.reservation.CancelBeforeDispatch()
|
||||
return
|
||||
|
||||
if SendUse(object) succeeds:
|
||||
reservation.MarkDispatched()
|
||||
else:
|
||||
reservation.CancelBeforeDispatch()
|
||||
|
||||
OnNaturalApproachCompletion(pending):
|
||||
if pending incarnation is stale:
|
||||
pending.reservation.CancelBeforeDispatch()
|
||||
else if SendUse(pending.object) succeeds:
|
||||
pending.reservation.MarkDispatched()
|
||||
else:
|
||||
pending.reservation.CancelBeforeDispatch()
|
||||
|
||||
OnApproachCancelledOrTargetRemoved(pending):
|
||||
pending.reservation.CancelBeforeDispatch()
|
||||
|
||||
reservation.MarkDispatched():
|
||||
// Ownership transfers to the server completion. Keep busyCount unchanged.
|
||||
make later local cancellation inert
|
||||
|
|
@ -117,10 +111,16 @@ retired session cannot decrement a new session's busy reference.
|
|||
|
||||
## Regression evidence
|
||||
|
||||
The connected trace showed successful NPC Use packets and matching
|
||||
`UseDone(0)`. A managed heap snapshot after the final completion showed
|
||||
`ItemInteractionController._busyCount == 0`, no pending inventory request, and
|
||||
no pending auto-wield transaction. The permanent failure path was instead the
|
||||
pre-wire close-range branch: it incremented busy before local movement, but
|
||||
cancellation or stale identity discarded the pending send, leaving no packet
|
||||
and therefore no possible `UseDone`.
|
||||
The 2026-07-25 Alcott trace captured the failure before correction:
|
||||
|
||||
```text
|
||||
[input] UseSelected Press
|
||||
[autowalk-pending] count=1 nodes=[0x41000003/ctx=0/jump=0x0]
|
||||
[autowalk-target] object=0x8000D085 status=Ok ...
|
||||
```
|
||||
|
||||
There was no `[B.4b] use`, no `UseDone`, and therefore no possible
|
||||
authoritative release of the busy reference. Focused tests now pin that close
|
||||
and far world Use both dispatch immediately, never install speculative local
|
||||
movement, never duplicate on a later movement callback, and retain busy
|
||||
ownership until exactly one `UseDone`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue