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 Penumbra.Meta.Manipulations;
using Penumbra.Mods;
using Penumbra.Api.Enums;
using Penumbra.Communication;
using Penumbra.Mods.Editor;
using Penumbra.String.Classes;
using Penumbra.Mods.Manager;
using Penumbra.Mods.Subclasses;
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>
internal void AddModSync(IMod mod, bool addMetaChanges)
{
if (mod.Index >= 0)
{
var settings = _collection[mod.Index].Settings;
if (settings is not { Enabled: true })
return;
var files = GetFiles(mod);
foreach (var (path, file) in files.FileRedirections)
AddFile(path, file, mod);
foreach (var (group, groupIndex) in mod.Groups.WithIndex().OrderByDescending(g => g.Value.Priority))
{
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);
foreach (var manip in files.Manipulations)
AddManipulation(manip, mod);
if (addMetaChanges)
{
@ -273,14 +246,15 @@ public sealed class CollectionCache : IDisposable
}
}
// Add all files and possibly manipulations of a specific submod
private void AddSubMod(ISubMod subMod, IMod parentMod)
private AppliedModData GetFiles(IMod mod)
{
foreach (var (path, file) in subMod.Files.Concat(subMod.FileSwaps))
AddFile(path, file, parentMod);
if (mod.Index < 0)
return mod.GetData();
foreach (var manip in subMod.Manipulations)
AddManipulation(manip, parentMod);
var settings = _collection[mod.Index].Settings;
return settings is not { Enabled: true }
? AppliedModData.Empty
: mod.GetData(settings);
}
/// <summary> Invoke only if not in a full recalculation. </summary>

View file

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

View file

@ -1,15 +1,27 @@
using OtterGui.Classes;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Subclasses;
using Penumbra.String.Classes;
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
{
LowerString Name { get; }
public int Index { get; }
public int Index { get; }
public ModPriority Priority { get; }
public AppliedModData GetData(ModSettings? settings = null);
public ISubMod Default { get; }
public IReadOnlyList<IModGroup> Groups { get; }

View file

@ -1,5 +1,7 @@
using OtterGui;
using OtterGui.Classes;
using Penumbra.Collections.Cache;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods.Editor;
using Penumbra.Mods.Subclasses;
using Penumbra.String.Classes;
@ -59,6 +61,23 @@ public sealed class Mod : IMod
public readonly SubMod Default;
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
=> Default;

View file

@ -1,6 +1,8 @@
using Newtonsoft.Json;
using Penumbra.Api.Enums;
using Penumbra.Meta.Manipulations;
using Penumbra.Services;
using Penumbra.String.Classes;
namespace Penumbra.Mods.Subclasses;
@ -24,6 +26,8 @@ public interface IModGroup : IReadOnlyCollection<ISubMod>
public bool MoveOption(int optionIdxFrom, int optionIdxTo);
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>
public Setting FixSetting(Setting setting);
}

View file

@ -14,6 +14,16 @@ public interface ISubMod
public IReadOnlyDictionary<Utf8GamePath, FullPath> FileSwaps { 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 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.Filesystem;
using Penumbra.Api.Enums;
using Penumbra.Meta.Manipulations;
using Penumbra.String.Classes;
namespace Penumbra.Mods.Subclasses;
@ -110,6 +112,15 @@ public sealed class MultiModGroup : IModGroup
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)
=> new(setting.Value & ((1ul << Count) - 1));
}

View file

@ -3,6 +3,8 @@ using Newtonsoft.Json.Linq;
using OtterGui;
using OtterGui.Filesystem;
using Penumbra.Api.Enums;
using Penumbra.Meta.Manipulations;
using Penumbra.String.Classes;
namespace Penumbra.Mods.Subclasses;
@ -114,6 +116,9 @@ public sealed class SingleModGroup : IModGroup
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)
=> 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 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
=> Default;
@ -53,7 +75,8 @@ public class TemporaryMod : IMod
dir = ModCreator.CreateModFolder(modManager.BasePath, collection.Name, config.ReplaceNonAsciiOnImport, true);
var fileDir = Directory.CreateDirectory(Path.Combine(dir.FullName, "files"));
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 defaultMod = mod.Default;
foreach (var (gamePath, fullPath) in collection.ResolvedFiles)
@ -86,7 +109,8 @@ public class TemporaryMod : IMod
saveService.ImmediateSave(new ModSaveGroup(dir, defaultMod, config.ReplaceNonAsciiOnImport));
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)
{