Add object-specific IPC for resolving paths and meta.

This commit is contained in:
Ottermandias 2022-12-03 14:06:47 +01:00
parent f1b495dff4
commit ca51c3b107
6 changed files with 103 additions and 16 deletions

@ -1 +1 @@
Subproject commit 1a3f9d501ad44e020500eb7d0a79f91a04e46c93
Subproject commit 891eb195c29904824004c45f84b92a9e1dd98ddf

View file

@ -13,7 +13,6 @@ using Penumbra.Api.Enums;
using Penumbra.Api.Helpers;
using Penumbra.String;
using Penumbra.String.Classes;
using Swan;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Api;
@ -582,6 +581,7 @@ public class IpcTester : IDisposable
private string _currentResolvePath = string.Empty;
private string _currentResolveCharacter = string.Empty;
private string _currentReversePath = string.Empty;
private int _currentReverseIdx = 0;
public Resolve( DalamudPluginInterface pi )
=> _pi = pi;
@ -598,6 +598,7 @@ public class IpcTester : IDisposable
ImGui.InputTextWithHint( "##resolveCharacter", "Character Name (leave blank for default)...", ref _currentResolveCharacter, 32 );
ImGui.InputTextWithHint( "##resolveInversePath", "Reverse-resolve this path...", ref _currentReversePath,
Utf8GamePath.MaxGamePathLength );
ImGui.InputInt( "##resolveIdx", ref _currentReverseIdx, 0, 0 );
using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit );
if( !table )
{
@ -628,6 +629,12 @@ public class IpcTester : IDisposable
ImGui.TextUnformatted( Ipc.ResolveCharacterPath.Subscriber( _pi ).Invoke( _currentResolvePath, _currentResolveCharacter ) );
}
DrawIntro( Ipc.ResolveGameObjectPath.Label, "Game Object Collection Resolve" );
if( _currentResolvePath.Length != 0 )
{
ImGui.TextUnformatted( Ipc.ResolveGameObjectPath.Subscriber( _pi ).Invoke( _currentResolvePath, _currentReverseIdx ) );
}
DrawIntro( Ipc.ReverseResolvePath.Label, "Reversed Game Paths" );
if( _currentReversePath.Length > 0 )
{
@ -655,6 +662,20 @@ public class IpcTester : IDisposable
}
}
}
DrawIntro( Ipc.ReverseResolveGameObjectPath.Label, "Reversed Game Paths (Game Object)" );
if( _currentReversePath.Length > 0 )
{
var list = Ipc.ReverseResolveGameObjectPath.Subscriber( _pi ).Invoke( _currentReversePath, _currentReverseIdx );
if( list.Length > 0 )
{
ImGui.TextUnformatted( list[ 0 ] );
if( list.Length > 1 && ImGui.IsItemHovered() )
{
ImGui.SetTooltip( string.Join( "\n", list.Skip( 1 ) ) );
}
}
}
}
}
@ -763,7 +784,8 @@ public class IpcTester : IDisposable
{
private readonly DalamudPluginInterface _pi;
private string _characterName = string.Empty;
private string _characterName = string.Empty;
private int _gameObjectIndex = 0;
public Meta( DalamudPluginInterface pi )
=> _pi = pi;
@ -777,6 +799,7 @@ public class IpcTester : IDisposable
}
ImGui.InputTextWithHint( "##characterName", "Character Name...", ref _characterName, 64 );
ImGui.InputInt( "##metaIdx", ref _gameObjectIndex, 0, 0 );
using var table = ImRaii.Table( string.Empty, 3, ImGuiTableFlags.SizingFixedFit );
if( !table )
{
@ -796,6 +819,13 @@ public class IpcTester : IDisposable
var base64 = Ipc.GetPlayerMetaManipulations.Subscriber( _pi ).Invoke();
ImGui.SetClipboardText( base64 );
}
DrawIntro( Ipc.GetGameObjectMetaManipulations.Label, "Game Object Manipulations" );
if( ImGui.Button( "Copy to Clipboard##GameObject" ) )
{
var base64 = Ipc.GetGameObjectMetaManipulations.Subscriber( _pi ).Invoke( _gameObjectIndex );
ImGui.SetClipboardText( base64 );
}
}
}

View file

@ -23,7 +23,7 @@ namespace Penumbra.Api;
public class PenumbraApi : IDisposable, IPenumbraApi
{
public (int, int) ApiVersion
=> ( 4, 16 );
=> ( 4, 17 );
private Penumbra? _penumbra;
private Lumina.GameData? _lumina;
@ -216,6 +216,13 @@ public class PenumbraApi : IDisposable, IPenumbraApi
public string ResolvePath( string path, string characterName )
=> ResolvePath( path, characterName, ushort.MaxValue );
public string ResolveGameObjectPath( string path, int gameObjectIdx )
{
CheckInitialized();
AssociatedCollection( gameObjectIdx, out var collection );
return ResolvePath( path, Penumbra.ModManager, collection );
}
public string ResolvePath( string path, string characterName, ushort worldId )
{
CheckInitialized();
@ -239,6 +246,19 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return ret.Select( r => r.ToString() ).ToArray();
}
public string[] ReverseResolveGameObjectPath( string path, int gameObjectIdx )
{
CheckInitialized();
if( !Penumbra.Config.EnableMods )
{
return new[] { path };
}
AssociatedCollection( gameObjectIdx, out var collection );
var ret = collection.ReverseResolvePath( new FullPath( path ) );
return ret.Select( r => r.ToString() ).ToArray();
}
public string[] ReverseResolvePlayerPath( string path )
{
CheckInitialized();
@ -794,6 +814,14 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return Functions.ToCompressedBase64( set, MetaManipulation.CurrentVersion );
}
public string GetGameObjectMetaManipulations( int gameObjectIdx )
{
CheckInitialized();
AssociatedCollection( gameObjectIdx, out var collection );
var set = collection.MetaCache?.Manipulations.ToArray() ?? Array.Empty< MetaManipulation >();
return Functions.ToCompressedBase64( set, MetaManipulation.CurrentVersion );
}
internal bool HasTooltip
=> ChangedItemTooltip != null;
@ -812,6 +840,26 @@ public class PenumbraApi : IDisposable, IPenumbraApi
}
}
// Return the collection associated to a current game object. If it does not exist, return the default collection.
// If the index is invalid, returns false and the default collection.
private unsafe bool AssociatedCollection( int gameObjectIdx, out ModCollection collection )
{
collection = Penumbra.CollectionManager.Default;
if( gameObjectIdx < 0 || gameObjectIdx >= Dalamud.Objects.Length )
{
return false;
}
var ptr = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )Dalamud.Objects.GetObjectAddress( gameObjectIdx );
var data = PathResolver.IdentifyCollection( ptr, false );
if( data.Valid )
{
collection = data.ModCollection;
}
return true;
}
// Resolve a path given by string for a specific collection.
private static string ResolvePath( string path, Mod.Manager _, ModCollection collection )
{

View file

@ -52,9 +52,11 @@ public class PenumbraIpcProviders : IDisposable
internal readonly FuncProvider< string, string > ResolveDefaultPath;
internal readonly FuncProvider< string, string > ResolveInterfacePath;
internal readonly FuncProvider< string, string > ResolvePlayerPath;
internal readonly FuncProvider< string, int, string > ResolveGameObjectPath;
internal readonly FuncProvider< string, string, string > ResolveCharacterPath;
internal readonly FuncProvider< string, string, string[] > ReverseResolvePath;
internal readonly FuncProvider< string, string[] > ReverseResolvePathPlayer;
internal readonly FuncProvider< string, int, string[] > ReverseResolveGameObjectPath;
internal readonly FuncProvider< string, string[] > ReverseResolvePlayerPath;
// Collections
internal readonly FuncProvider< IList< string > > GetCollections;
@ -67,6 +69,7 @@ public class PenumbraIpcProviders : IDisposable
// Meta
internal readonly FuncProvider< string > GetPlayerMetaManipulations;
internal readonly FuncProvider< string, string > GetMetaManipulations;
internal readonly FuncProvider< int, string > GetGameObjectMetaManipulations;
// Mods
internal readonly FuncProvider< IList< (string, string) > > GetMods;
@ -144,12 +147,14 @@ public class PenumbraIpcProviders : IDisposable
() => Api.GameObjectResourceResolved -= GameObjectResourceResolvedEvent );
// Resolve
ResolveDefaultPath = Ipc.ResolveDefaultPath.Provider( pi, Api.ResolveDefaultPath );
ResolveInterfacePath = Ipc.ResolveInterfacePath.Provider( pi, Api.ResolveInterfacePath );
ResolvePlayerPath = Ipc.ResolvePlayerPath.Provider( pi, Api.ResolvePlayerPath );
ResolveCharacterPath = Ipc.ResolveCharacterPath.Provider( pi, Api.ResolvePath );
ReverseResolvePath = Ipc.ReverseResolvePath.Provider( pi, Api.ReverseResolvePath );
ReverseResolvePathPlayer = Ipc.ReverseResolvePlayerPath.Provider( pi, Api.ReverseResolvePlayerPath );
ResolveDefaultPath = Ipc.ResolveDefaultPath.Provider( pi, Api.ResolveDefaultPath );
ResolveInterfacePath = Ipc.ResolveInterfacePath.Provider( pi, Api.ResolveInterfacePath );
ResolvePlayerPath = Ipc.ResolvePlayerPath.Provider( pi, Api.ResolvePlayerPath );
ResolveGameObjectPath = Ipc.ResolveGameObjectPath.Provider( pi, Api.ResolveGameObjectPath );
ResolveCharacterPath = Ipc.ResolveCharacterPath.Provider( pi, Api.ResolvePath );
ReverseResolvePath = Ipc.ReverseResolvePath.Provider( pi, Api.ReverseResolvePath );
ReverseResolveGameObjectPath = Ipc.ReverseResolveGameObjectPath.Provider( pi, Api.ReverseResolveGameObjectPath );
ReverseResolvePlayerPath = Ipc.ReverseResolvePlayerPath.Provider( pi, Api.ReverseResolvePlayerPath );
// Collections
GetCollections = Ipc.GetCollections.Provider( pi, Api.GetCollections );
@ -160,8 +165,9 @@ public class PenumbraIpcProviders : IDisposable
GetChangedItems = Ipc.GetChangedItems.Provider( pi, Api.GetChangedItemsForCollection );
// Meta
GetPlayerMetaManipulations = Ipc.GetPlayerMetaManipulations.Provider( pi, Api.GetPlayerMetaManipulations );
GetMetaManipulations = Ipc.GetMetaManipulations.Provider( pi, Api.GetMetaManipulations );
GetPlayerMetaManipulations = Ipc.GetPlayerMetaManipulations.Provider( pi, Api.GetPlayerMetaManipulations );
GetMetaManipulations = Ipc.GetMetaManipulations.Provider( pi, Api.GetMetaManipulations );
GetGameObjectMetaManipulations = Ipc.GetGameObjectMetaManipulations.Provider( pi, Api.GetGameObjectMetaManipulations );
// Mods
GetMods = Ipc.GetMods.Provider( pi, Api.GetModList );
@ -242,9 +248,11 @@ public class PenumbraIpcProviders : IDisposable
ResolveDefaultPath.Dispose();
ResolveInterfacePath.Dispose();
ResolvePlayerPath.Dispose();
ResolveGameObjectPath.Dispose();
ResolveCharacterPath.Dispose();
ReverseResolvePath.Dispose();
ReverseResolvePathPlayer.Dispose();
ReverseResolveGameObjectPath.Dispose();
ReverseResolvePlayerPath.Dispose();
// Collections
GetCollections.Dispose();
@ -257,6 +265,7 @@ public class PenumbraIpcProviders : IDisposable
// Meta
GetPlayerMetaManipulations.Dispose();
GetMetaManipulations.Dispose();
GetGameObjectMetaManipulations.Dispose();
// Mods
GetMods.Dispose();

View file

@ -17,7 +17,7 @@ namespace Penumbra.Interop.Resolver;
public unsafe partial class PathResolver
{
// Identify the correct collection for a GameObject by index and name.
private static ResolveData IdentifyCollection( GameObject* gameObject, bool useCache )
public static ResolveData IdentifyCollection( GameObject* gameObject, bool useCache )
{
if( gameObject == null )
{

View file

@ -67,7 +67,7 @@
"penumbra.gamedata": {
"type": "Project",
"dependencies": {
"Penumbra.Api": "[1.0.0, )",
"Penumbra.Api": "[1.0.3, )",
"Penumbra.String": "[1.0.0, )"
}
},