Split up ModInfo to allow named Settings and Settings without current runtime Information.

This commit is contained in:
Ottermandias 2021-02-23 18:31:33 +01:00
parent 44670198ab
commit 1a75fd953b
9 changed files with 296 additions and 121 deletions

View file

@ -1,20 +1,23 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Penumbra.Mods;
namespace Penumbra.Models
{
public class ModInfo
public class ModInfo : ModSettings
{
public ModInfo( ResourceMod mod )
=> Mod = mod;
public string FolderName { get; set; } = "";
public bool Enabled { get; set; }
public int Priority { get; set; }
public Dictionary< string, int > Conf { get; set; } = new();
[JsonIgnore]
public ResourceMod Mod { get; set; }
public bool FixSpecificSetting( string name )
=> FixSpecificSetting( Mod.Meta, name );
public bool FixInvalidSettings()
=> FixInvalidSettings( Mod.Meta );
}
}

View file

@ -43,5 +43,84 @@ namespace Penumbra.Models
// todo: handle broken mods properly
}
}
private static bool ApplySingleGroupFiles( OptionGroup group, RelPath relPath, int selection, HashSet< GamePath > paths )
{
if( group.Options[ selection ].OptionFiles.TryGetValue( relPath, out var groupPaths ) )
{
paths.UnionWith( groupPaths );
return true;
}
for( var i = 0; i < group.Options.Count; ++i )
{
if( i == selection )
{
continue;
}
if( group.Options[ i ].OptionFiles.ContainsKey( relPath ) )
{
return true;
}
}
return false;
}
private static bool ApplyMultiGroupFiles( OptionGroup group, RelPath relPath, int selection, HashSet< GamePath > paths )
{
var doNotAdd = false;
for( var i = 0; i < group.Options.Count; ++i )
{
if( ( selection & ( 1 << i ) ) != 0 )
{
if( group.Options[ i ].OptionFiles.TryGetValue( relPath, out var groupPaths ) )
{
paths.UnionWith( groupPaths );
}
}
else if( group.Options[ i ].OptionFiles.ContainsKey( relPath ) )
{
doNotAdd = true;
}
}
return doNotAdd;
}
public (bool configChanged, HashSet< GamePath > paths) GetFilesForConfig( RelPath relPath, ModSettings settings )
{
var doNotAdd = false;
var configChanged = false;
HashSet< GamePath > paths = new();
foreach( var group in Groups.Values )
{
configChanged |= settings.FixSpecificSetting( this, group.GroupName );
if( group.Options.Count == 0 )
{
continue;
}
switch( group.SelectionType )
{
case SelectType.Single:
doNotAdd |= ApplySingleGroupFiles( group, relPath, settings.Settings[ group.GroupName ], paths );
break;
case SelectType.Multi:
doNotAdd |= ApplyMultiGroupFiles( group, relPath, settings.Settings[ group.GroupName ], paths );
break;
}
}
if( !doNotAdd )
{
paths.Add( new GamePath( relPath ) );
}
return ( configChanged, paths );
}
}
}

View file

@ -0,0 +1,92 @@
using System.Collections.Generic;
using System.Linq;
using System;
namespace Penumbra.Models
{
public class ModSettings
{
public int Priority { get; set; }
public Dictionary< string, int > Settings { get; set; } = new();
// For backwards compatibility
private Dictionary< string, int > Conf
{
set => Settings = value;
}
public static ModSettings CreateFrom( NamedModSettings n, ModMeta meta )
{
ModSettings ret = new()
{
Priority = n.Priority,
Settings = n.Settings.Keys.ToDictionary( K => K, K => 0 )
};
foreach( var kvp in n.Settings )
{
if( !meta.Groups.TryGetValue( kvp.Key, out var info ) )
{
continue;
}
if( info.SelectionType == SelectType.Single )
{
if( n.Settings[ kvp.Key ].Count == 0 )
{
ret.Settings[ kvp.Key ] = 0;
}
else
{
var idx = info.Options.FindIndex( O => O.OptionName == n.Settings[ kvp.Key ].Last() );
ret.Settings[ kvp.Key ] = idx < 0 ? 0 : idx;
}
}
else
{
foreach( var idx in n.Settings[ kvp.Key ]
.Select( option => info.Options.FindIndex( O => O.OptionName == option ) )
.Where( idx => idx >= 0 ) )
{
ret.Settings[ kvp.Key ] |= 1 << idx;
}
}
}
return ret;
}
public bool FixSpecificSetting( ModMeta meta, string name )
{
if( !meta.Groups.TryGetValue( name, out var group ) )
{
return Settings.Remove( name );
}
if( Settings.TryGetValue( name, out var oldSetting ) )
{
Settings[ name ] = group.SelectionType switch
{
SelectType.Single => Math.Min( Math.Max( oldSetting, 0 ), group.Options.Count - 1 ),
SelectType.Multi => Math.Min( Math.Max( oldSetting, 0 ), ( 1 << group.Options.Count ) - 1 ),
_ => Settings[ group.GroupName ]
};
return oldSetting != Settings[ group.GroupName ];
}
Settings[ name ] = 0;
return true;
}
public bool FixInvalidSettings( ModMeta meta )
{
if( meta.Groups.Count == 0 )
{
return false;
}
return Settings.Keys.ToArray().Union( meta.Groups.Keys )
.Aggregate( false, ( current, name ) => current | FixSpecificSetting( meta, name ) );
}
}
}

View file

@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Linq;
namespace Penumbra.Models
{
public class NamedModSettings
{
public int Priority { get; set; }
public Dictionary< string, HashSet< string > > Settings { get; set; } = new();
public void AddFromModSetting( ModSettings s, ModMeta meta )
{
Priority = s.Priority;
Settings = s.Settings.Keys.ToDictionary( K => K, K => new HashSet< string >() );
foreach( var kvp in Settings )
{
if( !meta.Groups.TryGetValue( kvp.Key, out var info ) )
{
continue;
}
var setting = s.Settings[ kvp.Key ];
if( info.SelectionType == SelectType.Single )
{
var name = setting < info.Options.Count
? info.Options[ setting ].OptionName
: info.Options[ 0 ].OptionName;
kvp.Value.Add( name );
}
else
{
for( var i = 0; i < info.Options.Count; ++i )
{
if( ( ( setting >> i ) & 1 ) != 0 )
{
kvp.Value.Add( info.Options[ i ].OptionName );
}
}
}
}
}
}
}

View file

@ -146,13 +146,13 @@ namespace Penumbra.Mods
public ModInfo AddModSettings( ResourceMod mod )
{
var entry = new ModInfo(mod)
var entry = new ModInfo( mod )
{
Priority = ModSettings?.Count ?? 0,
FolderName = mod.ModBasePath.Name,
Enabled = true,
Enabled = true
};
entry.FixInvalidSettings();
#if DEBUG
PluginLog.Information( "creating mod settings {ModName}", entry.FolderName );
#endif
@ -170,14 +170,14 @@ namespace Penumbra.Mods
}
settings.Mod = mod;
settings.FixInvalidSettings();
return settings;
}
public IEnumerable< ModInfo > GetOrderedAndEnabledModSettings( bool invertOrder = false )
{
var query = ModSettings?
.Where( x => x.Enabled ) ?? Enumerable.Empty<ModInfo>();
.Where( x => x.Enabled ) ?? Enumerable.Empty< ModInfo >();
if( !invertOrder )
{

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Dalamud.Plugin;
using Penumbra.Models;
using Penumbra.Util;
@ -39,7 +38,6 @@ namespace Penumbra.Mods
Mods = new ModCollection( basePath );
Mods.Load();
Mods.Save();
CalculateEffectiveFileList();
}
@ -54,16 +52,19 @@ namespace Penumbra.Mods
return;
}
var changedSettings = false;
var registeredFiles = new Dictionary< GamePath, string >();
foreach( var (mod, settings) in Mods.GetOrderedAndEnabledModListWithSettings( _plugin!.Configuration!.InvertModListOrder ) )
{
mod.FileConflicts.Clear();
ProcessModFiles( registeredFiles, mod, settings );
changedSettings |= ProcessModFiles( registeredFiles, mod, settings );
ProcessSwappedFiles( registeredFiles, mod, settings );
}
if (changedSettings)
Mods.Save();
_plugin!.GameUtils!.ReloadPlayerResources();
}
@ -84,76 +85,21 @@ namespace Penumbra.Mods
}
}
private void ProcessModFiles( Dictionary< GamePath, string > registeredFiles, ResourceMod mod, ModInfo settings )
private bool ProcessModFiles( Dictionary< GamePath, string > registeredFiles, ResourceMod mod, ModInfo settings )
{
var changedConfig = settings.FixInvalidSettings();
foreach( var file in mod.ModFiles )
{
RelPath relativeFilePath = new( file, mod.ModBasePath );
var doNotAdd = false;
foreach( var group in mod.Meta.Groups.Values )
{
if( !settings.Conf.TryGetValue( group.GroupName, out var setting )
|| group.SelectionType == SelectType.Single
&& settings.Conf[ group.GroupName ] >= group.Options.Count )
{
settings.Conf[ group.GroupName ] = 0;
Mods!.Save();
setting = 0;
}
if( group.Options.Count == 0 )
{
continue;
}
if( group.SelectionType == SelectType.Multi )
{
settings.Conf[ group.GroupName ] &= ( 1 << group.Options.Count ) - 1;
}
HashSet< GamePath > paths;
switch( group.SelectionType )
{
case SelectType.Single:
if( group.Options[ setting ].OptionFiles.TryGetValue( relativeFilePath, out paths ) )
{
doNotAdd |= AddFiles( paths, file, registeredFiles, mod );
}
else
{
doNotAdd |= group.Options.Where( ( o, i ) => i != setting )
.Any( option => option.OptionFiles.ContainsKey( relativeFilePath ) );
}
break;
case SelectType.Multi:
for( var i = 0; i < group.Options.Count; ++i )
{
if( ( setting & ( 1 << i ) ) != 0 )
{
if( group.Options[ i ].OptionFiles.TryGetValue( relativeFilePath, out paths ) )
{
doNotAdd |= AddFiles( paths, file, registeredFiles, mod );
}
}
else
{
doNotAdd |= group.Options[ i ].OptionFiles.ContainsKey( relativeFilePath );
}
}
break;
}
}
if( !doNotAdd )
{
AddFiles( new GamePath[] { new( relativeFilePath ) }, file, registeredFiles, mod );
}
var (configChanged, gamePaths) = mod.Meta.GetFilesForConfig( relativeFilePath, settings );
changedConfig |= configChanged;
AddFiles( gamePaths, file, registeredFiles, mod );
}
return changedConfig;
}
private bool AddFiles( IEnumerable< GamePath > gamePaths, FileInfo file, Dictionary< GamePath, string > registeredFiles,
private void AddFiles( IEnumerable< GamePath > gamePaths, FileInfo file, Dictionary< GamePath, string > registeredFiles,
ResourceMod mod )
{
foreach( var gamePath in gamePaths )
@ -168,7 +114,6 @@ namespace Penumbra.Mods
mod.AddConflict( modName, gamePath );
}
}
return true;
}
public void ChangeModPriority( ModInfo info, bool up = false )

View file

@ -64,7 +64,8 @@ namespace Penumbra.UI
ImGui.PopStyleVar( 2 );
ImGui.SetWindowSize( new Vector2( ImGui.GetWindowSize().X - frameHeight, ImGui.GetWindowSize().Y ) );
// This seems wrong?
//ImGui.SetWindowSize( new Vector2( ImGui.GetWindowSize().X - frameHeight, ImGui.GetWindowSize().Y ) );
var itemWidth = ImGui.CalcItemWidth();
ImGui.PushItemWidth( Math.Max( 0f, itemWidth - frameHeight ) );
@ -127,7 +128,8 @@ namespace Penumbra.UI
frameMax, borderColor, halfFrame.X );
ImGui.PopStyleVar( 2 );
ImGui.SetWindowSize( new Vector2( ImGui.GetWindowSize().X + frameHeight, ImGui.GetWindowSize().Y ) );
// This seems wrong?
// ImGui.SetWindowSize( new Vector2( ImGui.GetWindowSize().X + frameHeight, ImGui.GetWindowSize().Y ) );
ImGui.Dummy( ZeroVector );
ImGui.EndGroup(); // Close first group

View file

@ -552,7 +552,7 @@ namespace Penumbra.UI
var oldEnabled = enabled;
if( ImGui.Checkbox( label, ref enabled ) && oldEnabled != enabled )
{
Mod.Conf[ group.GroupName ] ^= 1 << idx;
Mod.Settings[ group.GroupName ] ^= 1 << idx;
Save();
}
}
@ -567,7 +567,7 @@ namespace Penumbra.UI
ImGuiCustom.BeginFramedGroup( group.GroupName );
for( var i = 0; i < group.Options.Count; ++i )
{
DrawMultiSelectorCheckBox( group, i, Mod.Conf[ group.GroupName ],
DrawMultiSelectorCheckBox( group, i, Mod.Settings[ group.GroupName ],
$"{group.Options[ i ].OptionName}##{group.GroupName}" );
}
@ -581,11 +581,11 @@ namespace Penumbra.UI
return;
}
var code = Mod.Conf[ group.GroupName ];
var code = Mod.Settings[ group.GroupName ];
if( ImGui.Combo( group.GroupName, ref code
, group.Options.Select( x => x.OptionName ).ToArray(), group.Options.Count ) )
{
Mod.Conf[ group.GroupName ] = code;
Mod.Settings[ group.GroupName ] = code;
Save();
}
}

View file

@ -110,14 +110,18 @@ namespace Penumbra.UI
if( ImGuiCustom.BeginFramedGroupEdit( ref groupName )
&& groupName != group.GroupName && !Meta!.Groups.ContainsKey( groupName ) )
{
var oldConf = Mod!.Conf[ group.GroupName ];
var oldConf = Mod!.Settings[ group.GroupName ];
Meta.Groups.Remove( group.GroupName );
Mod.Conf.Remove( group.GroupName );
Mod.FixSpecificSetting( group.GroupName );
if( groupName.Length > 0 )
{
Meta.Groups[ groupName ] = new OptionGroup()
{ GroupName = groupName, SelectionType = SelectType.Multi, Options = group.Options };
Mod.Conf[ groupName ] = oldConf;
{
GroupName = groupName,
SelectionType = SelectType.Multi,
Options = group.Options
};
Mod.Settings[ groupName ] = oldConf;
}
return true;
@ -143,14 +147,13 @@ namespace Penumbra.UI
private void DrawMultiSelectorEdit( OptionGroup group )
{
var nameBoxStart = CheckMarkSize;
var flag = Mod!.Conf[ group.GroupName ];
var modChanged = DrawMultiSelectorEditBegin( group );
var flag = Mod!.Settings[ group.GroupName ];
var modChanged = DrawMultiSelectorEditBegin( group );
for( var i = 0; i < group.Options.Count; ++i )
{
var opt = group.Options[ i ];
var label = $"##{opt.OptionName}_{group.GroupName}";
var label = $"##{group.GroupName}_{i}";
DrawMultiSelectorCheckBox( group, i, flag, label );
ImGui.SameLine();
@ -168,8 +171,9 @@ namespace Penumbra.UI
{
group.Options.RemoveAt( i );
var bitmaskFront = ( 1 << i ) - 1;
Mod.Conf[ group.GroupName ] = ( flag & bitmaskFront ) | ( ( flag & ~bitmaskFront ) >> 1 );
modChanged = true;
var bitmaskBack = ~( bitmaskFront | ( 1 << i ) );
Mod.Settings[ group.GroupName ] = ( flag & bitmaskFront ) | ( ( flag & bitmaskBack ) >> 1 );
modChanged = true;
}
else if( newName != opt.OptionName )
{
@ -191,24 +195,28 @@ namespace Penumbra.UI
ImGuiCustom.EndFramedGroup();
}
private bool DrawSingleSelectorEditGroup( OptionGroup group )
private bool DrawSingleSelectorEditGroup( OptionGroup group, ref bool selectionChanged )
{
var groupName = group.GroupName;
if( ImGui.InputText( $"##{groupName}_add", ref groupName, 64, ImGuiInputTextFlags.EnterReturnsTrue )
&& !Meta!.Groups.ContainsKey( groupName ) )
{
var oldConf = Mod!.Conf[ group.GroupName ];
var oldConf = Mod!.Settings[ group.GroupName ];
if( groupName != group.GroupName )
{
Meta.Groups.Remove( group.GroupName );
Mod.Conf.Remove( group.GroupName );
selectionChanged |= Mod.FixSpecificSetting( group.GroupName );
}
if( groupName.Length > 0 )
{
Meta.Groups.Add( groupName,
new OptionGroup() { GroupName = groupName, Options = group.Options, SelectionType = SelectType.Single } );
Mod.Conf[ groupName ] = oldConf;
Meta.Groups.Add( groupName, new OptionGroup()
{
GroupName = groupName,
Options = group.Options,
SelectionType = SelectType.Single
} );
Mod.Settings[ groupName ] = oldConf;
}
return true;
@ -219,7 +227,7 @@ namespace Penumbra.UI
private float DrawSingleSelectorEdit( OptionGroup group )
{
var code = Mod!.Conf[ group.GroupName ];
var code = Mod!.Settings[ group.GroupName ];
var selectionChanged = false;
var modChanged = false;
if( ImGuiCustom.RenameableCombo( $"##{group.GroupName}", ref code, out var newName,
@ -229,11 +237,15 @@ namespace Penumbra.UI
{
if( newName.Length > 0 )
{
selectionChanged = true;
modChanged = true;
Mod.Conf[ group.GroupName ] = code;
selectionChanged = true;
modChanged = true;
Mod.Settings[ group.GroupName ] = code;
group.Options.Add( new Option()
{ OptionName = newName, OptionDesc = "", OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >() } );
{
OptionName = newName,
OptionDesc = "",
OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >()
} );
}
}
else
@ -242,32 +254,30 @@ namespace Penumbra.UI
{
modChanged = true;
group.Options.RemoveAt( code );
if( code >= group.Options.Count )
{
code = 0;
}
}
else if( newName != group.Options[ code ].OptionName )
else
{
modChanged = true;
group.Options[ code ] = new Option()
if( newName != group.Options[ code ].OptionName )
{
OptionName = newName, OptionDesc = group.Options[ code ].OptionDesc,
OptionFiles = group.Options[ code ].OptionFiles
};
modChanged = true;
group.Options[ code ] = new Option()
{
OptionName = newName, OptionDesc = group.Options[ code ].OptionDesc,
OptionFiles = group.Options[ code ].OptionFiles
};
}
selectionChanged |= Mod.Settings[ group.GroupName ] != code;
Mod.Settings[ group.GroupName ] = code;
}
if( Mod.Conf[ group.GroupName ] != code )
{
selectionChanged = true;
Mod.Conf[ group.GroupName ] = code;
}
selectionChanged |= Mod.FixSpecificSetting( group.GroupName );
}
}
ImGui.SameLine();
var labelEditPos = ImGui.GetCursorPosX();
modChanged |= DrawSingleSelectorEditGroup( group );
modChanged |= DrawSingleSelectorEditGroup( group, ref selectionChanged );
if( modChanged )
{
@ -296,7 +306,7 @@ namespace Penumbra.UI
Options = new List< Option >()
};
Mod.Conf[ newGroup ] = 0;
Mod.Settings[ newGroup ] = 0;
_selector.SaveCurrentMod();
Save();
}