fix(interaction): bind selection to live incarnations

Carry local WorldEntity identity through render hits, lighting pulses, and deferred movement actions so GUID reuse cannot target a replacement. Reset all session-owned selection and ItemHolder state and prevent combat auto-target during teardown.
This commit is contained in:
Erik 2026-07-21 07:07:58 +02:00
parent c271383714
commit 047a4c83b5
19 changed files with 374 additions and 42 deletions

View file

@ -964,6 +964,46 @@ public sealed class LiveEntityRuntime
out WorldEntity entity) =>
_visibleWorldEntitiesByGuid.TryGetValue(serverGuid, out entity!);
public bool TryGetInteractionEligibleRecord(
uint serverGuid,
out LiveEntityRecord record)
{
if (_recordsByGuid.TryGetValue(serverGuid, out LiveEntityRecord? found)
&& found.WorldEntity is { } entity
&& _visibleWorldEntitiesByGuid.TryGetValue(serverGuid, out WorldEntity? visible)
&& ReferenceEquals(entity, visible))
{
record = found;
return true;
}
record = null!;
return false;
}
/// <summary>
/// Resolves a render-published interaction candidate only while the same
/// logical WorldEntity incarnation still owns the server GUID. A stale
/// frame must never retarget a replacement which reused that GUID.
/// </summary>
public bool TryGetInteractionEligibleRecord(
uint serverGuid,
uint localEntityId,
out LiveEntityRecord record)
{
if (serverGuid != 0u
&& localEntityId != 0u
&& TryGetInteractionEligibleRecord(serverGuid, out LiveEntityRecord found)
&& found.WorldEntity!.Id == localEntityId)
{
record = found;
return true;
}
record = null!;
return false;
}
public bool ContainsWorldEntity(uint serverGuid) =>
_recordsByGuid.TryGetValue(serverGuid, out LiveEntityRecord? record)
&& record.WorldEntity is not null;