mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Add mod setting API functions.
This commit is contained in:
parent
df1a75b58a
commit
c578bd3a49
5 changed files with 245 additions and 39 deletions
|
|
@ -29,13 +29,14 @@ public enum PenumbraApiEc
|
|||
CollectionMissing = 2,
|
||||
ModMissing = 3,
|
||||
OptionGroupMissing = 4,
|
||||
SettingMissing = 5,
|
||||
OptionMissing = 5,
|
||||
|
||||
CharacterCollectionExists = 6,
|
||||
LowerPriority = 7,
|
||||
InvalidGamePath = 8,
|
||||
FileMissing = 9,
|
||||
InvalidManipulation = 10,
|
||||
InvalidArgument = 11,
|
||||
}
|
||||
|
||||
public interface IPenumbraApi : IPenumbraApiBase
|
||||
|
|
@ -75,7 +76,7 @@ public interface IPenumbraApi : IPenumbraApiBase
|
|||
public string ResolvePath( string gamePath, string characterName );
|
||||
|
||||
// Reverse resolves a given modded local path into its replacement in form of all applicable game path for given character
|
||||
public IList<string> ReverseResolvePath( string moddedPath, string characterName );
|
||||
public IList< string > ReverseResolvePath( string moddedPath, string characterName );
|
||||
|
||||
// Try to load a given gamePath with the resolved path from Penumbra.
|
||||
public T? GetFile< T >( string gamePath ) where T : FileResource;
|
||||
|
|
@ -109,12 +110,12 @@ public interface IPenumbraApi : IPenumbraApiBase
|
|||
|
||||
// Obtain the potential settings of a mod specified by its directory name first or mod name second.
|
||||
// Returns null if the mod could not be found.
|
||||
public IDictionary< string, (IList<string>, SelectType) >? GetAvailableModSettings( string modDirectory, string modName );
|
||||
public IDictionary< string, (IList< string >, SelectType) >? GetAvailableModSettings( string modDirectory, string modName );
|
||||
|
||||
// Obtain the enabled state, the priority, the settings of a mod specified by its directory name first or mod name second,
|
||||
// and whether these settings are inherited, or null if the collection does not set them at all.
|
||||
// If allowInheritance is false, only the collection itself will be checked.
|
||||
public (PenumbraApiEc, (bool, int, IDictionary< string, IList<string> >, bool)?) GetCurrentModSettings( string collectionName,
|
||||
public (PenumbraApiEc, (bool, int, IDictionary< string, IList< string > >, bool)?) GetCurrentModSettings( string collectionName,
|
||||
string modDirectory, string modName, bool allowInheritance );
|
||||
|
||||
// Try to set the inheritance state in the given collection of a mod specified by its directory name first or mod name second.
|
||||
|
|
@ -136,7 +137,7 @@ public interface IPenumbraApi : IPenumbraApiBase
|
|||
public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName, string option );
|
||||
|
||||
public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName,
|
||||
IReadOnlyList<string> options );
|
||||
IReadOnlyList< string > options );
|
||||
|
||||
|
||||
// Create a temporary collection without actual settings but with a cache.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
|
@ -12,12 +13,15 @@ using Penumbra.Collections;
|
|||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Api;
|
||||
|
||||
public class PenumbraApi : IDisposable, IPenumbraApi
|
||||
{
|
||||
public int ApiVersion { get; } = 4;
|
||||
public int ApiVersion
|
||||
=> 4;
|
||||
|
||||
private Penumbra? _penumbra;
|
||||
private Lumina.GameData? _lumina;
|
||||
public event GameObjectRedrawn? GameObjectRedrawn;
|
||||
|
|
@ -231,28 +235,173 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
}
|
||||
|
||||
public IDictionary< string, (IList< string >, SelectType) >? GetAvailableModSettings( string modDirectory, string modName )
|
||||
=> throw new NotImplementedException();
|
||||
{
|
||||
CheckInitialized();
|
||||
return Penumbra.ModManager.TryGetMod( modDirectory, modName, out var mod )
|
||||
? mod.Groups.ToDictionary( g => g.Name, g => ( ( IList< string > )g.Select( o => o.Name ).ToList(), g.Type ) )
|
||||
: null;
|
||||
}
|
||||
|
||||
public (PenumbraApiEc, (bool, int, IDictionary< string, IList< string > >, bool)?) GetCurrentModSettings( string collectionName,
|
||||
string modDirectory, string modName,
|
||||
bool allowInheritance )
|
||||
=> throw new NotImplementedException();
|
||||
string modDirectory, string modName, bool allowInheritance )
|
||||
{
|
||||
CheckInitialized();
|
||||
if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) )
|
||||
{
|
||||
return ( PenumbraApiEc.CollectionMissing, null );
|
||||
}
|
||||
|
||||
if( !Penumbra.ModManager.TryGetMod( modDirectory, modName, out var mod ) )
|
||||
{
|
||||
return ( PenumbraApiEc.ModMissing, null );
|
||||
}
|
||||
|
||||
var settings = allowInheritance ? collection.Settings[ mod.Index ] : collection[ mod.Index ].Settings;
|
||||
if( settings == null )
|
||||
{
|
||||
return ( PenumbraApiEc.Okay, null );
|
||||
}
|
||||
|
||||
var shareSettings = settings.ConvertToShareable( mod );
|
||||
return ( PenumbraApiEc.Okay,
|
||||
( shareSettings.Enabled, shareSettings.Priority, shareSettings.Settings, collection.Settings[ mod.Index ] != null ) );
|
||||
}
|
||||
|
||||
public PenumbraApiEc TryInheritMod( string collectionName, string modDirectory, string modName, bool inherit )
|
||||
=> throw new NotImplementedException();
|
||||
{
|
||||
CheckInitialized();
|
||||
if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) )
|
||||
{
|
||||
return PenumbraApiEc.CollectionMissing;
|
||||
}
|
||||
|
||||
if( !Penumbra.ModManager.TryGetMod( modDirectory, modName, out var mod ) )
|
||||
{
|
||||
return PenumbraApiEc.ModMissing;
|
||||
}
|
||||
|
||||
|
||||
return collection.SetModInheritance( mod.Index, inherit ) ? PenumbraApiEc.Okay : PenumbraApiEc.NothingChanged;
|
||||
}
|
||||
|
||||
public PenumbraApiEc TrySetMod( string collectionName, string modDirectory, string modName, bool enabled )
|
||||
=> throw new NotImplementedException();
|
||||
{
|
||||
CheckInitialized();
|
||||
if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) )
|
||||
{
|
||||
return PenumbraApiEc.CollectionMissing;
|
||||
}
|
||||
|
||||
if( !Penumbra.ModManager.TryGetMod( modDirectory, modName, out var mod ) )
|
||||
{
|
||||
return PenumbraApiEc.ModMissing;
|
||||
}
|
||||
|
||||
return collection.SetModState( mod.Index, enabled ) ? PenumbraApiEc.Okay : PenumbraApiEc.NothingChanged;
|
||||
}
|
||||
|
||||
public PenumbraApiEc TrySetModPriority( string collectionName, string modDirectory, string modName, int priority )
|
||||
=> throw new NotImplementedException();
|
||||
{
|
||||
CheckInitialized();
|
||||
if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) )
|
||||
{
|
||||
return PenumbraApiEc.CollectionMissing;
|
||||
}
|
||||
|
||||
public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName, string option )
|
||||
=> throw new NotImplementedException();
|
||||
if( !Penumbra.ModManager.TryGetMod( modDirectory, modName, out var mod ) )
|
||||
{
|
||||
return PenumbraApiEc.ModMissing;
|
||||
}
|
||||
|
||||
return collection.SetModPriority( mod.Index, priority ) ? PenumbraApiEc.Okay : PenumbraApiEc.NothingChanged;
|
||||
}
|
||||
|
||||
public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName,
|
||||
IReadOnlyList< string > options )
|
||||
=> throw new NotImplementedException();
|
||||
string optionName )
|
||||
{
|
||||
CheckInitialized();
|
||||
if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) )
|
||||
{
|
||||
return PenumbraApiEc.CollectionMissing;
|
||||
}
|
||||
|
||||
if( !Penumbra.ModManager.TryGetMod( modDirectory, modName, out var mod ) )
|
||||
{
|
||||
return PenumbraApiEc.ModMissing;
|
||||
}
|
||||
|
||||
var groupIdx = mod.Groups.IndexOf( g => g.Name == optionGroupName );
|
||||
if( groupIdx < 0 )
|
||||
{
|
||||
return PenumbraApiEc.OptionGroupMissing;
|
||||
}
|
||||
|
||||
var optionIdx = mod.Groups[ groupIdx ].IndexOf( o => o.Name == optionName );
|
||||
if( optionIdx < 0 )
|
||||
{
|
||||
return PenumbraApiEc.OptionMissing;
|
||||
}
|
||||
|
||||
var setting = mod.Groups[ groupIdx ].Type == SelectType.Multi ? 1u << optionIdx : ( uint )optionIdx;
|
||||
|
||||
return collection.SetModSetting( mod.Index, groupIdx, setting ) ? PenumbraApiEc.Okay : PenumbraApiEc.NothingChanged;
|
||||
}
|
||||
|
||||
public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName,
|
||||
IReadOnlyList< string > optionNames )
|
||||
{
|
||||
CheckInitialized();
|
||||
if( optionNames.Count == 0 )
|
||||
{
|
||||
return PenumbraApiEc.InvalidArgument;
|
||||
}
|
||||
|
||||
if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) )
|
||||
{
|
||||
return PenumbraApiEc.CollectionMissing;
|
||||
}
|
||||
|
||||
if( !Penumbra.ModManager.TryGetMod( modDirectory, modName, out var mod ) )
|
||||
{
|
||||
return PenumbraApiEc.ModMissing;
|
||||
}
|
||||
|
||||
var groupIdx = mod.Groups.IndexOf( g => g.Name == optionGroupName );
|
||||
if( groupIdx < 0 )
|
||||
{
|
||||
return PenumbraApiEc.OptionGroupMissing;
|
||||
}
|
||||
|
||||
var group = mod.Groups[ groupIdx ];
|
||||
|
||||
uint setting = 0;
|
||||
if( group.Type == SelectType.Single )
|
||||
{
|
||||
var name = optionNames[ ^1 ];
|
||||
var optionIdx = group.IndexOf( o => o.Name == name );
|
||||
if( optionIdx < 0 )
|
||||
{
|
||||
return PenumbraApiEc.OptionMissing;
|
||||
}
|
||||
|
||||
setting = ( uint )optionIdx;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach( var name in optionNames )
|
||||
{
|
||||
var optionIdx = group.IndexOf( o => o.Name == name );
|
||||
if( optionIdx < 0 )
|
||||
{
|
||||
return PenumbraApiEc.OptionMissing;
|
||||
}
|
||||
|
||||
setting |= 1u << optionIdx;
|
||||
}
|
||||
}
|
||||
|
||||
return collection.SetModSetting( mod.Index, groupIdx, setting ) ? PenumbraApiEc.Okay : PenumbraApiEc.NothingChanged;
|
||||
}
|
||||
|
||||
public PenumbraApiEc CreateTemporaryCollection( string collectionName, string? character, bool forceOverwriteCharacter )
|
||||
=> throw new NotImplementedException();
|
||||
|
|
|
|||
|
|
@ -24,17 +24,20 @@ public partial class ModCollection
|
|||
public event ModSettingChangeDelegate ModSettingChanged;
|
||||
|
||||
// Enable or disable the mod inheritance of mod idx.
|
||||
public void SetModInheritance( int idx, bool inherit )
|
||||
public bool SetModInheritance( int idx, bool inherit )
|
||||
{
|
||||
if( FixInheritance( idx, inherit ) )
|
||||
{
|
||||
ModSettingChanged.Invoke( ModSettingChange.Inheritance, idx, inherit ? 0 : 1, 0, false );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the enabled state mod idx to newValue if it differs from the current enabled state.
|
||||
// If mod idx is currently inherited, stop the inheritance.
|
||||
public void SetModState( int idx, bool newValue )
|
||||
public bool SetModState( int idx, bool newValue )
|
||||
{
|
||||
var oldValue = _settings[ idx ]?.Enabled ?? this[ idx ].Settings?.Enabled ?? false;
|
||||
if( newValue != oldValue )
|
||||
|
|
@ -42,7 +45,10 @@ public partial class ModCollection
|
|||
var inheritance = FixInheritance( idx, false );
|
||||
_settings[ idx ]!.Enabled = newValue;
|
||||
ModSettingChanged.Invoke( ModSettingChange.EnableState, idx, inheritance ? -1 : newValue ? 0 : 1, 0, false );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable or disable the mod inheritance of every mod in mods.
|
||||
|
|
@ -78,7 +84,7 @@ public partial class ModCollection
|
|||
|
||||
// Set the priority of mod idx to newValue if it differs from the current priority.
|
||||
// If mod idx is currently inherited, stop the inheritance.
|
||||
public void SetModPriority( int idx, int newValue )
|
||||
public bool SetModPriority( int idx, int newValue )
|
||||
{
|
||||
var oldValue = _settings[ idx ]?.Priority ?? this[ idx ].Settings?.Priority ?? 0;
|
||||
if( newValue != oldValue )
|
||||
|
|
@ -86,12 +92,15 @@ public partial class ModCollection
|
|||
var inheritance = FixInheritance( idx, false );
|
||||
_settings[ idx ]!.Priority = newValue;
|
||||
ModSettingChanged.Invoke( ModSettingChange.Priority, idx, inheritance ? -1 : oldValue, 0, false );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set a given setting group settingName of mod idx to newValue if it differs from the current value and fix it if necessary.
|
||||
// If mod idx is currently inherited, stop the inheritance.
|
||||
public void SetModSetting( int idx, int groupIdx, uint newValue )
|
||||
public bool SetModSetting( int idx, int groupIdx, uint newValue )
|
||||
{
|
||||
var settings = _settings[ idx ] != null ? _settings[ idx ]!.Settings : this[ idx ].Settings?.Settings;
|
||||
var oldValue = settings?[ groupIdx ] ?? 0;
|
||||
|
|
@ -100,31 +109,26 @@ public partial class ModCollection
|
|||
var inheritance = FixInheritance( idx, false );
|
||||
_settings[ idx ]!.SetValue( Penumbra.ModManager[ idx ], groupIdx, newValue );
|
||||
ModSettingChanged.Invoke( ModSettingChange.Setting, idx, inheritance ? -1 : ( int )oldValue, groupIdx, false );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Change one of the available mod settings for mod idx discerned by type.
|
||||
// If type == Setting, settingName should be a valid setting for that mod, otherwise it will be ignored.
|
||||
// The setting will also be automatically fixed if it is invalid for that setting group.
|
||||
// For boolean parameters, newValue == 0 will be treated as false and != 0 as true.
|
||||
public void ChangeModSetting( ModSettingChange type, int idx, int newValue, int groupIdx )
|
||||
public bool ChangeModSetting( ModSettingChange type, int idx, int newValue, int groupIdx )
|
||||
{
|
||||
switch( type )
|
||||
return type switch
|
||||
{
|
||||
case ModSettingChange.Inheritance:
|
||||
SetModInheritance( idx, newValue != 0 );
|
||||
break;
|
||||
case ModSettingChange.EnableState:
|
||||
SetModState( idx, newValue != 0 );
|
||||
break;
|
||||
case ModSettingChange.Priority:
|
||||
SetModPriority( idx, newValue );
|
||||
break;
|
||||
case ModSettingChange.Setting:
|
||||
SetModSetting( idx, groupIdx, ( uint )newValue );
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException( nameof( type ), type, null );
|
||||
}
|
||||
ModSettingChange.Inheritance => SetModInheritance( idx, newValue != 0 ),
|
||||
ModSettingChange.EnableState => SetModState( idx, newValue != 0 ),
|
||||
ModSettingChange.Priority => SetModPriority( idx, newValue ),
|
||||
ModSettingChange.Setting => SetModSetting( idx, groupIdx, ( uint )newValue ),
|
||||
_ => throw new ArgumentOutOfRangeException( nameof( type ), type, null ),
|
||||
};
|
||||
}
|
||||
|
||||
// Set inheritance of a mod without saving,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
|
|
@ -37,5 +38,28 @@ public sealed partial class Mod
|
|||
ModOptionChanged += OnModOptionChange;
|
||||
ModPathChanged += OnModPathChange;
|
||||
}
|
||||
|
||||
|
||||
// Try to obtain a mod by its directory name (unique identifier, preferred),
|
||||
// or the first mod of the given name if no directory fits.
|
||||
public bool TryGetMod( string modDirectory, string modName, [NotNullWhen( true )] out Mod? mod )
|
||||
{
|
||||
mod = null;
|
||||
foreach( var m in _mods )
|
||||
{
|
||||
if( m.ModPath.Name == modDirectory )
|
||||
{
|
||||
mod = m;
|
||||
return true;
|
||||
}
|
||||
|
||||
if( m.Name == modName )
|
||||
{
|
||||
mod ??= m;
|
||||
}
|
||||
}
|
||||
|
||||
return mod != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using OtterGui.Filesystem;
|
||||
using Penumbra.Util;
|
||||
|
||||
namespace Penumbra.Mods;
|
||||
|
||||
|
|
@ -10,7 +11,7 @@ namespace Penumbra.Mods;
|
|||
public class ModSettings
|
||||
{
|
||||
public static readonly ModSettings Empty = new();
|
||||
public List< uint > Settings { get; init; } = new();
|
||||
public List< uint > Settings { get; private init; } = new();
|
||||
public int Priority { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
|
|
@ -100,7 +101,7 @@ public class ModSettings
|
|||
private static uint FixSetting( IModGroup group, uint value )
|
||||
=> group.Type switch
|
||||
{
|
||||
SelectType.Single => ( uint )Math.Min( value, group.Count - 1 ),
|
||||
SelectType.Single => ( uint )Math.Min( value, group.Count - 1 ),
|
||||
SelectType.Multi => ( uint )( value & ( ( 1ul << group.Count ) - 1 ) ),
|
||||
_ => value,
|
||||
};
|
||||
|
|
@ -208,4 +209,31 @@ public class ModSettings
|
|||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the settings for a given mod in a shareable format, using the names of groups and options instead of indices.
|
||||
// Does not repair settings but ignores settings not fitting to the given mod.
|
||||
public (bool Enabled, int Priority, Dictionary< string, IList< string > > Settings) ConvertToShareable( Mod mod )
|
||||
{
|
||||
var dict = new Dictionary< string, IList< string > >( Settings.Count );
|
||||
foreach( var (setting, idx) in Settings.WithIndex() )
|
||||
{
|
||||
if( idx >= mod.Groups.Count )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var group = mod.Groups[ idx ];
|
||||
if( group.Type == SelectType.Single && setting < group.Count )
|
||||
{
|
||||
dict.Add( group.Name, new[] { group[ ( int )setting ].Name } );
|
||||
}
|
||||
else
|
||||
{
|
||||
var list = group.Where( ( _, optionIdx ) => ( setting & ( 1 << optionIdx ) ) != 0 ).Select( o => o.Name ).ToList();
|
||||
dict.Add( group.Name, list );
|
||||
}
|
||||
}
|
||||
|
||||
return ( Enabled, Priority, dict );
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue