From 1119fd0ec73a62615dd66edff8c7ccd3e30eaecd Mon Sep 17 00:00:00 2001 From: Haselnussbomber Date: Thu, 31 Jul 2025 00:33:49 +0200 Subject: [PATCH] Add AgentInterfacePtr --- .../Game/Addon/Lifecycle/AddonLifecycle.cs | 1 - Dalamud/Game/Gui/GameGui.cs | 80 +++++++--------- .../Game/NativeWrapper/AgentInterfacePtr.cs | 94 +++++++++++++++++++ Dalamud/Interface/Internal/UiDebug.cs | 6 +- .../Internal/UiDebug2/Browsing/AddonTree.cs | 2 +- .../Internal/UiDebug2/UiDebug2.Sidebar.cs | 2 +- Dalamud/Plugin/Services/IGameGui.cs | 20 ++-- 7 files changed, 141 insertions(+), 64 deletions(-) create mode 100644 Dalamud/Game/NativeWrapper/AgentInterfacePtr.cs diff --git a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs index e43ea848c..b44ab8764 100644 --- a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs +++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Runtime.CompilerServices; using Dalamud.Game.Addon.Lifecycle.AddonArgTypes; -using Dalamud.Game.Gui.NativeWrapper; using Dalamud.Hooking; using Dalamud.Hooking.Internal; using Dalamud.IoC; diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs index 3d600b04a..04b29f9d9 100644 --- a/Dalamud/Game/Gui/GameGui.cs +++ b/Dalamud/Game/Gui/GameGui.cs @@ -170,29 +170,17 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui /// public IntPtr GetUIModule() { - var framework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance(); - if (framework == null) - return IntPtr.Zero; - - var uiModule = framework->GetUIModule(); - if (uiModule == null) - return IntPtr.Zero; - - return (IntPtr)uiModule; + return (nint)UIModule.Instance(); } /// public AtkUnitBasePtr GetAddonByName(string name, int index = 1) { - var atkStage = AtkStage.Instance(); - if (atkStage == null) + var unitManager = RaptureAtkUnitManager.Instance(); + if (unitManager == null) return 0; - var unitMgr = atkStage->RaptureAtkUnitManager; - if (unitMgr == null) - return 0; - - var addon = unitMgr->GetAddonByName(name, index); + var addon = unitManager->GetAddonByName(name, index); if (addon == null) return 0; @@ -200,47 +188,43 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui } /// - public IntPtr FindAgentInterface(string addonName) + public AgentInterfacePtr GetAgentById(int id) + { + var agentModule = AgentModule.Instance(); + if (agentModule == null || id < 0 || id >= agentModule->Agents.Length) + return 0; + + return (nint)agentModule->Agents[id].Value; + } + + /// + public AgentInterfacePtr FindAgentInterface(string addonName) { var addon = this.GetAddonByName(addonName); return this.FindAgentInterface(addon); } /// - public IntPtr FindAgentInterface(void* addon) => this.FindAgentInterface((IntPtr)addon); - - /// - public IntPtr FindAgentInterface(IntPtr addonPtr) + public AgentInterfacePtr FindAgentInterface(AtkUnitBasePtr addon) { - if (addonPtr == IntPtr.Zero) - return IntPtr.Zero; + if (addon.IsNull) + return 0; - var uiModule = (UIModule*)this.GetUIModule(); - if (uiModule == null) - return IntPtr.Zero; - - var agentModule = uiModule->GetAgentModule(); + var agentModule = AgentModule.Instance(); if (agentModule == null) - return IntPtr.Zero; - - var addon = (AtkUnitBase*)addonPtr; - var addonId = addon->ParentId == 0 ? addon->Id : addon->ParentId; + return 0; + var addonId = addon.ParentId == 0 ? addon.Id : addon.ParentId; if (addonId == 0) - return IntPtr.Zero; + return 0; - var index = 0; - while (true) + foreach (AgentInterface* agent in agentModule->Agents) { - var agent = agentModule->GetAgentByInternalId((AgentId)index++); - if (agent == uiModule || agent == null) - break; - - if (agent->AddonId == addonId) - return new IntPtr(agent); + if (agent != null && agent->AddonId == addonId) + return (nint)agent; } - return IntPtr.Zero; + return 0; } /// @@ -463,17 +447,17 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui => this.gameGuiService.GetAddonByName(name, index); /// - public IntPtr FindAgentInterface(string addonName) + public AgentInterfacePtr GetAgentById(int id) + => this.gameGuiService.GetAgentById(id); + + /// + public AgentInterfacePtr FindAgentInterface(string addonName) => this.gameGuiService.FindAgentInterface(addonName); /// - public unsafe IntPtr FindAgentInterface(void* addon) + public AgentInterfacePtr FindAgentInterface(AtkUnitBasePtr addon) => this.gameGuiService.FindAgentInterface(addon); - /// - public IntPtr FindAgentInterface(IntPtr addonPtr) - => this.gameGuiService.FindAgentInterface(addonPtr); - private void UiHideToggledForward(object sender, bool toggled) => this.UiHideToggled?.Invoke(sender, toggled); private void HoveredItemForward(object sender, ulong itemId) => this.HoveredItemChanged?.Invoke(sender, itemId); diff --git a/Dalamud/Game/NativeWrapper/AgentInterfacePtr.cs b/Dalamud/Game/NativeWrapper/AgentInterfacePtr.cs new file mode 100644 index 000000000..a8d2de455 --- /dev/null +++ b/Dalamud/Game/NativeWrapper/AgentInterfacePtr.cs @@ -0,0 +1,94 @@ +using System.Runtime.InteropServices; + +using FFXIVClientStructs.FFXIV.Client.UI; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; + +namespace Dalamud.Game.NativeWrapper; + +/// +/// A wrapper for AgentInterface. +/// +/// The address to the AgentInterface. +[StructLayout(LayoutKind.Explicit, Size = 0x08)] +public readonly unsafe struct AgentInterfacePtr(nint address) : IEquatable +{ + /// + /// The address to the AgentInterface. + /// + [FieldOffset(0x00)] + public readonly nint Address = address; + + /// + /// Gets a value indicating whether the underlying pointer is a nullptr. + /// + public readonly bool IsNull => this.Address == 0; + + /// + /// Gets a value indicating whether the agents addon is visible. + /// + public readonly AtkUnitBasePtr Addon + { + get + { + if (this.IsNull) + return 0; + + var raptureAtkUnitManager = RaptureAtkUnitManager.Instance(); + if (raptureAtkUnitManager == null) + return 0; + + return (nint)raptureAtkUnitManager->GetAddonById(this.AddonId); + } + } + + /// + /// Gets a value indicating whether the agent is active. + /// + public readonly ushort AddonId => (ushort)(this.IsNull ? 0 : this.Struct->GetAddonId()); + + /// + /// Gets a value indicating whether the agent is active. + /// + public readonly bool IsAgentActive => !this.IsNull && this.Struct->IsAgentActive(); + + /// + /// Gets a value indicating whether the agents addon is ready. + /// + public readonly bool IsAddonReady => !this.IsNull && this.Struct->IsAddonReady(); + + /// + /// Gets a value indicating whether the agents addon is visible. + /// + public readonly bool IsAddonShown => !this.IsNull && this.Struct->IsAddonShown(); + + /// + /// Gets the AgentInterface*. + /// + /// Internal use only. + internal readonly AgentInterface* Struct => (AgentInterface*)this.Address; + + public static implicit operator nint(AgentInterfacePtr wrapper) => wrapper.Address; + + public static implicit operator AgentInterfacePtr(nint address) => new(address); + + public static bool operator ==(AgentInterfacePtr left, AgentInterfacePtr right) => left.Address == right.Address; + + public static bool operator !=(AgentInterfacePtr left, AgentInterfacePtr right) => left.Address != right.Address; + + /// + /// Focuses the AtkUnitBase. + /// + /// true when the addon was focused, false otherwise. + public readonly bool FocusAddon() => this.IsNull && this.Struct->FocusAddon(); + + /// Determines whether the specified AgentInterfacePtr is equal to the current AgentInterfacePtr. + /// The AgentInterfacePtr to compare with the current AgentInterfacePtr. + /// true if the specified AgentInterfacePtr is equal to the current AgentInterfacePtr; otherwise, false. + public readonly bool Equals(AgentInterfacePtr other) => this.Address == other.Address; + + /// + public override readonly bool Equals(object obj) => obj is AgentInterfacePtr wrapper && this.Equals(wrapper); + + /// + public override readonly int GetHashCode() => ((nuint)this.Address).GetHashCode(); +} diff --git a/Dalamud/Interface/Internal/UiDebug.cs b/Dalamud/Interface/Internal/UiDebug.cs index d0ebc8fac..21ec6964c 100644 --- a/Dalamud/Interface/Internal/UiDebug.cs +++ b/Dalamud/Interface/Internal/UiDebug.cs @@ -87,7 +87,7 @@ internal unsafe class UiDebug { var isVisible = atkUnitBase->IsVisible; var addonName = atkUnitBase->NameString; - var agent = Service.Get().FindAgentInterface(atkUnitBase); + var agent = Service.Get().FindAgentInterface((nint)atkUnitBase); ImGui.Text($"{addonName}"); ImGui.SameLine(); @@ -102,8 +102,8 @@ internal unsafe class UiDebug } ImGui.Separator(); - ImGuiHelpers.ClickToCopyText($"Address: {(ulong)atkUnitBase:X}", $"{(ulong)atkUnitBase:X}"); - ImGuiHelpers.ClickToCopyText($"Agent: {(ulong)agent:X}", $"{(ulong)agent:X}"); + ImGuiHelpers.ClickToCopyText($"Address: {(nint)atkUnitBase:X}", $"{(nint)atkUnitBase:X}"); + ImGuiHelpers.ClickToCopyText($"Agent: {(nint)agent:X}", $"{(nint)agent:X}"); ImGui.Separator(); ImGui.Text($"Position: [ {atkUnitBase->X} , {atkUnitBase->Y} ]"); diff --git a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs index 2dc1b6cfd..39aa9f4a8 100644 --- a/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs +++ b/Dalamud/Interface/Internal/UiDebug2/Browsing/AddonTree.cs @@ -152,7 +152,7 @@ public unsafe partial class AddonTree : IDisposable var uldManager = addon->UldManager; PrintFieldValuePair("Address", $"{(nint)addon:X}"); - PrintFieldValuePair("Agent", $"{GameGui.FindAgentInterface(addon):X}"); + PrintFieldValuePair("Agent", $"{GameGui.FindAgentInterface((nint)addon):X}"); PrintFieldValuePairs( ("X", $"{addon->X}"), diff --git a/Dalamud/Interface/Internal/UiDebug2/UiDebug2.Sidebar.cs b/Dalamud/Interface/Internal/UiDebug2/UiDebug2.Sidebar.cs index 50967453d..4820b2de4 100644 --- a/Dalamud/Interface/Internal/UiDebug2/UiDebug2.Sidebar.cs +++ b/Dalamud/Interface/Internal/UiDebug2/UiDebug2.Sidebar.cs @@ -49,7 +49,7 @@ internal unsafe partial class UiDebug2 /// Gets the base address for all unit lists. /// /// The address, if found. - internal static AtkUnitList* GetUnitListBaseAddr() => &((UIModule*)GameGui.GetUIModule())->GetRaptureAtkModule()->RaptureAtkUnitManager.AtkUnitManager.DepthLayerOneList; + internal static AtkUnitList* GetUnitListBaseAddr() => &RaptureAtkUnitManager.Instance()->DepthLayerOneList; private void DrawSidebar() { diff --git a/Dalamud/Plugin/Services/IGameGui.cs b/Dalamud/Plugin/Services/IGameGui.cs index 77e2600e6..487987d3d 100644 --- a/Dalamud/Plugin/Services/IGameGui.cs +++ b/Dalamud/Plugin/Services/IGameGui.cs @@ -86,27 +86,27 @@ public unsafe interface IGameGui /// /// Name of addon to find. /// Index of addon to find (1-indexed). - /// nint.Zero if unable to find UI, otherwise nint pointing to the start of the addon. + /// A pointer to the addon. public AtkUnitBasePtr GetAddonByName(string name, int index = 1); + /// + /// Find the agent associated with an addon, if possible. + /// + /// The agent id. + /// A pointer to the agent interface. + public AgentInterfacePtr GetAgentById(int id); + /// /// Find the agent associated with an addon, if possible. /// /// The addon name. /// A pointer to the agent interface. - public nint FindAgentInterface(string addonName); + public AgentInterfacePtr FindAgentInterface(string addonName); /// /// Find the agent associated with an addon, if possible. /// /// The addon address. /// A pointer to the agent interface. - public nint FindAgentInterface(void* addon); - - /// - /// Find the agent associated with an addon, if possible. - /// - /// The addon address. - /// A pointer to the agent interface. - public IntPtr FindAgentInterface(IntPtr addonPtr); + public AgentInterfacePtr FindAgentInterface(AtkUnitBasePtr addon); }