mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Merge branch 'xivdev:master' into master
This commit is contained in:
commit
809422e8d3
9 changed files with 914 additions and 203 deletions
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
|||
Subproject commit fa83386909ad0034f5ed7ea90d8bcedf6e8ba748
|
||||
Subproject commit 03934d3a19cb610898412045ad5ea7dad9766a59
|
||||
|
|
@ -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 );
|
||||
|
||||
|
|
@ -149,13 +144,12 @@ public interface IPenumbraApi : IPenumbraApiBase
|
|||
|
||||
// Set a temporary mod with the given paths, manipulations and priority and the name tag to all collections.
|
||||
// Can return Okay, InvalidGamePath, or InvalidManipulation.
|
||||
public PenumbraApiEc AddTemporaryModAll( string tag, IReadOnlyDictionary< string, string > paths, IReadOnlySet< string > manipCodes,
|
||||
public PenumbraApiEc AddTemporaryModAll( string tag, Dictionary< string, string > paths, HashSet< string > manipCodes,
|
||||
int priority );
|
||||
|
||||
// Set a temporary mod with the given paths, manipulations and priority and the name tag to the collection with the given name, which can be temporary.
|
||||
// Can return Okay, MissingCollection InvalidGamePath, or InvalidManipulation.
|
||||
public PenumbraApiEc AddTemporaryMod( string tag, string collectionName, IReadOnlyDictionary< string, string > paths,
|
||||
IReadOnlySet< string > manipCodes,
|
||||
public PenumbraApiEc AddTemporaryMod( string tag, string collectionName, Dictionary< string, string > paths, HashSet< string > manipCodes,
|
||||
int priority );
|
||||
|
||||
// Remove the temporary mod with the given tag and priority from the temporary mods applying to all collections, if it exists.
|
||||
|
|
|
|||
805
Penumbra/Api/IpcTester.cs
Normal file
805
Penumbra/Api/IpcTester.cs
Normal file
|
|
@ -0,0 +1,805 @@
|
|||
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 _lastSettingsError = 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", _lastSettingsError.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 );
|
||||
_lastSettingsError = _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 );
|
||||
_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( 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( 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( 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 )
|
||||
{
|
||||
_lastSettingsError = _pi
|
||||
.GetIpcSubscriber< string, string, string, string, string,
|
||||
PenumbraApiEc >( PenumbraIpc.LabelProviderTrySetModSetting ).InvokeFunc( _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() );
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted( group );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
using var _ = ImRaii.TreeNode( "Temp IPC" );
|
||||
if( !_ )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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( 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 );
|
||||
}
|
||||
|
||||
DrawIntro( PenumbraIpc.LabelProviderRemoveTemporaryCollection, "Remove Temporary Collection from Character" );
|
||||
if( ImGui.Button( "Delete##Collection" ) )
|
||||
{
|
||||
_lastTempError = _pi.GetIpcSubscriber< string, PenumbraApiEc >( PenumbraIpc.LabelProviderRemoveTemporaryCollection )
|
||||
.InvokeFunc( _tempCharacterName );
|
||||
}
|
||||
|
||||
DrawIntro( PenumbraIpc.LabelProviderAddTemporaryMod, "Add Temporary Mod to specific Collection" );
|
||||
if( ImGui.Button( "Add##Mod" ) )
|
||||
{
|
||||
_lastTempError = _pi
|
||||
.GetIpcSubscriber< string, string, IReadOnlyDictionary< string, string >, IReadOnlySet< string >, int, PenumbraApiEc >(
|
||||
PenumbraIpc.LabelProviderAddTemporaryMod )
|
||||
.InvokeFunc( _tempModName, _tempCollectionName,
|
||||
new Dictionary< string, string > { { _tempGamePath, _tempFilePath } },
|
||||
_tempManipulation.Length > 0 ? new HashSet< string > { _tempManipulation } : new HashSet< string >(), int.MaxValue );
|
||||
}
|
||||
|
||||
DrawIntro( PenumbraIpc.LabelProviderAddTemporaryModAll, "Add Temporary Mod to all Collections" );
|
||||
if( ImGui.Button( "Add##All" ) )
|
||||
{
|
||||
_lastTempError = _pi
|
||||
.GetIpcSubscriber< string, IReadOnlyDictionary< string, string >, IReadOnlySet< string >, int, PenumbraApiEc >(
|
||||
PenumbraIpc.LabelProviderAddTemporaryModAll )
|
||||
.InvokeFunc( _tempModName, new Dictionary< string, string > { { _tempGamePath, _tempFilePath } },
|
||||
_tempManipulation.Length > 0 ? new HashSet< string > { _tempManipulation } : new HashSet< string >(), 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", 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,6 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Dalamud.Configuration;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Logging;
|
||||
using Lumina.Data;
|
||||
using Newtonsoft.Json;
|
||||
|
|
@ -22,7 +20,7 @@ namespace Penumbra.Api;
|
|||
public class PenumbraApi : IDisposable, IPenumbraApi
|
||||
{
|
||||
public int ApiVersion
|
||||
=> 4;
|
||||
=> 5;
|
||||
|
||||
private Penumbra? _penumbra;
|
||||
private Lumina.GameData? _lumina;
|
||||
|
|
@ -55,10 +53,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 +73,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 +291,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 +312,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;
|
||||
|
|
@ -354,6 +340,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
public (PenumbraApiEc, string) CreateTemporaryCollection( string tag, string character, bool forceOverwriteCharacter )
|
||||
{
|
||||
CheckInitialized();
|
||||
|
||||
if( character.Length is 0 or > 32 || tag.Length == 0 )
|
||||
{
|
||||
return ( PenumbraApiEc.InvalidArgument, string.Empty );
|
||||
}
|
||||
|
||||
if( !forceOverwriteCharacter && Penumbra.CollectionManager.Characters.ContainsKey( character )
|
||||
|| Penumbra.TempMods.Collections.ContainsKey( character ) )
|
||||
{
|
||||
|
|
@ -376,8 +368,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
return PenumbraApiEc.Success;
|
||||
}
|
||||
|
||||
public PenumbraApiEc AddTemporaryModAll( string tag, IReadOnlyDictionary< string, string > paths, IReadOnlySet< string > manipCodes,
|
||||
int priority )
|
||||
public PenumbraApiEc AddTemporaryModAll( string tag, Dictionary< string, string > paths, HashSet< string > manipCodes, int priority )
|
||||
{
|
||||
CheckInitialized();
|
||||
if( !ConvertPaths( paths, out var p ) )
|
||||
|
|
@ -397,11 +388,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
};
|
||||
}
|
||||
|
||||
public PenumbraApiEc AddTemporaryMod( string tag, string collectionName, IReadOnlyDictionary< string, string > paths,
|
||||
IReadOnlySet< string > manipCodes, int priority )
|
||||
public PenumbraApiEc AddTemporaryMod( string tag, string collectionName, Dictionary< string, string > paths, HashSet< string > manipCodes,
|
||||
int priority )
|
||||
{
|
||||
CheckInitialized();
|
||||
if( !Penumbra.TempMods.Collections.TryGetValue( collectionName, out var collection )
|
||||
if( !Penumbra.TempMods.Collections.Values.FindFirst( c => c.Name == collectionName, out var collection )
|
||||
&& !Penumbra.CollectionManager.ByName( collectionName, out collection ) )
|
||||
{
|
||||
return PenumbraApiEc.CollectionMissing;
|
||||
|
|
@ -438,7 +429,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
public PenumbraApiEc RemoveTemporaryMod( string tag, string collectionName, int priority )
|
||||
{
|
||||
CheckInitialized();
|
||||
if( !Penumbra.TempMods.Collections.TryGetValue( collectionName, out var collection )
|
||||
if( !Penumbra.TempMods.Collections.Values.FindFirst( c => c.Name == collectionName, out var collection )
|
||||
&& !Penumbra.CollectionManager.ByName( collectionName, out collection ) )
|
||||
{
|
||||
return PenumbraApiEc.CollectionMissing;
|
||||
|
|
@ -542,16 +533,19 @@ public class PenumbraApi : IDisposable, IPenumbraApi
|
|||
manips = new HashSet< MetaManipulation >( manipStrings.Count );
|
||||
foreach( var m in manipStrings )
|
||||
{
|
||||
if( Functions.FromCompressedBase64< MetaManipulation >( m, out var manip ) != MetaManipulation.CurrentVersion )
|
||||
if( Functions.FromCompressedBase64< MetaManipulation[] >( m, out var manipArray ) != MetaManipulation.CurrentVersion )
|
||||
{
|
||||
manips = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !manips.Add( manip ) )
|
||||
foreach( var manip in manipArray! )
|
||||
{
|
||||
manips = null;
|
||||
return false;
|
||||
if( !manips.Add( manip ) )
|
||||
{
|
||||
manips = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
|
|
@ -532,10 +520,10 @@ public partial class PenumbraIpc
|
|||
internal ICallGateProvider< string, string, bool, (PenumbraApiEc, string) >? ProviderCreateTemporaryCollection;
|
||||
internal ICallGateProvider< string, PenumbraApiEc >? ProviderRemoveTemporaryCollection;
|
||||
|
||||
internal ICallGateProvider< string, IReadOnlyDictionary< string, string >, IReadOnlySet< string >, int, PenumbraApiEc >?
|
||||
internal ICallGateProvider< string, Dictionary< string, string >, HashSet< string >, int, PenumbraApiEc >?
|
||||
ProviderAddTemporaryModAll;
|
||||
|
||||
internal ICallGateProvider< string, string, IReadOnlyDictionary< string, string >, IReadOnlySet< string >, int, PenumbraApiEc >?
|
||||
internal ICallGateProvider< string, string, Dictionary< string, string >, HashSet< string >, int, PenumbraApiEc >?
|
||||
ProviderAddTemporaryMod;
|
||||
|
||||
internal ICallGateProvider< string, int, PenumbraApiEc >? ProviderRemoveTemporaryModAll;
|
||||
|
|
@ -568,7 +556,7 @@ public partial class PenumbraIpc
|
|||
try
|
||||
{
|
||||
ProviderAddTemporaryModAll =
|
||||
pi.GetIpcProvider< string, IReadOnlyDictionary< string, string >, IReadOnlySet< string >, int, PenumbraApiEc >(
|
||||
pi.GetIpcProvider< string, Dictionary< string, string >, HashSet< string >, int, PenumbraApiEc >(
|
||||
LabelProviderAddTemporaryModAll );
|
||||
ProviderAddTemporaryModAll.RegisterFunc( Api.AddTemporaryModAll );
|
||||
}
|
||||
|
|
@ -580,7 +568,7 @@ public partial class PenumbraIpc
|
|||
try
|
||||
{
|
||||
ProviderAddTemporaryMod =
|
||||
pi.GetIpcProvider< string, string, IReadOnlyDictionary< string, string >, IReadOnlySet< string >, int, PenumbraApiEc >(
|
||||
pi.GetIpcProvider< string, string, Dictionary< string, string >, HashSet< string >, int, PenumbraApiEc >(
|
||||
LabelProviderAddTemporaryMod );
|
||||
ProviderAddTemporaryMod.RegisterFunc( Api.AddTemporaryMod );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ public class TempModManager
|
|||
return RedirectResult.NotRegistered;
|
||||
}
|
||||
|
||||
var removed = _modsForAllCollections.RemoveAll( m =>
|
||||
var removed = list.RemoveAll( m =>
|
||||
{
|
||||
if( m.Name != tag || priority != null && m.Priority != priority.Value )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using Penumbra.Collections;
|
||||
|
|
@ -10,16 +11,16 @@ public unsafe partial class PathResolver
|
|||
{
|
||||
private ModCollection? _animationLoadCollection;
|
||||
|
||||
public delegate byte LoadTimelineResourcesDelegate( IntPtr timeline );
|
||||
public delegate ulong LoadTimelineResourcesDelegate( IntPtr timeline );
|
||||
|
||||
// The timeline object loads the requested .tmb and .pap files. The .tmb files load the respective .avfx files.
|
||||
// We can obtain the associated game object from the timelines 28'th vfunc and use that to apply the correct collection.
|
||||
[Signature( "E8 ?? ?? ?? ?? 83 7F ?? ?? 75 ?? 0F B6 87", DetourName = nameof( LoadTimelineResourcesDetour ) )]
|
||||
public Hook< LoadTimelineResourcesDelegate >? LoadTimelineResourcesHook;
|
||||
|
||||
private byte LoadTimelineResourcesDetour( IntPtr timeline )
|
||||
private ulong LoadTimelineResourcesDetour( IntPtr timeline )
|
||||
{
|
||||
byte ret;
|
||||
ulong ret;
|
||||
var old = _animationLoadCollection;
|
||||
try
|
||||
{
|
||||
|
|
@ -59,16 +60,16 @@ public unsafe partial class PathResolver
|
|||
_animationLoadCollection = last;
|
||||
}
|
||||
|
||||
public delegate ulong LoadSomeAvfx( uint a1, IntPtr gameObject, IntPtr gameObject2 );
|
||||
public delegate ulong LoadSomeAvfx( uint a1, IntPtr gameObject, IntPtr gameObject2, float unk1, IntPtr unk2, IntPtr unk3 );
|
||||
|
||||
[Signature( "E8 ?? ?? ?? ?? 45 0F B6 F7", DetourName = nameof( LoadSomeAvfxDetour ) )]
|
||||
public Hook< LoadSomeAvfx >? LoadSomeAvfxHook;
|
||||
|
||||
private ulong LoadSomeAvfxDetour( uint a1, IntPtr gameObject, IntPtr gameObject2 )
|
||||
private ulong LoadSomeAvfxDetour( uint a1, IntPtr gameObject, IntPtr gameObject2, float unk1, IntPtr unk2, IntPtr unk3 )
|
||||
{
|
||||
var last = _animationLoadCollection;
|
||||
_animationLoadCollection = IdentifyCollection( ( GameObject* )gameObject );
|
||||
var ret = LoadSomeAvfxHook!.Original( a1, gameObject, gameObject2 );
|
||||
var ret = LoadSomeAvfxHook!.Original( a1, gameObject, gameObject2, unk1, unk2, unk3 );
|
||||
_animationLoadCollection = last;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
|
|
@ -277,42 +279,57 @@ public unsafe partial class PathResolver
|
|||
return Penumbra.CollectionManager.Default;
|
||||
}
|
||||
|
||||
// Housing Retainers
|
||||
if( Penumbra.Config.UseDefaultCollectionForRetainers
|
||||
&& gameObject->ObjectKind == ( byte )ObjectKind.EventNpc
|
||||
&& gameObject->DataID == 1011832 )
|
||||
try
|
||||
{
|
||||
// Housing Retainers
|
||||
if( Penumbra.Config.UseDefaultCollectionForRetainers
|
||||
&& gameObject->ObjectKind == ( byte )ObjectKind.EventNpc
|
||||
&& gameObject->DataID == 1011832 )
|
||||
{
|
||||
return Penumbra.CollectionManager.Default;
|
||||
}
|
||||
|
||||
string? actorName = null;
|
||||
if( Penumbra.Config.PreferNamedCollectionsOverOwners )
|
||||
{
|
||||
// Early return if we prefer the actors own name over its owner.
|
||||
actorName = new Utf8String( gameObject->Name ).ToString();
|
||||
if( actorName.Length > 0
|
||||
&& CollectionByActorName( actorName, out var actorCollection ) )
|
||||
{
|
||||
return actorCollection;
|
||||
}
|
||||
}
|
||||
|
||||
// All these special cases are relevant for an empty name, so never collide with the above setting.
|
||||
// Only OwnerName can be applied to something with a non-empty name, and that is the specific case we want to handle.
|
||||
var actualName = gameObject->ObjectIndex switch
|
||||
{
|
||||
240 => Penumbra.Config.UseCharacterCollectionInMainWindow ? GetPlayerName() : null, // character window
|
||||
241 => GetInspectName() ?? GetCardName() ?? GetGlamourName(), // inspect, character card, glamour plate editor.
|
||||
242 => Penumbra.Config.UseCharacterCollectionInTryOn ? GetPlayerName() : null, // try-on
|
||||
243 => Penumbra.Config.UseCharacterCollectionInTryOn ? GetPlayerName() : null, // dye preview
|
||||
>= 200 => GetCutsceneName( gameObject ),
|
||||
_ => null,
|
||||
}
|
||||
?? GetOwnerName( gameObject ) ?? actorName ?? new Utf8String( gameObject->Name ).ToString();
|
||||
|
||||
// First check temporary character collections, then the own configuration.
|
||||
return CollectionByActorName( actualName, out var c ) ? c : Penumbra.CollectionManager.Default;
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Error identifying collection:\n{e}" );
|
||||
return Penumbra.CollectionManager.Default;
|
||||
}
|
||||
|
||||
string? actorName = null;
|
||||
if( Penumbra.Config.PreferNamedCollectionsOverOwners )
|
||||
{
|
||||
// Early return if we prefer the actors own name over its owner.
|
||||
actorName = new Utf8String( gameObject->Name ).ToString();
|
||||
if( actorName.Length > 0 && Penumbra.CollectionManager.Characters.TryGetValue( actorName, out var actorCollection ) )
|
||||
{
|
||||
return actorCollection;
|
||||
}
|
||||
}
|
||||
|
||||
// All these special cases are relevant for an empty name, so never collide with the above setting.
|
||||
// Only OwnerName can be applied to something with a non-empty name, and that is the specific case we want to handle.
|
||||
var actualName = gameObject->ObjectIndex switch
|
||||
{
|
||||
240 => Penumbra.Config.UseCharacterCollectionInMainWindow ? GetPlayerName() : null, // character window
|
||||
241 => GetInspectName() ?? GetCardName() ?? GetGlamourName(), // inspect, character card, glamour plate editor.
|
||||
242 => Penumbra.Config.UseCharacterCollectionInTryOn ? GetPlayerName() : null, // try-on
|
||||
243 => Penumbra.Config.UseCharacterCollectionInTryOn ? GetPlayerName() : null, // dye preview
|
||||
>= 200 => GetCutsceneName( gameObject ),
|
||||
_ => null,
|
||||
}
|
||||
?? GetOwnerName( gameObject ) ?? actorName ?? new Utf8String( gameObject->Name ).ToString();
|
||||
|
||||
// First check temporary character collections, then the own configuration.
|
||||
return Penumbra.TempMods.Collections.TryGetValue(actualName, out var c) ? c : Penumbra.CollectionManager.Character( actualName );
|
||||
}
|
||||
|
||||
// Check both temporary and permanent character collections. Temporary first.
|
||||
private static bool CollectionByActorName( string name, [NotNullWhen( true )] out ModCollection? collection )
|
||||
=> Penumbra.TempMods.Collections.TryGetValue( name, out collection )
|
||||
|| Penumbra.CollectionManager.Characters.TryGetValue( name, out collection );
|
||||
|
||||
|
||||
// Update collections linked to Game/DrawObjects due to a change in collection configuration.
|
||||
private void CheckCollections( ModCollection.Type type, ModCollection? _1, ModCollection? _2, string? name )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue