diff --git a/.editorconfig b/.editorconfig index e283b2a2..238bb1dc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -37,7 +37,7 @@ resharper_autodetect_indent_settings=true resharper_braces_redundant=true resharper_constructor_or_destructor_body=expression_body resharper_csharp_empty_block_style=together -resharper_csharp_max_line_length=144 +resharper_csharp_max_line_length=180 resharper_csharp_space_within_array_access_brackets=true resharper_enforce_line_ending_style=true resharper_int_align_assignments=true diff --git a/.gitmodules b/.gitmodules index df7b5848..b5eb77bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,7 @@ path = OtterGui url = git@github.com:Ottermandias/OtterGui.git branch = main +[submodule "Penumbra.Api"] + path = Penumbra.Api + url = git@github.com:Ottermandias/Penumbra.Api.git + branch = main diff --git a/Penumbra.Api b/Penumbra.Api new file mode 160000 index 00000000..0064bb82 --- /dev/null +++ b/Penumbra.Api @@ -0,0 +1 @@ +Subproject commit 0064bb82be9729676e7bf3202ff1407283e6f088 diff --git a/Penumbra.GameData/Enums/ChangedItemExtensions.cs b/Penumbra.GameData/Enums/ChangedItemExtensions.cs new file mode 100644 index 00000000..68674268 --- /dev/null +++ b/Penumbra.GameData/Enums/ChangedItemExtensions.cs @@ -0,0 +1,32 @@ +using System; +using Lumina.Excel.GeneratedSheets; +using Penumbra.Api.Enums; +using Action = Lumina.Excel.GeneratedSheets.Action; + +namespace Penumbra.GameData.Enums; + +public static class ChangedItemExtensions +{ + public static (ChangedItemType, uint) ChangedItemToTypeAndId( object? item ) + { + return item switch + { + null => ( ChangedItemType.None, 0 ), + Item i => ( ChangedItemType.Item, i.RowId ), + Action a => ( ChangedItemType.Action, a.RowId ), + _ => ( ChangedItemType.Customization, 0 ), + }; + } + + public static object? GetObject( this ChangedItemType type, uint id ) + { + return type switch + { + ChangedItemType.None => null, + ChangedItemType.Item => ObjectIdentification.DataManager?.GetExcelSheet< Item >()?.GetRow( id ), + ChangedItemType.Action => ObjectIdentification.DataManager?.GetExcelSheet< Action >()?.GetRow( id ), + ChangedItemType.Customization => null, + _ => throw new ArgumentOutOfRangeException( nameof( type ), type, null ), + }; + } +} \ No newline at end of file diff --git a/Penumbra.GameData/Enums/ChangedItemType.cs b/Penumbra.GameData/Enums/ChangedItemType.cs deleted file mode 100644 index fa33382d..00000000 --- a/Penumbra.GameData/Enums/ChangedItemType.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using Lumina.Excel.GeneratedSheets; -using Action = Lumina.Excel.GeneratedSheets.Action; - -namespace Penumbra.GameData.Enums -{ - public enum ChangedItemType - { - None, - Item, - Action, - Customization, - } - - public static class ChangedItemExtensions - { - public static (ChangedItemType, uint) ChangedItemToTypeAndId( object? item ) - { - return item switch - { - null => ( ChangedItemType.None, 0 ), - Item i => ( ChangedItemType.Item, i.RowId ), - Action a => ( ChangedItemType.Action, a.RowId ), - _ => ( ChangedItemType.Customization, 0 ), - }; - } - - public static object? GetObject( this ChangedItemType type, uint id ) - { - return type switch - { - ChangedItemType.None => null, - ChangedItemType.Item => ObjectIdentification.DataManager?.GetExcelSheet< Item >()?.GetRow( id ), - ChangedItemType.Action => ObjectIdentification.DataManager?.GetExcelSheet< Action >()?.GetRow( id ), - ChangedItemType.Customization => null, - _ => throw new ArgumentOutOfRangeException( nameof( type ), type, null ) - }; - } - } -} diff --git a/Penumbra.GameData/Enums/MouseButton.cs b/Penumbra.GameData/Enums/MouseButton.cs deleted file mode 100644 index 99948d7c..00000000 --- a/Penumbra.GameData/Enums/MouseButton.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Penumbra.GameData.Enums -{ - public enum MouseButton - { - None, - Left, - Right, - Middle, - } -} \ No newline at end of file diff --git a/Penumbra.GameData/Penumbra.GameData.csproj b/Penumbra.GameData/Penumbra.GameData.csproj index ad466524..cb51d6dc 100644 --- a/Penumbra.GameData/Penumbra.GameData.csproj +++ b/Penumbra.GameData/Penumbra.GameData.csproj @@ -34,6 +34,10 @@ $(AppData)\XIVLauncher\addon\Hooks\dev\ + + + + $(DalamudLibPath)Dalamud.dll diff --git a/Penumbra.sln b/Penumbra.sln index b43c7565..33e5a03d 100644 --- a/Penumbra.sln +++ b/Penumbra.sln @@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "Penumb EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OtterGui", "OtterGui\OtterGui.csproj", "{87750518-1A20-40B4-9FC1-22F906EFB290}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.Api", "Penumbra.Api\Penumbra.Api.csproj", "{1FE4D8DF-B56A-464F-B39E-CDC0ED4167D4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,6 +34,10 @@ Global {87750518-1A20-40B4-9FC1-22F906EFB290}.Debug|Any CPU.Build.0 = Debug|Any CPU {87750518-1A20-40B4-9FC1-22F906EFB290}.Release|Any CPU.ActiveCfg = Release|Any CPU {87750518-1A20-40B4-9FC1-22F906EFB290}.Release|Any CPU.Build.0 = Release|Any CPU + {1FE4D8DF-B56A-464F-B39E-CDC0ED4167D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1FE4D8DF-B56A-464F-B39E-CDC0ED4167D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1FE4D8DF-B56A-464F-B39E-CDC0ED4167D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1FE4D8DF-B56A-464F-B39E-CDC0ED4167D4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Penumbra/Api/IpcTester.cs b/Penumbra/Api/IpcTester.cs index 1fd29dec..486eb826 100644 --- a/Penumbra/Api/IpcTester.cs +++ b/Penumbra/Api/IpcTester.cs @@ -1,76 +1,94 @@ -using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Interface; using Dalamud.Plugin; -using Dalamud.Plugin.Ipc; using ImGuiNET; using OtterGui; using OtterGui.Raii; -using Penumbra.Collections; using Penumbra.GameData.ByteString; -using Penumbra.GameData.Enums; using Penumbra.Mods; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Numerics; -using System.Reflection; +using Penumbra.Api.Enums; +using Penumbra.Api.Helpers; namespace Penumbra.Api; public class IpcTester : IDisposable { - private readonly PenumbraIpc _ipc; - private readonly DalamudPluginInterface _pi; + private readonly PenumbraIpcProviders _ipcProviders; + private bool _subscribed = true; - private readonly ICallGateSubscriber< object? > _initialized; - private readonly ICallGateSubscriber< object? > _disposed; - private readonly ICallGateSubscriber< string, object? > _preSettingsDraw; - private readonly ICallGateSubscriber< string, object? > _postSettingsDraw; - private readonly ICallGateSubscriber< string, bool, object? > _modDirectoryChanged; - private readonly ICallGateSubscriber< IntPtr, int, object? > _redrawn; - private readonly ICallGateSubscriber< ModSettingChange, string, string, bool, object? > _settingChanged; - private readonly ICallGateSubscriber< IntPtr, string, IntPtr, IntPtr, IntPtr, object? > _characterBaseCreating; - private readonly ICallGateSubscriber< IntPtr, string, IntPtr, object? > _characterBaseCreated; - private readonly ICallGateSubscriber< IntPtr, string, string, object? > _gameObjectResourcePathResolved; + private readonly PluginState _pluginState; + private readonly Configuration _configuration; + private readonly Ui _ui; + private readonly Redrawing _redrawing; + private readonly GameState _gameState; + private readonly Resolve _resolve; + private readonly Collections _collections; + private readonly Meta _meta; + private readonly Mods _mods; + private readonly ModSettings _modSettings; + private readonly Temporary _temporary; - private readonly List< DateTimeOffset > _initializedList = new(); - private readonly List< DateTimeOffset > _disposedList = new(); - private bool _subscribed = false; - - - public IpcTester( DalamudPluginInterface pi, PenumbraIpc ipc ) + public IpcTester( DalamudPluginInterface pi, PenumbraIpcProviders ipcProviders ) { - _ipc = ipc; - _pi = pi; - _initialized = _pi.GetIpcSubscriber< object? >( PenumbraIpc.LabelProviderInitialized ); - _disposed = _pi.GetIpcSubscriber< object? >( PenumbraIpc.LabelProviderDisposed ); - _redrawn = _pi.GetIpcSubscriber< IntPtr, int, object? >( PenumbraIpc.LabelProviderGameObjectRedrawn ); - _preSettingsDraw = _pi.GetIpcSubscriber< string, object? >( PenumbraIpc.LabelProviderPreSettingsDraw ); - _postSettingsDraw = _pi.GetIpcSubscriber< string, object? >( PenumbraIpc.LabelProviderPostSettingsDraw ); - _settingChanged = _pi.GetIpcSubscriber< ModSettingChange, string, string, bool, object? >( PenumbraIpc.LabelProviderModSettingChanged ); - _modDirectoryChanged = _pi.GetIpcSubscriber< string, bool, object? >( PenumbraIpc.LabelProviderModDirectoryChanged ); - _characterBaseCreating = - _pi.GetIpcSubscriber< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >( PenumbraIpc.LabelProviderCreatingCharacterBase ); - _characterBaseCreated = _pi.GetIpcSubscriber< IntPtr, string, IntPtr, object? >( PenumbraIpc.LabelProviderCreatedCharacterBase ); - _gameObjectResourcePathResolved = - _pi.GetIpcSubscriber< IntPtr, string, string, object? >( PenumbraIpc.LabelProviderGameObjectResourcePathResolved ); + _ipcProviders = ipcProviders; + _pluginState = new PluginState( pi ); + _configuration = new Configuration( pi ); + _ui = new Ui( pi ); + _redrawing = new Redrawing( pi ); + _gameState = new GameState( pi ); + _resolve = new Resolve( pi ); + _collections = new Collections( pi ); + _meta = new Meta( pi ); + _mods = new Mods( pi ); + _modSettings = new ModSettings( pi ); + _temporary = new Temporary( pi ); + UnsubscribeEvents(); + } + + public void Draw() + { + try + { + SubscribeEvents(); + ImGui.TextUnformatted( $"API Version: {_ipcProviders.Api.ApiVersion.Breaking}.{_ipcProviders.Api.ApiVersion.Feature:D4}" ); + _pluginState.Draw(); + _configuration.Draw(); + _ui.Draw(); + _redrawing.Draw(); + _gameState.Draw(); + _resolve.Draw(); + _collections.Draw(); + _meta.Draw(); + _mods.Draw(); + _modSettings.Draw(); + _temporary.Draw(); + _temporary.DrawCollections(); + _temporary.DrawMods(); + } + catch( Exception e ) + { + Penumbra.Log.Error( $"Error during IPC Tests:\n{e}" ); + } } private void SubscribeEvents() { if( !_subscribed ) { - _initialized.Subscribe( AddInitialized ); - _disposed.Subscribe( AddDisposed ); - _redrawn.Subscribe( SetLastRedrawn ); - _preSettingsDraw.Subscribe( UpdateLastDrawnMod ); - _postSettingsDraw.Subscribe( UpdateLastDrawnMod ); - _settingChanged.Subscribe( UpdateLastModSetting ); - _characterBaseCreating.Subscribe( UpdateLastCreated ); - _characterBaseCreated.Subscribe( UpdateLastCreated2 ); - _modDirectoryChanged.Subscribe( UpdateModDirectoryChanged ); - _gameObjectResourcePathResolved.Subscribe( UpdateGameObjectResourcePath ); + _pluginState.Initialized.Enable(); + _pluginState.Disposed.Enable(); + _redrawing.Redrawn.Enable(); + _ui.PreSettingsDraw.Enable(); + _ui.PostSettingsDraw.Enable(); + _modSettings.SettingChanged.Enable(); + _gameState.CharacterBaseCreating.Enable(); + _gameState.CharacterBaseCreated.Enable(); + _configuration.ModDirectoryChanged.Enable(); + _gameState.GameObjectResourcePathResolved.Enable(); _subscribed = true; } } @@ -79,75 +97,37 @@ public class IpcTester : IDisposable { if( _subscribed ) { - _initialized.Unsubscribe( AddInitialized ); - _disposed.Unsubscribe( AddDisposed ); - _redrawn.Subscribe( SetLastRedrawn ); - _tooltip?.Unsubscribe( AddedTooltip ); - _click?.Unsubscribe( AddedClick ); - _preSettingsDraw.Unsubscribe( UpdateLastDrawnMod ); - _postSettingsDraw.Unsubscribe( UpdateLastDrawnMod ); - _settingChanged.Unsubscribe( UpdateLastModSetting ); - _characterBaseCreating.Unsubscribe( UpdateLastCreated ); - _characterBaseCreated.Unsubscribe( UpdateLastCreated2 ); - _modDirectoryChanged.Unsubscribe( UpdateModDirectoryChanged ); - _gameObjectResourcePathResolved.Unsubscribe( UpdateGameObjectResourcePath ); + _pluginState.Initialized.Disable(); + _pluginState.Disposed.Disable(); + _redrawing.Redrawn.Disable(); + _ui.PreSettingsDraw.Disable(); + _ui.PostSettingsDraw.Disable(); + _ui.Tooltip.Disable(); + _ui.Click.Disable(); + _modSettings.SettingChanged.Disable(); + _gameState.CharacterBaseCreating.Disable(); + _gameState.CharacterBaseCreated.Disable(); + _configuration.ModDirectoryChanged.Disable(); + _gameState.GameObjectResourcePathResolved.Disable(); _subscribed = false; } } public void Dispose() - => UnsubscribeEvents(); - - private void AddInitialized() - => _initializedList.Add( DateTimeOffset.UtcNow ); - - private void AddDisposed() - => _disposedList.Add( DateTimeOffset.UtcNow ); - - public void Draw() { - try - { - SubscribeEvents(); - DrawAvailable(); - DrawGeneral(); - DrawResolve(); - DrawRedraw(); - DrawChangedItems(); - DrawData(); - DrawSetting(); - DrawTemp(); - DrawTempCollections(); - DrawTempMods(); - } - catch( Exception e ) - { - Penumbra.Log.Error( $"Error during IPC Tests:\n{e}" ); - } - } - - private void DrawAvailable() - { - using var _ = ImRaii.TreeNode( "Availability" ); - if( !_ ) - { - return; - } - - ImGui.TextUnformatted( $"API Version: {_ipc.Api.ApiVersion.Breaking}.{_ipc.Api.ApiVersion.Feature:D4}" ); - ImGui.TextUnformatted( "Available subscriptions:" ); - using var indent = ImRaii.PushIndent(); - - var dict = _ipc.GetType().GetFields( BindingFlags.Static | BindingFlags.Public ).Where( f => f.IsLiteral ) - .ToDictionary( f => f.Name, f => f.GetValue( _ipc ) as string ); - foreach( var provider in _ipc.GetType().GetFields( BindingFlags.Instance | BindingFlags.NonPublic ) ) - { - var value = provider.GetValue( _ipc ); - if( value != null && dict.TryGetValue( "Label" + provider.Name, out var label ) ) - { - ImGui.TextUnformatted( label ); - } - } + _pluginState.Initialized.Dispose(); + _pluginState.Disposed.Dispose(); + _redrawing.Redrawn.Dispose(); + _ui.PreSettingsDraw.Dispose(); + _ui.PostSettingsDraw.Dispose(); + _ui.Tooltip.Dispose(); + _ui.Click.Dispose(); + _modSettings.SettingChanged.Dispose(); + _gameState.CharacterBaseCreating.Dispose(); + _gameState.CharacterBaseCreated.Dispose(); + _configuration.ModDirectoryChanged.Dispose(); + _gameState.GameObjectResourcePathResolved.Dispose(); + _subscribed = false; } private static void DrawIntro( string label, string info ) @@ -159,605 +139,849 @@ public class IpcTester : IDisposable ImGui.TableNextColumn(); } - private string _currentConfiguration = string.Empty; - private string _lastDrawnMod = string.Empty; - private DateTimeOffset _lastDrawnModTime = DateTimeOffset.MinValue; - private void UpdateLastDrawnMod( string name ) - => ( _lastDrawnMod, _lastDrawnModTime ) = ( name, DateTimeOffset.Now ); - - private string _lastModDirectory = string.Empty; - private bool _lastModDirectoryValid = false; - private DateTimeOffset _lastModDirectoryTime = DateTimeOffset.MinValue; - - private void UpdateModDirectoryChanged( string path, bool valid ) - => ( _lastModDirectory, _lastModDirectoryValid, _lastModDirectoryTime ) = ( path, valid, DateTimeOffset.Now ); - - private void DrawGeneral() + private class PluginState { - using var _ = ImRaii.TreeNode( "General IPC" ); - if( !_ ) + private readonly DalamudPluginInterface _pi; + public readonly EventSubscriber Initialized; + public readonly EventSubscriber Disposed; + + private readonly List< DateTimeOffset > _initializedList = new(); + private readonly List< DateTimeOffset > _disposedList = new(); + + public PluginState( DalamudPluginInterface pi ) { - return; + _pi = pi; + Initialized = Ipc.Initialized.Subscriber( pi, AddInitialized ); + Disposed = Ipc.Disposed.Subscriber( pi, AddDisposed ); } - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - - void DrawList( string label, string text, List< DateTimeOffset > list ) + public void Draw() { - DrawIntro( label, text ); - if( list.Count == 0 ) + using var _ = ImRaii.TreeNode( "Plugin State" ); + if( !_ ) { - ImGui.TextUnformatted( "Never" ); + return; } - else + + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + if( !table ) { - ImGui.TextUnformatted( list[ ^1 ].LocalDateTime.ToString( CultureInfo.CurrentCulture ) ); - if( list.Count > 1 && ImGui.IsItemHovered() ) + return; + } + + void DrawList( string label, string text, List< DateTimeOffset > list ) + { + DrawIntro( label, text ); + if( list.Count == 0 ) { - ImGui.SetTooltip( string.Join( "\n", - list.SkipLast( 1 ).Select( t => t.LocalDateTime.ToString( CultureInfo.CurrentCulture ) ) ) ); + ImGui.TextUnformatted( "Never" ); + } + else + { + ImGui.TextUnformatted( list[ ^1 ].LocalDateTime.ToString( CultureInfo.CurrentCulture ) ); + if( list.Count > 1 && ImGui.IsItemHovered() ) + { + ImGui.SetTooltip( string.Join( "\n", + list.SkipLast( 1 ).Select( t => t.LocalDateTime.ToString( CultureInfo.CurrentCulture ) ) ) ); + } + } + } + + DrawList( Ipc.Initialized.Label, "Last Initialized", _initializedList ); + DrawList( Ipc.Disposed.Label, "Last Disposed", _disposedList ); + DrawIntro( Ipc.ApiVersions.Label, "Current Version" ); + var (breaking, features) = Ipc.ApiVersions.Subscriber( _pi ).Invoke(); + ImGui.TextUnformatted( $"{breaking}.{features:D4}" ); + } + + private void AddInitialized() + => _initializedList.Add( DateTimeOffset.UtcNow ); + + private void AddDisposed() + => _disposedList.Add( DateTimeOffset.UtcNow ); + } + + private class Configuration + { + private readonly DalamudPluginInterface _pi; + public readonly EventSubscriber< string, bool > ModDirectoryChanged; + + private string _currentConfiguration = string.Empty; + private string _lastModDirectory = string.Empty; + private bool _lastModDirectoryValid; + private DateTimeOffset _lastModDirectoryTime = DateTimeOffset.MinValue; + + public Configuration( DalamudPluginInterface pi ) + { + _pi = pi; + ModDirectoryChanged = Ipc.ModDirectoryChanged.Subscriber( pi, UpdateModDirectoryChanged ); + } + + public void Draw() + { + using var _ = ImRaii.TreeNode( "Configuration" ); + if( !_ ) + { + return; + } + + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + if( !table ) + { + return; + } + + DrawIntro( Ipc.GetModDirectory.Label, "Current Mod Directory" ); + ImGui.TextUnformatted( Ipc.GetModDirectory.Subscriber( _pi ).Invoke() ); + DrawIntro( Ipc.ModDirectoryChanged.Label, "Last Mod Directory Change" ); + ImGui.TextUnformatted( _lastModDirectoryTime > DateTimeOffset.MinValue + ? $"{_lastModDirectory} ({( _lastModDirectoryValid ? "Valid" : "Invalid" )}) at {_lastModDirectoryTime}" + : "None" ); + DrawIntro( Ipc.GetConfiguration.Label, "Configuration" ); + if( ImGui.Button( "Get" ) ) + { + _currentConfiguration = Ipc.GetConfiguration.Subscriber( _pi ).Invoke(); + ImGui.OpenPopup( "Config Popup" ); + } + + DrawConfigPopup(); + } + + private void DrawConfigPopup() + { + ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); + using var popup = ImRaii.Popup( "Config Popup" ); + if( popup ) + { + using( var font = ImRaii.PushFont( UiBuilder.MonoFont ) ) + { + ImGuiUtil.TextWrapped( _currentConfiguration ); + } + + if( ImGui.Button( "Close", -Vector2.UnitX ) || !ImGui.IsWindowFocused() ) + { + ImGui.CloseCurrentPopup(); } } } - DrawList( PenumbraIpc.LabelProviderInitialized, "Last Initialized", _initializedList ); - DrawList( PenumbraIpc.LabelProviderDisposed, "Last Disposed", _disposedList ); - DrawIntro( PenumbraIpc.LabelProviderPostSettingsDraw, "Last Drawn Mod" ); - ImGui.TextUnformatted( _lastDrawnMod.Length > 0 ? $"{_lastDrawnMod} at {_lastDrawnModTime}" : "None" ); - DrawIntro( PenumbraIpc.LabelProviderApiVersions, "Current Version" ); - var (breaking, features) = _pi.GetIpcSubscriber< (int, int) >( PenumbraIpc.LabelProviderApiVersions ).InvokeFunc(); - ImGui.TextUnformatted( $"{breaking}.{features:D4}" ); - DrawIntro( PenumbraIpc.LabelProviderGetModDirectory, "Current Mod Directory" ); - ImGui.TextUnformatted( _pi.GetIpcSubscriber< string >( PenumbraIpc.LabelProviderGetModDirectory ).InvokeFunc() ); - DrawIntro( PenumbraIpc.LabelProviderModDirectoryChanged, "Last Mod Directory Change" ); - ImGui.TextUnformatted( _lastModDirectoryTime > DateTimeOffset.MinValue - ? $"{_lastModDirectory} ({( _lastModDirectoryValid ? "Valid" : "Invalid" )}) at {_lastModDirectoryTime}" - : "None" ); - DrawIntro( PenumbraIpc.LabelProviderGetConfiguration, "Configuration" ); - if( ImGui.Button( "Get" ) ) + private void UpdateModDirectoryChanged( string path, bool valid ) + => ( _lastModDirectory, _lastModDirectoryValid, _lastModDirectoryTime ) = ( path, valid, DateTimeOffset.Now ); + } + + private class Ui + { + private readonly DalamudPluginInterface _pi; + public readonly EventSubscriber< string > PreSettingsDraw; + public readonly EventSubscriber< string > PostSettingsDraw; + public readonly EventSubscriber< ChangedItemType, uint > Tooltip; + public readonly EventSubscriber< MouseButton, ChangedItemType, uint > Click; + + private string _lastDrawnMod = string.Empty; + private DateTimeOffset _lastDrawnModTime = DateTimeOffset.MinValue; + private bool _subscribedToTooltip = false; + private bool _subscribedToClick = false; + private string _lastClicked = string.Empty; + private string _lastHovered = string.Empty; + + public Ui( DalamudPluginInterface pi ) { - _currentConfiguration = _pi.GetIpcSubscriber< string >( PenumbraIpc.LabelProviderGetConfiguration ).InvokeFunc(); - ImGui.OpenPopup( "Config Popup" ); + _pi = pi; + PreSettingsDraw = Ipc.PreSettingsDraw.Subscriber( pi, UpdateLastDrawnMod ); + PostSettingsDraw = Ipc.PostSettingsDraw.Subscriber( pi, UpdateLastDrawnMod ); + Tooltip = Ipc.ChangedItemTooltip.Subscriber( pi, AddedTooltip ); + Click = Ipc.ChangedItemClick.Subscriber( pi, AddedClick ); } - ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); - using var popup = ImRaii.Popup( "Config Popup" ); - if( popup ) + public void Draw() { - using( var font = ImRaii.PushFont( UiBuilder.MonoFont ) ) + using var _ = ImRaii.TreeNode( "UI" ); + if( !_ ) { - ImGuiUtil.TextWrapped( _currentConfiguration ); + return; } - if( ImGui.Button( "Close", -Vector2.UnitX ) || ImGui.IsWindowFocused() ) + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + if( !table ) { - ImGui.CloseCurrentPopup(); + return; } - } - } - private string _currentResolvePath = string.Empty; - private string _currentResolveCharacter = string.Empty; - private string _currentDrawObjectString = string.Empty; - private string _currentReversePath = string.Empty; - private IntPtr _currentDrawObject = IntPtr.Zero; - private int _currentCutsceneActor = 0; - private string _lastCreatedGameObjectName = string.Empty; - private IntPtr _lastCreatedDrawObject = IntPtr.Zero; - private DateTimeOffset _lastCreatedGameObjectTime = DateTimeOffset.MaxValue; - private string _lastResolvedGamePath = string.Empty; - private string _lastResolvedFullPath = string.Empty; - private string _lastResolvedObject = string.Empty; - private DateTimeOffset _lastResolvedGamePathTime = DateTimeOffset.MaxValue; + DrawIntro( Ipc.PostSettingsDraw.Label, "Last Drawn Mod" ); + ImGui.TextUnformatted( _lastDrawnMod.Length > 0 ? $"{_lastDrawnMod} at {_lastDrawnModTime}" : "None" ); - private unsafe void UpdateLastCreated( IntPtr gameObject, string _, IntPtr _2, IntPtr _3, IntPtr _4 ) - { - var obj = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )gameObject; - _lastCreatedGameObjectName = new Utf8String( obj->GetName() ).ToString(); - _lastCreatedGameObjectTime = DateTimeOffset.Now; - _lastCreatedDrawObject = IntPtr.Zero; - } - - private unsafe void UpdateLastCreated2( IntPtr gameObject, string _, IntPtr drawObject ) - { - var obj = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )gameObject; - _lastCreatedGameObjectName = new Utf8String( obj->GetName() ).ToString(); - _lastCreatedGameObjectTime = DateTimeOffset.Now; - _lastCreatedDrawObject = drawObject; - } - - private unsafe void UpdateGameObjectResourcePath( IntPtr gameObject, string gamePath, string fullPath ) - { - var obj = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )gameObject; - _lastResolvedObject = obj != null ? new Utf8String( obj->GetName() ).ToString() : "Unknown"; - _lastResolvedGamePath = gamePath; - _lastResolvedFullPath = fullPath; - _lastResolvedGamePathTime = DateTimeOffset.Now; - } - - private void DrawResolve() - { - using var _ = ImRaii.TreeNode( "Resolve IPC" ); - if( !_ ) - { - return; - } - - ImGui.InputTextWithHint( "##resolvePath", "Resolve this game path...", ref _currentResolvePath, Utf8GamePath.MaxGamePathLength ); - ImGui.InputTextWithHint( "##resolveCharacter", "Character Name (leave blank for default)...", ref _currentResolveCharacter, 32 ); - ImGui.InputTextWithHint( "##resolveInversePath", "Reverse-resolve this path...", ref _currentReversePath, - Utf8GamePath.MaxGamePathLength ); - if( ImGui.InputTextWithHint( "##drawObject", "Draw Object Address..", ref _currentDrawObjectString, 16, - ImGuiInputTextFlags.CharsHexadecimal ) ) - { - _currentDrawObject = IntPtr.TryParse( _currentDrawObjectString, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var tmp ) - ? tmp - : IntPtr.Zero; - } - - ImGui.InputInt( "Cutscene Actor", ref _currentCutsceneActor, 0 ); - - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { - return; - } - - DrawIntro( PenumbraIpc.LabelProviderResolveDefault, "Default Collection Resolve" ); - if( _currentResolvePath.Length != 0 ) - { - ImGui.TextUnformatted( _pi.GetIpcSubscriber< string, string >( PenumbraIpc.LabelProviderResolveDefault ) - .InvokeFunc( _currentResolvePath ) ); - } - - DrawIntro( PenumbraIpc.LabelProviderResolveInterface, "Interface Collection Resolve" ); - if( _currentResolvePath.Length != 0 ) - { - ImGui.TextUnformatted( _pi.GetIpcSubscriber< string, string >( PenumbraIpc.LabelProviderResolveInterface ) - .InvokeFunc( _currentResolvePath ) ); - } - - DrawIntro( PenumbraIpc.LabelProviderResolveCharacter, "Character Collection Resolve" ); - if( _currentResolvePath.Length != 0 && _currentResolveCharacter.Length != 0 ) - { - ImGui.TextUnformatted( _pi.GetIpcSubscriber< string, string, string >( PenumbraIpc.LabelProviderResolveCharacter ) - .InvokeFunc( _currentResolvePath, _currentResolveCharacter ) ); - } - - DrawIntro( PenumbraIpc.LabelProviderGetDrawObjectInfo, "Draw Object Info" ); - if( _currentDrawObject == IntPtr.Zero ) - { - ImGui.TextUnformatted( "Invalid" ); - } - else - { - var (ptr, collection) = _pi.GetIpcSubscriber< IntPtr, (IntPtr, string) >( PenumbraIpc.LabelProviderGetDrawObjectInfo ) - .InvokeFunc( _currentDrawObject ); - ImGui.TextUnformatted( ptr == IntPtr.Zero ? $"No Actor Associated, {collection}" : $"{ptr:X}, {collection}" ); - } - - DrawIntro( PenumbraIpc.LabelProviderGetDrawObjectInfo, "Cutscene Parent" ); - ImGui.TextUnformatted( _pi.GetIpcSubscriber< int, int >( PenumbraIpc.LabelProviderGetCutsceneParentIndex ) - .InvokeFunc( _currentCutsceneActor ).ToString() ); - - DrawIntro( PenumbraIpc.LabelProviderReverseResolvePath, "Reversed Game Paths" ); - if( _currentReversePath.Length > 0 ) - { - var list = _pi.GetIpcSubscriber< string, string, string[] >( PenumbraIpc.LabelProviderReverseResolvePath ) - .InvokeFunc( _currentReversePath, _currentResolveCharacter ); - if( list.Length > 0 ) + DrawIntro( Ipc.ChangedItemTooltip.Label, "Add Tooltip" ); + if( ImGui.Checkbox( "##tooltip", ref _subscribedToTooltip ) ) { - ImGui.TextUnformatted( list[ 0 ] ); - if( list.Length > 1 && ImGui.IsItemHovered() ) + if( _subscribedToTooltip ) { - ImGui.SetTooltip( string.Join( "\n", list.Skip( 1 ) ) ); + Tooltip.Enable(); + } + else + { + Tooltip.Disable(); } } - } - DrawIntro( PenumbraIpc.LabelProviderReverseResolvePlayerPath, "Reversed Game Paths (Player)" ); - if( _currentReversePath.Length > 0 ) - { - var list = _pi.GetIpcSubscriber< string, string[] >( PenumbraIpc.LabelProviderReverseResolvePlayerPath ) - .InvokeFunc( _currentReversePath ); - if( list.Length > 0 ) + ImGui.SameLine(); + ImGui.TextUnformatted( _lastHovered ); + + DrawIntro( Ipc.ChangedItemClick.Label, "Subscribe Click" ); + if( ImGui.Checkbox( "##click", ref _subscribedToClick ) ) { - ImGui.TextUnformatted( list[ 0 ] ); - if( list.Length > 1 && ImGui.IsItemHovered() ) + if( _subscribedToClick ) { - ImGui.SetTooltip( string.Join( "\n", list.Skip( 1 ) ) ); + Click.Enable(); + } + else + { + Click.Disable(); } } + + ImGui.SameLine(); + ImGui.TextUnformatted( _lastClicked ); } - DrawIntro( PenumbraIpc.LabelProviderCreatingCharacterBase, "Last Drawobject created" ); - if( _lastCreatedGameObjectTime < DateTimeOffset.Now ) + private void UpdateLastDrawnMod( string name ) + => ( _lastDrawnMod, _lastDrawnModTime ) = ( name, DateTimeOffset.Now ); + + private void AddedTooltip( ChangedItemType type, uint id ) { - ImGui.TextUnformatted( _lastCreatedDrawObject != IntPtr.Zero - ? $"0x{_lastCreatedDrawObject:X} for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}" - : $"NULL for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}" ); + _lastHovered = $"{type} {id} at {DateTime.UtcNow.ToLocalTime().ToString( CultureInfo.CurrentCulture )}"; + ImGui.TextUnformatted( "IPC Test Successful" ); } - DrawIntro( PenumbraIpc.LabelProviderGameObjectResourcePathResolved, "Last GamePath resolved" ); - if( _lastResolvedGamePathTime < DateTimeOffset.Now ) + private void AddedClick( MouseButton button, ChangedItemType type, uint id ) { - ImGui.TextUnformatted( - $"{_lastResolvedGamePath} -> {_lastResolvedFullPath} for <{_lastResolvedObject}> at {_lastResolvedGamePathTime}" ); + _lastClicked = $"{button}-click on {type} {id} at {DateTime.UtcNow.ToLocalTime().ToString( CultureInfo.CurrentCulture )}"; } } - private string _redrawName = string.Empty; - private int _redrawIndex = 0; - private string _lastRedrawnString = "None"; - - private void SetLastRedrawn( IntPtr address, int index ) + private class Redrawing { - if( index < 0 || index > Dalamud.Objects.Length || address == IntPtr.Zero || Dalamud.Objects[ index ]?.Address != address ) + private readonly DalamudPluginInterface _pi; + public readonly EventSubscriber< IntPtr, int > Redrawn; + + private string _redrawName = string.Empty; + private int _redrawIndex = 0; + private string _lastRedrawnString = "None"; + + public Redrawing( DalamudPluginInterface pi ) { - _lastRedrawnString = "Invalid"; + _pi = pi; + Redrawn = Ipc.GameObjectRedrawn.Subscriber( pi, SetLastRedrawn ); } - _lastRedrawnString = $"{Dalamud.Objects[ index ]!.Name} (0x{address:X}, {index})"; - } - - private void DrawRedraw() - { - using var _ = ImRaii.TreeNode( "Redraw IPC" ); - if( !_ ) + public void Draw() { - return; - } - - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { - return; - } - - DrawIntro( PenumbraIpc.LabelProviderRedrawName, "Redraw by Name" ); - ImGui.SetNextItemWidth( 100 * ImGuiHelpers.GlobalScale ); - ImGui.InputTextWithHint( "##redrawName", "Name...", ref _redrawName, 32 ); - ImGui.SameLine(); - if( ImGui.Button( "Redraw##Name" ) ) - { - _pi.GetIpcSubscriber< string, int, object? >( PenumbraIpc.LabelProviderRedrawName ) - .InvokeAction( _redrawName, ( int )RedrawType.Redraw ); - } - - DrawIntro( PenumbraIpc.LabelProviderRedrawObject, "Redraw Player Character" ); - if( ImGui.Button( "Redraw##pc" ) && Dalamud.ClientState.LocalPlayer != null ) - { - _pi.GetIpcSubscriber< GameObject, int, object? >( PenumbraIpc.LabelProviderRedrawObject ) - .InvokeAction( Dalamud.ClientState.LocalPlayer, ( int )RedrawType.Redraw ); - } - - DrawIntro( PenumbraIpc.LabelProviderRedrawIndex, "Redraw by Index" ); - var tmp = _redrawIndex; - ImGui.SetNextItemWidth( 100 * ImGuiHelpers.GlobalScale ); - if( ImGui.DragInt( "##redrawIndex", ref tmp, 0.1f, 0, Dalamud.Objects.Length ) ) - { - _redrawIndex = Math.Clamp( tmp, 0, Dalamud.Objects.Length ); - } - - ImGui.SameLine(); - if( ImGui.Button( "Redraw##Index" ) ) - { - _pi.GetIpcSubscriber< int, int, object? >( PenumbraIpc.LabelProviderRedrawIndex ) - .InvokeAction( _redrawIndex, ( int )RedrawType.Redraw ); - } - - DrawIntro( PenumbraIpc.LabelProviderRedrawAll, "Redraw All" ); - if( ImGui.Button( "Redraw##All" ) ) - { - _pi.GetIpcSubscriber< int, object? >( PenumbraIpc.LabelProviderRedrawAll ).InvokeAction( ( int )RedrawType.Redraw ); - } - - DrawIntro( PenumbraIpc.LabelProviderGameObjectRedrawn, "Last Redrawn Object:" ); - ImGui.TextUnformatted( _lastRedrawnString ); - } - - private bool _subscribedToTooltip = false; - private bool _subscribedToClick = false; - private string _changedItemCollection = string.Empty; - private IReadOnlyDictionary< string, object? > _changedItems = new Dictionary< string, object? >(); - private string _lastClicked = string.Empty; - private string _lastHovered = string.Empty; - private ICallGateSubscriber< ChangedItemType, uint, object? >? _tooltip; - private ICallGateSubscriber< MouseButton, ChangedItemType, uint, object? >? _click; - - private void DrawChangedItems() - { - using var _ = ImRaii.TreeNode( "Changed Item IPC" ); - if( !_ ) - { - return; - } - - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { - return; - } - - DrawIntro( PenumbraIpc.LabelProviderChangedItemTooltip, "Add Tooltip" ); - if( ImGui.Checkbox( "##tooltip", ref _subscribedToTooltip ) ) - { - _tooltip = _pi.GetIpcSubscriber< ChangedItemType, uint, object? >( PenumbraIpc.LabelProviderChangedItemTooltip ); - if( _subscribedToTooltip ) + using var _ = ImRaii.TreeNode( "Redrawing" ); + if( !_ ) { - _tooltip.Subscribe( AddedTooltip ); + return; + } + + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + if( !table ) + { + return; + } + + DrawIntro( Ipc.RedrawObjectByName.Label, "Redraw by Name" ); + ImGui.SetNextItemWidth( 100 * ImGuiHelpers.GlobalScale ); + ImGui.InputTextWithHint( "##redrawName", "Name...", ref _redrawName, 32 ); + ImGui.SameLine(); + if( ImGui.Button( "Redraw##Name" ) ) + { + Ipc.RedrawObjectByName.Subscriber( _pi ).Invoke( _redrawName, RedrawType.Redraw ); + } + + DrawIntro( Ipc.RedrawObject.Label, "Redraw Player Character" ); + if( ImGui.Button( "Redraw##pc" ) && Dalamud.ClientState.LocalPlayer != null ) + { + Ipc.RedrawObject.Subscriber( _pi ).Invoke( Dalamud.ClientState.LocalPlayer, RedrawType.Redraw ); + } + + DrawIntro( Ipc.RedrawObjectByIndex.Label, "Redraw by Index" ); + var tmp = _redrawIndex; + ImGui.SetNextItemWidth( 100 * ImGuiHelpers.GlobalScale ); + if( ImGui.DragInt( "##redrawIndex", ref tmp, 0.1f, 0, Dalamud.Objects.Length ) ) + { + _redrawIndex = Math.Clamp( tmp, 0, Dalamud.Objects.Length ); + } + + ImGui.SameLine(); + if( ImGui.Button( "Redraw##Index" ) ) + { + Ipc.RedrawObjectByIndex.Subscriber( _pi ).Invoke( _redrawIndex, RedrawType.Redraw ); + } + + DrawIntro( Ipc.RedrawAll.Label, "Redraw All" ); + if( ImGui.Button( "Redraw##All" ) ) + { + Ipc.RedrawAll.Subscriber( _pi ).Invoke( RedrawType.Redraw ); + } + + DrawIntro( Ipc.GameObjectRedrawn.Label, "Last Redrawn Object:" ); + ImGui.TextUnformatted( _lastRedrawnString ); + } + + private void SetLastRedrawn( IntPtr address, int index ) + { + if( index < 0 || index > Dalamud.Objects.Length || address == IntPtr.Zero || Dalamud.Objects[ index ]?.Address != address ) + { + _lastRedrawnString = "Invalid"; + } + + _lastRedrawnString = $"{Dalamud.Objects[ index ]!.Name} (0x{address:X}, {index})"; + } + } + + private class GameState + { + private readonly DalamudPluginInterface _pi; + public readonly EventSubscriber< IntPtr, string, IntPtr, IntPtr, IntPtr > CharacterBaseCreating; + public readonly EventSubscriber< IntPtr, string, IntPtr > CharacterBaseCreated; + public readonly EventSubscriber< IntPtr, string, string > GameObjectResourcePathResolved; + + + private string _lastCreatedGameObjectName = string.Empty; + private IntPtr _lastCreatedDrawObject = IntPtr.Zero; + private DateTimeOffset _lastCreatedGameObjectTime = DateTimeOffset.MaxValue; + private string _lastResolvedGamePath = string.Empty; + private string _lastResolvedFullPath = string.Empty; + private string _lastResolvedObject = string.Empty; + private DateTimeOffset _lastResolvedGamePathTime = DateTimeOffset.MaxValue; + private string _currentDrawObjectString = string.Empty; + private IntPtr _currentDrawObject = IntPtr.Zero; + private int _currentCutsceneActor = 0; + + public GameState( DalamudPluginInterface pi ) + { + _pi = pi; + CharacterBaseCreating = Ipc.CreatingCharacterBase.Subscriber( pi, UpdateLastCreated ); + CharacterBaseCreated = Ipc.CreatedCharacterBase.Subscriber( pi, UpdateLastCreated2 ); + GameObjectResourcePathResolved = Ipc.GameObjectResourcePathResolved.Subscriber( pi, UpdateGameObjectResourcePath ); + } + + public void Draw() + { + using var _ = ImRaii.TreeNode( "Game State" ); + if( !_ ) + { + return; + } + + if( ImGui.InputTextWithHint( "##drawObject", "Draw Object Address..", ref _currentDrawObjectString, 16, + ImGuiInputTextFlags.CharsHexadecimal ) ) + { + _currentDrawObject = IntPtr.TryParse( _currentDrawObjectString, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var tmp ) + ? tmp + : IntPtr.Zero; + } + + ImGui.InputInt( "Cutscene Actor", ref _currentCutsceneActor, 0 ); + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + if( !table ) + { + return; + } + + DrawIntro( Ipc.GetDrawObjectInfo.Label, "Draw Object Info" ); + if( _currentDrawObject == IntPtr.Zero ) + { + ImGui.TextUnformatted( "Invalid" ); } else { - _tooltip.Unsubscribe( AddedTooltip ); + var (ptr, collection) = Ipc.GetDrawObjectInfo.Subscriber( _pi ).Invoke( _currentDrawObject ); + ImGui.TextUnformatted( ptr == IntPtr.Zero ? $"No Actor Associated, {collection}" : $"{ptr:X}, {collection}" ); } - } - ImGui.SameLine(); - ImGui.TextUnformatted( _lastHovered ); + DrawIntro( Ipc.GetCutsceneParentIndex.Label, "Cutscene Parent" ); + ImGui.TextUnformatted( Ipc.GetCutsceneParentIndex.Subscriber( _pi ).Invoke( _currentCutsceneActor ).ToString() ); - DrawIntro( PenumbraIpc.LabelProviderChangedItemClick, "Subscribe Click" ); - if( ImGui.Checkbox( "##click", ref _subscribedToClick ) ) - { - _click = _pi.GetIpcSubscriber< MouseButton, ChangedItemType, uint, object? >( PenumbraIpc.LabelProviderChangedItemClick ); - if( _subscribedToClick ) + DrawIntro( Ipc.CreatingCharacterBase.Label, "Last Drawobject created" ); + if( _lastCreatedGameObjectTime < DateTimeOffset.Now ) { - _click.Subscribe( AddedClick ); + ImGui.TextUnformatted( _lastCreatedDrawObject != IntPtr.Zero + ? $"0x{_lastCreatedDrawObject:X} for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}" + : $"NULL for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}" ); } - else + + DrawIntro( Ipc.GameObjectResourcePathResolved.Label, "Last GamePath resolved" ); + if( _lastResolvedGamePathTime < DateTimeOffset.Now ) { - _click.Unsubscribe( AddedClick ); + ImGui.TextUnformatted( + $"{_lastResolvedGamePath} -> {_lastResolvedFullPath} for <{_lastResolvedObject}> at {_lastResolvedGamePathTime}" ); } } - ImGui.SameLine(); - ImGui.TextUnformatted( _lastClicked ); - - DrawIntro( PenumbraIpc.LabelProviderGetChangedItems, "Changed Item List" ); - ImGui.SetNextItemWidth( 200 * ImGuiHelpers.GlobalScale ); - ImGui.InputTextWithHint( "##changedCollection", "Collection Name...", ref _changedItemCollection, 64 ); - ImGui.SameLine(); - if( ImGui.Button( "Get" ) ) + private unsafe void UpdateLastCreated( IntPtr gameObject, string _, IntPtr _2, IntPtr _3, IntPtr _4 ) { - _changedItems = _pi.GetIpcSubscriber< string, IReadOnlyDictionary< string, object? > >( PenumbraIpc.LabelProviderGetChangedItems ) - .InvokeFunc( _changedItemCollection ); - ImGui.OpenPopup( "Changed Item List" ); + var obj = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )gameObject; + _lastCreatedGameObjectName = new Utf8String( obj->GetName() ).ToString(); + _lastCreatedGameObjectTime = DateTimeOffset.Now; + _lastCreatedDrawObject = IntPtr.Zero; } - ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); - using var p = ImRaii.Popup( "Changed Item List" ); - if( p ) + private unsafe void UpdateLastCreated2( IntPtr gameObject, string _, IntPtr drawObject ) { + var obj = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )gameObject; + _lastCreatedGameObjectName = new Utf8String( obj->GetName() ).ToString(); + _lastCreatedGameObjectTime = DateTimeOffset.Now; + _lastCreatedDrawObject = drawObject; + } + + private unsafe void UpdateGameObjectResourcePath( IntPtr gameObject, string gamePath, string fullPath ) + { + var obj = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )gameObject; + _lastResolvedObject = obj != null ? new Utf8String( obj->GetName() ).ToString() : "Unknown"; + _lastResolvedGamePath = gamePath; + _lastResolvedFullPath = fullPath; + _lastResolvedGamePathTime = DateTimeOffset.Now; + } + } + + private class Resolve + { + private readonly DalamudPluginInterface _pi; + + private string _currentResolvePath = string.Empty; + private string _currentResolveCharacter = string.Empty; + private string _currentReversePath = string.Empty; + + public Resolve( DalamudPluginInterface pi ) + => _pi = pi; + + public void Draw() + { + using var _ = ImRaii.TreeNode( "Resolving" ); + if( !_ ) + { + return; + } + + ImGui.InputTextWithHint( "##resolvePath", "Resolve this game path...", ref _currentResolvePath, Utf8GamePath.MaxGamePathLength ); + ImGui.InputTextWithHint( "##resolveCharacter", "Character Name (leave blank for default)...", ref _currentResolveCharacter, 32 ); + ImGui.InputTextWithHint( "##resolveInversePath", "Reverse-resolve this path...", ref _currentReversePath, + Utf8GamePath.MaxGamePathLength ); + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + if( !table ) + { + return; + } + + DrawIntro( Ipc.ResolveDefaultPath.Label, "Default Collection Resolve" ); + if( _currentResolvePath.Length != 0 ) + { + ImGui.TextUnformatted( Ipc.ResolveDefaultPath.Subscriber( _pi ).Invoke( _currentResolvePath ) ); + } + + DrawIntro( Ipc.ResolveInterfacePath.Label, "Interface Collection Resolve" ); + if( _currentResolvePath.Length != 0 ) + { + ImGui.TextUnformatted( Ipc.ResolveInterfacePath.Subscriber( _pi ).Invoke( _currentResolvePath ) ); + } + + DrawIntro( Ipc.ResolvePlayerPath.Label, "Player Collection Resolve" ); + if( _currentResolvePath.Length != 0 ) + { + ImGui.TextUnformatted( Ipc.ResolvePlayerPath.Subscriber( _pi ).Invoke( _currentResolvePath ) ); + } + + DrawIntro( Ipc.ResolveCharacterPath.Label, "Character Collection Resolve" ); + if( _currentResolvePath.Length != 0 && _currentResolveCharacter.Length != 0 ) + { + ImGui.TextUnformatted( Ipc.ResolveCharacterPath.Subscriber( _pi ).Invoke( _currentResolvePath, _currentResolveCharacter ) ); + } + + DrawIntro( Ipc.ReverseResolvePath.Label, "Reversed Game Paths" ); + if( _currentReversePath.Length > 0 ) + { + var list = Ipc.ReverseResolvePath.Subscriber( _pi ).Invoke( _currentReversePath, _currentResolveCharacter ); + if( list.Length > 0 ) + { + ImGui.TextUnformatted( list[ 0 ] ); + if( list.Length > 1 && ImGui.IsItemHovered() ) + { + ImGui.SetTooltip( string.Join( "\n", list.Skip( 1 ) ) ); + } + } + } + + DrawIntro( Ipc.ReverseResolvePlayerPath.Label, "Reversed Game Paths (Player)" ); + if( _currentReversePath.Length > 0 ) + { + var list = Ipc.ReverseResolvePlayerPath.Subscriber( _pi ).Invoke( _currentReversePath ); + if( list.Length > 0 ) + { + ImGui.TextUnformatted( list[ 0 ] ); + if( list.Length > 1 && ImGui.IsItemHovered() ) + { + ImGui.SetTooltip( string.Join( "\n", list.Skip( 1 ) ) ); + } + } + } + } + } + + private class Collections + { + private readonly DalamudPluginInterface _pi; + + private string _characterCollectionName = string.Empty; + private IList< string > _collections = new List< string >(); + private string _changedItemCollection = string.Empty; + private IReadOnlyDictionary< string, object? > _changedItems = new Dictionary< string, object? >(); + + public Collections( DalamudPluginInterface pi ) + => _pi = pi; + + public void Draw() + { + using var _ = ImRaii.TreeNode( "Collections" ); + if( !_ ) + { + return; + } + + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + if( !table ) + { + return; + } + + DrawIntro( Ipc.GetCurrentCollectionName.Label, "Current Collection" ); + ImGui.TextUnformatted( Ipc.GetCurrentCollectionName.Subscriber( _pi ).Invoke() ); + DrawIntro( Ipc.GetDefaultCollectionName.Label, "Default Collection" ); + ImGui.TextUnformatted( Ipc.GetDefaultCollectionName.Subscriber( _pi ).Invoke() ); + DrawIntro( Ipc.GetInterfaceCollectionName.Label, "Interface Collection" ); + ImGui.TextUnformatted( Ipc.GetInterfaceCollectionName.Subscriber( _pi ).Invoke() ); + DrawIntro( Ipc.GetCharacterCollectionName.Label, "Character" ); + ImGui.SetNextItemWidth( 200 * ImGuiHelpers.GlobalScale ); + ImGui.InputTextWithHint( "##characterCollectionName", "Character Name...", ref _characterCollectionName, 64 ); + var (c, s) = Ipc.GetCharacterCollectionName.Subscriber( _pi ).Invoke( _characterCollectionName ); + ImGui.SameLine(); + ImGui.TextUnformatted( $"{c}, {( s ? "Custom" : "Default" )}" ); + + DrawIntro( Ipc.GetCollections.Label, "Collections" ); + if( ImGui.Button( "Get##Collections" ) ) + { + _collections = Ipc.GetCollections.Subscriber( _pi ).Invoke(); + ImGui.OpenPopup( "Collections" ); + } + + DrawIntro( Ipc.GetChangedItems.Label, "Changed Item List" ); + ImGui.SetNextItemWidth( 200 * ImGuiHelpers.GlobalScale ); + ImGui.InputTextWithHint( "##changedCollection", "Collection Name...", ref _changedItemCollection, 64 ); + ImGui.SameLine(); + if( ImGui.Button( "Get" ) ) + { + _changedItems = Ipc.GetChangedItems.Subscriber( _pi ).Invoke( _changedItemCollection ); + ImGui.OpenPopup( "Changed Item List" ); + } + + DrawChangedItemPopup(); + DrawCollectionPopup(); + } + + private void DrawChangedItemPopup() + { + ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); + using var p = ImRaii.Popup( "Changed Item List" ); + if( !p ) + { + return; + } + foreach( var item in _changedItems ) { ImGui.TextUnformatted( item.Key ); } - if( ImGui.Button( "Close", -Vector2.UnitX ) || ImGui.IsWindowFocused() ) + if( ImGui.Button( "Close", -Vector2.UnitX ) || !ImGui.IsWindowFocused() ) + { + ImGui.CloseCurrentPopup(); + } + } + + private void DrawCollectionPopup() + { + ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); + using var p = ImRaii.Popup( "Collections" ); + if( !p ) + { + return; + } + + foreach( var collection in _collections ) + { + ImGui.TextUnformatted( collection ); + } + + if( ImGui.Button( "Close", -Vector2.UnitX ) || !ImGui.IsWindowFocused() ) { ImGui.CloseCurrentPopup(); } } } - private void AddedTooltip( ChangedItemType type, uint id ) + private class Meta { - _lastHovered = $"{type} {id} at {DateTime.UtcNow.ToLocalTime().ToString( CultureInfo.CurrentCulture )}"; - ImGui.TextUnformatted( "IPC Test Successful" ); - } + private readonly DalamudPluginInterface _pi; - private void AddedClick( MouseButton button, ChangedItemType type, uint id ) - { - _lastClicked = $"{button}-click on {type} {id} at {DateTime.UtcNow.ToLocalTime().ToString( CultureInfo.CurrentCulture )}"; - } + private string _characterName = string.Empty; - private string _characterCollectionName = string.Empty; - private IList< (string, string) > _mods = new List< (string, string) >(); - private IList< string > _collections = new List< string >(); - private bool _collectionMode = false; + public Meta( DalamudPluginInterface pi ) + => _pi = pi; - private void DrawData() - { - using var _ = ImRaii.TreeNode( "Data IPC" ); - if( !_ ) + public void Draw() { - return; - } - - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { - return; - } - - DrawIntro( PenumbraIpc.LabelProviderCurrentCollectionName, "Current Collection" ); - ImGui.TextUnformatted( _pi.GetIpcSubscriber< string >( PenumbraIpc.LabelProviderCurrentCollectionName ).InvokeFunc() ); - DrawIntro( PenumbraIpc.LabelProviderDefaultCollectionName, "Default Collection" ); - ImGui.TextUnformatted( _pi.GetIpcSubscriber< string >( PenumbraIpc.LabelProviderDefaultCollectionName ).InvokeFunc() ); - DrawIntro( PenumbraIpc.LabelProviderInterfaceCollectionName, "Interface Collection" ); - ImGui.TextUnformatted( _pi.GetIpcSubscriber< string >( PenumbraIpc.LabelProviderInterfaceCollectionName ).InvokeFunc() ); - DrawIntro( PenumbraIpc.LabelProviderCharacterCollectionName, "Character" ); - ImGui.SetNextItemWidth( 200 * ImGuiHelpers.GlobalScale ); - ImGui.InputTextWithHint( "##characterCollectionName", "Character Name...", ref _characterCollectionName, 64 ); - var (c, s) = _pi.GetIpcSubscriber< string, (string, bool) >( PenumbraIpc.LabelProviderCharacterCollectionName ) - .InvokeFunc( _characterCollectionName ); - ImGui.SameLine(); - ImGui.TextUnformatted( $"{c}, {( s ? "Custom" : "Default" )}" ); - - DrawIntro( PenumbraIpc.LabelProviderGetCollections, "Collections" ); - if( ImGui.Button( "Get##Collections" ) ) - { - _collectionMode = true; - _collections = _pi.GetIpcSubscriber< IList< string > >( PenumbraIpc.LabelProviderGetCollections ).InvokeFunc(); - ImGui.OpenPopup( "Ipc Data" ); - } - - DrawIntro( PenumbraIpc.LabelProviderGetMods, "Mods" ); - if( ImGui.Button( "Get##Mods" ) ) - { - _collectionMode = false; - _mods = _pi.GetIpcSubscriber< IList< (string, string) > >( PenumbraIpc.LabelProviderGetMods ).InvokeFunc(); - ImGui.OpenPopup( "Ipc Data" ); - } - - DrawIntro( PenumbraIpc.LabelProviderGetMetaManipulations, "Meta Manipulations" ); - if( ImGui.Button( "Copy to Clipboard" ) ) - { - var base64 = _pi.GetIpcSubscriber< string, string >( PenumbraIpc.LabelProviderGetMetaManipulations ) - .InvokeFunc( _characterCollectionName ); - ImGui.SetClipboardText( base64 ); - } - - ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); - using var p = ImRaii.Popup( "Ipc Data" ); - if( p ) - { - if( _collectionMode ) + using var _ = ImRaii.TreeNode( "Meta" ); + if( !_ ) { - foreach( var collection in _collections ) - { - ImGui.TextUnformatted( collection ); - } - } - else - { - foreach( var (modDir, modName) in _mods ) - { - ImGui.TextUnformatted( $"{modDir}: {modName}" ); - } + return; } - if( ImGui.Button( "Close", -Vector2.UnitX ) || ImGui.IsWindowFocused() ) + ImGui.InputTextWithHint( "##characterName", "Character Name...", ref _characterName, 64 ); + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + if( !table ) + { + return; + } + + DrawIntro( Ipc.GetMetaManipulations.Label, "Meta Manipulations" ); + if( ImGui.Button( "Copy to Clipboard" ) ) + { + var base64 = Ipc.GetMetaManipulations.Subscriber( _pi ).Invoke( _characterName ); + ImGui.SetClipboardText( base64 ); + } + + DrawIntro( Ipc.GetPlayerMetaManipulations.Label, "Player Meta Manipulations" ); + if( ImGui.Button( "Copy to Clipboard##Player" ) ) + { + var base64 = Ipc.GetPlayerMetaManipulations.Subscriber( _pi ).Invoke(); + ImGui.SetClipboardText( base64 ); + } + } + } + + private class Mods + { + private readonly DalamudPluginInterface _pi; + + private string _modDirectory = string.Empty; + private string _modName = string.Empty; + private string _pathInput = string.Empty; + private PenumbraApiEc _lastReloadEc; + private PenumbraApiEc _lastAddEc; + private PenumbraApiEc _lastDeleteEc; + private PenumbraApiEc _lastSetPathEc; + private IList< (string, string) > _mods = new List< (string, string) >(); + + public Mods( DalamudPluginInterface pi ) + => _pi = pi; + + public void Draw() + { + using var _ = ImRaii.TreeNode( "Mods" ); + if( !_ ) + { + return; + } + + ImGui.InputTextWithHint( "##modDir", "Mod Directory Name...", ref _modDirectory, 100 ); + ImGui.InputTextWithHint( "##modName", "Mod Name...", ref _modName, 100 ); + ImGui.InputTextWithHint( "##path", "New Path...", ref _pathInput, 100 ); + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + if( !table ) + { + return; + } + + DrawIntro( Ipc.GetMods.Label, "Mods" ); + if( ImGui.Button( "Get##Mods" ) ) + { + _mods = Ipc.GetMods.Subscriber( _pi ).Invoke(); + ImGui.OpenPopup( "Mods" ); + } + + DrawIntro( Ipc.ReloadMod.Label, "Reload Mod" ); + if( ImGui.Button( "Reload" ) ) + { + _lastReloadEc = Ipc.ReloadMod.Subscriber( _pi ).Invoke( _modDirectory, _modName ); + } + + ImGui.SameLine(); + ImGui.TextUnformatted( _lastReloadEc.ToString() ); + + DrawIntro( Ipc.AddMod.Label, "Add Mod" ); + if( ImGui.Button( "Add" ) ) + { + _lastAddEc = Ipc.AddMod.Subscriber( _pi ).Invoke( _modDirectory ); + } + + ImGui.SameLine(); + ImGui.TextUnformatted( _lastAddEc.ToString() ); + + DrawIntro( Ipc.DeleteMod.Label, "Delete Mod" ); + if( ImGui.Button( "Delete" ) ) + { + _lastDeleteEc = Ipc.DeleteMod.Subscriber( _pi ).Invoke( _modDirectory, _modName ); + } + + ImGui.SameLine(); + ImGui.TextUnformatted( _lastDeleteEc.ToString() ); + + DrawIntro( Ipc.GetModPath.Label, "Current Path" ); + var (ec, path, def) = Ipc.GetModPath.Subscriber( _pi ).Invoke( _modDirectory, _modName ); + ImGui.TextUnformatted( $"{path} ({( def ? "Custom" : "Default" )}) [{ec}]" ); + + DrawIntro( Ipc.SetModPath.Label, "Set Path" ); + if( ImGui.Button( "Set" ) ) + { + _lastSetPathEc = Ipc.SetModPath.Subscriber( _pi ).Invoke( _modDirectory, _modName, _pathInput ); + } + + ImGui.SameLine(); + ImGui.TextUnformatted( _lastSetPathEc.ToString() ); + + + DrawModsPopup(); + } + + private void DrawModsPopup() + { + ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); + using var p = ImRaii.Popup( "Mods" ); + if( !p ) + { + return; + } + + foreach( var (modDir, modName) in _mods ) + { + ImGui.TextUnformatted( $"{modDir}: {modName}" ); + } + + if( ImGui.Button( "Close", -Vector2.UnitX ) || !ImGui.IsWindowFocused() ) { ImGui.CloseCurrentPopup(); } } } - private string _settingsModDirectory = string.Empty; - private string _settingsModName = string.Empty; - private string _settingsCollection = string.Empty; - private bool _settingsAllowInheritance = true; - private bool _settingsInherit = false; - private bool _settingsEnabled = false; - private int _settingsPriority = 0; - private IDictionary< string, (IList< string >, SelectType) >? _availableSettings; - private IDictionary< string, IList< string > >? _currentSettings = null; - private PenumbraApiEc _lastSettingsError = PenumbraApiEc.Success; - private ModSettingChange _lastSettingChangeType; - private string _lastSettingChangeCollection = string.Empty; - private string _lastSettingChangeMod = string.Empty; - private bool _lastSettingChangeInherited; - private DateTimeOffset _lastSettingChange; - private PenumbraApiEc _lastReloadEc = PenumbraApiEc.Success; - - - private void UpdateLastModSetting( ModSettingChange type, string collection, string mod, bool inherited ) + private class ModSettings { - _lastSettingChangeType = type; - _lastSettingChangeCollection = collection; - _lastSettingChangeMod = mod; - _lastSettingChangeInherited = inherited; - _lastSettingChange = DateTimeOffset.Now; - } + private readonly DalamudPluginInterface _pi; + public readonly EventSubscriber< ModSettingChange, string, string, bool > SettingChanged; - private void DrawSetting() - { - using var _ = ImRaii.TreeNode( "Settings IPC" ); - if( !_ ) + private PenumbraApiEc _lastSettingsError = PenumbraApiEc.Success; + private ModSettingChange _lastSettingChangeType; + private string _lastSettingChangeCollection = string.Empty; + private string _lastSettingChangeMod = string.Empty; + private bool _lastSettingChangeInherited; + private DateTimeOffset _lastSettingChange; + + private string _settingsModDirectory = string.Empty; + private string _settingsModName = string.Empty; + private string _settingsCollection = string.Empty; + private bool _settingsAllowInheritance = true; + private bool _settingsInherit = false; + private bool _settingsEnabled = false; + private int _settingsPriority = 0; + private IDictionary< string, (IList< string >, GroupType) >? _availableSettings; + private IDictionary< string, IList< string > >? _currentSettings = null; + + public ModSettings( DalamudPluginInterface pi ) { - return; + _pi = pi; + SettingChanged = Ipc.ModSettingChanged.Subscriber( pi, UpdateLastModSetting ); } - ImGui.InputTextWithHint( "##settingsDir", "Mod Directory Name...", ref _settingsModDirectory, 100 ); - ImGui.InputTextWithHint( "##settingsName", "Mod Name...", ref _settingsModName, 100 ); - ImGui.InputTextWithHint( "##settingsCollection", "Collection...", ref _settingsCollection, 100 ); - ImGui.Checkbox( "Allow Inheritance", ref _settingsAllowInheritance ); - - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) + public void Draw() { - return; - } - - DrawIntro( "Last Error", _lastSettingsError.ToString() ); - DrawIntro( PenumbraIpc.LabelProviderModSettingChanged, "Last Mod Setting Changed" ); - ImGui.TextUnformatted( _lastSettingChangeMod.Length > 0 - ? $"{_lastSettingChangeType} of {_lastSettingChangeMod} in {_lastSettingChangeCollection}{( _lastSettingChangeInherited ? " (Inherited)" : string.Empty )} at {_lastSettingChange}" - : "None" ); - DrawIntro( PenumbraIpc.LabelProviderGetAvailableModSettings, "Get Available Settings" ); - if( ImGui.Button( "Get##Available" ) ) - { - _availableSettings = _pi - .GetIpcSubscriber< string, string, IDictionary< string, (IList< string >, SelectType) >? >( - PenumbraIpc.LabelProviderGetAvailableModSettings ).InvokeFunc( _settingsModDirectory, _settingsModName ); - _lastSettingsError = _availableSettings == null ? PenumbraApiEc.ModMissing : PenumbraApiEc.Success; - } - - DrawIntro( PenumbraIpc.LabelProviderReloadMod, "Reload Mod" ); - if( ImGui.Button( "Reload" ) ) - { - _lastReloadEc = _pi.GetIpcSubscriber< string, string, PenumbraApiEc >( PenumbraIpc.LabelProviderReloadMod ) - .InvokeFunc( _settingsModDirectory, _settingsModName ); - } - - ImGui.SameLine(); - ImGui.TextUnformatted( _lastReloadEc.ToString() ); - - DrawIntro( PenumbraIpc.LabelProviderGetCurrentModSettings, "Get Current Settings" ); - if( ImGui.Button( "Get##Current" ) ) - { - var ret = _pi - .GetIpcSubscriber< string, string, string, bool, (PenumbraApiEc, (bool, int, IDictionary< string, IList< string > >, bool)?) >( - PenumbraIpc.LabelProviderGetCurrentModSettings ).InvokeFunc( _settingsCollection, _settingsModDirectory, _settingsModName, - _settingsAllowInheritance ); - _lastSettingsError = ret.Item1; - if( ret.Item1 == PenumbraApiEc.Success ) + using var _ = ImRaii.TreeNode( "Mod Settings" ); + if( !_ ) { - _settingsEnabled = ret.Item2?.Item1 ?? false; - _settingsInherit = ret.Item2?.Item4 ?? false; - _settingsPriority = ret.Item2?.Item2 ?? 0; - _currentSettings = ret.Item2?.Item3; + return; } - else + + ImGui.InputTextWithHint( "##settingsDir", "Mod Directory Name...", ref _settingsModDirectory, 100 ); + ImGui.InputTextWithHint( "##settingsName", "Mod Name...", ref _settingsModName, 100 ); + ImGui.InputTextWithHint( "##settingsCollection", "Collection...", ref _settingsCollection, 100 ); + ImGui.Checkbox( "Allow Inheritance", ref _settingsAllowInheritance ); + + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + if( !table ) { - _currentSettings = null; + return; } - } - DrawIntro( PenumbraIpc.LabelProviderTryInheritMod, "Inherit Mod" ); - ImGui.Checkbox( "##inherit", ref _settingsInherit ); - ImGui.SameLine(); - if( ImGui.Button( "Set##Inherit" ) ) - { - _lastSettingsError = _pi.GetIpcSubscriber< string, string, string, bool, PenumbraApiEc >( PenumbraIpc.LabelProviderTryInheritMod ) - .InvokeFunc( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsInherit ); - } + DrawIntro( "Last Error", _lastSettingsError.ToString() ); + DrawIntro( Ipc.ModSettingChanged.Label, "Last Mod Setting Changed" ); + ImGui.TextUnformatted( _lastSettingChangeMod.Length > 0 + ? $"{_lastSettingChangeType} of {_lastSettingChangeMod} in {_lastSettingChangeCollection}{( _lastSettingChangeInherited ? " (Inherited)" : string.Empty )} at {_lastSettingChange}" + : "None" ); + DrawIntro( Ipc.GetAvailableModSettings.Label, "Get Available Settings" ); + if( ImGui.Button( "Get##Available" ) ) + { + _availableSettings = Ipc.GetAvailableModSettings.Subscriber( _pi ).Invoke( _settingsModDirectory, _settingsModName ); + _lastSettingsError = _availableSettings == null ? PenumbraApiEc.ModMissing : PenumbraApiEc.Success; + } - DrawIntro( PenumbraIpc.LabelProviderTrySetMod, "Set Enabled" ); - ImGui.Checkbox( "##enabled", ref _settingsEnabled ); - ImGui.SameLine(); - if( ImGui.Button( "Set##Enabled" ) ) - { - _lastSettingsError = _pi.GetIpcSubscriber< string, string, string, bool, PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetMod ) - .InvokeFunc( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsEnabled ); - } - DrawIntro( PenumbraIpc.LabelProviderTrySetModPriority, "Set Priority" ); - ImGui.SetNextItemWidth( 200 * ImGuiHelpers.GlobalScale ); - ImGui.DragInt( "##Priority", ref _settingsPriority ); - ImGui.SameLine(); - if( ImGui.Button( "Set##Priority" ) ) - { - _lastSettingsError = _pi - .GetIpcSubscriber< string, string, string, int, PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetModPriority ) - .InvokeFunc( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsPriority ); - } + DrawIntro( Ipc.GetCurrentModSettings.Label, "Get Current Settings" ); + if( ImGui.Button( "Get##Current" ) ) + { + var ret = Ipc.GetCurrentModSettings.Subscriber( _pi ).Invoke( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsAllowInheritance ); + _lastSettingsError = ret.Item1; + if( ret.Item1 == PenumbraApiEc.Success ) + { + _settingsEnabled = ret.Item2?.Item1 ?? false; + _settingsInherit = ret.Item2?.Item4 ?? false; + _settingsPriority = ret.Item2?.Item2 ?? 0; + _currentSettings = ret.Item2?.Item3; + } + else + { + _currentSettings = null; + } + } + + DrawIntro( Ipc.TryInheritMod.Label, "Inherit Mod" ); + ImGui.Checkbox( "##inherit", ref _settingsInherit ); + ImGui.SameLine(); + if( ImGui.Button( "Set##Inherit" ) ) + { + _lastSettingsError = Ipc.TryInheritMod.Subscriber( _pi ).Invoke( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsInherit ); + } + + DrawIntro( Ipc.TrySetMod.Label, "Set Enabled" ); + ImGui.Checkbox( "##enabled", ref _settingsEnabled ); + ImGui.SameLine(); + if( ImGui.Button( "Set##Enabled" ) ) + { + _lastSettingsError = Ipc.TrySetMod.Subscriber( _pi ).Invoke( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsEnabled ); + } + + DrawIntro( Ipc.TrySetModPriority.Label, "Set Priority" ); + ImGui.SetNextItemWidth( 200 * ImGuiHelpers.GlobalScale ); + ImGui.DragInt( "##Priority", ref _settingsPriority ); + ImGui.SameLine(); + if( ImGui.Button( "Set##Priority" ) ) + { + _lastSettingsError = Ipc.TrySetModPriority.Subscriber( _pi ).Invoke( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsPriority ); + } + + DrawIntro( Ipc.TrySetModSetting.Label, "Set Setting(s)" ); + if( _availableSettings == null ) + { + return; + } - DrawIntro( PenumbraIpc.LabelProviderTrySetModSetting, "Set Setting(s)" ); - if( _availableSettings != null ) - { foreach( var (group, (list, type)) in _availableSettings ) { using var id = ImRaii.PushId( group ); @@ -802,19 +1026,15 @@ public class IpcTester : IDisposable ImGui.SameLine(); if( ImGui.Button( "Set##setting" ) ) { - if( type == SelectType.Single ) + if( type == GroupType.Single ) { - _lastSettingsError = _pi - .GetIpcSubscriber< string, string, string, string, string, - PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetModSetting ).InvokeFunc( _settingsCollection, - _settingsModDirectory, _settingsModName, group, current.Count > 0 ? current[ 0 ] : string.Empty ); + _lastSettingsError = Ipc.TrySetModSetting.Subscriber( _pi ).Invoke( _settingsCollection, + _settingsModDirectory, _settingsModName, group, current.Count > 0 ? current[ 0 ] : string.Empty ); } else { - _lastSettingsError = _pi - .GetIpcSubscriber< string, string, string, string, IReadOnlyList< string >, - PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetModSettings ).InvokeFunc( _settingsCollection, - _settingsModDirectory, _settingsModName, group, current.ToArray() ); + _lastSettingsError = Ipc.TrySetModSettings.Subscriber( _pi ).Invoke( _settingsCollection, + _settingsModDirectory, _settingsModName, group, current.ToArray() ); } } @@ -822,176 +1042,183 @@ public class IpcTester : IDisposable ImGui.TextUnformatted( group ); } } + + private void UpdateLastModSetting( ModSettingChange type, string collection, string mod, bool inherited ) + { + _lastSettingChangeType = type; + _lastSettingChangeCollection = collection; + _lastSettingChangeMod = mod; + _lastSettingChangeInherited = inherited; + _lastSettingChange = DateTimeOffset.Now; + } } - private string _tempCollectionName = string.Empty; - private string _tempCharacterName = string.Empty; - private bool _forceOverwrite = true; - private string _tempModName = string.Empty; - private PenumbraApiEc _lastTempError = PenumbraApiEc.Success; - private string _lastCreatedCollectionName = string.Empty; - private string _tempGamePath = "test/game/path.mtrl"; - private string _tempFilePath = "test/success.mtrl"; - private string _tempManipulation = string.Empty; - - - private void DrawTemp() + private class Temporary { - using var _ = ImRaii.TreeNode( "Temp IPC" ); - if( !_ ) - { - return; - } + public readonly DalamudPluginInterface _pi; - ImGui.InputTextWithHint( "##tempCollection", "Collection Name...", ref _tempCollectionName, 128 ); - ImGui.InputTextWithHint( "##tempCollectionChar", "Collection Character...", ref _tempCharacterName, 32 ); - ImGui.InputTextWithHint( "##tempMod", "Temporary Mod Name...", ref _tempModName, 32 ); - ImGui.InputTextWithHint( "##tempGame", "Game Path...", ref _tempGamePath, 256 ); - ImGui.InputTextWithHint( "##tempFile", "File Path...", ref _tempFilePath, 256 ); - ImGui.InputTextWithHint( "##tempManip", "Manipulation Base64 String...", ref _tempManipulation, 256 ); - ImGui.Checkbox( "Force Character Collection Overwrite", ref _forceOverwrite ); + public Temporary( DalamudPluginInterface pi ) + => _pi = pi; - using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); - if( !table ) - { - return; - } + public string LastCreatedCollectionName = string.Empty; - DrawIntro( "Last Error", _lastTempError.ToString() ); - DrawIntro( "Last Created Collection", _lastCreatedCollectionName ); - DrawIntro( PenumbraIpc.LabelProviderCreateTemporaryCollection, "Create Temporary Collection" ); - if( ImGui.Button( "Create##Collection" ) ) - { - ( _lastTempError, _lastCreatedCollectionName ) = - _pi.GetIpcSubscriber< string, string, bool, (PenumbraApiEc, string) >( PenumbraIpc.LabelProviderCreateTemporaryCollection ) - .InvokeFunc( _tempCollectionName, _tempCharacterName, _forceOverwrite ); - } + private string _tempCollectionName = string.Empty; + private string _tempCharacterName = string.Empty; + private string _tempModName = string.Empty; + private string _tempGamePath = "test/game/path.mtrl"; + private string _tempFilePath = "test/success.mtrl"; + private string _tempManipulation = string.Empty; + private PenumbraApiEc _lastTempError; + private bool _forceOverwrite; - DrawIntro( PenumbraIpc.LabelProviderRemoveTemporaryCollection, "Remove Temporary Collection from Character" ); - if( ImGui.Button( "Delete##Collection" ) ) + public void Draw() { - _lastTempError = _pi.GetIpcSubscriber< string, PenumbraApiEc >( PenumbraIpc.LabelProviderRemoveTemporaryCollection ) - .InvokeFunc( _tempCharacterName ); - } + using var _ = ImRaii.TreeNode( "Temporary" ); + if( !_ ) + { + return; + } - DrawIntro( PenumbraIpc.LabelProviderAddTemporaryMod, "Add Temporary Mod to specific Collection" ); - if( ImGui.Button( "Add##Mod" ) ) - { - _lastTempError = _pi - .GetIpcSubscriber< string, string, Dictionary< string, string >, string, int, PenumbraApiEc >( - PenumbraIpc.LabelProviderAddTemporaryMod ) - .InvokeFunc( _tempModName, _tempCollectionName, + ImGui.InputTextWithHint( "##tempCollection", "Collection Name...", ref _tempCollectionName, 128 ); + ImGui.InputTextWithHint( "##tempCollectionChar", "Collection Character...", ref _tempCharacterName, 32 ); + ImGui.InputTextWithHint( "##tempMod", "Temporary Mod Name...", ref _tempModName, 32 ); + ImGui.InputTextWithHint( "##tempGame", "Game Path...", ref _tempGamePath, 256 ); + ImGui.InputTextWithHint( "##tempFile", "File Path...", ref _tempFilePath, 256 ); + ImGui.InputTextWithHint( "##tempManip", "Manipulation Base64 String...", ref _tempManipulation, 256 ); + ImGui.Checkbox( "Force Character Collection Overwrite", ref _forceOverwrite ); + + + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + if( !table ) + { + return; + } + + DrawIntro( "Last Error", _lastTempError.ToString() ); + DrawIntro( "Last Created Collection", LastCreatedCollectionName ); + DrawIntro( Ipc.CreateTemporaryCollection.Label, "Create Temporary Collection" ); + if( ImGui.Button( "Create##Collection" ) ) + { + ( _lastTempError, LastCreatedCollectionName ) = Ipc.CreateTemporaryCollection.Subscriber( _pi ).Invoke( _tempCollectionName, _tempCharacterName, _forceOverwrite ); + } + + DrawIntro( Ipc.RemoveTemporaryCollection.Label, "Remove Temporary Collection from Character" ); + if( ImGui.Button( "Delete##Collection" ) ) + { + _lastTempError = Ipc.RemoveTemporaryCollection.Subscriber( _pi ).Invoke( _tempCharacterName ); + } + + DrawIntro( Ipc.AddTemporaryMod.Label, "Add Temporary Mod to specific Collection" ); + if( ImGui.Button( "Add##Mod" ) ) + { + _lastTempError = Ipc.AddTemporaryMod.Subscriber( _pi ).Invoke( _tempModName, _tempCollectionName, new Dictionary< string, string > { { _tempGamePath, _tempFilePath } }, _tempManipulation.Length > 0 ? _tempManipulation : string.Empty, int.MaxValue ); - } + } - DrawIntro( PenumbraIpc.LabelProviderAddTemporaryModAll, "Add Temporary Mod to all Collections" ); - if( ImGui.Button( "Add##All" ) ) - { - _lastTempError = _pi - .GetIpcSubscriber< string, Dictionary< string, string >, string, int, PenumbraApiEc >( - PenumbraIpc.LabelProviderAddTemporaryModAll ) - .InvokeFunc( _tempModName, new Dictionary< string, string > { { _tempGamePath, _tempFilePath } }, + DrawIntro( Ipc.AddTemporaryModAll.Label, "Add Temporary Mod to all Collections" ); + if( ImGui.Button( "Add##All" ) ) + { + _lastTempError = Ipc.AddTemporaryModAll.Subscriber( _pi ).Invoke( _tempModName, new Dictionary< string, string > { { _tempGamePath, _tempFilePath } }, _tempManipulation.Length > 0 ? _tempManipulation : string.Empty, int.MaxValue ); - } + } - DrawIntro( PenumbraIpc.LabelProviderRemoveTemporaryMod, "Remove Temporary Mod from specific Collection" ); - if( ImGui.Button( "Remove##Mod" ) ) - { - _lastTempError = _pi.GetIpcSubscriber< string, string, int, PenumbraApiEc >( PenumbraIpc.LabelProviderRemoveTemporaryMod ) - .InvokeFunc( _tempModName, _tempCollectionName, int.MaxValue ); - } - - DrawIntro( PenumbraIpc.LabelProviderRemoveTemporaryModAll, "Remove Temporary Mod from all Collections" ); - if( ImGui.Button( "Remove##ModAll" ) ) - { - _lastTempError = _pi.GetIpcSubscriber< string, int, PenumbraApiEc >( PenumbraIpc.LabelProviderRemoveTemporaryModAll ) - .InvokeFunc( _tempModName, int.MaxValue ); - } - } - - private void DrawTempCollections() - { - using var collTree = ImRaii.TreeNode( "Collections" ); - if( !collTree ) - { - return; - } - - using var table = ImRaii.Table( "##collTree", 5 ); - if( !table ) - { - return; - } - - foreach( var (character, collection) in Penumbra.TempMods.Collections ) - { - ImGui.TableNextColumn(); - ImGui.TextUnformatted( character ); - ImGui.TableNextColumn(); - ImGui.TextUnformatted( collection.Name ); - ImGui.TableNextColumn(); - ImGui.TextUnformatted( collection.ResolvedFiles.Count.ToString() ); - ImGui.TableNextColumn(); - ImGui.TextUnformatted( collection.MetaCache?.Count.ToString() ?? "0" ); - ImGui.TableNextColumn(); - if( ImGui.Button( $"Save##{character}" ) ) + DrawIntro( Ipc.RemoveTemporaryMod.Label, "Remove Temporary Mod from specific Collection" ); + if( ImGui.Button( "Remove##Mod" ) ) { - Mod.TemporaryMod.SaveTempCollection( collection, character ); + _lastTempError = Ipc.RemoveTemporaryMod.Subscriber( _pi ).Invoke( _tempModName, _tempCollectionName, int.MaxValue ); + } + + DrawIntro( Ipc.RemoveTemporaryModAll.Label, "Remove Temporary Mod from all Collections" ); + if( ImGui.Button( "Remove##ModAll" ) ) + { + _lastTempError = Ipc.RemoveTemporaryModAll.Subscriber( _pi ).Invoke( _tempModName, int.MaxValue ); } } - } - private void DrawTempMods() - { - using var modTree = ImRaii.TreeNode( "Mods" ); - if( !modTree ) + public void DrawCollections() { - return; - } + using var collTree = ImRaii.TreeNode( "Collections" ); + if( !collTree ) + { + return; + } - using var table = ImRaii.Table( "##modTree", 5 ); + using var table = ImRaii.Table( "##collTree", 5 ); + if( !table ) + { + return; + } - void PrintList( string collectionName, IReadOnlyList< Mod.TemporaryMod > list ) - { - foreach( var mod in list ) + foreach( var (character, collection) in Penumbra.TempMods.Collections ) { ImGui.TableNextColumn(); - ImGui.TextUnformatted( mod.Name ); + ImGui.TextUnformatted( character ); ImGui.TableNextColumn(); - ImGui.TextUnformatted( mod.Priority.ToString() ); + ImGui.TextUnformatted( collection.Name ); ImGui.TableNextColumn(); - ImGui.TextUnformatted( collectionName ); + ImGui.TextUnformatted( collection.ResolvedFiles.Count.ToString() ); ImGui.TableNextColumn(); - ImGui.TextUnformatted( mod.Default.Files.Count.ToString() ); - if( ImGui.IsItemHovered() ) + ImGui.TextUnformatted( collection.MetaCache?.Count.ToString() ?? "0" ); + ImGui.TableNextColumn(); + if( ImGui.Button( $"Save##{character}" ) ) { - using var tt = ImRaii.Tooltip(); - foreach( var (path, file) in mod.Default.Files ) - { - ImGui.TextUnformatted( $"{path} -> {file}" ); - } - } - - ImGui.TableNextColumn(); - ImGui.TextUnformatted( mod.TotalManipulations.ToString() ); - if( ImGui.IsItemHovered() ) - { - using var tt = ImRaii.Tooltip(); - foreach( var manip in mod.Default.Manipulations ) - { - ImGui.TextUnformatted( manip.ToString() ); - } + Mod.TemporaryMod.SaveTempCollection( collection, character ); } } } - if( table ) + public void DrawMods() { - PrintList( "All", Penumbra.TempMods.ModsForAllCollections ); - foreach( var (collection, list) in Penumbra.TempMods.Mods ) + using var modTree = ImRaii.TreeNode( "Mods" ); + if( !modTree ) { - PrintList( collection.Name, list ); + return; + } + + using var table = ImRaii.Table( "##modTree", 5 ); + + void PrintList( string collectionName, IReadOnlyList< Mod.TemporaryMod > list ) + { + foreach( var mod in list ) + { + ImGui.TableNextColumn(); + ImGui.TextUnformatted( mod.Name ); + ImGui.TableNextColumn(); + ImGui.TextUnformatted( mod.Priority.ToString() ); + ImGui.TableNextColumn(); + ImGui.TextUnformatted( collectionName ); + ImGui.TableNextColumn(); + ImGui.TextUnformatted( mod.Default.Files.Count.ToString() ); + if( ImGui.IsItemHovered() ) + { + using var tt = ImRaii.Tooltip(); + foreach( var (path, file) in mod.Default.Files ) + { + ImGui.TextUnformatted( $"{path} -> {file}" ); + } + } + + ImGui.TableNextColumn(); + ImGui.TextUnformatted( mod.TotalManipulations.ToString() ); + if( ImGui.IsItemHovered() ) + { + using var tt = ImRaii.Tooltip(); + foreach( var manip in mod.Default.Manipulations ) + { + ImGui.TextUnformatted( manip.ToString() ); + } + } + } + } + + if( table ) + { + PrintList( "All", Penumbra.TempMods.ModsForAllCollections ); + foreach( var (collection, list) in Penumbra.TempMods.Mods ) + { + PrintList( collection.Name, list ); + } } } } diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index fad36b1c..2da860f2 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -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 ) diff --git a/Penumbra/Api/PenumbraIpc.cs b/Penumbra/Api/PenumbraIpc.cs deleted file mode 100644 index ab9496c5..00000000 --- a/Penumbra/Api/PenumbraIpc.cs +++ /dev/null @@ -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( 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(); - } -} \ No newline at end of file diff --git a/Penumbra/Api/PenumbraIpcProviders.cs b/Penumbra/Api/PenumbraIpcProviders.cs new file mode 100644 index 00000000..26a982ce --- /dev/null +++ b/Penumbra/Api/PenumbraIpcProviders.cs @@ -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 ); +} \ No newline at end of file diff --git a/Penumbra/Api/RedrawController.cs b/Penumbra/Api/RedrawController.cs index 9aef89b7..ad37c587 100644 --- a/Penumbra/Api/RedrawController.cs +++ b/Penumbra/Api/RedrawController.cs @@ -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; diff --git a/Penumbra/Collections/ModCollection.Cache.cs b/Penumbra/Collections/ModCollection.Cache.cs index d2bb2d92..2d2262cc 100644 --- a/Penumbra/Collections/ModCollection.Cache.cs +++ b/Penumbra/Collections/ModCollection.Cache.cs @@ -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 ) diff --git a/Penumbra/Collections/ModCollection.Changes.cs b/Penumbra/Collections/ModCollection.Changes.cs index 32826dc8..fead3281 100644 --- a/Penumbra/Collections/ModCollection.Changes.cs +++ b/Penumbra/Collections/ModCollection.Changes.cs @@ -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. diff --git a/Penumbra/Collections/ModCollection.Inheritance.cs b/Penumbra/Collections/ModCollection.Inheritance.cs index 16415a09..44403758 100644 --- a/Penumbra/Collections/ModCollection.Inheritance.cs +++ b/Penumbra/Collections/ModCollection.Inheritance.cs @@ -3,6 +3,7 @@ using Penumbra.Mods; using System; using System.Collections.Generic; using System.Linq; +using Penumbra.Api.Enums; namespace Penumbra.Collections; diff --git a/Penumbra/Import/TexToolsImporter.ModPack.cs b/Penumbra/Import/TexToolsImporter.ModPack.cs index 0edef44e..ca8eb492 100644 --- a/Penumbra/Import/TexToolsImporter.ModPack.cs +++ b/Penumbra/Import/TexToolsImporter.ModPack.cs @@ -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 ) diff --git a/Penumbra/Import/TexToolsStructs.cs b/Penumbra/Import/TexToolsStructs.cs index 51b73b90..bc85893c 100644 --- a/Penumbra/Import/TexToolsStructs.cs +++ b/Penumbra/Import/TexToolsStructs.cs @@ -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; } diff --git a/Penumbra/Interop/ObjectReloader.cs b/Penumbra/Interop/ObjectReloader.cs index a04801b9..4fecf454 100644 --- a/Penumbra/Interop/ObjectReloader.cs +++ b/Penumbra/Interop/ObjectReloader.cs @@ -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; diff --git a/Penumbra/Interop/Resolver/PathResolver.DrawObjectState.cs b/Penumbra/Interop/Resolver/PathResolver.DrawObjectState.cs index 40228fad..aa6e7ed4 100644 --- a/Penumbra/Interop/Resolver/PathResolver.DrawObjectState.cs +++ b/Penumbra/Interop/Resolver/PathResolver.DrawObjectState.cs @@ -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; diff --git a/Penumbra/Mods/Manager/Mod.Manager.Options.cs b/Penumbra/Mods/Manager/Mod.Manager.Options.cs index 3e248c0e..704d1285 100644 --- a/Penumbra/Mods/Manager/Mod.Manager.Options.cs +++ b/Penumbra/Mods/Manager/Mod.Manager.Options.cs @@ -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 ); diff --git a/Penumbra/Mods/Mod.Creation.cs b/Penumbra/Mods/Mod.Creation.cs index 184dc8cb..699fceaa 100644 --- a/Penumbra/Mods/Mod.Creation.cs +++ b/Penumbra/Mods/Mod.Creation.cs @@ -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() { diff --git a/Penumbra/Mods/Mod.Files.cs b/Penumbra/Mods/Mod.Files.cs index c9c8683b..3f6a79b4 100644 --- a/Penumbra/Mods/Mod.Files.cs +++ b/Penumbra/Mods/Mod.Files.cs @@ -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 ) diff --git a/Penumbra/Mods/Mod.Meta.Migration.cs b/Penumbra/Mods/Mod.Meta.Migration.cs index f6bba3a3..69e33628 100644 --- a/Penumbra/Mods/Mod.Meta.Migration.cs +++ b/Penumbra/Mods/Mod.Meta.Migration.cs @@ -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(); diff --git a/Penumbra/Mods/ModFileSystem.cs b/Penumbra/Mods/ModFileSystem.cs index 1060504c..c8cbdf22 100644 --- a/Penumbra/Mods/ModFileSystem.cs +++ b/Penumbra/Mods/ModFileSystem.cs @@ -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 ); } \ No newline at end of file diff --git a/Penumbra/Mods/Subclasses/IModGroup.cs b/Penumbra/Mods/Subclasses/IModGroup.cs index fb2d305a..0c1ebf2f 100644 --- a/Penumbra/Mods/Subclasses/IModGroup.cs +++ b/Penumbra/Mods/Subclasses/IModGroup.cs @@ -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 ); } \ No newline at end of file diff --git a/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs b/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs index 91bc304b..fe7b1173 100644 --- a/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs +++ b/Penumbra/Mods/Subclasses/Mod.Files.MultiModGroup.cs @@ -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, diff --git a/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs b/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs index 3de02d5c..cfec230e 100644 --- a/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs +++ b/Penumbra/Mods/Subclasses/Mod.Files.SingleModGroup.cs @@ -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, diff --git a/Penumbra/Mods/Subclasses/ModSettings.cs b/Penumbra/Mods/Subclasses/ModSettings.cs index 6e8c7343..7b2a23ab 100644 --- a/Penumbra/Mods/Subclasses/ModSettings.cs +++ b/Penumbra/Mods/Subclasses/ModSettings.cs @@ -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 } ); } diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index 3c784c66..cd08a20b 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -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(); diff --git a/Penumbra/Penumbra.csproj b/Penumbra/Penumbra.csproj index 624558c1..b7ab0231 100644 --- a/Penumbra/Penumbra.csproj +++ b/Penumbra/Penumbra.csproj @@ -75,6 +75,7 @@ + diff --git a/Penumbra/UI/Classes/ModFileSystemSelector.cs b/Penumbra/UI/Classes/ModFileSystemSelector.cs index 77c7dea1..1ccf0e3c 100644 --- a/Penumbra/UI/Classes/ModFileSystemSelector.cs +++ b/Penumbra/UI/Classes/ModFileSystemSelector.cs @@ -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; diff --git a/Penumbra/UI/ConfigWindow.DebugTab.cs b/Penumbra/UI/ConfigWindow.DebugTab.cs index 8ced65cb..c9cf6284 100644 --- a/Penumbra/UI/ConfigWindow.DebugTab.cs +++ b/Penumbra/UI/ConfigWindow.DebugTab.cs @@ -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. diff --git a/Penumbra/UI/ConfigWindow.Misc.cs b/Penumbra/UI/ConfigWindow.Misc.cs index 5b302ade..e767f755 100644 --- a/Penumbra/UI/ConfigWindow.Misc.cs +++ b/Penumbra/UI/ConfigWindow.Misc.cs @@ -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; diff --git a/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs b/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs index e3113bcb..e5948ee8 100644 --- a/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs +++ b/Penumbra/UI/ConfigWindow.ModPanel.Edit.cs @@ -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(); diff --git a/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs b/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs index 6c5582b8..feacb0bc 100644 --- a/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs +++ b/Penumbra/UI/ConfigWindow.ModPanel.Settings.cs @@ -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; } diff --git a/Penumbra/UI/ConfigWindow.ModsTab.cs b/Penumbra/UI/ConfigWindow.ModsTab.cs index 35b01d19..ba88c75d 100644 --- a/Penumbra/UI/ConfigWindow.ModsTab.cs +++ b/Penumbra/UI/ConfigWindow.ModsTab.cs @@ -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; diff --git a/Penumbra/packages.lock.json b/Penumbra/packages.lock.json index 3b072b89..b68a4adc 100644 --- a/Penumbra/packages.lock.json +++ b/Penumbra/packages.lock.json @@ -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, )" + } } } } diff --git a/tmp/.editorconfig b/tmp/.editorconfig new file mode 100644 index 00000000..238bb1dc --- /dev/null +++ b/tmp/.editorconfig @@ -0,0 +1,85 @@ + +[*] +charset=utf-8 +end_of_line=lf +trim_trailing_whitespace=true +insert_final_newline=false +indent_style=space +indent_size=4 + +# Microsoft .NET properties +csharp_new_line_before_members_in_object_initializers=false +csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion +csharp_prefer_braces=true:none +csharp_space_after_cast=false +csharp_space_after_keywords_in_control_flow_statements=false +csharp_space_between_method_call_parameter_list_parentheses=true +csharp_space_between_method_declaration_parameter_list_parentheses=true +csharp_space_between_parentheses=control_flow_statements,expressions,type_casts +csharp_style_var_elsewhere=true:suggestion +csharp_style_var_for_built_in_types=true:suggestion +csharp_style_var_when_type_is_apparent=true:suggestion +dotnet_style_parentheses_in_arithmetic_binary_operators=never_if_unnecessary:none +dotnet_style_parentheses_in_other_binary_operators=never_if_unnecessary:none +dotnet_style_parentheses_in_relational_binary_operators=never_if_unnecessary:none +dotnet_style_predefined_type_for_locals_parameters_members=true:suggestion +dotnet_style_predefined_type_for_member_access=true:suggestion +dotnet_style_qualification_for_event=false:suggestion +dotnet_style_qualification_for_field=false:suggestion +dotnet_style_qualification_for_method=false:suggestion +dotnet_style_qualification_for_property=false:suggestion +dotnet_style_require_accessibility_modifiers=for_non_interface_members:suggestion + +# ReSharper properties +resharper_align_multiline_binary_expressions_chain=false +resharper_align_multiline_calls_chain=false +resharper_autodetect_indent_settings=true +resharper_braces_redundant=true +resharper_constructor_or_destructor_body=expression_body +resharper_csharp_empty_block_style=together +resharper_csharp_max_line_length=180 +resharper_csharp_space_within_array_access_brackets=true +resharper_enforce_line_ending_style=true +resharper_int_align_assignments=true +resharper_int_align_comments=true +resharper_int_align_fields=true +resharper_int_align_invocations=false +resharper_int_align_nested_ternary=true +resharper_int_align_properties=false +resharper_int_align_switch_expressions=true +resharper_int_align_switch_sections=true +resharper_int_align_variables=true +resharper_local_function_body=expression_body +resharper_method_or_operator_body=expression_body +resharper_place_attribute_on_same_line=false +resharper_space_after_cast=false +resharper_space_within_checked_parentheses=true +resharper_space_within_default_parentheses=true +resharper_space_within_nameof_parentheses=true +resharper_space_within_single_line_array_initializer_braces=true +resharper_space_within_sizeof_parentheses=true +resharper_space_within_typeof_parentheses=true +resharper_space_within_type_argument_angles=true +resharper_space_within_type_parameter_angles=true +resharper_use_indent_from_vs=false +resharper_wrap_lines=true + +# ReSharper inspection severities +resharper_arrange_redundant_parentheses_highlighting=hint +resharper_arrange_this_qualifier_highlighting=hint +resharper_arrange_type_member_modifiers_highlighting=hint +resharper_arrange_type_modifiers_highlighting=hint +resharper_built_in_type_reference_style_for_member_access_highlighting=hint +resharper_built_in_type_reference_style_highlighting=hint +resharper_redundant_base_qualifier_highlighting=warning +resharper_suggest_var_or_type_built_in_types_highlighting=hint +resharper_suggest_var_or_type_elsewhere_highlighting=hint +resharper_suggest_var_or_type_simple_types_highlighting=hint +resharper_web_config_module_not_resolved_highlighting=warning +resharper_web_config_type_not_resolved_highlighting=warning +resharper_web_config_wrong_module_highlighting=warning + +[*.{appxmanifest,asax,ascx,aspx,build,cg,cginc,compute,cs,cshtml,dtd,hlsl,hlsli,hlslinc,master,nuspec,razor,resw,resx,shader,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}] +indent_style=space +indent_size=4 +tab_width=4 diff --git a/tmp/.gitignore b/tmp/.gitignore new file mode 100644 index 00000000..3e168525 --- /dev/null +++ b/tmp/.gitignore @@ -0,0 +1,3 @@ +bin/ +obj/ +.vs/ \ No newline at end of file diff --git a/tmp/Delegates.cs b/tmp/Delegates.cs new file mode 100644 index 00000000..97726e81 --- /dev/null +++ b/tmp/Delegates.cs @@ -0,0 +1,16 @@ +using System; +using Penumbra.Api.Enums; + +namespace Penumbra.Api; + +// Delegates used by different events. +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, string collectionName, IntPtr modelId, IntPtr customize, + IntPtr equipData ); + +public delegate void CreatedCharacterBaseDelegate( IntPtr gameObject, string collectionName, IntPtr drawObject ); +public delegate void GameObjectResourceResolvedDelegate( IntPtr gameObject, string gamePath, string localPath ); \ No newline at end of file diff --git a/tmp/Enums/ChangedItemType.cs b/tmp/Enums/ChangedItemType.cs new file mode 100644 index 00000000..5cc1b5a7 --- /dev/null +++ b/tmp/Enums/ChangedItemType.cs @@ -0,0 +1,9 @@ +namespace Penumbra.Api.Enums; + +public enum ChangedItemType +{ + None, + Item, + Action, + Customization, +} \ No newline at end of file diff --git a/tmp/Enums/GroupType.cs b/tmp/Enums/GroupType.cs new file mode 100644 index 00000000..65a8ed39 --- /dev/null +++ b/tmp/Enums/GroupType.cs @@ -0,0 +1,7 @@ +namespace Penumbra.Api.Enums; + +public enum GroupType +{ + Single, + Multi, +} \ No newline at end of file diff --git a/tmp/Enums/ModSettingChange.cs b/tmp/Enums/ModSettingChange.cs new file mode 100644 index 00000000..5e556d50 --- /dev/null +++ b/tmp/Enums/ModSettingChange.cs @@ -0,0 +1,12 @@ +namespace Penumbra.Api.Enums; + +// 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. +} \ No newline at end of file diff --git a/tmp/Enums/MouseButton.cs b/tmp/Enums/MouseButton.cs new file mode 100644 index 00000000..2917c0f8 --- /dev/null +++ b/tmp/Enums/MouseButton.cs @@ -0,0 +1,9 @@ +namespace Penumbra.Api.Enums; + +public enum MouseButton +{ + None, + Left, + Right, + Middle, +} \ No newline at end of file diff --git a/tmp/Enums/PenumbraApiEc.cs b/tmp/Enums/PenumbraApiEc.cs new file mode 100644 index 00000000..a37aefa8 --- /dev/null +++ b/tmp/Enums/PenumbraApiEc.cs @@ -0,0 +1,20 @@ +namespace Penumbra.Api.Enums; + +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, + PathRenameFailed = 12, + UnknownError = 255, +} \ No newline at end of file diff --git a/Penumbra.GameData/Enums/RedrawType.cs b/tmp/Enums/RedrawType.cs similarity index 61% rename from Penumbra.GameData/Enums/RedrawType.cs rename to tmp/Enums/RedrawType.cs index 4b698377..0295554f 100644 --- a/Penumbra.GameData/Enums/RedrawType.cs +++ b/tmp/Enums/RedrawType.cs @@ -1,4 +1,4 @@ -namespace Penumbra.GameData.Enums; +namespace Penumbra.Api.Enums; public enum RedrawType { diff --git a/tmp/Helpers/ActionProvider.cs b/tmp/Helpers/ActionProvider.cs new file mode 100644 index 00000000..c070dca8 --- /dev/null +++ b/tmp/Helpers/ActionProvider.cs @@ -0,0 +1,66 @@ +using System; +using Dalamud.Logging; +using Dalamud.Plugin; +using Dalamud.Plugin.Ipc; + +namespace Penumbra.Api.Helpers; + +public sealed class ActionProvider : IDisposable +{ + private ICallGateProvider? _provider; + + public ActionProvider( DalamudPluginInterface pi, string label, Action action ) + { + try + { + _provider = pi.GetIpcProvider( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + + _provider?.RegisterAction( action ); + } + + public void Dispose() + { + _provider?.UnregisterAction(); + _provider = null; + GC.SuppressFinalize( this ); + } + + ~ActionProvider() + => Dispose(); +} + +public sealed class ActionProvider< T1, T2 > : IDisposable +{ + private ICallGateProvider< T1, T2, object? >? _provider; + + public ActionProvider( DalamudPluginInterface pi, string label, Action< T1, T2 > action ) + { + try + { + _provider = pi.GetIpcProvider< T1, T2, object? >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + + _provider?.RegisterAction( action ); + } + + public void Dispose() + { + _provider?.UnregisterAction(); + _provider = null; + GC.SuppressFinalize( this ); + } + + ~ActionProvider() + => Dispose(); +} \ No newline at end of file diff --git a/tmp/Helpers/ActionSubscriber.cs b/tmp/Helpers/ActionSubscriber.cs new file mode 100644 index 00000000..e924e4eb --- /dev/null +++ b/tmp/Helpers/ActionSubscriber.cs @@ -0,0 +1,54 @@ +using System; +using Dalamud.Logging; +using Dalamud.Plugin; +using Dalamud.Plugin.Ipc; + +namespace Penumbra.Api.Helpers; + +public readonly struct ActionSubscriber< T1 > +{ + private readonly ICallGateSubscriber< T1, object? >? _subscriber; + + public bool Valid + => _subscriber != null; + + public ActionSubscriber( DalamudPluginInterface pi, string label ) + { + try + { + _subscriber = pi.GetIpcSubscriber< T1, object? >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public void Invoke( T1 a ) + => _subscriber?.InvokeAction( a ); +} + +public readonly struct ActionSubscriber< T1, T2 > +{ + private readonly ICallGateSubscriber< T1, T2, object? >? _subscriber; + + public bool Valid + => _subscriber != null; + + public ActionSubscriber( DalamudPluginInterface pi, string label ) + { + try + { + _subscriber = pi.GetIpcSubscriber< T1, T2, object? >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public void Invoke( T1 a, T2 b ) + => _subscriber?.InvokeAction( a, b ); +} \ No newline at end of file diff --git a/tmp/Helpers/EventProvider.cs b/tmp/Helpers/EventProvider.cs new file mode 100644 index 00000000..b623d41e --- /dev/null +++ b/tmp/Helpers/EventProvider.cs @@ -0,0 +1,376 @@ +using System; +using Dalamud.Logging; +using Dalamud.Plugin; +using Dalamud.Plugin.Ipc; + +namespace Penumbra.Api.Helpers; + +public sealed class EventProvider : IDisposable +{ + private ICallGateProvider< object? >? _provider; + private Delegate? _unsubscriber; + + public EventProvider( DalamudPluginInterface pi, string label, (Action< Action > Add, Action< Action > Del)? subscribe = null ) + { + _unsubscriber = null; + try + { + _provider = pi.GetIpcProvider< object? >( label ); + subscribe?.Add( Invoke ); + _unsubscriber = subscribe?.Del; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + } + + public EventProvider( DalamudPluginInterface pi, string label, Action add, Action del ) + { + _unsubscriber = null; + try + { + _provider = pi.GetIpcProvider< object? >( label ); + add(); + _unsubscriber = del; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + } + + public void Invoke() + => _provider?.SendMessage(); + + public void Dispose() + { + switch( _unsubscriber ) + { + case Action< Action > a: + a( Invoke ); + break; + case Action b: + b(); + break; + } + + _unsubscriber = null; + _provider = null; + GC.SuppressFinalize( this ); + } + + ~EventProvider() + => Dispose(); +} + +public sealed class EventProvider< T1 > : IDisposable +{ + private ICallGateProvider< T1, object? >? _provider; + private Delegate? _unsubscriber; + + public EventProvider( DalamudPluginInterface pi, string label, (Action< Action< T1 > > Add, Action< Action< T1 > > Del)? subscribe = null ) + { + _unsubscriber = null; + try + { + _provider = pi.GetIpcProvider< T1, object? >( label ); + subscribe?.Add( Invoke ); + _unsubscriber = subscribe?.Del; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + } + + public EventProvider( DalamudPluginInterface pi, string label, Action add, Action del ) + { + _unsubscriber = null; + try + { + _provider = pi.GetIpcProvider< T1, object? >( label ); + add(); + _unsubscriber = del; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + } + + public void Invoke( T1 a ) + => _provider?.SendMessage( a ); + + public void Dispose() + { + switch( _unsubscriber ) + { + case Action< Action< T1 > > a: + a( Invoke ); + break; + case Action b: + b(); + break; + } + + _unsubscriber = null; + _provider = null; + GC.SuppressFinalize( this ); + } + + ~EventProvider() + => Dispose(); +} + +public sealed class EventProvider< T1, T2 > : IDisposable +{ + private ICallGateProvider< T1, T2, object? >? _provider; + private Delegate? _unsubscriber; + + public EventProvider( DalamudPluginInterface pi, string label, + (Action< Action< T1, T2 > > Add, Action< Action< T1, T2 > > Del)? subscribe = null ) + { + _unsubscriber = null; + try + { + _provider = pi.GetIpcProvider< T1, T2, object? >( label ); + subscribe?.Add( Invoke ); + _unsubscriber = subscribe?.Del; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + } + + public EventProvider( DalamudPluginInterface pi, string label, Action add, Action del ) + { + _unsubscriber = null; + try + { + _provider = pi.GetIpcProvider< T1, T2, object? >( label ); + add(); + _unsubscriber = del; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + } + + public void Invoke( T1 a, T2 b ) + => _provider?.SendMessage( a, b ); + + public void Dispose() + { + switch( _unsubscriber ) + { + case Action< Action< T1, T2 > > a: + a( Invoke ); + break; + case Action b: + b(); + break; + } + + _unsubscriber = null; + _provider = null; + GC.SuppressFinalize( this ); + } + + ~EventProvider() + => Dispose(); +} + +public sealed class EventProvider< T1, T2, T3 > : IDisposable +{ + private ICallGateProvider< T1, T2, T3, object? >? _provider; + private Delegate? _unsubscriber; + + public EventProvider( DalamudPluginInterface pi, string label, + (Action< Action< T1, T2, T3 > > Add, Action< Action< T1, T2, T3 > > Del)? subscribe = null ) + { + _unsubscriber = null; + try + { + _provider = pi.GetIpcProvider< T1, T2, T3, object? >( label ); + subscribe?.Add( Invoke ); + _unsubscriber = subscribe?.Del; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + } + + public EventProvider( DalamudPluginInterface pi, string label, Action add, Action del ) + { + _unsubscriber = null; + try + { + _provider = pi.GetIpcProvider< T1, T2, T3, object? >( label ); + add(); + _unsubscriber = del; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + } + + public void Invoke( T1 a, T2 b, T3 c ) + => _provider?.SendMessage( a, b, c ); + + public void Dispose() + { + switch( _unsubscriber ) + { + case Action< Action< T1, T2, T3 > > a: + a( Invoke ); + break; + case Action b: + b(); + break; + } + + _unsubscriber = null; + _provider = null; + GC.SuppressFinalize( this ); + } + + ~EventProvider() + => Dispose(); +} + +public sealed class EventProvider< T1, T2, T3, T4 > : IDisposable +{ + private ICallGateProvider< T1, T2, T3, T4, object? >? _provider; + private Delegate? _unsubscriber; + + public EventProvider( DalamudPluginInterface pi, string label, + (Action< Action< T1, T2, T3, T4 > > Add, Action< Action< T1, T2, T3, T4 > > Del)? subscribe = null ) + { + _unsubscriber = null; + try + { + _provider = pi.GetIpcProvider< T1, T2, T3, T4, object? >( label ); + subscribe?.Add( Invoke ); + _unsubscriber = subscribe?.Del; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + } + + public EventProvider( DalamudPluginInterface pi, string label, Action add, Action del ) + { + _unsubscriber = null; + try + { + _provider = pi.GetIpcProvider< T1, T2, T3, T4, object? >( label ); + add(); + _unsubscriber = del; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + } + + public void Invoke( T1 a, T2 b, T3 c, T4 d ) + => _provider?.SendMessage( a, b, c, d ); + + public void Dispose() + { + switch( _unsubscriber ) + { + case Action< Action< T1, T2, T3, T4 > > a: + a( Invoke ); + break; + case Action b: + b(); + break; + } + + _unsubscriber = null; + _provider = null; + GC.SuppressFinalize( this ); + } + + ~EventProvider() + => Dispose(); +} + +public sealed class EventProvider< T1, T2, T3, T4, T5 > : IDisposable +{ + private ICallGateProvider< T1, T2, T3, T4, T5, object? >? _provider; + private Delegate? _unsubscriber; + + public EventProvider( DalamudPluginInterface pi, string label, + (Action< Action< T1, T2, T3, T4, T5 > > Add, Action< Action< T1, T2, T3, T4, T5 > > Del)? subscribe = null ) + { + _unsubscriber = null; + try + { + _provider = pi.GetIpcProvider< T1, T2, T3, T4, T5, object? >( label ); + subscribe?.Add( Invoke ); + _unsubscriber = subscribe?.Del; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + } + + public EventProvider( DalamudPluginInterface pi, string label, Action add, Action del ) + { + _unsubscriber = null; + try + { + _provider = pi.GetIpcProvider< T1, T2, T3, T4, T5, object? >( label ); + add(); + _unsubscriber = del; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + } + + public void Invoke( T1 a, T2 b, T3 c, T4 d, T5 e ) + => _provider?.SendMessage( a, b, c, d, e ); + + public void Dispose() + { + switch( _unsubscriber ) + { + case Action< Action< T1, T2, T3, T4, T5 > > a: + a( Invoke ); + break; + case Action b: + b(); + break; + } + + _unsubscriber = null; + _provider = null; + GC.SuppressFinalize( this ); + } + + ~EventProvider() + => Dispose(); +} \ No newline at end of file diff --git a/tmp/Helpers/EventSubscriber.cs b/tmp/Helpers/EventSubscriber.cs new file mode 100644 index 00000000..0df6bc11 --- /dev/null +++ b/tmp/Helpers/EventSubscriber.cs @@ -0,0 +1,607 @@ +using System; +using System.Collections.Generic; +using Dalamud.Logging; +using Dalamud.Plugin; +using Dalamud.Plugin.Ipc; + +namespace Penumbra.Api.Helpers; + +public sealed class EventSubscriber : IDisposable +{ + private readonly string _label; + private readonly Dictionary< Action, Action > _delegates = new(); + private ICallGateSubscriber< object? >? _subscriber; + private bool _disabled; + + public EventSubscriber( DalamudPluginInterface pi, string label, params Action[] actions ) + { + _label = label; + try + { + _subscriber = pi.GetIpcSubscriber< object? >( label ); + foreach( var action in actions ) + { + Event += action; + } + + _disabled = false; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public void Enable() + { + if( _disabled && _subscriber != null ) + { + foreach( var action in _delegates.Keys ) + { + _subscriber.Subscribe( action ); + } + + _disabled = false; + } + } + + public void Disable() + { + if( !_disabled ) + { + if( _subscriber != null ) + { + foreach( var action in _delegates.Keys ) + { + _subscriber.Unsubscribe( action ); + } + } + + _disabled = true; + } + } + + public event Action Event + { + add + { + if( _subscriber != null && !_delegates.ContainsKey( value ) ) + { + void Action() + { + try + { + value(); + } + catch( Exception e ) + { + PluginLog.Error( $"Exception invoking IPC event {_label}:\n{e}" ); + } + } + + if( _delegates.TryAdd( value, Action ) && !_disabled ) + { + _subscriber.Subscribe( Action ); + } + } + } + remove + { + if( _subscriber != null && _delegates.Remove( value, out var action ) ) + { + _subscriber.Unsubscribe( action ); + } + } + } + + public void Dispose() + { + Disable(); + _subscriber = null; + _delegates.Clear(); + } + + ~EventSubscriber() + => Dispose(); +} + +public sealed class EventSubscriber< T1 > : IDisposable +{ + private readonly string _label; + private readonly Dictionary< Action< T1 >, Action< T1 > > _delegates = new(); + private ICallGateSubscriber< T1, object? >? _subscriber; + private bool _disabled; + + public EventSubscriber( DalamudPluginInterface pi, string label, params Action< T1 >[] actions ) + { + _label = label; + try + { + _subscriber = pi.GetIpcSubscriber< T1, object? >( label ); + foreach( var action in actions ) + { + Event += action; + } + + _disabled = false; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public void Enable() + { + if( _disabled && _subscriber != null ) + { + foreach( var action in _delegates.Keys ) + { + _subscriber.Subscribe( action ); + } + + _disabled = false; + } + } + + public void Disable() + { + if( !_disabled ) + { + if( _subscriber != null ) + { + foreach( var action in _delegates.Keys ) + { + _subscriber.Unsubscribe( action ); + } + } + + _disabled = true; + } + } + + public event Action< T1 > Event + { + add + { + if( _subscriber != null && !_delegates.ContainsKey( value ) ) + { + void Action( T1 a ) + { + try + { + value( a ); + } + catch( Exception e ) + { + PluginLog.Error( $"Exception invoking IPC event {_label}:\n{e}" ); + } + } + + if( _delegates.TryAdd( value, Action ) && !_disabled ) + { + _subscriber.Subscribe( Action ); + } + } + } + remove + { + if( _subscriber != null && _delegates.Remove( value, out var action ) ) + { + _subscriber.Unsubscribe( action ); + } + } + } + + public void Dispose() + { + Disable(); + _subscriber = null; + _delegates.Clear(); + } + + ~EventSubscriber() + => Dispose(); +} + +public sealed class EventSubscriber< T1, T2 > : IDisposable +{ + private readonly string _label; + private readonly Dictionary< Action< T1, T2 >, Action< T1, T2 > > _delegates = new(); + private ICallGateSubscriber< T1, T2, object? >? _subscriber; + private bool _disabled; + + public EventSubscriber( DalamudPluginInterface pi, string label, params Action< T1, T2 >[] actions ) + { + _label = label; + try + { + _subscriber = pi.GetIpcSubscriber< T1, T2, object? >( label ); + foreach( var action in actions ) + { + Event += action; + } + + _disabled = false; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public void Enable() + { + if( _disabled && _subscriber != null ) + { + foreach( var action in _delegates.Keys ) + { + _subscriber.Subscribe( action ); + } + + _disabled = false; + } + } + + public void Disable() + { + if( !_disabled ) + { + if( _subscriber != null ) + { + foreach( var action in _delegates.Keys ) + { + _subscriber.Unsubscribe( action ); + } + } + + _disabled = true; + } + } + + public event Action< T1, T2 > Event + { + add + { + if( _subscriber != null && !_delegates.ContainsKey( value ) ) + { + void Action( T1 a, T2 b ) + { + try + { + value( a, b ); + } + catch( Exception e ) + { + PluginLog.Error( $"Exception invoking IPC event {_label}:\n{e}" ); + } + } + + if( _delegates.TryAdd( value, Action ) && !_disabled ) + { + _subscriber.Subscribe( Action ); + } + } + } + remove + { + if( _subscriber != null && _delegates.Remove( value, out var action ) ) + { + _subscriber.Unsubscribe( action ); + } + } + } + + public void Dispose() + { + Disable(); + _subscriber = null; + _delegates.Clear(); + } + + ~EventSubscriber() + => Dispose(); +} + +public sealed class EventSubscriber< T1, T2, T3 > : IDisposable +{ + private readonly string _label; + private readonly Dictionary< Action< T1, T2, T3 >, Action< T1, T2, T3 > > _delegates = new(); + private ICallGateSubscriber< T1, T2, T3, object? >? _subscriber; + private bool _disabled; + + public EventSubscriber( DalamudPluginInterface pi, string label, params Action< T1, T2, T3 >[] actions ) + { + _label = label; + try + { + _subscriber = pi.GetIpcSubscriber< T1, T2, T3, object? >( label ); + foreach( var action in actions ) + { + Event += action; + } + + _disabled = false; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public void Enable() + { + if( _disabled && _subscriber != null ) + { + foreach( var action in _delegates.Keys ) + { + _subscriber.Subscribe( action ); + } + + _disabled = false; + } + } + + public void Disable() + { + if( !_disabled ) + { + if( _subscriber != null ) + { + foreach( var action in _delegates.Keys ) + { + _subscriber.Unsubscribe( action ); + } + } + + _disabled = true; + } + } + + public event Action< T1, T2, T3 > Event + { + add + { + if( _subscriber != null && !_delegates.ContainsKey( value ) ) + { + void Action( T1 a, T2 b, T3 c ) + { + try + { + value( a, b, c ); + } + catch( Exception e ) + { + PluginLog.Error( $"Exception invoking IPC event {_label}:\n{e}" ); + } + } + + if( _delegates.TryAdd( value, Action ) && !_disabled ) + { + _subscriber.Subscribe( Action ); + } + } + } + remove + { + if( _subscriber != null && _delegates.Remove( value, out var action ) ) + { + _subscriber.Unsubscribe( action ); + } + } + } + + public void Dispose() + { + Disable(); + _subscriber = null; + _delegates.Clear(); + } + + ~EventSubscriber() + => Dispose(); +} + +public sealed class EventSubscriber< T1, T2, T3, T4 > : IDisposable +{ + private readonly string _label; + private readonly Dictionary< Action< T1, T2, T3, T4 >, Action< T1, T2, T3, T4 > > _delegates = new(); + private ICallGateSubscriber< T1, T2, T3, T4, object? >? _subscriber; + private bool _disabled; + + public EventSubscriber( DalamudPluginInterface pi, string label, params Action< T1, T2, T3, T4 >[] actions ) + { + _label = label; + try + { + _subscriber = pi.GetIpcSubscriber< T1, T2, T3, T4, object? >( label ); + foreach( var action in actions ) + { + Event += action; + } + + _disabled = false; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public void Enable() + { + if( _disabled && _subscriber != null ) + { + foreach( var action in _delegates.Keys ) + { + _subscriber.Subscribe( action ); + } + + _disabled = false; + } + } + + public void Disable() + { + if( !_disabled ) + { + if( _subscriber != null ) + { + foreach( var action in _delegates.Keys ) + { + _subscriber.Unsubscribe( action ); + } + } + + _disabled = true; + } + } + + public event Action< T1, T2, T3, T4 > Event + { + add + { + if( _subscriber != null && !_delegates.ContainsKey( value ) ) + { + void Action( T1 a, T2 b, T3 c, T4 d ) + { + try + { + value( a, b, c, d ); + } + catch( Exception e ) + { + PluginLog.Error( $"Exception invoking IPC event {_label}:\n{e}" ); + } + } + + if( _delegates.TryAdd( value, Action ) && !_disabled ) + { + _subscriber.Subscribe( Action ); + } + } + } + remove + { + if( _subscriber != null && _delegates.Remove( value, out var action ) ) + { + _subscriber.Unsubscribe( action ); + } + } + } + + public void Dispose() + { + Disable(); + _subscriber = null; + _delegates.Clear(); + } + + ~EventSubscriber() + => Dispose(); +} + +public sealed class EventSubscriber< T1, T2, T3, T4, T5 > : IDisposable +{ + private readonly string _label; + private readonly Dictionary< Action< T1, T2, T3, T4, T5 >, Action< T1, T2, T3, T4, T5 > > _delegates = new(); + private ICallGateSubscriber< T1, T2, T3, T4, T5, object? >? _subscriber; + private bool _disabled; + + public EventSubscriber( DalamudPluginInterface pi, string label, params Action< T1, T2, T3, T4, T5 >[] actions ) + { + _label = label; + try + { + _subscriber = pi.GetIpcSubscriber< T1, T2, T3, T4, T5, object? >( label ); + foreach( var action in actions ) + { + Event += action; + } + + _disabled = false; + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public void Enable() + { + if( _disabled && _subscriber != null ) + { + foreach( var action in _delegates.Keys ) + { + _subscriber.Subscribe( action ); + } + + _disabled = false; + } + } + + public void Disable() + { + if( !_disabled ) + { + if( _subscriber != null ) + { + foreach( var action in _delegates.Keys ) + { + _subscriber.Unsubscribe( action ); + } + } + + _disabled = true; + } + } + + public event Action< T1, T2, T3, T4, T5 > Event + { + add + { + if( _subscriber != null && !_delegates.ContainsKey( value ) ) + { + void Action( T1 a, T2 b, T3 c, T4 d, T5 e ) + { + try + { + value( a, b, c, d, e ); + } + catch( Exception ex ) + { + PluginLog.Error( $"Exception invoking IPC event {_label}:\n{ex}" ); + } + } + + if( _delegates.TryAdd( value, Action ) && !_disabled ) + { + _subscriber.Subscribe( Action ); + } + } + } + remove + { + if( _subscriber != null && _delegates.Remove( value, out var action ) ) + { + _subscriber.Unsubscribe( action ); + } + } + } + + public void Dispose() + { + Disable(); + _subscriber = null; + _delegates.Clear(); + } + + ~EventSubscriber() + => Dispose(); +} \ No newline at end of file diff --git a/tmp/Helpers/FuncProvider.cs b/tmp/Helpers/FuncProvider.cs new file mode 100644 index 00000000..fac61ce3 --- /dev/null +++ b/tmp/Helpers/FuncProvider.cs @@ -0,0 +1,186 @@ +using System; +using Dalamud.Logging; +using Dalamud.Plugin; +using Dalamud.Plugin.Ipc; + +namespace Penumbra.Api.Helpers; + +public sealed class FuncProvider< TRet > : IDisposable +{ + private ICallGateProvider< TRet >? _provider; + + public FuncProvider( DalamudPluginInterface pi, string label, Func< TRet > func ) + { + try + { + _provider = pi.GetIpcProvider< TRet >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + + _provider?.RegisterFunc( func ); + } + + public void Dispose() + { + _provider?.UnregisterFunc(); + _provider = null; + GC.SuppressFinalize( this ); + } + + ~FuncProvider() + => Dispose(); +} + +public sealed class FuncProvider< T1, TRet > : IDisposable +{ + private ICallGateProvider< T1, TRet >? _provider; + + public FuncProvider( DalamudPluginInterface pi, string label, Func< T1, TRet > func ) + { + try + { + _provider = pi.GetIpcProvider< T1, TRet >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + + _provider?.RegisterFunc( func ); + } + + public void Dispose() + { + _provider?.UnregisterFunc(); + _provider = null; + GC.SuppressFinalize( this ); + } + + ~FuncProvider() + => Dispose(); +} + +public sealed class FuncProvider< T1, T2, TRet > : IDisposable +{ + private ICallGateProvider< T1, T2, TRet >? _provider; + + public FuncProvider( DalamudPluginInterface pi, string label, Func< T1, T2, TRet > func ) + { + try + { + _provider = pi.GetIpcProvider< T1, T2, TRet >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + + _provider?.RegisterFunc( func ); + } + + public void Dispose() + { + _provider?.UnregisterFunc(); + _provider = null; + GC.SuppressFinalize( this ); + } + + ~FuncProvider() + => Dispose(); +} + +public sealed class FuncProvider< T1, T2, T3, TRet > : IDisposable +{ + private ICallGateProvider< T1, T2, T3, TRet >? _provider; + + public FuncProvider( DalamudPluginInterface pi, string label, Func< T1, T2, T3, TRet > func ) + { + try + { + _provider = pi.GetIpcProvider< T1, T2, T3, TRet >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + + _provider?.RegisterFunc( func ); + } + + public void Dispose() + { + _provider?.UnregisterFunc(); + _provider = null; + GC.SuppressFinalize( this ); + } + + ~FuncProvider() + => Dispose(); +} + +public sealed class FuncProvider< T1, T2, T3, T4, TRet > : IDisposable +{ + private ICallGateProvider< T1, T2, T3, T4, TRet >? _provider; + + public FuncProvider( DalamudPluginInterface pi, string label, Func< T1, T2, T3, T4, TRet > func ) + { + try + { + _provider = pi.GetIpcProvider< T1, T2, T3, T4, TRet >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + + _provider?.RegisterFunc( func ); + } + + public void Dispose() + { + _provider?.UnregisterFunc(); + _provider = null; + GC.SuppressFinalize( this ); + } + + ~FuncProvider() + => Dispose(); +} + +public sealed class FuncProvider< T1, T2, T3, T4, T5, TRet > : IDisposable +{ + private ICallGateProvider< T1, T2, T3, T4, T5, TRet >? _provider; + + public FuncProvider( DalamudPluginInterface pi, string label, Func< T1, T2, T3, T4, T5, TRet > func ) + { + try + { + _provider = pi.GetIpcProvider< T1, T2, T3, T4, T5, TRet >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Provider for {label}\n{e}" ); + _provider = null; + } + + _provider?.RegisterFunc( func ); + } + + public void Dispose() + { + _provider?.UnregisterFunc(); + _provider = null; + GC.SuppressFinalize( this ); + } + + ~FuncProvider() + => Dispose(); +} \ No newline at end of file diff --git a/tmp/Helpers/FuncSubscriber.cs b/tmp/Helpers/FuncSubscriber.cs new file mode 100644 index 00000000..3f8cbdda --- /dev/null +++ b/tmp/Helpers/FuncSubscriber.cs @@ -0,0 +1,163 @@ +using System; +using Dalamud.Logging; +using Dalamud.Plugin; +using Dalamud.Plugin.Ipc; +using Dalamud.Plugin.Ipc.Exceptions; + +namespace Penumbra.Api.Helpers; + +public readonly struct FuncSubscriber< TRet > +{ + private readonly string _label; + private readonly ICallGateSubscriber< TRet >? _subscriber; + + public bool Valid + => _subscriber != null; + + public FuncSubscriber( DalamudPluginInterface pi, string label ) + { + _label = label; + try + { + _subscriber = pi.GetIpcSubscriber< TRet >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public TRet Invoke() + => _subscriber != null ? _subscriber.InvokeFunc() : throw new IpcNotReadyError( _label ); +} + +public readonly struct FuncSubscriber< T1, TRet > +{ + private readonly string _label; + private readonly ICallGateSubscriber< T1, TRet >? _subscriber; + + public bool Valid + => _subscriber != null; + + public FuncSubscriber( DalamudPluginInterface pi, string label ) + { + _label = label; + try + { + _subscriber = pi.GetIpcSubscriber< T1, TRet >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public TRet Invoke( T1 a ) + => _subscriber != null ? _subscriber.InvokeFunc( a ) : throw new IpcNotReadyError( _label ); +} + +public readonly struct FuncSubscriber< T1, T2, TRet > +{ + private readonly string _label; + private readonly ICallGateSubscriber< T1, T2, TRet >? _subscriber; + + public bool Valid + => _subscriber != null; + + public FuncSubscriber( DalamudPluginInterface pi, string label ) + { + _label = label; + try + { + _subscriber = pi.GetIpcSubscriber< T1, T2, TRet >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public TRet Invoke( T1 a, T2 b ) + => _subscriber != null ? _subscriber.InvokeFunc( a, b ) : throw new IpcNotReadyError( _label ); +} + +public readonly struct FuncSubscriber< T1, T2, T3, TRet > +{ + private readonly string _label; + private readonly ICallGateSubscriber< T1, T2, T3, TRet >? _subscriber; + + public bool Valid + => _subscriber != null; + + public FuncSubscriber( DalamudPluginInterface pi, string label ) + { + _label = label; + try + { + _subscriber = pi.GetIpcSubscriber< T1, T2, T3, TRet >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public TRet Invoke( T1 a, T2 b, T3 c ) + => _subscriber != null ? _subscriber.InvokeFunc( a, b, c ) : throw new IpcNotReadyError( _label ); +} + +public readonly struct FuncSubscriber< T1, T2, T3, T4, TRet > +{ + private readonly string _label; + private readonly ICallGateSubscriber< T1, T2, T3, T4, TRet >? _subscriber; + + public bool Valid + => _subscriber != null; + + public FuncSubscriber( DalamudPluginInterface pi, string label ) + { + _label = label; + try + { + _subscriber = pi.GetIpcSubscriber< T1, T2, T3, T4, TRet >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public TRet Invoke( T1 a, T2 b, T3 c, T4 d ) + => _subscriber != null ? _subscriber.InvokeFunc( a, b, c, d ) : throw new IpcNotReadyError( _label ); +} + +public readonly struct FuncSubscriber< T1, T2, T3, T4, T5, TRet > +{ + private readonly string _label; + private readonly ICallGateSubscriber< T1, T2, T3, T4, T5, TRet >? _subscriber; + + public bool Valid + => _subscriber != null; + + public FuncSubscriber( DalamudPluginInterface pi, string label ) + { + _label = label; + try + { + _subscriber = pi.GetIpcSubscriber< T1, T2, T3, T4, T5, TRet >( label ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC Subscriber for {label}\n{e}" ); + _subscriber = null; + } + } + + public TRet Invoke( T1 a, T2 b, T3 c, T4 d, T5 e ) + => _subscriber != null ? _subscriber.InvokeFunc( a, b, c, d, e ) : throw new IpcNotReadyError( _label ); +} \ No newline at end of file diff --git a/Penumbra/Api/IPenumbraApi.cs b/tmp/IPenumbraApi.cs similarity index 86% rename from Penumbra/Api/IPenumbraApi.cs rename to tmp/IPenumbraApi.cs index 257cfbde..51860312 100644 --- a/Penumbra/Api/IPenumbraApi.cs +++ b/tmp/IPenumbraApi.cs @@ -1,63 +1,28 @@ 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; +using Penumbra.Api.Enums; 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 { + #region Game State + // Obtain the currently set mod directory from the configuration. public string GetModDirectory(); + // Obtain the entire current penumbra configuration as a json encoded string. + public string GetConfiguration(); + // 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(); + #endregion + + #region UI // Triggered when the user hovers over a listed changed object in a mod tab. // Can be used to append tooltips. @@ -70,8 +35,36 @@ public interface IPenumbraApi : IPenumbraApiBase // Triggered when the user clicks a listed changed object in a mod tab. public event ChangedItemClick? ChangedItemClicked; + + #endregion + + #region Redrawing + + // 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 ); + + // Triggered whenever a game object is redrawn via Penumbra. public event GameObjectRedrawn? GameObjectRedrawn; + #endregion + + #region Game State + + // 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 ); + // 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; @@ -84,17 +77,9 @@ public interface IPenumbraApi : IPenumbraApiBase // 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 ); + #endregion - // 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 ); + #region Resolving // Resolve a given gamePath via Penumbra using the Default collection. // Returns the given gamePath if penumbra would not manipulate it. @@ -125,8 +110,9 @@ public interface IPenumbraApi : IPenumbraApiBase // 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 ); + #endregion + + #region Collections // Obtain a list of the names of all currently installed collections. public IList< string > GetCollections(); @@ -143,11 +129,24 @@ public interface IPenumbraApi : IPenumbraApiBase // 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 ); + // Gets a dictionary of effected items from a collection + public IReadOnlyDictionary< string, object? > GetChangedItemsForCollection( string collectionName ); - // Obtain the parent game object index for an unnamed cutscene actor by its index. - public int GetCutsceneParentIndex( int actor ); + #endregion + + #region Meta + + // 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 ); + + #endregion + + #region Mods // 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(); @@ -162,20 +161,29 @@ public interface IPenumbraApi : IPenumbraApiBase // 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(); + // Try to delete a mod given by its modDirectory or its name. + // Returns NothingDone if the mod can not be found or success otherwise. + // Note that success does only imply a successful call, not successful deletion. + public PenumbraApiEc DeleteMod( string modDirectory, string modName ); - // 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 ); + // Get the internal full filesystem path including search order for the specified mod. + // If success is returned, the second return value contains the full path + // and a bool indicating whether this is the default path (false) or a manually set one (true). + // Can return ModMissing or Success. + public (PenumbraApiEc, string, bool) GetModPath( string modDirectory, string modName ); + // Set the internal search order and filesystem path of the specified mod to the given path. + // Returns InvalidArgument if newPath is empty, ModMissing if the mod can not be found, + // PathRenameFailed if newPath could not be set and Success otherwise. + public PenumbraApiEc SetModPath( string modDirectory, string modName, string newPath ); - // ############## Mod Settings ################# + #endregion + + #region 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 ); + public IDictionary< string, (IList< string >, GroupType) >? 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. @@ -207,6 +215,10 @@ public interface IPenumbraApi : IPenumbraApiBase // This event gets fired when any setting in any collection changes. public event ModSettingChanged? ModSettingChanged; + #endregion + + #region Temporary + // 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. @@ -233,4 +245,6 @@ public interface IPenumbraApi : IPenumbraApiBase // 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 ); + + #endregion } \ No newline at end of file diff --git a/tmp/IPenumbraApiBase.cs b/tmp/IPenumbraApiBase.cs new file mode 100644 index 00000000..e4c452b4 --- /dev/null +++ b/tmp/IPenumbraApiBase.cs @@ -0,0 +1,11 @@ +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; } +} \ No newline at end of file diff --git a/tmp/Ipc/Collection.cs b/tmp/Ipc/Collection.cs new file mode 100644 index 00000000..3c40cd17 --- /dev/null +++ b/tmp/Ipc/Collection.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using Dalamud.Plugin; +using Penumbra.Api.Helpers; + +namespace Penumbra.Api; + +public static partial class Ipc +{ + public static class GetCollections + { + public const string Label = $"Penumbra.{nameof( GetCollections )}"; + + public static FuncProvider< IList< string > > Provider( DalamudPluginInterface pi, Func< IList< string > > func ) + => new(pi, Label, func); + + public static FuncSubscriber< IList< string > > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class GetCurrentCollectionName + { + public const string Label = $"Penumbra.{nameof( GetCurrentCollectionName )}"; + + public static FuncProvider< string > Provider( DalamudPluginInterface pi, Func< string > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class GetDefaultCollectionName + { + public const string Label = $"Penumbra.{nameof( GetDefaultCollectionName )}"; + + public static FuncProvider< string > Provider( DalamudPluginInterface pi, Func< string > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class GetInterfaceCollectionName + { + public const string Label = $"Penumbra.{nameof( GetInterfaceCollectionName )}"; + + public static FuncProvider< string > Provider( DalamudPluginInterface pi, Func< string > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class GetCharacterCollectionName + { + public const string Label = $"Penumbra.{nameof( GetCharacterCollectionName )}"; + + public static FuncProvider< string, (string, bool) > Provider( DalamudPluginInterface pi, Func< string, (string, bool) > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, (string, bool) > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class GetChangedItems + { + public const string Label = $"Penumbra.{nameof( GetChangedItems )}"; + + public static FuncProvider< string, IReadOnlyDictionary< string, object? > > Provider( DalamudPluginInterface pi, + Func< string, IReadOnlyDictionary< string, object? > > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, IReadOnlyDictionary< string, object? > > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } +} \ No newline at end of file diff --git a/tmp/Ipc/Configuration.cs b/tmp/Ipc/Configuration.cs new file mode 100644 index 00000000..67033458 --- /dev/null +++ b/tmp/Ipc/Configuration.cs @@ -0,0 +1,42 @@ +using System; +using Dalamud.Plugin; +using Penumbra.Api.Helpers; + +namespace Penumbra.Api; + +public static partial class Ipc +{ + public static class GetModDirectory + { + public const string Label = $"Penumbra.{nameof( GetModDirectory )}"; + + public static FuncProvider< string > Provider( DalamudPluginInterface pi, Func< string > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class GetConfiguration + { + public const string Label = $"Penumbra.{nameof( GetConfiguration )}"; + + public static FuncProvider< string > Provider( DalamudPluginInterface pi, Func< string > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class ModDirectoryChanged + { + public const string Label = $"Penumbra.{nameof( ModDirectoryChanged )}"; + + public static EventProvider< string, bool > Provider( DalamudPluginInterface pi, + Action< Action< string, bool > > sub, Action< Action< string, bool > > unsub ) + => new(pi, Label, ( sub, unsub )); + + public static EventSubscriber< string, bool > Subscriber( DalamudPluginInterface pi, params Action< string, bool >[] actions ) + => new(pi, Label, actions); + } +} \ No newline at end of file diff --git a/tmp/Ipc/GameState.cs b/tmp/Ipc/GameState.cs new file mode 100644 index 00000000..89889fda --- /dev/null +++ b/tmp/Ipc/GameState.cs @@ -0,0 +1,63 @@ +using System; +using Dalamud.Plugin; +using Penumbra.Api.Helpers; + +namespace Penumbra.Api; + +public static partial class Ipc +{ + public static class GetDrawObjectInfo + { + public const string Label = $"Penumbra.{nameof( GetDrawObjectInfo )}"; + + public static FuncProvider< nint, (nint, string) > Provider( DalamudPluginInterface pi, Func< nint, (nint, string) > func ) + => new(pi, Label, func); + + public static FuncSubscriber< nint, (nint, string) > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class GetCutsceneParentIndex + { + public const string Label = $"Penumbra.{nameof( GetCutsceneParentIndex )}"; + + public static FuncProvider< int, int > Provider( DalamudPluginInterface pi, Func< int, int > func ) + => new(pi, Label, func); + + public static FuncSubscriber< int, int > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class CreatingCharacterBase + { + public const string Label = $"Penumbra.{nameof( CreatingCharacterBase )}"; + + public static EventProvider< nint, string, nint, nint, nint > Provider( DalamudPluginInterface pi, Action add, Action del ) + => new(pi, Label, add, del); + + public static EventSubscriber< nint, string, nint, nint, nint > Subscriber( DalamudPluginInterface pi, params Action< nint, string, nint, nint, nint >[] actions ) + => new(pi, Label, actions); + } + + public static class CreatedCharacterBase + { + public const string Label = $"Penumbra.{nameof( CreatedCharacterBase )}"; + + public static EventProvider< nint, string, nint > Provider( DalamudPluginInterface pi, Action add, Action del ) + => new(pi, Label, add, del); + + public static EventSubscriber< nint, string, nint > Subscriber( DalamudPluginInterface pi, params Action< nint, string, nint >[] actions ) + => new(pi, Label, actions); + } + + public static class GameObjectResourcePathResolved + { + public const string Label = $"Penumbra.{nameof( GameObjectResourcePathResolved )}"; + + public static EventProvider< nint, string, string > Provider( DalamudPluginInterface pi, Action add, Action del ) + => new(pi, Label, add, del); + + public static EventSubscriber< nint, string, string > Subscriber( DalamudPluginInterface pi, params Action< nint, string, string >[] actions ) + => new(pi, Label, actions); + } +} \ No newline at end of file diff --git a/tmp/Ipc/Meta.cs b/tmp/Ipc/Meta.cs new file mode 100644 index 00000000..ef889f43 --- /dev/null +++ b/tmp/Ipc/Meta.cs @@ -0,0 +1,30 @@ +using System; +using Dalamud.Plugin; +using Penumbra.Api.Helpers; + +namespace Penumbra.Api; + +public static partial class Ipc +{ + public static class GetPlayerMetaManipulations + { + public const string Label = $"Penumbra.{nameof( GetPlayerMetaManipulations )}"; + + public static FuncProvider< string > Provider( DalamudPluginInterface pi, Func< string > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class GetMetaManipulations + { + public const string Label = $"Penumbra.{nameof( GetMetaManipulations )}"; + + public static FuncProvider< string, string > Provider( DalamudPluginInterface pi, Func< string, string > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } +} \ No newline at end of file diff --git a/tmp/Ipc/ModSettings.cs b/tmp/Ipc/ModSettings.cs new file mode 100644 index 00000000..8a0e9cf8 --- /dev/null +++ b/tmp/Ipc/ModSettings.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using Dalamud.Plugin; +using Penumbra.Api.Enums; +using Penumbra.Api.Helpers; + +namespace Penumbra.Api; + +using CurrentSettings = ValueTuple< PenumbraApiEc, (bool, int, IDictionary< string, IList< string > >, bool)? >; + +public static partial class Ipc +{ + public static class GetAvailableModSettings + { + public const string Label = $"Penumbra.{nameof( GetAvailableModSettings )}"; + + public static FuncProvider< string, string, IDictionary< string, (IList< string >, GroupType) >? > Provider( + DalamudPluginInterface pi, Func< string, string, IDictionary< string, (IList< string >, GroupType) >? > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, IDictionary< string, (IList< string >, GroupType) >? > Subscriber( + DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class GetCurrentModSettings + { + public const string Label = $"Penumbra.{nameof( GetCurrentModSettings )}"; + + public static FuncProvider< string, string, string, bool, CurrentSettings > Provider( DalamudPluginInterface pi, + Func< string, string, string, bool, CurrentSettings > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, string, bool, CurrentSettings > Subscriber( + DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class TryInheritMod + { + public const string Label = $"Penumbra.{nameof( TryInheritMod )}"; + + public static FuncProvider< string, string, string, bool, PenumbraApiEc > Provider( DalamudPluginInterface pi, + Func< string, string, string, bool, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, string, bool, PenumbraApiEc > Subscriber( + DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class TrySetMod + { + public const string Label = $"Penumbra.{nameof( TrySetMod )}"; + + public static FuncProvider< string, string, string, bool, PenumbraApiEc > Provider( DalamudPluginInterface pi, + Func< string, string, string, bool, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, string, bool, PenumbraApiEc > Subscriber( + DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class TrySetModPriority + { + public const string Label = $"Penumbra.{nameof( TrySetModPriority )}"; + + public static FuncProvider< string, string, string, int, PenumbraApiEc > Provider( DalamudPluginInterface pi, + Func< string, string, string, int, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, string, int, PenumbraApiEc > Subscriber( + DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class TrySetModSetting + { + public const string Label = $"Penumbra.{nameof( TrySetModSetting )}"; + + public static FuncProvider< string, string, string, string, string, PenumbraApiEc > Provider( DalamudPluginInterface pi, + Func< string, string, string, string, string, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, string, string, string, PenumbraApiEc > Subscriber( + DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class TrySetModSettings + { + public const string Label = $"Penumbra.{nameof( TrySetModSettings )}"; + + public static FuncProvider< string, string, string, string, IReadOnlyList< string >, PenumbraApiEc > Provider( + DalamudPluginInterface pi, + Func< string, string, string, string, IReadOnlyList< string >, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, string, string, IReadOnlyList< string >, PenumbraApiEc > Subscriber( + DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class ModSettingChanged + { + public const string Label = $"Penumbra.{nameof( ModSettingChanged )}"; + + public static EventProvider< ModSettingChange, string, string, bool > Provider( DalamudPluginInterface pi, Action add, Action del ) + => new(pi, Label, add, del); + + public static EventSubscriber< ModSettingChange, string, string, bool > Subscriber( DalamudPluginInterface pi, + params Action< ModSettingChange, string, string, bool >[] actions ) + => new(pi, Label, actions); + } +} \ No newline at end of file diff --git a/tmp/Ipc/Mods.cs b/tmp/Ipc/Mods.cs new file mode 100644 index 00000000..d5d09036 --- /dev/null +++ b/tmp/Ipc/Mods.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using Dalamud.Plugin; +using Penumbra.Api.Enums; +using Penumbra.Api.Helpers; + +namespace Penumbra.Api; + +public static partial class Ipc +{ + public static class GetMods + { + public const string Label = $"Penumbra.{nameof( GetMods )}"; + + public static FuncProvider< IList< (string, string) > > Provider( DalamudPluginInterface pi, Func< IList< (string, string) > > func ) + => new(pi, Label, func); + + public static FuncSubscriber< IList< (string, string) > > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class ReloadMod + { + public const string Label = $"Penumbra.{nameof( ReloadMod )}"; + + public static FuncProvider< string, string, PenumbraApiEc > Provider( DalamudPluginInterface pi, + Func< string, string, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, PenumbraApiEc > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class AddMod + { + public const string Label = $"Penumbra.{nameof( AddMod )}"; + + public static FuncProvider< string, PenumbraApiEc > Provider( DalamudPluginInterface pi, + Func< string, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, PenumbraApiEc > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class DeleteMod + { + public const string Label = $"Penumbra.{nameof( DeleteMod )}"; + + public static FuncProvider< string, string, PenumbraApiEc > Provider( DalamudPluginInterface pi, + Func< string, string, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, PenumbraApiEc > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class GetModPath + { + public const string Label = $"Penumbra.{nameof( GetModPath )}"; + + public static FuncProvider< string, string, (PenumbraApiEc, string, bool) > Provider( DalamudPluginInterface pi, + Func< string, string, (PenumbraApiEc, string, bool) > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, (PenumbraApiEc, string, bool) > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class SetModPath + { + public const string Label = $"Penumbra.{nameof( SetModPath )}"; + + public static FuncProvider< string, string, string, PenumbraApiEc > Provider( DalamudPluginInterface pi, + Func< string, string, string, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, string, PenumbraApiEc > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } +} \ No newline at end of file diff --git a/tmp/Ipc/PluginState.cs b/tmp/Ipc/PluginState.cs new file mode 100644 index 00000000..500bf1ee --- /dev/null +++ b/tmp/Ipc/PluginState.cs @@ -0,0 +1,68 @@ +using System; +using Dalamud.Plugin; +using Penumbra.Api.Helpers; + +namespace Penumbra.Api; + +public static partial class Ipc +{ + public static class Initialized + { + public const string Label = $"Penumbra.{nameof( Initialized )}"; + + public static EventProvider Provider( DalamudPluginInterface pi ) + => new(pi, Label); + + public static EventSubscriber Subscriber( DalamudPluginInterface pi, params Action[] actions ) + { + var ret = new EventSubscriber( pi, Label ); + foreach( var action in actions ) + { + ret.Event += action; + } + + return ret; + } + } + + public static class Disposed + { + public const string Label = $"Penumbra.{nameof( Disposed )}"; + + public static EventProvider Provider( DalamudPluginInterface pi ) + => new(pi, Label); + + public static EventSubscriber Subscriber( DalamudPluginInterface pi, params Action[] actions ) + { + var ret = new EventSubscriber( pi, Label ); + foreach( var action in actions ) + { + ret.Event += action; + } + + return ret; + } + } + + public static class ApiVersion + { + public const string Label = $"Penumbra.{nameof( ApiVersion )}"; + + public static FuncProvider< int > Provider( DalamudPluginInterface pi, Func< int > func ) + => new(pi, Label, func); + + public static FuncSubscriber< int > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class ApiVersions + { + public const string Label = $"Penumbra.{nameof( ApiVersions )}"; + + public static FuncProvider< (int Breaking, int Features) > Provider( DalamudPluginInterface pi, Func< (int, int) > func ) + => new(pi, Label, func); + + public static FuncSubscriber< (int Breaking, int Features) > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } +} \ No newline at end of file diff --git a/tmp/Ipc/Redraw.cs b/tmp/Ipc/Redraw.cs new file mode 100644 index 00000000..396dfe8a --- /dev/null +++ b/tmp/Ipc/Redraw.cs @@ -0,0 +1,65 @@ +using System; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin; +using Penumbra.Api.Enums; +using Penumbra.Api.Helpers; + +namespace Penumbra.Api; + +public static partial class Ipc +{ + public static class RedrawAll + { + public const string Label = $"Penumbra.{nameof( RedrawAll )}"; + + public static ActionProvider< RedrawType > Provider( DalamudPluginInterface pi, Action< RedrawType > action ) + => new(pi, Label, action); + + public static ActionSubscriber< RedrawType > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class RedrawObject + { + public const string Label = $"Penumbra.{nameof( RedrawObject )}"; + + public static ActionProvider< GameObject, RedrawType > Provider( DalamudPluginInterface pi, Action< GameObject, RedrawType > action ) + => new(pi, Label, action); + + public static ActionSubscriber< GameObject, RedrawType > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class RedrawObjectByIndex + { + public const string Label = $"Penumbra.{nameof( RedrawObjectByIndex )}"; + + public static ActionProvider< int, RedrawType > Provider( DalamudPluginInterface pi, Action< int, RedrawType > action ) + => new(pi, Label, action); + + public static ActionSubscriber< int, RedrawType > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class RedrawObjectByName + { + public const string Label = $"Penumbra.{nameof( RedrawObjectByName )}"; + + public static ActionProvider< string, RedrawType > Provider( DalamudPluginInterface pi, Action< string, RedrawType > action ) + => new(pi, Label, action); + + public static ActionSubscriber< string, RedrawType > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class GameObjectRedrawn + { + public const string Label = $"Penumbra.{nameof( GameObjectRedrawn )}"; + + public static EventProvider< nint, int > Provider( DalamudPluginInterface pi, Action add, Action del ) + => new(pi, Label, add, del); + + public static EventSubscriber< nint, int > Subscriber( DalamudPluginInterface pi, params Action< nint, int >[] actions ) + => new(pi, Label, actions); + } +} \ No newline at end of file diff --git a/tmp/Ipc/Resolve.cs b/tmp/Ipc/Resolve.cs new file mode 100644 index 00000000..8b9eb953 --- /dev/null +++ b/tmp/Ipc/Resolve.cs @@ -0,0 +1,74 @@ +using System; +using Dalamud.Plugin; +using Penumbra.Api.Helpers; + +namespace Penumbra.Api; + +public static partial class Ipc +{ + public static class ResolveDefaultPath + { + public const string Label = $"Penumbra.{nameof( ResolveDefaultPath )}"; + + public static FuncProvider< string, string > Provider( DalamudPluginInterface pi, Func< string, string > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class ResolveInterfacePath + { + public const string Label = $"Penumbra.{nameof( ResolveInterfacePath )}"; + + public static FuncProvider< string, string > Provider( DalamudPluginInterface pi, Func< string, string > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class ResolvePlayerPath + { + public const string Label = $"Penumbra.{nameof( ResolvePlayerPath )}"; + + public static FuncProvider< string, string > Provider( DalamudPluginInterface pi, Func< string, string > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class ResolveCharacterPath + { + public const string Label = $"Penumbra.{nameof( ResolveCharacterPath )}"; + + public static FuncProvider< string, string, string > Provider( DalamudPluginInterface pi, Func< string, string, string > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, string > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class ReverseResolvePath + { + public const string Label = $"Penumbra.{nameof( ReverseResolvePath )}"; + + public static FuncProvider< string, string, string[] > Provider( DalamudPluginInterface pi, Func< string, string, string[] > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, string[] > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class ReverseResolvePlayerPath + { + public const string Label = $"Penumbra.{nameof( ReverseResolvePlayerPath )}"; + + public static FuncProvider< string, string[] > Provider( DalamudPluginInterface pi, Func< string, string[] > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string[] > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } +} \ No newline at end of file diff --git a/tmp/Ipc/Temporary.cs b/tmp/Ipc/Temporary.cs new file mode 100644 index 00000000..55af6f22 --- /dev/null +++ b/tmp/Ipc/Temporary.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using Dalamud.Plugin; +using Penumbra.Api.Enums; +using Penumbra.Api.Helpers; + +namespace Penumbra.Api; + +public static partial class Ipc +{ + public static class CreateTemporaryCollection + { + public const string Label = $"Penumbra.{nameof( CreateTemporaryCollection )}"; + + public static FuncProvider< string, string, bool, (PenumbraApiEc, string) > Provider( DalamudPluginInterface pi, + Func< string, string, bool, (PenumbraApiEc, string) > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, bool, (PenumbraApiEc, string) > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class RemoveTemporaryCollection + { + public const string Label = $"Penumbra.{nameof( RemoveTemporaryCollection )}"; + + public static FuncProvider< string, PenumbraApiEc > Provider( DalamudPluginInterface pi, + Func< string, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, PenumbraApiEc > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class AddTemporaryModAll + { + public const string Label = $"Penumbra.{nameof( AddTemporaryModAll )}"; + + public static FuncProvider< string, Dictionary< string, string >, string, int, PenumbraApiEc > Provider( + DalamudPluginInterface pi, Func< string, Dictionary< string, string >, string, int, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, Dictionary< string, string >, string, int, PenumbraApiEc > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class AddTemporaryMod + { + public const string Label = $"Penumbra.{nameof( AddTemporaryMod )}"; + + public static FuncProvider< string, string, Dictionary< string, string >, string, int, PenumbraApiEc > Provider( + DalamudPluginInterface pi, Func< string, string, Dictionary< string, string >, string, int, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, Dictionary< string, string >, string, int, PenumbraApiEc > Subscriber( + DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class RemoveTemporaryModAll + { + public const string Label = $"Penumbra.{nameof( RemoveTemporaryModAll )}"; + + public static FuncProvider< string, int, PenumbraApiEc > Provider( + DalamudPluginInterface pi, Func< string, int, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, int, PenumbraApiEc > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } + + public static class RemoveTemporaryMod + { + public const string Label = $"Penumbra.{nameof( RemoveTemporaryMod )}"; + + public static FuncProvider< string, string, int, PenumbraApiEc > Provider( + DalamudPluginInterface pi, Func< string, string, int, PenumbraApiEc > func ) + => new(pi, Label, func); + + public static FuncSubscriber< string, string, int, PenumbraApiEc > Subscriber( DalamudPluginInterface pi ) + => new(pi, Label); + } +} \ No newline at end of file diff --git a/tmp/Ipc/Ui.cs b/tmp/Ipc/Ui.cs new file mode 100644 index 00000000..d88d6718 --- /dev/null +++ b/tmp/Ipc/Ui.cs @@ -0,0 +1,55 @@ +using System; +using Dalamud.Plugin; +using Penumbra.Api.Enums; +using Penumbra.Api.Helpers; + +namespace Penumbra.Api; + +public static partial class Ipc +{ + public static class PreSettingsDraw + { + public const string Label = $"Penumbra.{nameof( PreSettingsDraw )}"; + + public static EventProvider< string > Provider( DalamudPluginInterface pi, Action< Action< string > > sub, + Action< Action< string > > unsub ) + => new(pi, Label, ( sub, unsub )); + + public static EventSubscriber< string > Subscriber( DalamudPluginInterface pi, params Action< string >[] actions ) + => new(pi, Label, actions); + } + + public static class PostSettingsDraw + { + public const string Label = $"Penumbra.{nameof( PostSettingsDraw )}"; + + public static EventProvider< string > Provider( DalamudPluginInterface pi, Action< Action< string > > sub, + Action< Action< string > > unsub ) + => new(pi, Label, ( sub, unsub )); + + public static EventSubscriber< string > Subscriber( DalamudPluginInterface pi, params Action< string >[] actions ) + => new(pi, Label, actions); + } + + public static class ChangedItemTooltip + { + public const string Label = $"Penumbra.{nameof( ChangedItemTooltip )}"; + + public static EventProvider< ChangedItemType, uint > Provider( DalamudPluginInterface pi, Action add, Action del ) + => new(pi, Label, add, del); + + public static EventSubscriber< ChangedItemType, uint > Subscriber( DalamudPluginInterface pi, params Action< ChangedItemType, uint >[] actions ) + => new(pi, Label, actions); + } + + public static class ChangedItemClick + { + public const string Label = $"Penumbra.{nameof( ChangedItemClick )}"; + + public static EventProvider< MouseButton, ChangedItemType, uint > Provider( DalamudPluginInterface pi, Action add, Action del ) + => new(pi, Label, add, del); + + public static EventSubscriber< MouseButton, ChangedItemType, uint > Subscriber( DalamudPluginInterface pi, params Action< MouseButton, ChangedItemType, uint >[] actions ) + => new(pi, Label, actions); + } +} \ No newline at end of file diff --git a/tmp/Penumbra.Api.csproj b/tmp/Penumbra.Api.csproj new file mode 100644 index 00000000..8962883b --- /dev/null +++ b/tmp/Penumbra.Api.csproj @@ -0,0 +1,47 @@ + + + net6.0-windows + preview + x64 + Penumbra.Api + absolute gangstas + Penumbra + Copyright © 2022 + 1.0.0.0 + 1.0.0.0 + bin\$(Configuration)\ + true + enable + true + false + false + + + + full + DEBUG;TRACE + + + + pdbonly + + + + $(MSBuildWarningsAsMessages);MSB3277 + + + + $(AppData)\XIVLauncher\addon\Hooks\dev\ + + + + + $(DalamudLibPath)Dalamud.dll + False + + + $(DalamudLibPath)Lumina.dll + False + + + diff --git a/tmp/Penumbra.Api.csproj.DotSettings b/tmp/Penumbra.Api.csproj.DotSettings new file mode 100644 index 00000000..7d7508cb --- /dev/null +++ b/tmp/Penumbra.Api.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/tmp/README.md b/tmp/README.md new file mode 100644 index 00000000..1e9bdf1a --- /dev/null +++ b/tmp/README.md @@ -0,0 +1,4 @@ +# Penumbra + +This is an auxiliary repository for Penumbras external API. +For more information, see the [main repo](https://github.com/xivdev/Penumbra). \ No newline at end of file