Add VisibilityService to improve tick performance with many online pairs

This commit is contained in:
Loporrit
2025-05-14 12:01:29 +00:00
parent e603539319
commit fae8941dce
6 changed files with 73 additions and 11 deletions

View File

@@ -26,13 +26,14 @@ public class PairHandlerFactory
private readonly ServerConfigurationManager _serverConfigManager; private readonly ServerConfigurationManager _serverConfigManager;
private readonly PluginWarningNotificationService _pluginWarningNotificationManager; private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
private readonly PairAnalyzerFactory _pairAnalyzerFactory; private readonly PairAnalyzerFactory _pairAnalyzerFactory;
private readonly VisibilityService _visibilityService;
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager, public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService, FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService,
PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime, PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime,
FileCacheManager fileCacheManager, MareMediator mareMediator, PlayerPerformanceService playerPerformanceService, FileCacheManager fileCacheManager, MareMediator mareMediator, PlayerPerformanceService playerPerformanceService,
ServerConfigurationManager serverConfigManager, PairAnalyzerFactory pairAnalyzerFactory, ServerConfigurationManager serverConfigManager, PairAnalyzerFactory pairAnalyzerFactory,
MareConfigService configService) MareConfigService configService, VisibilityService visibilityService)
{ {
_loggerFactory = loggerFactory; _loggerFactory = loggerFactory;
_gameObjectHandlerFactory = gameObjectHandlerFactory; _gameObjectHandlerFactory = gameObjectHandlerFactory;
@@ -47,12 +48,13 @@ public class PairHandlerFactory
_serverConfigManager = serverConfigManager; _serverConfigManager = serverConfigManager;
_pairAnalyzerFactory = pairAnalyzerFactory; _pairAnalyzerFactory = pairAnalyzerFactory;
_configService = configService; _configService = configService;
_visibilityService = visibilityService;
} }
public PairHandler Create(Pair pair) public PairHandler Create(Pair pair)
{ {
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), pair, _pairAnalyzerFactory.Create(pair), _gameObjectHandlerFactory, return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), pair, _pairAnalyzerFactory.Create(pair), _gameObjectHandlerFactory,
_ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime, _ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime,
_fileCacheManager, _mareMediator, _playerPerformanceService, _serverConfigManager, _configService); _fileCacheManager, _mareMediator, _playerPerformanceService, _serverConfigManager, _configService, _visibilityService);
} }
} }

View File

@@ -33,6 +33,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
private readonly PlayerPerformanceService _playerPerformanceService; private readonly PlayerPerformanceService _playerPerformanceService;
private readonly ServerConfigurationManager _serverConfigManager; private readonly ServerConfigurationManager _serverConfigManager;
private readonly PluginWarningNotificationService _pluginWarningNotificationManager; private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
private readonly VisibilityService _visibilityService;
private CancellationTokenSource? _applicationCancellationTokenSource = new(); private CancellationTokenSource? _applicationCancellationTokenSource = new();
private Guid _applicationId; private Guid _applicationId;
private Task? _applicationTask; private Task? _applicationTask;
@@ -54,7 +55,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
FileCacheManager fileDbManager, MareMediator mediator, FileCacheManager fileDbManager, MareMediator mediator,
PlayerPerformanceService playerPerformanceService, PlayerPerformanceService playerPerformanceService,
ServerConfigurationManager serverConfigManager, ServerConfigurationManager serverConfigManager,
MareConfigService configService) : base(logger, mediator) MareConfigService configService, VisibilityService visibilityService) : base(logger, mediator)
{ {
Pair = pair; Pair = pair;
PairAnalyzer = pairAnalyzer; PairAnalyzer = pairAnalyzer;
@@ -68,8 +69,12 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
_playerPerformanceService = playerPerformanceService; _playerPerformanceService = playerPerformanceService;
_serverConfigManager = serverConfigManager; _serverConfigManager = serverConfigManager;
_configService = configService; _configService = configService;
_visibilityService = visibilityService;
_visibilityService.StartTracking(Pair.Ident);
Mediator.SubscribeKeyed<PlayerVisibilityMessage>(this, Pair.Ident, (msg) => UpdateVisibility(msg.IsVisible));
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
Mediator.Subscribe<ZoneSwitchStartMessage>(this, (_) => Mediator.Subscribe<ZoneSwitchStartMessage>(this, (_) =>
{ {
_downloadCancellationTokenSource?.CancelDispose(); _downloadCancellationTokenSource?.CancelDispose();
@@ -137,7 +142,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
} }
public long LastAppliedDataBytes { get; private set; } public long LastAppliedDataBytes { get; private set; }
public Pair Pair { get; private set; } public Pair Pair { get; private init; }
public PairAnalyzer PairAnalyzer { get; private init; } public PairAnalyzer PairAnalyzer { get; private init; }
public nint PlayerCharacter => _charaHandler?.Address ?? nint.Zero; public nint PlayerCharacter => _charaHandler?.Address ?? nint.Zero;
public unsafe uint PlayerCharacterId => (_charaHandler?.Address ?? nint.Zero) == nint.Zero public unsafe uint PlayerCharacterId => (_charaHandler?.Address ?? nint.Zero) == nint.Zero
@@ -255,6 +260,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
if (!disposing) return; if (!disposing) return;
_visibilityService.StopTracking(Pair.Ident);
SetUploading(isUploading: false); SetUploading(isUploading: false);
var name = PlayerName; var name = PlayerName;
Logger.LogDebug("Disposing {name} ({user})", name, Pair); Logger.LogDebug("Disposing {name} ({user})", name, Pair);
@@ -634,7 +641,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
} }
} }
private void FrameworkUpdate() private void UpdateVisibility(bool nowVisible)
{ {
if (string.IsNullOrEmpty(PlayerName)) if (string.IsNullOrEmpty(PlayerName))
{ {
@@ -647,7 +654,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
$"Initializing User For Character {pc.Name}"))); $"Initializing User For Character {pc.Name}")));
} }
if (_charaHandler?.Address != nint.Zero && !IsVisible) if (!IsVisible && nowVisible)
{ {
IsVisible = true; IsVisible = true;
Mediator.Publish(new PairHandlerVisibleMessage(this)); Mediator.Publish(new PairHandlerVisibleMessage(this));
@@ -666,10 +673,10 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
Logger.LogTrace("{this} visibility changed, now: {visi}, no cached data exists", this, IsVisible); Logger.LogTrace("{this} visibility changed, now: {visi}, no cached data exists", this, IsVisible);
} }
} }
else if (_charaHandler?.Address == nint.Zero && IsVisible) else if (IsVisible && !nowVisible)
{ {
IsVisible = false; IsVisible = false;
_charaHandler.Invalidate(); _charaHandler?.Invalidate();
_downloadCancellationTokenSource?.CancelDispose(); _downloadCancellationTokenSource?.CancelDispose();
_downloadCancellationTokenSource = null; _downloadCancellationTokenSource = null;
Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible); Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible);

View File

@@ -114,6 +114,7 @@ public sealed class Plugin : IDalamudPlugin
collection.AddSingleton<IpcProvider>(); collection.AddSingleton<IpcProvider>();
collection.AddSingleton<PluginWatcherService>(); collection.AddSingleton<PluginWatcherService>();
collection.AddSingleton<PlayerPerformanceService>(); collection.AddSingleton<PlayerPerformanceService>();
collection.AddSingleton<VisibilityService>();
collection.AddSingleton<EventAggregator>(); collection.AddSingleton<EventAggregator>();
collection.AddSingleton<DalamudUtilService>(); collection.AddSingleton<DalamudUtilService>();
collection.AddSingleton<DtrEntry>(); collection.AddSingleton<DtrEntry>();

View File

@@ -19,7 +19,7 @@ namespace MareSynchronos.Services;
public class DalamudUtilService : IHostedService, IMediatorSubscriber public class DalamudUtilService : IHostedService, IMediatorSubscriber
{ {
internal struct PlayerCharacter public struct PlayerCharacter
{ {
public uint ObjectId; public uint ObjectId;
public string Name; public string Name;
@@ -390,7 +390,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
return _gameGui.WorldToScreen(obj.Position, out var screenPos) ? screenPos : Vector2.Zero; return _gameGui.WorldToScreen(obj.Position, out var screenPos) ? screenPos : Vector2.Zero;
} }
internal PlayerCharacter FindPlayerByNameHash(string ident) public PlayerCharacter FindPlayerByNameHash(string ident)
{ {
_playerCharas.TryGetValue(ident, out var result); _playerCharas.TryGetValue(ident, out var result);
return result; return result;

View File

@@ -72,6 +72,7 @@ public record ProfilePopoutToggle(Pair? Pair) : MessageBase;
public record CompactUiChange(Vector2 Size, Vector2 Position) : MessageBase; public record CompactUiChange(Vector2 Size, Vector2 Position) : MessageBase;
public record ProfileOpenStandaloneMessage(Pair Pair) : MessageBase; public record ProfileOpenStandaloneMessage(Pair Pair) : MessageBase;
public record RemoveWindowMessage(WindowMediatorSubscriberBase Window) : MessageBase; public record RemoveWindowMessage(WindowMediatorSubscriberBase Window) : MessageBase;
public record PlayerVisibilityMessage(string Ident, bool IsVisible) : KeyedMessage(Ident, SameThread: true);
public record PairHandlerVisibleMessage(PairHandler Player) : MessageBase; public record PairHandlerVisibleMessage(PairHandler Player) : MessageBase;
public record OpenReportPopupMessage(Pair PairToReport) : MessageBase; public record OpenReportPopupMessage(Pair PairToReport) : MessageBase;
public record OpenBanUserPopupMessage(Pair PairToBan, GroupFullInfoDto GroupFullInfoDto) : MessageBase; public record OpenBanUserPopupMessage(Pair PairToBan, GroupFullInfoDto GroupFullInfoDto) : MessageBase;

View File

@@ -0,0 +1,51 @@
using MareSynchronos.Services.Mediator;
using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
namespace MareSynchronos.Services;
// Detect when players of interest are visible
public class VisibilityService : DisposableMediatorSubscriberBase
{
private readonly DalamudUtilService _dalamudUtil;
private ConcurrentDictionary<string, bool> _trackedPlayerVisibility = new();
private ConcurrentQueue<string> _removedPlayerMessageQueue = new();
public VisibilityService(ILogger<PairAnalyzer> logger, MareMediator mediator, DalamudUtilService dalamudUtil)
: base(logger, mediator)
{
_dalamudUtil = dalamudUtil;
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
}
public void StartTracking(string ident)
{
_trackedPlayerVisibility.TryAdd(ident, false);
}
public void StopTracking(string ident)
{
// No PairVisibilityMessage is emitted if the player was visible when removed
_trackedPlayerVisibility.TryRemove(ident, out _);
}
private void FrameworkUpdate()
{
foreach (var player in _trackedPlayerVisibility)
{
string ident = player.Key;
var findResult = _dalamudUtil.FindPlayerByNameHash(ident);
if (!player.Value && findResult.ObjectId != 0)
{
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: true, comparisonValue: false))
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: true));
}
else if (player.Value && findResult.ObjectId == 0)
{
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: false, comparisonValue: true))
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false));
}
}
}
}