Update Mod Merger for other group types.

This commit is contained in:
Ottermandias 2025-06-07 22:10:59 +02:00
parent 535694e9c8
commit 4c0e6d2a67
2 changed files with 139 additions and 48 deletions

View file

@ -6,6 +6,7 @@ using OtterGui.Extensions;
using OtterGui.Services; using OtterGui.Services;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Communication; using Penumbra.Communication;
using Penumbra.Mods.Groups;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
using Penumbra.Mods.Manager.OptionEditor; using Penumbra.Mods.Manager.OptionEditor;
using Penumbra.Mods.SubMods; using Penumbra.Mods.SubMods;
@ -44,13 +45,13 @@ public class ModMerger : IDisposable, IService
public ModMerger(ModManager mods, ModGroupEditor editor, ModSelection selection, DuplicateManager duplicates, public ModMerger(ModManager mods, ModGroupEditor editor, ModSelection selection, DuplicateManager duplicates,
CommunicatorService communicator, ModCreator creator, Configuration config) CommunicatorService communicator, ModCreator creator, Configuration config)
{ {
_editor = editor; _editor = editor;
_selection = selection; _selection = selection;
_duplicates = duplicates; _duplicates = duplicates;
_communicator = communicator; _communicator = communicator;
_creator = creator; _creator = creator;
_config = config; _config = config;
_mods = mods; _mods = mods;
_selection.Subscribe(OnSelectionChange, ModSelection.Priority.ModMerger); _selection.Subscribe(OnSelectionChange, ModSelection.Priority.ModMerger);
_communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModMerger); _communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModMerger);
} }
@ -99,26 +100,117 @@ public class ModMerger : IDisposable, IService
foreach (var originalGroup in MergeFromMod!.Groups) foreach (var originalGroup in MergeFromMod!.Groups)
{ {
var (group, groupIdx, groupCreated) = _editor.FindOrAddModGroup(MergeToMod!, originalGroup.Type, originalGroup.Name); switch (originalGroup.Type)
if (groupCreated)
_createdGroups.Add(groupIdx);
if (group == null)
throw new Exception(
$"The merged group {originalGroup.Name} already existed, but had a different type than the original group of type {originalGroup.Type}.");
foreach (var originalOption in originalGroup.DataContainers)
{ {
var (option, _, optionCreated) = _editor.FindOrAddOption(group, originalOption.GetName()); case GroupType.Single:
if (optionCreated) case GroupType.Multi:
{ {
_createdOptions.Add(option!); var (group, groupIdx, groupCreated) = _editor.FindOrAddModGroup(MergeToMod!, originalGroup.Type, originalGroup.Name);
// #TODO DataContainer <> Option. if (group is null)
MergeIntoOption([originalOption], (IModDataContainer)option!, false); throw new Exception(
$"The merged group {originalGroup.Name} already existed, but had a different type than the original group of type {originalGroup.Type}.");
if (groupCreated)
{
_createdGroups.Add(groupIdx);
group.Description = originalGroup.Description;
group.Image = originalGroup.Image;
group.DefaultSettings = originalGroup.DefaultSettings;
group.Page = originalGroup.Page;
group.Priority = originalGroup.Priority;
}
foreach (var originalOption in originalGroup.Options)
{
var (option, _, optionCreated) = _editor.FindOrAddOption(group, originalOption.Name);
if (optionCreated)
{
_createdOptions.Add(option!);
MergeIntoOption([(IModDataContainer)originalOption], (IModDataContainer)option!, false);
option!.Description = originalOption.Description;
if (option is MultiSubMod multiOption)
multiOption.Priority = ((MultiSubMod)originalOption).Priority;
}
else
{
throw new Exception(
$"Could not merge {MergeFromMod!.Name} into {MergeToMod!.Name}: The option {option!.FullName} already existed.");
}
}
break;
} }
else
case GroupType.Imc when originalGroup is ImcModGroup imc:
{ {
throw new Exception( var group = _editor.ImcEditor.AddModGroup(MergeToMod!, imc.Name, imc.Identifier, imc.DefaultEntry);
$"Could not merge {MergeFromMod!.Name} into {MergeToMod!.Name}: The option {option!.FullName} already existed."); if (group is null)
throw new Exception(
$"The merged group {originalGroup.Name} already existed, but groups of type {originalGroup.Type} can not be merged.");
group.AllVariants = imc.AllVariants;
group.OnlyAttributes = imc.OnlyAttributes;
group.Description = imc.Description;
group.Image = imc.Image;
group.DefaultSettings = imc.DefaultSettings;
group.Page = imc.Page;
group.Priority = imc.Priority;
foreach (var originalOption in imc.OptionData)
{
if (originalOption.IsDisableSubMod)
{
_editor.ImcEditor.ChangeCanBeDisabled(group, true);
var disable = group.OptionData.First(s => s.IsDisableSubMod);
disable.Description = originalOption.Description;
disable.Name = originalOption.Name;
continue;
}
var newOption = _editor.ImcEditor.AddOption(group, originalOption.Name);
if (newOption is null)
throw new Exception(
$"Could not merge {MergeFromMod!.Name} into {MergeToMod!.Name}: Unknown error when creating IMC option {originalOption.FullName}.");
newOption.Description = originalOption.Description;
newOption.AttributeMask = originalOption.AttributeMask;
}
break;
}
case GroupType.Combining when originalGroup is CombiningModGroup combining:
{
var group = _editor.CombiningEditor.AddModGroup(MergeToMod!, combining.Name);
if (group is null)
throw new Exception(
$"The merged group {originalGroup.Name} already existed, but groups of type {originalGroup.Type} can not be merged.");
group.Description = combining.Description;
group.Image = combining.Image;
group.DefaultSettings = combining.DefaultSettings;
group.Page = combining.Page;
group.Priority = combining.Priority;
foreach (var originalOption in combining.OptionData)
{
var option = _editor.CombiningEditor.AddOption(group, originalOption.Name);
if (option is null)
throw new Exception(
$"Could not merge {MergeFromMod!.Name} into {MergeToMod!.Name}: Unknown error when creating combining option {originalOption.FullName}.");
option.Description = originalOption.Description;
}
if (group.Data.Count != combining.Data.Count)
throw new Exception(
$"Could not merge {MergeFromMod!.Name} into {MergeToMod!.Name}: Unknown error caused data container counts in combining group {originalGroup.Name} to differ.");
foreach (var (originalContainer, container) in combining.Data.Zip(group.Data))
{
container.Name = originalContainer.Name;
MergeIntoOption([originalContainer], container, false);
}
break;
} }
} }
} }
@ -151,7 +243,6 @@ public class ModMerger : IDisposable, IService
if (!dir.Exists) if (!dir.Exists)
_createdDirectories.Add(dir.FullName); _createdDirectories.Add(dir.FullName);
CopyFiles(dir); CopyFiles(dir);
// #TODO DataContainer <> Option.
MergeIntoOption(MergeFromMod!.AllDataContainers.Reverse(), (IModDataContainer)option!, true); MergeIntoOption(MergeFromMod!.AllDataContainers.Reverse(), (IModDataContainer)option!, true);
} }

View file

@ -15,8 +15,8 @@ public abstract class ModOptionEditor<TGroup, TOption>(
where TOption : class, IModOption where TOption : class, IModOption
{ {
protected readonly CommunicatorService Communicator = communicator; protected readonly CommunicatorService Communicator = communicator;
protected readonly SaveService SaveService = saveService; protected readonly SaveService SaveService = saveService;
protected readonly Configuration Config = config; protected readonly Configuration Config = config;
/// <summary> Add a new, empty option group of the given type and name. </summary> /// <summary> Add a new, empty option group of the given type and name. </summary>
public TGroup? AddModGroup(Mod mod, string newName, SaveType saveType = SaveType.ImmediateSync) public TGroup? AddModGroup(Mod mod, string newName, SaveType saveType = SaveType.ImmediateSync)
@ -25,7 +25,7 @@ public abstract class ModOptionEditor<TGroup, TOption>(
return null; return null;
var maxPriority = mod.Groups.Count == 0 ? ModPriority.Default : mod.Groups.Max(o => o.Priority) + 1; var maxPriority = mod.Groups.Count == 0 ? ModPriority.Default : mod.Groups.Max(o => o.Priority) + 1;
var group = CreateGroup(mod, newName, maxPriority); var group = CreateGroup(mod, newName, maxPriority);
mod.Groups.Add(group); mod.Groups.Add(group);
SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport)); SaveService.Save(saveType, new ModSaveGroup(group, Config.ReplaceNonAsciiOnImport));
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupAdded, mod, group, null, null, -1); Communicator.ModOptionChanged.Invoke(ModOptionChangeType.GroupAdded, mod, group, null, null, -1);
@ -92,8 +92,8 @@ public abstract class ModOptionEditor<TGroup, TOption>(
/// <summary> Delete the given option from the given group. </summary> /// <summary> Delete the given option from the given group. </summary>
public void DeleteOption(TOption option) public void DeleteOption(TOption option)
{ {
var mod = option.Mod; var mod = option.Mod;
var group = option.Group; var group = option.Group;
var optionIdx = option.GetIndex(); var optionIdx = option.GetIndex();
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, group, option, null, -1); Communicator.ModOptionChanged.Invoke(ModOptionChangeType.PrepareChange, mod, group, option, null, -1);
RemoveOption((TGroup)group, optionIdx); RemoveOption((TGroup)group, optionIdx);
@ -104,7 +104,7 @@ public abstract class ModOptionEditor<TGroup, TOption>(
/// <summary> Move an option inside the given option group. </summary> /// <summary> Move an option inside the given option group. </summary>
public void MoveOption(TOption option, int optionIdxTo) public void MoveOption(TOption option, int optionIdxTo)
{ {
var idx = option.GetIndex(); var idx = option.GetIndex();
var group = (TGroup)option.Group; var group = (TGroup)option.Group;
if (!MoveOption(group, idx, optionIdxTo)) if (!MoveOption(group, idx, optionIdxTo))
return; return;
@ -113,10 +113,10 @@ public abstract class ModOptionEditor<TGroup, TOption>(
Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMoved, group.Mod, group, option, null, idx); Communicator.ModOptionChanged.Invoke(ModOptionChangeType.OptionMoved, group.Mod, group, option, null, idx);
} }
protected abstract TGroup CreateGroup(Mod mod, string newName, ModPriority priority, SaveType saveType = SaveType.ImmediateSync); protected abstract TGroup CreateGroup(Mod mod, string newName, ModPriority priority, SaveType saveType = SaveType.ImmediateSync);
protected abstract TOption? CloneOption(TGroup group, IModOption option); protected abstract TOption? CloneOption(TGroup group, IModOption option);
protected abstract void RemoveOption(TGroup group, int optionIndex); protected abstract void RemoveOption(TGroup group, int optionIndex);
protected abstract bool MoveOption(TGroup group, int optionIdxFrom, int optionIdxTo); protected abstract bool MoveOption(TGroup group, int optionIdxFrom, int optionIdxTo);
} }
public static class ModOptionChangeTypeExtension public static class ModOptionChangeTypeExtension
@ -132,22 +132,22 @@ public static class ModOptionChangeTypeExtension
{ {
(requiresSaving, requiresReloading, wasPrepared) = type switch (requiresSaving, requiresReloading, wasPrepared) = type switch
{ {
ModOptionChangeType.GroupRenamed => (true, false, false), ModOptionChangeType.GroupRenamed => (true, false, false),
ModOptionChangeType.GroupAdded => (true, false, false), ModOptionChangeType.GroupAdded => (true, false, false),
ModOptionChangeType.GroupDeleted => (true, true, false), ModOptionChangeType.GroupDeleted => (true, true, false),
ModOptionChangeType.GroupMoved => (true, false, false), ModOptionChangeType.GroupMoved => (true, false, false),
ModOptionChangeType.GroupTypeChanged => (true, true, true), ModOptionChangeType.GroupTypeChanged => (true, true, true),
ModOptionChangeType.PriorityChanged => (true, true, true), ModOptionChangeType.PriorityChanged => (true, true, true),
ModOptionChangeType.OptionAdded => (true, true, true), ModOptionChangeType.OptionAdded => (true, true, true),
ModOptionChangeType.OptionDeleted => (true, true, false), ModOptionChangeType.OptionDeleted => (true, true, false),
ModOptionChangeType.OptionMoved => (true, false, false), ModOptionChangeType.OptionMoved => (true, false, false),
ModOptionChangeType.OptionFilesChanged => (false, true, false), ModOptionChangeType.OptionFilesChanged => (false, true, false),
ModOptionChangeType.OptionFilesAdded => (false, true, true), ModOptionChangeType.OptionFilesAdded => (false, true, true),
ModOptionChangeType.OptionSwapsChanged => (false, true, false), ModOptionChangeType.OptionSwapsChanged => (false, true, false),
ModOptionChangeType.OptionMetaChanged => (false, true, false), ModOptionChangeType.OptionMetaChanged => (false, true, false),
ModOptionChangeType.DisplayChange => (false, false, false), ModOptionChangeType.DisplayChange => (false, false, false),
ModOptionChangeType.DefaultOptionChanged => (true, false, false), ModOptionChangeType.DefaultOptionChanged => (true, false, false),
_ => (false, false, false), _ => (false, false, false),
}; };
} }
} }