Implement IpcCallerMare and hand Mare control of any handled pairs

This commit is contained in:
Loporrit
2025-08-09 15:05:31 +00:00
parent e1d54aea43
commit 6bf60fe729
5 changed files with 129 additions and 13 deletions

View File

@@ -73,7 +73,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 PlayerVisibilityMessage(string Ident, bool IsVisible, bool Invalidate = false) : 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

@@ -1,3 +1,4 @@
using MareSynchronos.Interop.Ipc;
using MareSynchronos.Services.Mediator;
using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
@@ -7,19 +8,32 @@ namespace MareSynchronos.Services;
// Detect when players of interest are visible
public class VisibilityService : DisposableMediatorSubscriberBase
{
private readonly DalamudUtilService _dalamudUtil;
private readonly ConcurrentDictionary<string, bool> _trackedPlayerVisibility = new(StringComparer.Ordinal);
private enum TrackedPlayerStatus
{
NotVisible,
Visible,
MareHandled
};
public VisibilityService(ILogger<PairAnalyzer> logger, MareMediator mediator, DalamudUtilService dalamudUtil)
private readonly DalamudUtilService _dalamudUtil;
private readonly ConcurrentDictionary<string, TrackedPlayerStatus> _trackedPlayerVisibility = new(StringComparer.Ordinal);
private readonly List<string> _makeVisibleNextFrame = new();
private readonly IpcCallerMare _mare;
private readonly HashSet<nint> cachedMareAddresses = new();
private uint _cachedAddressSum = 0;
private uint _cachedAddressSumDebounce = 1;
public VisibilityService(ILogger<VisibilityService> logger, MareMediator mediator, IpcCallerMare mare, DalamudUtilService dalamudUtil)
: base(logger, mediator)
{
_mare = mare;
_dalamudUtil = dalamudUtil;
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
}
public void StartTracking(string ident)
{
_trackedPlayerVisibility.TryAdd(ident, value: false);
_trackedPlayerVisibility.TryAdd(ident, TrackedPlayerStatus.NotVisible);
}
public void StopTracking(string ident)
@@ -30,21 +44,62 @@ public class VisibilityService : DisposableMediatorSubscriberBase
private void FrameworkUpdate()
{
var mareHandledAddresses = _mare.GetHandledGameAddresses();
uint addressSum = 0;
foreach (var addr in mareHandledAddresses)
addressSum ^= (uint)addr.GetHashCode();
if (addressSum != _cachedAddressSum)
{
if (addressSum == _cachedAddressSumDebounce)
{
cachedMareAddresses.Clear();
foreach (var addr in mareHandledAddresses)
cachedMareAddresses.Add(addr);
_cachedAddressSum = addressSum;
}
else
{
_cachedAddressSumDebounce = addressSum;
}
}
foreach (var player in _trackedPlayerVisibility)
{
string ident = player.Key;
var findResult = _dalamudUtil.FindPlayerByNameHash(ident);
var isMareHandled = cachedMareAddresses.Contains(findResult.Address);
var isVisible = findResult.ObjectId != 0 && !isMareHandled;
if (!player.Value && findResult.ObjectId != 0)
if (player.Value == TrackedPlayerStatus.MareHandled && !isMareHandled)
_trackedPlayerVisibility.TryUpdate(ident, newValue: TrackedPlayerStatus.NotVisible, comparisonValue: TrackedPlayerStatus.MareHandled);
if (player.Value == TrackedPlayerStatus.NotVisible && isVisible)
{
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: true, comparisonValue: false))
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: true));
if (_makeVisibleNextFrame.Contains(ident))
{
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: TrackedPlayerStatus.Visible, comparisonValue: TrackedPlayerStatus.NotVisible))
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: true));
}
else
_makeVisibleNextFrame.Add(ident);
}
else if (player.Value && findResult.ObjectId == 0)
else if (player.Value == TrackedPlayerStatus.NotVisible && isMareHandled)
{
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: false, comparisonValue: true))
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false));
// Send a technically redundant visibility update with the added intent of triggering PairHandler to undo the application by name
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: TrackedPlayerStatus.MareHandled, comparisonValue: TrackedPlayerStatus.NotVisible))
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false, Invalidate: true));
}
else if (player.Value == TrackedPlayerStatus.Visible && !isVisible)
{
var newTrackedStatus = isMareHandled ? TrackedPlayerStatus.MareHandled : TrackedPlayerStatus.NotVisible;
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: newTrackedStatus, comparisonValue: TrackedPlayerStatus.Visible))
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false, Invalidate: isMareHandled));
}
if (!isVisible)
_makeVisibleNextFrame.Remove(ident);
}
}
}