Use external library for API interface and IPC.

This commit is contained in:
Ottermandias 2022-10-08 02:02:33 +02:00
parent b3f048bfe6
commit 918d5db6a6
69 changed files with 4026 additions and 1873 deletions

View file

@ -1,236 +0,0 @@
using Dalamud.Game.ClientState.Objects.Types;
using Lumina.Data;
using Penumbra.Collections;
using Penumbra.GameData.Enums;
using Penumbra.Mods;
using System;
using System.Collections.Generic;
namespace Penumbra.Api;
public interface IPenumbraApiBase
{
// The API version is staggered in two parts.
// The major/Breaking version only increments if there are changes breaking backwards compatibility.
// The minor/Feature version increments any time there is something added
// and resets when Breaking is incremented.
public (int Breaking, int Feature) ApiVersion { get; }
public bool Valid { get; }
}
public delegate void ChangedItemHover( object? item );
public delegate void ChangedItemClick( MouseButton button, object? item );
public delegate void GameObjectRedrawn( IntPtr objectPtr, int objectTableIndex );
public delegate void ModSettingChanged( ModSettingChange type, string collectionName, string modDirectory, bool inherited );
public delegate void CreatingCharacterBaseDelegate( IntPtr gameObject, ModCollection collection, IntPtr modelId, IntPtr customize,
IntPtr equipData );
public delegate void CreatedCharacterBaseDelegate( IntPtr gameObject, ModCollection collection, IntPtr drawObject );
public delegate void GameObjectResourceResolvedDelegate( IntPtr gameObject, string gamePath, string localPath );
public enum PenumbraApiEc
{
Success = 0,
NothingChanged = 1,
CollectionMissing = 2,
ModMissing = 3,
OptionGroupMissing = 4,
OptionMissing = 5,
CharacterCollectionExists = 6,
LowerPriority = 7,
InvalidGamePath = 8,
FileMissing = 9,
InvalidManipulation = 10,
InvalidArgument = 11,
UnknownError = 255,
}
public interface IPenumbraApi : IPenumbraApiBase
{
// Obtain the currently set mod directory from the configuration.
public string GetModDirectory();
// Fired whenever a mod directory change is finished.
// Gives the full path of the mod directory and whether Penumbra treats it as valid.
public event Action< string, bool >? ModDirectoryChanged;
// Obtain the entire current penumbra configuration as a json encoded string.
public string GetConfiguration();
// Triggered when the user hovers over a listed changed object in a mod tab.
// Can be used to append tooltips.
public event ChangedItemHover? ChangedItemTooltip;
// Events that are fired before and after the content of a mod settings panel are drawn.
// Both are fired inside the child window of the settings panel itself.
public event Action< string >? PreSettingsPanelDraw;
public event Action< string >? PostSettingsPanelDraw;
// Triggered when the user clicks a listed changed object in a mod tab.
public event ChangedItemClick? ChangedItemClicked;
public event GameObjectRedrawn? GameObjectRedrawn;
// Triggered when a character base is created and a corresponding gameObject could be found,
// before the Draw Object is actually created, so customize and equipdata can be manipulated beforehand.
public event CreatingCharacterBaseDelegate? CreatingCharacterBase;
// Triggered after a character base was created if a corresponding gameObject could be found,
// so you can apply flag changes after finishing.
public event CreatedCharacterBaseDelegate? CreatedCharacterBase;
// Triggered whenever a resource is redirected by Penumbra for a specific, identified game object.
// Does not trigger if the resource is not requested for a known game object.
public event GameObjectResourceResolvedDelegate? GameObjectResourceResolved;
// Queue redrawing of all actors of the given name with the given RedrawType.
public void RedrawObject( string name, RedrawType setting );
// Queue redrawing of the specific actor with the given RedrawType. Should only be used when the actor is sure to be valid.
public void RedrawObject( GameObject gameObject, RedrawType setting );
// Queue redrawing of the actor with the given object table index, if it exists, with the given RedrawType.
public void RedrawObject( int tableIndex, RedrawType setting );
// Queue redrawing of all currently available actors with the given RedrawType.
public void RedrawAll( RedrawType setting );
// Resolve a given gamePath via Penumbra using the Default collection.
// Returns the given gamePath if penumbra would not manipulate it.
public string ResolveDefaultPath( string gamePath );
// Resolve a given gamePath via Penumbra using the Interface collection.
// Returns the given gamePath if penumbra would not manipulate it.
public string ResolveInterfacePath( string gamePath );
// Resolve a given gamePath via Penumbra using the character collection for the given name (if it exists) and the Forced collections.
// Returns the given gamePath if penumbra would not manipulate it.
public string ResolvePath( string gamePath, string characterName );
// Resolve a given gamePath via Penumbra using any applicable character collections for the current character.
// Returns the given gamePath if penumbra would not manipulate it.
public string ResolvePlayerPath( string gamePath );
// Reverse resolves a given modded local path into its replacement in form of all applicable game paths for given character collection.
public string[] ReverseResolvePath( string moddedPath, string characterName );
// Reverse resolves a given modded local path into its replacement in form of all applicable game paths
// using the collection applying to the player character.
public string[] ReverseResolvePlayerPath( string moddedPath );
// Try to load a given gamePath with the resolved path from Penumbra.
public T? GetFile< T >( string gamePath ) where T : FileResource;
// Try to load a given gamePath with the resolved path from Penumbra.
public T? GetFile< T >( string gamePath, string characterName ) where T : FileResource;
// Gets a dictionary of effected items from a collection
public IReadOnlyDictionary< string, object? > GetChangedItemsForCollection( string collectionName );
// Obtain a list of the names of all currently installed collections.
public IList< string > GetCollections();
// Obtain the name of the currently selected collection.
public string GetCurrentCollection();
// Obtain the name of the default collection.
public string GetDefaultCollection();
// Obtain the name of the interface collection.
public string GetInterfaceCollection();
// Obtain the name of the collection associated with characterName and whether it is configured or inferred from default.
public (string, bool) GetCharacterCollection( string characterName );
// Obtain the game object associated with a given draw object and the name of the collection associated with this game object.
public (IntPtr, string) GetDrawObjectInfo( IntPtr drawObject );
// Obtain the parent game object index for an unnamed cutscene actor by its index.
public int GetCutsceneParentIndex( int actor );
// Obtain a list of all installed mods. The first string is their directory name, the second string is their mod name.
public IList< (string, string) > GetModList();
// Try to reload an existing mod by its directory name or mod name.
// Can return ModMissing or success.
// Reload is the same as if triggered by button press and might delete the mod if it is not valid anymore.
public PenumbraApiEc ReloadMod( string modDirectory, string modName );
// Try to add a new mod inside the mod root directory (modDirectory should only be the name, not the full name).
// Returns FileMissing if the directory does not exist or success otherwise.
// Note that success does only imply a successful call, not a successful mod load.
public PenumbraApiEc AddMod( string modDirectory );
// Obtain a base64 encoded, zipped json-string with a prepended version-byte of the current manipulations
// for the collection currently associated with the player.
public string GetPlayerMetaManipulations();
// Obtain a base64 encoded, zipped json-string with a prepended version-byte of the current manipulations
// for the given collection associated with the character name, or the default collection.
public string GetMetaManipulations( string characterName );
// ############## Mod Settings #################
// 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 );
// 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,
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.
// Returns Okay, NothingChanged, CollectionMissing or ModMissing.
public PenumbraApiEc TryInheritMod( string collectionName, string modDirectory, string modName, bool inherit );
// Try to set the enabled state in the given collection of a mod specified by its directory name first or mod name second. Also removes inheritance.
// Returns Okay, NothingChanged, CollectionMissing or ModMissing.
public PenumbraApiEc TrySetMod( string collectionName, string modDirectory, string modName, bool enabled );
// Try to set the priority in the given collection of a mod specified by its directory name first or mod name second. Also removes inheritance.
// Returns Okay, NothingChanged, CollectionMissing or ModMissing.
public PenumbraApiEc TrySetModPriority( string collectionName, string modDirectory, string modName, int priority );
// Try to set a specific option group in the given collection of a mod specified by its directory name first or mod name second. Also removes inheritance.
// If the group is a Single Selection group, options should be a single string, otherwise the array of enabled options.
// Returns Okay, NothingChanged, CollectionMissing or ModMissing, OptionGroupMissing or SettingMissing.
// If any setting can not be found, it will not change anything.
public PenumbraApiEc TrySetModSetting( string collectionName, string modDirectory, string modName, string optionGroupName, string option );
public PenumbraApiEc TrySetModSettings( string collectionName, string modDirectory, string modName, string optionGroupName,
IReadOnlyList< string > options );
// This event gets fired when any setting in any collection changes.
public event ModSettingChanged? ModSettingChanged;
// Create a temporary collection without actual settings but with a cache.
// If no character collection for this character exists or forceOverwriteCharacter is true,
// associate this collection to a specific character.
// Can return Okay, CharacterCollectionExists or NothingChanged, as well as the name of the new temporary collection on success.
public (PenumbraApiEc, string) CreateTemporaryCollection( string tag, string character, bool forceOverwriteCharacter );
// Remove the temporary collection associated with characterName if it exists.
// Can return Okay or NothingChanged.
public PenumbraApiEc RemoveTemporaryCollection( string characterName );
// Set a temporary mod with the given paths, manipulations and priority and the name tag to all collections.
// Can return Okay, InvalidGamePath, or InvalidManipulation.
public PenumbraApiEc AddTemporaryModAll( string tag, Dictionary< string, string > paths, string manipString, int priority );
// Set a temporary mod with the given paths, manipulations and priority and the name tag to the collection with the given name, which can be temporary.
// Can return Okay, MissingCollection InvalidGamePath, or InvalidManipulation.
public PenumbraApiEc AddTemporaryMod( string tag, string collectionName, Dictionary< string, string > paths, string manipString,
int priority );
// Remove the temporary mod with the given tag and priority from the temporary mods applying to all collections, if it exists.
// Can return Okay or NothingDone.
public PenumbraApiEc RemoveTemporaryModAll( string tag, int priority );
// Remove the temporary mod with the given tag and priority from the temporary mods applying to the collection of the given name, which can be temporary.
// Can return Okay or NothingDone.
public PenumbraApiEc RemoveTemporaryMod( string tag, string collectionName, int priority );
}

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,6 @@ using Newtonsoft.Json;
using OtterGui;
using Penumbra.Collections;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Resolver;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Manipulations;
@ -15,6 +14,7 @@ using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using Penumbra.Api.Enums;
namespace Penumbra.Api;
@ -272,7 +272,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return Penumbra.ModManager.Select( m => ( m.ModPath.Name, m.Name.Text ) ).ToArray();
}
public IDictionary< string, (IList< string >, SelectType) >? GetAvailableModSettings( string modDirectory, string modName )
public IDictionary< string, (IList< string >, GroupType) >? GetAvailableModSettings( string modDirectory, string modName )
{
CheckInitialized();
return Penumbra.ModManager.TryGetMod( modDirectory, modName, out var mod )
@ -330,6 +330,56 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return PenumbraApiEc.Success;
}
public PenumbraApiEc DeleteMod( string modDirectory, string modName )
{
CheckInitialized();
if( !Penumbra.ModManager.TryGetMod( modDirectory, modName, out var mod ) )
return PenumbraApiEc.NothingChanged;
Penumbra.ModManager.DeleteMod( mod.Index );
return PenumbraApiEc.Success;
}
public (PenumbraApiEc, string, bool) GetModPath( string modDirectory, string modName )
{
CheckInitialized();
if( !Penumbra.ModManager.TryGetMod( modDirectory, modName, out var mod )
|| !_penumbra!.ModFileSystem.FindLeaf( mod, out var leaf ) )
{
return ( PenumbraApiEc.ModMissing, string.Empty, false );
}
var fullPath = leaf.FullName();
return ( PenumbraApiEc.Success, fullPath, !ModFileSystem.ModHasDefaultPath( mod, fullPath ) );
}
public PenumbraApiEc SetModPath( string modDirectory, string modName, string newPath )
{
CheckInitialized();
if( newPath.Length == 0 )
{
return PenumbraApiEc.InvalidArgument;
}
if( !Penumbra.ModManager.TryGetMod( modDirectory, modName, out var mod )
|| !_penumbra!.ModFileSystem.FindLeaf( mod, out var leaf ) )
{
return PenumbraApiEc.ModMissing;
}
try
{
_penumbra.ModFileSystem.RenameAndMove( leaf, newPath );
return PenumbraApiEc.Success;
}
catch
{
return PenumbraApiEc.PathRenameFailed;
}
}
public PenumbraApiEc TryInheritMod( string collectionName, string modDirectory, string modName, bool inherit )
{
CheckInitialized();
@ -405,7 +455,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return PenumbraApiEc.OptionMissing;
}
var setting = mod.Groups[ groupIdx ].Type == SelectType.Multi ? 1u << optionIdx : ( uint )optionIdx;
var setting = mod.Groups[ groupIdx ].Type == GroupType.Multi ? 1u << optionIdx : ( uint )optionIdx;
return collection.SetModSetting( mod.Index, groupIdx, setting ) ? PenumbraApiEc.Success : PenumbraApiEc.NothingChanged;
}
@ -433,7 +483,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
var group = mod.Groups[ groupIdx ];
uint setting = 0;
if( group.Type == SelectType.Single )
if( group.Type == GroupType.Single )
{
var optionIdx = optionNames.Count == 0 ? -1 : group.IndexOf( o => o.Name == optionNames[ ^1 ] );
if( optionIdx < 0 )

View file

@ -1,878 +0,0 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Penumbra.Collections;
using Penumbra.GameData.Enums;
using System;
using System.Collections.Generic;
namespace Penumbra.Api;
public partial class PenumbraIpc : IDisposable
{
internal readonly IPenumbraApi Api;
internal readonly IpcTester Tester;
public PenumbraIpc( DalamudPluginInterface pi, IPenumbraApi api )
{
Api = api;
Tester = new IpcTester( pi, this );
InitializeGeneralProviders( pi );
InitializeResolveProviders( pi );
InitializeRedrawProviders( pi );
InitializeChangedItemProviders( pi );
InitializeDataProviders( pi );
InitializeSettingProviders( pi );
InitializeTempProviders( pi );
ProviderInitialized?.SendMessage();
InvokeModDirectoryChanged( Penumbra.ModManager.BasePath.FullName, Penumbra.ModManager.Valid );
}
public void Dispose()
{
DisposeDataProviders();
DisposeChangedItemProviders();
DisposeRedrawProviders();
DisposeResolveProviders();
DisposeGeneralProviders();
DisposeSettingProviders();
DisposeTempProviders();
ProviderDisposed?.SendMessage();
Tester.Dispose();
}
}
public partial class PenumbraIpc
{
public const string LabelProviderInitialized = "Penumbra.Initialized";
public const string LabelProviderDisposed = "Penumbra.Disposed";
public const string LabelProviderApiVersion = "Penumbra.ApiVersion";
public const string LabelProviderApiVersions = "Penumbra.ApiVersions";
public const string LabelProviderGetModDirectory = "Penumbra.GetModDirectory";
public const string LabelProviderModDirectoryChanged = "Penumbra.ModDirectoryChanged";
public const string LabelProviderGetConfiguration = "Penumbra.GetConfiguration";
public const string LabelProviderPreSettingsDraw = "Penumbra.PreSettingsDraw";
public const string LabelProviderPostSettingsDraw = "Penumbra.PostSettingsDraw";
internal ICallGateProvider< object? >? ProviderInitialized;
internal ICallGateProvider< object? >? ProviderDisposed;
internal ICallGateProvider< int >? ProviderApiVersion;
internal ICallGateProvider< (int Breaking, int Features) >? ProviderApiVersions;
internal ICallGateProvider< string >? ProviderGetModDirectory;
internal ICallGateProvider< string, bool, object? >? ProviderModDirectoryChanged;
internal ICallGateProvider< string >? ProviderGetConfiguration;
internal ICallGateProvider< string, object? >? ProviderPreSettingsDraw;
internal ICallGateProvider< string, object? >? ProviderPostSettingsDraw;
private void InitializeGeneralProviders( DalamudPluginInterface pi )
{
try
{
ProviderInitialized = pi.GetIpcProvider< object? >( LabelProviderInitialized );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderInitialized}:\n{e}" );
}
try
{
ProviderDisposed = pi.GetIpcProvider< object? >( LabelProviderDisposed );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderDisposed}:\n{e}" );
}
try
{
ProviderApiVersion = pi.GetIpcProvider< int >( LabelProviderApiVersion );
ProviderApiVersion.RegisterFunc( () =>
{
Penumbra.Log.Warning( $"{LabelProviderApiVersion} is outdated. Please use {LabelProviderApiVersions} instead." );
return Api.ApiVersion.Breaking;
} );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderApiVersion}:\n{e}" );
}
try
{
ProviderApiVersions = pi.GetIpcProvider< ( int, int ) >( LabelProviderApiVersions );
ProviderApiVersions.RegisterFunc( () => Api.ApiVersion );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderApiVersions}:\n{e}" );
}
try
{
ProviderGetModDirectory = pi.GetIpcProvider< string >( LabelProviderGetModDirectory );
ProviderGetModDirectory.RegisterFunc( Api.GetModDirectory );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderGetModDirectory}:\n{e}" );
}
try
{
ProviderModDirectoryChanged = pi.GetIpcProvider< string, bool, object? >( LabelProviderModDirectoryChanged );
Api.ModDirectoryChanged += InvokeModDirectoryChanged;
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderModDirectoryChanged}:\n{e}" );
}
try
{
ProviderGetConfiguration = pi.GetIpcProvider< string >( LabelProviderGetConfiguration );
ProviderGetConfiguration.RegisterFunc( Api.GetConfiguration );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderGetConfiguration}:\n{e}" );
}
try
{
ProviderPreSettingsDraw = pi.GetIpcProvider< string, object? >( LabelProviderPreSettingsDraw );
Api.PreSettingsPanelDraw += InvokeSettingsPreDraw;
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderPreSettingsDraw}:\n{e}" );
}
try
{
ProviderPostSettingsDraw = pi.GetIpcProvider< string, object? >( LabelProviderPostSettingsDraw );
Api.PostSettingsPanelDraw += InvokeSettingsPostDraw;
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderPostSettingsDraw}:\n{e}" );
}
}
private void DisposeGeneralProviders()
{
ProviderGetConfiguration?.UnregisterFunc();
ProviderGetModDirectory?.UnregisterFunc();
ProviderApiVersion?.UnregisterFunc();
ProviderApiVersions?.UnregisterFunc();
Api.PreSettingsPanelDraw -= InvokeSettingsPreDraw;
Api.PostSettingsPanelDraw -= InvokeSettingsPostDraw;
Api.ModDirectoryChanged -= InvokeModDirectoryChanged;
}
private void InvokeSettingsPreDraw( string modDirectory )
=> ProviderPreSettingsDraw!.SendMessage( modDirectory );
private void InvokeSettingsPostDraw( string modDirectory )
=> ProviderPostSettingsDraw!.SendMessage( modDirectory );
private void InvokeModDirectoryChanged( string modDirectory, bool valid )
=> ProviderModDirectoryChanged?.SendMessage( modDirectory, valid );
}
public partial class PenumbraIpc
{
public const string LabelProviderRedrawObject = "Penumbra.RedrawObject";
public const string LabelProviderRedrawName = "Penumbra.RedrawObjectByName";
public const string LabelProviderRedrawIndex = "Penumbra.RedrawObjectByIndex";
public const string LabelProviderRedrawAll = "Penumbra.RedrawAll";
public const string LabelProviderGameObjectRedrawn = "Penumbra.GameObjectRedrawn";
internal ICallGateProvider< string, int, object? >? ProviderRedrawName;
internal ICallGateProvider< GameObject, int, object? >? ProviderRedrawObject;
internal ICallGateProvider< int, int, object? >? ProviderRedrawIndex;
internal ICallGateProvider< int, object? >? ProviderRedrawAll;
internal ICallGateProvider< IntPtr, int, object? >? ProviderGameObjectRedrawn;
private static RedrawType CheckRedrawType( int value )
{
var type = ( RedrawType )value;
if( Enum.IsDefined( type ) )
{
return type;
}
throw new Exception( "The integer provided for a Redraw Function was not a valid RedrawType." );
}
private void InitializeRedrawProviders( DalamudPluginInterface pi )
{
try
{
ProviderRedrawName = pi.GetIpcProvider< string, int, object? >( LabelProviderRedrawName );
ProviderRedrawName.RegisterAction( ( s, i ) => Api.RedrawObject( s, CheckRedrawType( i ) ) );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderRedrawName}:\n{e}" );
}
try
{
ProviderRedrawObject = pi.GetIpcProvider< GameObject, int, object? >( LabelProviderRedrawObject );
ProviderRedrawObject.RegisterAction( ( s, i ) => Api.RedrawObject( s, CheckRedrawType( i ) ) );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderRedrawObject}:\n{e}" );
}
try
{
ProviderRedrawIndex = pi.GetIpcProvider< int, int, object? >( LabelProviderRedrawIndex );
ProviderRedrawIndex.RegisterAction( ( idx, i ) => Api.RedrawObject( idx, CheckRedrawType( i ) ) );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderRedrawName}:\n{e}" );
}
try
{
ProviderRedrawAll = pi.GetIpcProvider< int, object? >( LabelProviderRedrawAll );
ProviderRedrawAll.RegisterAction( i => Api.RedrawAll( CheckRedrawType( i ) ) );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderRedrawAll}:\n{e}" );
}
try
{
ProviderGameObjectRedrawn = pi.GetIpcProvider< IntPtr, int, object? >( LabelProviderGameObjectRedrawn );
Api.GameObjectRedrawn += OnGameObjectRedrawn;
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderGameObjectRedrawn}:\n{e}" );
}
}
private void OnGameObjectRedrawn( IntPtr objectAddress, int objectTableIndex )
=> ProviderGameObjectRedrawn?.SendMessage( objectAddress, objectTableIndex );
private void DisposeRedrawProviders()
{
ProviderRedrawName?.UnregisterAction();
ProviderRedrawObject?.UnregisterAction();
ProviderRedrawIndex?.UnregisterAction();
ProviderRedrawAll?.UnregisterAction();
Api.GameObjectRedrawn -= OnGameObjectRedrawn;
}
}
public partial class PenumbraIpc
{
public const string LabelProviderResolveDefault = "Penumbra.ResolveDefaultPath";
public const string LabelProviderResolveInterface = "Penumbra.ResolveInterfacePath";
public const string LabelProviderResolveCharacter = "Penumbra.ResolveCharacterPath";
public const string LabelProviderResolvePlayer = "Penumbra.ResolvePlayerPath";
public const string LabelProviderGetDrawObjectInfo = "Penumbra.GetDrawObjectInfo";
public const string LabelProviderGetCutsceneParentIndex = "Penumbra.GetCutsceneParentIndex";
public const string LabelProviderReverseResolvePath = "Penumbra.ReverseResolvePath";
public const string LabelProviderReverseResolvePlayerPath = "Penumbra.ReverseResolvePlayerPath";
public const string LabelProviderCreatingCharacterBase = "Penumbra.CreatingCharacterBase";
public const string LabelProviderCreatedCharacterBase = "Penumbra.CreatedCharacterBase";
public const string LabelProviderGameObjectResourcePathResolved = "Penumbra.GameObjectResourcePathResolved";
internal ICallGateProvider< string, string >? ProviderResolveDefault;
internal ICallGateProvider< string, string >? ProviderResolveInterface;
internal ICallGateProvider< string, string, string >? ProviderResolveCharacter;
internal ICallGateProvider< string, string >? ProviderResolvePlayer;
internal ICallGateProvider< IntPtr, (IntPtr, string) >? ProviderGetDrawObjectInfo;
internal ICallGateProvider< int, int >? ProviderGetCutsceneParentIndex;
internal ICallGateProvider< string, string, string[] >? ProviderReverseResolvePath;
internal ICallGateProvider< string, string[] >? ProviderReverseResolvePathPlayer;
internal ICallGateProvider< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >? ProviderCreatingCharacterBase;
internal ICallGateProvider< IntPtr, string, IntPtr, object? >? ProviderCreatedCharacterBase;
internal ICallGateProvider< IntPtr, string, string, object? >? ProviderGameObjectResourcePathResolved;
private void InitializeResolveProviders( DalamudPluginInterface pi )
{
try
{
ProviderResolveDefault = pi.GetIpcProvider< string, string >( LabelProviderResolveDefault );
ProviderResolveDefault.RegisterFunc( Api.ResolveDefaultPath );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderResolveDefault}:\n{e}" );
}
try
{
ProviderResolveInterface = pi.GetIpcProvider< string, string >( LabelProviderResolveInterface );
ProviderResolveInterface.RegisterFunc( Api.ResolveInterfacePath );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderResolveInterface}:\n{e}" );
}
try
{
ProviderResolveCharacter = pi.GetIpcProvider< string, string, string >( LabelProviderResolveCharacter );
ProviderResolveCharacter.RegisterFunc( Api.ResolvePath );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderResolveCharacter}:\n{e}" );
}
try
{
ProviderResolvePlayer = pi.GetIpcProvider< string, string >( LabelProviderResolvePlayer );
ProviderResolvePlayer.RegisterFunc( Api.ResolvePlayerPath );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderResolveCharacter}:\n{e}" );
}
try
{
ProviderGetDrawObjectInfo = pi.GetIpcProvider< IntPtr, (IntPtr, string) >( LabelProviderGetDrawObjectInfo );
ProviderGetDrawObjectInfo.RegisterFunc( Api.GetDrawObjectInfo );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderGetDrawObjectInfo}:\n{e}" );
}
try
{
ProviderGetCutsceneParentIndex = pi.GetIpcProvider< int, int >( LabelProviderGetCutsceneParentIndex );
ProviderGetCutsceneParentIndex.RegisterFunc( Api.GetCutsceneParentIndex );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderGetCutsceneParentIndex}:\n{e}" );
}
try
{
ProviderReverseResolvePath = pi.GetIpcProvider< string, string, string[] >( LabelProviderReverseResolvePath );
ProviderReverseResolvePath.RegisterFunc( Api.ReverseResolvePath );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderReverseResolvePath}:\n{e}" );
}
try
{
ProviderReverseResolvePathPlayer = pi.GetIpcProvider< string, string[] >( LabelProviderReverseResolvePlayerPath );
ProviderReverseResolvePathPlayer.RegisterFunc( Api.ReverseResolvePlayerPath );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderReverseResolvePlayerPath}:\n{e}" );
}
try
{
ProviderCreatingCharacterBase =
pi.GetIpcProvider< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >( LabelProviderCreatingCharacterBase );
Api.CreatingCharacterBase += CreatingCharacterBaseEvent;
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderCreatingCharacterBase}:\n{e}" );
}
try
{
ProviderCreatedCharacterBase =
pi.GetIpcProvider< IntPtr, string, IntPtr, object? >( LabelProviderCreatedCharacterBase );
Api.CreatedCharacterBase += CreatedCharacterBaseEvent;
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderCreatedCharacterBase}:\n{e}" );
}
try
{
ProviderGameObjectResourcePathResolved =
pi.GetIpcProvider< IntPtr, string, string, object? >( LabelProviderGameObjectResourcePathResolved );
Api.GameObjectResourceResolved += GameObjectResourceResolvedEvent;
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderGameObjectResourcePathResolved}:\n{e}" );
}
}
private void GameObjectResourceResolvedEvent( IntPtr gameObject, string gamePath, string localPath )
{
ProviderGameObjectResourcePathResolved?.SendMessage( gameObject, gamePath, localPath );
}
private void DisposeResolveProviders()
{
ProviderGetDrawObjectInfo?.UnregisterFunc();
ProviderGetCutsceneParentIndex?.UnregisterFunc();
ProviderResolveDefault?.UnregisterFunc();
ProviderResolveInterface?.UnregisterFunc();
ProviderResolveCharacter?.UnregisterFunc();
ProviderReverseResolvePath?.UnregisterFunc();
ProviderReverseResolvePathPlayer?.UnregisterFunc();
Api.CreatingCharacterBase -= CreatingCharacterBaseEvent;
Api.CreatedCharacterBase -= CreatedCharacterBaseEvent;
Api.GameObjectResourceResolved -= GameObjectResourceResolvedEvent;
}
private void CreatingCharacterBaseEvent( IntPtr gameObject, ModCollection collection, IntPtr modelId, IntPtr customize, IntPtr equipData )
{
ProviderCreatingCharacterBase?.SendMessage( gameObject, collection.Name, modelId, customize, equipData );
}
private void CreatedCharacterBaseEvent( IntPtr gameObject, ModCollection collection, IntPtr drawObject )
{
ProviderCreatedCharacterBase?.SendMessage( gameObject, collection.Name, drawObject );
}
}
public partial class PenumbraIpc
{
public const string LabelProviderChangedItemTooltip = "Penumbra.ChangedItemTooltip";
public const string LabelProviderChangedItemClick = "Penumbra.ChangedItemClick";
public const string LabelProviderGetChangedItems = "Penumbra.GetChangedItems";
internal ICallGateProvider< ChangedItemType, uint, object? >? ProviderChangedItemTooltip;
internal ICallGateProvider< MouseButton, ChangedItemType, uint, object? >? ProviderChangedItemClick;
internal ICallGateProvider< string, IReadOnlyDictionary< string, object? > >? ProviderGetChangedItems;
private void OnClick( MouseButton click, object? item )
{
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId( item );
ProviderChangedItemClick?.SendMessage( click, type, id );
}
private void OnTooltip( object? item )
{
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId( item );
ProviderChangedItemTooltip?.SendMessage( type, id );
}
private void InitializeChangedItemProviders( DalamudPluginInterface pi )
{
try
{
ProviderChangedItemTooltip = pi.GetIpcProvider< ChangedItemType, uint, object? >( LabelProviderChangedItemTooltip );
Api.ChangedItemTooltip += OnTooltip;
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderChangedItemTooltip}:\n{e}" );
}
try
{
ProviderChangedItemClick = pi.GetIpcProvider< MouseButton, ChangedItemType, uint, object? >( LabelProviderChangedItemClick );
Api.ChangedItemClicked += OnClick;
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" );
}
try
{
ProviderGetChangedItems = pi.GetIpcProvider< string, IReadOnlyDictionary< string, object? > >( LabelProviderGetChangedItems );
ProviderGetChangedItems.RegisterFunc( Api.GetChangedItemsForCollection );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" );
}
}
private void DisposeChangedItemProviders()
{
ProviderGetChangedItems?.UnregisterFunc();
Api.ChangedItemClicked -= OnClick;
Api.ChangedItemTooltip -= OnTooltip;
}
}
public partial class PenumbraIpc
{
public const string LabelProviderGetMods = "Penumbra.GetMods";
public const string LabelProviderGetCollections = "Penumbra.GetCollections";
public const string LabelProviderCurrentCollectionName = "Penumbra.GetCurrentCollectionName";
public const string LabelProviderDefaultCollectionName = "Penumbra.GetDefaultCollectionName";
public const string LabelProviderInterfaceCollectionName = "Penumbra.GetInterfaceCollectionName";
public const string LabelProviderCharacterCollectionName = "Penumbra.GetCharacterCollectionName";
public const string LabelProviderGetPlayerMetaManipulations = "Penumbra.GetPlayerMetaManipulations";
public const string LabelProviderGetMetaManipulations = "Penumbra.GetMetaManipulations";
internal ICallGateProvider< IList< (string, string) > >? ProviderGetMods;
internal ICallGateProvider< IList< string > >? ProviderGetCollections;
internal ICallGateProvider< string >? ProviderCurrentCollectionName;
internal ICallGateProvider< string >? ProviderDefaultCollectionName;
internal ICallGateProvider< string >? ProviderInterfaceCollectionName;
internal ICallGateProvider< string, (string, bool) >? ProviderCharacterCollectionName;
internal ICallGateProvider< string >? ProviderGetPlayerMetaManipulations;
internal ICallGateProvider< string, string >? ProviderGetMetaManipulations;
private void InitializeDataProviders( DalamudPluginInterface pi )
{
try
{
ProviderGetMods = pi.GetIpcProvider< IList< (string, string) > >( LabelProviderGetMods );
ProviderGetMods.RegisterFunc( Api.GetModList );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderGetMods}:\n{e}" );
}
try
{
ProviderGetCollections = pi.GetIpcProvider< IList< string > >( LabelProviderGetCollections );
ProviderGetCollections.RegisterFunc( Api.GetCollections );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderGetCollections}:\n{e}" );
}
try
{
ProviderCurrentCollectionName = pi.GetIpcProvider< string >( LabelProviderCurrentCollectionName );
ProviderCurrentCollectionName.RegisterFunc( Api.GetCurrentCollection );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderCurrentCollectionName}:\n{e}" );
}
try
{
ProviderDefaultCollectionName = pi.GetIpcProvider< string >( LabelProviderDefaultCollectionName );
ProviderDefaultCollectionName.RegisterFunc( Api.GetDefaultCollection );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderDefaultCollectionName}:\n{e}" );
}
try
{
ProviderInterfaceCollectionName = pi.GetIpcProvider<string>( LabelProviderInterfaceCollectionName );
ProviderInterfaceCollectionName.RegisterFunc( Api.GetInterfaceCollection );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderInterfaceCollectionName}:\n{e}" );
}
try
{
ProviderCharacterCollectionName = pi.GetIpcProvider< string, (string, bool) >( LabelProviderCharacterCollectionName );
ProviderCharacterCollectionName.RegisterFunc( Api.GetCharacterCollection );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderCharacterCollectionName}:\n{e}" );
}
try
{
ProviderGetPlayerMetaManipulations = pi.GetIpcProvider< string >( LabelProviderGetPlayerMetaManipulations );
ProviderGetPlayerMetaManipulations.RegisterFunc( Api.GetPlayerMetaManipulations );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderGetPlayerMetaManipulations}:\n{e}" );
}
try
{
ProviderGetMetaManipulations = pi.GetIpcProvider< string, string >( LabelProviderGetMetaManipulations );
ProviderGetMetaManipulations.RegisterFunc( Api.GetMetaManipulations );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderGetMetaManipulations}:\n{e}" );
}
}
private void DisposeDataProviders()
{
ProviderGetMods?.UnregisterFunc();
ProviderGetCollections?.UnregisterFunc();
ProviderCurrentCollectionName?.UnregisterFunc();
ProviderDefaultCollectionName?.UnregisterFunc();
ProviderInterfaceCollectionName?.UnregisterFunc();
ProviderCharacterCollectionName?.UnregisterFunc();
ProviderGetMetaManipulations?.UnregisterFunc();
}
}
public partial class PenumbraIpc
{
public const string LabelProviderGetAvailableModSettings = "Penumbra.GetAvailableModSettings";
public const string LabelProviderReloadMod = "Penumbra.ReloadMod";
public const string LabelProviderAddMod = "Penumbra.AddMod";
public const string LabelProviderGetCurrentModSettings = "Penumbra.GetCurrentModSettings";
public const string LabelProviderTryInheritMod = "Penumbra.TryInheritMod";
public const string LabelProviderTrySetMod = "Penumbra.TrySetMod";
public const string LabelProviderTrySetModPriority = "Penumbra.TrySetModPriority";
public const string LabelProviderTrySetModSetting = "Penumbra.TrySetModSetting";
public const string LabelProviderTrySetModSettings = "Penumbra.TrySetModSettings";
public const string LabelProviderModSettingChanged = "Penumbra.ModSettingChanged";
internal ICallGateProvider< ModSettingChange, string, string, bool, object? >? ProviderModSettingChanged;
internal ICallGateProvider< string, string, IDictionary< string, (IList< string >, Mods.SelectType) >? >? ProviderGetAvailableModSettings;
internal ICallGateProvider< string, string, PenumbraApiEc >? ProviderReloadMod;
internal ICallGateProvider< string, PenumbraApiEc >? ProviderAddMod;
internal ICallGateProvider< string, string, string, bool, (PenumbraApiEc, (bool, int, IDictionary< string, IList< string > >, bool)?) >?
ProviderGetCurrentModSettings;
internal ICallGateProvider< string, string, string, bool, PenumbraApiEc >? ProviderTryInheritMod;
internal ICallGateProvider< string, string, string, bool, PenumbraApiEc >? ProviderTrySetMod;
internal ICallGateProvider< string, string, string, int, PenumbraApiEc >? ProviderTrySetModPriority;
internal ICallGateProvider< string, string, string, string, string, PenumbraApiEc >? ProviderTrySetModSetting;
internal ICallGateProvider< string, string, string, string, IReadOnlyList< string >, PenumbraApiEc >? ProviderTrySetModSettings;
private void InitializeSettingProviders( DalamudPluginInterface pi )
{
try
{
ProviderModSettingChanged = pi.GetIpcProvider< ModSettingChange, string, string, bool, object? >( LabelProviderModSettingChanged );
Api.ModSettingChanged += InvokeModSettingChanged;
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderModSettingChanged}:\n{e}" );
}
try
{
ProviderGetAvailableModSettings =
pi.GetIpcProvider< string, string, IDictionary< string, (IList< string >, Mods.SelectType) >? >(
LabelProviderGetAvailableModSettings );
ProviderGetAvailableModSettings.RegisterFunc( Api.GetAvailableModSettings );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderGetAvailableModSettings}:\n{e}" );
}
try
{
ProviderReloadMod = pi.GetIpcProvider< string, string, PenumbraApiEc >( LabelProviderReloadMod );
ProviderReloadMod.RegisterFunc( Api.ReloadMod );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderReloadMod}:\n{e}" );
}
try
{
ProviderAddMod = pi.GetIpcProvider< string, PenumbraApiEc >( LabelProviderAddMod );
ProviderAddMod.RegisterFunc( Api.AddMod );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderChangedItemClick}:\n{e}" );
}
try
{
ProviderGetCurrentModSettings =
pi.GetIpcProvider< string, string, string, bool, (PenumbraApiEc, (bool, int, IDictionary< string, IList< string > >, bool)?) >(
LabelProviderGetCurrentModSettings );
ProviderGetCurrentModSettings.RegisterFunc( Api.GetCurrentModSettings );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderGetCurrentModSettings}:\n{e}" );
}
try
{
ProviderTryInheritMod = pi.GetIpcProvider< string, string, string, bool, PenumbraApiEc >( LabelProviderTryInheritMod );
ProviderTryInheritMod.RegisterFunc( Api.TryInheritMod );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderTryInheritMod}:\n{e}" );
}
try
{
ProviderTrySetMod = pi.GetIpcProvider< string, string, string, bool, PenumbraApiEc >( LabelProviderTrySetMod );
ProviderTrySetMod.RegisterFunc( Api.TrySetMod );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderTrySetMod}:\n{e}" );
}
try
{
ProviderTrySetModPriority = pi.GetIpcProvider< string, string, string, int, PenumbraApiEc >( LabelProviderTrySetModPriority );
ProviderTrySetModPriority.RegisterFunc( Api.TrySetModPriority );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderTrySetModPriority}:\n{e}" );
}
try
{
ProviderTrySetModSetting =
pi.GetIpcProvider< string, string, string, string, string, PenumbraApiEc >( LabelProviderTrySetModSetting );
ProviderTrySetModSetting.RegisterFunc( Api.TrySetModSetting );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderTrySetModSetting}:\n{e}" );
}
try
{
ProviderTrySetModSettings =
pi.GetIpcProvider< string, string, string, string, IReadOnlyList< string >, PenumbraApiEc >( LabelProviderTrySetModSettings );
ProviderTrySetModSettings.RegisterFunc( Api.TrySetModSettings );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderTrySetModSettings}:\n{e}" );
}
}
private void DisposeSettingProviders()
{
Api.ModSettingChanged -= InvokeModSettingChanged;
ProviderGetAvailableModSettings?.UnregisterFunc();
ProviderReloadMod?.UnregisterFunc();
ProviderAddMod?.UnregisterFunc();
ProviderGetCurrentModSettings?.UnregisterFunc();
ProviderTryInheritMod?.UnregisterFunc();
ProviderTrySetMod?.UnregisterFunc();
ProviderTrySetModPriority?.UnregisterFunc();
ProviderTrySetModSetting?.UnregisterFunc();
ProviderTrySetModSettings?.UnregisterFunc();
}
private void InvokeModSettingChanged( ModSettingChange type, string collection, string mod, bool inherited )
=> ProviderModSettingChanged?.SendMessage( type, collection, mod, inherited );
}
public partial class PenumbraIpc
{
public const string LabelProviderCreateTemporaryCollection = "Penumbra.CreateTemporaryCollection";
public const string LabelProviderRemoveTemporaryCollection = "Penumbra.RemoveTemporaryCollection";
public const string LabelProviderAddTemporaryModAll = "Penumbra.AddTemporaryModAll";
public const string LabelProviderAddTemporaryMod = "Penumbra.AddTemporaryMod";
public const string LabelProviderRemoveTemporaryModAll = "Penumbra.RemoveTemporaryModAll";
public const string LabelProviderRemoveTemporaryMod = "Penumbra.RemoveTemporaryMod";
internal ICallGateProvider< string, string, bool, (PenumbraApiEc, string) >? ProviderCreateTemporaryCollection;
internal ICallGateProvider< string, PenumbraApiEc >? ProviderRemoveTemporaryCollection;
internal ICallGateProvider< string, Dictionary< string, string >, string, int, PenumbraApiEc >?
ProviderAddTemporaryModAll;
internal ICallGateProvider< string, string, Dictionary< string, string >, string, int, PenumbraApiEc >?
ProviderAddTemporaryMod;
internal ICallGateProvider< string, int, PenumbraApiEc >? ProviderRemoveTemporaryModAll;
internal ICallGateProvider< string, string, int, PenumbraApiEc >? ProviderRemoveTemporaryMod;
private void InitializeTempProviders( DalamudPluginInterface pi )
{
try
{
ProviderCreateTemporaryCollection =
pi.GetIpcProvider< string, string, bool, (PenumbraApiEc, string) >( LabelProviderCreateTemporaryCollection );
ProviderCreateTemporaryCollection.RegisterFunc( Api.CreateTemporaryCollection );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderCreateTemporaryCollection}:\n{e}" );
}
try
{
ProviderRemoveTemporaryCollection =
pi.GetIpcProvider< string, PenumbraApiEc >( LabelProviderRemoveTemporaryCollection );
ProviderRemoveTemporaryCollection.RegisterFunc( Api.RemoveTemporaryCollection );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderRemoveTemporaryCollection}:\n{e}" );
}
try
{
ProviderAddTemporaryModAll =
pi.GetIpcProvider< string, Dictionary< string, string >, string, int, PenumbraApiEc >(
LabelProviderAddTemporaryModAll );
ProviderAddTemporaryModAll.RegisterFunc( Api.AddTemporaryModAll );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderAddTemporaryModAll}:\n{e}" );
}
try
{
ProviderAddTemporaryMod =
pi.GetIpcProvider< string, string, Dictionary< string, string >, string, int, PenumbraApiEc >(
LabelProviderAddTemporaryMod );
ProviderAddTemporaryMod.RegisterFunc( Api.AddTemporaryMod );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderAddTemporaryMod}:\n{e}" );
}
try
{
ProviderRemoveTemporaryModAll = pi.GetIpcProvider< string, int, PenumbraApiEc >( LabelProviderRemoveTemporaryModAll );
ProviderRemoveTemporaryModAll.RegisterFunc( Api.RemoveTemporaryModAll );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderRemoveTemporaryModAll}:\n{e}" );
}
try
{
ProviderRemoveTemporaryMod = pi.GetIpcProvider< string, string, int, PenumbraApiEc >( LabelProviderRemoveTemporaryMod );
ProviderRemoveTemporaryMod.RegisterFunc( Api.RemoveTemporaryMod );
}
catch( Exception e )
{
Penumbra.Log.Error( $"Error registering IPC provider for {LabelProviderRemoveTemporaryMod}:\n{e}" );
}
}
private void DisposeTempProviders()
{
ProviderCreateTemporaryCollection?.UnregisterFunc();
ProviderRemoveTemporaryCollection?.UnregisterFunc();
ProviderAddTemporaryModAll?.UnregisterFunc();
ProviderAddTemporaryMod?.UnregisterFunc();
ProviderRemoveTemporaryModAll?.UnregisterFunc();
ProviderRemoveTemporaryMod?.UnregisterFunc();
}
}

View file

@ -0,0 +1,307 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin;
using Penumbra.Collections;
using Penumbra.GameData.Enums;
using System;
using System.Collections.Generic;
using Penumbra.Api.Enums;
using Penumbra.Api.Helpers;
namespace Penumbra.Api;
using CurrentSettings = ValueTuple< PenumbraApiEc, (bool, int, IDictionary< string, IList< string > >, bool)? >;
public class PenumbraIpcProviders : IDisposable
{
internal readonly IPenumbraApi Api;
internal readonly IpcTester Tester;
// Plugin State
internal readonly EventProvider Initialized;
internal readonly EventProvider Disposed;
internal readonly FuncProvider< int > ApiVersion;
internal readonly FuncProvider< (int Breaking, int Features) > ApiVersions;
// Configuration
internal readonly FuncProvider< string > GetModDirectory;
internal readonly FuncProvider< string > GetConfiguration;
internal readonly EventProvider< string, bool > ModDirectoryChanged;
// UI
internal readonly EventProvider< string > PreSettingsDraw;
internal readonly EventProvider< string > PostSettingsDraw;
internal readonly EventProvider< ChangedItemType, uint > ChangedItemTooltip;
internal readonly EventProvider< MouseButton, ChangedItemType, uint > ChangedItemClick;
// Redrawing
internal readonly ActionProvider< RedrawType > RedrawAll;
internal readonly ActionProvider< GameObject, RedrawType > RedrawObject;
internal readonly ActionProvider< int, RedrawType > RedrawObjectByIndex;
internal readonly ActionProvider< string, RedrawType > RedrawObjectByName;
internal readonly EventProvider< nint, int > GameObjectRedrawn;
// Game State
internal readonly FuncProvider< nint, (nint, string) > GetDrawObjectInfo;
internal readonly FuncProvider< int, int > GetCutsceneParentIndex;
internal readonly EventProvider< nint, string, nint, nint, nint > CreatingCharacterBase;
internal readonly EventProvider< nint, string, nint > CreatedCharacterBase;
internal readonly EventProvider< nint, string, string > GameObjectResourcePathResolved;
// Resolve
internal readonly FuncProvider< string, string > ResolveDefaultPath;
internal readonly FuncProvider< string, string > ResolveInterfacePath;
internal readonly FuncProvider< string, string > ResolvePlayerPath;
internal readonly FuncProvider< string, string, string > ResolveCharacterPath;
internal readonly FuncProvider< string, string, string[] > ReverseResolvePath;
internal readonly FuncProvider< string, string[] > ReverseResolvePathPlayer;
// Collections
internal readonly FuncProvider< IList< string > > GetCollections;
internal readonly FuncProvider< string > GetCurrentCollectionName;
internal readonly FuncProvider< string > GetDefaultCollectionName;
internal readonly FuncProvider< string > GetInterfaceCollectionName;
internal readonly FuncProvider< string, (string, bool) > GetCharacterCollectionName;
internal readonly FuncProvider< string, IReadOnlyDictionary< string, object? > > GetChangedItems;
// Meta
internal readonly FuncProvider< string > GetPlayerMetaManipulations;
internal readonly FuncProvider< string, string > GetMetaManipulations;
// Mods
internal readonly FuncProvider< IList< (string, string) > > GetMods;
internal readonly FuncProvider< string, string, PenumbraApiEc > ReloadMod;
internal readonly FuncProvider< string, PenumbraApiEc > AddMod;
internal readonly FuncProvider< string, string, PenumbraApiEc > DeleteMod;
internal readonly FuncProvider< string, string, (PenumbraApiEc, string, bool) > GetModPath;
internal readonly FuncProvider< string, string, string, PenumbraApiEc > SetModPath;
// ModSettings
internal readonly FuncProvider< string, string, IDictionary< string, (IList< string >, GroupType) >? > GetAvailableModSettings;
internal readonly FuncProvider< string, string, string, bool, CurrentSettings > GetCurrentModSettings;
internal readonly FuncProvider< string, string, string, bool, PenumbraApiEc > TryInheritMod;
internal readonly FuncProvider< string, string, string, bool, PenumbraApiEc > TrySetMod;
internal readonly FuncProvider< string, string, string, int, PenumbraApiEc > TrySetModPriority;
internal readonly FuncProvider< string, string, string, string, string, PenumbraApiEc > TrySetModSetting;
internal readonly FuncProvider< string, string, string, string, IReadOnlyList< string >, PenumbraApiEc > TrySetModSettings;
internal readonly EventProvider< ModSettingChange, string, string, bool > ModSettingChanged;
// Temporary
internal readonly FuncProvider< string, string, bool, (PenumbraApiEc, string) > CreateTemporaryCollection;
internal readonly FuncProvider< string, PenumbraApiEc > RemoveTemporaryCollection;
internal readonly FuncProvider< string, Dictionary< string, string >, string, int, PenumbraApiEc > AddTemporaryModAll;
internal readonly FuncProvider< string, string, Dictionary< string, string >, string, int, PenumbraApiEc > AddTemporaryMod;
internal readonly FuncProvider< string, int, PenumbraApiEc > RemoveTemporaryModAll;
internal readonly FuncProvider< string, string, int, PenumbraApiEc > RemoveTemporaryMod;
public PenumbraIpcProviders( DalamudPluginInterface pi, IPenumbraApi api )
{
Api = api;
// Plugin State
Initialized = Ipc.Initialized.Provider( pi );
Disposed = Ipc.Disposed.Provider( pi );
ApiVersion = Ipc.ApiVersion.Provider( pi, DeprecatedVersion );
ApiVersions = Ipc.ApiVersions.Provider( pi, () => Api.ApiVersion );
// Configuration
GetModDirectory = Ipc.GetModDirectory.Provider( pi, Api.GetModDirectory );
GetConfiguration = Ipc.GetConfiguration.Provider( pi, Api.GetConfiguration );
ModDirectoryChanged = Ipc.ModDirectoryChanged.Provider( pi, a => Api.ModDirectoryChanged += a, a => Api.ModDirectoryChanged -= a );
// UI
PreSettingsDraw = Ipc.PreSettingsDraw.Provider( pi, a => Api.PreSettingsPanelDraw += a, a => Api.PreSettingsPanelDraw -= a );
PostSettingsDraw = Ipc.PostSettingsDraw.Provider( pi, a => Api.PostSettingsPanelDraw += a, a => Api.PostSettingsPanelDraw -= a );
ChangedItemTooltip = Ipc.ChangedItemTooltip.Provider( pi, () => Api.ChangedItemTooltip += OnTooltip, () => Api.ChangedItemTooltip -= OnTooltip );
ChangedItemClick = Ipc.ChangedItemClick.Provider( pi, () => Api.ChangedItemClicked += OnClick, () => Api.ChangedItemClicked -= OnClick );
// Redrawing
RedrawAll = Ipc.RedrawAll.Provider( pi, Api.RedrawAll );
RedrawObject = Ipc.RedrawObject.Provider( pi, Api.RedrawObject );
RedrawObjectByIndex = Ipc.RedrawObjectByIndex.Provider( pi, Api.RedrawObject );
RedrawObjectByName = Ipc.RedrawObjectByName.Provider( pi, Api.RedrawObject );
GameObjectRedrawn = Ipc.GameObjectRedrawn.Provider( pi, () => Api.GameObjectRedrawn += OnGameObjectRedrawn, () => Api.GameObjectRedrawn -= OnGameObjectRedrawn );
// Game State
GetDrawObjectInfo = Ipc.GetDrawObjectInfo.Provider( pi, Api.GetDrawObjectInfo );
GetCutsceneParentIndex = Ipc.GetCutsceneParentIndex.Provider( pi, Api.GetCutsceneParentIndex );
CreatingCharacterBase = Ipc.CreatingCharacterBase.Provider( pi,
() => Api.CreatingCharacterBase += CreatingCharacterBaseEvent,
() => Api.CreatingCharacterBase -= CreatingCharacterBaseEvent );
CreatedCharacterBase = Ipc.CreatedCharacterBase.Provider( pi,
() => Api.CreatedCharacterBase += CreatedCharacterBaseEvent,
() => Api.CreatedCharacterBase -= CreatedCharacterBaseEvent );
GameObjectResourcePathResolved = Ipc.GameObjectResourcePathResolved.Provider( pi,
() => Api.GameObjectResourceResolved += GameObjectResourceResolvedEvent,
() => Api.GameObjectResourceResolved -= GameObjectResourceResolvedEvent );
// Resolve
ResolveDefaultPath = Ipc.ResolveDefaultPath.Provider( pi, Api.ResolveDefaultPath );
ResolveInterfacePath = Ipc.ResolveInterfacePath.Provider( pi, Api.ResolveInterfacePath );
ResolvePlayerPath = Ipc.ResolvePlayerPath.Provider( pi, Api.ResolvePlayerPath );
ResolveCharacterPath = Ipc.ResolveCharacterPath.Provider( pi, Api.ResolvePath );
ReverseResolvePath = Ipc.ReverseResolvePath.Provider( pi, Api.ReverseResolvePath );
ReverseResolvePathPlayer = Ipc.ReverseResolvePlayerPath.Provider( pi, Api.ReverseResolvePlayerPath );
// Collections
GetCollections = Ipc.GetCollections.Provider( pi, Api.GetCollections );
GetCurrentCollectionName = Ipc.GetCurrentCollectionName.Provider( pi, Api.GetCurrentCollection );
GetDefaultCollectionName = Ipc.GetDefaultCollectionName.Provider( pi, Api.GetDefaultCollection );
GetInterfaceCollectionName = Ipc.GetInterfaceCollectionName.Provider( pi, Api.GetInterfaceCollection );
GetCharacterCollectionName = Ipc.GetCharacterCollectionName.Provider( pi, Api.GetCharacterCollection );
GetChangedItems = Ipc.GetChangedItems.Provider( pi, Api.GetChangedItemsForCollection );
// Meta
GetPlayerMetaManipulations = Ipc.GetPlayerMetaManipulations.Provider( pi, Api.GetPlayerMetaManipulations );
GetMetaManipulations = Ipc.GetMetaManipulations.Provider( pi, Api.GetMetaManipulations );
// Mods
GetMods = Ipc.GetMods.Provider( pi, Api.GetModList );
ReloadMod = Ipc.ReloadMod.Provider( pi, Api.ReloadMod );
AddMod = Ipc.AddMod.Provider( pi, Api.AddMod );
DeleteMod = Ipc.DeleteMod.Provider( pi, Api.DeleteMod );
GetModPath = Ipc.GetModPath.Provider( pi, Api.GetModPath );
SetModPath = Ipc.SetModPath.Provider( pi, Api.SetModPath );
// ModSettings
GetAvailableModSettings = Ipc.GetAvailableModSettings.Provider( pi, Api.GetAvailableModSettings );
GetCurrentModSettings = Ipc.GetCurrentModSettings.Provider( pi, Api.GetCurrentModSettings );
TryInheritMod = Ipc.TryInheritMod.Provider( pi, Api.TryInheritMod );
TrySetMod = Ipc.TrySetMod.Provider( pi, Api.TrySetMod );
TrySetModPriority = Ipc.TrySetModPriority.Provider( pi, Api.TrySetModPriority );
TrySetModSetting = Ipc.TrySetModSetting.Provider( pi, Api.TrySetModSetting );
TrySetModSettings = Ipc.TrySetModSettings.Provider( pi, Api.TrySetModSettings );
ModSettingChanged = Ipc.ModSettingChanged.Provider( pi,
() => Api.ModSettingChanged += ModSettingChangedEvent,
() => Api.ModSettingChanged -= ModSettingChangedEvent );
// Temporary
CreateTemporaryCollection = Ipc.CreateTemporaryCollection.Provider( pi, Api.CreateTemporaryCollection );
RemoveTemporaryCollection = Ipc.RemoveTemporaryCollection.Provider( pi, Api.RemoveTemporaryCollection );
AddTemporaryModAll = Ipc.AddTemporaryModAll.Provider( pi, Api.AddTemporaryModAll );
AddTemporaryMod = Ipc.AddTemporaryMod.Provider( pi, Api.AddTemporaryMod );
RemoveTemporaryModAll = Ipc.RemoveTemporaryModAll.Provider( pi, Api.RemoveTemporaryModAll );
RemoveTemporaryMod = Ipc.RemoveTemporaryMod.Provider( pi, Api.RemoveTemporaryMod );
Tester = new IpcTester( pi, this );
Initialized.Invoke();
}
public void Dispose()
{
// Plugin State
Initialized.Dispose();
ApiVersion.Dispose();
ApiVersions.Dispose();
// Configuration
GetModDirectory.Dispose();
GetConfiguration.Dispose();
ModDirectoryChanged.Dispose();
// UI
PreSettingsDraw.Dispose();
PostSettingsDraw.Dispose();
ChangedItemTooltip.Dispose();
ChangedItemClick.Dispose();
// Redrawing
RedrawAll.Dispose();
RedrawObject.Dispose();
RedrawObjectByIndex.Dispose();
RedrawObjectByName.Dispose();
GameObjectRedrawn.Dispose();
// Game State
GetDrawObjectInfo.Dispose();
GetCutsceneParentIndex.Dispose();
CreatingCharacterBase.Dispose();
CreatedCharacterBase.Dispose();
GameObjectResourcePathResolved.Dispose();
// Resolve
ResolveDefaultPath.Dispose();
ResolveInterfacePath.Dispose();
ResolvePlayerPath.Dispose();
ResolveCharacterPath.Dispose();
ReverseResolvePath.Dispose();
ReverseResolvePathPlayer.Dispose();
// Collections
GetCollections.Dispose();
GetCurrentCollectionName.Dispose();
GetDefaultCollectionName.Dispose();
GetInterfaceCollectionName.Dispose();
GetCharacterCollectionName.Dispose();
GetChangedItems.Dispose();
// Meta
GetPlayerMetaManipulations.Dispose();
GetMetaManipulations.Dispose();
// Mods
GetMods.Dispose();
ReloadMod.Dispose();
AddMod.Dispose();
DeleteMod.Dispose();
GetModPath.Dispose();
SetModPath.Dispose();
// ModSettings
GetAvailableModSettings.Dispose();
GetCurrentModSettings.Dispose();
TryInheritMod.Dispose();
TrySetMod.Dispose();
TrySetModPriority.Dispose();
TrySetModSetting.Dispose();
TrySetModSettings.Dispose();
ModSettingChanged.Dispose();
// Temporary
CreateTemporaryCollection.Dispose();
RemoveTemporaryCollection.Dispose();
AddTemporaryModAll.Dispose();
AddTemporaryMod.Dispose();
RemoveTemporaryModAll.Dispose();
RemoveTemporaryMod.Dispose();
Disposed.Invoke();
Disposed.Dispose();
Tester.Dispose();
}
// Wrappers
private int DeprecatedVersion()
{
Penumbra.Log.Warning( $"{Ipc.ApiVersion.Label} is outdated. Please use {Ipc.ApiVersions.Label} instead." );
return Api.ApiVersion.Breaking;
}
private void OnClick( MouseButton click, object? item )
{
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId( item );
ChangedItemClick.Invoke( click, type, id );
}
private void OnTooltip( object? item )
{
var (type, id) = ChangedItemExtensions.ChangedItemToTypeAndId( item );
ChangedItemTooltip.Invoke( type, id );
}
private void OnGameObjectRedrawn( IntPtr objectAddress, int objectTableIndex )
=> GameObjectRedrawn.Invoke( objectAddress, objectTableIndex );
private void CreatingCharacterBaseEvent( IntPtr gameObject, string collectionName, IntPtr modelId, IntPtr customize, IntPtr equipData )
=> CreatingCharacterBase.Invoke( gameObject, collectionName, modelId, customize, equipData );
private void CreatedCharacterBaseEvent( IntPtr gameObject, string collectionName, IntPtr drawObject )
=> CreatedCharacterBase.Invoke( gameObject, collectionName, drawObject );
private void GameObjectResourceResolvedEvent( IntPtr gameObject, string gamePath, string localPath )
=> GameObjectResourcePathResolved.Invoke( gameObject, gamePath, localPath );
private void ModSettingChangedEvent( ModSettingChange type, string collection, string mod, bool inherited )
=> ModSettingChanged.Invoke( type, collection, mod, inherited );
}

View file

@ -1,8 +1,8 @@
using EmbedIO;
using EmbedIO.Routing;
using EmbedIO.WebApi;
using Penumbra.GameData.Enums;
using System.Threading.Tasks;
using Penumbra.Api.Enums;
namespace Penumbra.Api;

View file

@ -7,6 +7,7 @@ using Penumbra.Mods;
using System;
using System.Collections.Generic;
using System.Linq;
using Penumbra.Api.Enums;
namespace Penumbra.Collections;
@ -269,10 +270,10 @@ public partial class ModCollection
var config = settings.Settings[ groupIndex ];
switch( group.Type )
{
case SelectType.Single:
case GroupType.Single:
AddSubMod( group[ ( int )config ], mod );
break;
case SelectType.Multi:
case GroupType.Multi:
{
foreach( var (option, _) in group.WithIndex()
.Where( p => ( ( 1 << p.Item2 ) & config ) != 0 )

View file

@ -2,20 +2,10 @@ using Penumbra.Mods;
using System;
using System.Collections.Generic;
using System.Linq;
using Penumbra.Api.Enums;
namespace Penumbra.Collections;
// Different types a mod setting can change:
public enum ModSettingChange
{
Inheritance, // it was set to inherit from other collections or not inherit anymore
EnableState, // it was enabled or disabled
Priority, // its priority was changed
Setting, // a specific setting was changed
MultiInheritance, // multiple mods were set to inherit from other collections or not inherit anymore.
MultiEnableState, // multiple mods were enabled or disabled at once.
}
public partial class ModCollection
{
// If the change type is a bool, oldValue will be 1 for true and 0 for false.

View file

@ -3,6 +3,7 @@ using Penumbra.Mods;
using System;
using System.Collections.Generic;
using System.Linq;
using Penumbra.Api.Enums;
namespace Penumbra.Collections;

View file

@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using Newtonsoft.Json;
using OtterGui;
using Penumbra.Api.Enums;
using Penumbra.Mods;
using Penumbra.Util;
using SharpCompress.Archives.Zip;
@ -162,7 +163,7 @@ public partial class TexToolsImporter
foreach( var group in page.ModGroups.Where( group => group.GroupName.Length > 0 && group.OptionList.Length > 0 ) )
{
var allOptions = group.OptionList.Where( option => option.Name.Length > 0 && option.ModsJsons.Length > 0 ).ToList();
var (numGroups, maxOptions) = group.SelectionType == SelectType.Single
var (numGroups, maxOptions) = group.SelectionType == GroupType.Single
? ( 1, allOptions.Count )
: ( 1 + allOptions.Count / IModGroup.MaxMultiOptions, IModGroup.MaxMultiOptions );
_currentGroupName = GetGroupName( group.GroupName, groupNames );
@ -177,7 +178,7 @@ public partial class TexToolsImporter
?? new DirectoryInfo( Path.Combine( _currentModDirectory.FullName,
numGroups == 1 ? $"Group {groupPriority + 1}" : $"Group {groupPriority + 1}, Part {groupId + 1}" ) );
uint? defaultSettings = group.SelectionType == SelectType.Multi ? 0u : null;
uint? defaultSettings = group.SelectionType == GroupType.Multi ? 0u : null;
for( var i = 0; i + optionIdx < allOptions.Count && i < maxOptions; ++i )
{
var option = allOptions[ i + optionIdx ];
@ -195,7 +196,7 @@ public partial class TexToolsImporter
if( option.IsChecked )
{
defaultSettings = group.SelectionType == SelectType.Multi
defaultSettings = group.SelectionType == GroupType.Multi
? ( defaultSettings!.Value | ( 1u << i ) )
: ( uint )i;
}
@ -207,7 +208,7 @@ public partial class TexToolsImporter
// Handle empty options for single select groups without creating a folder for them.
// We only want one of those at most, and it should usually be the first option.
if( group.SelectionType == SelectType.Single )
if( group.SelectionType == GroupType.Single )
{
var empty = group.OptionList.FirstOrDefault( o => o.Name.Length > 0 && o.ModsJsons.Length == 0 );
if( empty != null )

View file

@ -1,4 +1,5 @@
using System;
using Penumbra.Api.Enums;
using Penumbra.Mods;
namespace Penumbra.Import;
@ -34,7 +35,7 @@ internal class ModPackPage
internal class ModGroup
{
public string GroupName = string.Empty;
public SelectType SelectionType = SelectType.Single;
public GroupType SelectionType = GroupType.Single;
public OptionList[] OptionList = Array.Empty< OptionList >();
}
@ -46,7 +47,7 @@ internal class OptionList
public string ImagePath = string.Empty;
public SimpleMod[] ModsJsons = Array.Empty< SimpleMod >();
public string GroupName = string.Empty;
public SelectType SelectionType = SelectType.Single;
public GroupType SelectionType = GroupType.Single;
public bool IsChecked = false;
}

View file

@ -6,6 +6,7 @@ using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Penumbra.Api;
using Penumbra.Api.Enums;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs;

View file

@ -149,7 +149,7 @@ public unsafe partial class PathResolver
try
{
var modelPtr = &a;
CreatingCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection, ( IntPtr )modelPtr, b, c );
CreatingCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection.Name, ( IntPtr )modelPtr, b, c );
}
catch( Exception e )
{
@ -163,7 +163,7 @@ public unsafe partial class PathResolver
if( LastGameObject != null && ret != IntPtr.Zero )
{
_drawObjectToObject[ ret ] = ( _lastCreatedCollection!, LastGameObject->ObjectIndex );
CreatedCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection, ret );
CreatedCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection.Name, ret );
}
return ret;

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using OtterGui;
using OtterGui.Filesystem;
using Penumbra.Api.Enums;
using Penumbra.GameData.ByteString;
using Penumbra.Meta.Manipulations;
using Penumbra.Util;
@ -16,7 +17,7 @@ public sealed partial class Mod
public delegate void ModOptionChangeDelegate( ModOptionChangeType type, Mod mod, int groupIdx, int optionIdx, int movedToIdx );
public event ModOptionChangeDelegate ModOptionChanged;
public void ChangeModGroupType( Mod mod, int groupIdx, SelectType type )
public void ChangeModGroupType( Mod mod, int groupIdx, GroupType type )
{
var group = mod._groups[ groupIdx ];
if( group.Type == type )
@ -61,7 +62,7 @@ public sealed partial class Mod
ModOptionChanged.Invoke( ModOptionChangeType.GroupRenamed, mod, groupIdx, -1, -1 );
}
public void AddModGroup( Mod mod, SelectType type, string newName )
public void AddModGroup( Mod mod, GroupType type, string newName )
{
if( !VerifyFileName( mod, null, newName, true ) )
{
@ -70,7 +71,7 @@ public sealed partial class Mod
var maxPriority = mod._groups.Count == 0 ? 0 : mod._groups.Max( o => o.Priority ) + 1;
mod._groups.Add( type == SelectType.Multi
mod._groups.Add( type == GroupType.Multi
? new MultiModGroup { Name = newName, Priority = maxPriority }
: new SingleModGroup { Name = newName, Priority = maxPriority } );
ModOptionChanged.Invoke( ModOptionChangeType.GroupAdded, mod, mod._groups.Count - 1, -1, -1 );

View file

@ -5,6 +5,7 @@ using System.Text;
using Dalamud.Utility;
using OtterGui.Classes;
using OtterGui.Filesystem;
using Penumbra.Api.Enums;
using Penumbra.GameData.ByteString;
using Penumbra.Import;
@ -62,12 +63,12 @@ public partial class Mod
}
// Create a file for an option group from given data.
internal static void CreateOptionGroup( DirectoryInfo baseFolder, SelectType type, string name,
internal static void CreateOptionGroup( DirectoryInfo baseFolder, GroupType type, string name,
int priority, int index, uint defaultSettings, string desc, IEnumerable< ISubMod > subMods )
{
switch( type )
{
case SelectType.Multi:
case GroupType.Multi:
{
var group = new MultiModGroup()
{
@ -80,7 +81,7 @@ public partial class Mod
IModGroup.Save( group, baseFolder, index );
break;
}
case SelectType.Single:
case GroupType.Single:
{
var group = new SingleModGroup()
{

View file

@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using Newtonsoft.Json.Linq;
using OtterGui;
using Penumbra.Api.Enums;
using Penumbra.GameData.ByteString;
using Penumbra.Meta.Manipulations;
@ -79,10 +80,10 @@ public partial class Mod
try
{
var json = JObject.Parse( File.ReadAllText( file.FullName ) );
switch( json[ nameof( Type ) ]?.ToObject< SelectType >() ?? SelectType.Single )
switch( json[ nameof( Type ) ]?.ToObject< GroupType >() ?? GroupType.Single )
{
case SelectType.Multi: return MultiModGroup.Load( mod, json, groupIdx );
case SelectType.Single: return SingleModGroup.Load( mod, json, groupIdx );
case GroupType.Multi: return MultiModGroup.Load( mod, json, groupIdx );
case GroupType.Single: return SingleModGroup.Load( mod, json, groupIdx );
}
}
catch( Exception e )

View file

@ -6,6 +6,7 @@ using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OtterGui;
using Penumbra.Api.Enums;
using Penumbra.GameData.ByteString;
namespace Penumbra.Mods;
@ -128,7 +129,7 @@ public sealed partial class Mod
switch( group.SelectionType )
{
case SelectType.Multi:
case GroupType.Multi:
var optionPriority = 0;
var newMultiGroup = new MultiModGroup()
@ -144,7 +145,7 @@ public sealed partial class Mod
}
break;
case SelectType.Single:
case GroupType.Single:
if( group.Options.Count == 1 )
{
AddFilesToSubMod( mod._default, mod.ModPath, group.Options[ 0 ], seenMetaFiles );
@ -209,7 +210,7 @@ public sealed partial class Mod
public string GroupName = string.Empty;
[JsonConverter( typeof( Newtonsoft.Json.Converters.StringEnumConverter ) )]
public SelectType SelectionType = SelectType.Single;
public GroupType SelectionType = GroupType.Single;
public List< OptionV0 > Options = new();

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
@ -121,8 +122,7 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
CreateLeaf( Root, name, mod );
break;
case ModPathChangeType.Deleted:
var leaf = Root.GetAllDescendants( ISortMode< Mod >.Lexicographical ).OfType< Leaf >().FirstOrDefault( l => l.Value == mod );
if( leaf != null )
if( FindLeaf( mod, out var leaf ) )
{
Delete( leaf );
}
@ -137,6 +137,16 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
}
}
// Search the entire filesystem for the leaf corresponding to a mod.
public bool FindLeaf( Mod mod, [NotNullWhen( true )] out Leaf? leaf )
{
leaf = Root.GetAllDescendants( ISortMode< Mod >.Lexicographical )
.OfType< Leaf >()
.FirstOrDefault( l => l.Value == mod );
return leaf != null;
}
// Used for saving and loading.
private static string ModToIdentifier( Mod mod )
=> mod.ModPath.Name;
@ -144,15 +154,16 @@ public sealed class ModFileSystem : FileSystem< Mod >, IDisposable
private static string ModToName( Mod mod )
=> mod.Name.Text.FixName();
private static (string, bool) SaveMod( Mod mod, string fullPath )
// Return whether a mod has a custom path or is just a numbered default path.
public static bool ModHasDefaultPath( Mod mod, string fullPath )
{
var regex = new Regex( $@"^{Regex.Escape( ModToName( mod ) )}( \(\d+\))?$" );
// Only save pairs with non-default paths.
if( regex.IsMatch( fullPath ) )
{
return ( string.Empty, false );
}
return ( ModToIdentifier( mod ), true );
return regex.IsMatch( fullPath );
}
private static (string, bool) SaveMod( Mod mod, string fullPath )
// Only save pairs with non-default paths.
=> ModHasDefaultPath( mod, fullPath )
? ( string.Empty, false )
: ( ModToIdentifier( mod ), true );
}

View file

@ -3,22 +3,17 @@ using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using OtterGui.Filesystem;
using Penumbra.Api.Enums;
namespace Penumbra.Mods;
public enum SelectType
{
Single,
Multi,
}
public interface IModGroup : IEnumerable< ISubMod >
{
public const int MaxMultiOptions = 32;
public string Name { get; }
public string Description { get; }
public SelectType Type { get; }
public GroupType Type { get; }
public int Priority { get; }
public uint DefaultSettings { get; set; }
@ -31,8 +26,8 @@ public interface IModGroup : IEnumerable< ISubMod >
public bool IsOption
=> Type switch
{
SelectType.Single => Count > 1,
SelectType.Multi => Count > 0,
GroupType.Single => Count > 1,
GroupType.Multi => Count > 0,
_ => false,
};
@ -90,7 +85,7 @@ public interface IModGroup : IEnumerable< ISubMod >
j.WriteStartArray();
for( var idx = 0; idx < group.Count; ++idx )
{
ISubMod.WriteSubMod( j, serializer, group[ idx ], basePath, group.Type == SelectType.Multi ? group.OptionPriority( idx ) : null );
ISubMod.WriteSubMod( j, serializer, group[ idx ], basePath, group.Type == GroupType.Multi ? group.OptionPriority( idx ) : null );
}
j.WriteEndArray();
@ -98,7 +93,7 @@ public interface IModGroup : IEnumerable< ISubMod >
Penumbra.Log.Debug( $"Saved group file {file} for group {groupIdx + 1}: {group.Name}." );
}
public IModGroup Convert( SelectType type );
public IModGroup Convert( GroupType type );
public bool MoveOption( int optionIdxFrom, int optionIdxTo );
public void UpdatePositions( int from = 0 );
}

View file

@ -7,6 +7,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OtterGui;
using OtterGui.Filesystem;
using Penumbra.Api.Enums;
namespace Penumbra.Mods;
@ -15,8 +16,8 @@ public partial class Mod
// Groups that allow all available options to be selected at once.
private sealed class MultiModGroup : IModGroup
{
public SelectType Type
=> SelectType.Multi;
public GroupType Type
=> GroupType.Multi;
public string Name { get; set; } = "Group";
public string Description { get; set; } = "A non-exclusive group of settings.";
@ -79,12 +80,12 @@ public partial class Mod
return ret;
}
public IModGroup Convert( SelectType type )
public IModGroup Convert( GroupType type )
{
switch( type )
{
case SelectType.Multi: return this;
case SelectType.Single:
case GroupType.Multi: return this;
case GroupType.Single:
var multi = new SingleModGroup()
{
Name = Name,

View file

@ -6,6 +6,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OtterGui;
using OtterGui.Filesystem;
using Penumbra.Api.Enums;
namespace Penumbra.Mods;
@ -14,8 +15,8 @@ public partial class Mod
// Groups that allow only one of their available options to be selected.
private sealed class SingleModGroup : IModGroup
{
public SelectType Type
=> SelectType.Single;
public GroupType Type
=> GroupType.Single;
public string Name { get; set; } = "Option";
public string Description { get; set; } = "A mutually exclusive group of settings.";
@ -72,12 +73,12 @@ public partial class Mod
return ret;
}
public IModGroup Convert( SelectType type )
public IModGroup Convert( GroupType type )
{
switch( type )
{
case SelectType.Single: return this;
case SelectType.Multi:
case GroupType.Single: return this;
case GroupType.Multi:
var multi = new MultiModGroup()
{
Name = Name,

View file

@ -4,6 +4,7 @@ using System.Linq;
using System.Numerics;
using OtterGui;
using OtterGui.Filesystem;
using Penumbra.Api.Enums;
namespace Penumbra.Mods;
@ -56,8 +57,8 @@ public class ModSettings
var config = Settings[ groupIdx ];
Settings[ groupIdx ] = group.Type switch
{
SelectType.Single => ( uint )Math.Max( Math.Min( group.Count - 1, BitOperations.TrailingZeroCount( config ) ), 0 ),
SelectType.Multi => 1u << ( int )config,
GroupType.Single => ( uint )Math.Max( Math.Min( group.Count - 1, BitOperations.TrailingZeroCount( config ) ), 0 ),
GroupType.Multi => 1u << ( int )config,
_ => config,
};
return config != Settings[ groupIdx ];
@ -70,8 +71,8 @@ public class ModSettings
var config = Settings[ groupIdx ];
Settings[ groupIdx ] = group.Type switch
{
SelectType.Single => config >= optionIdx ? config > 1 ? config - 1 : 0 : config,
SelectType.Multi => Functions.RemoveBit( config, optionIdx ),
GroupType.Single => config >= optionIdx ? config > 1 ? config - 1 : 0 : config,
GroupType.Multi => Functions.RemoveBit( config, optionIdx ),
_ => config,
};
return config != Settings[ groupIdx ];
@ -87,8 +88,8 @@ public class ModSettings
var config = Settings[ groupIdx ];
Settings[ groupIdx ] = group.Type switch
{
SelectType.Single => config == optionIdx ? ( uint )movedToIdx : config,
SelectType.Multi => Functions.MoveBit( config, optionIdx, movedToIdx ),
GroupType.Single => config == optionIdx ? ( uint )movedToIdx : config,
GroupType.Multi => Functions.MoveBit( config, optionIdx, movedToIdx ),
_ => config,
};
return config != Settings[ groupIdx ];
@ -101,8 +102,8 @@ public class ModSettings
private static uint FixSetting( IModGroup group, uint value )
=> group.Type switch
{
SelectType.Single => ( uint )Math.Min( value, group.Count - 1 ),
SelectType.Multi => ( uint )( value & ( ( 1ul << group.Count ) - 1 ) ),
GroupType.Single => ( uint )Math.Min( value, group.Count - 1 ),
GroupType.Multi => ( uint )( value & ( ( 1ul << group.Count ) - 1 ) ),
_ => value,
};
@ -202,7 +203,7 @@ public class ModSettings
}
var group = mod.Groups[ idx ];
if( group.Type == SelectType.Single && setting < group.Count )
if( group.Type == GroupType.Single && setting < group.Count )
{
dict.Add( group.Name, new[] { group[ ( int )setting ].Name } );
}

View file

@ -16,6 +16,7 @@ using OtterGui.Classes;
using OtterGui.Log;
using OtterGui.Widgets;
using Penumbra.Api;
using Penumbra.Api.Enums;
using Penumbra.GameData.Enums;
using Penumbra.Interop;
using Penumbra.UI;
@ -57,16 +58,16 @@ public class Penumbra : IDalamudPlugin
public static FrameworkManager Framework { get; private set; } = null!;
public static int ImcExceptions = 0;
public readonly ResourceLogger ResourceLogger;
public readonly PathResolver PathResolver;
public readonly ObjectReloader ObjectReloader;
public readonly ModFileSystem ModFileSystem;
public readonly PenumbraApi Api;
public readonly PenumbraIpc Ipc;
private readonly ConfigWindow _configWindow;
private readonly LaunchButton _launchButton;
private readonly WindowSystem _windowSystem;
private readonly Changelog _changelog;
public readonly ResourceLogger ResourceLogger;
public readonly PathResolver PathResolver;
public readonly ObjectReloader ObjectReloader;
public readonly ModFileSystem ModFileSystem;
public readonly PenumbraApi Api;
public readonly PenumbraIpcProviders IpcProviders;
private readonly ConfigWindow _configWindow;
private readonly LaunchButton _launchButton;
private readonly WindowSystem _windowSystem;
private readonly Changelog _changelog;
internal WebServer? WebServer;
@ -95,9 +96,9 @@ public class Penumbra : IDalamudPlugin
ModManager.DiscoverMods();
CollectionManager = new ModCollection.Manager( ModManager );
CollectionManager.CreateNecessaryCaches();
ModFileSystem = ModFileSystem.Load();
ObjectReloader = new ObjectReloader();
PathResolver = new PathResolver( ResourceLoader );
ModFileSystem = ModFileSystem.Load();
ObjectReloader = new ObjectReloader();
PathResolver = new PathResolver( ResourceLoader );
Dalamud.Commands.AddHandler( CommandName, new CommandInfo( OnCommand )
{
@ -133,8 +134,8 @@ public class Penumbra : IDalamudPlugin
ResidentResources.Reload();
}
Api = new PenumbraApi( this );
Ipc = new PenumbraIpc( Dalamud.PluginInterface, Api );
Api = new PenumbraApi( this );
IpcProviders = new PenumbraIpcProviders( Dalamud.PluginInterface, Api );
SubscribeItemLinks();
if( ImcExceptions > 0 )
{
@ -279,7 +280,7 @@ public class Penumbra : IDalamudPlugin
{
ShutdownWebServer();
DisposeInterface();
Ipc?.Dispose();
IpcProviders?.Dispose();
Api?.Dispose();
ObjectReloader?.Dispose();
ModFileSystem?.Dispose();

View file

@ -75,6 +75,7 @@
<ItemGroup>
<ProjectReference Include="..\OtterGui\OtterGui.csproj" />
<ProjectReference Include="..\Penumbra.GameData\Penumbra.GameData.csproj" />
<ProjectReference Include="..\Penumbra.Api\Penumbra.Api.csproj" />
</ItemGroup>
<ItemGroup>

View file

@ -13,6 +13,7 @@ using System.Collections.Concurrent;
using System.IO;
using System.Linq;
using System.Numerics;
using Penumbra.Api.Enums;
namespace Penumbra.UI.Classes;

View file

@ -444,11 +444,11 @@ public partial class ConfigWindow
{
if( !ImGui.CollapsingHeader( "IPC" ) )
{
_window._penumbra.Ipc.Tester.UnsubscribeEvents();
_window._penumbra.IpcProviders.Tester.UnsubscribeEvents();
return;
}
_window._penumbra.Ipc.Tester.Draw();
_window._penumbra.IpcProviders.Tester.Draw();
}
// Helper to print a property and its value in a 2-column table.

View file

@ -7,6 +7,7 @@ using Lumina.Data.Parsing;
using Lumina.Excel.GeneratedSheets;
using OtterGui;
using OtterGui.Raii;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums;

View file

@ -8,6 +8,7 @@ using Dalamud.Interface.Components;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.Api.Enums;
using Penumbra.Mods;
namespace Penumbra.UI;
@ -236,7 +237,7 @@ public partial class ConfigWindow
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Plus.ToIconString(), window._iconButtonSize,
tt, !nameValid, true ) )
{
Penumbra.ModManager.AddModGroup( mod, SelectType.Single, _newGroupName );
Penumbra.ModManager.AddModGroup( mod, GroupType.Single, _newGroupName );
Reset();
}
}
@ -496,7 +497,7 @@ public partial class ConfigWindow
ImGui.TableNextColumn();
if( group.Type == SelectType.Single )
if( group.Type == GroupType.Single )
{
if( ImGui.RadioButton( "##default", group.DefaultSettings == optionIdx ) )
{
@ -532,7 +533,7 @@ public partial class ConfigWindow
}
ImGui.TableNextColumn();
if( group.Type == SelectType.Multi )
if( group.Type == GroupType.Multi )
{
if( Input.Priority( "##Priority", groupIdx, optionIdx, group.OptionPriority( optionIdx ), out var priority,
50 * ImGuiHelpers.GlobalScale ) )
@ -564,7 +565,7 @@ public partial class ConfigWindow
}
ImGui.TableNextColumn();
var canAddGroup = mod.Groups[ groupIdx ].Type != SelectType.Multi || mod.Groups[ groupIdx ].Count < IModGroup.MaxMultiOptions;
var canAddGroup = mod.Groups[ groupIdx ].Type != GroupType.Multi || mod.Groups[ groupIdx ].Count < IModGroup.MaxMultiOptions;
var validName = _newOptionName.Length > 0 && _newOptionNameIdx == groupIdx;
var tt = canAddGroup
? validName ? "Add a new option to this group." : "Please enter a name for the new option."
@ -636,11 +637,11 @@ public partial class ConfigWindow
// Draw a combo to select single or multi group and switch between them.
private void DrawGroupCombo( IModGroup group, int groupIdx )
{
static string GroupTypeName( SelectType type )
static string GroupTypeName( GroupType type )
=> type switch
{
SelectType.Single => "Single Group",
SelectType.Multi => "Multi Group",
GroupType.Single => "Single Group",
GroupType.Multi => "Multi Group",
_ => "Unknown",
};
@ -651,16 +652,16 @@ public partial class ConfigWindow
return;
}
if( ImGui.Selectable( GroupTypeName( SelectType.Single ), group.Type == SelectType.Single ) )
if( ImGui.Selectable( GroupTypeName( GroupType.Single ), group.Type == GroupType.Single ) )
{
Penumbra.ModManager.ChangeModGroupType( _mod, groupIdx, SelectType.Single );
Penumbra.ModManager.ChangeModGroupType( _mod, groupIdx, GroupType.Single );
}
var canSwitchToMulti = group.Count <= IModGroup.MaxMultiOptions;
using var style = ImRaii.PushStyle( ImGuiStyleVar.Alpha, 0.5f, !canSwitchToMulti );
if( ImGui.Selectable( GroupTypeName( SelectType.Multi ), group.Type == SelectType.Multi ) && canSwitchToMulti )
if( ImGui.Selectable( GroupTypeName( GroupType.Multi ), group.Type == GroupType.Multi ) && canSwitchToMulti )
{
Penumbra.ModManager.ChangeModGroupType( _mod, groupIdx, SelectType.Multi );
Penumbra.ModManager.ChangeModGroupType( _mod, groupIdx, GroupType.Multi );
}
style.Pop();

View file

@ -5,6 +5,7 @@ using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using OtterGui.Widgets;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.Mods;
using Penumbra.UI.Classes;
@ -154,7 +155,7 @@ public partial class ConfigWindow
// If a description is provided, add a help marker besides it.
private void DrawSingleGroup( IModGroup group, int groupIdx )
{
if( group.Type != SelectType.Single || !group.IsOption )
if( group.Type != GroupType.Single || !group.IsOption )
{
return;
}
@ -193,7 +194,7 @@ public partial class ConfigWindow
// If a description is provided, add a help marker in the title.
private void DrawMultiGroup( IModGroup group, int groupIdx )
{
if( group.Type != SelectType.Multi || !group.IsOption )
if( group.Type != GroupType.Multi || !group.IsOption )
{
return;
}

View file

@ -8,6 +8,7 @@ using System;
using System.Linq;
using System.Numerics;
using Dalamud.Interface;
using Penumbra.Api.Enums;
using Penumbra.GameData.Enums;
namespace Penumbra.UI;

View file

@ -58,26 +58,17 @@
"System.ValueTuple": "4.5.0"
}
},
"DirectXTex": {
"type": "Project"
},
"directxtexc": {
"type": "Project",
"dependencies": {
"DirectXTex": "[1.0.0, )"
}
},
"ottergui": {
"type": "Project"
},
"ottertex": {
"type": "Project",
"dependencies": {
"DirectXTexC": "[1.0.0, )"
}
"penumbra.api": {
"type": "Project"
},
"penumbra.gamedata": {
"type": "Project"
"type": "Project",
"dependencies": {
"Penumbra.Api": "[1.0.0, )"
}
}
}
}