Fix some issues with removing mods from collection caches.

This commit is contained in:
Ottermandias 2023-05-11 17:55:48 +02:00
parent cbda4614a9
commit 3f03712e24
5 changed files with 144 additions and 29 deletions

View file

@ -24,6 +24,7 @@ public class CollectionCache : IDisposable
{
private readonly CollectionCacheManager _manager;
private readonly ModCollection _collection;
public readonly CollectionModData ModData = new();
public readonly SortedList<string, (SingleArray<IMod>, object?)> _changedItems = new();
public readonly Dictionary<Utf8GamePath, ModPath> ResolvedFiles = new();
public readonly MetaCache Meta;
@ -124,13 +125,21 @@ public class CollectionCache : IDisposable
/// <summary> Force a file to be resolved to a specific path regardless of conflicts. </summary>
internal void ForceFile(Utf8GamePath path, FullPath fullPath)
{
if (CheckFullPath(path, fullPath))
ResolvedFiles[path] = new ModPath(Mod.ForcedFiles, fullPath);
if (!CheckFullPath(path, fullPath))
return;
if (ResolvedFiles.Remove(path, out var modPath))
ModData.RemovePath(modPath.Mod, path);
ResolvedFiles.Add(path, new ModPath(Mod.ForcedFiles, fullPath));
ModData.AddPath(Mod.ForcedFiles, path);
}
/// <summary> Force a file resolve to be removed. </summary>
internal void RemoveFile(Utf8GamePath path)
=> ResolvedFiles.Remove(path);
internal void RemovePath(Utf8GamePath path)
{
if (ResolvedFiles.Remove(path, out var modPath))
ModData.RemovePath(modPath.Mod, path);
}
public void ReloadMod(IMod mod, bool addMetaChanges)
{
@ -141,20 +150,19 @@ public class CollectionCache : IDisposable
public void RemoveMod(IMod mod, bool addMetaChanges)
{
var conflicts = Conflicts(mod);
foreach (var (path, _) in mod.AllSubMods.SelectMany(s => s.Files.Concat(s.FileSwaps)))
var (paths, manipulations) = ModData.RemoveMod(mod);
foreach (var path in paths)
{
if (!ResolvedFiles.TryGetValue(path, out var modPath))
continue;
if (modPath.Mod == mod)
ResolvedFiles.Remove(path);
if (ResolvedFiles.Remove(path, out var mp) && mp.Mod != mod)
Penumbra.Log.Warning(
$"Invalid mod state, removing {mod.Name} and associated file {path} returned current mod {mp.Mod.Name}.");
}
foreach (var manipulation in mod.AllSubMods.SelectMany(s => s.Manipulations))
foreach (var manipulation in manipulations)
{
if (Meta.TryGetValue(manipulation, out var registeredMod) && registeredMod == mod)
Meta.RevertMod(manipulation);
if (Meta.RevertMod(manipulation, out var mp) && mp != mod)
Penumbra.Log.Warning(
$"Invalid mod state, removing {mod.Name} and associated manipulation {manipulation} returned current mod {mp.Name}.");
}
_conflicts.Remove(mod);
@ -247,7 +255,10 @@ public class CollectionCache : IDisposable
return;
if (ResolvedFiles.TryAdd(path, new ModPath(mod, file)))
{
ModData.AddPath(mod, path);
return;
}
var modPath = ResolvedFiles[path];
// Lower prioritized option in the same mod.
@ -255,7 +266,11 @@ public class CollectionCache : IDisposable
return;
if (AddConflict(path, mod, modPath.Mod))
{
ModData.RemovePath(modPath.Mod, path);
ResolvedFiles[path] = new ModPath(mod, file);
ModData.AddPath(mod, path);
}
}
@ -332,6 +347,7 @@ public class CollectionCache : IDisposable
if (!Meta.TryGetValue(manip, out var existingMod))
{
Meta.ApplyMod(manip, mod);
ModData.AddManip(mod, manip);
return;
}
@ -340,7 +356,11 @@ public class CollectionCache : IDisposable
return;
if (AddConflict(manip, mod, existingMod))
{
ModData.RemoveManip(existingMod, manip);
Meta.ApplyMod(manip, mod);
ModData.AddManip(mod, manip);
}
}

View file

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods;
using Penumbra.String.Classes;
namespace Penumbra.Collections.Cache;
public class CollectionModData
{
private readonly Dictionary<IMod, (HashSet<Utf8GamePath>, HashSet<MetaManipulation>)> _data = new();
public IEnumerable<(IMod, IReadOnlySet<Utf8GamePath>, IReadOnlySet<MetaManipulation>)> Data
=> _data.Select(kvp => (kvp.Key, (IReadOnlySet<Utf8GamePath>)kvp.Value.Item1, (IReadOnlySet<MetaManipulation>)kvp.Value.Item2));
public (IReadOnlyCollection<Utf8GamePath> Paths, IReadOnlyCollection<MetaManipulation> Manipulations) RemoveMod(IMod mod)
{
if (_data.Remove(mod, out var data))
return data;
return (Array.Empty<Utf8GamePath>(), Array.Empty<MetaManipulation>());
}
public void AddPath(IMod mod, Utf8GamePath path)
{
if (_data.TryGetValue(mod, out var data))
{
data.Item1.Add(path);
}
else
{
data = (new HashSet<Utf8GamePath> { path }, new HashSet<MetaManipulation>());
_data.Add(mod, data);
}
}
public void AddManip(IMod mod, MetaManipulation manipulation)
{
if (_data.TryGetValue(mod, out var data))
{
data.Item2.Add(manipulation);
}
else
{
data = (new HashSet<Utf8GamePath>(), new HashSet<MetaManipulation> { manipulation });
_data.Add(mod, data);
}
}
public void RemovePath(IMod mod, Utf8GamePath path)
{
if (_data.TryGetValue(mod, out var data) && data.Item1.Remove(path) && data.Item1.Count == 0 && data.Item2.Count == 0)
_data.Remove(mod);
}
public void RemoveManip(IMod mod, MetaManipulation manip)
{
if (_data.TryGetValue(mod, out var data) && data.Item2.Remove(manip) && data.Item1.Count == 0 && data.Item2.Count == 0)
_data.Remove(mod);
}
}

View file

@ -27,7 +27,7 @@ public readonly struct ImcCache : IDisposable
{
foreach( var (path, file) in _imcFiles )
{
collection._cache!.RemoveFile( path );
collection._cache!.RemovePath( path );
file.Reset();
}

View file

@ -108,9 +108,9 @@ public class MetaCache : IDisposable, IEnumerable<KeyValuePair<MetaManipulation,
};
}
public bool RevertMod(MetaManipulation manip)
public bool RevertMod(MetaManipulation manip, [NotNullWhen(true)] out IMod? mod)
{
var ret = _manipulations.Remove(manip);
var ret = _manipulations.Remove(manip, out mod);
if (!_manager.CharacterUtility.Ready)
return ret;

View file

@ -11,6 +11,7 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource;
using ImGuiNET;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using OtterGui.Widgets;
using Penumbra.Api;
using Penumbra.Collections.Manager;
@ -20,9 +21,11 @@ using Penumbra.Import.Structs;
using Penumbra.Interop.ResourceLoading;
using Penumbra.Interop.PathResolving;
using Penumbra.Interop.Structs;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
using Penumbra.Services;
using Penumbra.String;
using Penumbra.UI.Classes;
using Penumbra.Util;
using static OtterGui.Raii.ImRaii;
using CharacterBase = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBase;
@ -122,6 +125,8 @@ public class DebugTab : Window, ITab
ImGui.NewLine();
DrawActorsDebug();
ImGui.NewLine();
DrawCollectionCaches();
ImGui.NewLine();
DrawDebugCharacterUtility();
ImGui.NewLine();
DrawStainTemplates();
@ -138,6 +143,45 @@ public class DebugTab : Window, ITab
ImGui.NewLine();
}
private void DrawCollectionCaches()
{
if (!ImGui.CollapsingHeader($"Collections ({_collectionManager.Caches.Count}/{_collectionManager.Storage.Count - 1} Caches)###Collections"))
return;
foreach (var collection in _collectionManager.Storage)
{
if (collection.HasCache)
{
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.FolderExpanded.Value());
using var node = TreeNode(collection.AnonymizedName);
if (!node)
continue;
color.Pop();
foreach (var (mod, paths, manips) in collection._cache!.ModData.Data.OrderBy(t => t.Item1.Name))
{
using var id = mod is TemporaryMod t ? PushId(t.Priority) : PushId(((Mod)mod).ModPath.Name);
using var node2 = TreeNode(mod.Name.Text);
if (!node2)
continue;
foreach (var path in paths)
TreeNode(path.ToString(), ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf).Dispose();
foreach (var manip in manips)
TreeNode(manip.ToString(), ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf).Dispose();
}
}
else
{
using var color = ImRaii.PushColor(ImGuiCol.Text, ColorId.UndefinedMod.Value());
TreeNode(collection.AnonymizedName, ImGuiTreeNodeFlags.Bullet | ImGuiTreeNodeFlags.Leaf).Dispose();
}
}
}
/// <summary> Draw general information about mod and collection state. </summary>
private void DrawDebugTabGeneral()
{
@ -171,17 +215,6 @@ public class DebugTab : Window, ITab
}
}
using (var tree = TreeNode($"Collections ({_collectionManager.Caches.Count}/{_collectionManager.Storage.Count - 1})###Collections"))
{
if (tree)
{
using var table = Table("##DebugCollectionsTable", 2, ImGuiTableFlags.SizingFixedFit);
if (table)
foreach (var collection in _collectionManager.Storage)
PrintValue(collection.Name, collection.HasCache.ToString());
}
}
var issues = _modManager.WithIndex().Count(p => p.Index != p.Value.Index);
using (var tree = TreeNode($"Mods ({issues} Issues)###Mods"))
{
@ -245,7 +278,7 @@ public class DebugTab : Window, ITab
using var table = Table("##DebugFramework", 2, ImGuiTableFlags.SizingFixedFit);
if (table)
{
foreach(var important in _framework.Important)
foreach (var important in _framework.Important)
PrintValue(important, "Immediate");
foreach (var (onTick, idx) in _framework.OnTick.WithIndex())