diff --git a/Penumbra/Interop/Loader/CharacterResolver.cs b/Penumbra/Interop/Loader/CharacterResolver.cs index 68b83b41..640cc0d9 100644 --- a/Penumbra/Interop/Loader/CharacterResolver.cs +++ b/Penumbra/Interop/Loader/CharacterResolver.cs @@ -76,7 +76,7 @@ public class CharacterResolver : IDisposable _loader.FileLoaded -= ImcLoadResource; } - // Use the default method of path replacement. + /// Use the default method of path replacement. private (FullPath?, ResolveData) DefaultResolver(Utf8GamePath path) { var resolved = _collectionManager.Default.ResolvePath(path); diff --git a/Penumbra/Interop/Resolver/CutsceneCharacters.cs b/Penumbra/Interop/Resolver/CutsceneService.cs similarity index 78% rename from Penumbra/Interop/Resolver/CutsceneCharacters.cs rename to Penumbra/Interop/Resolver/CutsceneService.cs index 06b4f228..4bb8799c 100644 --- a/Penumbra/Interop/Resolver/CutsceneCharacters.cs +++ b/Penumbra/Interop/Resolver/CutsceneService.cs @@ -8,7 +8,7 @@ using Penumbra.Interop.Services; namespace Penumbra.Interop.Resolver; -public class CutsceneCharacters : IDisposable +public class CutsceneService : IDisposable { public const int CutsceneStartIdx = 200; public const int CutsceneSlots = 40; @@ -23,7 +23,7 @@ public class CutsceneCharacters : IDisposable .Where(i => _objects[i] != null) .Select(i => KeyValuePair.Create(i, this[i] ?? _objects[i]!)); - public CutsceneCharacters(ObjectTable objects, GameEventManager events) + public CutsceneService(ObjectTable objects, GameEventManager events) { _objects = objects; _events = events; @@ -69,19 +69,19 @@ public class CutsceneCharacters : IDisposable private unsafe void OnCharacterDestructor(Character* character) { - if (character->GameObject.ObjectIndex is >= CutsceneStartIdx and < CutsceneEndIdx) - { - var idx = character->GameObject.ObjectIndex - CutsceneStartIdx; - _copiedCharacters[idx] = -1; - } + if (character->GameObject.ObjectIndex is < CutsceneStartIdx or >= CutsceneEndIdx) + return; + + var idx = character->GameObject.ObjectIndex - CutsceneStartIdx; + _copiedCharacters[idx] = -1; } private unsafe void OnCharacterCopy(Character* target, Character* source) { - if (target != null && target->GameObject.ObjectIndex is >= CutsceneStartIdx and < CutsceneEndIdx) - { - var idx = target->GameObject.ObjectIndex - CutsceneStartIdx; - _copiedCharacters[idx] = (short)(source != null ? source->GameObject.ObjectIndex : -1); - } + if (target == null || target->GameObject.ObjectIndex is < CutsceneStartIdx or >= CutsceneEndIdx) + return; + + var idx = target->GameObject.ObjectIndex - CutsceneStartIdx; + _copiedCharacters[idx] = (short)(source != null ? source->GameObject.ObjectIndex : -1); } } diff --git a/Penumbra/Interop/Resolver/IdentifiedCollectionCache.cs b/Penumbra/Interop/Resolver/IdentifiedCollectionCache.cs index fa09f64d..83739dca 100644 --- a/Penumbra/Interop/Resolver/IdentifiedCollectionCache.cs +++ b/Penumbra/Interop/Resolver/IdentifiedCollectionCache.cs @@ -1,27 +1,31 @@ using System; using System.Collections; using System.Collections.Generic; +using Dalamud.Game.ClientState; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Object; using Penumbra.Collections; using Penumbra.GameData.Actors; -using Penumbra.Interop.Services; +using Penumbra.Interop.Services; using Penumbra.Services; namespace Penumbra.Interop.Resolver; -public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable<(IntPtr Address, ActorIdentifier Identifier, ModCollection Collection)> +public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable<(nint Address, ActorIdentifier Identifier, ModCollection Collection)> { private readonly CommunicatorService _communicator; private readonly GameEventManager _events; - private readonly Dictionary _cache = new(317); - private bool _dirty = false; - private bool _enabled = false; + private readonly ClientState _clientState; + private readonly Dictionary _cache = new(317); + private bool _dirty; + private bool _enabled; - public IdentifiedCollectionCache(CommunicatorService communicator, GameEventManager events) + public IdentifiedCollectionCache(ClientState clientState, CommunicatorService communicator, GameEventManager events) { + _clientState = clientState; _communicator = communicator; _events = events; + Enable(); } public void Enable() @@ -29,10 +33,10 @@ public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable<(IntPtr if (_enabled) return; - _communicator.CollectionChange.Event += CollectionChangeClear; - DalamudServices.SClientState.TerritoryChanged += TerritoryClear; - _events.CharacterDestructor += OnCharacterDestruct; - _enabled = true; + _communicator.CollectionChange.Event += CollectionChangeClear; + _clientState.TerritoryChanged += TerritoryClear; + _events.CharacterDestructor += OnCharacterDestruct; + _enabled = true; } public void Disable() @@ -40,10 +44,10 @@ public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable<(IntPtr if (!_enabled) return; - _communicator.CollectionChange.Event -= CollectionChangeClear; - DalamudServices.SClientState.TerritoryChanged -= TerritoryClear; - _events.CharacterDestructor -= OnCharacterDestruct; - _enabled = false; + _communicator.CollectionChange.Event -= CollectionChangeClear; + _clientState.TerritoryChanged -= TerritoryClear; + _events.CharacterDestructor -= OnCharacterDestruct; + _enabled = false; } public ResolveData Set(ModCollection collection, ActorIdentifier identifier, GameObject* data) @@ -54,7 +58,7 @@ public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable<(IntPtr _cache.Clear(); } - _cache[(IntPtr)data] = (identifier, collection); + _cache[(nint)data] = (identifier, collection); return collection.ToResolveData(data); } @@ -65,7 +69,7 @@ public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable<(IntPtr _dirty = false; _cache.Clear(); } - else if (_cache.TryGetValue((IntPtr)gameObject, out var p)) + else if (_cache.TryGetValue((nint)gameObject, out var p)) { resolve = p.Item2.ToResolveData(gameObject); return true; @@ -81,7 +85,7 @@ public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable<(IntPtr GC.SuppressFinalize(this); } - public IEnumerator<(IntPtr Address, ActorIdentifier Identifier, ModCollection Collection)> GetEnumerator() + public IEnumerator<(nint Address, ActorIdentifier Identifier, ModCollection Collection)> GetEnumerator() { foreach (var (address, (identifier, collection)) in _cache) { @@ -108,5 +112,5 @@ public unsafe class IdentifiedCollectionCache : IDisposable, IEnumerable<(IntPtr => _dirty = _cache.Count > 0; private void OnCharacterDestruct(Character* character) - => _cache.Remove((IntPtr)character); + => _cache.Remove((nint)character); } diff --git a/Penumbra/Interop/Resolver/PathResolver.PathState.cs b/Penumbra/Interop/Resolver/PathResolver.PathState.cs index 2b4aceca..1cf2b206 100644 --- a/Penumbra/Interop/Resolver/PathResolver.PathState.cs +++ b/Penumbra/Interop/Resolver/PathResolver.PathState.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Threading; using Dalamud.Utility.Signatures; using Penumbra.Collections; using Penumbra.GameData; @@ -33,6 +34,8 @@ public unsafe partial class PathResolver // This map links files to their corresponding collection, if it is non-default. private readonly ConcurrentDictionary< ByteString, ResolveData > _pathCollections = new(); + private readonly ThreadLocal _resolveData = new ThreadLocal(() => ResolveData.Invalid, true); + public PathState( PathResolver parent ) { SignatureHelper.Initialise( this ); @@ -60,6 +63,7 @@ public unsafe partial class PathResolver public void Dispose() { + _resolveData.Dispose(); _human.Dispose(); _weapon.Dispose(); _demiHuman.Dispose(); @@ -76,7 +80,10 @@ public unsafe partial class PathResolver => _pathCollections.TryGetValue( path, out collection ); public bool Consume( ByteString path, out ResolveData collection ) - => _pathCollections.TryRemove( path, out collection ); + { + collection = _resolveData.IsValueCreated && _resolveData.Value.Valid ? _resolveData.Value : ResolveData.Invalid; + return _pathCollections.TryRemove(path, out collection); + } // Just add or remove the resolved path. [MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )] @@ -89,6 +96,7 @@ public unsafe partial class PathResolver var gamePath = new ByteString( ( byte* )path ); SetCollection( gameObject, gamePath, collection ); + _resolveData.Value = collection.ToResolveData(gameObject); return path; } diff --git a/Penumbra/Interop/Resolver/PathResolver.cs b/Penumbra/Interop/Resolver/PathResolver.cs index 059a0550..3b805acd 100644 --- a/Penumbra/Interop/Resolver/PathResolver.cs +++ b/Penumbra/Interop/Resolver/PathResolver.cs @@ -1,16 +1,14 @@ using System; using System.Collections; using System.Collections.Generic; +using Dalamud.Game.ClientState; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using FFXIVClientStructs.FFXIV.Client.System.Resource; -using OtterGui.Classes; using Penumbra.Collections; using Penumbra.GameData.Enums; using Penumbra.Interop.Loader; using Penumbra.Interop.Services; -using Penumbra.Interop.Structs; using Penumbra.Services; using Penumbra.String; using Penumbra.String.Classes; @@ -18,6 +16,20 @@ using Penumbra.Util; namespace Penumbra.Interop.Resolver; +//public class PathResolver2 : IDisposable +//{ +// public readonly CutsceneService Cutscenes; +// public readonly IdentifiedCollectionCache Identified; +// +// public PathResolver(StartTracker timer, CutsceneService cutscenes, IdentifiedCollectionCache identified) +// { +// using var t = timer.Measure(StartTimeType.PathResolver); +// Cutscenes = cutscenes; +// Identified = identified; +// } +//} + + // The Path Resolver handles character collections. // It will hook any path resolving functions for humans, // as well as DrawObject creation. @@ -29,7 +41,7 @@ public partial class PathResolver : IDisposable private readonly CommunicatorService _communicator; private readonly ResourceLoader _loader; - private static readonly CutsceneCharacters Cutscenes = new(DalamudServices.SObjects, Penumbra.GameEvents); // TODO + private static readonly CutsceneService Cutscenes = new(DalamudServices.SObjects, Penumbra.GameEvents); // TODO private static DrawObjectState _drawObjects = null!; // TODO private static readonly BitArray ValidHumanModels; internal static IdentifiedCollectionCache IdentifiedCache = null!; // TODO @@ -41,11 +53,11 @@ public partial class PathResolver : IDisposable static PathResolver() => ValidHumanModels = GetValidHumanModels(DalamudServices.SGameData); - public unsafe PathResolver(StartTracker timer, CommunicatorService communicator, GameEventManager events, ResourceLoader loader) + public unsafe PathResolver(IdentifiedCollectionCache cache, StartTracker timer, ClientState clientState, CommunicatorService communicator, GameEventManager events, ResourceLoader loader) { using var tApi = timer.Measure(StartTimeType.PathResolver); _communicator = communicator; - IdentifiedCache = new IdentifiedCollectionCache(communicator, events); + IdentifiedCache = cache; SignatureHelper.Initialise(this); _drawObjects = new DrawObjectState(_communicator); _loader = loader; diff --git a/Penumbra/PenumbraNew.cs b/Penumbra/PenumbraNew.cs index 3d52c6be..c74af610 100644 --- a/Penumbra/PenumbraNew.cs +++ b/Penumbra/PenumbraNew.cs @@ -15,7 +15,6 @@ using Penumbra.Interop.Services; using Penumbra.Mods; using Penumbra.Services; using Penumbra.UI; -using Penumbra.UI.Classes; using Penumbra.UI.AdvancedWindow; using Penumbra.UI.ModsTab; using Penumbra.UI.Tabs; @@ -60,12 +59,12 @@ public class PenumbraNew .AddSingleton() .AddSingleton() .AddSingleton(); - + // Add Game Services services.AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() @@ -74,7 +73,11 @@ public class PenumbraNew .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton(); + .AddSingleton(); + + // Add PathResolver + services.AddSingleton() + .AddSingleton(); // Add Configuration services.AddTransient() diff --git a/Penumbra/Services/Wrappers.cs b/Penumbra/Services/Wrappers.cs index e0ab8aae..131507cc 100644 --- a/Penumbra/Services/Wrappers.cs +++ b/Penumbra/Services/Wrappers.cs @@ -29,7 +29,7 @@ public sealed class ItemService : AsyncServiceWrapper public sealed class ActorService : AsyncServiceWrapper { public ActorService(StartTracker tracker, DalamudPluginInterface pi, ObjectTable objects, ClientState clientState, - Framework framework, DataManager gameData, GameGui gui, CutsceneCharacters cutscene) + Framework framework, DataManager gameData, GameGui gui, CutsceneService cutscene) : base(nameof(ActorService), tracker, StartTimeType.Actors, () => new ActorManager(pi, objects, clientState, framework, gameData, gui, idx => (short)cutscene.GetParentIndex(idx))) { }