mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Fix some issues with removing mods from collection caches.
This commit is contained in:
parent
cbda4614a9
commit
3f03712e24
5 changed files with 144 additions and 29 deletions
|
|
@ -24,6 +24,7 @@ public class CollectionCache : IDisposable
|
||||||
{
|
{
|
||||||
private readonly CollectionCacheManager _manager;
|
private readonly CollectionCacheManager _manager;
|
||||||
private readonly ModCollection _collection;
|
private readonly ModCollection _collection;
|
||||||
|
public readonly CollectionModData ModData = new();
|
||||||
public readonly SortedList<string, (SingleArray<IMod>, object?)> _changedItems = new();
|
public readonly SortedList<string, (SingleArray<IMod>, object?)> _changedItems = new();
|
||||||
public readonly Dictionary<Utf8GamePath, ModPath> ResolvedFiles = new();
|
public readonly Dictionary<Utf8GamePath, ModPath> ResolvedFiles = new();
|
||||||
public readonly MetaCache Meta;
|
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>
|
/// <summary> Force a file to be resolved to a specific path regardless of conflicts. </summary>
|
||||||
internal void ForceFile(Utf8GamePath path, FullPath fullPath)
|
internal void ForceFile(Utf8GamePath path, FullPath fullPath)
|
||||||
{
|
{
|
||||||
if (CheckFullPath(path, fullPath))
|
if (!CheckFullPath(path, fullPath))
|
||||||
ResolvedFiles[path] = new ModPath(Mod.ForcedFiles, 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>
|
/// <summary> Force a file resolve to be removed. </summary>
|
||||||
internal void RemoveFile(Utf8GamePath path)
|
internal void RemovePath(Utf8GamePath path)
|
||||||
=> ResolvedFiles.Remove(path);
|
{
|
||||||
|
if (ResolvedFiles.Remove(path, out var modPath))
|
||||||
|
ModData.RemovePath(modPath.Mod, path);
|
||||||
|
}
|
||||||
|
|
||||||
public void ReloadMod(IMod mod, bool addMetaChanges)
|
public void ReloadMod(IMod mod, bool addMetaChanges)
|
||||||
{
|
{
|
||||||
|
|
@ -141,20 +150,19 @@ public class CollectionCache : IDisposable
|
||||||
public void RemoveMod(IMod mod, bool addMetaChanges)
|
public void RemoveMod(IMod mod, bool addMetaChanges)
|
||||||
{
|
{
|
||||||
var conflicts = Conflicts(mod);
|
var conflicts = Conflicts(mod);
|
||||||
|
var (paths, manipulations) = ModData.RemoveMod(mod);
|
||||||
foreach (var (path, _) in mod.AllSubMods.SelectMany(s => s.Files.Concat(s.FileSwaps)))
|
foreach (var path in paths)
|
||||||
{
|
{
|
||||||
if (!ResolvedFiles.TryGetValue(path, out var modPath))
|
if (ResolvedFiles.Remove(path, out var mp) && mp.Mod != mod)
|
||||||
continue;
|
Penumbra.Log.Warning(
|
||||||
|
$"Invalid mod state, removing {mod.Name} and associated file {path} returned current mod {mp.Mod.Name}.");
|
||||||
if (modPath.Mod == mod)
|
|
||||||
ResolvedFiles.Remove(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var manipulation in mod.AllSubMods.SelectMany(s => s.Manipulations))
|
foreach (var manipulation in manipulations)
|
||||||
{
|
{
|
||||||
if (Meta.TryGetValue(manipulation, out var registeredMod) && registeredMod == mod)
|
if (Meta.RevertMod(manipulation, out var mp) && mp != mod)
|
||||||
Meta.RevertMod(manipulation);
|
Penumbra.Log.Warning(
|
||||||
|
$"Invalid mod state, removing {mod.Name} and associated manipulation {manipulation} returned current mod {mp.Name}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_conflicts.Remove(mod);
|
_conflicts.Remove(mod);
|
||||||
|
|
@ -247,7 +255,10 @@ public class CollectionCache : IDisposable
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ResolvedFiles.TryAdd(path, new ModPath(mod, file)))
|
if (ResolvedFiles.TryAdd(path, new ModPath(mod, file)))
|
||||||
|
{
|
||||||
|
ModData.AddPath(mod, path);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var modPath = ResolvedFiles[path];
|
var modPath = ResolvedFiles[path];
|
||||||
// Lower prioritized option in the same mod.
|
// Lower prioritized option in the same mod.
|
||||||
|
|
@ -255,7 +266,11 @@ public class CollectionCache : IDisposable
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (AddConflict(path, mod, modPath.Mod))
|
if (AddConflict(path, mod, modPath.Mod))
|
||||||
|
{
|
||||||
|
ModData.RemovePath(modPath.Mod, path);
|
||||||
ResolvedFiles[path] = new ModPath(mod, file);
|
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))
|
if (!Meta.TryGetValue(manip, out var existingMod))
|
||||||
{
|
{
|
||||||
Meta.ApplyMod(manip, mod);
|
Meta.ApplyMod(manip, mod);
|
||||||
|
ModData.AddManip(mod, manip);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -340,7 +356,11 @@ public class CollectionCache : IDisposable
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (AddConflict(manip, mod, existingMod))
|
if (AddConflict(manip, mod, existingMod))
|
||||||
|
{
|
||||||
|
ModData.RemoveManip(existingMod, manip);
|
||||||
Meta.ApplyMod(manip, mod);
|
Meta.ApplyMod(manip, mod);
|
||||||
|
ModData.AddManip(mod, manip);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
62
Penumbra/Collections/Cache/CollectionModData.cs
Normal file
62
Penumbra/Collections/Cache/CollectionModData.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,7 +27,7 @@ public readonly struct ImcCache : IDisposable
|
||||||
{
|
{
|
||||||
foreach( var (path, file) in _imcFiles )
|
foreach( var (path, file) in _imcFiles )
|
||||||
{
|
{
|
||||||
collection._cache!.RemoveFile( path );
|
collection._cache!.RemovePath( path );
|
||||||
file.Reset();
|
file.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
if (!_manager.CharacterUtility.Ready)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ using FFXIVClientStructs.FFXIV.Client.System.Resource;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
|
using OtterGui.Raii;
|
||||||
using OtterGui.Widgets;
|
using OtterGui.Widgets;
|
||||||
using Penumbra.Api;
|
using Penumbra.Api;
|
||||||
using Penumbra.Collections.Manager;
|
using Penumbra.Collections.Manager;
|
||||||
|
|
@ -20,9 +21,11 @@ using Penumbra.Import.Structs;
|
||||||
using Penumbra.Interop.ResourceLoading;
|
using Penumbra.Interop.ResourceLoading;
|
||||||
using Penumbra.Interop.PathResolving;
|
using Penumbra.Interop.PathResolving;
|
||||||
using Penumbra.Interop.Structs;
|
using Penumbra.Interop.Structs;
|
||||||
|
using Penumbra.Mods;
|
||||||
using Penumbra.Mods.Manager;
|
using Penumbra.Mods.Manager;
|
||||||
using Penumbra.Services;
|
using Penumbra.Services;
|
||||||
using Penumbra.String;
|
using Penumbra.String;
|
||||||
|
using Penumbra.UI.Classes;
|
||||||
using Penumbra.Util;
|
using Penumbra.Util;
|
||||||
using static OtterGui.Raii.ImRaii;
|
using static OtterGui.Raii.ImRaii;
|
||||||
using CharacterBase = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBase;
|
using CharacterBase = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBase;
|
||||||
|
|
@ -122,6 +125,8 @@ public class DebugTab : Window, ITab
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
DrawActorsDebug();
|
DrawActorsDebug();
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
|
DrawCollectionCaches();
|
||||||
|
ImGui.NewLine();
|
||||||
DrawDebugCharacterUtility();
|
DrawDebugCharacterUtility();
|
||||||
ImGui.NewLine();
|
ImGui.NewLine();
|
||||||
DrawStainTemplates();
|
DrawStainTemplates();
|
||||||
|
|
@ -138,6 +143,45 @@ public class DebugTab : Window, ITab
|
||||||
ImGui.NewLine();
|
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>
|
/// <summary> Draw general information about mod and collection state. </summary>
|
||||||
private void DrawDebugTabGeneral()
|
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);
|
var issues = _modManager.WithIndex().Count(p => p.Index != p.Value.Index);
|
||||||
using (var tree = TreeNode($"Mods ({issues} Issues)###Mods"))
|
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);
|
using var table = Table("##DebugFramework", 2, ImGuiTableFlags.SizingFixedFit);
|
||||||
if (table)
|
if (table)
|
||||||
{
|
{
|
||||||
foreach(var important in _framework.Important)
|
foreach (var important in _framework.Important)
|
||||||
PrintValue(important, "Immediate");
|
PrintValue(important, "Immediate");
|
||||||
|
|
||||||
foreach (var (onTick, idx) in _framework.OnTick.WithIndex())
|
foreach (var (onTick, idx) in _framework.OnTick.WithIndex())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue