From 40168d7daf418c5743558bb2906ae52456e9d7e7 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Wed, 22 Jan 2025 23:14:09 +0100 Subject: [PATCH] Fix issue with IPC adding mods before character utility is ready in rare cases. --- Penumbra/Api/IpcProviders.cs | 20 +++++++++++++--- .../Cache/CollectionCacheManager.cs | 6 ++--- .../Communication/CharacterUtilityFinished.cs | 23 +++++++++++++++++++ Penumbra/Interop/Services/CharacterUtility.cs | 21 +++++++++-------- Penumbra/Mods/Editor/ModMetaEditor.cs | 5 ++++ 5 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 Penumbra/Communication/CharacterUtilityFinished.cs diff --git a/Penumbra/Api/IpcProviders.cs b/Penumbra/Api/IpcProviders.cs index f6948832..fc97290f 100644 --- a/Penumbra/Api/IpcProviders.cs +++ b/Penumbra/Api/IpcProviders.cs @@ -2,6 +2,8 @@ using Dalamud.Plugin; using OtterGui.Services; using Penumbra.Api.Api; using Penumbra.Api.Helpers; +using Penumbra.Communication; +using CharacterUtility = Penumbra.Interop.Services.CharacterUtility; namespace Penumbra.Api; @@ -9,11 +11,13 @@ public sealed class IpcProviders : IDisposable, IApiService { private readonly List _providers; - private readonly EventProvider _disposedProvider; - private readonly EventProvider _initializedProvider; + private readonly EventProvider _disposedProvider; + private readonly EventProvider _initializedProvider; + private readonly CharacterUtility _characterUtility; - public IpcProviders(IDalamudPluginInterface pi, IPenumbraApi api) + public IpcProviders(IDalamudPluginInterface pi, IPenumbraApi api, CharacterUtility characterUtility) { + _characterUtility = characterUtility; _disposedProvider = IpcSubscribers.Disposed.Provider(pi); _initializedProvider = IpcSubscribers.Initialized.Provider(pi); _providers = @@ -115,11 +119,21 @@ public sealed class IpcProviders : IDisposable, IApiService IpcSubscribers.OpenMainWindow.Provider(pi, api.Ui), IpcSubscribers.CloseMainWindow.Provider(pi, api.Ui), ]; + if (_characterUtility.Ready) + _initializedProvider.Invoke(); + else + _characterUtility.LoadingFinished.Subscribe(OnCharacterUtilityReady, CharacterUtilityFinished.Priority.IpcProvider); + } + + private void OnCharacterUtilityReady() + { _initializedProvider.Invoke(); + _characterUtility.LoadingFinished.Unsubscribe(OnCharacterUtilityReady); } public void Dispose() { + _characterUtility.LoadingFinished.Unsubscribe(OnCharacterUtilityReady); foreach (var provider in _providers) provider.Dispose(); _providers.Clear(); diff --git a/Penumbra/Collections/Cache/CollectionCacheManager.cs b/Penumbra/Collections/Cache/CollectionCacheManager.cs index 27b969c2..c46759c7 100644 --- a/Penumbra/Collections/Cache/CollectionCacheManager.cs +++ b/Penumbra/Collections/Cache/CollectionCacheManager.cs @@ -71,7 +71,7 @@ public class CollectionCacheManager : IDisposable, IService _communicator.ModDiscoveryFinished.Subscribe(OnModDiscoveryFinished, ModDiscoveryFinished.Priority.CollectionCacheManager); if (!MetaFileManager.CharacterUtility.Ready) - MetaFileManager.CharacterUtility.LoadingFinished += IncrementCounters; + MetaFileManager.CharacterUtility.LoadingFinished.Subscribe(IncrementCounters, CharacterUtilityFinished.Priority.CollectionCacheManager); } public void Dispose() @@ -83,7 +83,7 @@ public class CollectionCacheManager : IDisposable, IService _communicator.ModOptionChanged.Unsubscribe(OnModOptionChange); _communicator.ModSettingChanged.Unsubscribe(OnModSettingChange); _communicator.CollectionInheritanceChanged.Unsubscribe(OnCollectionInheritanceChange); - MetaFileManager.CharacterUtility.LoadingFinished -= IncrementCounters; + MetaFileManager.CharacterUtility.LoadingFinished.Unsubscribe(IncrementCounters); foreach (var collection in _storage) { @@ -298,7 +298,7 @@ public class CollectionCacheManager : IDisposable, IService { foreach (var collection in _storage.Where(c => c.HasCache)) collection.Counters.IncrementChange(); - MetaFileManager.CharacterUtility.LoadingFinished -= IncrementCounters; + MetaFileManager.CharacterUtility.LoadingFinished.Unsubscribe(IncrementCounters); } private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, Setting oldValue, int groupIdx, bool _) diff --git a/Penumbra/Communication/CharacterUtilityFinished.cs b/Penumbra/Communication/CharacterUtilityFinished.cs new file mode 100644 index 00000000..fbeeb8a7 --- /dev/null +++ b/Penumbra/Communication/CharacterUtilityFinished.cs @@ -0,0 +1,23 @@ +using OtterGui.Classes; +using Penumbra.Api; +using Penumbra.Interop.Services; + +namespace Penumbra.Communication; + +/// +/// Triggered when the Character Utility becomes ready. +/// +public sealed class CharacterUtilityFinished() : EventWrapper(nameof(CharacterUtilityFinished)) +{ + public enum Priority + { + /// + OnFinishedLoading = int.MaxValue, + + /// + IpcProvider = int.MinValue, + + /// + CollectionCacheManager = 0, + } +} diff --git a/Penumbra/Interop/Services/CharacterUtility.cs b/Penumbra/Interop/Services/CharacterUtility.cs index 1641e42d..0add9d46 100644 --- a/Penumbra/Interop/Services/CharacterUtility.cs +++ b/Penumbra/Interop/Services/CharacterUtility.cs @@ -1,6 +1,7 @@ using Dalamud.Plugin.Services; using Dalamud.Utility.Signatures; using OtterGui.Services; +using Penumbra.Communication; using Penumbra.GameData; using Penumbra.Interop.Structs; @@ -26,14 +27,16 @@ public unsafe class CharacterUtility : IDisposable, IRequiredService public CharacterUtilityData* Address => *_characterUtilityAddress; - public bool Ready { get; private set; } - public event Action LoadingFinished; - public nint DefaultHumanPbdResource { get; private set; } - public nint DefaultTransparentResource { get; private set; } - public nint DefaultDecalResource { get; private set; } - public nint DefaultSkinShpkResource { get; private set; } - public nint DefaultCharacterStockingsShpkResource { get; private set; } - public nint DefaultCharacterLegacyShpkResource { get; private set; } + public bool Ready { get; private set; } + + public readonly CharacterUtilityFinished LoadingFinished = new(); + + public nint DefaultHumanPbdResource { get; private set; } + public nint DefaultTransparentResource { get; private set; } + public nint DefaultDecalResource { get; private set; } + public nint DefaultSkinShpkResource { get; private set; } + public nint DefaultCharacterStockingsShpkResource { get; private set; } + public nint DefaultCharacterLegacyShpkResource { get; private set; } /// /// The relevant indices depend on which meta manipulations we allow for. @@ -61,7 +64,7 @@ public unsafe class CharacterUtility : IDisposable, IRequiredService .Select(idx => new MetaList(new InternalIndex(idx))) .ToArray(); _framework = framework; - LoadingFinished += () => Penumbra.Log.Debug("Loading of CharacterUtility finished."); + LoadingFinished.Subscribe(() => Penumbra.Log.Debug("Loading of CharacterUtility finished."), CharacterUtilityFinished.Priority.OnFinishedLoading); LoadDefaultResources(null!); if (!Ready) _framework.Update += LoadDefaultResources; diff --git a/Penumbra/Mods/Editor/ModMetaEditor.cs b/Penumbra/Mods/Editor/ModMetaEditor.cs index c06af9c7..c5c8fb8b 100644 --- a/Penumbra/Mods/Editor/ModMetaEditor.cs +++ b/Penumbra/Mods/Editor/ModMetaEditor.cs @@ -70,6 +70,11 @@ public class ModMetaEditor( public static bool DeleteDefaultValues(MetaFileManager metaFileManager, MetaDictionary dict) { + if (!metaFileManager.CharacterUtility.Ready) + { + Penumbra.Log.Warning("Trying to delete default meta values before CharacterUtility was ready, skipped."); + return false; + } var clone = dict.Clone(); dict.ClearForDefault();