From 94fdd848b718ef2c7e932faff6b108f7ed7287d8 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sat, 8 Jun 2024 16:06:37 +0200 Subject: [PATCH] Expand on MetaDictionary to use separate dictionaries. --- Penumbra/Api/Api/TemporaryApi.cs | 4 +- Penumbra/Meta/Manipulations/MetaDictionary.cs | 244 +++++++++++++++++- Penumbra/Mods/Editor/IMod.cs | 2 +- Penumbra/Mods/Editor/ModMerger.cs | 2 +- Penumbra/Mods/Editor/ModMetaEditor.cs | 2 +- Penumbra/Mods/Groups/IModGroup.cs | 2 +- Penumbra/Mods/Groups/ImcModGroup.cs | 2 +- Penumbra/Mods/Groups/MultiModGroup.cs | 2 +- Penumbra/Mods/Groups/SingleModGroup.cs | 2 +- Penumbra/Mods/ItemSwap/ItemSwapContainer.cs | 21 +- .../Manager/OptionEditor/ModGroupEditor.cs | 2 +- Penumbra/Mods/Mod.cs | 2 +- Penumbra/Mods/SubMods/DefaultSubMod.cs | 2 +- Penumbra/Mods/SubMods/OptionSubMod.cs | 2 +- Penumbra/Mods/SubMods/SubMod.cs | 2 +- 15 files changed, 257 insertions(+), 36 deletions(-) diff --git a/Penumbra/Api/Api/TemporaryApi.cs b/Penumbra/Api/Api/TemporaryApi.cs index 09a9b7c4..995ec388 100644 --- a/Penumbra/Api/Api/TemporaryApi.cs +++ b/Penumbra/Api/Api/TemporaryApi.cs @@ -174,8 +174,8 @@ public class TemporaryApi( return false; } - manips = new MetaDictionary(manipArray!.Length); - foreach (var manip in manipArray.Where(m => m.Validate())) + manips = []; + foreach (var manip in manipArray!.Where(m => m.Validate())) { if (manips.Add(manip)) continue; diff --git a/Penumbra/Meta/Manipulations/MetaDictionary.cs b/Penumbra/Meta/Manipulations/MetaDictionary.cs index 65252c5d..b0b7f011 100644 --- a/Penumbra/Meta/Manipulations/MetaDictionary.cs +++ b/Penumbra/Meta/Manipulations/MetaDictionary.cs @@ -1,19 +1,237 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Penumbra.GameData.Structs; +using Penumbra.Util; using ImcEntry = Penumbra.GameData.Structs.ImcEntry; namespace Penumbra.Meta.Manipulations; [JsonConverter(typeof(Converter))] -public sealed class MetaDictionary : HashSet +public sealed class MetaDictionary : IEnumerable { - public MetaDictionary() - { } + private readonly Dictionary _imc = []; + private readonly Dictionary _eqp = []; + private readonly Dictionary _eqdp = []; + private readonly Dictionary _est = []; + private readonly Dictionary _rsp = []; + private readonly Dictionary _gmp = []; + private readonly HashSet _globalEqp = []; - public MetaDictionary(int capacity) - : base(capacity) - { } + public int Count { get; private set; } + + public void Clear() + { + _imc.Clear(); + _eqp.Clear(); + _eqdp.Clear(); + _est.Clear(); + _rsp.Clear(); + _gmp.Clear(); + _globalEqp.Clear(); + } + + public IEnumerator GetEnumerator() + => _imc.Select(kvp => new MetaManipulation(new ImcManipulation(kvp.Key, kvp.Value))) + .Concat(_eqp.Select(kvp => new MetaManipulation(new EqpManipulation(kvp.Key, kvp.Value)))) + .Concat(_eqdp.Select(kvp => new MetaManipulation(new EqdpManipulation(kvp.Key, kvp.Value)))) + .Concat(_est.Select(kvp => new MetaManipulation(new EstManipulation(kvp.Key, kvp.Value)))) + .Concat(_rsp.Select(kvp => new MetaManipulation(new RspManipulation(kvp.Key, kvp.Value)))) + .Concat(_gmp.Select(kvp => new MetaManipulation(new GmpManipulation(kvp.Key, kvp.Value)))) + .Concat(_globalEqp.Select(manip => new MetaManipulation(manip))).GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + public bool Add(MetaManipulation manip) + { + var ret = manip.ManipulationType switch + { + MetaManipulation.Type.Imc => _imc.TryAdd(manip.Imc.Identifier, manip.Imc.Entry), + MetaManipulation.Type.Eqdp => _eqdp.TryAdd(manip.Eqdp.Identifier, manip.Eqdp.Entry), + MetaManipulation.Type.Eqp => _eqp.TryAdd(manip.Eqp.Identifier, manip.Eqp.Entry), + MetaManipulation.Type.Est => _est.TryAdd(manip.Est.Identifier, manip.Est.Entry), + MetaManipulation.Type.Gmp => _gmp.TryAdd(manip.Gmp.Identifier, manip.Gmp.Entry), + MetaManipulation.Type.Rsp => _rsp.TryAdd(manip.Rsp.Identifier, manip.Rsp.Entry), + MetaManipulation.Type.GlobalEqp => _globalEqp.Add(manip.GlobalEqp), + _ => false, + }; + + if (ret) + ++Count; + return ret; + } + + public bool TryAdd(ImcIdentifier identifier, ImcEntry entry) + { + if (!_imc.TryAdd(identifier, entry)) + return false; + + ++Count; + return true; + } + + public bool TryAdd(EqpIdentifier identifier, EqpEntry entry) + { + if (!_eqp.TryAdd(identifier, entry)) + return false; + + ++Count; + return true; + } + + public bool TryAdd(EqdpIdentifier identifier, EqdpEntry entry) + { + if (!_eqdp.TryAdd(identifier, entry)) + return false; + + ++Count; + return true; + } + + public bool TryAdd(EstIdentifier identifier, EstEntry entry) + { + if (!_est.TryAdd(identifier, entry)) + return false; + + ++Count; + return true; + } + + public bool TryAdd(GmpIdentifier identifier, GmpEntry entry) + { + if (!_gmp.TryAdd(identifier, entry)) + return false; + + ++Count; + return true; + } + + public bool TryAdd(RspIdentifier identifier, RspEntry entry) + { + if (!_rsp.TryAdd(identifier, entry)) + return false; + + ++Count; + return true; + } + + public bool TryAdd(GlobalEqpManipulation identifier) + { + if (!_globalEqp.Add(identifier)) + return false; + + ++Count; + return true; + } + + public bool Remove(MetaManipulation manip) + { + var ret = manip.ManipulationType switch + { + MetaManipulation.Type.Imc => _imc.Remove(manip.Imc.Identifier), + MetaManipulation.Type.Eqdp => _eqdp.Remove(manip.Eqdp.Identifier), + MetaManipulation.Type.Eqp => _eqp.Remove(manip.Eqp.Identifier), + MetaManipulation.Type.Est => _est.Remove(manip.Est.Identifier), + MetaManipulation.Type.Gmp => _gmp.Remove(manip.Gmp.Identifier), + MetaManipulation.Type.Rsp => _rsp.Remove(manip.Rsp.Identifier), + MetaManipulation.Type.GlobalEqp => _globalEqp.Remove(manip.GlobalEqp), + _ => false, + }; + if (ret) + --Count; + return ret; + } + + public void UnionWith(IEnumerable manips) + { + foreach (var manip in manips) + Add(manip); + } + + public bool TryGetValue(MetaManipulation identifier, out MetaManipulation oldValue) + { + switch (identifier.ManipulationType) + { + case MetaManipulation.Type.Imc: + if (_imc.TryGetValue(identifier.Imc.Identifier, out var oldImc)) + { + oldValue = new MetaManipulation(new ImcManipulation(identifier.Imc.Identifier, oldImc)); + return true; + } + + break; + case MetaManipulation.Type.Eqdp: + if (_eqp.TryGetValue(identifier.Eqp.Identifier, out var oldEqdp)) + { + oldValue = new MetaManipulation(new EqpManipulation(identifier.Eqp.Identifier, oldEqdp)); + return true; + } + + break; + case MetaManipulation.Type.Eqp: + if (_eqdp.TryGetValue(identifier.Eqdp.Identifier, out var oldEqp)) + { + oldValue = new MetaManipulation(new EqdpManipulation(identifier.Eqdp.Identifier, oldEqp)); + return true; + } + + break; + case MetaManipulation.Type.Est: + if (_est.TryGetValue(identifier.Est.Identifier, out var oldEst)) + { + oldValue = new MetaManipulation(new EstManipulation(identifier.Est.Identifier, oldEst)); + return true; + } + + break; + case MetaManipulation.Type.Gmp: + if (_gmp.TryGetValue(identifier.Gmp.Identifier, out var oldGmp)) + { + oldValue = new MetaManipulation(new GmpManipulation(identifier.Gmp.Identifier, oldGmp)); + return true; + } + + break; + case MetaManipulation.Type.Rsp: + if (_rsp.TryGetValue(identifier.Rsp.Identifier, out var oldRsp)) + { + oldValue = new MetaManipulation(new RspManipulation(identifier.Rsp.Identifier, oldRsp)); + return true; + } + + break; + case MetaManipulation.Type.GlobalEqp: + if (_globalEqp.TryGetValue(identifier.GlobalEqp, out var oldGlobalEqp)) + { + oldValue = new MetaManipulation(oldGlobalEqp); + return true; + } + + break; + } + + oldValue = default; + return false; + } + + public void SetTo(MetaDictionary other) + { + _imc.SetTo(other._imc); + _eqp.SetTo(other._eqp); + _eqdp.SetTo(other._eqdp); + _est.SetTo(other._est); + _rsp.SetTo(other._rsp); + _gmp.SetTo(other._gmp); + _globalEqp.SetTo(other._globalEqp); + Count = _imc.Count + _eqp.Count + _eqdp.Count + _est.Count + _rsp.Count + _gmp.Count + _globalEqp.Count; + } + + public MetaDictionary Clone() + { + var ret = new MetaDictionary(); + ret.SetTo(this); + return ret; + } private class Converter : JsonConverter { @@ -67,7 +285,7 @@ public sealed class MetaDictionary : HashSet var identifier = ImcIdentifier.FromJson(manip); var entry = manip["Entry"]?.ToObject(); if (identifier.HasValue && entry.HasValue) - dict.Add(new MetaManipulation(new ImcManipulation(identifier.Value, entry.Value))); + dict.TryAdd(identifier.Value, entry.Value); else Penumbra.Log.Warning("Invalid IMC Manipulation encountered."); break; @@ -77,7 +295,7 @@ public sealed class MetaDictionary : HashSet var identifier = EqdpIdentifier.FromJson(manip); var entry = (EqdpEntry?)manip["Entry"]?.ToObject(); if (identifier.HasValue && entry.HasValue) - dict.Add(new MetaManipulation(new EqdpManipulation(identifier.Value, entry.Value))); + dict.TryAdd(identifier.Value, entry.Value); else Penumbra.Log.Warning("Invalid EQDP Manipulation encountered."); break; @@ -87,7 +305,7 @@ public sealed class MetaDictionary : HashSet var identifier = EqpIdentifier.FromJson(manip); var entry = (EqpEntry?)manip["Entry"]?.ToObject(); if (identifier.HasValue && entry.HasValue) - dict.Add(new MetaManipulation(new EqpManipulation(identifier.Value, entry.Value))); + dict.TryAdd(identifier.Value, entry.Value); else Penumbra.Log.Warning("Invalid EQP Manipulation encountered."); break; @@ -97,7 +315,7 @@ public sealed class MetaDictionary : HashSet var identifier = EstIdentifier.FromJson(manip); var entry = manip["Entry"]?.ToObject(); if (identifier.HasValue && entry.HasValue) - dict.Add(new MetaManipulation(new EstManipulation(identifier.Value, entry.Value))); + dict.TryAdd(identifier.Value, entry.Value); else Penumbra.Log.Warning("Invalid EST Manipulation encountered."); break; @@ -107,7 +325,7 @@ public sealed class MetaDictionary : HashSet var identifier = GmpIdentifier.FromJson(manip); var entry = manip["Entry"]?.ToObject(); if (identifier.HasValue && entry.HasValue) - dict.Add(new MetaManipulation(new GmpManipulation(identifier.Value, entry.Value))); + dict.TryAdd(identifier.Value, entry.Value); else Penumbra.Log.Warning("Invalid GMP Manipulation encountered."); break; @@ -117,7 +335,7 @@ public sealed class MetaDictionary : HashSet var identifier = RspIdentifier.FromJson(manip); var entry = manip["Entry"]?.ToObject(); if (identifier.HasValue && entry.HasValue) - dict.Add(new MetaManipulation(new RspManipulation(identifier.Value, entry.Value))); + dict.TryAdd(identifier.Value, entry.Value); else Penumbra.Log.Warning("Invalid RSP Manipulation encountered."); break; @@ -126,7 +344,7 @@ public sealed class MetaDictionary : HashSet { var identifier = GlobalEqpManipulation.FromJson(manip); if (identifier.HasValue) - dict.Add(new MetaManipulation(identifier.Value)); + dict.TryAdd(identifier.Value); else Penumbra.Log.Warning("Invalid Global EQP Manipulation encountered."); break; diff --git a/Penumbra/Mods/Editor/IMod.cs b/Penumbra/Mods/Editor/IMod.cs index d4c881e9..06c31846 100644 --- a/Penumbra/Mods/Editor/IMod.cs +++ b/Penumbra/Mods/Editor/IMod.cs @@ -8,7 +8,7 @@ namespace Penumbra.Mods.Editor; public record struct AppliedModData( Dictionary FileRedirections, - HashSet Manipulations) + MetaDictionary Manipulations) { public static readonly AppliedModData Empty = new([], []); } diff --git a/Penumbra/Mods/Editor/ModMerger.cs b/Penumbra/Mods/Editor/ModMerger.cs index f5fc9cd7..4faced80 100644 --- a/Penumbra/Mods/Editor/ModMerger.cs +++ b/Penumbra/Mods/Editor/ModMerger.cs @@ -158,7 +158,7 @@ public class ModMerger : IDisposable { var redirections = option.Files.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); var swaps = option.FileSwaps.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); - var manips = option.Manipulations.ToHashSet(); + var manips = option.Manipulations.Clone(); foreach (var originalOption in mergeOptions) { diff --git a/Penumbra/Mods/Editor/ModMetaEditor.cs b/Penumbra/Mods/Editor/ModMetaEditor.cs index 86853755..45d9f8a1 100644 --- a/Penumbra/Mods/Editor/ModMetaEditor.cs +++ b/Penumbra/Mods/Editor/ModMetaEditor.cs @@ -145,7 +145,7 @@ public class ModMetaEditor(ModManager modManager) if (!Changes) return; - modManager.OptionEditor.SetManipulations(container, Recombine().ToHashSet()); + modManager.OptionEditor.SetManipulations(container, [..Recombine()]); Changes = false; } diff --git a/Penumbra/Mods/Groups/IModGroup.cs b/Penumbra/Mods/Groups/IModGroup.cs index fcc8c093..00f47e25 100644 --- a/Penumbra/Mods/Groups/IModGroup.cs +++ b/Penumbra/Mods/Groups/IModGroup.cs @@ -43,7 +43,7 @@ public interface IModGroup public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer); - public void AddData(Setting setting, Dictionary redirections, HashSet manipulations); + public void AddData(Setting setting, Dictionary redirections, MetaDictionary manipulations); public void AddChangedItems(ObjectIdentification identifier, IDictionary changedItems); /// Ensure that a value is valid for a group. diff --git a/Penumbra/Mods/Groups/ImcModGroup.cs b/Penumbra/Mods/Groups/ImcModGroup.cs index b336203d..383bc9fd 100644 --- a/Penumbra/Mods/Groups/ImcModGroup.cs +++ b/Penumbra/Mods/Groups/ImcModGroup.cs @@ -99,7 +99,7 @@ public class ImcModGroup(Mod mod) : IModGroup => new(Identifier.ObjectType, Identifier.BodySlot, Identifier.PrimaryId, Identifier.SecondaryId.Id, variant.Id, Identifier.EquipSlot, DefaultEntry with { AttributeMask = mask }); - public void AddData(Setting setting, Dictionary redirections, HashSet manipulations) + public void AddData(Setting setting, Dictionary redirections, MetaDictionary manipulations) { if (IsDisabled(setting)) return; diff --git a/Penumbra/Mods/Groups/MultiModGroup.cs b/Penumbra/Mods/Groups/MultiModGroup.cs index 7816d628..220d0a7c 100644 --- a/Penumbra/Mods/Groups/MultiModGroup.cs +++ b/Penumbra/Mods/Groups/MultiModGroup.cs @@ -116,7 +116,7 @@ public sealed class MultiModGroup(Mod mod) : IModGroup, ITexToolsGroup public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer) => new MultiModGroupEditDrawer(editDrawer, this); - public void AddData(Setting setting, Dictionary redirections, HashSet manipulations) + public void AddData(Setting setting, Dictionary redirections, MetaDictionary manipulations) { foreach (var (option, index) in OptionData.WithIndex().OrderByDescending(o => o.Value.Priority)) { diff --git a/Penumbra/Mods/Groups/SingleModGroup.cs b/Penumbra/Mods/Groups/SingleModGroup.cs index a6ebd846..a559d609 100644 --- a/Penumbra/Mods/Groups/SingleModGroup.cs +++ b/Penumbra/Mods/Groups/SingleModGroup.cs @@ -101,7 +101,7 @@ public sealed class SingleModGroup(Mod mod) : IModGroup, ITexToolsGroup public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer) => new SingleModGroupEditDrawer(editDrawer, this); - public void AddData(Setting setting, Dictionary redirections, HashSet manipulations) + public void AddData(Setting setting, Dictionary redirections, MetaDictionary manipulations) { if (OptionData.Count == 0) return; diff --git a/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs b/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs index af5b2d3a..67a5d007 100644 --- a/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs +++ b/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs @@ -23,7 +23,7 @@ public class ItemSwapContainer public IReadOnlyDictionary ModRedirections => _appliedModData.FileRedirections; - public IReadOnlySet ModManipulations + public MetaDictionary ModManipulations => _appliedModData.Manipulations; public readonly List Swaps = []; @@ -42,9 +42,10 @@ public class ItemSwapContainer NoSwaps, } - public bool WriteMod(ModManager manager, Mod mod, IModDataContainer container, WriteType writeType = WriteType.NoSwaps, DirectoryInfo? directory = null) + public bool WriteMod(ModManager manager, Mod mod, IModDataContainer container, WriteType writeType = WriteType.NoSwaps, + DirectoryInfo? directory = null) { - var convertedManips = new HashSet(Swaps.Count); + var convertedManips = new MetaDictionary(); var convertedFiles = new Dictionary(Swaps.Count); var convertedSwaps = new Dictionary(Swaps.Count); directory ??= mod.ModPath; @@ -98,13 +99,9 @@ public class ItemSwapContainer { Clear(); if (mod == null || mod.Index < 0) - { - _appliedModData = AppliedModData.Empty; - } + _appliedModData = AppliedModData.Empty; else - { _appliedModData = ModSettings.GetResolveData(mod, settings); - } } public ItemSwapContainer(MetaFileManager manager, ObjectIdentification identifier) @@ -121,7 +118,13 @@ public class ItemSwapContainer private Func MetaResolver(ModCollection? collection) { - var set = collection?.MetaCache?.Manipulations.ToHashSet() ?? _appliedModData.Manipulations; + if (collection?.MetaCache?.Manipulations is { } cache) + { + MetaDictionary dict = [.. cache]; + return m => dict.TryGetValue(m, out var a) ? a : m; + } + + var set = _appliedModData.Manipulations; return m => set.TryGetValue(m, out var a) ? a : m; } diff --git a/Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs b/Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs index 55e01015..01092862 100644 --- a/Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs +++ b/Penumbra/Mods/Manager/OptionEditor/ModGroupEditor.cs @@ -142,7 +142,7 @@ public class ModGroupEditor( } /// Set the meta manipulations for a given option. Replaces existing manipulations. - public void SetManipulations(IModDataContainer subMod, HashSet manipulations, SaveType saveType = SaveType.Queue) + public void SetManipulations(IModDataContainer subMod, MetaDictionary manipulations, SaveType saveType = SaveType.Queue) { if (subMod.Manipulations.Count == manipulations.Count && subMod.Manipulations.All(m => manipulations.TryGetValue(m, out var old) && old.EntryEquals(m))) diff --git a/Penumbra/Mods/Mod.cs b/Penumbra/Mods/Mod.cs index 783ef3e6..a7f87dcd 100644 --- a/Penumbra/Mods/Mod.cs +++ b/Penumbra/Mods/Mod.cs @@ -71,7 +71,7 @@ public sealed class Mod : IMod return AppliedModData.Empty; var dictRedirections = new Dictionary(TotalFileCount); - var setManips = new HashSet(TotalManipulations); + var setManips = new MetaDictionary(); foreach (var (group, groupIndex) in Groups.WithIndex().OrderByDescending(g => g.Value.Priority)) { var config = settings.Settings[groupIndex]; diff --git a/Penumbra/Mods/SubMods/DefaultSubMod.cs b/Penumbra/Mods/SubMods/DefaultSubMod.cs index 5a300a48..dcd33610 100644 --- a/Penumbra/Mods/SubMods/DefaultSubMod.cs +++ b/Penumbra/Mods/SubMods/DefaultSubMod.cs @@ -21,7 +21,7 @@ public class DefaultSubMod(IMod mod) : IModDataContainer IModGroup? IModDataContainer.Group => null; - public void AddTo(Dictionary redirections, HashSet manipulations) + public void AddTo(Dictionary redirections, MetaDictionary manipulations) => SubMod.AddContainerTo(this, redirections, manipulations); public string GetName() diff --git a/Penumbra/Mods/SubMods/OptionSubMod.cs b/Penumbra/Mods/SubMods/OptionSubMod.cs index 378f6dc8..ed7b6ff8 100644 --- a/Penumbra/Mods/SubMods/OptionSubMod.cs +++ b/Penumbra/Mods/SubMods/OptionSubMod.cs @@ -35,7 +35,7 @@ public abstract class OptionSubMod(IModGroup group) : IModOption, IModDataContai public Dictionary FileSwaps { get; set; } = []; public MetaDictionary Manipulations { get; set; } = []; - public void AddDataTo(Dictionary redirections, HashSet manipulations) + public void AddDataTo(Dictionary redirections, MetaDictionary manipulations) => SubMod.AddContainerTo(this, redirections, manipulations); public string GetName() diff --git a/Penumbra/Mods/SubMods/SubMod.cs b/Penumbra/Mods/SubMods/SubMod.cs index b984b570..06a924c8 100644 --- a/Penumbra/Mods/SubMods/SubMod.cs +++ b/Penumbra/Mods/SubMods/SubMod.cs @@ -21,7 +21,7 @@ public static class SubMod /// Add all unique meta manipulations, file redirections and then file swaps from a ModDataContainer to the given sets. Skip any keys that are already contained. [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] public static void AddContainerTo(IModDataContainer container, Dictionary redirections, - HashSet manipulations) + MetaDictionary manipulations) { foreach (var (path, file) in container.Files) redirections.TryAdd(path, file);