mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
Cache collections instead of looking them up for every single file.
This commit is contained in:
parent
f676bd1889
commit
2fac923452
12 changed files with 194 additions and 28 deletions
|
|
@ -162,10 +162,15 @@ public partial class ActorManager
|
|||
((FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)actor)->NameID);
|
||||
}
|
||||
case ObjectKind.EventNpc:
|
||||
{
|
||||
var dataId = actor->DataID;
|
||||
// Special case for squadron that is also in the game functions, cf. E8 ?? ?? ?? ?? 89 87 ?? ?? ?? ?? 4C 89 BF
|
||||
if (dataId == 0xf845d)
|
||||
dataId = actor->GetNpcID();
|
||||
return check
|
||||
? CreateNpc(ObjectKind.EventNpc, actor->DataID, actor->ObjectIndex)
|
||||
: CreateIndividualUnchecked(IdentifierType.Npc, ByteString.Empty, actor->ObjectIndex, ObjectKind.EventNpc,
|
||||
actor->ObjectIndex);
|
||||
? CreateNpc(ObjectKind.EventNpc, dataId, actor->ObjectIndex)
|
||||
: CreateIndividualUnchecked(IdentifierType.Npc, ByteString.Empty, actor->ObjectIndex, ObjectKind.EventNpc, dataId);
|
||||
}
|
||||
case ObjectKind.MountType:
|
||||
case ObjectKind.Companion:
|
||||
case (ObjectKind)15: // TODO: CS Update
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ public class TempModManager
|
|||
public bool CollectionByName( string name, [NotNullWhen( true )] out ModCollection? collection )
|
||||
=> Collections.Values.FindFirst( c => string.Equals( c.Name, name, StringComparison.OrdinalIgnoreCase ), out collection );
|
||||
|
||||
public event ModCollection.Manager.CollectionChangeDelegate? CollectionChanged;
|
||||
|
||||
// These functions to check specific redirections or meta manipulations for existence are currently unused.
|
||||
//public bool IsRegistered( string tag, ModCollection? collection, Utf8GamePath gamePath, out FullPath? fullPath, out int priority )
|
||||
//{
|
||||
|
|
@ -151,6 +153,7 @@ public class TempModManager
|
|||
{
|
||||
var collection = ModCollection.CreateNewTemporary( tag, characterName );
|
||||
_collections[ characterName ] = collection;
|
||||
CollectionChanged?.Invoke(CollectionType.Temporary, null, collection );
|
||||
return collection.Name;
|
||||
}
|
||||
|
||||
|
|
@ -160,6 +163,7 @@ public class TempModManager
|
|||
{
|
||||
_mods.Remove( c );
|
||||
c.ClearCache();
|
||||
CollectionChanged?.Invoke( CollectionType.Temporary, c, null );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ public partial class ModCollection
|
|||
RemoveCache( oldCollectionIdx );
|
||||
|
||||
UpdateCurrentCollectionInUse();
|
||||
CollectionChanged.Invoke( collectionType, this[ oldCollectionIdx ], newCollection, Individuals[ individualIndex ].DisplayName );
|
||||
CollectionChanged.Invoke( collectionType, this[ oldCollectionIdx ], newCollection, collectionType == CollectionType.Individual ? Individuals[ individualIndex ].DisplayName : string.Empty );
|
||||
}
|
||||
|
||||
private void UpdateCurrentCollectionInUse()
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ public enum CollectionType : byte
|
|||
Interface, // The ui collection was changed
|
||||
Individual, // An individual collection was changed
|
||||
Current, // The current collection was changed
|
||||
Temporary, // A temporary collections was set or deleted via IPC
|
||||
}
|
||||
|
||||
public static class CollectionTypeExtensions
|
||||
|
|
|
|||
129
Penumbra/Interop/Resolver/IdentifiedCollectionCache.cs
Normal file
129
Penumbra/Interop/Resolver/IdentifiedCollectionCache.cs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
||||
namespace Penumbra.Interop.Resolver;
|
||||
|
||||
public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable< (IntPtr Address, ActorIdentifier Identifier, ModCollection Collection) >
|
||||
{
|
||||
private readonly Dictionary< IntPtr, (ActorIdentifier, ModCollection) > _cache = new(317);
|
||||
private bool _dirty = false;
|
||||
private bool _enabled = false;
|
||||
|
||||
public IdentifiedCollectionCache()
|
||||
{
|
||||
SignatureHelper.Initialise( this );
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
if( _enabled )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Penumbra.CollectionManager.CollectionChanged += CollectionChangeClear;
|
||||
Penumbra.TempMods.CollectionChanged += CollectionChangeClear;
|
||||
Dalamud.ClientState.TerritoryChanged += TerritoryClear;
|
||||
_characterDtorHook.Enable();
|
||||
_enabled = true;
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
if( !_enabled )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Penumbra.CollectionManager.CollectionChanged -= CollectionChangeClear;
|
||||
Penumbra.TempMods.CollectionChanged -= CollectionChangeClear;
|
||||
Dalamud.ClientState.TerritoryChanged -= TerritoryClear;
|
||||
_characterDtorHook.Disable();
|
||||
_enabled = false;
|
||||
}
|
||||
|
||||
public ResolveData Set( ModCollection collection, ActorIdentifier identifier, GameObject* data )
|
||||
{
|
||||
if( _dirty )
|
||||
{
|
||||
_dirty = false;
|
||||
_cache.Clear();
|
||||
}
|
||||
|
||||
_cache[ ( IntPtr )data ] = ( identifier, collection );
|
||||
return collection.ToResolveData( data );
|
||||
}
|
||||
|
||||
public bool TryGetValue( GameObject* gameObject, out ResolveData resolve )
|
||||
{
|
||||
if( _dirty )
|
||||
{
|
||||
_dirty = false;
|
||||
_cache.Clear();
|
||||
}
|
||||
else if( _cache.TryGetValue( ( IntPtr )gameObject, out var p ) )
|
||||
{
|
||||
resolve = p.Item2.ToResolveData( gameObject );
|
||||
return true;
|
||||
}
|
||||
|
||||
resolve = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disable();
|
||||
_characterDtorHook.Dispose();
|
||||
GC.SuppressFinalize( this );
|
||||
}
|
||||
|
||||
public IEnumerator< (IntPtr Address, ActorIdentifier Identifier, ModCollection Collection) > GetEnumerator()
|
||||
{
|
||||
foreach( var (address, (identifier, collection)) in _cache )
|
||||
{
|
||||
if( _dirty )
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return ( address, identifier, collection );
|
||||
}
|
||||
}
|
||||
|
||||
~IdentifiedCollectionCache()
|
||||
=> Dispose();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
private void CollectionChangeClear( CollectionType type, ModCollection? _1, ModCollection? _2, string _3 )
|
||||
{
|
||||
if( type is not (CollectionType.Current or CollectionType.Interface or CollectionType.Inactive) )
|
||||
{
|
||||
_dirty = _cache.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void TerritoryClear( object? _1, ushort _2 )
|
||||
=> _dirty = _cache.Count > 0;
|
||||
|
||||
private delegate void CharacterDestructorDelegate( Character* character );
|
||||
|
||||
[Signature( "48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 48 8D 05 ?? ?? ?? ?? 48 8B D9 48 89 01 48 8D 05 ?? ?? ?? ?? 48 89 81 ?? ?? ?? ?? 48 8D 05",
|
||||
DetourName = nameof( CharacterDestructorDetour ) )]
|
||||
private Hook< CharacterDestructorDelegate > _characterDtorHook = null!;
|
||||
|
||||
private void CharacterDestructorDetour( Character* character )
|
||||
{
|
||||
_cache.Remove( ( IntPtr )character );
|
||||
_characterDtorHook.Original( character );
|
||||
}
|
||||
}
|
||||
|
|
@ -118,7 +118,7 @@ public unsafe partial class PathResolver
|
|||
if( idx >= 0 && idx < Dalamud.Objects.Length )
|
||||
{
|
||||
var obj = Dalamud.Objects[ idx ];
|
||||
_animationLoadData = obj != null ? IdentifyCollection( ( GameObject* )obj.Address ) : ResolveData.Invalid;
|
||||
_animationLoadData = obj != null ? IdentifyCollection( ( GameObject* )obj.Address, true ) : ResolveData.Invalid;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -165,7 +165,7 @@ public unsafe partial class PathResolver
|
|||
private ulong LoadSomeAvfxDetour( uint a1, IntPtr gameObject, IntPtr gameObject2, float unk1, IntPtr unk2, IntPtr unk3 )
|
||||
{
|
||||
var last = _animationLoadData;
|
||||
_animationLoadData = IdentifyCollection( ( GameObject* )gameObject );
|
||||
_animationLoadData = IdentifyCollection( ( GameObject* )gameObject, true );
|
||||
var ret = _loadSomeAvfxHook.Original( a1, gameObject, gameObject2, unk1, unk2, unk3 );
|
||||
_animationLoadData = last;
|
||||
return ret;
|
||||
|
|
@ -187,7 +187,7 @@ public unsafe partial class PathResolver
|
|||
var actorIdx = ( int )( *( *( ulong** )timelinePtr + 1 ) >> 3 );
|
||||
if( actorIdx >= 0 && actorIdx < Dalamud.Objects.Length )
|
||||
{
|
||||
_animationLoadData = IdentifyCollection( ( GameObject* )( Dalamud.Objects[ actorIdx ]?.Address ?? IntPtr.Zero ) );
|
||||
_animationLoadData = IdentifyCollection( ( GameObject* )( Dalamud.Objects[ actorIdx ]?.Address ?? IntPtr.Zero ), true );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +202,7 @@ public unsafe partial class PathResolver
|
|||
private void SomeActionLoadDetour( IntPtr gameObject )
|
||||
{
|
||||
var last = _animationLoadData;
|
||||
_animationLoadData = IdentifyCollection( ( GameObject* )gameObject );
|
||||
_animationLoadData = IdentifyCollection( ( GameObject* )gameObject, true );
|
||||
_someActionLoadHook.Original( gameObject );
|
||||
_animationLoadData = last;
|
||||
}
|
||||
|
|
@ -214,7 +214,7 @@ public unsafe partial class PathResolver
|
|||
{
|
||||
var last = _animationLoadData;
|
||||
var gameObject = ( GameObject* )( unk - 0x8D0 );
|
||||
_animationLoadData = IdentifyCollection( gameObject );
|
||||
_animationLoadData = IdentifyCollection( gameObject, true );
|
||||
_someOtherAvfxHook.Original( unk );
|
||||
_animationLoadData = last;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public unsafe partial class PathResolver
|
|||
{
|
||||
if( parentObject == IntPtr.Zero && LastGameObject != null )
|
||||
{
|
||||
var collection = IdentifyCollection( LastGameObject );
|
||||
var collection = IdentifyCollection( LastGameObject, true );
|
||||
_drawObjectToObject[ drawObject ] = ( collection, LastGameObject->ObjectIndex );
|
||||
return collection;
|
||||
}
|
||||
|
|
@ -86,6 +86,7 @@ public unsafe partial class PathResolver
|
|||
_weaponReloadHook.Enable();
|
||||
InitializeDrawObjects();
|
||||
Penumbra.CollectionManager.CollectionChanged += CheckCollections;
|
||||
Penumbra.TempMods.CollectionChanged += CheckCollections;
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
|
|
@ -95,6 +96,7 @@ public unsafe partial class PathResolver
|
|||
_enableDrawHook.Disable();
|
||||
_weaponReloadHook.Disable();
|
||||
Penumbra.CollectionManager.CollectionChanged -= CheckCollections;
|
||||
Penumbra.TempMods.CollectionChanged -= CheckCollections;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
@ -139,7 +141,7 @@ public unsafe partial class PathResolver
|
|||
var meta = DisposableContainer.Empty;
|
||||
if( LastGameObject != null )
|
||||
{
|
||||
_lastCreatedCollection = IdentifyCollection( LastGameObject );
|
||||
_lastCreatedCollection = IdentifyCollection( LastGameObject, false );
|
||||
// Change the transparent or 1.0 Decal if necessary.
|
||||
var decal = new CharacterUtility.DecalReverter( _lastCreatedCollection.ModCollection, UsesDecal( a, c ) );
|
||||
// Change the rsp parameters if necessary.
|
||||
|
|
@ -235,7 +237,7 @@ public unsafe partial class PathResolver
|
|||
_drawObjectToObject.Remove( key );
|
||||
}
|
||||
|
||||
var newCollection = IdentifyCollection( obj );
|
||||
var newCollection = IdentifyCollection( obj, false );
|
||||
_drawObjectToObject[ key ] = ( newCollection, idx );
|
||||
}
|
||||
}
|
||||
|
|
@ -249,7 +251,7 @@ public unsafe partial class PathResolver
|
|||
var ptr = ( GameObject* )Dalamud.Objects.GetObjectAddress( i );
|
||||
if( ptr != null && ptr->IsCharacter() && ptr->DrawObject != null )
|
||||
{
|
||||
_drawObjectToObject[ ( IntPtr )ptr->DrawObject ] = ( IdentifyCollection( ptr ), ptr->ObjectIndex );
|
||||
_drawObjectToObject[ ( IntPtr )ptr->DrawObject ] = ( IdentifyCollection( ptr, false ), ptr->ObjectIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 )
|
||||
private static ResolveData IdentifyCollection( GameObject* gameObject, bool useCache )
|
||||
{
|
||||
if( gameObject == null )
|
||||
{
|
||||
|
|
@ -26,6 +26,11 @@ public unsafe partial class PathResolver
|
|||
|
||||
try
|
||||
{
|
||||
if( useCache && IdentifiedCache.TryGetValue( gameObject, out var data ) )
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
// Login screen. Names are populated after actors are drawn,
|
||||
// so it is not possible to fetch names from the ui list.
|
||||
// Actors are also not named. So use Yourself > Players > Racial > Default.
|
||||
|
|
@ -34,7 +39,7 @@ public unsafe partial class PathResolver
|
|||
var collection = Penumbra.CollectionManager.ByType( CollectionType.Yourself )
|
||||
?? CollectionByAttributes( gameObject )
|
||||
?? Penumbra.CollectionManager.Default;
|
||||
return collection.ToResolveData( gameObject );
|
||||
return IdentifiedCache.Set( collection, ActorIdentifier.Invalid, gameObject );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -44,7 +49,7 @@ public unsafe partial class PathResolver
|
|||
?? CollectionByAttributes( gameObject )
|
||||
?? CheckOwnedCollection( identifier, gameObject )
|
||||
?? Penumbra.CollectionManager.Default;
|
||||
return collection.ToResolveData( gameObject );
|
||||
return IdentifiedCache.Set( collection, identifier, gameObject );
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ using System.Runtime.CompilerServices;
|
|||
using Dalamud.Hooking;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Interop.Resolver;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ public partial class PathResolver : IDisposable
|
|||
private static readonly CutsceneCharacters Cutscenes = new();
|
||||
private static readonly DrawObjectState DrawObjects = new();
|
||||
private static readonly BitArray ValidHumanModels;
|
||||
internal static readonly IdentifiedCollectionCache IdentifiedCache = new();
|
||||
private readonly AnimationState _animations;
|
||||
private readonly PathState _paths;
|
||||
private readonly MetaState _meta;
|
||||
|
|
@ -87,6 +88,7 @@ public partial class PathResolver : IDisposable
|
|||
Enabled = true;
|
||||
Cutscenes.Enable();
|
||||
DrawObjects.Enable();
|
||||
IdentifiedCache.Enable();
|
||||
_animations.Enable();
|
||||
_paths.Enable();
|
||||
_meta.Enable();
|
||||
|
|
@ -107,6 +109,7 @@ public partial class PathResolver : IDisposable
|
|||
_animations.Disable();
|
||||
DrawObjects.Disable();
|
||||
Cutscenes.Disable();
|
||||
IdentifiedCache.Disable();
|
||||
_paths.Disable();
|
||||
_meta.Disable();
|
||||
_materials.Disable();
|
||||
|
|
@ -122,6 +125,7 @@ public partial class PathResolver : IDisposable
|
|||
_animations.Dispose();
|
||||
DrawObjects.Dispose();
|
||||
Cutscenes.Dispose();
|
||||
IdentifiedCache.Dispose();
|
||||
_meta.Dispose();
|
||||
_materials.Dispose();
|
||||
}
|
||||
|
|
@ -147,11 +151,11 @@ public partial class PathResolver : IDisposable
|
|||
if( DrawObjects.LastGameObject != null
|
||||
&& ( DrawObjects.LastGameObject->DrawObject == null || DrawObjects.LastGameObject->DrawObject == ( DrawObject* )drawObject ) )
|
||||
{
|
||||
resolveData = IdentifyCollection( DrawObjects.LastGameObject );
|
||||
resolveData = IdentifyCollection( DrawObjects.LastGameObject, true );
|
||||
return DrawObjects.LastGameObject;
|
||||
}
|
||||
|
||||
resolveData = IdentifyCollection( null );
|
||||
resolveData = IdentifyCollection( null, true );
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
||||
namespace Penumbra.UI.Classes;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using OtterGui.Raii;
|
|||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.Interop.Loader;
|
||||
using Penumbra.Interop.Resolver;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.String;
|
||||
using CharacterUtility = Penumbra.Interop.CharacterUtility;
|
||||
|
|
@ -250,6 +251,23 @@ public partial class ConfigWindow
|
|||
}
|
||||
}
|
||||
|
||||
using( var identifiedTree = ImRaii.TreeNode( "Identified Collections" ) )
|
||||
{
|
||||
if( identifiedTree )
|
||||
{
|
||||
using var table = ImRaii.Table( "##PathCollectionsIdentifiedTable", 3, ImGuiTableFlags.SizingFixedFit );
|
||||
if( table )
|
||||
{
|
||||
foreach( var (address, identifier, collection) in PathResolver.IdentifiedCache )
|
||||
{
|
||||
ImGuiUtil.DrawTableColumn( $"0x{address:X}" );
|
||||
ImGuiUtil.DrawTableColumn( identifier.ToString() );
|
||||
ImGuiUtil.DrawTableColumn( collection.Name );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using var cutsceneTree = ImRaii.TreeNode( "Cutscene Actors" );
|
||||
if( cutsceneTree )
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue