From 28828a620ba3be287165292d7dd9690ae56de5d8 Mon Sep 17 00:00:00 2001 From: Amber <78602618+Glyceri@users.noreply.github.com> Date: Tue, 10 Sep 2024 09:13:34 +0200 Subject: [PATCH] Pet Nicknames IPC implementation (#74) --- .../Interop/Ipc/IpcCallerPetNames.cs | 157 ++++++++++++++++++ MareSynchronos/Interop/Ipc/IpcManager.cs | 5 +- .../PlayerData/Data/CharacterData.cs | 4 +- .../PlayerData/Data/PlayerChanges.cs | 1 + .../PlayerData/Factories/PlayerDataFactory.cs | 5 + .../PlayerData/Handlers/PairHandler.cs | 6 + .../PlayerData/Pairs/OptionalPluginWarning.cs | 1 + .../Services/CacheCreationService.cs | 25 +++ MareSynchronos/Plugin.cs | 5 +- MareSynchronos/Services/Mediator/Messages.cs | 1 + .../PluginWarningNotificationService.cs | 7 + 11 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 MareSynchronos/Interop/Ipc/IpcCallerPetNames.cs diff --git a/MareSynchronos/Interop/Ipc/IpcCallerPetNames.cs b/MareSynchronos/Interop/Ipc/IpcCallerPetNames.cs new file mode 100644 index 0000000..5954c37 --- /dev/null +++ b/MareSynchronos/Interop/Ipc/IpcCallerPetNames.cs @@ -0,0 +1,157 @@ +using Dalamud.Game.ClientState.Objects.SubKinds; +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 IpcCallerPetNames : IIpcCaller +{ + private readonly ILogger _logger; + private readonly DalamudUtilService _dalamudUtil; + private readonly MareMediator _mareMediator; + + private readonly ICallGateSubscriber _petnamesReady; + private readonly ICallGateSubscriber _petnamesDisposing; + private readonly ICallGateSubscriber<(uint, uint)> _apiVersion; + private readonly ICallGateSubscriber _enabled; + + private readonly ICallGateSubscriber _playerDataChanged; + private readonly ICallGateSubscriber _getPlayerData; + private readonly ICallGateSubscriber _setPlayerData; + private readonly ICallGateSubscriber _clearPlayerData; + + public IpcCallerPetNames(ILogger logger, IDalamudPluginInterface pi, DalamudUtilService dalamudUtil, + MareMediator mareMediator) + { + _logger = logger; + _dalamudUtil = dalamudUtil; + _mareMediator = mareMediator; + + _petnamesReady = pi.GetIpcSubscriber("PetRenamer.Ready"); + _petnamesDisposing = pi.GetIpcSubscriber("PetRenamer.Disposing"); + _apiVersion = pi.GetIpcSubscriber<(uint, uint)>("PetRenamer.ApiVersion"); + _enabled = pi.GetIpcSubscriber("PetRenamer.Enabled"); + + _playerDataChanged = pi.GetIpcSubscriber("PetRenamer.PlayerDataChanged"); + _getPlayerData = pi.GetIpcSubscriber("PetRenamer.GetPlayerData"); + _setPlayerData = pi.GetIpcSubscriber("PetRenamer.SetPlayerData"); + _clearPlayerData = pi.GetIpcSubscriber("PetRenamer.ClearPlayerData"); + + _petnamesReady.Subscribe(OnPetNicknamesReady); + _petnamesDisposing.Subscribe(OnPetNicknamesDispose); + _playerDataChanged.Subscribe(OnLocalPetNicknamesDataChange); + + CheckAPI(); + } + + public bool APIAvailable { get; private set; } = false; + + public void CheckAPI() + { + try + { + APIAvailable = _enabled?.InvokeFunc() ?? false; + if (APIAvailable) + { + APIAvailable = _apiVersion?.InvokeFunc() is { Item1: 3, Item2: >= 1 }; + } + } + catch + { + APIAvailable = false; + } + } + + private void OnPetNicknamesReady() + { + CheckAPI(); + } + + private void OnPetNicknamesDispose() + { + _mareMediator.Publish(new PetNamesMessage(string.Empty)); + } + + public string GetLocalNames() + { + if (!APIAvailable) return string.Empty; + + try + { + string localNameData = _getPlayerData.InvokeFunc(); + return string.IsNullOrEmpty(localNameData) ? string.Empty : localNameData; + } + catch (Exception e) + { + _logger.LogWarning(e, "Could not obtain Pet Nicknames data"); + } + + return string.Empty; + } + + public async Task SetPlayerData(nint character, string playerData) + { + if (!APIAvailable) return; + + _logger.LogTrace("Applying Pet Nicknames data to {chara}", character.ToString("X")); + + try + { + await _dalamudUtil.RunOnFrameworkThread(() => + { + if (string.IsNullOrEmpty(playerData)) + { + var gameObj = _dalamudUtil.CreateGameObject(character); + if (gameObj is IPlayerCharacter pc) + { + _clearPlayerData.InvokeAction(pc.ObjectIndex); + } + } + else + { + _setPlayerData.InvokeAction(playerData); + } + }).ConfigureAwait(false); + } + catch (Exception e) + { + _logger.LogWarning(e, "Could not apply Pet Nicknames data"); + } + } + + public async Task ClearPlayerData(nint characterPointer) + { + if (!APIAvailable) return; + try + { + await _dalamudUtil.RunOnFrameworkThread(() => + { + var gameObj = _dalamudUtil.CreateGameObject(characterPointer); + if (gameObj is IPlayerCharacter pc) + { + _logger.LogTrace("Pet Nicknames removing for {addr}", pc.Address.ToString("X")); + _clearPlayerData.InvokeAction(pc.ObjectIndex); + } + }).ConfigureAwait(false); + } + catch (Exception e) + { + _logger.LogWarning(e, "Could not clear Pet Nicknames data"); + } + } + + private void OnLocalPetNicknamesDataChange(string data) + { + _mareMediator.Publish(new PetNamesMessage(data)); + } + + public void Dispose() + { + _petnamesReady.Unsubscribe(OnPetNicknamesReady); + _petnamesDisposing.Unsubscribe(OnPetNicknamesDispose); + _playerDataChanged.Unsubscribe(OnLocalPetNicknamesDataChange); + } +} diff --git a/MareSynchronos/Interop/Ipc/IpcManager.cs b/MareSynchronos/Interop/Ipc/IpcManager.cs index 9b92154..0408420 100644 --- a/MareSynchronos/Interop/Ipc/IpcManager.cs +++ b/MareSynchronos/Interop/Ipc/IpcManager.cs @@ -7,13 +7,14 @@ public sealed partial class IpcManager : DisposableMediatorSubscriberBase { public IpcManager(ILogger logger, MareMediator mediator, IpcCallerPenumbra penumbraIpc, IpcCallerGlamourer glamourerIpc, IpcCallerCustomize customizeIpc, IpcCallerHeels heelsIpc, - IpcCallerHonorific honorificIpc) : base(logger, mediator) + IpcCallerHonorific honorificIpc, IpcCallerPetNames ipcCallerPetNames) : base(logger, mediator) { CustomizePlus = customizeIpc; Heels = heelsIpc; Glamourer = glamourerIpc; Penumbra = penumbraIpc; Honorific = honorificIpc; + PetNames = ipcCallerPetNames; if (Initialized) { @@ -39,6 +40,7 @@ public sealed partial class IpcManager : DisposableMediatorSubscriberBase public IpcCallerHeels Heels { get; init; } public IpcCallerGlamourer Glamourer { get; } public IpcCallerPenumbra Penumbra { get; } + public IpcCallerPetNames PetNames { get; } private void PeriodicApiStateCheck() { @@ -48,5 +50,6 @@ public sealed partial class IpcManager : DisposableMediatorSubscriberBase Heels.CheckAPI(); CustomizePlus.CheckAPI(); Honorific.CheckAPI(); + PetNames.CheckAPI(); } } \ No newline at end of file diff --git a/MareSynchronos/PlayerData/Data/CharacterData.cs b/MareSynchronos/PlayerData/Data/CharacterData.cs index 21643fe..fe767d3 100644 --- a/MareSynchronos/PlayerData/Data/CharacterData.cs +++ b/MareSynchronos/PlayerData/Data/CharacterData.cs @@ -16,6 +16,7 @@ public class CharacterData public string HeelsData { get; set; } = string.Empty; public string HonorificData { get; set; } = string.Empty; public string ManipulationString { get; set; } = string.Empty; + public string PetNamesData { get; set; } = string.Empty; public API.Data.CharacterData ToAPI() { @@ -44,7 +45,8 @@ public class CharacterData ManipulationData = ManipulationString, HeelsData = HeelsData, CustomizePlusData = CustomizePlusScale.ToDictionary(d => d.Key, d => d.Value), - HonorificData = HonorificData + HonorificData = HonorificData, + PetNamesData = PetNamesData }; } diff --git a/MareSynchronos/PlayerData/Data/PlayerChanges.cs b/MareSynchronos/PlayerData/Data/PlayerChanges.cs index 034fd58..8c2f1f8 100644 --- a/MareSynchronos/PlayerData/Data/PlayerChanges.cs +++ b/MareSynchronos/PlayerData/Data/PlayerChanges.cs @@ -9,4 +9,5 @@ public enum PlayerChanges Heels = 5, Honorific = 7, ForcedRedraw = 8, + PetNames = 10, } \ No newline at end of file diff --git a/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs b/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs index 40d09ea..03589e9 100644 --- a/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs +++ b/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs @@ -204,6 +204,11 @@ public class PlayerDataFactory _logger.LogDebug("Honorific is now: {data}", previousData.HonorificData); previousData.HeelsData = await getHeelsOffset.ConfigureAwait(false); _logger.LogDebug("Heels is now: {heels}", previousData.HeelsData); + if (objectKind == ObjectKind.Player) + { + previousData.PetNamesData = _ipcManager.PetNames.GetLocalNames(); + _logger.LogDebug("Pet Nicknames is now: {moodles}", previousData.PetNamesData); + } if (previousData.FileReplacements.TryGetValue(objectKind, out HashSet? fileReplacements)) { diff --git a/MareSynchronos/PlayerData/Handlers/PairHandler.cs b/MareSynchronos/PlayerData/Handlers/PairHandler.cs index 2a8f04b..1410c39 100644 --- a/MareSynchronos/PlayerData/Handlers/PairHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/PairHandler.cs @@ -341,6 +341,10 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase } break; + case PlayerChanges.PetNames: + await _ipcManager.PetNames.SetPlayerData(handler.Address, charaData.PetNamesData).ConfigureAwait(false); + break; + case PlayerChanges.ForcedRedraw: await _ipcManager.Penumbra.RedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false); break; @@ -570,6 +574,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase tempHandler.CompareNameAndThrow(name); Logger.LogDebug("[{applicationId}] Restoring Honorific for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); await _ipcManager.Honorific.ClearTitleAsync(address).ConfigureAwait(false); + Logger.LogDebug("[{applicationId}] Restoring Pet Nicknames for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name); + await _ipcManager.PetNames.ClearPlayerData(address).ConfigureAwait(false); } else if (objectKind == ObjectKind.MinionOrMount) { diff --git a/MareSynchronos/PlayerData/Pairs/OptionalPluginWarning.cs b/MareSynchronos/PlayerData/Pairs/OptionalPluginWarning.cs index d2f64c0..8d2e5c8 100644 --- a/MareSynchronos/PlayerData/Pairs/OptionalPluginWarning.cs +++ b/MareSynchronos/PlayerData/Pairs/OptionalPluginWarning.cs @@ -5,4 +5,5 @@ public record OptionalPluginWarning public bool ShownHeelsWarning { get; set; } = false; public bool ShownCustomizePlusWarning { get; set; } = false; public bool ShownHonorificWarning { get; set; } = false; + public bool ShowPetNicknamesWarning { get; set; } = false; } \ No newline at end of file diff --git a/MareSynchronos/PlayerData/Services/CacheCreationService.cs b/MareSynchronos/PlayerData/Services/CacheCreationService.cs index e6647d9..7f57aa4 100644 --- a/MareSynchronos/PlayerData/Services/CacheCreationService.cs +++ b/MareSynchronos/PlayerData/Services/CacheCreationService.cs @@ -20,6 +20,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase private readonly Dictionary _playerRelatedObjects = []; private Task? _cacheCreationTask; private CancellationTokenSource _honorificCts = new(); + private CancellationTokenSource _petNicknamesCts = new(); private bool _isZoning = false; private readonly Dictionary _glamourerCts = new(); @@ -108,6 +109,15 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase HonorificChanged(); } }); + Mediator.Subscribe(this, (msg) => + { + if (_isZoning) return; + if (!string.Equals(msg.PetNicknamesData, _playerData.PetNamesData, StringComparison.Ordinal)) + { + Logger.LogDebug("Received Pet Nicknames change, updating player"); + PetNicknamesChanged(); + } + }); Mediator.Subscribe(this, async (msg) => { Logger.LogDebug("Received Penumbra Mod settings change, updating player"); @@ -162,6 +172,21 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase await AddPlayerCacheToCreate().ConfigureAwait(false); }, token); } + + private void PetNicknamesChanged() + { + _petNicknamesCts?.Cancel(); + _petNicknamesCts?.Dispose(); + _petNicknamesCts = new(); + var token = _petNicknamesCts.Token; + + _ = Task.Run(async () => + { + await Task.Delay(TimeSpan.FromSeconds(3), token).ConfigureAwait(false); + await AddPlayerCacheToCreate().ConfigureAwait(false); + }, token); + } + private void ProcessCacheCreation() { if (_isZoning) return; diff --git a/MareSynchronos/Plugin.cs b/MareSynchronos/Plugin.cs index 7408215..1781418 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -111,9 +111,12 @@ public sealed class Plugin : IDalamudPlugin s.GetRequiredService(), s.GetRequiredService())); collection.AddSingleton((s) => new IpcCallerHonorific(s.GetRequiredService>(), pluginInterface, s.GetRequiredService(), s.GetRequiredService())); + collection.AddSingleton((s) => new IpcCallerPetNames(s.GetRequiredService>(), pluginInterface, + s.GetRequiredService(), s.GetRequiredService())); collection.AddSingleton((s) => new IpcManager(s.GetRequiredService>(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), - s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); + s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), + s.GetRequiredService())); collection.AddSingleton((s) => new MareConfigService(pluginInterface.ConfigDirectory.FullName)); collection.AddSingleton((s) => new ServerConfigService(pluginInterface.ConfigDirectory.FullName)); diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index 86458c5..cde467c 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -40,6 +40,7 @@ public record HeelsOffsetMessage : MessageBase; public record PenumbraResourceLoadMessage(IntPtr GameObject, string GamePath, string FilePath) : SameThreadMessage; public record CustomizePlusMessage(string ProfileName) : MessageBase; public record HonorificMessage(string NewHonorificTitle) : MessageBase; +public record PetNamesMessage(string PetNicknamesData) : MessageBase; public record HonorificReadyMessage : MessageBase; public record PlayerChangedMessage(CharacterData Data) : MessageBase; public record CharacterChangedMessage(GameObjectHandler GameObjectHandler) : MessageBase; diff --git a/MareSynchronos/Services/PluginWarningNotificationService.cs b/MareSynchronos/Services/PluginWarningNotificationService.cs index 2458763..15b4822 100644 --- a/MareSynchronos/Services/PluginWarningNotificationService.cs +++ b/MareSynchronos/Services/PluginWarningNotificationService.cs @@ -31,6 +31,7 @@ public class PluginWarningNotificationService ShownCustomizePlusWarning = _mareConfigService.Current.DisableOptionalPluginWarnings, ShownHeelsWarning = _mareConfigService.Current.DisableOptionalPluginWarnings, ShownHonorificWarning = _mareConfigService.Current.DisableOptionalPluginWarnings, + ShowPetNicknamesWarning = _mareConfigService.Current.DisableOptionalPluginWarnings }; } @@ -52,6 +53,12 @@ public class PluginWarningNotificationService warning.ShownHonorificWarning = true; } + if (changes.Contains(PlayerChanges.PetNames) && !warning.ShowPetNicknamesWarning && !_ipcManager.PetNames.APIAvailable) + { + missingPluginsForData.Add("PetNicknames"); + warning.ShowPetNicknamesWarning = true; + } + if (missingPluginsForData.Any()) { _mediator.Publish(new NotificationMessage("Missing plugins for " + playerName,