mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 21:24:18 +01:00
Start group rework.
This commit is contained in:
parent
75cfffeba7
commit
8fc7de64d9
9 changed files with 106 additions and 45 deletions
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue