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);
}