From 1b4eea4d1ec8bd0fd5f93a12c6cb94061f94050f Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Wed, 22 Jun 2022 10:19:11 +0200 Subject: [PATCH] Add IPC Test stuff, only temp missing. --- OtterGui | 2 +- Penumbra/Api/IPenumbraApi.cs | 11 +- Penumbra/Api/IpcTester.cs | 726 +++++++++++++++++++++++++++ Penumbra/Api/PenumbraApi.cs | 18 +- Penumbra/Api/PenumbraIpc.cs | 54 +- Penumbra/UI/ConfigWindow.DebugTab.cs | 90 +--- 6 files changed, 755 insertions(+), 146 deletions(-) create mode 100644 Penumbra/Api/IpcTester.cs diff --git a/OtterGui b/OtterGui index fa833869..03934d3a 160000 --- a/OtterGui +++ b/OtterGui @@ -1 +1 @@ -Subproject commit fa83386909ad0034f5ed7ea90d8bcedf6e8ba748 +Subproject commit 03934d3a19cb610898412045ad5ea7dad9766a59 diff --git a/Penumbra/Api/IPenumbraApi.cs b/Penumbra/Api/IPenumbraApi.cs index 9ba916d4..01ecc868 100644 --- a/Penumbra/Api/IPenumbraApi.cs +++ b/Penumbra/Api/IPenumbraApi.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using Dalamud.Configuration; -using Dalamud.Game.ClientState.Objects.Types; using Lumina.Data; using Penumbra.GameData.Enums; using Penumbra.Mods; @@ -41,8 +39,8 @@ public interface IPenumbraApi : IPenumbraApiBase // Obtain the currently set mod directory from the configuration. public string GetModDirectory(); - // Obtain the entire current penumbra configuration. - public IPluginConfiguration GetConfiguration(); + // Obtain the entire current penumbra configuration as a json encoded string. + public string GetConfiguration(); // Triggered when the user hovers over a listed changed object in a mod tab. // Can be used to append tooltips. @@ -58,9 +56,6 @@ public interface IPenumbraApi : IPenumbraApiBase // 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 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 all currently available actors with the given RedrawType. public void RedrawAll( RedrawType setting ); @@ -73,7 +68,7 @@ public interface IPenumbraApi : IPenumbraApiBase public string ResolvePath( string gamePath, string characterName ); // Reverse resolves a given modded local path into its replacement in form of all applicable game path for given character - public IList< string > ReverseResolvePath( string moddedPath, string characterName ); + public IList ReverseResolvePath( string moddedPath, string characterName ); // Try to load a given gamePath with the resolved path from Penumbra. public T? GetFile< T >( string gamePath ) where T : FileResource; diff --git a/Penumbra/Api/IpcTester.cs b/Penumbra/Api/IpcTester.cs new file mode 100644 index 00000000..069d4c3e --- /dev/null +++ b/Penumbra/Api/IpcTester.cs @@ -0,0 +1,726 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Numerics; +using System.Reflection; +using Dalamud.Interface; +using Dalamud.Logging; +using Dalamud.Plugin; +using Dalamud.Plugin.Ipc; +using ImGuiNET; +using OtterGui; +using OtterGui.Raii; +using Penumbra.GameData.ByteString; +using Penumbra.GameData.Enums; +using Penumbra.Mods; +using Penumbra.Util; + +namespace Penumbra.Api; + +public class IpcTester : IDisposable +{ + private readonly PenumbraIpc _ipc; + private readonly DalamudPluginInterface _pi; + + private readonly ICallGateSubscriber< object? > _initialized; + private readonly ICallGateSubscriber< object? > _disposed; + private readonly ICallGateSubscriber< IntPtr, int, object? > _redrawn; + + private readonly List< DateTimeOffset > _initializedList = new(); + private readonly List< DateTimeOffset > _disposedList = new(); + + public IpcTester( DalamudPluginInterface pi, PenumbraIpc ipc ) + { + _ipc = ipc; + _pi = pi; + _initialized = _pi.GetIpcSubscriber< object? >( PenumbraIpc.LabelProviderInitialized ); + _disposed = _pi.GetIpcSubscriber< object? >( PenumbraIpc.LabelProviderDisposed ); + _redrawn = _pi.GetIpcSubscriber< IntPtr, int, object? >( PenumbraIpc.LabelProviderGameObjectRedrawn ); + _initialized.Subscribe( AddInitialized ); + _disposed.Subscribe( AddDisposed ); + _redrawn.Subscribe( SetLastRedrawn ); + } + + public void Dispose() + { + _initialized.Unsubscribe( AddInitialized ); + _disposed.Unsubscribe( AddDisposed ); + _redrawn.Subscribe( SetLastRedrawn ); + _tooltip?.Unsubscribe( AddedTooltip ); + _click?.Unsubscribe( AddedClick ); + } + + private void AddInitialized() + => _initializedList.Add( DateTimeOffset.UtcNow ); + + private void AddDisposed() + => _disposedList.Add( DateTimeOffset.UtcNow ); + + public void Draw() + { + try + { + DrawAvailable(); + DrawGeneral(); + DrawResolve(); + DrawRedraw(); + DrawChangedItems(); + DrawData(); + DrawSetting(); + DrawTemp(); + DrawTempCollections(); + DrawTempMods(); + } + catch( Exception e ) + { + PluginLog.Error( $"Error during IPC Tests:\n{e}" ); + } + } + + private void DrawAvailable() + { + using var _ = ImRaii.TreeNode( "Availability" ); + if( !_ ) + { + return; + } + + ImGui.TextUnformatted( $"API Version: {_ipc.Api.ApiVersion}" ); + 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 ); + } + } + } + + private static void DrawIntro( string label, string info ) + { + ImGui.TableNextColumn(); + ImGui.TextUnformatted( label ); + ImGui.TableNextColumn(); + ImGui.TextUnformatted( info ); + ImGui.TableNextColumn(); + } + + private string _currentConfiguration = string.Empty; + + private void DrawGeneral() + { + using var _ = ImRaii.TreeNode( "General IPC" ); + if( !_ ) + { + return; + } + + using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit ); + + void DrawList( string label, string text, List< DateTimeOffset > list ) + { + DrawIntro( label, text ); + if( list.Count == 0 ) + { + 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( PenumbraIpc.LabelProviderInitialized, "Last Initialized", _initializedList ); + DrawList( PenumbraIpc.LabelProviderDisposed, "Last Disposed", _disposedList ); + DrawIntro( PenumbraIpc.LabelProviderApiVersion, "Current Version" ); + ImGui.TextUnformatted( _pi.GetIpcSubscriber< int >( PenumbraIpc.LabelProviderApiVersion ).InvokeFunc().ToString() ); + DrawIntro( PenumbraIpc.LabelProviderGetModDirectory, "Current Mod Directory" ); + ImGui.TextUnformatted( _pi.GetIpcSubscriber< string >( PenumbraIpc.LabelProviderGetModDirectory ).InvokeFunc() ); + DrawIntro( PenumbraIpc.LabelProviderGetConfiguration, "Configuration" ); + if( ImGui.Button( "Get" ) ) + { + _currentConfiguration = _pi.GetIpcSubscriber< string >( PenumbraIpc.LabelProviderGetConfiguration ).InvokeFunc(); + ImGui.OpenPopup( "Config Popup" ); + } + + 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(); + } + } + } + + 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 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; + } + + 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.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.LabelProviderReverseResolvePath, "Reversed Game Paths" ); + if( _currentReversePath.Length > 0 ) + { + var list = _pi.GetIpcSubscriber< string, string, string[] >( PenumbraIpc.LabelProviderReverseResolvePath ) + .InvokeFunc( _currentReversePath, _currentResolveCharacter ); + if( list.Length > 0 ) + { + ImGui.TextUnformatted( list[ 0 ] ); + if( list.Length > 1 && ImGui.IsItemHovered() ) + { + ImGui.SetTooltip( string.Join( "\n", list.Skip( 1 ) ) ); + } + } + } + } + + private string _redrawName = string.Empty; + private int _redrawIndex = 0; + private string _lastRedrawnString = "None"; + + 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 void DrawRedraw() + { + using var _ = ImRaii.TreeNode( "Redraw IPC" ); + if( !_ ) + { + 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.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 ) + { + _tooltip.Subscribe( AddedTooltip ); + } + else + { + _tooltip.Unsubscribe( AddedTooltip ); + } + } + + ImGui.SameLine(); + ImGui.TextUnformatted( _lastHovered ); + + DrawIntro( PenumbraIpc.LabelProviderChangedItemClick, "Subscribe Click" ); + if( ImGui.Checkbox( "##click", ref _subscribedToClick ) ) + { + _click = _pi.GetIpcSubscriber< MouseButton, ChangedItemType, uint, object? >( PenumbraIpc.LabelProviderChangedItemClick ); + if( _subscribedToClick ) + { + _click.Subscribe( AddedClick ); + } + else + { + _click.Unsubscribe( AddedClick ); + } + } + + 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" ) ) + { + _changedItems = _pi.GetIpcSubscriber< string, IReadOnlyDictionary< string, object? > >( PenumbraIpc.LabelProviderGetChangedItems ) + .InvokeFunc( _changedItemCollection ); + ImGui.OpenPopup( "Changed Item List" ); + } + + ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); + using var p = ImRaii.Popup( "Changed Item List" ); + if( p ) + { + foreach( var item in _changedItems ) + { + ImGui.TextUnformatted( item.Key ); + } + + if( ImGui.Button( "Close", -Vector2.UnitX ) || ImGui.IsWindowFocused() ) + { + ImGui.CloseCurrentPopup(); + } + } + } + + private void AddedTooltip( ChangedItemType type, uint id ) + { + _lastHovered = $"{type} {id} at {DateTime.UtcNow.ToLocalTime().ToString( CultureInfo.CurrentCulture )}"; + ImGui.TextUnformatted( "IPC Test Successful" ); + } + + private void AddedClick( MouseButton button, ChangedItemType type, uint id ) + { + _lastClicked = $"{button}-click on {type} {id} at {DateTime.UtcNow.ToLocalTime().ToString( CultureInfo.CurrentCulture )}"; + } + + 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; + + private void DrawData() + { + using var _ = ImRaii.TreeNode( "Data IPC" ); + if( !_ ) + { + 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.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" ); + } + + ImGui.SetNextWindowSize( ImGuiHelpers.ScaledVector2( 500, 500 ) ); + using var p = ImRaii.Popup( "Ipc Data" ); + if( p ) + { + if( _collectionMode ) + { + foreach( var collection in _collections ) + { + ImGui.TextUnformatted( collection ); + } + } + else + { + 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 _lastError = PenumbraApiEc.Success; + + + private void DrawSetting() + { + using var _ = ImRaii.TreeNode( "Settings IPC" ); + if( !_ ) + { + return; + } + + 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 ) + { + return; + } + + DrawIntro( "Last Error", _lastError.ToString() ); + + 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 ); + _lastError = _availableSettings == null ? PenumbraApiEc.ModMissing : PenumbraApiEc.Success; + } + + 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 ); + _lastError = 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( PenumbraIpc.LabelProviderTryInheritMod, "Inherit Mod" ); + ImGui.Checkbox( "##inherit", ref _settingsInherit ); + ImGui.SameLine(); + if( ImGui.Button( "Set##Inherit" ) ) + { + _lastError = _pi.GetIpcSubscriber< string, string, string, bool, PenumbraApiEc >( PenumbraIpc.LabelProviderTryInheritMod ) + .InvokeFunc( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsInherit ); + } + + DrawIntro( PenumbraIpc.LabelProviderTrySetMod, "Set Enabled" ); + ImGui.Checkbox( "##enabled", ref _settingsEnabled ); + ImGui.SameLine(); + if( ImGui.Button( "Set##Enabled" ) ) + { + _lastError = _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" ) ) + { + _lastError = _pi.GetIpcSubscriber< string, string, string, int, PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetModPriority ) + .InvokeFunc( _settingsCollection, _settingsModDirectory, _settingsModName, _settingsPriority ); + } + + DrawIntro( PenumbraIpc.LabelProviderTrySetModSetting, "Set Setting(s)" ); + if( _availableSettings != null ) + { + foreach( var (group, (list, type)) in _availableSettings ) + { + using var id = ImRaii.PushId( group ); + var preview = list.Count > 0 ? list[ 0 ] : string.Empty; + IList< string > current; + if( _currentSettings != null && _currentSettings.TryGetValue( group, out current! ) && current.Count > 0 ) + { + preview = current[ 0 ]; + } + else + { + current = new List< string >(); + if( _currentSettings != null ) + { + _currentSettings[ group ] = current; + } + } + + ImGui.SetNextItemWidth( 200 * ImGuiHelpers.GlobalScale ); + using( var c = ImRaii.Combo( "##group", preview ) ) + { + if( c ) + { + foreach( var s in list ) + { + var contained = current.Contains( s ); + if( ImGui.Checkbox( s, ref contained ) ) + { + if( contained ) + { + current.Add( s ); + } + else + { + current.Remove( s ); + } + } + } + } + } + + ImGui.SameLine(); + if( ImGui.Button( "Set##setting" ) ) + { + if( type == SelectType.Single ) + { + _lastError = _pi + .GetIpcSubscriber< string, string, string, string, string, + PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetModSetting ).InvokeFunc( _settingsCollection, + _settingsModDirectory, _settingsModName, group, current.Count > 0 ? current[ 0 ] : string.Empty ); + } + else + { + _lastError = _pi + .GetIpcSubscriber< string, string, string, string, IReadOnlyList< string >, + PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetModSettings ).InvokeFunc( _settingsCollection, + _settingsModDirectory, _settingsModName, group, current.ToArray() ); + } + } + ImGui.SameLine(); + ImGui.TextUnformatted( group ); + } + } + } + + private void DrawTemp() + { + using var _ = ImRaii.TreeNode( "Temp IPC" ); + if( !_ ) + { + return; + } + } + + private void DrawTempCollections() + { + using var collTree = ImRaii.TreeNode( "Collections" ); + if( !collTree ) + { + return; + } + + using var table = ImRaii.Table( "##collTree", 4 ); + 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" ); + } + } + + private void DrawTempMods() + { + using var modTree = ImRaii.TreeNode( "Mods" ); + if( !modTree ) + { + 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 ); + } + } + } +} \ No newline at end of file diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 99898ba5..0cbf419e 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -55,10 +55,10 @@ public class PenumbraApi : IDisposable, IPenumbraApi return Penumbra.Config.ModDirectory; } - public IPluginConfiguration GetConfiguration() + public string GetConfiguration() { CheckInitialized(); - return JsonConvert.DeserializeObject< Configuration >( JsonConvert.SerializeObject( Penumbra.Config ) ); + return JsonConvert.SerializeObject( Penumbra.Config, Formatting.Indented ); } public event ChangedItemHover? ChangedItemTooltip; @@ -75,12 +75,6 @@ public class PenumbraApi : IDisposable, IPenumbraApi _penumbra!.ObjectReloader.RedrawObject( name, setting ); } - public void RedrawObject( GameObject? gameObject, RedrawType setting ) - { - CheckInitialized(); - _penumbra!.ObjectReloader.RedrawObject( gameObject, setting ); - } - public void RedrawAll( RedrawType setting ) { CheckInitialized(); @@ -299,11 +293,6 @@ public class PenumbraApi : IDisposable, IPenumbraApi IReadOnlyList< string > optionNames ) { CheckInitialized(); - if( optionNames.Count == 0 ) - { - return PenumbraApiEc.InvalidArgument; - } - if( !Penumbra.CollectionManager.ByName( collectionName, out var collection ) ) { return PenumbraApiEc.CollectionMissing; @@ -325,8 +314,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi uint setting = 0; if( group.Type == SelectType.Single ) { - var name = optionNames[ ^1 ]; - var optionIdx = group.IndexOf( o => o.Name == optionNames[ ^1 ] ); + var optionIdx = optionNames.Count == 0 ? -1 : group.IndexOf( o => o.Name == optionNames[ ^1 ] ); if( optionIdx < 0 ) { return PenumbraApiEc.OptionMissing; diff --git a/Penumbra/Api/PenumbraIpc.cs b/Penumbra/Api/PenumbraIpc.cs index 1aa4ce24..d146f0c3 100644 --- a/Penumbra/Api/PenumbraIpc.cs +++ b/Penumbra/Api/PenumbraIpc.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Dalamud.Configuration; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Logging; using Dalamud.Plugin; @@ -12,11 +11,12 @@ namespace Penumbra.Api; public partial class PenumbraIpc : IDisposable { internal readonly IPenumbraApi Api; + internal readonly IpcTester Tester; public PenumbraIpc( DalamudPluginInterface pi, IPenumbraApi api ) { - Api = api; - + Api = api; + Tester = new IpcTester( pi, this ); InitializeGeneralProviders( pi ); InitializeResolveProviders( pi ); InitializeRedrawProviders( pi ); @@ -37,6 +37,7 @@ public partial class PenumbraIpc : IDisposable DisposeSettingProviders(); DisposeTempProviders(); ProviderDisposed?.SendMessage(); + Tester.Dispose(); } } @@ -48,11 +49,11 @@ public partial class PenumbraIpc public const string LabelProviderGetModDirectory = "Penumbra.GetModDirectory"; public const string LabelProviderGetConfiguration = "Penumbra.GetConfiguration"; - internal ICallGateProvider< object? >? ProviderInitialized; - internal ICallGateProvider< object? >? ProviderDisposed; - internal ICallGateProvider< int >? ProviderApiVersion; - internal ICallGateProvider< string >? ProviderGetModDirectory; - internal ICallGateProvider< IPluginConfiguration >? ProviderGetConfiguration; + internal ICallGateProvider< object? >? ProviderInitialized; + internal ICallGateProvider< object? >? ProviderDisposed; + internal ICallGateProvider< int >? ProviderApiVersion; + internal ICallGateProvider< string >? ProviderGetModDirectory; + internal ICallGateProvider< string >? ProviderGetConfiguration; private void InitializeGeneralProviders( DalamudPluginInterface pi ) { @@ -96,7 +97,7 @@ public partial class PenumbraIpc try { - ProviderGetConfiguration = pi.GetIpcProvider< IPluginConfiguration >( LabelProviderGetConfiguration ); + ProviderGetConfiguration = pi.GetIpcProvider< string >( LabelProviderGetConfiguration ); ProviderGetConfiguration.RegisterFunc( Api.GetConfiguration ); } catch( Exception e ) @@ -117,15 +118,13 @@ public partial class PenumbraIpc { public const string LabelProviderRedrawName = "Penumbra.RedrawObjectByName"; public const string LabelProviderRedrawIndex = "Penumbra.RedrawObjectByIndex"; - public const string LabelProviderRedrawObject = "Penumbra.RedrawObject"; public const string LabelProviderRedrawAll = "Penumbra.RedrawAll"; public const string LabelProviderGameObjectRedrawn = "Penumbra.GameObjectRedrawn"; - internal ICallGateProvider< string, int, object >? ProviderRedrawName; - internal ICallGateProvider< int, int, object >? ProviderRedrawIndex; - internal ICallGateProvider< GameObject, int, object >? ProviderRedrawObject; - internal ICallGateProvider< int, object >? ProviderRedrawAll; - internal ICallGateProvider< IntPtr, int, object? >? ProviderGameObjectRedrawn; + internal ICallGateProvider< string, int, object? >? ProviderRedrawName; + internal ICallGateProvider< int, int, object? >? ProviderRedrawIndex; + internal ICallGateProvider< int, object? >? ProviderRedrawAll; + internal ICallGateProvider< IntPtr, int, object? >? ProviderGameObjectRedrawn; private static RedrawType CheckRedrawType( int value ) { @@ -142,7 +141,7 @@ public partial class PenumbraIpc { try { - ProviderRedrawName = pi.GetIpcProvider< string, int, object >( LabelProviderRedrawName ); + ProviderRedrawName = pi.GetIpcProvider< string, int, object? >( LabelProviderRedrawName ); ProviderRedrawName.RegisterAction( ( s, i ) => Api.RedrawObject( s, CheckRedrawType( i ) ) ); } catch( Exception e ) @@ -152,7 +151,7 @@ public partial class PenumbraIpc try { - ProviderRedrawIndex = pi.GetIpcProvider< int, int, object >( LabelProviderRedrawIndex ); + ProviderRedrawIndex = pi.GetIpcProvider< int, int, object? >( LabelProviderRedrawIndex ); ProviderRedrawIndex.RegisterAction( ( idx, i ) => Api.RedrawObject( idx, CheckRedrawType( i ) ) ); } catch( Exception e ) @@ -162,17 +161,7 @@ public partial class PenumbraIpc try { - ProviderRedrawObject = pi.GetIpcProvider< GameObject, int, object >( LabelProviderRedrawObject ); - ProviderRedrawObject.RegisterAction( ( o, i ) => Api.RedrawObject( o, CheckRedrawType( i ) ) ); - } - catch( Exception e ) - { - PluginLog.Error( $"Error registering IPC provider for {LabelProviderRedrawObject}:\n{e}" ); - } - - try - { - ProviderRedrawAll = pi.GetIpcProvider< int, object >( LabelProviderRedrawAll ); + ProviderRedrawAll = pi.GetIpcProvider< int, object? >( LabelProviderRedrawAll ); ProviderRedrawAll.RegisterAction( i => Api.RedrawAll( CheckRedrawType( i ) ) ); } catch( Exception e ) @@ -198,7 +187,6 @@ public partial class PenumbraIpc { ProviderRedrawName?.UnregisterAction(); ProviderRedrawIndex?.UnregisterAction(); - ProviderRedrawObject?.UnregisterAction(); ProviderRedrawAll?.UnregisterAction(); Api.GameObjectRedrawn -= OnGameObjectRedrawn; } @@ -274,8 +262,8 @@ public partial class PenumbraIpc 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< 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 ) @@ -294,7 +282,7 @@ public partial class PenumbraIpc { try { - ProviderChangedItemTooltip = pi.GetIpcProvider< ChangedItemType, uint, object >( LabelProviderChangedItemTooltip ); + ProviderChangedItemTooltip = pi.GetIpcProvider< ChangedItemType, uint, object? >( LabelProviderChangedItemTooltip ); Api.ChangedItemTooltip += OnTooltip; } catch( Exception e ) @@ -304,7 +292,7 @@ public partial class PenumbraIpc try { - ProviderChangedItemClick = pi.GetIpcProvider< MouseButton, ChangedItemType, uint, object >( LabelProviderChangedItemClick ); + ProviderChangedItemClick = pi.GetIpcProvider< MouseButton, ChangedItemType, uint, object? >( LabelProviderChangedItemClick ); Api.ChangedItemClicked += OnClick; } catch( Exception e ) diff --git a/Penumbra/UI/ConfigWindow.DebugTab.cs b/Penumbra/UI/ConfigWindow.DebugTab.cs index ebd1b67f..aa58ad32 100644 --- a/Penumbra/UI/ConfigWindow.DebugTab.cs +++ b/Penumbra/UI/ConfigWindow.DebugTab.cs @@ -404,95 +404,7 @@ public partial class ConfigWindow { return; } - - var ipc = _window._penumbra.Ipc; - ImGui.TextUnformatted( $"API Version: {ipc.Api.ApiVersion}" ); - 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 ); - } - } - - using( var collTree = ImRaii.TreeNode( "Collections" ) ) - { - if( collTree ) - { - using var table = ImRaii.Table( "##collTree", 4 ); - if( table ) - { - 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" ); - } - } - } - } - - using( var modTree = ImRaii.TreeNode( "Mods" ) ) - { - if( modTree ) - { - 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 ); - } - } - } - } + _window._penumbra.Ipc.Tester.Draw(); } // Helper to print a property and its value in a 2-column table.