diff --git a/Penumbra.Api b/Penumbra.Api index 1df06807..d2a1406b 160000 --- a/Penumbra.Api +++ b/Penumbra.Api @@ -1 +1 @@ -Subproject commit 1df06807650a79813791effaa01fb7c4710b3dab +Subproject commit d2a1406bc32f715c0687613f02e3f74caf7ceea9 diff --git a/Penumbra.GameData b/Penumbra.GameData index d53db6a3..a1262e24 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit d53db6a358cedecd3ef18f62f12a07deff4b61ee +Subproject commit a1262e242ca33bb0e9e4f080d294d9160b5e54eb diff --git a/Penumbra/Api/IpcTester.cs b/Penumbra/Api/IpcTester.cs index 380b741c..e1dd81cb 100644 --- a/Penumbra/Api/IpcTester.cs +++ b/Penumbra/Api/IpcTester.cs @@ -1,3 +1,4 @@ +using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Interface; using Dalamud.Interface.Utility; using Dalamud.Plugin; @@ -18,6 +19,7 @@ using Penumbra.Collections.Manager; using Dalamud.Plugin.Services; using Penumbra.GameData.Structs; using Penumbra.GameData.Enums; +using Penumbra.GameData.Interop; namespace Penumbra.Api; @@ -40,7 +42,7 @@ public class IpcTester : IDisposable private readonly Temporary _temporary; private readonly ResourceTree _resourceTree; - public IpcTester(Configuration config, DalamudPluginInterface pi, IObjectTable objects, IClientState clientState, + public IpcTester(Configuration config, DalamudPluginInterface pi, ObjectManager objects, IClientState clientState, PenumbraIpcProviders ipcProviders, ModManager modManager, CollectionManager collections, TempModManager tempMods, TempCollectionManager tempCollections, SaveService saveService) { @@ -280,16 +282,16 @@ public class IpcTester : IDisposable { ImGui.SetNextWindowSize(ImGuiHelpers.ScaledVector2(500, 500)); using var popup = ImRaii.Popup("Config Popup"); - if (popup) - { - using (var font = ImRaii.PushFont(UiBuilder.MonoFont)) - { - ImGuiUtil.TextWrapped(_currentConfiguration); - } + if (!popup) + return; - if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused()) - ImGui.CloseCurrentPopup(); + using (ImRaii.PushFont(UiBuilder.MonoFont)) + { + ImGuiUtil.TextWrapped(_currentConfiguration); } + + if (ImGui.Button("Close", -Vector2.UnitX) || !ImGui.IsWindowFocused()) + ImGui.CloseCurrentPopup(); } private void UpdateModDirectoryChanged(string path, bool valid) @@ -304,15 +306,15 @@ public class IpcTester : IDisposable public readonly EventSubscriber Tooltip; public readonly EventSubscriber Click; - private string _lastDrawnMod = string.Empty; - private DateTimeOffset _lastDrawnModTime = DateTimeOffset.MinValue; - private bool _subscribedToTooltip = false; - private bool _subscribedToClick = false; - private string _lastClicked = string.Empty; - private string _lastHovered = string.Empty; - private TabType _selectTab = TabType.None; - private string _modName = string.Empty; - private PenumbraApiEc _ec = PenumbraApiEc.Success; + private string _lastDrawnMod = string.Empty; + private DateTimeOffset _lastDrawnModTime = DateTimeOffset.MinValue; + private bool _subscribedToTooltip; + private bool _subscribedToClick; + private string _lastClicked = string.Empty; + private string _lastHovered = string.Empty; + private TabType _selectTab = TabType.None; + private string _modName = string.Empty; + private PenumbraApiEc _ec = PenumbraApiEc.Success; public Ui(DalamudPluginInterface pi) { @@ -401,14 +403,14 @@ public class IpcTester : IDisposable { private readonly DalamudPluginInterface _pi; private readonly IClientState _clientState; - private readonly IObjectTable _objects; + private readonly ObjectManager _objects; public readonly EventSubscriber Redrawn; - private string _redrawName = string.Empty; - private int _redrawIndex = 0; + private string _redrawName = string.Empty; + private int _redrawIndex; private string _lastRedrawnString = "None"; - public Redrawing(DalamudPluginInterface pi, IObjectTable objects, IClientState clientState) + public Redrawing(DalamudPluginInterface pi, ObjectManager objects, IClientState clientState) { _pi = pi; _objects = objects; @@ -440,8 +442,8 @@ public class IpcTester : IDisposable DrawIntro(Ipc.RedrawObjectByIndex.Label, "Redraw by Index"); var tmp = _redrawIndex; ImGui.SetNextItemWidth(100 * UiHelpers.Scale); - if (ImGui.DragInt("##redrawIndex", ref tmp, 0.1f, 0, _objects.Length)) - _redrawIndex = Math.Clamp(tmp, 0, _objects.Length); + if (ImGui.DragInt("##redrawIndex", ref tmp, 0.1f, 0, _objects.Count)) + _redrawIndex = Math.Clamp(tmp, 0, _objects.Count); ImGui.SameLine(); if (ImGui.Button("Redraw##Index")) @@ -458,12 +460,12 @@ public class IpcTester : IDisposable private void SetLastRedrawn(IntPtr address, int index) { if (index < 0 - || index > _objects.Length + || index > _objects.Count || address == IntPtr.Zero - || _objects[index]?.Address != address) + || _objects[index].Address != address) _lastRedrawnString = "Invalid"; - _lastRedrawnString = $"{_objects[index]!.Name} (0x{address:X}, {index})"; + _lastRedrawnString = $"{_objects[index].Utf8Name} (0x{address:X}, {index})"; } } @@ -588,8 +590,8 @@ public class IpcTester : IDisposable private string _currentResolvePath = string.Empty; private string _currentResolveCharacter = string.Empty; private string _currentReversePath = string.Empty; - private int _currentReverseIdx = 0; - private Task<(string[], string[][])> _task = Task.FromException<(string[], string[][])>(new Exception()); + private int _currentReverseIdx; + private Task<(string[], string[][])> _task = Task.FromException<(string[], string[][])>(new Exception()); public Resolve(DalamudPluginInterface pi) => _pi = pi; @@ -696,8 +698,6 @@ public class IpcTester : IDisposable return text; } - ; - DrawIntro(Ipc.ResolvePlayerPaths.Label, "Resolved Paths (Player)"); if (forwardArray.Length > 0 || reverseArray.Length > 0) { @@ -721,18 +721,18 @@ public class IpcTester : IDisposable { private readonly DalamudPluginInterface _pi; - private int _objectIdx = 0; + private int _objectIdx; private string _collectionName = string.Empty; private bool _allowCreation = true; private bool _allowDeletion = true; private ApiCollectionType _type = ApiCollectionType.Current; private string _characterCollectionName = string.Empty; - private IList _collections = new List(); + private IList _collections = []; private string _changedItemCollection = string.Empty; private IReadOnlyDictionary _changedItems = new Dictionary(); private PenumbraApiEc _returnCode = PenumbraApiEc.Success; - private string? _oldCollection = null; + private string? _oldCollection; public Collections(DalamudPluginInterface pi) => _pi = pi; @@ -845,8 +845,8 @@ public class IpcTester : IDisposable { private readonly DalamudPluginInterface _pi; - private string _characterName = string.Empty; - private int _gameObjectIndex = 0; + private string _characterName = string.Empty; + private int _gameObjectIndex; public Meta(DalamudPluginInterface pi) => _pi = pi; @@ -1040,11 +1040,11 @@ public class IpcTester : IDisposable private string _settingsModName = string.Empty; private string _settingsCollection = string.Empty; private bool _settingsAllowInheritance = true; - private bool _settingsInherit = false; - private bool _settingsEnabled = false; - private int _settingsPriority = 0; + private bool _settingsInherit; + private bool _settingsEnabled; + private int _settingsPriority; private IDictionary, GroupType)>? _availableSettings; - private IDictionary>? _currentSettings = null; + private IDictionary>? _currentSettings; public ModSettings(DalamudPluginInterface pi) { @@ -1287,7 +1287,7 @@ public class IpcTester : IDisposable private string _tempFilePath = "test/success.mtrl"; private string _tempManipulation = string.Empty; private PenumbraApiEc _lastTempError; - private int _tempActorIndex = 0; + private int _tempActorIndex; private bool _forceOverwrite; public void Draw() @@ -1441,15 +1441,13 @@ public class IpcTester : IDisposable } } - private class ResourceTree + private class ResourceTree(DalamudPluginInterface pi, ObjectManager objects) { - private readonly DalamudPluginInterface _pi; - private readonly IObjectTable _objects; - private readonly Stopwatch _stopwatch = new(); + private readonly Stopwatch _stopwatch = new(); private string _gameObjectIndices = "0"; private ResourceType _type = ResourceType.Mtrl; - private bool _withUIData = false; + private bool _withUiData; private (string, IReadOnlyDictionary?)[]? _lastGameObjectResourcePaths; private (string, IReadOnlyDictionary?)[]? _lastPlayerResourcePaths; @@ -1459,12 +1457,6 @@ public class IpcTester : IDisposable private (string, Ipc.ResourceTree)[]? _lastPlayerResourceTrees; private TimeSpan _lastCallDuration; - public ResourceTree(DalamudPluginInterface pi, IObjectTable objects) - { - _pi = pi; - _objects = objects; - } - public void Draw() { using var _ = ImRaii.TreeNode("Resource Tree"); @@ -1473,7 +1465,7 @@ public class IpcTester : IDisposable ImGui.InputText("GameObject indices", ref _gameObjectIndices, 511); ImGuiUtil.GenericEnumCombo("Resource type", ImGui.CalcItemWidth(), _type, out _type, Enum.GetValues()); - ImGui.Checkbox("Also get names and icons", ref _withUIData); + ImGui.Checkbox("Also get names and icons", ref _withUiData); using var table = ImRaii.Table(string.Empty, 3, ImGuiTableFlags.SizingFixedFit); if (!table) @@ -1483,7 +1475,7 @@ public class IpcTester : IDisposable if (ImGui.Button("Get##GameObjectResourcePaths")) { var gameObjects = GetSelectedGameObjects(); - var subscriber = Ipc.GetGameObjectResourcePaths.Subscriber(_pi); + var subscriber = Ipc.GetGameObjectResourcePaths.Subscriber(pi); _stopwatch.Restart(); var resourcePaths = subscriber.Invoke(gameObjects); @@ -1499,7 +1491,7 @@ public class IpcTester : IDisposable DrawIntro(Ipc.GetPlayerResourcePaths.Label, "Get local player resource paths"); if (ImGui.Button("Get##PlayerResourcePaths")) { - var subscriber = Ipc.GetPlayerResourcePaths.Subscriber(_pi); + var subscriber = Ipc.GetPlayerResourcePaths.Subscriber(pi); _stopwatch.Restart(); var resourcePaths = subscriber.Invoke(); @@ -1515,9 +1507,9 @@ public class IpcTester : IDisposable if (ImGui.Button("Get##GameObjectResourcesOfType")) { var gameObjects = GetSelectedGameObjects(); - var subscriber = Ipc.GetGameObjectResourcesOfType.Subscriber(_pi); + var subscriber = Ipc.GetGameObjectResourcesOfType.Subscriber(pi); _stopwatch.Restart(); - var resourcesOfType = subscriber.Invoke(_type, _withUIData, gameObjects); + var resourcesOfType = subscriber.Invoke(_type, _withUiData, gameObjects); _lastCallDuration = _stopwatch.Elapsed; _lastGameObjectResourcesOfType = gameObjects @@ -1531,9 +1523,9 @@ public class IpcTester : IDisposable DrawIntro(Ipc.GetPlayerResourcesOfType.Label, "Get local player resources of type"); if (ImGui.Button("Get##PlayerResourcesOfType")) { - var subscriber = Ipc.GetPlayerResourcesOfType.Subscriber(_pi); + var subscriber = Ipc.GetPlayerResourcesOfType.Subscriber(pi); _stopwatch.Restart(); - var resourcesOfType = subscriber.Invoke(_type, _withUIData); + var resourcesOfType = subscriber.Invoke(_type, _withUiData); _lastCallDuration = _stopwatch.Elapsed; _lastPlayerResourcesOfType = resourcesOfType @@ -1547,9 +1539,9 @@ public class IpcTester : IDisposable if (ImGui.Button("Get##GameObjectResourceTrees")) { var gameObjects = GetSelectedGameObjects(); - var subscriber = Ipc.GetGameObjectResourceTrees.Subscriber(_pi); + var subscriber = Ipc.GetGameObjectResourceTrees.Subscriber(pi); _stopwatch.Restart(); - var trees = subscriber.Invoke(_withUIData, gameObjects); + var trees = subscriber.Invoke(_withUiData, gameObjects); _lastCallDuration = _stopwatch.Elapsed; _lastGameObjectResourceTrees = gameObjects @@ -1563,9 +1555,9 @@ public class IpcTester : IDisposable DrawIntro(Ipc.GetPlayerResourceTrees.Label, "Get local player resource trees"); if (ImGui.Button("Get##PlayerResourceTrees")) { - var subscriber = Ipc.GetPlayerResourceTrees.Subscriber(_pi); + var subscriber = Ipc.GetPlayerResourceTrees.Subscriber(pi); _stopwatch.Restart(); - var trees = subscriber.Invoke(_withUIData); + var trees = subscriber.Invoke(_withUiData); _lastCallDuration = _stopwatch.Elapsed; _lastPlayerResourceTrees = trees @@ -1666,13 +1658,13 @@ public class IpcTester : IDisposable { DrawWithHeaders(result, resources => { - using var table = ImRaii.Table(string.Empty, _withUIData ? 3 : 2, ImGuiTableFlags.SizingFixedFit); + using var table = ImRaii.Table(string.Empty, _withUiData ? 3 : 2, ImGuiTableFlags.SizingFixedFit); if (!table) return; ImGui.TableSetupColumn("Resource Handle", ImGuiTableColumnFlags.WidthStretch, 0.15f); - ImGui.TableSetupColumn("Actual Path", ImGuiTableColumnFlags.WidthStretch, _withUIData ? 0.55f : 0.85f); - if (_withUIData) + ImGui.TableSetupColumn("Actual Path", ImGuiTableColumnFlags.WidthStretch, _withUiData ? 0.55f : 0.85f); + if (_withUiData) ImGui.TableSetupColumn("Icon & Name", ImGuiTableColumnFlags.WidthStretch, 0.3f); ImGui.TableHeadersRow(); @@ -1682,7 +1674,7 @@ public class IpcTester : IDisposable TextUnformattedMono($"0x{resourceHandle:X}"); ImGui.TableNextColumn(); ImGui.TextUnformatted(actualPath); - if (_withUIData) + if (_withUiData) { ImGui.TableNextColumn(); TextUnformattedMono(icon.ToString()); @@ -1699,11 +1691,11 @@ public class IpcTester : IDisposable { ImGui.TextUnformatted($"Name: {tree.Name}\nRaceCode: {(GenderRace)tree.RaceCode}"); - using var table = ImRaii.Table(string.Empty, _withUIData ? 7 : 5, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.Resizable); + using var table = ImRaii.Table(string.Empty, _withUiData ? 7 : 5, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.Resizable); if (!table) return; - if (_withUIData) + if (_withUiData) { ImGui.TableSetupColumn("Name", ImGuiTableColumnFlags.WidthStretch, 0.5f); ImGui.TableSetupColumn("Type", ImGuiTableColumnFlags.WidthStretch, 0.1f); @@ -1726,11 +1718,11 @@ public class IpcTester : IDisposable ImGui.TableNextColumn(); var hasChildren = node.Children.Any(); using var treeNode = ImRaii.TreeNode( - $"{(_withUIData ? node.Name ?? "Unknown" : node.Type)}##{node.ObjectAddress:X8}", + $"{(_withUiData ? node.Name ?? "Unknown" : node.Type)}##{node.ObjectAddress:X8}", hasChildren ? ImGuiTreeNodeFlags.SpanFullWidth : ImGuiTreeNodeFlags.SpanFullWidth | ImGuiTreeNodeFlags.Leaf | ImGuiTreeNodeFlags.NoTreePushOnOpen); - if (_withUIData) + if (_withUiData) { ImGui.TableNextColumn(); TextUnformattedMono(node.Type.ToString()); @@ -1770,10 +1762,10 @@ public class IpcTester : IDisposable private unsafe string GameObjectToString(ObjectIndex gameObjectIndex) { - var gameObject = _objects[gameObjectIndex.Index]; + var gameObject = objects[gameObjectIndex]; - return gameObject != null - ? $"[{gameObjectIndex}] {gameObject.Name} ({gameObject.ObjectKind})" + return gameObject.Valid + ? $"[{gameObjectIndex}] {gameObject.Utf8Name} ({(ObjectKind)gameObject.AsObject->ObjectKind})" : $"[{gameObjectIndex}] null"; } } diff --git a/Penumbra/Api/PenumbraApi.cs b/Penumbra/Api/PenumbraApi.cs index f5bb67bd..5146383d 100644 --- a/Penumbra/Api/PenumbraApi.cs +++ b/Penumbra/Api/PenumbraApi.cs @@ -20,6 +20,7 @@ using Penumbra.String.Classes; using Penumbra.Services; using Penumbra.Collections.Manager; using Penumbra.Communication; +using Penumbra.GameData.Interop; using Penumbra.Import.Textures; using Penumbra.Interop.Services; using Penumbra.UI; @@ -93,7 +94,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi private IDataManager _gameData; private IFramework _framework; - private IObjectTable _objects; + private ObjectManager _objects; private ModManager _modManager; private ResourceLoader _resourceLoader; private Configuration _config; @@ -111,7 +112,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi private TextureManager _textureManager; private ResourceTreeFactory _resourceTreeFactory; - public unsafe PenumbraApi(CommunicatorService communicator, IDataManager gameData, IFramework framework, IObjectTable objects, + public unsafe PenumbraApi(CommunicatorService communicator, IDataManager gameData, IFramework framework, ObjectManager objects, ModManager modManager, ResourceLoader resourceLoader, Configuration config, CollectionManager collectionManager, TempCollectionManager tempCollections, TempModManager tempMods, ActorManager actors, CollectionResolver collectionResolver, CutsceneService cutsceneService, ModImportManager modImportManager, CollectionEditor collectionEditor, RedrawService redrawService, @@ -928,10 +929,10 @@ public class PenumbraApi : IDisposable, IPenumbraApi { CheckInitialized(); - if (actorIndex < 0 || actorIndex >= _objects.Length) + if (actorIndex < 0 || actorIndex >= _objects.Count) return PenumbraApiEc.InvalidArgument; - var identifier = _actors.FromObject(_objects[actorIndex], false, false, true); + var identifier = _actors.FromObject(_objects[actorIndex], out _, false, false, true); if (!identifier.IsValid) return PenumbraApiEc.InvalidArgument; @@ -1076,11 +1077,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi public IReadOnlyDictionary?[] GetGameObjectResourcePaths(ushort[] gameObjects) { - var characters = gameObjects.Select(index => _objects[index]).OfType(); + var characters = gameObjects.Select(index => _objects.GetDalamudObject((int) index)).OfType(); var resourceTrees = _resourceTreeFactory.FromCharacters(characters, 0); var pathDictionaries = ResourceTreeApiHelper.GetResourcePathDictionaries(resourceTrees); - return Array.ConvertAll(gameObjects, obj => pathDictionaries.TryGetValue(obj, out var pathDict) ? pathDict : null); + return Array.ConvertAll(gameObjects, obj => pathDictionaries.GetValueOrDefault(obj)); } public IReadOnlyDictionary> GetPlayerResourcePaths() @@ -1091,39 +1092,39 @@ public class PenumbraApi : IDisposable, IPenumbraApi return pathDictionaries.AsReadOnly(); } - public IReadOnlyDictionary?[] GetGameObjectResourcesOfType(ResourceType type, bool withUIData, + public IReadOnlyDictionary?[] GetGameObjectResourcesOfType(ResourceType type, bool withUiData, params ushort[] gameObjects) { - var characters = gameObjects.Select(index => _objects[index]).OfType(); - var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUIData ? ResourceTreeFactory.Flags.WithUiData : 0); + var characters = gameObjects.Select(index => _objects.GetDalamudObject((int)index)).OfType(); + var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUiData ? ResourceTreeFactory.Flags.WithUiData : 0); var resDictionaries = ResourceTreeApiHelper.GetResourcesOfType(resourceTrees, type); - return Array.ConvertAll(gameObjects, obj => resDictionaries.TryGetValue(obj, out var resDict) ? resDict : null); + return Array.ConvertAll(gameObjects, obj => resDictionaries.GetValueOrDefault(obj)); } public IReadOnlyDictionary> GetPlayerResourcesOfType(ResourceType type, - bool withUIData) + bool withUiData) { var resourceTrees = _resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly - | (withUIData ? ResourceTreeFactory.Flags.WithUiData : 0)); + | (withUiData ? ResourceTreeFactory.Flags.WithUiData : 0)); var resDictionaries = ResourceTreeApiHelper.GetResourcesOfType(resourceTrees, type); return resDictionaries.AsReadOnly(); } - public Ipc.ResourceTree?[] GetGameObjectResourceTrees(bool withUIData, params ushort[] gameObjects) + public Ipc.ResourceTree?[] GetGameObjectResourceTrees(bool withUiData, params ushort[] gameObjects) { - var characters = gameObjects.Select(index => _objects[index]).OfType(); - var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUIData ? ResourceTreeFactory.Flags.WithUiData : 0); + var characters = gameObjects.Select(index => _objects.GetDalamudObject((int)index)).OfType(); + var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUiData ? ResourceTreeFactory.Flags.WithUiData : 0); var resDictionary = ResourceTreeApiHelper.EncapsulateResourceTrees(resourceTrees); - return Array.ConvertAll(gameObjects, obj => resDictionary.TryGetValue(obj, out var nodes) ? nodes : null); + return Array.ConvertAll(gameObjects, obj => resDictionary.GetValueOrDefault(obj)); } - public IReadOnlyDictionary GetPlayerResourceTrees(bool withUIData) + public IReadOnlyDictionary GetPlayerResourceTrees(bool withUiData) { var resourceTrees = _resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly - | (withUIData ? ResourceTreeFactory.Flags.WithUiData : 0)); + | (withUiData ? ResourceTreeFactory.Flags.WithUiData : 0)); var resDictionary = ResourceTreeApiHelper.EncapsulateResourceTrees(resourceTrees); return resDictionary.AsReadOnly(); @@ -1165,11 +1166,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi private unsafe bool AssociatedCollection(int gameObjectIdx, out ModCollection collection) { collection = _collectionManager.Active.Default; - if (gameObjectIdx < 0 || gameObjectIdx >= _objects.Length) + if (gameObjectIdx < 0 || gameObjectIdx >= _objects.Count) return false; - var ptr = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_objects.GetObjectAddress(gameObjectIdx); - var data = _collectionResolver.IdentifyCollection(ptr, false); + var ptr = _objects[gameObjectIdx]; + var data = _collectionResolver.IdentifyCollection(ptr.AsObject, false); if (data.Valid) collection = data.ModCollection; @@ -1179,10 +1180,10 @@ public class PenumbraApi : IDisposable, IPenumbraApi [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private unsafe ActorIdentifier AssociatedIdentifier(int gameObjectIdx) { - if (gameObjectIdx < 0 || gameObjectIdx >= _objects.Length) + if (gameObjectIdx < 0 || gameObjectIdx >= _objects.Count) return ActorIdentifier.Invalid; - var ptr = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_objects.GetObjectAddress(gameObjectIdx); + var ptr = _objects[gameObjectIdx]; return _actors.FromObject(ptr, out _, false, true, true); } diff --git a/Penumbra/Interop/Hooks/Animation/LoadCharacterVfx.cs b/Penumbra/Interop/Hooks/Animation/LoadCharacterVfx.cs index 69c22773..77aaa742 100644 --- a/Penumbra/Interop/Hooks/Animation/LoadCharacterVfx.cs +++ b/Penumbra/Interop/Hooks/Animation/LoadCharacterVfx.cs @@ -1,9 +1,9 @@ -using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Object; using OtterGui.Services; using Penumbra.Collections; using Penumbra.CrashHandler.Buffers; using Penumbra.GameData; +using Penumbra.GameData.Interop; using Penumbra.Interop.PathResolving; using Penumbra.Interop.Structs; using Penumbra.Services; @@ -16,10 +16,10 @@ public sealed unsafe class LoadCharacterVfx : FastHookGameObjectType switch { - 0 => _objects.SearchById(vfxParams->GameObjectId), + 0 => _objects.ById(vfxParams->GameObjectId), 2 => _objects[(int)vfxParams->GameObjectId], 4 => GetOwnedObject(vfxParams->GameObjectId), - _ => null, + _ => Actor.Null, }; - newData = obj != null + newData = obj.Valid ? _collectionResolver.IdentifyCollection((GameObject*)obj.Address, true) : ResolveData.Invalid; } var last = _state.SetAnimationData(newData); _crashHandler.LogAnimation(newData.AssociatedGameObject, newData.ModCollection, AnimationInvocationType.LoadCharacterVfx); - var ret = Task.Result.Original(vfxPath, vfxParams, unk1, unk2, unk3, unk4); + var ret = Task.Result.Original(vfxPath, vfxParams, unk1, unk2, unk3, unk4); Penumbra.Log.Excessive( $"[Load Character VFX] Invoked with {new ByteString(vfxPath)}, 0x{vfxParams->GameObjectId:X}, {vfxParams->TargetCount}, {unk1}, {unk2}, {unk3}, {unk4} -> 0x{ret:X}."); _state.RestoreAnimationData(last); @@ -59,13 +59,11 @@ public sealed unsafe class LoadCharacterVfx : FastHook Search an object by its id, then get its minion/mount/ornament. - private Dalamud.Game.ClientState.Objects.Types.GameObject? GetOwnedObject(uint id) + private Actor GetOwnedObject(uint id) { - var owner = _objects.SearchById(id); - if (owner == null) - return null; - - var idx = ((GameObject*)owner.Address)->ObjectIndex; - return _objects[idx + 1]; + var owner = _objects.ById(id); + return !owner.Valid + ? Actor.Null + : _objects[owner.Index.Index + 1]; } } diff --git a/Penumbra/Interop/Hooks/Animation/LoadTimelineResources.cs b/Penumbra/Interop/Hooks/Animation/LoadTimelineResources.cs index ade957b9..4bae084e 100644 --- a/Penumbra/Interop/Hooks/Animation/LoadTimelineResources.cs +++ b/Penumbra/Interop/Hooks/Animation/LoadTimelineResources.cs @@ -3,8 +3,8 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Object; using OtterGui.Services; using Penumbra.Collections; -using Penumbra.CrashHandler; using Penumbra.GameData; +using Penumbra.GameData.Interop; using Penumbra.Interop.PathResolving; using Penumbra.Services; @@ -19,11 +19,11 @@ public sealed unsafe class LoadTimelineResources : FastHook Use timelines vfuncs to obtain the associated game object. - public static ResolveData GetDataFromTimeline(IObjectTable objects, CollectionResolver resolver, nint timeline) + public static ResolveData GetDataFromTimeline(ObjectManager objects, CollectionResolver resolver, nint timeline) { try { @@ -64,10 +64,10 @@ public sealed unsafe class LoadTimelineResources : FastHook**)timeline)[0][Offsets.GetGameObjectIdxVfunc]; var idx = getGameObjectIdx(timeline); - if (idx >= 0 && idx < objects.Length) + if (idx >= 0 && idx < objects.Count) { - var obj = (GameObject*)objects.GetObjectAddress(idx); - return obj != null ? resolver.IdentifyCollection(obj, true) : ResolveData.Invalid; + var obj = objects[idx]; + return obj.Valid ? resolver.IdentifyCollection(obj.AsObject, true) : ResolveData.Invalid; } } } diff --git a/Penumbra/Interop/Hooks/Animation/ScheduleClipUpdate.cs b/Penumbra/Interop/Hooks/Animation/ScheduleClipUpdate.cs index 91b63838..342ffc25 100644 --- a/Penumbra/Interop/Hooks/Animation/ScheduleClipUpdate.cs +++ b/Penumbra/Interop/Hooks/Animation/ScheduleClipUpdate.cs @@ -1,7 +1,7 @@ -using Dalamud.Plugin.Services; using OtterGui.Services; using Penumbra.CrashHandler.Buffers; using Penumbra.GameData; +using Penumbra.GameData.Interop; using Penumbra.Interop.PathResolving; using Penumbra.Interop.Structs; using Penumbra.Services; @@ -13,10 +13,10 @@ public sealed unsafe class ScheduleClipUpdate : FastHook { private readonly GameState _state; private readonly CollectionResolver _collectionResolver; - private readonly IObjectTable _objects; + private readonly ObjectManager _objects; private readonly CrashHandlerService _crashHandler; - public SomePapLoad(HookManager hooks, GameState state, CollectionResolver collectionResolver, IObjectTable objects, + public SomePapLoad(HookManager hooks, GameState state, CollectionResolver collectionResolver, ObjectManager objects, CrashHandlerService crashHandler) { _state = state; @@ -36,9 +35,9 @@ public sealed unsafe class SomePapLoad : FastHook if (timelinePtr != nint.Zero) { var actorIdx = (int)(*(*(ulong**)timelinePtr + 1) >> 3); - if (actorIdx >= 0 && actorIdx < _objects.Length) + if (actorIdx >= 0 && actorIdx < _objects.Count) { - var newData = _collectionResolver.IdentifyCollection((GameObject*)_objects.GetObjectAddress(actorIdx), true); + var newData = _collectionResolver.IdentifyCollection(_objects[actorIdx].AsObject, true); var last = _state.SetAnimationData(newData); _crashHandler.LogAnimation(newData.AssociatedGameObject, newData.ModCollection, AnimationInvocationType.PapLoad); Task.Result.Original(a1, a2, a3, a4); diff --git a/Penumbra/Interop/MaterialPreview/LiveColorTablePreviewer.cs b/Penumbra/Interop/MaterialPreview/LiveColorTablePreviewer.cs index a8e4ea4d..4d35e68a 100644 --- a/Penumbra/Interop/MaterialPreview/LiveColorTablePreviewer.cs +++ b/Penumbra/Interop/MaterialPreview/LiveColorTablePreviewer.cs @@ -1,6 +1,7 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; using Penumbra.GameData.Files; +using Penumbra.GameData.Interop; using Penumbra.Interop.SafeHandles; namespace Penumbra.Interop.MaterialPreview; @@ -20,7 +21,7 @@ public sealed unsafe class LiveColorTablePreviewer : LiveMaterialPreviewerBase public Half[] ColorTable { get; } - public LiveColorTablePreviewer(IObjectTable objects, IFramework framework, MaterialInfo materialInfo) + public LiveColorTablePreviewer(ObjectManager objects, IFramework framework, MaterialInfo materialInfo) : base(objects, materialInfo) { _framework = framework; diff --git a/Penumbra/Interop/MaterialPreview/LiveMaterialPreviewer.cs b/Penumbra/Interop/MaterialPreview/LiveMaterialPreviewer.cs index 9ed7ca3d..0556fdc4 100644 --- a/Penumbra/Interop/MaterialPreview/LiveMaterialPreviewer.cs +++ b/Penumbra/Interop/MaterialPreview/LiveMaterialPreviewer.cs @@ -1,5 +1,5 @@ -using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel; +using Penumbra.GameData.Interop; namespace Penumbra.Interop.MaterialPreview; @@ -11,7 +11,7 @@ public sealed unsafe class LiveMaterialPreviewer : LiveMaterialPreviewerBase private readonly float[] _originalMaterialParameter; private readonly uint[] _originalSamplerFlags; - public LiveMaterialPreviewer(IObjectTable objects, MaterialInfo materialInfo) + public LiveMaterialPreviewer(ObjectManager objects, MaterialInfo materialInfo) : base(objects, materialInfo) { var mtrlHandle = Material->MaterialResourceHandle; diff --git a/Penumbra/Interop/MaterialPreview/LiveMaterialPreviewerBase.cs b/Penumbra/Interop/MaterialPreview/LiveMaterialPreviewerBase.cs index 07986f52..f176990e 100644 --- a/Penumbra/Interop/MaterialPreview/LiveMaterialPreviewerBase.cs +++ b/Penumbra/Interop/MaterialPreview/LiveMaterialPreviewerBase.cs @@ -1,12 +1,13 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Graphics.Render; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using Penumbra.GameData.Interop; namespace Penumbra.Interop.MaterialPreview; public abstract unsafe class LiveMaterialPreviewerBase : IDisposable { - private readonly IObjectTable _objects; + private readonly ObjectManager _objects; public readonly MaterialInfo MaterialInfo; public readonly CharacterBase* DrawObject; @@ -14,7 +15,7 @@ public abstract unsafe class LiveMaterialPreviewerBase : IDisposable protected bool Valid; - public LiveMaterialPreviewerBase(IObjectTable objects, MaterialInfo materialInfo) + public LiveMaterialPreviewerBase(ObjectManager objects, MaterialInfo materialInfo) { _objects = objects; diff --git a/Penumbra/Interop/MaterialPreview/MaterialInfo.cs b/Penumbra/Interop/MaterialPreview/MaterialInfo.cs index 686b5a86..61e7c764 100644 --- a/Penumbra/Interop/MaterialPreview/MaterialInfo.cs +++ b/Penumbra/Interop/MaterialPreview/MaterialInfo.cs @@ -1,10 +1,11 @@ -using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Graphics.Render; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; using Penumbra.Interop.ResourceTree; using Penumbra.String; +using Model = Penumbra.GameData.Interop.Model; namespace Penumbra.Interop.MaterialPreview; @@ -18,13 +19,13 @@ public enum DrawObjectType public readonly record struct MaterialInfo(ObjectIndex ObjectIndex, DrawObjectType Type, int ModelSlot, int MaterialSlot) { - public nint GetCharacter(IObjectTable objects) - => objects.GetObjectAddress(ObjectIndex.Index); + public Actor GetCharacter(ObjectManager objects) + => objects[ObjectIndex]; - public nint GetDrawObject(nint address) + public nint GetDrawObject(Actor address) => GetDrawObject(Type, address); - public unsafe Material* GetDrawObjectMaterial(IObjectTable objects) + public unsafe Material* GetDrawObjectMaterial(ObjectManager objects) => GetDrawObjectMaterial((CharacterBase*)GetDrawObject(GetCharacter(objects))); public unsafe Material* GetDrawObjectMaterial(CharacterBase* drawObject) @@ -60,13 +61,13 @@ public readonly record struct MaterialInfo(ObjectIndex ObjectIndex, DrawObjectTy foreach (var type in Enum.GetValues()) { - var drawObject = (CharacterBase*)GetDrawObject(type, objectPtr); - if (drawObject == null) + var drawObject = GetDrawObject(type, objectPtr); + if (!drawObject.Valid) continue; - for (var i = 0; i < drawObject->SlotCount; ++i) + for (var i = 0; i < drawObject.AsCharacterBase->SlotCount; ++i) { - var model = drawObject->Models[i]; + var model = drawObject.AsCharacterBase->Models[i]; if (model == null) continue; @@ -88,19 +89,18 @@ public readonly record struct MaterialInfo(ObjectIndex ObjectIndex, DrawObjectTy return result; } - private static unsafe nint GetDrawObject(DrawObjectType type, nint address) + private static unsafe Model GetDrawObject(DrawObjectType type, Actor address) { - var gameObject = (Character*)address; - if (gameObject == null) - return nint.Zero; + if (!address.Valid) + return Model.Null; return type switch { - DrawObjectType.Character => (nint)gameObject->GameObject.GetDrawObject(), - DrawObjectType.Mainhand => (nint)gameObject->DrawData.Weapon(DrawDataContainer.WeaponSlot.MainHand).DrawObject, - DrawObjectType.Offhand => (nint)gameObject->DrawData.Weapon(DrawDataContainer.WeaponSlot.OffHand).DrawObject, - DrawObjectType.Vfx => (nint)gameObject->DrawData.Weapon(DrawDataContainer.WeaponSlot.Unk).DrawObject, - _ => nint.Zero, + DrawObjectType.Character => address.Model, + DrawObjectType.Mainhand => address.AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.MainHand).DrawObject, + DrawObjectType.Offhand => address.AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.OffHand).DrawObject, + DrawObjectType.Vfx => address.AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.Unk).DrawObject, + _ => Model.Null, }; } } diff --git a/Penumbra/Interop/PathResolving/CollectionResolver.cs b/Penumbra/Interop/PathResolving/CollectionResolver.cs index 1a715f13..fa122e39 100644 --- a/Penumbra/Interop/PathResolving/CollectionResolver.cs +++ b/Penumbra/Interop/PathResolving/CollectionResolver.cs @@ -6,6 +6,7 @@ using Penumbra.Collections.Manager; using Penumbra.GameData.Actors; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Enums; +using Penumbra.GameData.Interop; using Penumbra.Util; using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character; using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; @@ -170,10 +171,10 @@ public sealed unsafe class CollectionResolver( : null; /// Check for the Yourself collection. - private ModCollection? CheckYourself(ActorIdentifier identifier, GameObject* actor) + private ModCollection? CheckYourself(ActorIdentifier identifier, Actor actor) { - if (actor->ObjectIndex == 0 - || cutscenes.GetParentIndex(actor->ObjectIndex) == 0 + if (actor.Index == 0 + || cutscenes.GetParentIndex(actor.Index.Index) == 0 || identifier.Equals(actors.GetCurrentPlayer())) return collectionManager.Active.ByType(CollectionType.Yourself); @@ -181,23 +182,23 @@ public sealed unsafe class CollectionResolver( } /// Check special collections given the actor. Returns notYetReady if the customize array is not filled. - private ModCollection? CollectionByAttributes(GameObject* actor, ref bool notYetReady) + private ModCollection? CollectionByAttributes(Actor actor, ref bool notYetReady) { - if (!actor->IsCharacter()) + if (!actor.IsCharacter) return null; // Only handle human models. - var character = (Character*)actor; - if (!IsModelHuman((uint)character->CharacterData.ModelCharaId)) + + if (!IsModelHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId)) return null; - if (character->DrawData.CustomizeData[0] == 0) + if (actor.Customize->Data[0] == 0) { notYetReady = true; return null; } - var bodyType = character->DrawData.CustomizeData[2]; + var bodyType = actor.Customize->Data[2]; var collection = bodyType switch { 3 => collectionManager.Active.ByType(CollectionType.NonPlayerElderly), @@ -207,9 +208,9 @@ public sealed unsafe class CollectionResolver( if (collection != null) return collection; - var race = (SubRace)character->DrawData.CustomizeData[4]; - var gender = (Gender)(character->DrawData.CustomizeData[1] + 1); - var isNpc = actor->ObjectKind != (byte)ObjectKind.Player; + var race = (SubRace)actor.Customize->Data[4]; + var gender = (Gender)(actor.Customize->Data[1] + 1); + var isNpc = !actor.IsPlayer; var type = CollectionTypeExtensions.FromParts(race, gender, isNpc); collection = collectionManager.Active.ByType(type); @@ -218,15 +219,14 @@ public sealed unsafe class CollectionResolver( } /// Get the collection applying to the owner if it is available. - private ModCollection? CheckOwnedCollection(ActorIdentifier identifier, GameObject* owner, ref bool notYetReady) + private ModCollection? CheckOwnedCollection(ActorIdentifier identifier, Actor owner, ref bool notYetReady) { - if (identifier.Type != IdentifierType.Owned || !config.UseOwnerNameForCharacterCollection || owner == null) + if (identifier.Type != IdentifierType.Owned || !config.UseOwnerNameForCharacterCollection || !owner.Valid) return null; var id = actors.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName, identifier.HomeWorld.Id, ObjectKind.None, uint.MaxValue); - return CheckYourself(id, owner) - ?? CollectionByAttributes(owner, ref notYetReady); + return CheckYourself(id, owner) ?? CollectionByAttributes(owner, ref notYetReady); } } diff --git a/Penumbra/Interop/PathResolving/CutsceneService.cs b/Penumbra/Interop/PathResolving/CutsceneService.cs index 89b9f917..93fee11e 100644 --- a/Penumbra/Interop/PathResolving/CutsceneService.cs +++ b/Penumbra/Interop/PathResolving/CutsceneService.cs @@ -3,6 +3,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Object; using OtterGui.Services; using Penumbra.GameData.Enums; +using Penumbra.GameData.Interop; using Penumbra.Interop.Hooks.Objects; using Penumbra.String; @@ -14,17 +15,17 @@ public sealed class CutsceneService : IService, IDisposable public const int CutsceneEndIdx = (int)ScreenActor.CutsceneEnd; public const int CutsceneSlots = CutsceneEndIdx - CutsceneStartIdx; - private readonly IObjectTable _objects; + private readonly ObjectManager _objects; private readonly CopyCharacter _copyCharacter; private readonly CharacterDestructor _characterDestructor; private readonly short[] _copiedCharacters = Enumerable.Repeat((short)-1, CutsceneSlots).ToArray(); public IEnumerable> Actors => Enumerable.Range(CutsceneStartIdx, CutsceneSlots) - .Where(i => _objects[i] != null) - .Select(i => KeyValuePair.Create(i, this[i] ?? _objects[i]!)); + .Where(i => _objects[i].Valid) + .Select(i => KeyValuePair.Create(i, this[i] ?? _objects.GetDalamudObject(i)!)); - public unsafe CutsceneService(IObjectTable objects, CopyCharacter copyCharacter, CharacterDestructor characterDestructor, + public unsafe CutsceneService(ObjectManager objects, CopyCharacter copyCharacter, CharacterDestructor characterDestructor, IClientState clientState) { _objects = objects; @@ -42,13 +43,13 @@ public sealed class CutsceneService : IService, IDisposable /// Does not check for valid input index. /// Returns null if no connected actor is set or the actor does not exist anymore. /// - public Dalamud.Game.ClientState.Objects.Types.GameObject? this[int idx] + private Dalamud.Game.ClientState.Objects.Types.GameObject? this[int idx] { get { Debug.Assert(idx is >= CutsceneStartIdx and < CutsceneEndIdx); idx = _copiedCharacters[idx - CutsceneStartIdx]; - return idx < 0 ? null : _objects[idx]; + return idx < 0 ? null : _objects.GetDalamudObject(idx); } } @@ -64,10 +65,10 @@ public sealed class CutsceneService : IService, IDisposable if (parentIdx is < -1 or >= CutsceneEndIdx) return false; - if (_objects.GetObjectAddress(copyIdx) == nint.Zero) + if (!_objects[copyIdx].Valid) return false; - if (parentIdx != -1 && _objects.GetObjectAddress(parentIdx) == nint.Zero) + if (parentIdx != -1 && !_objects[parentIdx].Valid) return false; _copiedCharacters[copyIdx - CutsceneStartIdx] = (short)parentIdx; @@ -99,9 +100,9 @@ public sealed class CutsceneService : IService, IDisposable { // A hack to deal with GPose actors leaving and thus losing the link, we just set the home world instead. // I do not think this breaks anything? - var address = (GameObject*)_objects.GetObjectAddress(i + CutsceneStartIdx); - if (address != null && address->GetObjectKind() is (byte)ObjectKind.Pc) - ((Character*)address)->HomeWorld = character->HomeWorld; + var address = _objects[i + CutsceneStartIdx]; + if (address.IsPlayer) + address.AsCharacter->HomeWorld = character->HomeWorld; _copiedCharacters[i] = -1; } @@ -125,7 +126,7 @@ public sealed class CutsceneService : IService, IDisposable /// Try to recover GPose actors on reloads into a running game. /// This is not 100% accurate due to world IDs, minions etc., but will be mostly sane. - private unsafe void RecoverGPoseActors() + private void RecoverGPoseActors() { Dictionary? actors = null; @@ -143,11 +144,11 @@ public sealed class CutsceneService : IService, IDisposable bool TryGetName(int idx, out ByteString name) { name = ByteString.Empty; - var address = (GameObject*)_objects.GetObjectAddress(idx); - if (address == null) + var address = _objects[idx]; + if (!address.Valid) return false; - name = new ByteString(address->Name); + name = address.Utf8Name; return !name.IsEmpty; } diff --git a/Penumbra/Interop/PathResolving/DrawObjectState.cs b/Penumbra/Interop/PathResolving/DrawObjectState.cs index b3ae108b..784ac3fb 100644 --- a/Penumbra/Interop/PathResolving/DrawObjectState.cs +++ b/Penumbra/Interop/PathResolving/DrawObjectState.cs @@ -1,8 +1,7 @@ -using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Character; -using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using OtterGui.Services; +using Penumbra.GameData.Interop; using Object = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.Object; using Penumbra.GameData.Structs; using Penumbra.Interop.Hooks.Objects; @@ -11,7 +10,7 @@ namespace Penumbra.Interop.PathResolving; public sealed class DrawObjectState : IDisposable, IReadOnlyDictionary, IService { - private readonly IObjectTable _objects; + private readonly ObjectManager _objects; private readonly CreateCharacterBase _createCharacterBase; private readonly WeaponReload _weaponReload; private readonly CharacterBaseDestructor _characterBaseDestructor; @@ -22,7 +21,7 @@ public sealed class DrawObjectState : IDisposable, IReadOnlyDictionary _gameState.LastGameObject; - public unsafe DrawObjectState(IObjectTable objects, CreateCharacterBase createCharacterBase, WeaponReload weaponReload, + public unsafe DrawObjectState(ObjectManager objects, CreateCharacterBase createCharacterBase, WeaponReload weaponReload, CharacterBaseDestructor characterBaseDestructor, GameState gameState) { _objects = objects; @@ -95,11 +94,11 @@ public sealed class DrawObjectState : IDisposable, IReadOnlyDictionary private unsafe void InitializeDrawObjects() { - for (var i = 0; i < _objects.Length; ++i) + for (var i = 0; i < _objects.Count; ++i) { - var ptr = (GameObject*)_objects.GetObjectAddress(i); - if (ptr != null && ptr->IsCharacter() && ptr->DrawObject != null) - IterateDrawObjectTree(&ptr->DrawObject->Object, (nint)ptr, false, false); + var ptr = _objects[i]; + if (ptr is { IsCharacter: true, Model.Valid: true }) + IterateDrawObjectTree((Object*)ptr.Model.Address, ptr, false, false); } } diff --git a/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs b/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs index df5e1964..ae7187f0 100644 --- a/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs +++ b/Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs @@ -1,39 +1,26 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Object; using Penumbra.Api.Enums; -using Penumbra.Collections; using Penumbra.GameData.Actors; using Penumbra.GameData.Data; using Penumbra.GameData.Enums; +using Penumbra.GameData.Interop; using Penumbra.Interop.PathResolving; using Penumbra.String.Classes; namespace Penumbra.Interop.ResourceTree; -public class ResourceTreeFactory +public class ResourceTreeFactory( + IDataManager gameData, + ObjectManager objects, + CollectionResolver resolver, + ObjectIdentification identifier, + Configuration config, + ActorManager actors, + PathState pathState) { - private readonly IDataManager _gameData; - private readonly IObjectTable _objects; - private readonly CollectionResolver _collectionResolver; - private readonly ObjectIdentification _identifier; - private readonly Configuration _config; - private readonly ActorManager _actors; - private readonly PathState _pathState; - - public ResourceTreeFactory(IDataManager gameData, IObjectTable objects, CollectionResolver resolver, ObjectIdentification identifier, - Configuration config, ActorManager actors, PathState pathState) - { - _gameData = gameData; - _objects = objects; - _collectionResolver = resolver; - _identifier = identifier; - _config = config; - _actors = actors; - _pathState = pathState; - } - private TreeBuildCache CreateTreeBuildCache() - => new(_objects, _gameData, _actors); + => new(objects, gameData, actors); public IEnumerable GetLocalPlayerRelatedCharacters() { @@ -80,7 +67,7 @@ public class ResourceTreeFactory if (drawObjStruct == null) return null; - var collectionResolveData = _collectionResolver.IdentifyCollection(gameObjStruct, true); + var collectionResolveData = resolver.IdentifyCollection(gameObjStruct, true); if (!collectionResolveData.Valid) return null; @@ -89,9 +76,9 @@ public class ResourceTreeFactory var networked = character.ObjectId != Dalamud.Game.ClientState.Objects.Types.GameObject.InvalidGameObjectId; var tree = new ResourceTree(name, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related, networked, collectionResolveData.ModCollection.Name); - var globalContext = new GlobalResolveContext(_identifier, collectionResolveData.ModCollection, + var globalContext = new GlobalResolveContext(identifier, collectionResolveData.ModCollection, cache, (flags & Flags.WithUiData) != 0); - using (var _ = _pathState.EnterInternalResolve()) + using (var _ = pathState.EnterInternalResolve()) { tree.LoadResources(globalContext); } @@ -103,56 +90,12 @@ public class ResourceTreeFactory // ResolveGamePaths(tree, collectionResolveData.ModCollection); if (globalContext.WithUiData) ResolveUiData(tree); - FilterFullPaths(tree, (flags & Flags.RedactExternalPaths) != 0 ? _config.ModDirectory : null); + FilterFullPaths(tree, (flags & Flags.RedactExternalPaths) != 0 ? config.ModDirectory : null); Cleanup(tree); return tree; } - private static void ResolveGamePaths(ResourceTree tree, ModCollection collection) - { - var forwardDictionary = new Dictionary(); - var reverseDictionary = new Dictionary>(); - foreach (var node in tree.FlatNodes) - { - if (node.PossibleGamePaths.Length == 0 && !node.FullPath.InternalName.IsEmpty) - reverseDictionary.TryAdd(node.FullPath.ToPath(), null!); - else if (node.FullPath.InternalName.IsEmpty && node.PossibleGamePaths.Length == 1) - forwardDictionary.TryAdd(node.GamePath, null); - } - - foreach (var key in forwardDictionary.Keys) - forwardDictionary[key] = collection.ResolvePath(key); - - var reverseResolvedArray = collection.ReverseResolvePaths(reverseDictionary.Keys); - foreach (var (key, set) in reverseDictionary.Keys.Zip(reverseResolvedArray)) - reverseDictionary[key] = set; - - foreach (var node in tree.FlatNodes) - { - if (node.PossibleGamePaths.Length == 0 && !node.FullPath.InternalName.IsEmpty) - { - if (!reverseDictionary.TryGetValue(node.FullPath.ToPath(), out var resolvedSet)) - continue; - - if (resolvedSet.Count != 1) - { - Penumbra.Log.Debug( - $"Found {resolvedSet.Count} game paths while reverse-resolving {node.FullPath} in {collection.Name}:"); - foreach (var gamePath in resolvedSet) - Penumbra.Log.Debug($"Game path: {gamePath}"); - } - - node.PossibleGamePaths = resolvedSet.ToArray(); - } - else if (node.FullPath.InternalName.IsEmpty && node.PossibleGamePaths.Length == 1) - { - if (forwardDictionary.TryGetValue(node.GamePath, out var resolved)) - node.FullPath = resolved ?? new FullPath(node.GamePath); - } - } - } - private static void ResolveUiData(ResourceTree tree) { foreach (var node in tree.FlatNodes) @@ -217,12 +160,12 @@ public class ResourceTreeFactory private unsafe (string Name, bool PlayerRelated) GetCharacterName(Dalamud.Game.ClientState.Objects.Types.Character character, TreeBuildCache cache) { - var identifier = _actors.FromObject((GameObject*)character.Address, out var owner, true, false, false); + var identifier = actors.FromObject((GameObject*)character.Address, out var owner, true, false, false); switch (identifier.Type) { case IdentifierType.Player: return (identifier.PlayerName.ToString(), true); case IdentifierType.Owned: - var ownerChara = _objects.CreateObjectReference((nint)owner) as Dalamud.Game.ClientState.Objects.Types.Character; + var ownerChara = objects.Objects.CreateObjectReference(owner) as Dalamud.Game.ClientState.Objects.Types.Character; if (ownerChara != null) { var ownerName = GetCharacterName(ownerChara, cache); diff --git a/Penumbra/Interop/ResourceTree/TreeBuildCache.cs b/Penumbra/Interop/ResourceTree/TreeBuildCache.cs index 7582c753..2798002a 100644 --- a/Penumbra/Interop/ResourceTree/TreeBuildCache.cs +++ b/Penumbra/Interop/ResourceTree/TreeBuildCache.cs @@ -3,19 +3,19 @@ using Dalamud.Plugin.Services; using Penumbra.GameData.Actors; using Penumbra.GameData.Enums; using Penumbra.GameData.Files; +using Penumbra.GameData.Interop; using Penumbra.GameData.Structs; -using Penumbra.String; using Penumbra.String.Classes; namespace Penumbra.Interop.ResourceTree; -internal readonly struct TreeBuildCache(IObjectTable objects, IDataManager dataManager, ActorManager actors) +internal readonly struct TreeBuildCache(ObjectManager objects, IDataManager dataManager, ActorManager actors) { private readonly Dictionary _shaderPackages = []; public unsafe bool IsLocalPlayerRelated(Character character) { - var player = objects[0]; + var player = objects.GetDalamudObject(0); if (player == null) return false; @@ -31,30 +31,30 @@ internal readonly struct TreeBuildCache(IObjectTable objects, IDataManager dataM } public IEnumerable GetCharacters() - => objects.OfType(); + => objects.Objects.OfType(); public IEnumerable GetLocalPlayerRelatedCharacters() { - var player = objects[0]; + var player = objects.GetDalamudObject(0); if (player == null) yield break; yield return (Character)player; - var minion = objects[1]; + var minion = objects.GetDalamudObject(1); if (minion != null) yield return (Character)minion; var playerId = player.ObjectId; for (var i = 2; i < ObjectIndex.CutsceneStart.Index; i += 2) { - if (objects[i] is Character owned && owned.OwnerId == playerId) + if (objects.GetDalamudObject(i) is Character owned && owned.OwnerId == playerId) yield return owned; } for (var i = ObjectIndex.CutsceneStart.Index; i < ObjectIndex.CharacterScreen.Index; ++i) { - var character = objects[i] as Character; + var character = objects.GetDalamudObject((int) i) as Character; if (character == null) continue; @@ -62,34 +62,11 @@ internal readonly struct TreeBuildCache(IObjectTable objects, IDataManager dataM if (parent < 0) continue; - if (parent is 0 or 1 || objects[parent]?.OwnerId == playerId) + if (parent is 0 or 1 || objects.GetDalamudObject(parent)?.OwnerId == playerId) yield return character; } } - private unsafe ByteString GetPlayerName(GameObject player) - { - var gameObject = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)player.Address; - return new ByteString(gameObject->Name); - } - - private unsafe bool GetOwnedId(ByteString playerName, uint playerId, int idx, [NotNullWhen(true)] out Character? character) - { - character = objects[idx] as Character; - if (character == null) - return false; - - var actorId = actors.FromObject(character, out var owner, true, true, true); - if (!actorId.IsValid) - return false; - if (owner != null && owner->OwnerID != playerId) - return false; - if (actorId.Type is not IdentifierType.Player || !actorId.PlayerName.Equals(playerName)) - return false; - - return true; - } - /// Try to read a shpk file from the given path and cache it on success. public ShpkFile? ReadShaderPackage(FullPath path) => ReadFile(dataManager, path, _shaderPackages, bytes => new ShpkFile(bytes)); diff --git a/Penumbra/Interop/Services/RedrawService.cs b/Penumbra/Interop/Services/RedrawService.cs index e0a94d30..a6fec0b5 100644 --- a/Penumbra/Interop/Services/RedrawService.cs +++ b/Penumbra/Interop/Services/RedrawService.cs @@ -11,6 +11,7 @@ using Penumbra.Api.Enums; using Penumbra.Communication; using Penumbra.GameData; using Penumbra.GameData.Enums; +using Penumbra.GameData.Interop; using Penumbra.Interop.Structs; using Penumbra.Mods; using Penumbra.Mods.Editor; @@ -57,7 +58,7 @@ public unsafe partial class RedrawService // this will be in obj and true will be returned. private bool FindCorrectActor(int idx, out GameObject? obj) { - obj = _objects[idx]; + obj = _objects.GetDalamudObject(idx); if (!InGPose || obj == null || IsGPoseActor(idx)) return false; @@ -70,21 +71,21 @@ public unsafe partial class RedrawService if (name == gPoseName) { - obj = _objects[GPosePlayerIdx + i]; + obj = _objects.GetDalamudObject(GPosePlayerIdx + i); return true; } } for (; _gPoseNameCounter < GPoseSlots; ++_gPoseNameCounter) { - var gPoseName = _objects[GPosePlayerIdx + _gPoseNameCounter]?.Name.ToString(); + var gPoseName = _objects.GetDalamudObject(GPosePlayerIdx + _gPoseNameCounter)?.Name.ToString(); _gPoseNames[_gPoseNameCounter] = gPoseName; if (gPoseName == null) break; if (name == gPoseName) { - obj = _objects[GPosePlayerIdx + _gPoseNameCounter]; + obj = _objects.GetDalamudObject(GPosePlayerIdx + _gPoseNameCounter); return true; } } @@ -111,7 +112,7 @@ public sealed unsafe partial class RedrawService : IDisposable private const int FurnitureIdx = 1337; private readonly IFramework _framework; - private readonly IObjectTable _objects; + private readonly ObjectManager _objects; private readonly ITargetManager _targets; private readonly ICondition _conditions; private readonly IClientState _clientState; @@ -133,7 +134,7 @@ public sealed unsafe partial class RedrawService : IDisposable public event GameObjectRedrawnDelegate? GameObjectRedrawn; - public RedrawService(IFramework framework, IObjectTable objects, ITargetManager targets, ICondition conditions, IClientState clientState, + public RedrawService(IFramework framework, ObjectManager objects, ITargetManager targets, ICondition conditions, IClientState clientState, Configuration config, CommunicatorService communicator) { _framework = framework; @@ -170,7 +171,7 @@ public sealed unsafe partial class RedrawService : IDisposable if (gPose) DisableDraw(actor!); - if (actor is PlayerCharacter && _objects[tableIndex + 1] is { ObjectKind: ObjectKind.MountType or ObjectKind.Ornament } mountOrOrnament) + if (actor is PlayerCharacter && _objects.GetDalamudObject(tableIndex + 1) is { ObjectKind: ObjectKind.MountType or ObjectKind.Ornament } mountOrOrnament) { *ActorDrawState(mountOrOrnament) |= DrawState.Invisibility; if (gPose) @@ -189,7 +190,7 @@ public sealed unsafe partial class RedrawService : IDisposable if (gPose) EnableDraw(actor!); - if (actor is PlayerCharacter && _objects[tableIndex + 1] is { ObjectKind: ObjectKind.MountType or ObjectKind.Ornament } mountOrOrnament) + if (actor is PlayerCharacter && _objects.GetDalamudObject(tableIndex + 1) is { ObjectKind: ObjectKind.MountType or ObjectKind.Ornament } mountOrOrnament) { *ActorDrawState(mountOrOrnament) &= ~DrawState.Invisibility; if (gPose) @@ -212,7 +213,7 @@ public sealed unsafe partial class RedrawService : IDisposable private void ReloadActorAfterGPose(GameObject? actor) { - if (_objects[GPosePlayerIdx] != null) + if (_objects[GPosePlayerIdx].Valid) { ReloadActor(actor); return; @@ -230,7 +231,7 @@ public sealed unsafe partial class RedrawService : IDisposable if (_target < 0) return; - var actor = _objects[_target]; + var actor = _objects.GetDalamudObject(_target); if (actor == null || _targets.Target != null) return; @@ -316,12 +317,12 @@ public sealed unsafe partial class RedrawService : IDisposable if (idx < 0) { var newIdx = ~idx; - WriteInvisible(_objects[newIdx]); + WriteInvisible(_objects.GetDalamudObject(newIdx)); _afterGPoseQueue[numKept++] = newIdx; } else { - WriteVisible(_objects[idx]); + WriteVisible(_objects.GetDalamudObject(idx)); } } @@ -357,8 +358,8 @@ public sealed unsafe partial class RedrawService : IDisposable private GameObject? GetLocalPlayer() { - var gPosePlayer = _objects[GPosePlayerIdx]; - return gPosePlayer ?? _objects[0]; + var gPosePlayer = _objects.GetDalamudObject(GPosePlayerIdx); + return gPosePlayer ?? _objects.GetDalamudObject(0); } public bool GetName(string lowerName, out GameObject? actor) @@ -379,7 +380,7 @@ public sealed unsafe partial class RedrawService : IDisposable if (!ret && lowerName.Length > 1 && lowerName[0] == '#' && ushort.TryParse(lowerName[1..], out var objectIndex)) { ret = true; - actor = _objects[objectIndex]; + actor = _objects.GetDalamudObject((int) objectIndex); } return ret; @@ -387,8 +388,8 @@ public sealed unsafe partial class RedrawService : IDisposable public void RedrawObject(int tableIndex, RedrawType settings) { - if (tableIndex >= 0 && tableIndex < _objects.Length) - RedrawObject(_objects[tableIndex], settings); + if (tableIndex >= 0 && tableIndex < _objects.Count) + RedrawObject(_objects.GetDalamudObject(tableIndex), settings); } public void RedrawObject(string name, RedrawType settings) @@ -399,13 +400,13 @@ public sealed unsafe partial class RedrawService : IDisposable else if (GetName(lowerName, out var target)) RedrawObject(target, settings); else - foreach (var actor in _objects.Where(a => a.Name.ToString().ToLowerInvariant() == lowerName)) + foreach (var actor in _objects.Objects.Where(a => a.Name.ToString().ToLowerInvariant() == lowerName)) RedrawObject(actor, settings); } public void RedrawAll(RedrawType settings) { - foreach (var actor in _objects) + foreach (var actor in _objects.Objects) RedrawObject(actor, settings); } diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs index 72dd91d3..6cf24f62 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.cs @@ -12,6 +12,7 @@ using Penumbra.Collections.Manager; using Penumbra.Communication; using Penumbra.GameData.Enums; using Penumbra.GameData.Files; +using Penumbra.GameData.Interop; using Penumbra.Import.Models; using Penumbra.Import.Textures; using Penumbra.Interop.Hooks.Objects; @@ -46,7 +47,7 @@ public partial class ModEditWindow : Window, IDisposable private readonly IDragDropManager _dragDropManager; private readonly IDataManager _gameData; private readonly IFramework _framework; - private readonly IObjectTable _objects; + private readonly ObjectManager _objects; private readonly CharacterBaseDestructor _characterBaseDestructor; private Vector2 _iconSize = Vector2.Zero; @@ -446,7 +447,7 @@ public partial class ModEditWindow : Window, IDisposable DrawOptionSelectHeader(); - var setsEqual = !_editor!.SwapEditor.Changes; + var setsEqual = !_editor.SwapEditor.Changes; var tt = setsEqual ? "No changes staged." : "Apply the currently staged changes to the option."; ImGui.NewLine(); if (ImGuiUtil.DrawDisabledButton("Apply Changes", Vector2.Zero, tt, setsEqual)) @@ -577,7 +578,7 @@ public partial class ModEditWindow : Window, IDisposable Configuration config, ModEditor editor, ResourceTreeFactory resourceTreeFactory, MetaFileManager metaFileManager, StainService stainService, ActiveCollections activeCollections, ModMergeTab modMergeTab, CommunicatorService communicator, TextureManager textures, ModelManager models, IDragDropManager dragDropManager, - ChangedItemDrawer changedItemDrawer, IObjectTable objects, IFramework framework, CharacterBaseDestructor characterBaseDestructor) + ChangedItemDrawer changedItemDrawer, ObjectManager objects, IFramework framework, CharacterBaseDestructor characterBaseDestructor) : base(WindowBaseLabel) { _performance = performance; diff --git a/Penumbra/UI/PredefinedTagManager.cs b/Penumbra/UI/PredefinedTagManager.cs index 17e8432b..0e5377d6 100644 --- a/Penumbra/UI/PredefinedTagManager.cs +++ b/Penumbra/UI/PredefinedTagManager.cs @@ -71,8 +71,7 @@ public sealed class PredefinedTagManager : ISavable, IReadOnlyList foreach (var (tag, data) in tags) _predefinedTags.TryAdd(tag, data); break; - default: - throw new Exception($"Invalid version {version}."); + default: throw new Exception($"Invalid version {version}."); } } catch (Exception ex) diff --git a/Penumbra/UI/Tabs/Debug/DebugTab.cs b/Penumbra/UI/Tabs/Debug/DebugTab.cs index ab7ccf0c..2003f0ef 100644 --- a/Penumbra/UI/Tabs/Debug/DebugTab.cs +++ b/Penumbra/UI/Tabs/Debug/DebugTab.cs @@ -21,6 +21,7 @@ using Penumbra.Collections.Manager; using Penumbra.GameData.Actors; using Penumbra.GameData.DataContainers; using Penumbra.GameData.Files; +using Penumbra.GameData.Interop; using Penumbra.Import.Structs; using Penumbra.Import.Textures; using Penumbra.Interop.PathResolving; @@ -90,12 +91,12 @@ public class DebugTab : Window, ITab private readonly RedrawService _redraws; private readonly DictEmote _emotes; private readonly Diagnostics _diagnostics; - private readonly IObjectTable _objects; + private readonly ObjectManager _objects; private readonly IClientState _clientState; private readonly IpcTester _ipcTester; private readonly CrashHandlerPanel _crashHandlerPanel; - public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, IObjectTable objects, + public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager, ObjectManager objects, IClientState clientState, ValidityChecker validityChecker, ModManager modManager, HttpApi httpApi, ActorManager actors, StainService stains, CharacterUtility characterUtility, ResidentResourceManager residentResources, @@ -430,7 +431,7 @@ public class DebugTab : Window, ITab DrawSpecial("Current Card", _actors.GetCardPlayer()); DrawSpecial("Current Glamour", _actors.GetGlamourPlayer()); - foreach (var obj in _objects) + foreach (var obj in _objects.Objects) { ImGuiUtil.DrawTableColumn($"{((GameObject*)obj.Address)->ObjectIndex}"); ImGuiUtil.DrawTableColumn($"0x{obj.Address:X}"); diff --git a/Penumbra/UI/Tabs/ModsTab.cs b/Penumbra/UI/Tabs/ModsTab.cs index bb8856b3..e4d94bb5 100644 --- a/Penumbra/UI/Tabs/ModsTab.cs +++ b/Penumbra/UI/Tabs/ModsTab.cs @@ -14,6 +14,7 @@ using Penumbra.Mods.Manager; using Penumbra.UI.ModsTab; using ModFileSystemSelector = Penumbra.UI.ModsTab.ModFileSystemSelector; using Penumbra.Collections.Manager; +using Penumbra.GameData.Interop; namespace Penumbra.UI.Tabs; @@ -28,7 +29,7 @@ public class ModsTab( IClientState clientState, CollectionSelectHeader collectionHeader, ITargetManager targets, - IObjectTable objectTable) + ObjectManager objects) : ITab { private readonly ActiveCollections _activeCollections = collectionManager.Active; @@ -128,7 +129,7 @@ public class ModsTab( using var disabled = ImRaii.Disabled(clientState.LocalPlayer == null); ImGui.SameLine(); var buttonWidth = frameHeight with { X = ImGui.GetContentRegionAvail().X / 5 }; - var tt = objectTable.GetObjectAddress(0) == nint.Zero + var tt = !objects[0].Valid ? "\nCan only be used when you are logged in and your character is available." : string.Empty; DrawButton(buttonWidth, "All", string.Empty, tt);