Add GameEventManager, change cutscene character and subfile container resets.

This commit is contained in:
Ottermandias 2023-01-22 15:16:53 +01:00
parent 7ab1426a2c
commit 24fda725a2
7 changed files with 183 additions and 107 deletions

View file

@ -2,11 +2,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Hooking;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Penumbra.GameData;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
namespace Penumbra.Interop.Resolver;
@ -16,17 +12,18 @@ public class CutsceneCharacters : IDisposable
public const int CutsceneSlots = 40;
public const int CutsceneEndIdx = CutsceneStartIdx + CutsceneSlots;
private readonly short[] _copiedCharacters = Enumerable.Repeat( ( short )-1, CutsceneSlots ).ToArray();
private readonly GameEventManager _events;
private readonly short[] _copiedCharacters = Enumerable.Repeat( ( short )-1, CutsceneSlots ).ToArray();
public IEnumerable< KeyValuePair< int, global::Dalamud.Game.ClientState.Objects.Types.GameObject > > Actors
=> Enumerable.Range( CutsceneStartIdx, CutsceneSlots )
.Where( i => Dalamud.Objects[ i ] != null )
.Select( i => KeyValuePair.Create( i, this[ i ] ?? Dalamud.Objects[ i ]! ) );
public CutsceneCharacters()
public CutsceneCharacters(GameEventManager events)
{
SignatureHelper.Initialise( this );
Dalamud.Conditions.ConditionChange += Reset;
_events = events;
Enable();
}
// Get the related actor to a cutscene actor.
@ -53,71 +50,36 @@ public class CutsceneCharacters : IDisposable
return -1;
}
public void Reset( ConditionFlag flag, bool value )
public unsafe void Enable()
{
switch( flag )
{
case ConditionFlag.BetweenAreas:
case ConditionFlag.BetweenAreas51:
if( !value )
{
return;
}
break;
case ConditionFlag.OccupiedInCutSceneEvent:
case ConditionFlag.WatchingCutscene:
case ConditionFlag.WatchingCutscene78:
if( value )
{
return;
}
break;
default: return;
}
for( var i = 0; i < _copiedCharacters.Length; ++i )
{
_copiedCharacters[ i ] = -1;
}
_events.CopyCharacter += OnCharacterCopy;
_events.CharacterDestructor += OnCharacterDestructor;
}
public void Enable()
=> _copyCharacterHook.Enable();
public void Disable()
=> _copyCharacterHook.Disable();
public unsafe void Disable()
{
_events.CopyCharacter -= OnCharacterCopy;
_events.CharacterDestructor -= OnCharacterDestructor;
}
public void Dispose()
=> Disable();
private unsafe void OnCharacterDestructor( Character* character )
{
_copyCharacterHook.Dispose();
Dalamud.Conditions.ConditionChange -= Reset;
if( character->GameObject.ObjectIndex is >= CutsceneStartIdx and < CutsceneEndIdx )
{
var idx = character->GameObject.ObjectIndex - CutsceneStartIdx;
_copiedCharacters[ idx ] = -1;
}
}
private unsafe delegate ulong CopyCharacterDelegate( GameObject* target, GameObject* source, uint unk );
[Signature( Sigs.CopyCharacter, DetourName = nameof( CopyCharacterDetour ) )]
private readonly Hook< CopyCharacterDelegate > _copyCharacterHook = null!;
private unsafe ulong CopyCharacterDetour( GameObject* target, GameObject* source, uint unk )
private unsafe void OnCharacterCopy( Character* target, Character* source )
{
try
if( target != null && target->GameObject.ObjectIndex is >= CutsceneStartIdx and < CutsceneEndIdx )
{
if( target != null && target->ObjectIndex is >= CutsceneStartIdx and < CutsceneEndIdx )
{
var parent = source == null || source->ObjectIndex is < 0 or >= CutsceneStartIdx
? -1
: source->ObjectIndex;
_copiedCharacters[ target->ObjectIndex - CutsceneStartIdx ] = ( short )parent;
Penumbra.Log.Debug( $"Set cutscene character {target->ObjectIndex} to {parent}." );
}
var idx = target->GameObject.ObjectIndex - CutsceneStartIdx;
_copiedCharacters[idx] = (short) (source != null ? source->GameObject.ObjectIndex : -1);
}
catch
{
// ignored
}
return _copyCharacterHook.Original( target, source, unk );
}
}

View file

@ -1,25 +1,23 @@
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;
using Penumbra.GameData.Actors;
namespace Penumbra.Interop.Resolver;
public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable< (IntPtr Address, ActorIdentifier Identifier, ModCollection Collection) >
{
private readonly GameEventManager _events;
private readonly Dictionary< IntPtr, (ActorIdentifier, ModCollection) > _cache = new(317);
private bool _dirty = false;
private bool _enabled = false;
public IdentifiedCollectionCache()
public IdentifiedCollectionCache(GameEventManager events)
{
SignatureHelper.Initialise( this );
_events = events;
}
public void Enable()
@ -32,8 +30,8 @@ public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable< (IntPt
Penumbra.CollectionManager.CollectionChanged += CollectionChangeClear;
Penumbra.TempMods.CollectionChanged += CollectionChangeClear;
Dalamud.ClientState.TerritoryChanged += TerritoryClear;
_characterDtorHook.Enable();
_enabled = true;
_events.CharacterDestructor += OnCharacterDestruct;
_enabled = true;
}
public void Disable()
@ -46,8 +44,8 @@ public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable< (IntPt
Penumbra.CollectionManager.CollectionChanged -= CollectionChangeClear;
Penumbra.TempMods.CollectionChanged -= CollectionChangeClear;
Dalamud.ClientState.TerritoryChanged -= TerritoryClear;
_characterDtorHook.Disable();
_enabled = false;
_events.CharacterDestructor -= OnCharacterDestruct;
_enabled = false;
}
public ResolveData Set( ModCollection collection, ActorIdentifier identifier, GameObject* data )
@ -82,7 +80,6 @@ public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable< (IntPt
public void Dispose()
{
Disable();
_characterDtorHook.Dispose();
GC.SuppressFinalize( this );
}
@ -116,14 +113,6 @@ public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable< (IntPt
private void TerritoryClear( object? _1, ushort _2 )
=> _dirty = _cache.Count > 0;
private delegate void CharacterDestructorDelegate( Character* character );
[Signature( Sigs.CharacterDestructor, DetourName = nameof( CharacterDestructorDetour ) )]
private Hook< CharacterDestructorDelegate > _characterDtorHook = null!;
private void CharacterDestructorDetour( Character* character )
{
_cache.Remove( ( IntPtr )character );
_characterDtorHook.Original( character );
}
private void OnCharacterDestruct( Character* character )
=> _cache.Remove( ( IntPtr )character );
}

View file

@ -24,18 +24,20 @@ public unsafe partial class PathResolver
// Thus, we need to ensure the correct files are loaded when a material is loaded.
public class SubfileHelper : IDisposable, IReadOnlyCollection< KeyValuePair< IntPtr, ResolveData > >
{
private readonly ResourceLoader _loader;
private readonly ResourceLoader _loader;
private readonly GameEventManager _events;
private readonly ThreadLocal< ResolveData > _mtrlData = new(() => ResolveData.Invalid);
private readonly ThreadLocal< ResolveData > _avfxData = new(() => ResolveData.Invalid);
private readonly ConcurrentDictionary< IntPtr, ResolveData > _subFileCollection = new();
public SubfileHelper( ResourceLoader loader )
public SubfileHelper( ResourceLoader loader, GameEventManager events )
{
SignatureHelper.Initialise( this );
_loader = loader;
_events = events;
}
// Check specifically for shpk and tex files whether we are currently in a material load.
@ -85,7 +87,7 @@ public unsafe partial class PathResolver
_apricotResourceLoadHook.Enable();
_loader.ResourceLoadCustomization += SubfileLoadHandler;
_loader.ResourceLoaded += SubfileContainerRequested;
_loader.FileLoaded += SubfileContainerLoaded;
_events.ResourceHandleDestructor += ResourceDestroyed;
}
public void Disable()
@ -95,7 +97,7 @@ public unsafe partial class PathResolver
_apricotResourceLoadHook.Disable();
_loader.ResourceLoadCustomization -= SubfileLoadHandler;
_loader.ResourceLoaded -= SubfileContainerRequested;
_loader.FileLoaded -= SubfileContainerLoaded;
_events.ResourceHandleDestructor -= ResourceDestroyed;
}
public void Dispose()
@ -121,16 +123,8 @@ public unsafe partial class PathResolver
}
}
private void SubfileContainerLoaded( ResourceHandle* handle, ByteString path, bool success, bool custom )
{
switch( handle->FileType )
{
case ResourceType.Mtrl:
case ResourceType.Avfx:
_subFileCollection.TryRemove( ( IntPtr )handle, out _ );
break;
}
}
private void ResourceDestroyed( ResourceHandle* handle )
=> _subFileCollection.TryRemove( ( IntPtr )handle, out _ );
// We need to set the correct collection for the actual material path that is loaded
// before actually loading the file.

View file

@ -24,10 +24,10 @@ public partial class PathResolver : IDisposable
public bool Enabled { get; private set; }
private readonly ResourceLoader _loader;
private static readonly CutsceneCharacters Cutscenes = new();
private static readonly CutsceneCharacters Cutscenes = new(Penumbra.GameEvents);
private static readonly DrawObjectState DrawObjects = new();
private static readonly BitArray ValidHumanModels;
internal static readonly IdentifiedCollectionCache IdentifiedCache = new();
internal static readonly IdentifiedCollectionCache IdentifiedCache = new(Penumbra.GameEvents);
private readonly AnimationState _animations;
private readonly PathState _paths;
private readonly MetaState _meta;
@ -43,7 +43,7 @@ public partial class PathResolver : IDisposable
_animations = new AnimationState( DrawObjects );
_paths = new PathState( this );
_meta = new MetaState( _paths.HumanVTable );
_subFiles = new SubfileHelper( _loader );
_subFiles = new SubfileHelper( _loader, Penumbra.GameEvents );
}
// The modified resolver that handles game path resolving.
@ -175,6 +175,9 @@ public partial class PathResolver : IDisposable
internal IEnumerable< KeyValuePair< IntPtr, ResolveData > > ResourceCollections
=> _subFiles;
internal int SubfileCount
=> _subFiles.Count;
internal ResolveData CurrentMtrlData
=> _subFiles.MtrlData;