Rename IpcCallerMare, implement checking for Lightless/Snowcloak.

- Do not sync with pairs handled by Lightless/Snowcloak.
- Easy to add extra clients into this list.
This commit is contained in:
2025-09-11 15:28:04 +01:00
parent 74c83abe91
commit 6b7f9a6ea0
7 changed files with 103 additions and 210 deletions

View File

@@ -1,44 +0,0 @@
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;
}
}
}

View File

@@ -0,0 +1,83 @@
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 IpcCallerOtherSync : DisposableMediatorSubscriberBase
{
private readonly ICallGateSubscriber<List<nint>> _lightlessHandledGameAddresses;
private readonly ICallGateSubscriber<List<nint>> _snowcloakHandledGameAddresses;
private readonly List<nint> _emptyList = [];
private bool _lightlessLoaded;
private bool _snowcloakLoaded;
public IpcCallerOtherSync(ILogger<IpcCallerOtherSync> logger, IDalamudPluginInterface pi, MareMediator mediator) : base(logger, mediator)
{
_lightlessHandledGameAddresses = pi.GetIpcSubscriber<List<nint>>("LightlessSync.GetHandledAddresses");
_snowcloakHandledGameAddresses = pi.GetIpcSubscriber<List<nint>>("SnowcloakSync.GetHandledAddresses");
_lightlessLoaded = PluginWatcherService.GetInitialPluginState(pi, "LightlessSync")?.IsLoaded ?? false;
Mediator.SubscribeKeyed<PluginChangeMessage>(this, "LightlessSync", (msg) =>
{
_lightlessLoaded = msg.IsLoaded;
});
_snowcloakLoaded = PluginWatcherService.GetInitialPluginState(pi, "SnowcloakSync")?.IsLoaded ?? false;
Mediator.SubscribeKeyed<PluginChangeMessage>(this, "SnowcloakSync", (msg) =>
{
_snowcloakLoaded = msg.IsLoaded;
});
}
public bool APIAvailable { get; private set; } = false;
// Must be called on framework thread
public IReadOnlyList<nint> GetHandledGameAddresses()
{
if (!_lightlessLoaded && !_snowcloakLoaded) return _emptyList;
try
{
return GetLightlessHandledGameAddresses().Concat(GetSnowcloakHandledGameAddresses()).ToList();
}
catch
{
return _emptyList;
}
}
private List<nint> GetLightlessHandledGameAddresses()
{
if (!_lightlessLoaded) return _emptyList;
try
{
return _lightlessHandledGameAddresses.InvokeFunc();
}
catch
{
return _emptyList;
}
}
private List<nint> GetSnowcloakHandledGameAddresses()
{
if (!_snowcloakLoaded) return _emptyList;
try
{
return _snowcloakHandledGameAddresses.InvokeFunc();
}
catch
{
return _emptyList;
}
}
}

View File

@@ -5,7 +5,6 @@ using MareSynchronos.MareConfiguration;
using MareSynchronos.PlayerData.Handlers; using MareSynchronos.PlayerData.Handlers;
using MareSynchronos.Services; using MareSynchronos.Services;
using MareSynchronos.Services.Mediator; using MareSynchronos.Services.Mediator;
using MareSynchronos.Utils;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -15,25 +14,12 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
{ {
private readonly ILogger<IpcProvider> _logger; private readonly ILogger<IpcProvider> _logger;
private readonly IDalamudPluginInterface _pi; private readonly IDalamudPluginInterface _pi;
private readonly MareConfigService _mareConfig;
private readonly CharaDataManager _charaDataManager; private readonly CharaDataManager _charaDataManager;
private ICallGateProvider<string, IGameObject, bool>? _loadFileProvider; private ICallGateProvider<string, IGameObject, bool>? _loadFileProvider;
private ICallGateProvider<string, IGameObject, Task<bool>>? _loadFileAsyncProvider; private ICallGateProvider<string, IGameObject, Task<bool>>? _loadFileAsyncProvider;
private ICallGateProvider<List<nint>>? _handledGameAddresses; private ICallGateProvider<List<nint>>? _handledGameAddresses;
private readonly List<GameObjectHandler> _activeGameObjectHandlers = []; private readonly List<GameObjectHandler> _activeGameObjectHandlers = [];
private ICallGateProvider<string, IGameObject, bool>? _loadFileProviderMare;
private ICallGateProvider<string, IGameObject, Task<bool>>? _loadFileAsyncProviderMare;
private ICallGateProvider<List<nint>>? _handledGameAddressesMare;
private bool _marePluginEnabled = false;
private bool _impersonating = false;
private DateTime _unregisterTime = DateTime.UtcNow;
private CancellationTokenSource _registerDelayCts = new();
public bool MarePluginEnabled => _marePluginEnabled;
public bool ImpersonationActive => _impersonating;
public MareMediator Mediator { get; init; } public MareMediator Mediator { get; init; }
public IpcProvider(ILogger<IpcProvider> logger, IDalamudPluginInterface pi, MareConfigService mareConfig, public IpcProvider(ILogger<IpcProvider> logger, IDalamudPluginInterface pi, MareConfigService mareConfig,
@@ -41,7 +27,6 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
{ {
_logger = logger; _logger = logger;
_pi = pi; _pi = pi;
_mareConfig = mareConfig;
_charaDataManager = charaDataManager; _charaDataManager = charaDataManager;
Mediator = mareMediator; Mediator = mareMediator;
@@ -55,15 +40,6 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
if (msg.OwnedObject) return; if (msg.OwnedObject) return;
_activeGameObjectHandlers.Remove(msg.GameObjectHandler); _activeGameObjectHandlers.Remove(msg.GameObjectHandler);
}); });
/*
_marePluginEnabled = PluginWatcherService.GetInitialPluginState(pi, "MareSynchronos")?.IsLoaded ?? false;
Mediator.SubscribeKeyed<PluginChangeMessage>(this, "MareSynchronos", p => {
_marePluginEnabled = p.IsLoaded;
HandleMareImpersonation(automatic: true);
});
*/
_marePluginEnabled = true;
} }
public Task StartAsync(CancellationToken cancellationToken) public Task StartAsync(CancellationToken cancellationToken)
@@ -76,72 +52,10 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
_handledGameAddresses = _pi.GetIpcProvider<List<nint>>("ClubPenguinSync.GetHandledAddresses"); _handledGameAddresses = _pi.GetIpcProvider<List<nint>>("ClubPenguinSync.GetHandledAddresses");
_handledGameAddresses.RegisterFunc(GetHandledAddresses); _handledGameAddresses.RegisterFunc(GetHandledAddresses);
_loadFileProviderMare = _pi.GetIpcProvider<string, IGameObject, bool>("MareSynchronos.LoadMcdf");
_loadFileAsyncProviderMare = _pi.GetIpcProvider<string, IGameObject, Task<bool>>("MareSynchronos.LoadMcdfAsync");
_handledGameAddressesMare = _pi.GetIpcProvider<List<nint>>("MareSynchronos.GetHandledAddresses");
HandleMareImpersonation(automatic: true);
_logger.LogInformation("Started IpcProviderService"); _logger.LogInformation("Started IpcProviderService");
return Task.CompletedTask; return Task.CompletedTask;
} }
public void HandleMareImpersonation(bool automatic = false)
{
if (_marePluginEnabled)
{
if (_impersonating)
{
_loadFileProviderMare?.UnregisterFunc();
_loadFileAsyncProviderMare?.UnregisterFunc();
_handledGameAddressesMare?.UnregisterFunc();
_impersonating = false;
_unregisterTime = DateTime.UtcNow;
_logger.LogDebug("Unregistered MareSynchronos API");
}
}
else
{
if (_mareConfig.Current.MareAPI)
{
var cancelToken = _registerDelayCts.Token;
_ = Task.Run(async () =>
{
// Wait before registering to reduce the chance of a race condition
if (automatic)
await Task.Delay(5000).ConfigureAwait(false);
if (cancelToken.IsCancellationRequested)
return;
if (_marePluginEnabled)
{
_logger.LogDebug("Not registering MareSynchronos API: Mare plugin is loaded");
return;
}
_loadFileProviderMare?.RegisterFunc(LoadMcdf);
_loadFileAsyncProviderMare?.RegisterFunc(LoadMcdfAsync);
_handledGameAddressesMare?.RegisterFunc(MareGetHandledAddresses);
_impersonating = true;
_logger.LogDebug("Registered MareSynchronos API");
}, cancelToken);
}
else
{
_registerDelayCts = _registerDelayCts.CancelRecreate();
if (_impersonating)
{
_loadFileProviderMare?.UnregisterFunc();
_loadFileAsyncProviderMare?.UnregisterFunc();
_handledGameAddressesMare?.UnregisterFunc();
_impersonating = false;
_unregisterTime = DateTime.UtcNow;
_logger.LogDebug("Unregistered MareSynchronos API");
}
}
}
}
public Task StopAsync(CancellationToken cancellationToken) public Task StopAsync(CancellationToken cancellationToken)
{ {
_logger.LogDebug("Stopping IpcProvider Service"); _logger.LogDebug("Stopping IpcProvider Service");
@@ -149,14 +63,6 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
_loadFileAsyncProvider?.UnregisterFunc(); _loadFileAsyncProvider?.UnregisterFunc();
_handledGameAddresses?.UnregisterFunc(); _handledGameAddresses?.UnregisterFunc();
_registerDelayCts.Cancel();
if (_impersonating)
{
_loadFileProviderMare?.UnregisterFunc();
_loadFileAsyncProviderMare?.UnregisterFunc();
_handledGameAddressesMare?.UnregisterFunc();
}
Mediator.UnsubscribeAll(this); Mediator.UnsubscribeAll(this);
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -186,19 +92,4 @@ public class IpcProvider : IHostedService, IMediatorSubscriber
{ {
return _activeGameObjectHandlers.Where(g => g.Address != nint.Zero).Select(g => g.Address).Distinct().ToList(); return _activeGameObjectHandlers.Where(g => g.Address != nint.Zero).Select(g => g.Address).Distinct().ToList();
} }
private List<nint> MareGetHandledAddresses()
{
if (!_impersonating)
{
if ((DateTime.UtcNow - _unregisterTime).TotalSeconds >= 1.0)
{
_logger.LogWarning("GetHandledAddresses called when it should not be registered");
_handledGameAddressesMare?.UnregisterFunc();
}
return [];
}
return GetHandledAddresses();
}
} }

View File

@@ -2,7 +2,7 @@
<Project Sdk="Dalamud.NET.Sdk/13.1.0"> <Project Sdk="Dalamud.NET.Sdk/13.1.0">
<PropertyGroup> <PropertyGroup>
<AssemblyName>ClubPenguinSync</AssemblyName> <AssemblyName>ClubPenguinSync</AssemblyName>
<Version>1.7.0.8</Version> <Version>1.7.1.0</Version>
<PackageProjectUrl>https://github.com/Rawrington/ClubPenguinSync/</PackageProjectUrl> <PackageProjectUrl>https://github.com/Rawrington/ClubPenguinSync/</PackageProjectUrl>
</PropertyGroup> </PropertyGroup>

View File

@@ -178,7 +178,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<IpcCallerOtherSync>();
collection.AddSingleton<IpcManager>(); collection.AddSingleton<IpcManager>();
collection.AddSingleton<NotificationService>(); collection.AddSingleton<NotificationService>();

View File

@@ -12,21 +12,21 @@ public class VisibilityService : DisposableMediatorSubscriberBase
{ {
NotVisible, NotVisible,
Visible, Visible,
MareHandled OtherSyncHandled
}; };
private readonly DalamudUtilService _dalamudUtil; private readonly DalamudUtilService _dalamudUtil;
private readonly ConcurrentDictionary<string, TrackedPlayerStatus> _trackedPlayerVisibility = new(StringComparer.Ordinal); private readonly ConcurrentDictionary<string, TrackedPlayerStatus> _trackedPlayerVisibility = new(StringComparer.Ordinal);
private readonly List<string> _makeVisibleNextFrame = new(); private readonly List<string> _makeVisibleNextFrame = new();
private readonly IpcCallerMare _mare; private readonly IpcCallerOtherSync _otherSync;
private readonly HashSet<nint> cachedMareAddresses = new(); private readonly HashSet<nint> cachedOtherSyncAddresses = new();
private uint _cachedAddressSum = 0; private uint _cachedAddressSum = 0;
private uint _cachedAddressSumDebounce = 1; private uint _cachedAddressSumDebounce = 1;
public VisibilityService(ILogger<VisibilityService> logger, MareMediator mediator, IpcCallerMare mare, DalamudUtilService dalamudUtil) public VisibilityService(ILogger<VisibilityService> logger, MareMediator mediator, IpcCallerOtherSync otherSync, DalamudUtilService dalamudUtil)
: base(logger, mediator) : base(logger, mediator)
{ {
_mare = mare; _otherSync = otherSync;
_dalamudUtil = dalamudUtil; _dalamudUtil = dalamudUtil;
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate()); Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
} }
@@ -44,19 +44,19 @@ public class VisibilityService : DisposableMediatorSubscriberBase
private void FrameworkUpdate() private void FrameworkUpdate()
{ {
var mareHandledAddresses = _mare.GetHandledGameAddresses(); var otherSyncHandledAddresses = _otherSync.GetHandledGameAddresses();
uint addressSum = 0; uint addressSum = 0;
foreach (var addr in mareHandledAddresses) foreach (var addr in otherSyncHandledAddresses)
addressSum ^= (uint)addr.GetHashCode(); addressSum ^= (uint)addr.GetHashCode();
if (addressSum != _cachedAddressSum) if (addressSum != _cachedAddressSum)
{ {
if (addressSum == _cachedAddressSumDebounce) if (addressSum == _cachedAddressSumDebounce)
{ {
cachedMareAddresses.Clear(); cachedOtherSyncAddresses.Clear();
foreach (var addr in mareHandledAddresses) foreach (var addr in otherSyncHandledAddresses)
cachedMareAddresses.Add(addr); cachedOtherSyncAddresses.Add(addr);
_cachedAddressSum = addressSum; _cachedAddressSum = addressSum;
} }
else else
@@ -69,11 +69,11 @@ public class VisibilityService : DisposableMediatorSubscriberBase
{ {
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 isOtherSyncHandled = cachedOtherSyncAddresses.Contains(findResult.Address);
var isVisible = findResult.ObjectId != 0 && !isMareHandled; var isVisible = findResult.ObjectId != 0 && !isOtherSyncHandled;
if (player.Value == TrackedPlayerStatus.MareHandled && !isMareHandled) if (player.Value == TrackedPlayerStatus.OtherSyncHandled && !isOtherSyncHandled)
_trackedPlayerVisibility.TryUpdate(ident, newValue: TrackedPlayerStatus.NotVisible, comparisonValue: TrackedPlayerStatus.MareHandled); _trackedPlayerVisibility.TryUpdate(ident, newValue: TrackedPlayerStatus.NotVisible, comparisonValue: TrackedPlayerStatus.OtherSyncHandled);
if (player.Value == TrackedPlayerStatus.NotVisible && isVisible) if (player.Value == TrackedPlayerStatus.NotVisible && isVisible)
{ {
@@ -85,17 +85,17 @@ public class VisibilityService : DisposableMediatorSubscriberBase
else else
_makeVisibleNextFrame.Add(ident); _makeVisibleNextFrame.Add(ident);
} }
else if (player.Value == TrackedPlayerStatus.NotVisible && isMareHandled) else if (player.Value == TrackedPlayerStatus.NotVisible && isOtherSyncHandled)
{ {
// Send a technically redundant visibility update with the added intent of triggering PairHandler to undo the application by name // 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)) if (_trackedPlayerVisibility.TryUpdate(ident, newValue: TrackedPlayerStatus.OtherSyncHandled, comparisonValue: TrackedPlayerStatus.NotVisible))
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false, Invalidate: true)); Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false, Invalidate: true));
} }
else if (player.Value == TrackedPlayerStatus.Visible && !isVisible) else if (player.Value == TrackedPlayerStatus.Visible && !isVisible)
{ {
var newTrackedStatus = isMareHandled ? TrackedPlayerStatus.MareHandled : TrackedPlayerStatus.NotVisible; var newTrackedStatus = isOtherSyncHandled ? TrackedPlayerStatus.OtherSyncHandled : TrackedPlayerStatus.NotVisible;
if (_trackedPlayerVisibility.TryUpdate(ident, newValue: newTrackedStatus, comparisonValue: TrackedPlayerStatus.Visible)) if (_trackedPlayerVisibility.TryUpdate(ident, newValue: newTrackedStatus, comparisonValue: TrackedPlayerStatus.Visible))
Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false, Invalidate: isMareHandled)); Mediator.Publish<PlayerVisibilityMessage>(new(ident, IsVisible: false, Invalidate: isOtherSyncHandled));
} }
if (!isVisible) if (!isVisible)

View File

@@ -585,43 +585,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
_uiShared.BigText("Advanced"); _uiShared.BigText("Advanced");
bool mareApi = _configService.Current.MareAPI;
using (var disabled = ImRaii.Disabled(true))
{
bool dummyFalse = false;
if (ImGui.Checkbox("Enable Mare Synchronos API", ref dummyFalse))
{
_configService.Current.MareAPI = mareApi;
_configService.Save();
_ipcProvider.HandleMareImpersonation();
}
_uiShared.DrawHelpText("Enables handling of the Mare Synchronos API. This currently includes:\n\n" +
" - MCDF loading support for other plugins\n" +
" - Blocking Moodles applications to paired users\n\n" +
"If the Mare Synchronos plugin is loaded while this option is enabled, control of its API will be relinquished.");
}
using (_ = ImRaii.PushIndent())
{
ImGui.SameLine(300.0f * ImGuiHelpers.GlobalScale);
UiSharedService.ColorTextWrapped("Mare API impersonation is currently unavailable", ImGuiColors.DalamudGrey2);
/*
if (_ipcProvider.ImpersonationActive)
{
UiSharedService.ColorTextWrapped("Mare API active!", ImGuiColors.HealerGreen);
}
else
{
if (!mareApi)
UiSharedService.ColorTextWrapped("Mare API inactive: Option is disabled", ImGuiColors.DalamudYellow);
else if (_ipcProvider.MarePluginEnabled)
UiSharedService.ColorTextWrapped("Mare API inactive: Mare plugin is loaded", ImGuiColors.DalamudYellow);
else
UiSharedService.ColorTextWrapped("Mare API inactive: Unknown reason", ImGuiColors.DalamudRed);
}
*/
}
bool logEvents = _configService.Current.LogEvents; bool logEvents = _configService.Current.LogEvents;
if (ImGui.Checkbox("Log Event Viewer data to disk", ref logEvents)) if (ImGui.Checkbox("Log Event Viewer data to disk", ref logEvents))
{ {