Added seperate collections for other states, moved clean up of penumbra collection out of config. Safe read of ptr on process, fixed notfications on popup and notifications with flags.
This commit is contained in:
@@ -14,6 +14,7 @@ using BattleNpcSubKind = FFXIVClientStructs.FFXIV.Client.Game.Object.BattleNpcSu
|
||||
using IPlayerCharacter = Dalamud.Game.ClientState.Objects.SubKinds.IPlayerCharacter;
|
||||
using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
||||
using LightlessObjectKind = LightlessSync.API.Data.Enum.ObjectKind;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LightlessSync.Services.ActorTracking;
|
||||
|
||||
@@ -57,6 +58,8 @@ public sealed class ActorObjectService : IHostedService, IDisposable, IMediatorS
|
||||
private bool _hooksActive;
|
||||
private static readonly TimeSpan SnapshotRefreshInterval = TimeSpan.FromSeconds(1);
|
||||
private DateTime _nextRefreshAllowed = DateTime.MinValue;
|
||||
private int _warmStartQueued;
|
||||
private int _warmStartRan;
|
||||
|
||||
public ActorObjectService(
|
||||
ILogger<ActorObjectService> logger,
|
||||
@@ -74,7 +77,21 @@ public sealed class ActorObjectService : IHostedService, IDisposable, IMediatorS
|
||||
_clientState = clientState;
|
||||
_condition = condition;
|
||||
_mediator = mediator;
|
||||
_mediator.Subscribe<PenumbraInitializedMessage>(this, _ =>
|
||||
{
|
||||
QueueWarmStart("PenumbraInitialized");
|
||||
});
|
||||
|
||||
_mediator.Subscribe<ConnectedMessage>(this, _ =>
|
||||
{
|
||||
QueueWarmStart("Connected");
|
||||
});
|
||||
|
||||
// Optional: helps after zoning
|
||||
_mediator.Subscribe<ZoneSwitchEndMessage>(this, _ =>
|
||||
{
|
||||
QueueWarmStart("ZoneSwitchEnd");
|
||||
});
|
||||
_mediator.Subscribe<GameObjectHandlerCreatedMessage>(this, (msg) =>
|
||||
{
|
||||
if (!msg.OwnedObject) return;
|
||||
@@ -96,7 +113,6 @@ public sealed class ActorObjectService : IHostedService, IDisposable, IMediatorS
|
||||
}
|
||||
|
||||
private bool IsZoning => _condition[ConditionFlag.BetweenAreas] || _condition[ConditionFlag.BetweenAreas51];
|
||||
|
||||
private ActorSnapshot Snapshot => Volatile.Read(ref _snapshot);
|
||||
private GposeSnapshot CurrentGposeSnapshot => Volatile.Read(ref _gposeSnapshot);
|
||||
|
||||
@@ -341,6 +357,8 @@ public sealed class ActorObjectService : IHostedService, IDisposable, IMediatorS
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_warmStartRan = 0;
|
||||
|
||||
DisposeHooks();
|
||||
_activePlayers.Clear();
|
||||
_gposePlayers.Clear();
|
||||
@@ -1147,6 +1165,57 @@ public sealed class ActorObjectService : IHostedService, IDisposable, IMediatorS
|
||||
PublishGposeSnapshot();
|
||||
}
|
||||
|
||||
private void QueueWarmStart(string reason)
|
||||
{
|
||||
if (Interlocked.Exchange(ref _warmStartQueued, 1) == 1)
|
||||
return;
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Interlocked.Exchange(ref _warmStartRan, 1) == 1)
|
||||
return;
|
||||
|
||||
await Task.Delay(500).ConfigureAwait(false);
|
||||
|
||||
if (IsZoning)
|
||||
return;
|
||||
|
||||
await _framework.RunOnFrameworkThread(() =>
|
||||
{
|
||||
RefreshTrackedActorsInternal();
|
||||
|
||||
var snapshot = Snapshot;
|
||||
|
||||
var published = new HashSet<nint>();
|
||||
|
||||
foreach (var d in snapshot.PlayerDescriptors)
|
||||
{
|
||||
if (d.Address != nint.Zero && published.Add(d.Address))
|
||||
_mediator.Publish(new ActorTrackedMessage(d));
|
||||
}
|
||||
|
||||
foreach (var d in snapshot.OwnedDescriptors)
|
||||
{
|
||||
if (d.Address != nint.Zero && published.Add(d.Address))
|
||||
_mediator.Publish(new ActorTrackedMessage(d));
|
||||
}
|
||||
|
||||
_logger.LogDebug("WarmStart republished {count} actors ({reason})", published.Count, reason);
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogDebug(ex, "WarmStart failed ({reason})", reason);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interlocked.Exchange(ref _warmStartQueued, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private unsafe void TrackGposeObject(GameObject* gameObject)
|
||||
{
|
||||
if (gameObject == null)
|
||||
@@ -1240,6 +1309,7 @@ public sealed class ActorObjectService : IHostedService, IDisposable, IMediatorS
|
||||
return true;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Auto)]
|
||||
private readonly record struct LoadState(bool IsValid, bool IsLoaded)
|
||||
{
|
||||
public static LoadState Invalid => new(false, false);
|
||||
|
||||
Reference in New Issue
Block a user