Implement IpcCallerMare and hand Mare control of any handled pairs
This commit is contained in:
44
MareSynchronos/Interop/Ipc/IpcCallerMare.cs
Normal file
44
MareSynchronos/Interop/Ipc/IpcCallerMare.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Dalamud.Plugin;
|
||||||
|
using Dalamud.Plugin.Ipc;
|
||||||
|
using MareSynchronos.Services;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MareSynchronos.Interop.Ipc;
|
||||||
|
|
||||||
|
public sealed class IpcCallerMare : DisposableMediatorSubscriberBase
|
||||||
|
{
|
||||||
|
private readonly ICallGateSubscriber<List<nint>> _mareHandledGameAddresses;
|
||||||
|
private readonly List<nint> _emptyList = [];
|
||||||
|
|
||||||
|
private bool _pluginLoaded;
|
||||||
|
|
||||||
|
public IpcCallerMare(ILogger<IpcCallerMare> logger, IDalamudPluginInterface pi, MareMediator mediator) : base(logger, mediator)
|
||||||
|
{
|
||||||
|
_mareHandledGameAddresses = pi.GetIpcSubscriber<List<nint>>("MareSynchronos.GetHandledAddresses");
|
||||||
|
|
||||||
|
_pluginLoaded = PluginWatcherService.GetInitialPluginState(pi, "MareSynchronos")?.IsLoaded ?? false;
|
||||||
|
|
||||||
|
Mediator.SubscribeKeyed<PluginChangeMessage>(this, "MareSynchronos", (msg) =>
|
||||||
|
{
|
||||||
|
_pluginLoaded = msg.IsLoaded;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool APIAvailable { get; private set; } = false;
|
||||||
|
|
||||||
|
// Must be called on framework thread
|
||||||
|
public IReadOnlyList<nint> GetHandledGameAddresses()
|
||||||
|
{
|
||||||
|
if (!_pluginLoaded) return _emptyList;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _mareHandledGameAddresses.InvokeFunc();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return _emptyList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,7 +74,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
_visibilityService.StartTracking(Pair.Ident);
|
_visibilityService.StartTracking(Pair.Ident);
|
||||||
|
|
||||||
Mediator.SubscribeKeyed<PlayerVisibilityMessage>(this, Pair.Ident, (msg) => UpdateVisibility(msg.IsVisible));
|
Mediator.SubscribeKeyed<PlayerVisibilityMessage>(this, Pair.Ident, (msg) => UpdateVisibility(msg.IsVisible, msg.Invalidate));
|
||||||
|
|
||||||
Mediator.Subscribe<ZoneSwitchStartMessage>(this, (_) =>
|
Mediator.Subscribe<ZoneSwitchStartMessage>(this, (_) =>
|
||||||
{
|
{
|
||||||
@@ -694,7 +694,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateVisibility(bool nowVisible)
|
private void UpdateVisibility(bool nowVisible, bool invalidate = false)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(PlayerName))
|
if (string.IsNullOrEmpty(PlayerName))
|
||||||
{
|
{
|
||||||
@@ -707,6 +707,22 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
$"Initializing User For Character {pc.Name}")));
|
$"Initializing User For Character {pc.Name}")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This was triggered by the character becoming handled by Mare, so unapply everything
|
||||||
|
// There seems to be a good chance that this races Mare and then crashes
|
||||||
|
if (!nowVisible && invalidate)
|
||||||
|
{
|
||||||
|
bool wasVisible = IsVisible;
|
||||||
|
IsVisible = false;
|
||||||
|
_charaHandler?.Invalidate();
|
||||||
|
_downloadCancellationTokenSource?.CancelDispose();
|
||||||
|
_downloadCancellationTokenSource = null;
|
||||||
|
if (wasVisible)
|
||||||
|
Logger.LogTrace("{this} visibility changed, now: {visi}", this, IsVisible);
|
||||||
|
Logger.LogDebug("Invalidating {this}", this);
|
||||||
|
UndoApplication();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!IsVisible && nowVisible)
|
if (!IsVisible && nowVisible)
|
||||||
{
|
{
|
||||||
// This is deferred application attempt, avoid any log output
|
// This is deferred application attempt, avoid any log output
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton<IpcCallerMoodles>();
|
collection.AddSingleton<IpcCallerMoodles>();
|
||||||
collection.AddSingleton<IpcCallerPetNames>();
|
collection.AddSingleton<IpcCallerPetNames>();
|
||||||
collection.AddSingleton<IpcCallerBrio>();
|
collection.AddSingleton<IpcCallerBrio>();
|
||||||
|
collection.AddSingleton<IpcCallerMare>();
|
||||||
collection.AddSingleton<IpcManager>();
|
collection.AddSingleton<IpcManager>();
|
||||||
collection.AddSingleton<NotificationService>();
|
collection.AddSingleton<NotificationService>();
|
||||||
collection.AddSingleton<NoSnapService>();
|
collection.AddSingleton<NoSnapService>();
|
||||||
|
|||||||
@@ -73,7 +73,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 PlayerVisibilityMessage(string Ident, bool IsVisible, bool Invalidate = false) : 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;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using MareSynchronos.Interop.Ipc;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@@ -7,19 +8,32 @@ namespace MareSynchronos.Services;
|
|||||||
// Detect when players of interest are visible
|
// Detect when players of interest are visible
|
||||||
public class VisibilityService : DisposableMediatorSubscriberBase
|
public class VisibilityService : DisposableMediatorSubscriberBase
|
||||||
{
|
{
|
||||||
private readonly DalamudUtilService _dalamudUtil;
|
private enum TrackedPlayerStatus
|
||||||
private readonly ConcurrentDictionary<string, bool> _trackedPlayerVisibility = new(StringComparer.Ordinal);
|
{
|
||||||
|
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)
|
: base(logger, mediator)
|
||||||
{
|
{
|
||||||
|
_mare = mare;
|
||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
|
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartTracking(string ident)
|
public void StartTracking(string ident)
|
||||||
{
|
{
|
||||||
_trackedPlayerVisibility.TryAdd(ident, value: false);
|
_trackedPlayerVisibility.TryAdd(ident, TrackedPlayerStatus.NotVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopTracking(string ident)
|
public void StopTracking(string ident)
|
||||||
@@ -30,21 +44,62 @@ public class VisibilityService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
private void FrameworkUpdate()
|
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)
|
foreach (var player in _trackedPlayerVisibility)
|
||||||
{
|
{
|
||||||
string ident = player.Key;
|
string ident = player.Key;
|
||||||
var findResult = _dalamudUtil.FindPlayerByNameHash(ident);
|
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))
|
if (_makeVisibleNextFrame.Contains(ident))
|
||||||
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: true));
|
{
|
||||||
|
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))
|
// Send a technically redundant visibility update with the added intent of triggering PairHandler to undo the application by name
|
||||||
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false));
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user