Expand on MetaDictionary to use separate dictionaries.

This commit is contained in:
Ottermandias 2024-06-08 16:06:37 +02:00
parent d7b60206d7
commit 94fdd848b7
15 changed files with 257 additions and 36 deletions

View file

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

View file

@ -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<MetaManipulation>
public sealed class MetaDictionary : IEnumerable<MetaManipulation>
{
public MetaDictionary()
{ }
private readonly Dictionary<ImcIdentifier, ImcEntry> _imc = [];
private readonly Dictionary<EqpIdentifier, EqpEntry> _eqp = [];
private readonly Dictionary<EqdpIdentifier, EqdpEntry> _eqdp = [];
private readonly Dictionary<EstIdentifier, EstEntry> _est = [];
private readonly Dictionary<RspIdentifier, RspEntry> _rsp = [];
private readonly Dictionary<GmpIdentifier, GmpEntry> _gmp = [];
private readonly HashSet<GlobalEqpManipulation> _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<MetaManipulation> 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<MetaManipulation> 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<MetaDictionary>
{
@ -67,7 +285,7 @@ public sealed class MetaDictionary : HashSet<MetaManipulation>
var identifier = ImcIdentifier.FromJson(manip);
var entry = manip["Entry"]?.ToObject<ImcEntry>();
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<MetaManipulation>
var identifier = EqdpIdentifier.FromJson(manip);
var entry = (EqdpEntry?)manip["Entry"]?.ToObject<ushort>();
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<MetaManipulation>
var identifier = EqpIdentifier.FromJson(manip);
var entry = (EqpEntry?)manip["Entry"]?.ToObject<ulong>();
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<MetaManipulation>
var identifier = EstIdentifier.FromJson(manip);
var entry = manip["Entry"]?.ToObject<EstEntry>();
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<MetaManipulation>
var identifier = GmpIdentifier.FromJson(manip);
var entry = manip["Entry"]?.ToObject<GmpEntry>();
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<MetaManipulation>
var identifier = RspIdentifier.FromJson(manip);
var entry = manip["Entry"]?.ToObject<RspEntry>();
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<MetaManipulation>
{
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;

View file

@ -8,7 +8,7 @@ namespace Penumbra.Mods.Editor;
public record struct AppliedModData(
Dictionary<Utf8GamePath, FullPath> FileRedirections,
HashSet<MetaManipulation> Manipulations)
MetaDictionary Manipulations)
{
public static readonly AppliedModData Empty = new([], []);
}

View file

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

View file

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

View file

@ -43,7 +43,7 @@ public interface IModGroup
public IModGroupEditDrawer EditDrawer(ModGroupEditDrawer editDrawer);
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations);
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations);
public void AddChangedItems(ObjectIdentification identifier, IDictionary<string, object?> changedItems);
/// <summary> Ensure that a value is valid for a group. </summary>

View file

@ -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<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations)
{
if (IsDisabled(setting))
return;

View file

@ -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<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations)
{
foreach (var (option, index) in OptionData.WithIndex().OrderByDescending(o => o.Value.Priority))
{

View file

@ -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<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations)
{
if (OptionData.Count == 0)
return;

View file

@ -23,7 +23,7 @@ public class ItemSwapContainer
public IReadOnlyDictionary<Utf8GamePath, FullPath> ModRedirections
=> _appliedModData.FileRedirections;
public IReadOnlySet<MetaManipulation> ModManipulations
public MetaDictionary ModManipulations
=> _appliedModData.Manipulations;
public readonly List<Swap> 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<MetaManipulation>(Swaps.Count);
var convertedManips = new MetaDictionary();
var convertedFiles = new Dictionary<Utf8GamePath, FullPath>(Swaps.Count);
var convertedSwaps = new Dictionary<Utf8GamePath, FullPath>(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<MetaManipulation, MetaManipulation> 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;
}

View file

@ -142,7 +142,7 @@ public class ModGroupEditor(
}
/// <summary> Set the meta manipulations for a given option. Replaces existing manipulations. </summary>
public void SetManipulations(IModDataContainer subMod, HashSet<MetaManipulation> 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)))

View file

@ -71,7 +71,7 @@ public sealed class Mod : IMod
return AppliedModData.Empty;
var dictRedirections = new Dictionary<Utf8GamePath, FullPath>(TotalFileCount);
var setManips = new HashSet<MetaManipulation>(TotalManipulations);
var setManips = new MetaDictionary();
foreach (var (group, groupIndex) in Groups.WithIndex().OrderByDescending(g => g.Value.Priority))
{
var config = settings.Settings[groupIndex];

View file

@ -21,7 +21,7 @@ public class DefaultSubMod(IMod mod) : IModDataContainer
IModGroup? IModDataContainer.Group
=> null;
public void AddTo(Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
public void AddTo(Dictionary<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations)
=> SubMod.AddContainerTo(this, redirections, manipulations);
public string GetName()

View file

@ -35,7 +35,7 @@ public abstract class OptionSubMod(IModGroup group) : IModOption, IModDataContai
public Dictionary<Utf8GamePath, FullPath> FileSwaps { get; set; } = [];
public MetaDictionary Manipulations { get; set; } = [];
public void AddDataTo(Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
public void AddDataTo(Dictionary<Utf8GamePath, FullPath> redirections, MetaDictionary manipulations)
=> SubMod.AddContainerTo(this, redirections, manipulations);
public string GetName()

View file

@ -21,7 +21,7 @@ public static class SubMod
/// <summary> 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. </summary>
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
public static void AddContainerTo(IModDataContainer container, Dictionary<Utf8GamePath, FullPath> redirections,
HashSet<MetaManipulation> manipulations)
MetaDictionary manipulations)
{
foreach (var (path, file) in container.Files)
redirections.TryAdd(path, file);