Start group rework.

This commit is contained in:
Ottermandias 2024-04-19 17:55:28 +02:00
parent 75cfffeba7
commit 8fc7de64d9
9 changed files with 106 additions and 45 deletions

View file

@ -2,12 +2,10 @@ using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
using Penumbra.Mods; using Penumbra.Mods;
using Penumbra.Api.Enums;
using Penumbra.Communication; using Penumbra.Communication;
using Penumbra.Mods.Editor; using Penumbra.Mods.Editor;
using Penumbra.String.Classes; using Penumbra.String.Classes;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
using Penumbra.Mods.Subclasses;
namespace Penumbra.Collections.Cache; namespace Penumbra.Collections.Cache;
@ -231,37 +229,12 @@ public sealed class CollectionCache : IDisposable
/// <summary> Add all files and possibly manipulations of a given mod according to its settings in this collection. </summary> /// <summary> Add all files and possibly manipulations of a given mod according to its settings in this collection. </summary>
internal void AddModSync(IMod mod, bool addMetaChanges) internal void AddModSync(IMod mod, bool addMetaChanges)
{ {
if (mod.Index >= 0) var files = GetFiles(mod);
{ foreach (var (path, file) in files.FileRedirections)
var settings = _collection[mod.Index].Settings; AddFile(path, file, mod);
if (settings is not { Enabled: true })
return;
foreach (var (group, groupIndex) in mod.Groups.WithIndex().OrderByDescending(g => g.Value.Priority)) foreach (var manip in files.Manipulations)
{ AddManipulation(manip, mod);
if (group.Count == 0)
continue;
var config = settings.Settings[groupIndex];
switch (group)
{
case SingleModGroup single:
AddSubMod(single[config.AsIndex], mod);
break;
case MultiModGroup multi:
{
foreach (var (option, _) in multi.WithIndex()
.Where(p => config.HasFlag(p.Index))
.OrderByDescending(p => group.OptionPriority(p.Index)))
AddSubMod(option, mod);
break;
}
}
}
}
AddSubMod(mod.Default, mod);
if (addMetaChanges) if (addMetaChanges)
{ {
@ -273,14 +246,15 @@ public sealed class CollectionCache : IDisposable
} }
} }
// Add all files and possibly manipulations of a specific submod private AppliedModData GetFiles(IMod mod)
private void AddSubMod(ISubMod subMod, IMod parentMod)
{ {
foreach (var (path, file) in subMod.Files.Concat(subMod.FileSwaps)) if (mod.Index < 0)
AddFile(path, file, parentMod); return mod.GetData();
foreach (var manip in subMod.Manipulations) var settings = _collection[mod.Index].Settings;
AddManipulation(manip, parentMod); return settings is not { Enabled: true }
? AppliedModData.Empty
: mod.GetData(settings);
} }
/// <summary> Invoke only if not in a full recalculation. </summary> /// <summary> Invoke only if not in a full recalculation. </summary>

View file

@ -1,10 +1,12 @@
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
using Penumbra.Mods;
using Penumbra.Mods.Editor; using Penumbra.Mods.Editor;
using Penumbra.String.Classes; using Penumbra.String.Classes;
namespace Penumbra.Collections.Cache; namespace Penumbra.Collections.Cache;
/// <summary>
/// Contains associations between a mod and the paths and meta manipulations affected by that mod.
/// </summary>
public class CollectionModData public class CollectionModData
{ {
private readonly Dictionary<IMod, (HashSet<Utf8GamePath>, HashSet<MetaManipulation>)> _data = new(); private readonly Dictionary<IMod, (HashSet<Utf8GamePath>, HashSet<MetaManipulation>)> _data = new();
@ -17,7 +19,7 @@ public class CollectionModData
if (_data.Remove(mod, out var data)) if (_data.Remove(mod, out var data))
return data; return data;
return (Array.Empty<Utf8GamePath>(), Array.Empty<MetaManipulation>()); return ([], []);
} }
public void AddPath(IMod mod, Utf8GamePath path) public void AddPath(IMod mod, Utf8GamePath path)
@ -28,7 +30,7 @@ public class CollectionModData
} }
else else
{ {
data = (new HashSet<Utf8GamePath> { path }, new HashSet<MetaManipulation>()); data = ([path], []);
_data.Add(mod, data); _data.Add(mod, data);
} }
} }
@ -41,7 +43,7 @@ public class CollectionModData
} }
else else
{ {
data = (new HashSet<Utf8GamePath>(), new HashSet<MetaManipulation> { manipulation }); data = ([], [manipulation]);
_data.Add(mod, data); _data.Add(mod, data);
} }
} }

View file

@ -1,8 +1,17 @@
using OtterGui.Classes; using OtterGui.Classes;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Subclasses; using Penumbra.Mods.Subclasses;
using Penumbra.String.Classes;
namespace Penumbra.Mods.Editor; namespace Penumbra.Mods.Editor;
public record struct AppliedModData(
IReadOnlyCollection<KeyValuePair<Utf8GamePath, FullPath>> FileRedirections,
IReadOnlyCollection<MetaManipulation> Manipulations)
{
public static readonly AppliedModData Empty = new([], []);
}
public interface IMod public interface IMod
{ {
LowerString Name { get; } LowerString Name { get; }
@ -10,6 +19,9 @@ public interface IMod
public int Index { get; } public int Index { get; }
public ModPriority Priority { get; } public ModPriority Priority { get; }
public AppliedModData GetData(ModSettings? settings = null);
public ISubMod Default { get; } public ISubMod Default { get; }
public IReadOnlyList<IModGroup> Groups { get; } public IReadOnlyList<IModGroup> Groups { get; }

View file

@ -1,5 +1,7 @@
using OtterGui; using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using Penumbra.Collections.Cache;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor; using Penumbra.Mods.Editor;
using Penumbra.Mods.Subclasses; using Penumbra.Mods.Subclasses;
using Penumbra.String.Classes; using Penumbra.String.Classes;
@ -59,6 +61,23 @@ public sealed class Mod : IMod
public readonly SubMod Default; public readonly SubMod Default;
public readonly List<IModGroup> Groups = []; public readonly List<IModGroup> Groups = [];
public AppliedModData GetData(ModSettings? settings = null)
{
if (settings is not { Enabled: true })
return AppliedModData.Empty;
var dictRedirections = new Dictionary<Utf8GamePath, FullPath>(TotalFileCount);
var setManips = new HashSet<MetaManipulation>(TotalManipulations);
foreach (var (group, groupIndex) in Groups.WithIndex().OrderByDescending(g => g.Value.Priority))
{
var config = settings.Settings[groupIndex];
group.AddData(config, dictRedirections, setManips);
}
((ISubMod)Default).AddData(dictRedirections, setManips);
return new AppliedModData(dictRedirections, setManips);
}
ISubMod IMod.Default ISubMod IMod.Default
=> Default; => Default;

View file

@ -1,6 +1,8 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Meta.Manipulations;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.String.Classes;
namespace Penumbra.Mods.Subclasses; namespace Penumbra.Mods.Subclasses;
@ -24,6 +26,8 @@ public interface IModGroup : IReadOnlyCollection<ISubMod>
public bool MoveOption(int optionIdxFrom, int optionIdxTo); public bool MoveOption(int optionIdxFrom, int optionIdxTo);
public void UpdatePositions(int from = 0); public void UpdatePositions(int from = 0);
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations);
/// <summary> Ensure that a value is valid for a group. </summary> /// <summary> Ensure that a value is valid for a group. </summary>
public Setting FixSetting(Setting setting); public Setting FixSetting(Setting setting);
} }

View file

@ -14,6 +14,16 @@ public interface ISubMod
public IReadOnlyDictionary<Utf8GamePath, FullPath> FileSwaps { get; } public IReadOnlyDictionary<Utf8GamePath, FullPath> FileSwaps { get; }
public IReadOnlySet<MetaManipulation> Manipulations { get; } public IReadOnlySet<MetaManipulation> Manipulations { get; }
public void AddData(Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
{
foreach (var (path, file) in Files)
redirections.TryAdd(path, file);
foreach (var (path, file) in FileSwaps)
redirections.TryAdd(path, file);
manipulations.UnionWith(Manipulations);
}
public bool IsDefault { get; } public bool IsDefault { get; }
public static void WriteSubMod(JsonWriter j, JsonSerializer serializer, ISubMod mod, DirectoryInfo basePath, ModPriority? priority) public static void WriteSubMod(JsonWriter j, JsonSerializer serializer, ISubMod mod, DirectoryInfo basePath, ModPriority? priority)

View file

@ -5,6 +5,8 @@ using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Meta.Manipulations;
using Penumbra.String.Classes;
namespace Penumbra.Mods.Subclasses; namespace Penumbra.Mods.Subclasses;
@ -110,6 +112,15 @@ public sealed class MultiModGroup : IModGroup
o.SetPosition(o.GroupIdx, i); o.SetPosition(o.GroupIdx, i);
} }
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
{
foreach (var (option, index) in PrioritizedOptions.WithIndex().OrderByDescending(o => o.Value.Priority))
{
if (setting.HasFlag(index))
((ISubMod)option.Mod).AddData(redirections, manipulations);
}
}
public Setting FixSetting(Setting setting) public Setting FixSetting(Setting setting)
=> new(setting.Value & ((1ul << Count) - 1)); => new(setting.Value & ((1ul << Count) - 1));
} }

View file

@ -3,6 +3,8 @@ using Newtonsoft.Json.Linq;
using OtterGui; using OtterGui;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Meta.Manipulations;
using Penumbra.String.Classes;
namespace Penumbra.Mods.Subclasses; namespace Penumbra.Mods.Subclasses;
@ -114,6 +116,9 @@ public sealed class SingleModGroup : IModGroup
o.SetPosition(o.GroupIdx, i); o.SetPosition(o.GroupIdx, i);
} }
public void AddData(Setting setting, Dictionary<Utf8GamePath, FullPath> redirections, HashSet<MetaManipulation> manipulations)
=> this[setting.AsIndex].AddData(redirections, manipulations);
public Setting FixSetting(Setting setting) public Setting FixSetting(Setting setting)
=> Count == 0 ? Setting.Zero : new Setting(Math.Min(setting.Value, (ulong)(Count - 1))); => Count == 0 ? Setting.Zero : new Setting(Math.Min(setting.Value, (ulong)(Count - 1)));
} }

View file

@ -20,6 +20,28 @@ public class TemporaryMod : IMod
public readonly SubMod Default; public readonly SubMod Default;
public AppliedModData GetData(ModSettings? settings = null)
{
Dictionary<Utf8GamePath, FullPath> dict;
if (Default.FileSwapData.Count == 0)
{
dict = Default.FileData;
}
else if (Default.FileData.Count == 0)
{
dict = Default.FileSwapData;
}
else
{
// Need to ensure uniqueness.
dict = new Dictionary<Utf8GamePath, FullPath>(Default.FileData.Count + Default.FileSwaps.Count);
foreach (var (gamePath, file) in Default.FileData.Concat(Default.FileSwaps))
dict.TryAdd(gamePath, file);
}
return new AppliedModData(dict, Default.Manipulations);
}
ISubMod IMod.Default ISubMod IMod.Default
=> Default; => Default;
@ -53,7 +75,8 @@ public class TemporaryMod : IMod
dir = ModCreator.CreateModFolder(modManager.BasePath, collection.Name, config.ReplaceNonAsciiOnImport, true); dir = ModCreator.CreateModFolder(modManager.BasePath, collection.Name, config.ReplaceNonAsciiOnImport, true);
var fileDir = Directory.CreateDirectory(Path.Combine(dir.FullName, "files")); var fileDir = Directory.CreateDirectory(Path.Combine(dir.FullName, "files"));
modManager.DataEditor.CreateMeta(dir, collection.Name, character ?? config.DefaultModAuthor, modManager.DataEditor.CreateMeta(dir, collection.Name, character ?? config.DefaultModAuthor,
$"Mod generated from temporary collection {collection.Id} for {character ?? "Unknown Character"} with name {collection.Name}.", null, null); $"Mod generated from temporary collection {collection.Id} for {character ?? "Unknown Character"} with name {collection.Name}.",
null, null);
var mod = new Mod(dir); var mod = new Mod(dir);
var defaultMod = mod.Default; var defaultMod = mod.Default;
foreach (var (gamePath, fullPath) in collection.ResolvedFiles) foreach (var (gamePath, fullPath) in collection.ResolvedFiles)
@ -86,7 +109,8 @@ public class TemporaryMod : IMod
saveService.ImmediateSave(new ModSaveGroup(dir, defaultMod, config.ReplaceNonAsciiOnImport)); saveService.ImmediateSave(new ModSaveGroup(dir, defaultMod, config.ReplaceNonAsciiOnImport));
modManager.AddMod(dir); modManager.AddMod(dir);
Penumbra.Log.Information($"Successfully generated mod {mod.Name} at {mod.ModPath.FullName} for collection {collection.Identifier}."); Penumbra.Log.Information(
$"Successfully generated mod {mod.Name} at {mod.ModPath.FullName} for collection {collection.Identifier}.");
} }
catch (Exception e) catch (Exception e)
{ {