diff --git a/Dalamud/Game/AddonEventManager/AddonEventEntry.cs b/Dalamud/Game/AddonEventManager/AddonEventEntry.cs
index 83f1a724c..48c3feb24 100644
--- a/Dalamud/Game/AddonEventManager/AddonEventEntry.cs
+++ b/Dalamud/Game/AddonEventManager/AddonEventEntry.cs
@@ -1,4 +1,6 @@
-using Dalamud.Memory;
+using System;
+
+using Dalamud.Memory;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Component.GUI;
@@ -46,9 +48,14 @@ internal unsafe class AddonEventEntry
/// Gets the event type for this event.
///
required public AddonEventType EventType { get; init; }
+
+ ///
+ /// Gets the event handle for this event.
+ ///
+ required internal IAddonEventHandle Handle { get; init; }
///
/// Gets the formatted log string for this AddonEventEntry.
///
- internal string LogString => $"ParamKey: {this.ParamKey}, Addon: {this.AddonName}, Event: {this.EventType}";
+ internal string LogString => $"ParamKey: {this.ParamKey}, Addon: {this.AddonName}, Event: {this.EventType}, GUID: {this.Handle.EventGuid}";
}
diff --git a/Dalamud/Game/AddonEventManager/AddonEventHandle.cs b/Dalamud/Game/AddonEventManager/AddonEventHandle.cs
new file mode 100644
index 000000000..48abba9a0
--- /dev/null
+++ b/Dalamud/Game/AddonEventManager/AddonEventHandle.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Dalamud.Game.Addon;
+
+///
+/// Class that represents a addon event handle.
+///
+public class AddonEventHandle : IAddonEventHandle
+{
+ ///
+ public uint ParamKey { get; init; }
+
+ ///
+ public string AddonName { get; init; } = "NullAddon";
+
+ ///
+ public AddonEventType EventType { get; init; }
+
+ ///
+ public Guid EventGuid { get; init; }
+}
diff --git a/Dalamud/Game/AddonEventManager/AddonEventManager.cs b/Dalamud/Game/AddonEventManager/AddonEventManager.cs
index 89554074a..dfc037e23 100644
--- a/Dalamud/Game/AddonEventManager/AddonEventManager.cs
+++ b/Dalamud/Game/AddonEventManager/AddonEventManager.cs
@@ -77,33 +77,32 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType
/// Registers an event handler for the specified addon, node, and type.
///
/// Unique ID for this plugin.
- /// Unique Id for this event, maximum 0x10000.
/// The parent addon for this event.
/// The node that will trigger this event.
/// The event type for this event.
/// The handler to call when event is triggered.
- internal void AddEvent(string pluginId, uint eventId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
+ /// IAddonEventHandle used to remove the event.
+ internal IAddonEventHandle? AddEvent(string pluginId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
{
if (this.pluginEventControllers.FirstOrDefault(entry => entry.PluginId == pluginId) is { } eventController)
{
- eventController.AddEvent(eventId, atkUnitBase, atkResNode, eventType, eventHandler);
- }
- else
- {
- Log.Verbose($"Unable to locate controller for {pluginId}. No event was added.");
+ return eventController.AddEvent(atkUnitBase, atkResNode, eventType, eventHandler);
}
+
+ Log.Verbose($"Unable to locate controller for {pluginId}. No event was added.");
+ return null;
}
///
/// Unregisters an event handler with the specified event id and event type.
///
/// Unique ID for this plugin.
- /// The Unique Id for this event.
- internal void RemoveEvent(string pluginId, uint eventId)
+ /// The Unique Id for this event.
+ internal void RemoveEvent(string pluginId, IAddonEventHandle eventHandle)
{
if (this.pluginEventControllers.FirstOrDefault(entry => entry.PluginId == pluginId) is { } eventController)
{
- eventController.RemoveEvent(eventId);
+ eventController.RemoveEvent(eventHandle);
}
else
{
@@ -239,12 +238,12 @@ internal class AddonEventManagerPluginScoped : IDisposable, IServiceType, IAddon
}
///
- public void AddEvent(uint eventId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
- => this.eventManagerService.AddEvent(this.plugin.Manifest.WorkingPluginId.ToString(), eventId, atkUnitBase, atkResNode, eventType, eventHandler);
+ public IAddonEventHandle? AddEvent(IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
+ => this.eventManagerService.AddEvent(this.plugin.Manifest.WorkingPluginId.ToString(), atkUnitBase, atkResNode, eventType, eventHandler);
///
- public void RemoveEvent(uint eventId)
- => this.eventManagerService.RemoveEvent(this.plugin.Manifest.WorkingPluginId.ToString(), eventId);
+ public void RemoveEvent(IAddonEventHandle eventHandle)
+ => this.eventManagerService.RemoveEvent(this.plugin.Manifest.WorkingPluginId.ToString(), eventHandle);
///
public void SetCursor(AddonCursorType cursor)
diff --git a/Dalamud/Game/AddonEventManager/IAddonEventHandle.cs b/Dalamud/Game/AddonEventManager/IAddonEventHandle.cs
new file mode 100644
index 000000000..3b2c5c3ae
--- /dev/null
+++ b/Dalamud/Game/AddonEventManager/IAddonEventHandle.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Dalamud.Game.Addon;
+
+///
+/// Interface representing the data used for managing AddonEvents.
+///
+public interface IAddonEventHandle
+{
+ ///
+ /// Gets the param key associated with this event.
+ ///
+ public uint ParamKey { get; init; }
+
+ ///
+ /// Gets the name of the addon that this event was attached to.
+ ///
+ public string AddonName { get; init; }
+
+ ///
+ /// Gets the event type associated with this handle.
+ ///
+ public AddonEventType EventType { get; init; }
+
+ ///
+ /// Gets the unique ID for this handle.
+ ///
+ public Guid EventGuid { get; init; }
+}
diff --git a/Dalamud/Game/AddonEventManager/PluginEventController.cs b/Dalamud/Game/AddonEventManager/PluginEventController.cs
index 852d78128..b66bbc99e 100644
--- a/Dalamud/Game/AddonEventManager/PluginEventController.cs
+++ b/Dalamud/Game/AddonEventManager/PluginEventController.cs
@@ -4,6 +4,7 @@ using System.Linq;
using Dalamud.Game.Gui;
using Dalamud.Logging.Internal;
+using Dalamud.Memory;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Component.GUI;
@@ -39,17 +40,27 @@ internal unsafe class PluginEventController : IDisposable
///
/// Adds a tracked event.
///
- /// Unique ID of the event to add.
/// The Parent addon for the event.
/// The Node for the event.
/// The Event Type.
/// The delegate to call when invoking this event.
- public void AddEvent(uint eventId, nint atkUnitBase, nint atkResNode, AddonEventType atkEventType, IAddonEventManager.AddonEventHandler handler)
+ /// IAddonEventHandle used to remove the event.
+ public IAddonEventHandle AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType atkEventType, IAddonEventManager.AddonEventHandler handler)
{
var node = (AtkResNode*)atkResNode;
var addon = (AtkUnitBase*)atkUnitBase;
var eventType = (AtkEventType)atkEventType;
-
+ var eventId = this.GetNextParamKey();
+ var eventGuid = Guid.NewGuid();
+
+ var eventHandle = new AddonEventHandle
+ {
+ AddonName = MemoryHelper.ReadStringNullTerminated((nint)addon->Name),
+ ParamKey = eventId,
+ EventType = atkEventType,
+ EventGuid = eventGuid,
+ };
+
var eventEntry = new AddonEventEntry
{
Addon = atkUnitBase,
@@ -57,22 +68,25 @@ internal unsafe class PluginEventController : IDisposable
Node = atkResNode,
EventType = atkEventType,
ParamKey = eventId,
+ Handle = eventHandle,
};
- Log.Verbose($"Adding Event: {eventEntry.LogString}");
+ Log.Verbose($"Adding Event. {eventEntry.LogString}");
this.EventListener.RegisterEvent(addon, node, eventType, eventId);
this.Events.Add(eventEntry);
+
+ return eventHandle;
}
///
/// Removes a tracked event, also attempts to un-attach the event from native.
///
- /// Unique ID of the event to remove.
- public void RemoveEvent(uint eventId)
+ /// Unique ID of the event to remove.
+ public void RemoveEvent(IAddonEventHandle handle)
{
- if (this.Events.FirstOrDefault(registeredEvent => registeredEvent.ParamKey == eventId) is not { } targetEvent) return;
+ if (this.Events.FirstOrDefault(registeredEvent => registeredEvent.Handle == handle) is not { } targetEvent) return;
- Log.Verbose($"Removing Event: {targetEvent.LogString}");
+ Log.Verbose($"Removing Event. {targetEvent.LogString}");
this.TryRemoveEventFromNative(targetEvent);
this.Events.Remove(targetEvent);
}
@@ -89,7 +103,7 @@ internal unsafe class PluginEventController : IDisposable
foreach (var registeredEvent in events)
{
- this.RemoveEvent(registeredEvent.ParamKey);
+ this.RemoveEvent(registeredEvent.Handle);
}
}
}
@@ -99,11 +113,21 @@ internal unsafe class PluginEventController : IDisposable
{
foreach (var registeredEvent in this.Events.ToList())
{
- this.RemoveEvent(registeredEvent.ParamKey);
+ this.RemoveEvent(registeredEvent.Handle);
}
this.EventListener.Dispose();
}
+
+ private uint GetNextParamKey()
+ {
+ for (var i = 0u; i < uint.MaxValue; ++i)
+ {
+ if (this.Events.All(registeredEvent => registeredEvent.ParamKey != i)) return i;
+ }
+
+ throw new OverflowException($"uint.MaxValue number of ParamKeys used for {this.PluginId}");
+ }
///
/// Attempts to remove a tracked event from native UI.
diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs
index 64c3e16b3..390f58b1e 100644
--- a/Dalamud/Game/Gui/Dtr/DtrBar.cs
+++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs
@@ -25,9 +25,6 @@ namespace Dalamud.Game.Gui.Dtr;
public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
{
private const uint BaseNodeId = 1000;
- private const uint MouseOverEventIdOffset = 10000;
- private const uint MouseOutEventIdOffset = 20000;
- private const uint MouseClickEventIdOffset = 30000;
private static readonly ModuleLog Log = new("DtrBar");
@@ -51,6 +48,8 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
private readonly ConcurrentBag newEntries = new();
private readonly List entries = new();
+
+ private readonly Dictionary> eventHandles = new();
private uint runningNodeIds = BaseNodeId;
@@ -328,6 +327,11 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
private void RecreateNodes()
{
this.runningNodeIds = BaseNodeId;
+ if (this.entries.Any())
+ {
+ this.eventHandles.Clear();
+ }
+
foreach (var entry in this.entries)
{
entry.TextNode = this.MakeNode(++this.runningNodeIds);
@@ -362,10 +366,14 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
var dtr = this.GetDtr();
if (dtr == null || dtr->RootNode == null || dtr->UldManager.NodeList == null || node == null) return false;
- this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOverEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseOver, this.DtrEventHandler);
- this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOutEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseOut, this.DtrEventHandler);
- this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseClickEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseClick, this.DtrEventHandler);
-
+ this.eventHandles.TryAdd(node->AtkResNode.NodeID, new List());
+ this.eventHandles[node->AtkResNode.NodeID].AddRange(new List
+ {
+ this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, (nint)dtr, (nint)node, AddonEventType.MouseOver, this.DtrEventHandler),
+ this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, (nint)dtr, (nint)node, AddonEventType.MouseOut, this.DtrEventHandler),
+ this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, (nint)dtr, (nint)node, AddonEventType.MouseClick, this.DtrEventHandler),
+ });
+
var lastChild = dtr->RootNode->ChildNode;
while (lastChild->PrevSiblingNode != null) lastChild = lastChild->PrevSiblingNode;
Log.Debug($"Found last sibling: {(ulong)lastChild:X}");
@@ -387,9 +395,8 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
var dtr = this.GetDtr();
if (dtr == null || dtr->RootNode == null || dtr->UldManager.NodeList == null || node == null) return;
- this.uiEventManager.RemoveEvent(AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOverEventIdOffset);
- this.uiEventManager.RemoveEvent(AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOutEventIdOffset);
- this.uiEventManager.RemoveEvent(AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseClickEventIdOffset);
+ this.eventHandles[node->AtkResNode.NodeID].ForEach(handle => this.uiEventManager.RemoveEvent(AddonEventManager.DalamudInternalKey, handle));
+ this.eventHandles[node->AtkResNode.NodeID].Clear();
var tmpPrevNode = node->AtkResNode.PrevSiblingNode;
var tmpNextNode = node->AtkResNode.NextSiblingNode;
diff --git a/Dalamud/Plugin/Services/IAddonEventManager.cs b/Dalamud/Plugin/Services/IAddonEventManager.cs
index f3588f469..52f836b4f 100644
--- a/Dalamud/Plugin/Services/IAddonEventManager.cs
+++ b/Dalamud/Plugin/Services/IAddonEventManager.cs
@@ -18,18 +18,18 @@ public interface IAddonEventManager
///
/// Registers an event handler for the specified addon, node, and type.
///
- /// Unique Id for this event, maximum 0x10000.
/// The parent addon for this event.
/// The node that will trigger this event.
/// The event type for this event.
/// The handler to call when event is triggered.
- void AddEvent(uint eventId, nint atkUnitBase, nint atkResNode, AddonEventType eventType, AddonEventHandler eventHandler);
+ /// IAddonEventHandle used to remove the event. Null if no event was added.
+ IAddonEventHandle? AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType eventType, AddonEventHandler eventHandler);
///
/// Unregisters an event handler with the specified event id and event type.
///
- /// The Unique Id for this event.
- void RemoveEvent(uint eventId);
+ /// Unique handle identifying this event.
+ void RemoveEvent(IAddonEventHandle eventHandle);
///
/// Force the game cursor to be the specified cursor.