diff --git a/Penumbra/Api/Api/CollectionApi.cs b/Penumbra/Api/Api/CollectionApi.cs index c40feb12..a95e2124 100644 --- a/Penumbra/Api/Api/CollectionApi.cs +++ b/Penumbra/Api/Api/CollectionApi.cs @@ -2,14 +2,37 @@ using OtterGui.Services; using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.Collections.Manager; +using Penumbra.Communication; using Penumbra.Mods; +using Penumbra.Mods.Editor; +using Penumbra.Services; +using Penumbra.String.Classes; namespace Penumbra.Api.Api; -public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : IPenumbraApiCollection, IApiService +public class CollectionApi : IPenumbraApiCollection, IApiService, IDisposable { + private readonly CollectionManager _collections; + private readonly ApiHelpers _helpers; + private readonly CommunicatorService _communicator; + + public CollectionApi(CollectionManager collections, ApiHelpers helpers, CommunicatorService communicator) + { + _collections = collections; + _helpers = helpers; + _communicator = communicator; + _communicator.ResolvedFileChanged.Subscribe(OnResolvedFileChange, Communication.ResolvedFileChanged.Priority.ApiResolvedFile); + } + + public void Dispose() + { + _communicator.ResolvedFileChanged.Unsubscribe(OnResolvedFileChange); + } + + public event ResolvedFileChangedDelegate? ResolvedFileChanged; + public Dictionary GetCollections() - => collections.Storage.ToDictionary(c => c.Identity.Id, c => c.Identity.Name); + => _collections.Storage.ToDictionary(c => c.Identity.Id, c => c.Identity.Name); public List<(Guid Id, string Name)> GetCollectionsByIdentifier(string identifier) { @@ -17,13 +40,13 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : return []; var list = new List<(Guid Id, string Name)>(4); - if (Guid.TryParse(identifier, out var guid) && collections.Storage.ById(guid, out var collection) && collection != ModCollection.Empty) + if (Guid.TryParse(identifier, out var guid) && _collections.Storage.ById(guid, out var collection) && collection != ModCollection.Empty) list.Add((collection.Identity.Id, collection.Identity.Name)); else if (identifier.Length >= 8) - list.AddRange(collections.Storage.Where(c => c.Identity.Identifier.StartsWith(identifier, StringComparison.OrdinalIgnoreCase)) + list.AddRange(_collections.Storage.Where(c => c.Identity.Identifier.StartsWith(identifier, StringComparison.OrdinalIgnoreCase)) .Select(c => (c.Identity.Id, c.Identity.Name))); - list.AddRange(collections.Storage + list.AddRange(_collections.Storage .Where(c => string.Equals(c.Identity.Name, identifier, StringComparison.OrdinalIgnoreCase) && !list.Contains((c.Identity.Id, c.Identity.Name))) .Select(c => (c.Identity.Id, c.Identity.Name))); @@ -32,7 +55,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : public Func CheckCurrentChangedItemFunc() { - var weakRef = new WeakReference(collections); + var weakRef = new WeakReference(_collections); return s => { if (!weakRef.TryGetTarget(out var c)) @@ -45,11 +68,31 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : }; } + public Dictionary GetResolvedFilesForCollection(Guid collectionId) + { + try + { + if (!_collections.Storage.ById(collectionId, out var collection)) + collection = ModCollection.Empty; + + if (collection.HasCache) + return collection.ResolvedFiles.ToDictionary(kvp => kvp.Key.ToString(), kvp => kvp.Value.Path.ToString()); + + Penumbra.Log.Warning($"Collection {collectionId} does not exist or is not loaded."); + return []; + } + catch (Exception e) + { + Penumbra.Log.Error($"Could not obtain Resolved Files for {collectionId}:\n{e}"); + throw; + } + } + public Dictionary GetChangedItemsForCollection(Guid collectionId) { try { - if (!collections.Storage.ById(collectionId, out var collection)) + if (!_collections.Storage.ById(collectionId, out var collection)) collection = ModCollection.Empty; if (collection.HasCache) @@ -70,7 +113,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : if (!Enum.IsDefined(type)) return null; - var collection = collections.Active.ByType((CollectionType)type); + var collection = _collections.Active.ByType((CollectionType)type); return collection == null ? null : (collection.Identity.Id, collection.Identity.Name); } @@ -79,19 +122,19 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : public (bool ObjectValid, bool IndividualSet, (Guid Id, string Name) EffectiveCollection) GetCollectionForObject(int gameObjectIdx) { - var id = helpers.AssociatedIdentifier(gameObjectIdx); + var id = _helpers.AssociatedIdentifier(gameObjectIdx); if (!id.IsValid) - return (false, false, (collections.Active.Default.Identity.Id, collections.Active.Default.Identity.Name)); + return (false, false, (_collections.Active.Default.Identity.Id, _collections.Active.Default.Identity.Name)); - if (collections.Active.Individuals.TryGetValue(id, out var collection)) + if (_collections.Active.Individuals.TryGetValue(id, out var collection)) return (true, true, (collection.Identity.Id, collection.Identity.Name)); - helpers.AssociatedCollection(gameObjectIdx, out collection); + _helpers.AssociatedCollection(gameObjectIdx, out collection); return (true, false, (collection.Identity.Id, collection.Identity.Name)); } public Guid[] GetCollectionByName(string name) - => collections.Storage.Where(c => string.Equals(name, c.Identity.Name, StringComparison.OrdinalIgnoreCase)).Select(c => c.Identity.Id) + => _collections.Storage.Where(c => string.Equals(name, c.Identity.Name, StringComparison.OrdinalIgnoreCase)).Select(c => c.Identity.Id) .ToArray(); public (PenumbraApiEc, (Guid Id, string Name)? OldCollection) SetCollection(ApiCollectionType type, Guid? collectionId, @@ -100,7 +143,7 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : if (!Enum.IsDefined(type)) return (PenumbraApiEc.InvalidArgument, null); - var oldCollection = collections.Active.ByType((CollectionType)type); + var oldCollection = _collections.Active.ByType((CollectionType)type); var old = oldCollection != null ? (oldCollection.Identity.Id, oldCollection.Identity.Name) : new ValueTuple?(); if (collectionId == null) { @@ -110,11 +153,11 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : if (!allowDelete || type is ApiCollectionType.Current or ApiCollectionType.Default or ApiCollectionType.Interface) return (PenumbraApiEc.AssignmentDeletionDisallowed, old); - collections.Active.RemoveSpecialCollection((CollectionType)type); + _collections.Active.RemoveSpecialCollection((CollectionType)type); return (PenumbraApiEc.Success, old); } - if (!collections.Storage.ById(collectionId.Value, out var collection)) + if (!_collections.Storage.ById(collectionId.Value, out var collection)) return (PenumbraApiEc.CollectionMissing, old); if (old == null) @@ -122,25 +165,25 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : if (!allowCreateNew) return (PenumbraApiEc.AssignmentCreationDisallowed, old); - collections.Active.CreateSpecialCollection((CollectionType)type); + _collections.Active.CreateSpecialCollection((CollectionType)type); } else if (old.Value.Item1 == collection.Identity.Id) { return (PenumbraApiEc.NothingChanged, old); } - collections.Active.SetCollection(collection, (CollectionType)type); + _collections.Active.SetCollection(collection, (CollectionType)type); return (PenumbraApiEc.Success, old); } public (PenumbraApiEc, (Guid Id, string Name)? OldCollection) SetCollectionForObject(int gameObjectIdx, Guid? collectionId, bool allowCreateNew, bool allowDelete) { - var id = helpers.AssociatedIdentifier(gameObjectIdx); + var id = _helpers.AssociatedIdentifier(gameObjectIdx); if (!id.IsValid) - return (PenumbraApiEc.InvalidIdentifier, (collections.Active.Default.Identity.Id, collections.Active.Default.Identity.Name)); + return (PenumbraApiEc.InvalidIdentifier, (_collections.Active.Default.Identity.Id, _collections.Active.Default.Identity.Name)); - var oldCollection = collections.Active.Individuals.TryGetValue(id, out var c) ? c : null; + var oldCollection = _collections.Active.Individuals.TryGetValue(id, out var c) ? c : null; var old = oldCollection != null ? (oldCollection.Identity.Id, oldCollection.Identity.Name) : new ValueTuple?(); if (collectionId == null) { @@ -150,12 +193,12 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : if (!allowDelete) return (PenumbraApiEc.AssignmentDeletionDisallowed, old); - var idx = collections.Active.Individuals.Index(id); - collections.Active.RemoveIndividualCollection(idx); + var idx = _collections.Active.Individuals.Index(id); + _collections.Active.RemoveIndividualCollection(idx); return (PenumbraApiEc.Success, old); } - if (!collections.Storage.ById(collectionId.Value, out var collection)) + if (!_collections.Storage.ById(collectionId.Value, out var collection)) return (PenumbraApiEc.CollectionMissing, old); if (old == null) @@ -163,15 +206,21 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) : if (!allowCreateNew) return (PenumbraApiEc.AssignmentCreationDisallowed, old); - var ids = collections.Active.Individuals.GetGroup(id); - collections.Active.CreateIndividualCollection(ids); + var ids = _collections.Active.Individuals.GetGroup(id); + _collections.Active.CreateIndividualCollection(ids); } else if (old.Value.Item1 == collection.Identity.Id) { return (PenumbraApiEc.NothingChanged, old); } - collections.Active.SetCollection(collection, CollectionType.Individual, collections.Active.Individuals.Index(id)); + _collections.Active.SetCollection(collection, CollectionType.Individual, _collections.Active.Individuals.Index(id)); return (PenumbraApiEc.Success, old); } + + private void OnResolvedFileChange(ModCollection collection, ResolvedFileChange type, Utf8GamePath gamePath, FullPath newPath, FullPath oldPath, IMod? mod) + { + // Penumbra.Log.Debug($"[API]{{{collection.Identity.Name}}} | {{{mod}}} | {type} Redirect of [ {gamePath} ] ([ {oldPath} ] -> [ {newPath} ])"); + ResolvedFileChanged?.Invoke(type, collection.Identity.Id, mod?.Name ?? string.Empty, gamePath.ToString(), oldPath.ToString(), newPath.ToString()); + } } diff --git a/Penumbra/Api/DalamudSubstitutionProvider.cs b/Penumbra/Api/DalamudSubstitutionProvider.cs index e10dc461..6ac8bb23 100644 --- a/Penumbra/Api/DalamudSubstitutionProvider.cs +++ b/Penumbra/Api/DalamudSubstitutionProvider.cs @@ -1,6 +1,7 @@ using Dalamud.Interface; using Dalamud.Plugin.Services; using OtterGui.Services; +using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.Collections.Manager; using Penumbra.Communication; @@ -85,7 +86,7 @@ public class DalamudSubstitutionProvider : IDisposable, IApiService ResetSubstitutions(enumerable); } - private void OnResolvedFileChange(ModCollection collection, ResolvedFileChanged.Type type, Utf8GamePath key, FullPath _1, FullPath _2, + private void OnResolvedFileChange(ModCollection collection, ResolvedFileChange type, Utf8GamePath key, FullPath _1, FullPath _2, IMod? _3) { if (_activeCollectionData.Interface != collection) @@ -93,13 +94,13 @@ public class DalamudSubstitutionProvider : IDisposable, IApiService switch (type) { - case ResolvedFileChanged.Type.Added: - case ResolvedFileChanged.Type.Removed: - case ResolvedFileChanged.Type.Replaced: + case ResolvedFileChange.Added: + case ResolvedFileChange.Removed: + case ResolvedFileChange.Replaced: ResetSubstitutions([key]); break; - case ResolvedFileChanged.Type.FullRecomputeStart: - case ResolvedFileChanged.Type.FullRecomputeFinished: + case ResolvedFileChange.FullRecomputeStart: + case ResolvedFileChange.FullRecomputeFinished: ResetSubstitutions(collection.ResolvedFiles.Keys); break; } diff --git a/Penumbra/Api/IpcProviders.cs b/Penumbra/Api/IpcProviders.cs index 7cbe29f6..293cd736 100644 --- a/Penumbra/Api/IpcProviders.cs +++ b/Penumbra/Api/IpcProviders.cs @@ -24,12 +24,14 @@ public sealed class IpcProviders : IDisposable, IApiService [ IpcSubscribers.GetCollections.Provider(pi, api.Collection), IpcSubscribers.GetCollectionsByIdentifier.Provider(pi, api.Collection), + IpcSubscribers.GetResolvedFilesForCollection.Provider(pi, api.Collection), IpcSubscribers.GetChangedItemsForCollection.Provider(pi, api.Collection), IpcSubscribers.GetCollection.Provider(pi, api.Collection), IpcSubscribers.GetCollectionForObject.Provider(pi, api.Collection), IpcSubscribers.SetCollection.Provider(pi, api.Collection), IpcSubscribers.SetCollectionForObject.Provider(pi, api.Collection), IpcSubscribers.CheckCurrentChangedItemFunc.Provider(pi, api.Collection), + IpcSubscribers.ResolvedFileChanged.Provider(pi, api.Collection), IpcSubscribers.ConvertTextureFile.Provider(pi, api.Editing), IpcSubscribers.ConvertTextureData.Provider(pi, api.Editing), diff --git a/Penumbra/Api/IpcTester/CollectionsIpcTester.cs b/Penumbra/Api/IpcTester/CollectionsIpcTester.cs index f033b7c3..a7e7f7f9 100644 --- a/Penumbra/Api/IpcTester/CollectionsIpcTester.cs +++ b/Penumbra/Api/IpcTester/CollectionsIpcTester.cs @@ -6,6 +6,7 @@ using OtterGui; using OtterGui.Raii; using OtterGui.Services; using Penumbra.Api.Enums; +using Penumbra.Api.Helpers; using Penumbra.Api.IpcSubscribers; using Penumbra.Collections.Manager; using Penumbra.GameData.Data; @@ -13,8 +14,18 @@ using ImGuiClip = OtterGui.ImGuiClip; namespace Penumbra.Api.IpcTester; -public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService +public class CollectionsIpcTester : IUiService, IDisposable { + private readonly IDalamudPluginInterface _pi; + public readonly EventSubscriber ResolveFileChange; + + private ResolvedFileChange _lastResolvedFileChangeType; + private Guid _lastResolvedFileChangeCollection = Guid.Empty; + private string _lastResolvedFileChangeMod = string.Empty; + private string _lastResolvedFileChangeGamePath = string.Empty; + private string _lastResolvedFileChangeOldFilePath = string.Empty; + private string _lastResolvedFileChangeNewFilePath = string.Empty; + private int _objectIdx; private string _collectionIdString = string.Empty; private Guid? _collectionId; @@ -23,10 +34,23 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService private ApiCollectionType _type = ApiCollectionType.Yourself; private Dictionary _collections = []; + private (string, string)[] _resolvedFiles= []; private (string, ChangedItemType, uint)[] _changedItems = []; private PenumbraApiEc _returnCode = PenumbraApiEc.Success; private (Guid Id, string Name)? _oldCollection; + public CollectionsIpcTester(IDalamudPluginInterface pi) + { + _pi = pi; + ResolveFileChange = ResolvedFileChanged.Subscriber(pi, UpdateLastResolvedChange); + ResolveFileChange.Disable(); + } + + public void Dispose() + { + ResolveFileChange.Dispose(); + } + public void Draw() { using var _ = ImRaii.TreeNode("Collections"); @@ -45,11 +69,17 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService return; IpcTester.DrawIntro("Last Return Code", _returnCode.ToString()); + + IpcTester.DrawIntro(ResolvedFileChanged.Label, "Last Resolved File Change"); + ImGui.TextUnformatted(_lastResolvedFileChangeMod.Length > 0 + ? $"{_lastResolvedFileChangeType} of {_lastResolvedFileChangeMod} in {_lastResolvedFileChangeCollection} for game path {_lastResolvedFileChangeGamePath} from {_lastResolvedFileChangeOldFilePath} to {_lastResolvedFileChangeNewFilePath}" + : "None"); + if (_oldCollection != null) ImGui.TextUnformatted(!_oldCollection.HasValue ? "Created" : _oldCollection.ToString()); IpcTester.DrawIntro(GetCollectionsByIdentifier.Label, "Collection Identifier"); - var collectionList = new GetCollectionsByIdentifier(pi).Invoke(_collectionIdString); + var collectionList = new GetCollectionsByIdentifier(_pi).Invoke(_collectionIdString); if (collectionList.Count == 0) { DrawCollection(null); @@ -68,27 +98,27 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService } IpcTester.DrawIntro(GetCollection.Label, "Current Collection"); - DrawCollection(new GetCollection(pi).Invoke(ApiCollectionType.Current)); + DrawCollection(new GetCollection(_pi).Invoke(ApiCollectionType.Current)); IpcTester.DrawIntro(GetCollection.Label, "Default Collection"); - DrawCollection(new GetCollection(pi).Invoke(ApiCollectionType.Default)); + DrawCollection(new GetCollection(_pi).Invoke(ApiCollectionType.Default)); IpcTester.DrawIntro(GetCollection.Label, "Interface Collection"); - DrawCollection(new GetCollection(pi).Invoke(ApiCollectionType.Interface)); + DrawCollection(new GetCollection(_pi).Invoke(ApiCollectionType.Interface)); IpcTester.DrawIntro(GetCollection.Label, "Special Collection"); - DrawCollection(new GetCollection(pi).Invoke(_type)); + DrawCollection(new GetCollection(_pi).Invoke(_type)); IpcTester.DrawIntro(GetCollections.Label, "Collections"); DrawCollectionPopup(); if (ImGui.Button("Get##Collections")) { - _collections = new GetCollections(pi).Invoke(); + _collections = new GetCollections(_pi).Invoke(); ImGui.OpenPopup("Collections"); } IpcTester.DrawIntro(GetCollectionForObject.Label, "Get Object Collection"); - var (valid, individual, effectiveCollection) = new GetCollectionForObject(pi).Invoke(_objectIdx); + var (valid, individual, effectiveCollection) = new GetCollectionForObject(_pi).Invoke(_objectIdx); DrawCollection(effectiveCollection); ImGui.SameLine(); ImGui.TextUnformatted($"({(valid ? "Valid" : "Invalid")} Object{(individual ? ", Individual Assignment)" : ")")}"); @@ -96,24 +126,34 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService IpcTester.DrawIntro(SetCollection.Label, "Set Special Collection"); if (ImGui.Button("Set##SpecialCollection")) (_returnCode, _oldCollection) = - new SetCollection(pi).Invoke(_type, _collectionId.GetValueOrDefault(Guid.Empty), _allowCreation, _allowDeletion); + new SetCollection(_pi).Invoke(_type, _collectionId.GetValueOrDefault(Guid.Empty), _allowCreation, _allowDeletion); ImGui.TableNextColumn(); if (ImGui.Button("Remove##SpecialCollection")) - (_returnCode, _oldCollection) = new SetCollection(pi).Invoke(_type, null, _allowCreation, _allowDeletion); + (_returnCode, _oldCollection) = new SetCollection(_pi).Invoke(_type, null, _allowCreation, _allowDeletion); IpcTester.DrawIntro(SetCollectionForObject.Label, "Set Object Collection"); if (ImGui.Button("Set##ObjectCollection")) - (_returnCode, _oldCollection) = new SetCollectionForObject(pi).Invoke(_objectIdx, _collectionId.GetValueOrDefault(Guid.Empty), + (_returnCode, _oldCollection) = new SetCollectionForObject(_pi).Invoke(_objectIdx, _collectionId.GetValueOrDefault(Guid.Empty), _allowCreation, _allowDeletion); ImGui.TableNextColumn(); if (ImGui.Button("Remove##ObjectCollection")) - (_returnCode, _oldCollection) = new SetCollectionForObject(pi).Invoke(_objectIdx, null, _allowCreation, _allowDeletion); + (_returnCode, _oldCollection) = new SetCollectionForObject(_pi).Invoke(_objectIdx, null, _allowCreation, _allowDeletion); + + IpcTester.DrawIntro(GetResolvedFilesForCollection.Label, "Resolved Files List"); + DrawResolvedFilesPopup(); + if (ImGui.Button("Get##ResolvedFiles")) + { + var files = new GetResolvedFilesForCollection(_pi).Invoke(_collectionId.GetValueOrDefault(Guid.Empty)); + _resolvedFiles = files.Select(kvp => (kvp.Key, kvp.Value)).ToArray(); + ImGui.OpenPopup("Resolved Files List"); + } + IpcTester.DrawIntro(GetChangedItemsForCollection.Label, "Changed Item List"); DrawChangedItemPopup(); if (ImGui.Button("Get##ChangedItems")) { - var items = new GetChangedItemsForCollection(pi).Invoke(_collectionId.GetValueOrDefault(Guid.Empty)); + var items = new GetChangedItemsForCollection(_pi).Invoke(_collectionId.GetValueOrDefault(Guid.Empty)); _changedItems = items.Select(kvp => { var (type, id) = kvp.Value.ToApiObject(); @@ -123,10 +163,33 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService } IpcTester.DrawIntro(RedrawCollectionMembers.Label, "Redraw Collection Members"); if (ImGui.Button("Redraw##ObjectCollection")) - new RedrawCollectionMembers(pi).Invoke(collectionList[0].Id, RedrawType.Redraw); + new RedrawCollectionMembers(_pi).Invoke(collectionList[0].Id, RedrawType.Redraw); } + private void DrawResolvedFilesPopup() + { + // calculate the width by getting the max width of the longest string in each item. + var width = _resolvedFiles.Length > 0 + ? ImGui.CalcTextSize(_resolvedFiles.OrderByDescending(i => i.Item1.Length + i.Item2.Length).Select(i => i.Item1 + i.Item2).First()).X : 500f; + ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(width, 500)); + using var p = ImRaii.Popup("Resolved Files List"); + if (!p) + return; + + using (var table = ImRaii.Table("##ResolvedFiles", 2, ImGuiTableFlags.SizingFixedFit)) + { + if (table) + ImGuiClip.ClippedDraw(_resolvedFiles, t => + { + ImGuiUtil.DrawTableColumn(t.Item1); + ImGuiUtil.DrawTableColumn(t.Item2); + }, ImGui.GetTextLineHeightWithSpacing()); + } + if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused()) + ImGui.CloseCurrentPopup(); + } + private void DrawChangedItemPopup() { ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500)); @@ -186,4 +249,14 @@ public class CollectionsIpcTester(IDalamudPluginInterface pi) : IUiService ImGuiUtil.CopyOnClickSelectable(collection.Value.Id.ToString()); } } + + private void UpdateLastResolvedChange(ResolvedFileChange type, Guid collection, string mod, string gamePath, string oldFilePath, string newFilePath) + { + _lastResolvedFileChangeType = type; + _lastResolvedFileChangeCollection = collection; + _lastResolvedFileChangeMod = mod; + _lastResolvedFileChangeGamePath = gamePath; + _lastResolvedFileChangeOldFilePath = oldFilePath; + _lastResolvedFileChangeNewFilePath = newFilePath; + } } diff --git a/Penumbra/Api/IpcTester/IpcTester.cs b/Penumbra/Api/IpcTester/IpcTester.cs index b03d7e03..60cd2381 100644 --- a/Penumbra/Api/IpcTester/IpcTester.cs +++ b/Penumbra/Api/IpcTester/IpcTester.cs @@ -72,6 +72,7 @@ public class IpcTester( return; Penumbra.Log.Debug("[IPCTester] Subscribed to IPC events for IPC tester."); + collectionsIpcTester.ResolveFileChange.Enable(); gameStateIpcTester.GameObjectResourcePathResolved.Enable(); gameStateIpcTester.CharacterBaseCreated.Enable(); gameStateIpcTester.CharacterBaseCreating.Enable(); @@ -111,6 +112,7 @@ public class IpcTester( Penumbra.Log.Debug("[IPCTester] Unsubscribed from IPC events for IPC tester."); _subscribed = false; + collectionsIpcTester.ResolveFileChange.Disable(); gameStateIpcTester.GameObjectResourcePathResolved.Disable(); gameStateIpcTester.CharacterBaseCreated.Disable(); gameStateIpcTester.CharacterBaseCreating.Disable(); diff --git a/Penumbra/Collections/Cache/CollectionCache.cs b/Penumbra/Collections/Cache/CollectionCache.cs index 8294624b..103f199f 100644 --- a/Penumbra/Collections/Cache/CollectionCache.cs +++ b/Penumbra/Collections/Cache/CollectionCache.cs @@ -1,13 +1,13 @@ using Dalamud.Interface.ImGuiNotification; using OtterGui.Classes; +using OtterGui.Extensions; +using Penumbra.Api.Enums; +using Penumbra.GameData.Data; using Penumbra.Meta.Manipulations; using Penumbra.Mods; -using Penumbra.Communication; using Penumbra.Mods.Editor; using Penumbra.String.Classes; using Penumbra.Util; -using Penumbra.GameData.Data; -using OtterGui.Extensions; namespace Penumbra.Collections.Cache; @@ -23,7 +23,7 @@ public sealed class CollectionCache : IDisposable private readonly CollectionCacheManager _manager; private readonly ModCollection _collection; public readonly CollectionModData ModData = new(); - private readonly SortedList, IIdentifiedObjectData)> _changedItems = []; + private readonly SortedList, IIdentifiedObjectData)> _changedItems = []; public readonly ConcurrentDictionary ResolvedFiles = new(); public readonly CustomResourceCache CustomResources; public readonly MetaCache Meta; @@ -149,20 +149,20 @@ public sealed class CollectionCache : IDisposable { ResolvedFiles.TryAdd(path, new ModPath(Mod.ForcedFiles, fullPath)); CustomResources.Invalidate(path); - InvokeResolvedFileChange(_collection, ResolvedFileChanged.Type.Replaced, path, fullPath, modPath.Path, + InvokeResolvedFileChange(_collection, ResolvedFileChange.Replaced, path, fullPath, modPath.Path, Mod.ForcedFiles); } else { CustomResources.Invalidate(path); - InvokeResolvedFileChange(_collection, ResolvedFileChanged.Type.Removed, path, FullPath.Empty, modPath.Path, null); + InvokeResolvedFileChange(_collection, ResolvedFileChange.Removed, path, FullPath.Empty, modPath.Path, null); } } else if (fullPath.FullName.Length > 0) { ResolvedFiles.TryAdd(path, new ModPath(Mod.ForcedFiles, fullPath)); CustomResources.Invalidate(path); - InvokeResolvedFileChange(_collection, ResolvedFileChanged.Type.Added, path, fullPath, FullPath.Empty, Mod.ForcedFiles); + InvokeResolvedFileChange(_collection, ResolvedFileChange.Added, path, fullPath, FullPath.Empty, Mod.ForcedFiles); } } @@ -186,10 +186,9 @@ public sealed class CollectionCache : IDisposable { CustomResources.Invalidate(path); if (mp.Mod != mod) - Penumbra.Log.Warning( - $"Invalid mod state, removing {mod.Name} and associated file {path} returned current mod {mp.Mod.Name}."); + Penumbra.Log.Warning($"Invalid mod state, removing {mod.Name} and associated file {path} returned current mod {mp.Mod.Name}."); else - _manager.ResolvedFileChanged.Invoke(_collection, ResolvedFileChanged.Type.Removed, path, FullPath.Empty, mp.Path, mp.Mod); + _manager.ResolvedFileChanged.Invoke(_collection, ResolvedFileChange.Removed, path, FullPath.Empty, mp.Path, mp.Mod); } } @@ -272,7 +271,7 @@ public sealed class CollectionCache : IDisposable } /// Invoke only if not in a full recalculation. - private void InvokeResolvedFileChange(ModCollection collection, ResolvedFileChanged.Type type, Utf8GamePath key, FullPath value, + private void InvokeResolvedFileChange(ModCollection collection, ResolvedFileChange type, Utf8GamePath key, FullPath value, FullPath old, IMod? mod) { if (Calculating == -1) @@ -315,7 +314,7 @@ public sealed class CollectionCache : IDisposable { ModData.AddPath(mod, path); CustomResources.Invalidate(path); - InvokeResolvedFileChange(_collection, ResolvedFileChanged.Type.Added, path, file, FullPath.Empty, mod); + InvokeResolvedFileChange(_collection, ResolvedFileChange.Added, path, file, FullPath.Empty, mod); return; } @@ -330,7 +329,7 @@ public sealed class CollectionCache : IDisposable ResolvedFiles[path] = new ModPath(mod, file); ModData.AddPath(mod, path); CustomResources.Invalidate(path); - InvokeResolvedFileChange(_collection, ResolvedFileChanged.Type.Replaced, path, file, modPath.Path, mod); + InvokeResolvedFileChange(_collection, ResolvedFileChange.Replaced, path, file, modPath.Path, mod); } } catch (Exception ex) diff --git a/Penumbra/Collections/Cache/CollectionCacheManager.cs b/Penumbra/Collections/Cache/CollectionCacheManager.cs index ec48e608..8445bdcc 100644 --- a/Penumbra/Collections/Cache/CollectionCacheManager.cs +++ b/Penumbra/Collections/Cache/CollectionCacheManager.cs @@ -170,7 +170,7 @@ public class CollectionCacheManager : IDisposable, IService cache.Calculating = Environment.CurrentManagedThreadId; try { - ResolvedFileChanged.Invoke(collection, ResolvedFileChanged.Type.FullRecomputeStart, Utf8GamePath.Empty, FullPath.Empty, + ResolvedFileChanged.Invoke(collection, ResolvedFileChange.FullRecomputeStart, Utf8GamePath.Empty, FullPath.Empty, FullPath.Empty, null); cache.ResolvedFiles.Clear(); cache.Meta.Reset(); @@ -189,7 +189,7 @@ public class CollectionCacheManager : IDisposable, IService collection.Counters.IncrementChange(); MetaFileManager.ApplyDefaultFiles(collection); - ResolvedFileChanged.Invoke(collection, ResolvedFileChanged.Type.FullRecomputeFinished, Utf8GamePath.Empty, FullPath.Empty, + ResolvedFileChanged.Invoke(collection, ResolvedFileChange.FullRecomputeFinished, Utf8GamePath.Empty, FullPath.Empty, FullPath.Empty, null); } diff --git a/Penumbra/Communication/ResolvedFileChanged.cs b/Penumbra/Communication/ResolvedFileChanged.cs index 0c91a18b..fd8572df 100644 --- a/Penumbra/Communication/ResolvedFileChanged.cs +++ b/Penumbra/Communication/ResolvedFileChanged.cs @@ -1,4 +1,6 @@ using OtterGui.Classes; +using Penumbra.Api.Api; +using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.Mods.Editor; using Penumbra.String.Classes; @@ -16,20 +18,14 @@ namespace Penumbra.Communication; /// Parameter is the mod responsible for the new redirection if any. /// public sealed class ResolvedFileChanged() - : EventWrapper( + : EventWrapper( nameof(ResolvedFileChanged)) { - public enum Type - { - Added, - Removed, - Replaced, - FullRecomputeStart, - FullRecomputeFinished, - } - public enum Priority { + /// + ApiResolvedFile = int.MinValue, + /// DalamudSubstitutionProvider = 0, diff --git a/Penumbra/Interop/Services/SchedulerResourceManagementService.cs b/Penumbra/Interop/Services/SchedulerResourceManagementService.cs index b7f57a44..b1e5579e 100644 --- a/Penumbra/Interop/Services/SchedulerResourceManagementService.cs +++ b/Penumbra/Interop/Services/SchedulerResourceManagementService.cs @@ -4,6 +4,7 @@ using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.System.Scheduler.Resource; using Lumina.Excel.Sheets; using OtterGui.Services; +using Penumbra.Api.Enums; using Penumbra.Collections; using Penumbra.Communication; using Penumbra.GameData; @@ -41,15 +42,15 @@ public unsafe class SchedulerResourceManagementService : IService, IDisposable interop.InitializeFromAttributes(this); } - private void OnResolvedFileChange(ModCollection collection, ResolvedFileChanged.Type type, Utf8GamePath gamePath, FullPath oldPath, + private void OnResolvedFileChange(ModCollection collection, ResolvedFileChange type, Utf8GamePath gamePath, FullPath oldPath, FullPath newPath, IMod? mod) { switch (type) { - case ResolvedFileChanged.Type.Added: + case ResolvedFileChange.Added: CheckFile(gamePath); return; - case ResolvedFileChanged.Type.FullRecomputeFinished: + case ResolvedFileChange.FullRecomputeFinished: foreach (var path in collection.ResolvedFiles.Keys) CheckFile(path); return;