Merge branch 'pr/n248_master'

This commit is contained in:
Ottermandias 2022-09-05 14:10:05 +02:00
commit 1ba38a7704
17 changed files with 336 additions and 191 deletions

View file

@ -27,6 +27,7 @@ public delegate void CreatingCharacterBaseDelegate( IntPtr gameObject, ModCollec
IntPtr equipData );
public delegate void CreatedCharacterBaseDelegate( IntPtr gameObject, ModCollection collection, IntPtr drawObject );
public delegate void GameObjectResourceResolvedDelegate( IntPtr gameObject, string gamePath, string localPath );
public enum PenumbraApiEc
{
@ -79,6 +80,10 @@ public interface IPenumbraApi : IPenumbraApiBase
// so you can apply flag changes after finishing.
public event CreatedCharacterBaseDelegate? CreatedCharacterBase;
// Triggered whenever a resource is redirected by Penumbra for a specific, identified game object.
// Does not trigger if the resource is not requested for a known game object.
public event GameObjectResourceResolvedDelegate? GameObjectResourceResolved;
// Queue redrawing of all actors of the given name with the given RedrawType.
public void RedrawObject( string name, RedrawType setting );

View file

@ -33,9 +33,12 @@ public class IpcTester : IDisposable
private readonly ICallGateSubscriber< ModSettingChange, string, string, bool, object? > _settingChanged;
private readonly ICallGateSubscriber< IntPtr, string, IntPtr, IntPtr, IntPtr, object? > _characterBaseCreating;
private readonly ICallGateSubscriber< IntPtr, string, IntPtr, object? > _characterBaseCreated;
private readonly ICallGateSubscriber< IntPtr, string, string, object? > _gameObjectResourcePathResolved;
private readonly List< DateTimeOffset > _initializedList = new();
private readonly List< DateTimeOffset > _disposedList = new();
private bool _subscribed = false;
public IpcTester( DalamudPluginInterface pi, PenumbraIpc ipc )
{
@ -51,31 +54,51 @@ public class IpcTester : IDisposable
_characterBaseCreating =
_pi.GetIpcSubscriber< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >( PenumbraIpc.LabelProviderCreatingCharacterBase );
_characterBaseCreated = _pi.GetIpcSubscriber< IntPtr, string, IntPtr, object? >( PenumbraIpc.LabelProviderCreatedCharacterBase );
_initialized.Subscribe( AddInitialized );
_disposed.Subscribe( AddDisposed );
_redrawn.Subscribe( SetLastRedrawn );
_preSettingsDraw.Subscribe( UpdateLastDrawnMod );
_postSettingsDraw.Subscribe( UpdateLastDrawnMod );
_settingChanged.Subscribe( UpdateLastModSetting );
_characterBaseCreating.Subscribe( UpdateLastCreated );
_characterBaseCreated.Subscribe( UpdateLastCreated2 );
_modDirectoryChanged.Subscribe( UpdateModDirectoryChanged );
_gameObjectResourcePathResolved =
_pi.GetIpcSubscriber< IntPtr, string, string, object? >( PenumbraIpc.LabelProviderGameObjectResourcePathResolved );
}
private void SubscribeEvents()
{
if( !_subscribed )
{
_initialized.Subscribe( AddInitialized );
_disposed.Subscribe( AddDisposed );
_redrawn.Subscribe( SetLastRedrawn );
_preSettingsDraw.Subscribe( UpdateLastDrawnMod );
_postSettingsDraw.Subscribe( UpdateLastDrawnMod );
_settingChanged.Subscribe( UpdateLastModSetting );
_characterBaseCreating.Subscribe( UpdateLastCreated );
_characterBaseCreated.Subscribe( UpdateLastCreated2 );
_modDirectoryChanged.Subscribe( UpdateModDirectoryChanged );
_gameObjectResourcePathResolved.Subscribe( UpdateGameObjectResourcePath );
_subscribed = true;
}
}
public void UnsubscribeEvents()
{
if( _subscribed )
{
_initialized.Unsubscribe( AddInitialized );
_disposed.Unsubscribe( AddDisposed );
_redrawn.Subscribe( SetLastRedrawn );
_tooltip?.Unsubscribe( AddedTooltip );
_click?.Unsubscribe( AddedClick );
_preSettingsDraw.Unsubscribe( UpdateLastDrawnMod );
_postSettingsDraw.Unsubscribe( UpdateLastDrawnMod );
_settingChanged.Unsubscribe( UpdateLastModSetting );
_characterBaseCreating.Unsubscribe( UpdateLastCreated );
_characterBaseCreated.Unsubscribe( UpdateLastCreated2 );
_modDirectoryChanged.Unsubscribe( UpdateModDirectoryChanged );
_gameObjectResourcePathResolved.Unsubscribe( UpdateGameObjectResourcePath );
_subscribed = false;
}
}
public void Dispose()
{
_initialized.Unsubscribe( AddInitialized );
_disposed.Unsubscribe( AddDisposed );
_redrawn.Subscribe( SetLastRedrawn );
_tooltip?.Unsubscribe( AddedTooltip );
_click?.Unsubscribe( AddedClick );
_preSettingsDraw.Unsubscribe( UpdateLastDrawnMod );
_postSettingsDraw.Unsubscribe( UpdateLastDrawnMod );
_settingChanged.Unsubscribe( UpdateLastModSetting );
_characterBaseCreating.Unsubscribe( UpdateLastCreated );
_characterBaseCreated.Unsubscribe( UpdateLastCreated2 );
_modDirectoryChanged.Unsubscribe( UpdateModDirectoryChanged );
}
=> UnsubscribeEvents();
private void AddInitialized()
=> _initializedList.Add( DateTimeOffset.UtcNow );
@ -87,6 +110,7 @@ public class IpcTester : IDisposable
{
try
{
SubscribeEvents();
DrawAvailable();
DrawGeneral();
DrawResolve();
@ -224,6 +248,10 @@ public class IpcTester : IDisposable
private string _lastCreatedGameObjectName = string.Empty;
private IntPtr _lastCreatedDrawObject = IntPtr.Zero;
private DateTimeOffset _lastCreatedGameObjectTime = DateTimeOffset.MaxValue;
private string _lastResolvedGamePath = string.Empty;
private string _lastResolvedFullPath = string.Empty;
private string _lastResolvedObject = string.Empty;
private DateTimeOffset _lastResolvedGamePathTime = DateTimeOffset.MaxValue;
private unsafe void UpdateLastCreated( IntPtr gameObject, string _, IntPtr _2, IntPtr _3, IntPtr _4 )
{
@ -241,6 +269,15 @@ public class IpcTester : IDisposable
_lastCreatedDrawObject = drawObject;
}
private unsafe void UpdateGameObjectResourcePath( IntPtr gameObject, string gamePath, string fullPath )
{
var obj = ( FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* )gameObject;
_lastResolvedObject = obj != null ? new Utf8String( obj->GetName() ).ToString() : "Unknown";
_lastResolvedGamePath = gamePath;
_lastResolvedFullPath = fullPath;
_lastResolvedGamePathTime = DateTimeOffset.Now;
}
private void DrawResolve()
{
using var _ = ImRaii.TreeNode( "Resolve IPC" );
@ -336,6 +373,13 @@ public class IpcTester : IDisposable
? $"0x{_lastCreatedDrawObject:X} for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}"
: $"NULL for <{_lastCreatedGameObjectName}> at {_lastCreatedGameObjectTime}" );
}
DrawIntro( PenumbraIpc.LabelProviderGameObjectResourcePathResolved, "Last GamePath resolved" );
if( _lastResolvedGamePathTime < DateTimeOffset.Now )
{
ImGui.TextUnformatted(
$"{_lastResolvedGamePath} -> {_lastResolvedFullPath} for <{_lastResolvedObject}> at {_lastResolvedGamePathTime}" );
}
}
private string _redrawName = string.Empty;

View file

@ -13,6 +13,7 @@ using Penumbra.Collections;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Resolver;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Manipulations;
using Penumbra.Mods;
@ -21,7 +22,7 @@ namespace Penumbra.Api;
public class PenumbraApi : IDisposable, IPenumbraApi
{
public (int, int) ApiVersion
=> ( 4, 12 );
=> ( 4, 13 );
private Penumbra? _penumbra;
private Lumina.GameData? _lumina;
@ -54,7 +55,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
public bool Valid
=> _penumbra != null;
public PenumbraApi( Penumbra penumbra )
public unsafe PenumbraApi( Penumbra penumbra )
{
_penumbra = penumbra;
_lumina = ( Lumina.GameData? )Dalamud.GameData.GetType()
@ -66,10 +67,12 @@ public class PenumbraApi : IDisposable, IPenumbraApi
}
Penumbra.CollectionManager.CollectionChanged += SubscribeToNewCollections;
Penumbra.ResourceLoader.ResourceLoaded += OnResourceLoaded;
}
public void Dispose()
public unsafe void Dispose()
{
Penumbra.ResourceLoader.ResourceLoaded -= OnResourceLoaded;
Penumbra.CollectionManager.CollectionChanged -= SubscribeToNewCollections;
_penumbra = null;
_lumina = null;
@ -90,6 +93,16 @@ public class PenumbraApi : IDisposable, IPenumbraApi
return Penumbra.Config.ModDirectory;
}
private unsafe void OnResourceLoaded( ResourceHandle* _, Utf8GamePath originalPath, FullPath? manipulatedPath,
ResolveData resolveData )
{
if( resolveData.AssociatedGameObject != IntPtr.Zero )
{
GameObjectResourceResolved?.Invoke( resolveData.AssociatedGameObject, originalPath.ToString(),
manipulatedPath?.ToString() ?? originalPath.ToString() );
}
}
public event Action< string, bool >? ModDirectoryChanged
{
add => Penumbra.ModManager.ModDirectoryChanged += value;
@ -103,6 +116,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
}
public event ChangedItemHover? ChangedItemTooltip;
public event GameObjectResourceResolvedDelegate? GameObjectResourceResolved;
public void RedrawObject( int tableIndex, RedrawType setting )
{
@ -232,7 +246,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
{
CheckInitialized();
var (obj, collection) = PathResolver.IdentifyDrawObject( drawObject );
return ( obj, collection.Name );
return ( obj, collection.ModCollection.Name );
}
public int GetCutsceneParentIndex( int actor )

View file

@ -274,15 +274,16 @@ public partial class PenumbraIpc
public partial class PenumbraIpc
{
public const string LabelProviderResolveDefault = "Penumbra.ResolveDefaultPath";
public const string LabelProviderResolveCharacter = "Penumbra.ResolveCharacterPath";
public const string LabelProviderResolvePlayer = "Penumbra.ResolvePlayerPath";
public const string LabelProviderGetDrawObjectInfo = "Penumbra.GetDrawObjectInfo";
public const string LabelProviderGetCutsceneParentIndex = "Penumbra.GetCutsceneParentIndex";
public const string LabelProviderReverseResolvePath = "Penumbra.ReverseResolvePath";
public const string LabelProviderReverseResolvePlayerPath = "Penumbra.ReverseResolvePlayerPath";
public const string LabelProviderCreatingCharacterBase = "Penumbra.CreatingCharacterBase";
public const string LabelProviderCreatedCharacterBase = "Penumbra.CreatedCharacterBase";
public const string LabelProviderResolveDefault = "Penumbra.ResolveDefaultPath";
public const string LabelProviderResolveCharacter = "Penumbra.ResolveCharacterPath";
public const string LabelProviderResolvePlayer = "Penumbra.ResolvePlayerPath";
public const string LabelProviderGetDrawObjectInfo = "Penumbra.GetDrawObjectInfo";
public const string LabelProviderGetCutsceneParentIndex = "Penumbra.GetCutsceneParentIndex";
public const string LabelProviderReverseResolvePath = "Penumbra.ReverseResolvePath";
public const string LabelProviderReverseResolvePlayerPath = "Penumbra.ReverseResolvePlayerPath";
public const string LabelProviderCreatingCharacterBase = "Penumbra.CreatingCharacterBase";
public const string LabelProviderCreatedCharacterBase = "Penumbra.CreatedCharacterBase";
public const string LabelProviderGameObjectResourcePathResolved = "Penumbra.GameObjectResourcePathResolved";
internal ICallGateProvider< string, string >? ProviderResolveDefault;
internal ICallGateProvider< string, string, string >? ProviderResolveCharacter;
@ -293,6 +294,7 @@ public partial class PenumbraIpc
internal ICallGateProvider< string, string[] >? ProviderReverseResolvePathPlayer;
internal ICallGateProvider< IntPtr, string, IntPtr, IntPtr, IntPtr, object? >? ProviderCreatingCharacterBase;
internal ICallGateProvider< IntPtr, string, IntPtr, object? >? ProviderCreatedCharacterBase;
internal ICallGateProvider< IntPtr, string, string, object? >? ProviderGameObjectResourcePathResolved;
private void InitializeResolveProviders( DalamudPluginInterface pi )
{
@ -387,6 +389,22 @@ public partial class PenumbraIpc
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderCreatedCharacterBase}:\n{e}" );
}
try
{
ProviderGameObjectResourcePathResolved =
pi.GetIpcProvider< IntPtr, string, string, object? >( LabelProviderGameObjectResourcePathResolved );
Api.GameObjectResourceResolved += GameObjectResourceResolvedEvent;
}
catch( Exception e )
{
PluginLog.Error( $"Error registering IPC provider for {LabelProviderGameObjectResourcePathResolved}:\n{e}" );
}
}
private void GameObjectResourceResolvedEvent( IntPtr gameObject, string gamePath, string localPath )
{
ProviderGameObjectResourcePathResolved?.SendMessage( gameObject, gamePath, localPath );
}
private void DisposeResolveProviders()
@ -397,8 +415,9 @@ public partial class PenumbraIpc
ProviderResolveCharacter?.UnregisterFunc();
ProviderReverseResolvePath?.UnregisterFunc();
ProviderReverseResolvePathPlayer?.UnregisterFunc();
Api.CreatingCharacterBase -= CreatingCharacterBaseEvent;
Api.CreatedCharacterBase -= CreatedCharacterBaseEvent;
Api.CreatingCharacterBase -= CreatingCharacterBaseEvent;
Api.CreatedCharacterBase -= CreatedCharacterBaseEvent;
Api.GameObjectResourceResolved -= GameObjectResourceResolvedEvent;
}
private void CreatingCharacterBaseEvent( IntPtr gameObject, ModCollection collection, IntPtr modelId, IntPtr customize, IntPtr equipData )

View file

@ -0,0 +1,46 @@
using System;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
namespace Penumbra.Collections;
public readonly struct ResolveData
{
public static readonly ResolveData Invalid = new(ModCollection.Empty);
public readonly ModCollection ModCollection;
public readonly IntPtr AssociatedGameObject;
public bool Valid
=> ModCollection != ModCollection.Empty;
public ResolveData()
{
ModCollection = ModCollection.Empty;
AssociatedGameObject = IntPtr.Zero;
}
public ResolveData( ModCollection collection, IntPtr gameObject )
{
ModCollection = collection;
AssociatedGameObject = gameObject;
}
public ResolveData( ModCollection collection )
: this( collection, IntPtr.Zero )
{ }
public override string ToString()
=> ModCollection.Name;
}
public static class ResolveDataExtensions
{
public static ResolveData ToResolveData( this ModCollection collection )
=> new(collection);
public static ResolveData ToResolveData( this ModCollection collection, IntPtr ptr )
=> new(collection, ptr);
public static unsafe ResolveData ToResolveData( this ModCollection collection, void* ptr )
=> new(collection, ( IntPtr )ptr);
}

View file

@ -7,6 +7,7 @@ using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
using FFXIVClientStructs.STD;
using Penumbra.Collections;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums;
@ -47,7 +48,7 @@ public unsafe partial class ResourceLoader
public Utf8GamePath OriginalPath;
public FullPath ManipulatedPath;
public ResourceCategory Category;
public object? ResolverInfo;
public ResolveData ResolverInfo;
public ResourceType Extension;
}
@ -58,18 +59,18 @@ public unsafe partial class ResourceLoader
public void EnableDebug()
{
_decRefHook?.Enable();
_decRefHook.Enable();
ResourceLoaded += AddModifiedDebugInfo;
}
public void DisableDebug()
{
_decRefHook?.Disable();
_decRefHook.Disable();
ResourceLoaded -= AddModifiedDebugInfo;
}
private void AddModifiedDebugInfo( Structs.ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
object? resolverInfo )
ResolveData resolverInfo )
{
if( manipulatedPath == null || manipulatedPath.Value.Crc64 == 0 )
{
@ -243,7 +244,7 @@ public unsafe partial class ResourceLoader
private static void LogPath( Utf8GamePath path, bool synchronous )
=> PluginLog.Information( $"[ResourceLoader] Requested {path} {( synchronous ? "synchronously." : "asynchronously." )}" );
private static void LogResource( Structs.ResourceHandle* handle, Utf8GamePath path, FullPath? manipulatedPath, object? _ )
private static void LogResource( Structs.ResourceHandle* handle, Utf8GamePath path, FullPath? manipulatedPath, ResolveData _ )
{
var pathString = manipulatedPath != null ? $"custom file {manipulatedPath} instead of {path}" : path.ToString();
PluginLog.Information( $"[ResourceLoader] Loaded {pathString} to 0x{( ulong )handle:X}. (Refcount {handle->RefCount})" );

View file

@ -9,6 +9,7 @@ using Dalamud.Hooking;
using Dalamud.Logging;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
using Penumbra.Collections;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs;
@ -113,18 +114,18 @@ public unsafe partial class ResourceLoader
// Use the default method of path replacement.
public static (FullPath?, object?) DefaultResolver( Utf8GamePath path )
public static (FullPath?, ResolveData) DefaultResolver( Utf8GamePath path )
{
var resolved = Penumbra.CollectionManager.Default.ResolvePath( path );
return ( resolved, null );
return ( resolved, Penumbra.CollectionManager.Default.ToResolveData() );
}
// Try all resolve path subscribers or use the default replacer.
private (FullPath?, object?) ResolvePath( Utf8GamePath path, ResourceCategory category, ResourceType resourceType, int resourceHash )
private (FullPath?, ResolveData) ResolvePath( Utf8GamePath path, ResourceCategory category, ResourceType resourceType, int resourceHash )
{
if( !DoReplacements || _incMode.Value )
{
return ( null, null );
return ( null, ResolveData.Invalid );
}
path = path.ToLower();

View file

@ -2,6 +2,7 @@ using System;
using Dalamud.Hooking;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
using Penumbra.Collections;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs;
@ -116,9 +117,9 @@ public unsafe partial class ResourceLoader : IDisposable
// Event fired whenever a resource is returned.
// If the path was manipulated by penumbra, manipulatedPath will be the file path of the loaded resource.
// resolveData is additional data returned by the current ResolvePath function and is user-defined.
// resolveData is additional data returned by the current ResolvePath function which can contain the collection and associated game object.
public delegate void ResourceLoadedDelegate( ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath,
object? resolveData );
ResolveData resolveData );
public event ResourceLoadedDelegate? ResourceLoaded;
@ -133,7 +134,7 @@ public unsafe partial class ResourceLoader : IDisposable
// Resolving goes through all subscribed functions in arbitrary order until one returns true,
// or uses default resolving if none return true.
public delegate bool ResolvePathDelegate( Utf8GamePath path, ResourceCategory category, ResourceType type, int hash,
out (FullPath?, object?) ret );
out (FullPath?, ResolveData) ret );
public event ResolvePathDelegate? ResolvePathCustomization;

View file

@ -15,8 +15,8 @@ public unsafe partial class PathResolver
{
private readonly DrawObjectState _drawObjectState;
private ModCollection? _animationLoadCollection;
private ModCollection? _lastAvfxCollection;
private ResolveData _animationLoadData = ResolveData.Invalid;
private ResolveData _lastAvfxData = ResolveData.Invalid;
public AnimationState( DrawObjectState drawObjectState )
{
@ -24,46 +24,48 @@ public unsafe partial class PathResolver
SignatureHelper.Initialise( this );
}
public bool HandleFiles( ResourceType type, Utf8GamePath _, [NotNullWhen( true )] out ModCollection? collection )
public bool HandleFiles( ResourceType type, Utf8GamePath _, out ResolveData resolveData )
{
switch( type )
{
case ResourceType.Tmb:
case ResourceType.Pap:
case ResourceType.Scd:
if( _animationLoadCollection != null )
if( _animationLoadData.Valid )
{
collection = _animationLoadCollection;
resolveData = _animationLoadData;
return true;
}
break;
case ResourceType.Avfx:
_lastAvfxCollection = _animationLoadCollection ?? Penumbra.CollectionManager.Default;
if( _animationLoadCollection != null )
_lastAvfxData = _animationLoadData.Valid
? _animationLoadData
: Penumbra.CollectionManager.Default.ToResolveData();
if( _animationLoadData.Valid )
{
collection = _animationLoadCollection;
resolveData = _animationLoadData;
return true;
}
break;
case ResourceType.Atex:
if( _lastAvfxCollection != null )
if( _lastAvfxData.Valid )
{
collection = _lastAvfxCollection;
resolveData = _lastAvfxData;
return true;
}
if( _animationLoadCollection != null )
if( _animationLoadData.Valid )
{
collection = _animationLoadCollection;
resolveData = _animationLoadData;
return true;
}
break;
}
collection = null;
resolveData = ResolveData.Invalid;
return false;
}
@ -107,7 +109,7 @@ public unsafe partial class PathResolver
private ulong LoadTimelineResourcesDetour( IntPtr timeline )
{
ulong ret;
var old = _animationLoadCollection;
var old = _animationLoadData;
try
{
if( timeline != IntPtr.Zero )
@ -117,11 +119,11 @@ public unsafe partial class PathResolver
if( idx >= 0 && idx < Dalamud.Objects.Length )
{
var obj = Dalamud.Objects[ idx ];
_animationLoadCollection = obj != null ? IdentifyCollection( ( GameObject* )obj.Address ) : null;
_animationLoadData = obj != null ? IdentifyCollection( ( GameObject* )obj.Address ) : ResolveData.Invalid;
}
else
{
_animationLoadCollection = null;
_animationLoadData = ResolveData.Invalid;
}
}
}
@ -130,7 +132,7 @@ public unsafe partial class PathResolver
ret = _loadTimelineResourcesHook.Original( timeline );
}
_animationLoadCollection = old;
_animationLoadData = old;
return ret;
}
@ -145,11 +147,14 @@ public unsafe partial class PathResolver
private void CharacterBaseLoadAnimationDetour( IntPtr drawObject )
{
var last = _animationLoadCollection;
_animationLoadCollection = _drawObjectState.LastCreatedCollection
?? ( FindParent( drawObject, out var collection ) != null ? collection : Penumbra.CollectionManager.Default );
var last = _animationLoadData;
_animationLoadData = _drawObjectState.LastCreatedCollection.Valid
? _drawObjectState.LastCreatedCollection
: FindParent( drawObject, out var collection ) != null
? collection
: Penumbra.CollectionManager.Default.ToResolveData();
_characterBaseLoadAnimationHook.Original( drawObject );
_animationLoadCollection = last;
_animationLoadData = last;
}
@ -160,10 +165,10 @@ public unsafe partial class PathResolver
private ulong LoadSomeAvfxDetour( uint a1, IntPtr gameObject, IntPtr gameObject2, float unk1, IntPtr unk2, IntPtr unk3 )
{
var last = _animationLoadCollection;
_animationLoadCollection = IdentifyCollection( ( GameObject* )gameObject );
var last = _animationLoadData;
_animationLoadData = IdentifyCollection( ( GameObject* )gameObject );
var ret = _loadSomeAvfxHook.Original( a1, gameObject, gameObject2, unk1, unk2, unk3 );
_animationLoadCollection = last;
_animationLoadData = last;
return ret;
}
@ -177,18 +182,18 @@ public unsafe partial class PathResolver
private void LoadSomePapDetour( IntPtr a1, int a2, IntPtr a3, int a4 )
{
var timelinePtr = a1 + 0x50;
var last = _animationLoadCollection;
var last = _animationLoadData;
if( timelinePtr != IntPtr.Zero )
{
var actorIdx = ( int )( *( *( ulong** )timelinePtr + 1 ) >> 3 );
if( actorIdx >= 0 && actorIdx < Dalamud.Objects.Length )
{
_animationLoadCollection = IdentifyCollection( ( GameObject* )( Dalamud.Objects[ actorIdx ]?.Address ?? IntPtr.Zero ) );
_animationLoadData = IdentifyCollection( ( GameObject* )( Dalamud.Objects[ actorIdx ]?.Address ?? IntPtr.Zero ) );
}
}
_loadSomePapHook.Original( a1, a2, a3, a4 );
_animationLoadCollection = last;
_animationLoadData = last;
}
// Seems to load character actions when zoning or changing class, maybe.
@ -197,10 +202,10 @@ public unsafe partial class PathResolver
private void SomeActionLoadDetour( IntPtr gameObject )
{
var last = _animationLoadCollection;
_animationLoadCollection = IdentifyCollection( ( GameObject* )gameObject );
var last = _animationLoadData;
_animationLoadData = IdentifyCollection( ( GameObject* )gameObject );
_someActionLoadHook.Original( gameObject );
_animationLoadCollection = last;
_animationLoadData = last;
}
[Signature( "E8 ?? ?? ?? ?? 44 84 A3", DetourName = nameof( SomeOtherAvfxDetour ) )]
@ -208,11 +213,11 @@ public unsafe partial class PathResolver
private void SomeOtherAvfxDetour( IntPtr unk )
{
var last = _animationLoadCollection;
var last = _animationLoadData;
var gameObject = ( GameObject* )( unk - 0x8D0 );
_animationLoadCollection = IdentifyCollection( gameObject );
_animationLoadData = IdentifyCollection( gameObject );
_someOtherAvfxHook.Original( unk );
_animationLoadCollection = last;
_animationLoadData = last;
}
}
}

View file

@ -20,13 +20,13 @@ public unsafe partial class PathResolver
public static event CreatingCharacterBaseDelegate? CreatingCharacterBase;
public static event CreatedCharacterBaseDelegate? CreatedCharacterBase;
public IEnumerable< KeyValuePair< IntPtr, (ModCollection, int) > > DrawObjects
public IEnumerable< KeyValuePair< IntPtr, (ResolveData, int) > > DrawObjects
=> _drawObjectToObject;
public int Count
=> _drawObjectToObject.Count;
public bool TryGetValue( IntPtr drawObject, out (ModCollection, int) value, out GameObject* gameObject )
public bool TryGetValue( IntPtr drawObject, out (ResolveData, int) value, out GameObject* gameObject )
{
gameObject = null;
if( !_drawObjectToObject.TryGetValue( drawObject, out value ) )
@ -40,7 +40,7 @@ public unsafe partial class PathResolver
// Set and update a parent object if it exists and a last game object is set.
public ModCollection? CheckParentDrawObject( IntPtr drawObject, IntPtr parentObject )
public ResolveData CheckParentDrawObject( IntPtr drawObject, IntPtr parentObject )
{
if( parentObject == IntPtr.Zero && LastGameObject != null )
{
@ -49,26 +49,26 @@ public unsafe partial class PathResolver
return collection;
}
return null;
return ResolveData.Invalid;
}
public bool HandleDecalFile( ResourceType type, Utf8GamePath gamePath, [NotNullWhen( true )] out ModCollection? collection )
public bool HandleDecalFile( ResourceType type, Utf8GamePath gamePath, out ResolveData resolveData )
{
if( type == ResourceType.Tex
&& LastCreatedCollection != null
&& LastCreatedCollection.Valid
&& gamePath.Path.Substring( "chara/common/texture/".Length ).StartsWith( 'd', 'e', 'c', 'a', 'l', '_', 'f', 'a', 'c', 'e' ) )
{
collection = LastCreatedCollection!;
resolveData = LastCreatedCollection;
return true;
}
collection = null;
resolveData = ResolveData.Invalid;
return false;
}
public ModCollection? LastCreatedCollection
public ResolveData LastCreatedCollection
=> _lastCreatedCollection;
public GameObject* LastGameObject { get; private set; }
@ -124,8 +124,8 @@ public unsafe partial class PathResolver
// This map links DrawObjects directly to Actors (by ObjectTable index) and their collections.
// It contains any DrawObjects that correspond to a human actor, even those without specific collections.
private readonly Dictionary< IntPtr, (ModCollection, int) > _drawObjectToObject = new();
private ModCollection? _lastCreatedCollection;
private readonly Dictionary< IntPtr, (ResolveData, int) > _drawObjectToObject = new();
private ResolveData _lastCreatedCollection = ResolveData.Invalid;
// Keep track of created DrawObjects that are CharacterBase,
// and use the last game object that called EnableDraw to link them.
@ -141,14 +141,14 @@ public unsafe partial class PathResolver
if( LastGameObject != null )
{
var modelPtr = &a;
CreatingCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!, ( IntPtr )modelPtr, b, c );
CreatingCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection, ( IntPtr )modelPtr, b, c );
}
var ret = _characterBaseCreateHook.Original( a, b, c, d );
if( LastGameObject != null )
{
_drawObjectToObject[ ret ] = ( _lastCreatedCollection!, LastGameObject->ObjectIndex );
CreatedCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!, ret );
CreatedCharacterBase?.Invoke( ( IntPtr )LastGameObject, _lastCreatedCollection!.ModCollection, ret );
}
return ret;

View file

@ -40,7 +40,7 @@ public unsafe partial class PathResolver
return null;
}
var ui = ( AtkUnitBase* )addon;
var ui = ( AtkUnitBase* )addon;
var nodeId = Dalamud.GameData.GetExcelSheet< Title >()?.GetRow( *_inspectTitleId )?.IsPrefix == true ? 2u : 6u;
var text = ( AtkTextNode* )ui->UldManager.SearchNodeById( nodeId );
@ -61,7 +61,8 @@ public unsafe partial class PathResolver
{
return null;
}
var data = *( byte** )( (byte*) agent + 0x28 );
var data = *( byte** )( ( byte* )agent + 0x28 );
if( data == null )
{
return null;
@ -139,11 +140,11 @@ public unsafe partial class PathResolver
}
// Identify the correct collection for a GameObject by index and name.
private static ModCollection IdentifyCollection( GameObject* gameObject )
private static ResolveData IdentifyCollection( GameObject* gameObject )
{
if( gameObject == null )
{
return Penumbra.CollectionManager.Default;
return new ResolveData( Penumbra.CollectionManager.Default );
}
try
@ -153,8 +154,9 @@ public unsafe partial class PathResolver
// Actors are also not named. So use Yourself > Players > Racial > Default.
if( !Dalamud.ClientState.IsLoggedIn )
{
return Penumbra.CollectionManager.ByType( CollectionType.Yourself )
var collection = Penumbra.CollectionManager.ByType( CollectionType.Yourself )
?? ( CollectionByActor( string.Empty, gameObject, out var c ) ? c : Penumbra.CollectionManager.Default );
return collection.ToResolveData( gameObject );
}
else
{
@ -163,7 +165,7 @@ public unsafe partial class PathResolver
&& gameObject->ObjectKind == ( byte )ObjectKind.EventNpc
&& gameObject->DataID is 1011832 or 1011021 ) // cf. "E8 ?? ?? ?? ?? 0F B6 F8 88 45", male or female retainer
{
return Penumbra.CollectionManager.Default;
return Penumbra.CollectionManager.Default.ToResolveData( gameObject );
}
string? actorName = null;
@ -174,7 +176,7 @@ public unsafe partial class PathResolver
if( actorName.Length > 0
&& CollectionByActorName( actorName, out var actorCollection ) )
{
return actorCollection;
return actorCollection.ToResolveData( gameObject );
}
}
@ -193,17 +195,18 @@ public unsafe partial class PathResolver
?? GetOwnerName( gameObject ) ?? actorName ?? new Utf8String( gameObject->Name ).ToString();
// First check temporary character collections, then the own configuration, then special collections.
return CollectionByActorName( actualName, out var c )
var collection = CollectionByActorName( actualName, out var c )
? c
: CollectionByActor( actualName, gameObject, out c )
? c
: Penumbra.CollectionManager.Default;
return collection.ToResolveData( gameObject );
}
}
catch( Exception e )
{
PluginLog.Error( $"Error identifying collection:\n{e}" );
return Penumbra.CollectionManager.Default;
return Penumbra.CollectionManager.Default.ToResolveData( gameObject );
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Dalamud.Hooking;
using Dalamud.Logging;
using Dalamud.Utility.Signatures;
@ -20,7 +21,7 @@ public unsafe partial class PathResolver
{
private readonly PathState _paths;
private ModCollection? _mtrlCollection;
private ResolveData _mtrlData = ResolveData.Invalid;
public MaterialState( PathState paths )
{
@ -29,30 +30,30 @@ public unsafe partial class PathResolver
}
// Check specifically for shpk and tex files whether we are currently in a material load.
public bool HandleSubFiles( ResourceType type, [NotNullWhen( true )] out ModCollection? collection )
public bool HandleSubFiles( ResourceType type, out ResolveData collection )
{
if( _mtrlCollection != null && type is ResourceType.Tex or ResourceType.Shpk )
if( _mtrlData.Valid && type is ResourceType.Tex or ResourceType.Shpk )
{
collection = _mtrlCollection;
collection = _mtrlData;
return true;
}
collection = null;
collection = ResolveData.Invalid;
return false;
}
// Materials need to be set per collection so they can load their textures independently from each other.
public static void HandleCollection( ModCollection collection, string path, bool nonDefault, ResourceType type, FullPath? resolved,
out (FullPath?, object?) data )
public static void HandleCollection( ResolveData resolveData, string path, bool nonDefault, ResourceType type, FullPath? resolved,
out (FullPath?, ResolveData) data )
{
if( nonDefault && type == ResourceType.Mtrl )
{
var fullPath = new FullPath( $"|{collection.Name}_{collection.ChangeCounter}|{path}" );
data = ( fullPath, collection );
var fullPath = new FullPath( $"|{resolveData.ModCollection.Name}_{resolveData.ModCollection.ChangeCounter}|{path}" );
data = ( fullPath, resolveData );
}
else
{
data = ( resolved, collection );
data = ( resolved, resolveData );
}
}
@ -73,8 +74,8 @@ public unsafe partial class PathResolver
public void Dispose()
{
Disable();
_loadMtrlShpkHook?.Dispose();
_loadMtrlTexHook?.Dispose();
_loadMtrlShpkHook.Dispose();
_loadMtrlTexHook.Dispose();
}
// We need to set the correct collection for the actual material path that is loaded
@ -96,7 +97,10 @@ public unsafe partial class PathResolver
#if DEBUG
PluginLog.Verbose( "Using MtrlLoadHandler with collection {$Split:l} for path {$Path:l}.", name, path );
#endif
_paths.SetCollection( path, collection );
var objFromObjTable = Dalamud.Objects.FirstOrDefault( f => f.Name.TextValue == name );
var gameObjAddr = objFromObjTable?.Address ?? IntPtr.Zero;
_paths.SetCollection( gameObjAddr, path, collection );
}
else
{
@ -123,7 +127,7 @@ public unsafe partial class PathResolver
{
LoadMtrlHelper( mtrlResourceHandle );
var ret = _loadMtrlTexHook.Original( mtrlResourceHandle );
_mtrlCollection = null;
_mtrlData = ResolveData.Invalid;
return ret;
}
@ -135,7 +139,7 @@ public unsafe partial class PathResolver
{
LoadMtrlHelper( mtrlResourceHandle );
var ret = _loadMtrlShpkHook.Original( mtrlResourceHandle );
_mtrlCollection = null;
_mtrlData = ResolveData.Invalid;
return ret;
}
@ -148,7 +152,7 @@ public unsafe partial class PathResolver
var mtrl = ( MtrlResource* )mtrlResourceHandle;
var mtrlPath = Utf8String.FromSpanUnsafe( mtrl->Handle.FileNameSpan(), true, null, true );
_mtrlCollection = _paths.TryGetValue( mtrlPath, out var c ) ? c : null;
_mtrlData = _paths.TryGetValue( mtrlPath, out var c ) ? c : ResolveData.Invalid;
}
}
}

View file

@ -79,11 +79,11 @@ public unsafe partial class PathResolver
private void OnModelLoadCompleteDetour( IntPtr drawObject )
{
var collection = GetCollection( drawObject );
if( collection != null )
var collection = GetResolveData( drawObject );
if( collection.Valid )
{
using var eqp = MetaChanger.ChangeEqp( collection );
using var eqdp = MetaChanger.ChangeEqdp( collection );
using var eqp = MetaChanger.ChangeEqp( collection.ModCollection );
using var eqdp = MetaChanger.ChangeEqdp( collection.ModCollection );
_onModelLoadCompleteHook.Original.Invoke( drawObject );
}
else
@ -106,11 +106,11 @@ public unsafe partial class PathResolver
return;
}
var collection = GetCollection( drawObject );
if( collection != null )
var collection = GetResolveData( drawObject );
if( collection.Valid )
{
using var eqp = MetaChanger.ChangeEqp( collection );
using var eqdp = MetaChanger.ChangeEqdp( collection );
using var eqp = MetaChanger.ChangeEqp( collection.ModCollection );
using var eqdp = MetaChanger.ChangeEqdp( collection.ModCollection );
_updateModelsHook.Original.Invoke( drawObject );
}
else
@ -212,26 +212,26 @@ public unsafe partial class PathResolver
return new MetaChanger( MetaManipulation.Type.Eqp );
}
public static MetaChanger ChangeEqp( PathResolver resolver, IntPtr drawObject )
public static MetaChanger ChangeEqp( PathResolver _, IntPtr drawObject )
{
var collection = GetCollection( drawObject );
if( collection != null )
var resolveData = GetResolveData( drawObject );
if( resolveData.Valid )
{
return ChangeEqp( collection );
return ChangeEqp( resolveData.ModCollection );
}
return new MetaChanger( MetaManipulation.Type.Unknown );
}
// We only need to change anything if it is actually equipment here.
public static MetaChanger ChangeEqdp( PathResolver resolver, IntPtr drawObject, uint modelType )
public static MetaChanger ChangeEqdp( PathResolver _, IntPtr drawObject, uint modelType )
{
if( modelType < 10 )
{
var collection = GetCollection( drawObject );
if( collection != null )
var collection = GetResolveData( drawObject );
if( collection.Valid )
{
return ChangeEqdp( collection );
return ChangeEqdp( collection.ModCollection );
}
}
@ -246,10 +246,10 @@ public unsafe partial class PathResolver
public static MetaChanger ChangeGmp( PathResolver resolver, IntPtr drawObject )
{
var collection = GetCollection( drawObject );
if( collection != null )
var resolveData = GetResolveData( drawObject );
if( resolveData.Valid )
{
collection.SetGmpFiles();
resolveData.ModCollection.SetGmpFiles();
return new MetaChanger( MetaManipulation.Type.Gmp );
}
@ -258,30 +258,30 @@ public unsafe partial class PathResolver
public static MetaChanger ChangeEst( PathResolver resolver, IntPtr drawObject )
{
var collection = GetCollection( drawObject );
if( collection != null )
var resolveData = GetResolveData( drawObject );
if( resolveData.Valid )
{
collection.SetEstFiles();
resolveData.ModCollection.SetEstFiles();
return new MetaChanger( MetaManipulation.Type.Est );
}
return new MetaChanger( MetaManipulation.Type.Unknown );
}
public static MetaChanger ChangeCmp( GameObject* gameObject, out ModCollection? collection )
public static MetaChanger ChangeCmp( GameObject* gameObject, out ResolveData resolveData )
{
if( gameObject != null )
{
collection = IdentifyCollection( gameObject );
if( collection != Penumbra.CollectionManager.Default && collection.HasCache )
resolveData = IdentifyCollection( gameObject );
if( resolveData.ModCollection != Penumbra.CollectionManager.Default && resolveData.ModCollection.HasCache )
{
collection.SetCmpFiles();
resolveData.ModCollection.SetCmpFiles();
return new MetaChanger( MetaManipulation.Type.Rsp );
}
}
else
{
collection = null;
resolveData = ResolveData.Invalid;
}
return new MetaChanger( MetaManipulation.Type.Unknown );
@ -289,10 +289,10 @@ public unsafe partial class PathResolver
public static MetaChanger ChangeCmp( PathResolver resolver, IntPtr drawObject )
{
var collection = GetCollection( drawObject );
if( collection != null )
var resolveData = GetResolveData( drawObject );
if( resolveData.Valid )
{
collection.SetCmpFiles();
resolveData.ModCollection.SetCmpFiles();
return new MetaChanger( MetaManipulation.Type.Rsp );
}

View file

@ -32,7 +32,7 @@ public unsafe partial class PathResolver
private readonly ResolverHooks _monster;
// This map links files to their corresponding collection, if it is non-default.
private readonly ConcurrentDictionary< Utf8String, ModCollection > _pathCollections = new();
private readonly ConcurrentDictionary< Utf8String, ResolveData > _pathCollections = new();
public PathState( PathResolver parent )
{
@ -70,18 +70,18 @@ public unsafe partial class PathResolver
public int Count
=> _pathCollections.Count;
public IEnumerable< KeyValuePair< Utf8String, ModCollection > > Paths
public IEnumerable< KeyValuePair< Utf8String, ResolveData > > Paths
=> _pathCollections;
public bool TryGetValue( Utf8String path, [NotNullWhen( true )] out ModCollection? collection )
public bool TryGetValue( Utf8String path, out ResolveData collection )
=> _pathCollections.TryGetValue( path, out collection );
public bool Consume( Utf8String path, [NotNullWhen( true )] out ModCollection? collection )
public bool Consume( Utf8String path, out ResolveData collection )
=> _pathCollections.TryRemove( path, out collection );
// Just add or remove the resolved path.
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
public IntPtr ResolvePath( ModCollection collection, IntPtr path )
public IntPtr ResolvePath( IntPtr gameObject, ModCollection collection, IntPtr path )
{
if( path == IntPtr.Zero )
{
@ -89,20 +89,20 @@ public unsafe partial class PathResolver
}
var gamePath = new Utf8String( ( byte* )path );
SetCollection( gamePath, collection );
SetCollection( gameObject, gamePath, collection );
return path;
}
// Special handling for paths so that we do not store non-owned temporary strings in the dictionary.
public void SetCollection( Utf8String path, ModCollection collection )
public void SetCollection( IntPtr gameObject, Utf8String path, ModCollection collection )
{
if( _pathCollections.ContainsKey( path ) || path.IsOwned )
{
_pathCollections[ path ] = collection;
_pathCollections[ path ] = collection.ToResolveData( gameObject );
}
else
{
_pathCollections[ path.Clone() ] = collection;
_pathCollections[ path.Clone() ] = collection.ToResolveData( gameObject );
}
}
}

View file

@ -2,6 +2,7 @@ using System;
using System.Runtime.CompilerServices;
using Dalamud.Hooking;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Penumbra.Collections;
namespace Penumbra.Interop.Resolver;
@ -226,9 +227,9 @@ public partial class PathResolver
// Implementation
[MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
private IntPtr ResolvePath( IntPtr drawObject, IntPtr path )
=> _parent._paths.ResolvePath( FindParent( drawObject, out var collection ) == null
=> _parent._paths.ResolvePath( (IntPtr?)FindParent( drawObject, out _) ?? IntPtr.Zero, FindParent( drawObject, out var collection ) == null
? Penumbra.CollectionManager.Default
: collection, path );
: collection.ModCollection, path );
// Weapons have the characters DrawObject as a parent,
// but that may not be set yet when creating a new object, so we have to do the same detour
@ -239,20 +240,20 @@ public partial class PathResolver
var parent = FindParent( drawObject, out var collection );
if( parent != null )
{
return _parent._paths.ResolvePath( collection, path );
return _parent._paths.ResolvePath( (IntPtr)parent, collection.ModCollection, path );
}
var parentObject = ( IntPtr )( ( DrawObject* )drawObject )->Object.ParentObject;
var parentCollection = DrawObjects.CheckParentDrawObject( drawObject, parentObject );
if( parentCollection != null )
if( parentCollection.Valid )
{
return _parent._paths.ResolvePath( parentCollection, path );
return _parent._paths.ResolvePath( (IntPtr)FindParent(parentObject, out _), parentCollection.ModCollection, path );
}
parent = FindParent( parentObject, out collection );
return _parent._paths.ResolvePath( parent == null
return _parent._paths.ResolvePath( (IntPtr?)parent ?? IntPtr.Zero, parent == null
? Penumbra.CollectionManager.Default
: collection, path );
: collection.ModCollection, path );
}
}
}

View file

@ -40,7 +40,7 @@ public partial class PathResolver : IDisposable
}
// The modified resolver that handles game path resolving.
private bool CharacterResolver( Utf8GamePath gamePath, ResourceCategory _1, ResourceType type, int _2, out (FullPath?, object?) data )
private bool CharacterResolver( Utf8GamePath gamePath, ResourceCategory _1, ResourceType type, int _2, out (FullPath?, ResolveData) data )
{
// Check if the path was marked for a specific collection,
// or if it is a file loaded by a material, and if we are currently in a material load,
@ -48,23 +48,23 @@ public partial class PathResolver : IDisposable
// If not use the default collection.
// We can remove paths after they have actually been loaded.
// A potential next request will add the path anew.
var nonDefault = _materials.HandleSubFiles( type, out var collection )
|| _paths.Consume( gamePath.Path, out collection )
|| _animations.HandleFiles( type, gamePath, out collection )
|| DrawObjects.HandleDecalFile( type, gamePath, out collection );
if( !nonDefault || collection == null )
var nonDefault = _materials.HandleSubFiles( type, out var resolveData )
|| _paths.Consume( gamePath.Path, out resolveData )
|| _animations.HandleFiles( type, gamePath, out resolveData )
|| DrawObjects.HandleDecalFile( type, gamePath, out resolveData );
if( !nonDefault || !resolveData.Valid )
{
collection = Penumbra.CollectionManager.Default;
resolveData = Penumbra.CollectionManager.Default.ToResolveData();
}
// Resolve using character/default collection first, otherwise forced, as usual.
var resolved = collection.ResolvePath( gamePath );
var resolved = resolveData.ModCollection.ResolvePath( gamePath );
// Since mtrl files load their files separately, we need to add the new, resolved path
// so that the functions loading tex and shpk can find that path and use its collection.
// We also need to handle defaulted materials against a non-default collection.
var path = resolved == null ? gamePath.Path.ToString() : resolved.Value.FullName;
MaterialState.HandleCollection( collection, path, nonDefault, type, resolved, out data );
MaterialState.HandleCollection( resolveData, path, nonDefault, type, resolved, out data );
return true;
}
@ -117,50 +117,50 @@ public partial class PathResolver : IDisposable
_materials.Dispose();
}
public static unsafe (IntPtr, ModCollection) IdentifyDrawObject( IntPtr drawObject )
public static unsafe (IntPtr, ResolveData) IdentifyDrawObject( IntPtr drawObject )
{
var parent = FindParent( drawObject, out var collection );
return ( ( IntPtr )parent, collection );
var parent = FindParent( drawObject, out var resolveData );
return ( ( IntPtr )parent, resolveData );
}
public int CutsceneActor( int idx )
=> Cutscenes.GetParentIndex( idx );
// Use the stored information to find the GameObject and Collection linked to a DrawObject.
public static unsafe GameObject* FindParent( IntPtr drawObject, out ModCollection collection )
public static unsafe GameObject* FindParent( IntPtr drawObject, out ResolveData resolveData )
{
if( DrawObjects.TryGetValue( drawObject, out var data, out var gameObject ) )
{
collection = data.Item1;
resolveData = data.Item1;
return gameObject;
}
if( DrawObjects.LastGameObject != null
&& ( DrawObjects.LastGameObject->DrawObject == null || DrawObjects.LastGameObject->DrawObject == ( DrawObject* )drawObject ) )
{
collection = IdentifyCollection( DrawObjects.LastGameObject );
resolveData = IdentifyCollection( DrawObjects.LastGameObject );
return DrawObjects.LastGameObject;
}
collection = IdentifyCollection( null );
resolveData = IdentifyCollection( null );
return null;
}
private static unsafe ModCollection? GetCollection( IntPtr drawObject )
private static unsafe ResolveData GetResolveData( IntPtr drawObject )
{
var parent = FindParent( drawObject, out var collection );
if( parent == null || collection == Penumbra.CollectionManager.Default )
var parent = FindParent( drawObject, out var resolveData );
if( parent == null || resolveData.ModCollection == Penumbra.CollectionManager.Default )
{
return null;
return ResolveData.Invalid;
}
return collection.HasCache ? collection : null;
return resolveData.ModCollection.HasCache ? resolveData : ResolveData.Invalid;
}
internal IEnumerable< KeyValuePair< Utf8String, ModCollection > > PathCollections
internal IEnumerable< KeyValuePair< Utf8String, ResolveData > > PathCollections
=> _paths.Paths;
internal IEnumerable< KeyValuePair< IntPtr, (ModCollection, int) > > DrawObjectMap
internal IEnumerable< KeyValuePair< IntPtr, (ResolveData, int) > > DrawObjectMap
=> DrawObjects.DrawObjects;
internal IEnumerable< KeyValuePair< int, global::Dalamud.Game.ClientState.Objects.Types.GameObject > > CutsceneActors

View file

@ -177,7 +177,7 @@ public partial class ConfigWindow
ImGui.TableNextColumn();
ImGui.TextUnformatted( name );
ImGui.TableNextColumn();
ImGui.TextUnformatted( c.Name );
ImGui.TextUnformatted( c.ModCollection.Name );
}
}
}
@ -195,7 +195,7 @@ public partial class ConfigWindow
ImGui.TableNextColumn();
ImGuiNative.igTextUnformatted( path.Path, path.Path + path.Length );
ImGui.TableNextColumn();
ImGui.TextUnformatted( collection.Name );
ImGui.TextUnformatted( collection.ModCollection.Name );
}
}
}
@ -422,6 +422,7 @@ public partial class ConfigWindow
{
if( !ImGui.CollapsingHeader( "IPC" ) )
{
_window._penumbra.Ipc.Tester.UnsubscribeEvents();
return;
}