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 PluginWarningNotificationService _pluginWarningNotificationManager;
private readonly PairAnalyzerFactory _pairAnalyzerFactory;
private readonly VisibilityService _visibilityService;
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService,
PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime,
FileCacheManager fileCacheManager, MareMediator mareMediator, PlayerPerformanceService playerPerformanceService,
ServerConfigurationManager serverConfigManager, PairAnalyzerFactory pairAnalyzerFactory,
MareConfigService configService)
MareConfigService configService, VisibilityService visibilityService)
{
_loggerFactory = loggerFactory;
_gameObjectHandlerFactory = gameObjectHandlerFactory;
@@ -47,12 +48,13 @@ public class PairHandlerFactory
_serverConfigManager = serverConfigManager;
_pairAnalyzerFactory = pairAnalyzerFactory;
_configService = configService;
_visibilityService = visibilityService;
}
public PairHandler Create(Pair pair)
{
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), pair, _pairAnalyzerFactory.Create(pair), _gameObjectHandlerFactory,
_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 ServerConfigurationManager _serverConfigManager;
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
private readonly VisibilityService _visibilityService;
private CancellationTokenSource? _applicationCancellationTokenSource = new();
private Guid _applicationId;
private Task? _applicationTask;
@@ -54,7 +55,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
FileCacheManager fileDbManager, MareMediator mediator,
PlayerPerformanceService playerPerformanceService,
ServerConfigurationManager serverConfigManager,
MareConfigService configService) : base(logger, mediator)
MareConfigService configService, VisibilityService visibilityService) : base(logger, mediator)
{
Pair = pair;
PairAnalyzer = pairAnalyzer;
@@ -68,8 +69,12 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
_playerPerformanceService = playerPerformanceService;
_serverConfigManager = serverConfigManager;
_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, (_) =>
{
_downloadCancellationTokenSource?.CancelDispose();
@@ -137,7 +142,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
}
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 nint PlayerCharacter => _charaHandler?.Address ?? nint.Zero;
public unsafe uint PlayerCharacterId => (_charaHandler?.Address ?? nint.Zero) == nint.Zero
@@ -255,6 +260,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
if (!disposing) return;
_visibilityService.StopTracking(Pair.Ident);
SetUploading(isUploading: false);
var name = PlayerName;
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))
{
@@ -647,7 +654,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
$"Initializing User For Character {pc.Name}")));
}
if (_charaHandler?.Address != nint.Zero && !IsVisible)
if (!IsVisible && nowVisible)
{
IsVisible = true;
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);
}
}
else if (_charaHandler?.Address == nint.Zero && IsVisible)
else if (IsVisible && !nowVisible)
{
IsVisible = false;
_charaHandler.Invalidate();
_charaHandler?.Invalidate();
_downloadCancellationTokenSource?.CancelDispose();
_downloadCancellationTokenSource = null;
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<PluginWatcherService>();
collection.AddSingleton<PlayerPerformanceService>();
collection.AddSingleton<VisibilityService>();
collection.AddSingleton<EventAggregator>();
collection.AddSingleton<DalamudUtilService>();
collection.AddSingleton<DtrEntry>();

View File

@@ -19,7 +19,7 @@ namespace MareSynchronos.Services;
public class DalamudUtilService : IHostedService, IMediatorSubscriber
{
internal struct PlayerCharacter
public struct PlayerCharacter
{
public uint ObjectId;
public string Name;
@@ -390,7 +390,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
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);
return result;

View File

@@ -72,6 +72,7 @@ public record ProfilePopoutToggle(Pair? Pair) : MessageBase;
public record CompactUiChange(Vector2 Size, Vector2 Position) : MessageBase;
public record ProfileOpenStandaloneMessage(Pair Pair) : 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 OpenReportPopupMessage(Pair PairToReport) : 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));
}
}
}
}