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 8be4968..ae6934c 100644 --- a/MareSynchronos/Interop/Ipc/IpcManager.cs +++ b/MareSynchronos/Interop/Ipc/IpcManager.cs @@ -7,7 +7,7 @@ public sealed partial class IpcManager : DisposableMediatorSubscriberBase { public IpcManager(ILogger logger, MareMediator mediator, IpcCallerPenumbra penumbraIpc, IpcCallerGlamourer glamourerIpc, IpcCallerCustomize customizeIpc, IpcCallerHeels heelsIpc, - IpcCallerHonorific honorificIpc, IpcCallerMoodles moodlesIpc) : base(logger, mediator) + IpcCallerHonorific honorificIpc, IpcCallerMoodles moodlesIpc, IpcCallerPetNames ipcCallerPetNames) : base(logger, mediator) { CustomizePlus = customizeIpc; Heels = heelsIpc; @@ -15,6 +15,7 @@ public sealed partial class IpcManager : DisposableMediatorSubscriberBase Penumbra = penumbraIpc; Honorific = honorificIpc; Moodles = moodlesIpc; + PetNames = ipcCallerPetNames; if (Initialized) { @@ -41,6 +42,7 @@ public sealed partial class IpcManager : DisposableMediatorSubscriberBase public IpcCallerGlamourer Glamourer { get; } public IpcCallerPenumbra Penumbra { get; } public IpcCallerMoodles Moodles { get; } + public IpcCallerPetNames PetNames { get; } private void PeriodicApiStateCheck() { @@ -51,5 +53,6 @@ public sealed partial class IpcManager : DisposableMediatorSubscriberBase CustomizePlus.CheckAPI(); Honorific.CheckAPI(); Moodles.CheckAPI(); + PetNames.CheckAPI(); } } \ No newline at end of file diff --git a/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs b/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs index f8f8659..0326cd3 100644 --- a/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs +++ b/MareSynchronos/PlayerData/Factories/PlayerDataFactory.cs @@ -217,6 +217,9 @@ public class PlayerDataFactory { previousData.MoodlesData = await _ipcManager.Moodles.GetStatusAsync(playerRelatedObject.Address).ConfigureAwait(false) ?? string.Empty; _logger.LogDebug("Moodles is now: {moodles}", previousData.MoodlesData); + + previousData.MoodlesData = _ipcManager.PetNames.GetLocalNames(); + _logger.LogDebug("Pet Nicknames is now: {moodles}", previousData.MoodlesData); } if (previousData.FileReplacements.TryGetValue(objectKind, out HashSet? fileReplacements)) diff --git a/MareSynchronos/PlayerData/Handlers/PairHandler.cs b/MareSynchronos/PlayerData/Handlers/PairHandler.cs index ea99323..e09dd27 100644 --- a/MareSynchronos/PlayerData/Handlers/PairHandler.cs +++ b/MareSynchronos/PlayerData/Handlers/PairHandler.cs @@ -344,6 +344,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase case PlayerChanges.Moodles: await _ipcManager.Moodles.SetStatusAsync(handler.Address, charaData.MoodlesData).ConfigureAwait(false); + + await _ipcManager.PetNames.SetPlayerData(handler.Address, charaData.MoodlesData).ConfigureAwait(false); break; case PlayerChanges.ForcedRedraw: @@ -593,6 +595,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase await _ipcManager.Honorific.ClearTitleAsync(address).ConfigureAwait(false); Logger.LogDebug("[{applicationId}] Restoring Moodles for {alias}/{name}", applicationId, Pair.UserData.AliasOrUID, name); await _ipcManager.Moodles.RevertStatusAsync(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 f27e536..29954c7 100644 --- a/MareSynchronos/PlayerData/Pairs/OptionalPluginWarning.cs +++ b/MareSynchronos/PlayerData/Pairs/OptionalPluginWarning.cs @@ -6,4 +6,5 @@ public record OptionalPluginWarning public bool ShownCustomizePlusWarning { get; set; } = false; public bool ShownHonorificWarning { get; set; } = false; public bool ShownMoodlesWarning { 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 bbdb28e..cf43f65 100644 --- a/MareSynchronos/PlayerData/Services/CacheCreationService.cs +++ b/MareSynchronos/PlayerData/Services/CacheCreationService.cs @@ -21,6 +21,7 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase private Task? _cacheCreationTask; private CancellationTokenSource _honorificCts = new(); private CancellationTokenSource _moodlesCts = new(); + private CancellationTokenSource _petNicknamesCts = new(); private bool _isZoning = false; private readonly Dictionary _glamourerCts = new(); @@ -123,6 +124,15 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase MoodlesChanged(); } }); + Mediator.Subscribe(this, (msg) => + { + if (_isZoning) return; + if (!string.Equals(msg.PetNicknamesData, _playerData.MoodlesData, StringComparison.Ordinal)) + { + Logger.LogDebug("Received Pet Nicknames change, updating player"); + PetNicknamesChanged(); + } + }); Mediator.Subscribe(this, (msg) => { Logger.LogDebug("Received Penumbra Mod settings change, updating player"); @@ -192,6 +202,20 @@ public sealed class CacheCreationService : DisposableMediatorSubscriberBase }, 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 c4f75b6..963e612 100644 --- a/MareSynchronos/Plugin.cs +++ b/MareSynchronos/Plugin.cs @@ -138,10 +138,12 @@ public sealed class Plugin : IDalamudPlugin s.GetRequiredService(), s.GetRequiredService())); collection.AddSingleton((s) => new IpcCallerMoodles(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())); collection.AddSingleton((s) => new NotificationService(s.GetRequiredService>(), s.GetRequiredService(), s.GetRequiredService(), notificationManager, chatGui, s.GetRequiredService())); diff --git a/MareSynchronos/Services/Mediator/Messages.cs b/MareSynchronos/Services/Mediator/Messages.cs index e2ded6a..340a268 100644 --- a/MareSynchronos/Services/Mediator/Messages.cs +++ b/MareSynchronos/Services/Mediator/Messages.cs @@ -41,6 +41,7 @@ public record PenumbraResourceLoadMessage(IntPtr GameObject, string GamePath, st public record CustomizePlusMessage(nint? Address) : MessageBase; public record HonorificMessage(string NewHonorificTitle) : MessageBase; public record MoodlesMessage(IntPtr Address) : 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 4885832..f883e8e 100644 --- a/MareSynchronos/Services/PluginWarningNotificationService.cs +++ b/MareSynchronos/Services/PluginWarningNotificationService.cs @@ -31,7 +31,8 @@ public class PluginWarningNotificationService ShownCustomizePlusWarning = _mareConfigService.Current.DisableOptionalPluginWarnings, ShownHeelsWarning = _mareConfigService.Current.DisableOptionalPluginWarnings, ShownHonorificWarning = _mareConfigService.Current.DisableOptionalPluginWarnings, - ShownMoodlesWarning = _mareConfigService.Current.DisableOptionalPluginWarnings + ShownMoodlesWarning = _mareConfigService.Current.DisableOptionalPluginWarnings, + ShowPetNicknamesWarning = _mareConfigService.Current.DisableOptionalPluginWarnings }; } @@ -59,6 +60,12 @@ public class PluginWarningNotificationService warning.ShownMoodlesWarning = true; } + if (changes.Contains(PlayerChanges.Moodles) && !warning.ShowPetNicknamesWarning && !_ipcManager.PetNames.APIAvailable) + { + missingPluginsForData.Add("PetNicknames"); + warning.ShowPetNicknamesWarning = true; + } + if (missingPluginsForData.Any()) { _mediator.Publish(new NotificationMessage("Missing plugins for " + playerName,