fix(world): restore portal and distant use lifecycles

This commit is contained in:
Erik 2026-07-25 09:59:25 +02:00
parent c730632075
commit 1a14812c44
18 changed files with 401 additions and 196 deletions

View file

@ -24,21 +24,19 @@ internal sealed class SelectionInteractionController
private readonly OutboundInteractionQueue _outbound = new();
private readonly PlayerApproachCompletionState _approachCompletions;
private readonly Action<string>? _toast;
private PendingPostArrivalAction? _pendingPostArrival;
private PendingPostArrivalPickup? _pendingPostArrival;
private readonly record struct QueuedInteractionIdentity(
uint ServerGuid,
uint? LocalEntityId,
ClientObject? ClientObject);
private readonly record struct PendingPostArrivalAction(
private readonly record struct PendingPostArrivalPickup(
uint ServerGuid,
uint LocalEntityId,
bool IsPickup,
uint DestinationContainerId,
int Placement,
ulong PendingPlacementToken,
ItemUseRequestReservation? UseReservation,
PlayerApproachToken ApproachToken);
public SelectionInteractionController(
@ -238,46 +236,23 @@ internal sealed class SelectionInteractionController
return;
}
// ItemHolder::UseObject @ 0x00588A80 sends Event_UseEvent for an
// accepted carried-item activation before CPlayerSystem::UsingItem.
// A carried source has no world pose to approach: routing it through
// TryGetApproach drops the packet because inventory objects are not
// live spatial entities.
// ItemHolder::UseObject @ 0x00588A80 sends Event_UseEvent before
// CPlayerSystem::UsingItem for every accepted ordinary Use. The latter
// is presentation/local inventory policy; it never delays the wire
// request behind a client-side TurnTo/MoveTo completion. ACE owns the
// authoritative approach chain for spatial targets.
if (_items.IsOwnedByPlayer(serverGuid))
{
SendUseNow(serverGuid, reservation);
return;
}
if (!_query.IsUseable(serverGuid)
|| !_query.TryGetApproach(serverGuid, out InteractionApproach approach))
if (!_query.IsUseable(serverGuid))
{
reservation?.CancelBeforeDispatch();
return;
}
if (approach.IsCloseRange)
{
var pending = new PendingPostArrivalAction(
serverGuid,
approach.Target.LocalEntityId,
IsPickup: false,
DestinationContainerId: 0u,
Placement: 0,
PendingPlacementToken: 0u,
UseReservation: reservation,
ApproachToken: default);
bool started = _movement.BeginApproach(
approach,
token => _pendingPostArrival = pending with { ApproachToken = token });
if (!started && _pendingPostArrival?.ServerGuid == pending.ServerGuid)
CancelPendingApproach();
else if (!started)
reservation?.CancelBeforeDispatch();
return;
}
_movement.BeginApproach(approach);
SendUseNow(serverGuid, reservation);
}
@ -327,14 +302,12 @@ internal sealed class SelectionInteractionController
if (approach.IsCloseRange)
{
var pending = new PendingPostArrivalAction(
var pending = new PendingPostArrivalPickup(
itemGuid,
approach.Target.LocalEntityId,
IsPickup: true,
destinationContainerId,
placement,
pendingPlacementToken,
UseReservation: null,
ApproachToken: default);
bool started = _movement.BeginApproach(
approach,
@ -386,46 +359,33 @@ internal sealed class SelectionInteractionController
_pendingPostArrival = null;
if (!_query.IsCurrent(pending.ServerGuid, pending.LocalEntityId))
{
if (pending.IsPickup)
CancelPickupPresentation(
pending.ServerGuid,
pending.PendingPlacementToken);
else
pending.UseReservation?.CancelBeforeDispatch();
CancelPickupPresentation(
pending.ServerGuid,
pending.PendingPlacementToken);
return;
}
if (pending.IsPickup)
if (!IsCurrentPickupPresentation(
pending.ServerGuid,
pending.DestinationContainerId,
pending.Placement,
pending.PendingPlacementToken))
{
if (!IsCurrentPickupPresentation(
return;
}
if (!_items.TryDispatchInventoryRequest(
InventoryRequestKind.Pickup,
pending.ServerGuid,
() => _transport.TrySendPickup(
pending.ServerGuid,
pending.DestinationContainerId,
pending.Placement,
pending.PendingPlacementToken))
{
return;
}
if (!_items.TryDispatchInventoryRequest(
InventoryRequestKind.Pickup,
pending.ServerGuid,
() => _transport.TrySendPickup(
pending.ServerGuid,
pending.DestinationContainerId,
pending.Placement,
out _),
pending.PendingPlacementToken))
{
CancelPickupPresentation(
pending.ServerGuid,
pending.PendingPlacementToken);
}
}
else
out _),
pending.PendingPlacementToken))
{
if (_transport.TrySendUse(pending.ServerGuid, out _))
pending.UseReservation?.MarkDispatched();
else
pending.UseReservation?.CancelBeforeDispatch();
CancelPickupPresentation(
pending.ServerGuid,
pending.PendingPlacementToken);
}
}
@ -548,12 +508,9 @@ internal sealed class SelectionInteractionController
if (_pendingPostArrival is not { } pending)
return;
_pendingPostArrival = null;
if (pending.IsPickup)
CancelPickupPresentation(
pending.ServerGuid,
pending.PendingPlacementToken);
else
pending.UseReservation?.CancelBeforeDispatch();
CancelPickupPresentation(
pending.ServerGuid,
pending.PendingPlacementToken);
}
private void CancelPickupPresentation(uint itemGuid, ulong token = 0u)