From b6b9c81a5758681f47234eb9ead2f29ad7a8948f Mon Sep 17 00:00:00 2001 From: azyges Date: Sat, 3 Jan 2026 13:53:55 +0900 Subject: [PATCH] tighten the check --- .../ActorTracking/ActorObjectService.cs | 70 ++++++++++++++++--- 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/LightlessSync/Services/ActorTracking/ActorObjectService.cs b/LightlessSync/Services/ActorTracking/ActorObjectService.cs index cf22e66..e443496 100644 --- a/LightlessSync/Services/ActorTracking/ActorObjectService.cs +++ b/LightlessSync/Services/ActorTracking/ActorObjectService.cs @@ -6,6 +6,7 @@ using FFXIVClientStructs.Interop; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using LightlessSync.PlayerData.Handlers; using LightlessSync.Services.Mediator; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -16,7 +17,7 @@ using LightlessObjectKind = LightlessSync.API.Data.Enum.ObjectKind; namespace LightlessSync.Services.ActorTracking; -public sealed class ActorObjectService : IHostedService, IDisposable +public sealed class ActorObjectService : IHostedService, IDisposable, IMediatorSubscriber { public readonly record struct ActorDescriptor( string Name, @@ -36,6 +37,8 @@ public sealed class ActorObjectService : IHostedService, IDisposable private readonly IClientState _clientState; private readonly ICondition _condition; private readonly LightlessMediator _mediator; + private readonly object _playerRelatedHandlerLock = new(); + private readonly HashSet _playerRelatedHandlers = []; private readonly ConcurrentDictionary _activePlayers = new(); private readonly ConcurrentDictionary _gposePlayers = new(); @@ -71,6 +74,25 @@ public sealed class ActorObjectService : IHostedService, IDisposable _clientState = clientState; _condition = condition; _mediator = mediator; + + _mediator.Subscribe(this, (msg) => + { + if (!msg.OwnedObject) return; + lock (_playerRelatedHandlerLock) + { + _playerRelatedHandlers.Add(msg.GameObjectHandler); + } + RefreshTrackedActors(force: true); + }); + _mediator.Subscribe(this, (msg) => + { + if (!msg.OwnedObject) return; + lock (_playerRelatedHandlerLock) + { + _playerRelatedHandlers.Remove(msg.GameObjectHandler); + } + RefreshTrackedActors(force: true); + }); } private bool IsZoning => _condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51]; @@ -84,6 +106,7 @@ public sealed class ActorObjectService : IHostedService, IDisposable public IReadOnlyList PlayerDescriptors => Snapshot.PlayerDescriptors; public IReadOnlyList OwnedDescriptors => Snapshot.OwnedDescriptors; public IReadOnlyList GposeDescriptors => CurrentGposeSnapshot.GposeDescriptors; + public LightlessMediator Mediator => _mediator; public bool TryGetActorByHash(string hash, out ActorDescriptor descriptor) => _actorsByHash.TryGetValue(hash, out descriptor); public bool TryGetValidatedActorByHash(string hash, out ActorDescriptor descriptor) @@ -324,6 +347,11 @@ public sealed class ActorObjectService : IHostedService, IDisposable _actorsByHash.Clear(); _actorsByName.Clear(); _pendingHashResolutions.Clear(); + _mediator.UnsubscribeAll(this); + lock (_playerRelatedHandlerLock) + { + _playerRelatedHandlers.Clear(); + } Volatile.Write(ref _snapshot, ActorSnapshot.Empty); Volatile.Write(ref _gposeSnapshot, GposeSnapshot.Empty); return Task.CompletedTask; @@ -500,7 +528,9 @@ public sealed class ActorObjectService : IHostedService, IDisposable if (objectKind is DalamudObjectKind.MountType or DalamudObjectKind.Companion) { var expectedMinionOrMount = GetMinionOrMountAddress(localPlayerAddress, localEntityId); - if (expectedMinionOrMount != nint.Zero && (nint)gameObject == expectedMinionOrMount) + if (expectedMinionOrMount != nint.Zero + && (nint)gameObject == expectedMinionOrMount + && IsPlayerRelatedOwnedAddress(expectedMinionOrMount, LightlessObjectKind.MinionOrMount)) { var resolvedOwner = ownerId != 0 ? ownerId : localEntityId; return (LightlessObjectKind.MinionOrMount, resolvedOwner); @@ -514,16 +544,37 @@ public sealed class ActorObjectService : IHostedService, IDisposable return (null, ownerId); var expectedPet = GetPetAddress(localPlayerAddress, localEntityId); - if (expectedPet != nint.Zero && (nint)gameObject == expectedPet) + if (expectedPet != nint.Zero + && (nint)gameObject == expectedPet + && IsPlayerRelatedOwnedAddress(expectedPet, LightlessObjectKind.Pet)) return (LightlessObjectKind.Pet, ownerId); var expectedCompanion = GetCompanionAddress(localPlayerAddress, localEntityId); - if (expectedCompanion != nint.Zero && (nint)gameObject == expectedCompanion) + if (expectedCompanion != nint.Zero + && (nint)gameObject == expectedCompanion + && IsPlayerRelatedOwnedAddress(expectedCompanion, LightlessObjectKind.Companion)) return (LightlessObjectKind.Companion, ownerId); return (null, ownerId); } + private bool IsPlayerRelatedOwnedAddress(nint address, LightlessObjectKind expectedKind) + { + if (address == nint.Zero) + return false; + + lock (_playerRelatedHandlerLock) + { + foreach (var handler in _playerRelatedHandlers) + { + if (handler.Address == address && handler.ObjectKind == expectedKind) + return true; + } + } + + return false; + } + private unsafe nint GetMinionOrMountAddress(nint localPlayerAddress, uint ownerEntityId) { if (localPlayerAddress == nint.Zero) @@ -531,20 +582,20 @@ public sealed class ActorObjectService : IHostedService, IDisposable var playerObject = (GameObject*)localPlayerAddress; var candidateAddress = _objectTable.GetObjectAddress(playerObject->ObjectIndex + 1); + if (ownerEntityId == 0) + return nint.Zero; + if (candidateAddress != nint.Zero) { var candidate = (GameObject*)candidateAddress; var candidateKind = (DalamudObjectKind)candidate->ObjectKind; if (candidateKind is DalamudObjectKind.MountType or DalamudObjectKind.Companion) { - if (ownerEntityId == 0 || ResolveOwnerId(candidate) == ownerEntityId) + if (ResolveOwnerId(candidate) == ownerEntityId) return candidateAddress; } } - if (ownerEntityId == 0) - return candidateAddress; - foreach (var obj in _objectTable) { if (obj is null || obj.Address == nint.Zero || obj.Address == localPlayerAddress) @@ -558,7 +609,7 @@ public sealed class ActorObjectService : IHostedService, IDisposable return obj.Address; } - return candidateAddress; + return nint.Zero; } private unsafe nint GetPetAddress(nint localPlayerAddress, uint ownerEntityId) @@ -1029,6 +1080,7 @@ public sealed class ActorObjectService : IHostedService, IDisposable public void Dispose() { DisposeHooks(); + _mediator.UnsubscribeAll(this); GC.SuppressFinalize(this); }