From 26838d9f5c9dba482c39ba06a75683e66837fd8c Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Thu, 21 Sep 2023 20:47:49 -0700 Subject: [PATCH] Auto generate paramkeys and return handles to events. --- .../Game/AddonEventManager/AddonEventEntry.cs | 11 ++++- .../AddonEventManager/AddonEventHandle.cs | 21 +++++++++ .../AddonEventManager/AddonEventManager.cs | 27 ++++++------ .../AddonEventManager/IAddonEventHandle.cs | 29 ++++++++++++ .../PluginEventController.cs | 44 ++++++++++++++----- Dalamud/Game/Gui/Dtr/DtrBar.cs | 27 +++++++----- Dalamud/Plugin/Services/IAddonEventManager.cs | 8 ++-- 7 files changed, 127 insertions(+), 40 deletions(-) create mode 100644 Dalamud/Game/AddonEventManager/AddonEventHandle.cs create mode 100644 Dalamud/Game/AddonEventManager/IAddonEventHandle.cs 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.