From 9115cbaac1cbb50f088dcec94f57506a04b5cbe1 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Fri, 8 Jul 2022 10:45:57 +0200 Subject: [PATCH] Add ReverseResolvePlayer. --- Penumbra/Api/IPenumbraApi.cs | 12 +++-- Penumbra/Api/IpcTester.cs | 21 +++++++-- Penumbra/Api/PenumbraApi.cs | 19 ++++++-- Penumbra/Api/PenumbraIpc.cs | 46 ++++++++++++------- .../Interop/Resolver/PathResolver.Data.cs | 28 ++++++++++- 5 files changed, 99 insertions(+), 27 deletions(-) diff --git a/Penumbra/Api/IPenumbraApi.cs b/Penumbra/Api/IPenumbraApi.cs index 43ccd0c5..da11cfb7 100644 --- a/Penumbra/Api/IPenumbraApi.cs +++ b/Penumbra/Api/IPenumbraApi.cs @@ -22,7 +22,9 @@ public delegate void ChangedItemHover( object? item ); public delegate void ChangedItemClick( MouseButton button, object? item ); public delegate void GameObjectRedrawn( IntPtr objectPtr, int objectTableIndex ); public delegate void ModSettingChanged( ModSettingChange type, string collectionName, string modDirectory, bool inherited ); -public delegate void CreatingCharacterBaseDelegate( IntPtr gameObject, ModCollection collection, IntPtr customize, IntPtr equipData ); + +public delegate void CreatingCharacterBaseDelegate( IntPtr gameObject, ModCollection collection, IntPtr modelId, IntPtr customize, + IntPtr equipData ); public enum PenumbraApiEc { @@ -87,8 +89,12 @@ public interface IPenumbraApi : IPenumbraApiBase // Returns the given gamePath if penumbra would not manipulate it. 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 ); + // Reverse resolves a given modded local path into its replacement in form of all applicable game paths for given character collection. + public string[] ReverseResolvePath( string moddedPath, string characterName ); + + // Reverse resolves a given modded local path into its replacement in form of all applicable game paths + // using the collection applying to the player character. + public string[] ReverseResolvePathPlayer( string moddedPath ); // 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 index 87669449..cc098c98 100644 --- a/Penumbra/Api/IpcTester.cs +++ b/Penumbra/Api/IpcTester.cs @@ -30,7 +30,7 @@ public class IpcTester : IDisposable private readonly ICallGateSubscriber< string, object? > _postSettingsDraw; private readonly ICallGateSubscriber< IntPtr, int, object? > _redrawn; private readonly ICallGateSubscriber< ModSettingChange, string, string, bool, object? > _settingChanged; - private readonly ICallGateSubscriber< IntPtr, string, IntPtr, IntPtr, object? > _characterBaseCreated; + private readonly ICallGateSubscriber< IntPtr, string, IntPtr, IntPtr, IntPtr, object? > _characterBaseCreated; private readonly List< DateTimeOffset > _initializedList = new(); private readonly List< DateTimeOffset > _disposedList = new(); @@ -46,7 +46,7 @@ public class IpcTester : IDisposable _postSettingsDraw = _pi.GetIpcSubscriber< string, object? >( PenumbraIpc.LabelProviderPostSettingsDraw ); _settingChanged = _pi.GetIpcSubscriber< ModSettingChange, string, string, bool, object? >( PenumbraIpc.LabelProviderModSettingChanged ); _characterBaseCreated = - _pi.GetIpcSubscriber< IntPtr, string, IntPtr, IntPtr, object? >( PenumbraIpc.LabelProviderCreatingCharacterBase ); + _pi.GetIpcSubscriber< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >( PenumbraIpc.LabelProviderCreatingCharacterBase ); _initialized.Subscribe( AddInitialized ); _disposed.Subscribe( AddDisposed ); _redrawn.Subscribe( SetLastRedrawn ); @@ -204,7 +204,7 @@ public class IpcTester : IDisposable private string _lastCreatedGameObjectName = string.Empty; private DateTimeOffset _lastCreatedGameObjectTime = DateTimeOffset.MaxValue; - private unsafe void UpdateLastCreated( IntPtr gameObject, string _, IntPtr _2, IntPtr _3 ) + private unsafe void UpdateLastCreated( IntPtr gameObject, string _, IntPtr _2, IntPtr _3, IntPtr _4 ) { var obj = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )gameObject; _lastCreatedGameObjectName = new Utf8String( obj->GetName() ).ToString(); @@ -278,6 +278,21 @@ public class IpcTester : IDisposable } } + DrawIntro( PenumbraIpc.LabelProviderReverseResolvePathPlayer, "Reversed Game Paths (Player)" ); + if( _currentReversePath.Length > 0 ) + { + var list = _pi.GetIpcSubscriber< string, string[] >( PenumbraIpc.LabelProviderReverseResolvePathPlayer ) + .InvokeFunc( _currentReversePath ); + if( list.Length > 0 ) + { + ImGui.TextUnformatted( list[ 0 ] ); + if( list.Length > 1 && ImGui.IsItemHovered() ) + { + ImGui.SetTooltip( string.Join( "\n", list.Skip( 1 ) ) ); + } + } + } + DrawIntro( PenumbraIpc.LabelProviderCreatingCharacterBase, "Last Drawobject created" ); if( _lastCreatedGameObjectTime < DateTimeOffset.Now ) { diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index 588fda8b..39adf0be 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -12,6 +12,7 @@ using OtterGui; using Penumbra.Collections; using Penumbra.GameData.ByteString; using Penumbra.GameData.Enums; +using Penumbra.Interop.Resolver; using Penumbra.Meta.Manipulations; using Penumbra.Mods; using Penumbra.Util; @@ -21,7 +22,7 @@ namespace Penumbra.Api; public class PenumbraApi : IDisposable, IPenumbraApi { public (int, int) ApiVersion - => ( 4, 8 ); + => ( 4, 9 ); private Penumbra? _penumbra; private Lumina.GameData? _lumina; @@ -129,7 +130,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi Penumbra.CollectionManager.Character( characterName ) ); } - public IList< string > ReverseResolvePath( string path, string characterName ) + public string[] ReverseResolvePath( string path, string characterName ) { CheckInitialized(); if( !Penumbra.Config.EnableMods ) @@ -138,7 +139,19 @@ public class PenumbraApi : IDisposable, IPenumbraApi } var ret = Penumbra.CollectionManager.Character( characterName ).ReverseResolvePath( new FullPath( path ) ); - return ret.Select( r => r.ToString() ).ToList(); + return ret.Select( r => r.ToString() ).ToArray(); + } + + public string[] ReverseResolvePathPlayer( string path ) + { + CheckInitialized(); + if( !Penumbra.Config.EnableMods ) + { + return new[] { path }; + } + + var ret = PathResolver.PlayerCollection().ReverseResolvePath( new FullPath( path ) ); + return ret.Select( r => r.ToString() ).ToArray(); } public T? GetFile< T >( string gamePath ) where T : FileResource diff --git a/Penumbra/Api/PenumbraIpc.cs b/Penumbra/Api/PenumbraIpc.cs index ac443531..c97cca85 100644 --- a/Penumbra/Api/PenumbraIpc.cs +++ b/Penumbra/Api/PenumbraIpc.cs @@ -257,17 +257,19 @@ public partial class PenumbraIpc public partial class PenumbraIpc { - public const string LabelProviderResolveDefault = "Penumbra.ResolveDefaultPath"; - public const string LabelProviderResolveCharacter = "Penumbra.ResolveCharacterPath"; - public const string LabelProviderGetDrawObjectInfo = "Penumbra.GetDrawObjectInfo"; - public const string LabelProviderReverseResolvePath = "Penumbra.ReverseResolvePath"; - public const string LabelProviderCreatingCharacterBase = "Penumbra.CreatingCharacterBase"; + public const string LabelProviderResolveDefault = "Penumbra.ResolveDefaultPath"; + public const string LabelProviderResolveCharacter = "Penumbra.ResolveCharacterPath"; + public const string LabelProviderGetDrawObjectInfo = "Penumbra.GetDrawObjectInfo"; + public const string LabelProviderReverseResolvePath = "Penumbra.ReverseResolvePath"; + public const string LabelProviderReverseResolvePathPlayer = "Penumbra.ReverseResolvePathPlayer"; + public const string LabelProviderCreatingCharacterBase = "Penumbra.CreatingCharacterBase"; - internal ICallGateProvider< string, string >? ProviderResolveDefault; - internal ICallGateProvider< string, string, string >? ProviderResolveCharacter; - internal ICallGateProvider< IntPtr, (IntPtr, string) >? ProviderGetDrawObjectInfo; - internal ICallGateProvider< string, string, IList< string > >? ProviderReverseResolvePath; - internal ICallGateProvider< IntPtr, string, IntPtr, IntPtr, object? >? ProviderCreatingCharacterBase; + internal ICallGateProvider< string, string >? ProviderResolveDefault; + internal ICallGateProvider< string, string, string >? ProviderResolveCharacter; + internal ICallGateProvider< IntPtr, (IntPtr, string) >? ProviderGetDrawObjectInfo; + internal ICallGateProvider< string, string, string[] >? ProviderReverseResolvePath; + internal ICallGateProvider< string, string[] >? ProviderReverseResolvePathPlayer; + internal ICallGateProvider< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >? ProviderCreatingCharacterBase; private void InitializeResolveProviders( DalamudPluginInterface pi ) { @@ -303,18 +305,29 @@ public partial class PenumbraIpc try { - ProviderReverseResolvePath = pi.GetIpcProvider< string, string, IList< string > >( LabelProviderReverseResolvePath ); + ProviderReverseResolvePath = pi.GetIpcProvider< string, string, string[] >( LabelProviderReverseResolvePath ); ProviderReverseResolvePath.RegisterFunc( Api.ReverseResolvePath ); } catch( Exception e ) { - PluginLog.Error( $"Error registering IPC provider for {LabelProviderGetDrawObjectInfo}:\n{e}" ); + PluginLog.Error( $"Error registering IPC provider for {LabelProviderReverseResolvePath}:\n{e}" ); } try { - ProviderCreatingCharacterBase = pi.GetIpcProvider< IntPtr, string, IntPtr, IntPtr, object? >( LabelProviderCreatingCharacterBase ); - Api.CreatingCharacterBase += CreatingCharacterBaseEvent; + ProviderReverseResolvePathPlayer = pi.GetIpcProvider< string, string[] >( LabelProviderReverseResolvePathPlayer ); + ProviderReverseResolvePathPlayer.RegisterFunc( Api.ReverseResolvePathPlayer ); + } + catch( Exception e ) + { + PluginLog.Error( $"Error registering IPC provider for {LabelProviderReverseResolvePathPlayer}:\n{e}" ); + } + + try + { + ProviderCreatingCharacterBase = + pi.GetIpcProvider< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >( LabelProviderCreatingCharacterBase ); + Api.CreatingCharacterBase += CreatingCharacterBaseEvent; } catch( Exception e ) { @@ -328,12 +341,13 @@ public partial class PenumbraIpc ProviderResolveDefault?.UnregisterFunc(); ProviderResolveCharacter?.UnregisterFunc(); ProviderReverseResolvePath?.UnregisterFunc(); + ProviderReverseResolvePathPlayer?.UnregisterFunc(); Api.CreatingCharacterBase -= CreatingCharacterBaseEvent; } - private void CreatingCharacterBaseEvent( IntPtr gameObject, ModCollection collection, IntPtr customize, IntPtr equipData ) + private void CreatingCharacterBaseEvent( IntPtr gameObject, ModCollection collection, IntPtr modelId, IntPtr customize, IntPtr equipData ) { - ProviderCreatingCharacterBase?.SendMessage( gameObject, collection.Name, customize, equipData ); + ProviderCreatingCharacterBase?.SendMessage( gameObject, collection.Name, modelId, customize, equipData ); } } diff --git a/Penumbra/Interop/Resolver/PathResolver.Data.cs b/Penumbra/Interop/Resolver/PathResolver.Data.cs index 1f07dd85..e5ac64cd 100644 --- a/Penumbra/Interop/Resolver/PathResolver.Data.cs +++ b/Penumbra/Interop/Resolver/PathResolver.Data.cs @@ -31,14 +31,14 @@ public unsafe partial class PathResolver private ModCollection? _lastCreatedCollection; public event CreatingCharacterBaseDelegate? CreatingCharacterBase; - private IntPtr CharacterBaseCreateDetour( uint a, IntPtr b, IntPtr c, byte d ) { using var cmp = MetaChanger.ChangeCmp( this, out _lastCreatedCollection ); if( LastGameObject != null ) { - CreatingCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!, b, c ); + var modelPtr = &a; + CreatingCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!, ( IntPtr )modelPtr, b, c ); } var ret = CharacterBaseCreateHook!.Original( a, b, c, d ); @@ -341,6 +341,30 @@ public unsafe partial class PathResolver } } + // Get the collection applying to the current player character + // or the default collection if no player exists. + public static ModCollection PlayerCollection() + { + var player = Dalamud.ClientState.LocalPlayer; + if( player == null ) + { + return Penumbra.CollectionManager.Default; + } + + var name = player.Name.TextValue; + if( CollectionByActorName( name, out var c ) ) + { + return c; + } + + if( CollectionByActor( name, ( GameObject* )player.Address, out c ) ) + { + return c; + } + + return Penumbra.CollectionManager.Default; + } + // 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 )