Track CollectionCache ResolvedFiles

This commit is contained in:
Cordelia Mist 2025-12-29 17:08:47 -08:00
parent 13500264b7
commit 2b60f204e8
9 changed files with 198 additions and 75 deletions

View file

@ -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<Guid, string> 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<string, (string ModDirectory, string ModName)[]> CheckCurrentChangedItemFunc()
{
var weakRef = new WeakReference<CollectionManager>(collections);
var weakRef = new WeakReference<CollectionManager>(_collections);
return s =>
{
if (!weakRef.TryGetTarget(out var c))
@ -45,11 +68,31 @@ public class CollectionApi(CollectionManager collections, ApiHelpers helpers) :
};
}
public Dictionary<string, string> 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<string, object?> 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<Guid, string>?();
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<Guid, string>?();
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());
}
}

View file

@ -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;
}

View file

@ -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),

View file

@ -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<ResolvedFileChange, Guid, string, string, string, string> 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<Guid, string> _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;
}
}

View file

@ -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();

View file

@ -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<string, (SingleArray<IMod>, IIdentifiedObjectData)> _changedItems = [];
private readonly SortedList<string, (SingleArray<IMod>, IIdentifiedObjectData)> _changedItems = [];
public readonly ConcurrentDictionary<Utf8GamePath, ModPath> 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
}
/// <summary> Invoke only if not in a full recalculation. </summary>
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)

View file

@ -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);
}

View file

@ -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;
/// <item>Parameter is the mod responsible for the new redirection if any. </item>
/// </list> </summary>
public sealed class ResolvedFileChanged()
: EventWrapper<ModCollection, ResolvedFileChanged.Type, Utf8GamePath, FullPath, FullPath, IMod?, ResolvedFileChanged.Priority>(
: EventWrapper<ModCollection, ResolvedFileChange, Utf8GamePath, FullPath, FullPath, IMod?, ResolvedFileChanged.Priority>(
nameof(ResolvedFileChanged))
{
public enum Type
{
Added,
Removed,
Replaced,
FullRecomputeStart,
FullRecomputeFinished,
}
public enum Priority
{
/// <seealso cref="CollectionApi.OnResolvedFileChange"/>
ApiResolvedFile = int.MinValue,
/// <seealso cref="Api.DalamudSubstitutionProvider.OnResolvedFileChange"/>
DalamudSubstitutionProvider = 0,

View file

@ -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;