From 674f02136ba9c669425f4c59ae722c653076a53e Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Tue, 19 Sep 2023 17:16:55 -0700 Subject: [PATCH 1/8] [AddonEventManager] Properly track and cleanup events. Also replaced DTR hooks with AddonLifecycle events. --- .../Game/AddonEventManager/AddonEventEntry.cs | 54 ++++ .../AddonEventManager/AddonEventListener.cs | 5 + .../AddonEventManager/AddonEventManager.cs | 258 +++++++++--------- .../PluginEventController.cs | 182 ++++++++++++ Dalamud/Game/Gui/Dtr/DtrBar.cs | 117 +++----- Dalamud/Game/Gui/Dtr/DtrBarAddressResolver.cs | 29 -- Dalamud/Plugin/Services/IAddonEventManager.cs | 4 +- 7 files changed, 421 insertions(+), 228 deletions(-) create mode 100644 Dalamud/Game/AddonEventManager/AddonEventEntry.cs create mode 100644 Dalamud/Game/AddonEventManager/PluginEventController.cs delete mode 100644 Dalamud/Game/Gui/Dtr/DtrBarAddressResolver.cs diff --git a/Dalamud/Game/AddonEventManager/AddonEventEntry.cs b/Dalamud/Game/AddonEventManager/AddonEventEntry.cs new file mode 100644 index 000000000..22b4756c1 --- /dev/null +++ b/Dalamud/Game/AddonEventManager/AddonEventEntry.cs @@ -0,0 +1,54 @@ +using Dalamud.Memory; +using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Component.GUI; + +namespace Dalamud.Game.AddonEventManager; + +/// +/// This class represents a registered event that a plugin registers with a native ui node. +/// Contains all necessary information to track and clean up events automatically. +/// +internal unsafe class AddonEventEntry +{ + /// + /// Name of an invalid addon. + /// + public const string InvalidAddonName = "NullAddon"; + + private string? addonName; + + /// + /// Gets the pointer to the addons AtkUnitBase. + /// + required public nint Addon { get; init; } + + /// + /// Gets the name of the addon this args referrers to. + /// + public string AddonName => this.Addon == nint.Zero ? InvalidAddonName : this.addonName ??= MemoryHelper.ReadString((nint)((AtkUnitBase*)this.Addon)->Name, 0x20); + + /// + /// Gets the pointer to the event source. + /// + required public nint Node { get; init; } + + /// + /// Gets the handler that gets called when this event is triggered. + /// + required public IAddonEventManager.AddonEventHandler Handler { get; init; } + + /// + /// Gets the unique id for this event. + /// + required public uint ParamKey { get; init; } + + /// + /// Gets the event type for this event. + /// + required public AddonEventType EventType { get; init; } + + /// + /// Gets the formatted log string for this AddonEventEntry. + /// + internal string LogString => $"ParamKey: {this.ParamKey}, Addon: {this.AddonName}, Event: {this.EventType}"; +} diff --git a/Dalamud/Game/AddonEventManager/AddonEventListener.cs b/Dalamud/Game/AddonEventManager/AddonEventListener.cs index cb0aa1502..8f724f890 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventListener.cs +++ b/Dalamud/Game/AddonEventManager/AddonEventListener.cs @@ -39,6 +39,11 @@ internal unsafe class AddonEventListener : IDisposable /// Event Data. /// Unknown Parameter. public delegate void ReceiveEventDelegate(AtkEventListener* self, AtkEventType eventType, uint eventParam, AtkEvent* eventData, nint unknown); + + /// + /// Gets the address of this listener. + /// + public nint Address => (nint)this.eventListener; /// public void Dispose() diff --git a/Dalamud/Game/AddonEventManager/AddonEventManager.cs b/Dalamud/Game/AddonEventManager/AddonEventManager.cs index 4718d4800..0aa4612c1 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventManager.cs +++ b/Dalamud/Game/AddonEventManager/AddonEventManager.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; +using System.Linq; +using Dalamud.Game.AddonLifecycle; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Logging.Internal; +using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -16,15 +19,24 @@ namespace Dalamud.Game.AddonEventManager; /// [InterfaceVersion("1.0")] [ServiceManager.EarlyLoadedService] -internal unsafe class AddonEventManager : IDisposable, IServiceType, IAddonEventManager +internal unsafe class AddonEventManager : IDisposable, IServiceType { + /// + /// PluginName for Dalamud Internal use. + /// + public const string DalamudInternalKey = "Dalamud.Internal"; + private static readonly ModuleLog Log = new("AddonEventManager"); + [ServiceManager.ServiceDependency] + private readonly AddonLifecycle.AddonLifecycle addonLifecycle = Service.Get(); + + private readonly AddonLifecycleEventListener finalizeEventListener; + private readonly AddonEventManagerAddressResolver address; private readonly Hook onUpdateCursor; - private readonly AddonEventListener eventListener; - private readonly Dictionary eventHandlers; + private readonly List pluginEventControllers; private AddonCursorType? cursorOverride; @@ -34,64 +46,109 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType, IAddonEvent this.address = new AddonEventManagerAddressResolver(); this.address.Setup(sigScanner); - this.eventHandlers = new Dictionary(); - this.eventListener = new AddonEventListener(this.DalamudAddonEventHandler); + this.pluginEventControllers = new List + { + new(DalamudInternalKey), // Create entry for Dalamud's Internal Use. + }; this.cursorOverride = null; this.onUpdateCursor = Hook.FromAddress(this.address.UpdateCursor, this.UpdateCursorDetour); + + this.finalizeEventListener = new AddonLifecycleEventListener(AddonEvent.PreFinalize, string.Empty, this.OnAddonFinalize); + this.addonLifecycle.RegisterListener(this.finalizeEventListener); } private delegate nint UpdateCursorDelegate(RaptureAtkModule* module); - /// - public void AddEvent(uint eventId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler) - { - if (!this.eventHandlers.ContainsKey(eventId)) - { - var type = (AtkEventType)eventType; - var node = (AtkResNode*)atkResNode; - var addon = (AtkUnitBase*)atkUnitBase; - - this.eventHandlers.Add(eventId, eventHandler); - this.eventListener.RegisterEvent(addon, node, type, eventId); - } - else - { - Log.Warning($"Attempted to register already registered eventId: {eventId}"); - } - } - - /// - public void RemoveEvent(uint eventId, IntPtr atkResNode, AddonEventType eventType) - { - if (this.eventHandlers.ContainsKey(eventId)) - { - var type = (AtkEventType)eventType; - var node = (AtkResNode*)atkResNode; - - this.eventListener.UnregisterEvent(node, type, eventId); - this.eventHandlers.Remove(eventId); - } - else - { - Log.Warning($"Attempted to unregister already unregistered eventId: {eventId}"); - } - } - /// public void Dispose() { this.onUpdateCursor.Dispose(); - this.eventListener.Dispose(); - this.eventHandlers.Clear(); + + foreach (var pluginEventController in this.pluginEventControllers) + { + pluginEventController.Dispose(); + } + + this.addonLifecycle.UnregisterListener(this.finalizeEventListener); + } + + /// + /// 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) + { + 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."); + } + } + + /// + /// 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) + { + if (this.pluginEventControllers.FirstOrDefault(entry => entry.PluginId == pluginId) is { } eventController) + { + eventController.RemoveEvent(eventId); + } + else + { + Log.Verbose($"Unable to locate controller for {pluginId}. No event was removed."); + } } - /// - public void SetCursor(AddonCursorType cursor) => this.cursorOverride = cursor; + /// + /// Force the game cursor to be the specified cursor. + /// + /// Which cursor to use. + internal void SetCursor(AddonCursorType cursor) => this.cursorOverride = cursor; - /// - public void ResetCursor() => this.cursorOverride = null; + /// + /// Un-forces the game cursor. + /// + internal void ResetCursor() => this.cursorOverride = null; + + /// + /// Adds a new managed event controller if one doesn't already exist for this pluginId. + /// + /// Unique ID for this plugin. + internal void AddPluginEventController(string pluginId) + { + if (this.pluginEventControllers.All(entry => entry.PluginId != pluginId)) + { + Log.Verbose($"Creating new PluginEventController for: {pluginId}"); + this.pluginEventControllers.Add(new PluginEventController(pluginId)); + } + } + + /// + /// Removes an existing managed event controller for the specified plugin. + /// + /// Unique ID for this plugin. + internal void RemovePluginEventController(string pluginId) + { + if (this.pluginEventControllers.FirstOrDefault(entry => entry.PluginId == pluginId) is { } controller) + { + Log.Verbose($"Removing PluginEventController for: {pluginId}"); + this.pluginEventControllers.Remove(controller); + controller.Dispose(); + } + } [ServiceManager.CallWhenServicesReady] private void ContinueConstruction() @@ -99,6 +156,22 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType, IAddonEvent this.onUpdateCursor.Enable(); } + /// + /// When an addon finalizes, check it for any registered events, and unregister them. + /// + /// Event type that triggered this call. + /// Addon that triggered this call. + private void OnAddonFinalize(AddonEvent eventType, AddonArgs addonInfo) + { + // It shouldn't be possible for this event to be anything other than PreFinalize. + if (eventType != AddonEvent.PreFinalize) return; + + foreach (var pluginList in this.pluginEventControllers) + { + pluginList.RemoveForAddon(addonInfo.AddonName); + } + } + private nint UpdateCursorDetour(RaptureAtkModule* module) { try @@ -123,22 +196,6 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType, IAddonEvent return this.onUpdateCursor!.Original(module); } - - private void DalamudAddonEventHandler(AtkEventListener* self, AtkEventType eventType, uint eventParam, AtkEvent* eventData, IntPtr unknown) - { - if (this.eventHandlers.TryGetValue(eventParam, out var handler) && eventData is not null) - { - try - { - // We passed the AtkUnitBase into the EventData.Node field from our AddonEventHandler - handler?.Invoke((AddonEventType)eventType, (nint)eventData->Node, (nint)eventData->Target); - } - catch (Exception exception) - { - Log.Error(exception, "Exception in DalamudAddonEventHandler custom event invoke."); - } - } - } } /// @@ -150,25 +207,24 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType, IAddonEvent #pragma warning disable SA1015 [ResolveVia] #pragma warning restore SA1015 -internal unsafe class AddonEventManagerPluginScoped : IDisposable, IServiceType, IAddonEventManager +internal class AddonEventManagerPluginScoped : IDisposable, IServiceType, IAddonEventManager { - private static readonly ModuleLog Log = new("AddonEventManager"); - [ServiceManager.ServiceDependency] - private readonly AddonEventManager baseEventManager = Service.Get(); + private readonly AddonEventManager eventManagerService = Service.Get(); - private readonly AddonEventListener eventListener; - private readonly Dictionary eventHandlers; + private readonly LocalPlugin plugin; private bool isForcingCursor; /// /// Initializes a new instance of the class. /// - public AddonEventManagerPluginScoped() + /// Plugin info for the plugin that requested this service. + public AddonEventManagerPluginScoped(LocalPlugin plugin) { - this.eventHandlers = new Dictionary(); - this.eventListener = new AddonEventListener(this.PluginAddonEventHandler); + this.plugin = plugin; + + this.eventManagerService.AddPluginEventController(plugin.Manifest.WorkingPluginId.ToString()); } /// @@ -177,54 +233,26 @@ internal unsafe class AddonEventManagerPluginScoped : IDisposable, IServiceType, // if multiple plugins force cursors and dispose without un-forcing them then all forces will be cleared. if (this.isForcingCursor) { - this.baseEventManager.ResetCursor(); + this.eventManagerService.ResetCursor(); } - this.eventListener.Dispose(); - this.eventHandlers.Clear(); + this.eventManagerService.RemovePluginEventController(this.plugin.Manifest.WorkingPluginId.ToString()); } /// - public void AddEvent(uint eventId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler) - { - if (!this.eventHandlers.ContainsKey(eventId)) - { - var type = (AtkEventType)eventType; - var node = (AtkResNode*)atkResNode; - var addon = (AtkUnitBase*)atkUnitBase; + 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); - this.eventHandlers.Add(eventId, eventHandler); - this.eventListener.RegisterEvent(addon, node, type, eventId); - } - else - { - Log.Warning($"Attempted to register already registered eventId: {eventId}"); - } - } - /// - public void RemoveEvent(uint eventId, IntPtr atkResNode, AddonEventType eventType) - { - if (this.eventHandlers.ContainsKey(eventId)) - { - var type = (AtkEventType)eventType; - var node = (AtkResNode*)atkResNode; - - this.eventListener.UnregisterEvent(node, type, eventId); - this.eventHandlers.Remove(eventId); - } - else - { - Log.Warning($"Attempted to unregister already unregistered eventId: {eventId}"); - } - } + public void RemoveEvent(uint eventId) + => this.eventManagerService.RemoveEvent(this.plugin.Manifest.WorkingPluginId.ToString(), eventId); /// public void SetCursor(AddonCursorType cursor) { this.isForcingCursor = true; - this.baseEventManager.SetCursor(cursor); + this.eventManagerService.SetCursor(cursor); } /// @@ -232,22 +260,6 @@ internal unsafe class AddonEventManagerPluginScoped : IDisposable, IServiceType, { this.isForcingCursor = false; - this.baseEventManager.ResetCursor(); - } - - private void PluginAddonEventHandler(AtkEventListener* self, AtkEventType eventType, uint eventParam, AtkEvent* eventData, IntPtr unknown) - { - if (this.eventHandlers.TryGetValue(eventParam, out var handler) && eventData is not null) - { - try - { - // We passed the AtkUnitBase into the EventData.Node field from our AddonEventHandler - handler?.Invoke((AddonEventType)eventType, (nint)eventData->Node, (nint)eventData->Target); - } - catch (Exception exception) - { - Log.Error(exception, "Exception in PluginAddonEventHandler custom event invoke."); - } - } + this.eventManagerService.ResetCursor(); } } diff --git a/Dalamud/Game/AddonEventManager/PluginEventController.cs b/Dalamud/Game/AddonEventManager/PluginEventController.cs new file mode 100644 index 000000000..2b9ba8e89 --- /dev/null +++ b/Dalamud/Game/AddonEventManager/PluginEventController.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Dalamud.Game.Gui; +using Dalamud.Logging.Internal; +using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Component.GUI; + +namespace Dalamud.Game.AddonEventManager; + +/// +/// Class to manage creating and cleaning up events per-plugin. +/// +internal unsafe class PluginEventController : IDisposable +{ + private static readonly ModuleLog Log = new("AddonEventManager"); + + /// + /// Initializes a new instance of the class. + /// + /// The Unique ID for this plugin. + public PluginEventController(string pluginId) + { + this.PluginId = pluginId; + + this.EventListener = new AddonEventListener(this.PluginEventListHandler); + } + + /// + /// Gets the unique ID for this PluginEventList. + /// + public string PluginId { get; init; } + + private AddonEventListener EventListener { get; init; } + + private List Events { get; } = new(); + + /// + /// 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) + { + var node = (AtkResNode*)atkResNode; + var addon = (AtkUnitBase*)atkUnitBase; + var eventType = (AtkEventType)atkEventType; + + var eventEntry = new AddonEventEntry + { + Addon = atkUnitBase, + Handler = handler, + Node = atkResNode, + EventType = atkEventType, + ParamKey = eventId, + }; + + Log.Verbose($"Adding Event: {eventEntry.LogString}"); + this.EventListener.RegisterEvent(addon, node, eventType, eventId); + this.Events.Add(eventEntry); + } + + /// + /// 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) + { + if (this.Events.FirstOrDefault(registeredEvent => registeredEvent.ParamKey == eventId) is not { } targetEvent) return; + + Log.Verbose($"Removing Event: {targetEvent.LogString}"); + this.TryRemoveEventFromNative(targetEvent); + this.Events.Remove(targetEvent); + } + + /// + /// Removes all events attached to the specified addon. + /// + /// Addon name to remove events from. + public void RemoveForAddon(string addonName) + { + foreach (var registeredEvent in this.Events.Where(entry => entry.AddonName == addonName).ToList()) + { + Log.Verbose($"Addon: {addonName} is Finalizing, removing event: {registeredEvent.LogString}"); + this.RemoveEvent(registeredEvent.ParamKey); + } + } + + /// + public void Dispose() + { + foreach (var registeredEvent in this.Events.ToList()) + { + this.RemoveEvent(registeredEvent.ParamKey); + } + + this.EventListener.Dispose(); + } + + /// + /// Attempts to remove a tracked event from native UI. + /// This method performs several safety checks to only remove events from a still active addon. + /// If any of these checks fail, it likely means the native UI already cleaned up the event, and we don't have to worry about them. + /// + /// Event entry to remove. + private void TryRemoveEventFromNative(AddonEventEntry eventEntry) + { + // Is the eventEntry addon valid? + if (eventEntry.AddonName is AddonEventEntry.InvalidAddonName) return; + + // Is an addon with the same name active? + var currentAddonPointer = Service.Get().GetAddonByName(eventEntry.AddonName); + if (currentAddonPointer == nint.Zero) return; + + // Is our stored addon pointer the same as the active addon pointer? + if (currentAddonPointer != eventEntry.Addon) return; + + // Does this addon contain the node this event is for? (by address) + var atkUnitBase = (AtkUnitBase*)currentAddonPointer; + var nodeFound = false; + foreach (var index in Enumerable.Range(0, atkUnitBase->UldManager.NodeListCount)) + { + var node = atkUnitBase->UldManager.NodeList[index]; + + // If this node matches our node, then we know our node is still valid. + if (node is not null && (nint)node == eventEntry.Node) + { + nodeFound = true; + } + } + + // If we didn't find the node, we can't remove the event. + if (!nodeFound) return; + + // Does the node have a registered event matching the parameters we have? + var atkResNode = (AtkResNode*)eventEntry.Node; + var eventType = (AtkEventType)eventEntry.EventType; + var currentEvent = atkResNode->AtkEventManager.Event; + var eventFound = false; + while (currentEvent is not null) + { + var paramKeyMatches = currentEvent->Param == eventEntry.ParamKey; + var eventListenerAddressMatches = (nint)currentEvent->Listener == this.EventListener.Address; + var eventTypeMatches = currentEvent->Type == eventType; + + if (paramKeyMatches && eventListenerAddressMatches && eventTypeMatches) + { + eventFound = true; + break; + } + + // Move to the next event. + currentEvent = currentEvent->NextEvent; + } + + // If we didn't find the event, we can't remove the event. + if (!eventFound) return; + + // We have a valid addon, valid node, valid event, and valid key. + this.EventListener.UnregisterEvent(atkResNode, eventType, eventEntry.ParamKey); + } + + private void PluginEventListHandler(AtkEventListener* self, AtkEventType eventType, uint eventParam, AtkEvent* eventData, IntPtr unknown) + { + try + { + if (eventData is null) return; + if (this.Events.FirstOrDefault(handler => handler.ParamKey == eventParam) is not { } eventInfo) return; + + // We stored the AtkUnitBase* in EventData->Node, and EventData->Target contains the node that triggered the event. + eventInfo.Handler.Invoke((AddonEventType)eventType, (nint)eventData->Node, (nint)eventData->Target); + } + catch (Exception exception) + { + Log.Error(exception, "Exception in PluginEventList custom event invoke."); + } + } +} diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index ae01d4886..0298dd203 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -5,19 +5,16 @@ using System.Linq; using Dalamud.Configuration.Internal; using Dalamud.Game.AddonEventManager; +using Dalamud.Game.AddonLifecycle; using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Logging.Internal; -using Dalamud.Memory; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Graphics; using FFXIVClientStructs.FFXIV.Client.System.Memory; using FFXIVClientStructs.FFXIV.Component.GUI; -using DalamudAddonEventManager = Dalamud.Game.AddonEventManager.AddonEventManager; - namespace Dalamud.Game.Gui.Dtr; /// @@ -45,23 +42,27 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar private readonly DalamudConfiguration configuration = Service.Get(); [ServiceManager.ServiceDependency] - private readonly DalamudAddonEventManager uiEventManager = Service.Get(); + private readonly AddonEventManager.AddonEventManager uiEventManager = Service.Get(); - private readonly DtrBarAddressResolver address; + [ServiceManager.ServiceDependency] + private readonly AddonLifecycle.AddonLifecycle addonLifecycle = Service.Get(); + + private readonly AddonLifecycleEventListener dtrPostDrawListener; + private readonly AddonLifecycleEventListener dtrPostRequestedUpdateListener; + private readonly ConcurrentBag newEntries = new(); private readonly List entries = new(); - private readonly Hook onAddonDrawHook; - private readonly Hook onAddonRequestedUpdateHook; + private uint runningNodeIds = BaseNodeId; [ServiceManager.ServiceConstructor] - private DtrBar(SigScanner sigScanner) + private DtrBar() { - this.address = new DtrBarAddressResolver(); - this.address.Setup(sigScanner); + this.dtrPostDrawListener = new AddonLifecycleEventListener(AddonEvent.PostDraw, "_DTR", this.OnDtrPostDraw); + this.dtrPostRequestedUpdateListener = new AddonLifecycleEventListener(AddonEvent.PostRequestedUpdate, "_DTR", this.OnAddonRequestedUpdateDetour); - this.onAddonDrawHook = Hook.FromAddress(this.address.AtkUnitBaseDraw, this.OnAddonDrawDetour); - this.onAddonRequestedUpdateHook = Hook.FromAddress(this.address.AddonRequestedUpdate, this.OnAddonRequestedUpdateDetour); + this.addonLifecycle.RegisterListener(this.dtrPostDrawListener); + this.addonLifecycle.RegisterListener(this.dtrPostRequestedUpdateListener); this.framework.Update += this.Update; @@ -70,10 +71,6 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar this.configuration.QueueSave(); } - private delegate void AddonDrawDelegate(AtkUnitBase* addon); - - private delegate void AddonRequestedUpdateDelegate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData); - /// public DtrBarEntry Get(string title, SeString? text = null) { @@ -104,8 +101,8 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar /// void IDisposable.Dispose() { - this.onAddonDrawHook.Dispose(); - this.onAddonRequestedUpdateHook.Dispose(); + this.addonLifecycle.UnregisterListener(this.dtrPostDrawListener); + this.addonLifecycle.UnregisterListener(this.dtrPostRequestedUpdateListener); foreach (var entry in this.entries) this.RemoveNode(entry.TextNode); @@ -167,13 +164,6 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar return xPos.CompareTo(yPos); }); } - - [ServiceManager.CallWhenServicesReady] - private void ContinueConstruction() - { - this.onAddonDrawHook.Enable(); - this.onAddonRequestedUpdateHook.Enable(); - } private AtkUnitBase* GetDtr() => (AtkUnitBase*)this.gameGui.GetAddonByName("_DTR").ToPointer(); @@ -260,37 +250,26 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar this.ApplySort(); } } - - // This hooks all AtkUnitBase.Draw calls, then checks for our specific addon name. - // AddonDtr doesn't implement it's own Draw method, would need to replace vtable entry to be more efficient. - private void OnAddonDrawDetour(AtkUnitBase* addon) + + private void OnDtrPostDraw(AddonEvent eventType, AddonArgs addonInfo) { - this.onAddonDrawHook!.Original(addon); + var addon = (AtkUnitBase*)addonInfo.Addon; - try - { - if (MemoryHelper.ReadString((nint)addon->Name, 0x20) is not "_DTR") return; - - this.UpdateNodePositions(addon); + this.UpdateNodePositions(addon); - if (!this.configuration.DtrSwapDirection) - { - var targetSize = (ushort)this.CalculateTotalSize(); - var sizeDelta = targetSize - addon->RootNode->Width; - - if (addon->RootNode->Width != targetSize) - { - addon->RootNode->SetWidth(targetSize); - addon->SetX((short)(addon->GetX() - sizeDelta)); - - // force a RequestedUpdate immediately to force the game to right-justify it immediately. - addon->OnUpdate(AtkStage.GetSingleton()->GetNumberArrayData(), AtkStage.GetSingleton()->GetStringArrayData()); - } - } - } - catch (Exception e) + if (!this.configuration.DtrSwapDirection) { - Log.Error(e, "Exception in OnAddonDraw."); + var targetSize = (ushort)this.CalculateTotalSize(); + var sizeDelta = targetSize - addon->RootNode->Width; + + if (addon->RootNode->Width != targetSize) + { + addon->RootNode->SetWidth(targetSize); + addon->SetX((short)(addon->GetX() - sizeDelta)); + + // force a RequestedUpdate immediately to force the game to right-justify it immediately. + addon->OnUpdate(AtkStage.GetSingleton()->GetNumberArrayData(), AtkStage.GetSingleton()->GetStringArrayData()); + } } } @@ -317,18 +296,11 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar } } - private void OnAddonRequestedUpdateDetour(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData) + private void OnAddonRequestedUpdateDetour(AddonEvent eventType, AddonArgs addonInfo) { - this.onAddonRequestedUpdateHook.Original(addon, numberArrayData, stringArrayData); - - try - { - this.UpdateNodePositions(addon); - } - catch (Exception e) - { - Log.Error(e, "Exception in OnAddonRequestedUpdate."); - } + var addon = (AtkUnitBase*)addonInfo.Addon; + + this.UpdateNodePositions(addon); } /// @@ -386,9 +358,9 @@ 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(node->AtkResNode.NodeID + MouseOverEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseOver, this.DtrEventHandler); - this.uiEventManager.AddEvent(node->AtkResNode.NodeID + MouseOutEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseOut, this.DtrEventHandler); - this.uiEventManager.AddEvent(node->AtkResNode.NodeID + MouseClickEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseClick, this.DtrEventHandler); + this.uiEventManager.AddEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOverEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseOver, this.DtrEventHandler); + this.uiEventManager.AddEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOutEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseOut, this.DtrEventHandler); + this.uiEventManager.AddEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseClickEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseClick, this.DtrEventHandler); var lastChild = dtr->RootNode->ChildNode; while (lastChild->PrevSiblingNode != null) lastChild = lastChild->PrevSiblingNode; @@ -406,14 +378,14 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar return true; } - private bool RemoveNode(AtkTextNode* node) + private void RemoveNode(AtkTextNode* node) { var dtr = this.GetDtr(); - if (dtr == null || dtr->RootNode == null || dtr->UldManager.NodeList == null || node == null) return false; + if (dtr == null || dtr->RootNode == null || dtr->UldManager.NodeList == null || node == null) return; - this.uiEventManager.RemoveEvent(node->AtkResNode.NodeID + MouseOverEventIdOffset, (nint)node, AddonEventType.MouseOver); - this.uiEventManager.RemoveEvent(node->AtkResNode.NodeID + MouseOutEventIdOffset, (nint)node, AddonEventType.MouseOut); - this.uiEventManager.RemoveEvent(node->AtkResNode.NodeID + MouseClickEventIdOffset, (nint)node, AddonEventType.MouseClick); + this.uiEventManager.RemoveEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOverEventIdOffset); + this.uiEventManager.RemoveEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOutEventIdOffset); + this.uiEventManager.RemoveEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseClickEventIdOffset); var tmpPrevNode = node->AtkResNode.PrevSiblingNode; var tmpNextNode = node->AtkResNode.NextSiblingNode; @@ -429,7 +401,6 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar dtr->UldManager.UpdateDrawNodeList(); dtr->UpdateCollisionNodeList(false); Log.Debug("Updated node draw list"); - return true; } private AtkTextNode* MakeNode(uint nodeId) diff --git a/Dalamud/Game/Gui/Dtr/DtrBarAddressResolver.cs b/Dalamud/Game/Gui/Dtr/DtrBarAddressResolver.cs deleted file mode 100644 index 1e6fd09cd..000000000 --- a/Dalamud/Game/Gui/Dtr/DtrBarAddressResolver.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace Dalamud.Game.Gui.Dtr; - -/// -/// DtrBar memory address resolver. -/// -public class DtrBarAddressResolver : BaseAddressResolver -{ - /// - /// Gets the address of the AtkUnitBaseDraw method. - /// This is the base handler for all addons. - /// We will use this here because _DTR does not have a overloaded handler, so we must use the base handler. - /// - public nint AtkUnitBaseDraw { get; private set; } - - /// - /// Gets the address of the DTRRequestUpdate method. - /// - public nint AddonRequestedUpdate { get; private set; } - - /// - /// Scan for and setup any configured address pointers. - /// - /// The signature scanner to facilitate setup. - protected override void Setup64Bit(SigScanner scanner) - { - this.AtkUnitBaseDraw = scanner.ScanText("48 83 EC 28 F6 81 ?? ?? ?? ?? ?? 4C 8B C1"); - this.AddonRequestedUpdate = scanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B BA ?? ?? ?? ?? 48 8B F1 49 8B 98 ?? ?? ?? ?? 33 D2"); - } -} diff --git a/Dalamud/Plugin/Services/IAddonEventManager.cs b/Dalamud/Plugin/Services/IAddonEventManager.cs index dbbfd784b..aa7e71478 100644 --- a/Dalamud/Plugin/Services/IAddonEventManager.cs +++ b/Dalamud/Plugin/Services/IAddonEventManager.cs @@ -29,9 +29,7 @@ public interface IAddonEventManager /// Unregisters an event handler with the specified event id and event type. /// /// The Unique Id for this event. - /// The node for this event. - /// The event type for this event. - void RemoveEvent(uint eventId, nint atkResNode, AddonEventType eventType); + void RemoveEvent(uint eventId); /// /// Force the game cursor to be the specified cursor. From f48c6d499b765285c297c05906c762bd19e5ee1d Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Tue, 19 Sep 2023 22:08:06 -0700 Subject: [PATCH 2/8] [AddonEventManager] Cleanup logging --- .../Game/AddonEventManager/PluginEventController.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Dalamud/Game/AddonEventManager/PluginEventController.cs b/Dalamud/Game/AddonEventManager/PluginEventController.cs index 2b9ba8e89..42424c6ec 100644 --- a/Dalamud/Game/AddonEventManager/PluginEventController.cs +++ b/Dalamud/Game/AddonEventManager/PluginEventController.cs @@ -83,10 +83,14 @@ internal unsafe class PluginEventController : IDisposable /// Addon name to remove events from. public void RemoveForAddon(string addonName) { - foreach (var registeredEvent in this.Events.Where(entry => entry.AddonName == addonName).ToList()) + if (this.Events.Where(entry => entry.AddonName == addonName).ToList() is { Count: not 0 } events) { - Log.Verbose($"Addon: {addonName} is Finalizing, removing event: {registeredEvent.LogString}"); - this.RemoveEvent(registeredEvent.ParamKey); + Log.Verbose($"Addon: {addonName} is Finalizing, removing {events.Count} events."); + + foreach (var registeredEvent in events) + { + this.RemoveEvent(registeredEvent.ParamKey); + } } } From 6f40449ab3abe57c5bfe278bf9994fe088e50071 Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Wed, 20 Sep 2023 09:46:16 -0700 Subject: [PATCH 3/8] Adjust Namespaces --- Dalamud/Game/AddonEventManager/AddonCursorType.cs | 2 +- Dalamud/Game/AddonEventManager/AddonEventEntry.cs | 2 +- .../Game/AddonEventManager/AddonEventListener.cs | 2 +- Dalamud/Game/AddonEventManager/AddonEventManager.cs | 5 ++--- .../AddonEventManagerAddressResolver.cs | 2 +- Dalamud/Game/AddonEventManager/AddonEventType.cs | 2 +- .../Game/AddonEventManager/PluginEventController.cs | 2 +- Dalamud/Game/AddonLifecycle/AddonArgs.cs | 2 +- Dalamud/Game/AddonLifecycle/AddonEvent.cs | 2 +- Dalamud/Game/AddonLifecycle/AddonLifecycle.cs | 2 +- .../AddonLifecycle/AddonLifecycleAddressResolver.cs | 2 +- .../AddonLifecycle/AddonLifecycleEventListener.cs | 2 +- Dalamud/Game/Gui/Dtr/DtrBar.cs | 13 +++++++++---- Dalamud/Plugin/Services/IAddonEventManager.cs | 2 +- Dalamud/Plugin/Services/IAddonLifecycle.cs | 2 +- 15 files changed, 24 insertions(+), 20 deletions(-) diff --git a/Dalamud/Game/AddonEventManager/AddonCursorType.cs b/Dalamud/Game/AddonEventManager/AddonCursorType.cs index 8ba3a901b..57d58c60c 100644 --- a/Dalamud/Game/AddonEventManager/AddonCursorType.cs +++ b/Dalamud/Game/AddonEventManager/AddonCursorType.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.AddonEventManager; +namespace Dalamud.Game.Addon; /// /// Reimplementation of CursorType. diff --git a/Dalamud/Game/AddonEventManager/AddonEventEntry.cs b/Dalamud/Game/AddonEventManager/AddonEventEntry.cs index 22b4756c1..83f1a724c 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventEntry.cs +++ b/Dalamud/Game/AddonEventManager/AddonEventEntry.cs @@ -2,7 +2,7 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.AddonEventManager; +namespace Dalamud.Game.Addon; /// /// This class represents a registered event that a plugin registers with a native ui node. diff --git a/Dalamud/Game/AddonEventManager/AddonEventListener.cs b/Dalamud/Game/AddonEventManager/AddonEventListener.cs index 8f724f890..6f7c55c4c 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventListener.cs +++ b/Dalamud/Game/AddonEventManager/AddonEventListener.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.AddonEventManager; +namespace Dalamud.Game.Addon; /// /// Event listener class for managing custom events. diff --git a/Dalamud/Game/AddonEventManager/AddonEventManager.cs b/Dalamud/Game/AddonEventManager/AddonEventManager.cs index 0aa4612c1..89554074a 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventManager.cs +++ b/Dalamud/Game/AddonEventManager/AddonEventManager.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; -using Dalamud.Game.AddonLifecycle; using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; @@ -12,7 +11,7 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.AddonEventManager; +namespace Dalamud.Game.Addon; /// /// Service provider for addon event management. @@ -29,7 +28,7 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType private static readonly ModuleLog Log = new("AddonEventManager"); [ServiceManager.ServiceDependency] - private readonly AddonLifecycle.AddonLifecycle addonLifecycle = Service.Get(); + private readonly AddonLifecycle addonLifecycle = Service.Get(); private readonly AddonLifecycleEventListener finalizeEventListener; diff --git a/Dalamud/Game/AddonEventManager/AddonEventManagerAddressResolver.cs b/Dalamud/Game/AddonEventManager/AddonEventManagerAddressResolver.cs index ba1c07db8..71a6093bb 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventManagerAddressResolver.cs +++ b/Dalamud/Game/AddonEventManager/AddonEventManagerAddressResolver.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.AddonEventManager; +namespace Dalamud.Game.Addon; /// /// AddonEventManager memory address resolver. diff --git a/Dalamud/Game/AddonEventManager/AddonEventType.cs b/Dalamud/Game/AddonEventManager/AddonEventType.cs index eef9763ff..74f35c257 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventType.cs +++ b/Dalamud/Game/AddonEventManager/AddonEventType.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.AddonEventManager; +namespace Dalamud.Game.Addon; /// /// Reimplementation of AtkEventType. diff --git a/Dalamud/Game/AddonEventManager/PluginEventController.cs b/Dalamud/Game/AddonEventManager/PluginEventController.cs index 42424c6ec..852d78128 100644 --- a/Dalamud/Game/AddonEventManager/PluginEventController.cs +++ b/Dalamud/Game/AddonEventManager/PluginEventController.cs @@ -7,7 +7,7 @@ using Dalamud.Logging.Internal; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.AddonEventManager; +namespace Dalamud.Game.Addon; /// /// Class to manage creating and cleaning up events per-plugin. diff --git a/Dalamud/Game/AddonLifecycle/AddonArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgs.cs index 50c995abb..4ae306817 100644 --- a/Dalamud/Game/AddonLifecycle/AddonArgs.cs +++ b/Dalamud/Game/AddonLifecycle/AddonArgs.cs @@ -1,7 +1,7 @@ using Dalamud.Memory; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.AddonLifecycle; +namespace Dalamud.Game.Addon; /// /// Addon argument data for use in event subscribers. diff --git a/Dalamud/Game/AddonLifecycle/AddonEvent.cs b/Dalamud/Game/AddonLifecycle/AddonEvent.cs index faef30c88..cfc83fb8a 100644 --- a/Dalamud/Game/AddonLifecycle/AddonEvent.cs +++ b/Dalamud/Game/AddonLifecycle/AddonEvent.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.AddonLifecycle; +namespace Dalamud.Game.Addon; /// /// Enumeration for available AddonLifecycle events. diff --git a/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs b/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs index c416b6d1f..68233eeb8 100644 --- a/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs +++ b/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs @@ -11,7 +11,7 @@ using Dalamud.Logging.Internal; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Component.GUI; -namespace Dalamud.Game.AddonLifecycle; +namespace Dalamud.Game.Addon; /// /// This class provides events for in-game addon lifecycles. diff --git a/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs b/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs index 079e09c80..d68fee9ed 100644 --- a/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs +++ b/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.AddonLifecycle; +namespace Dalamud.Game.Addon; /// /// AddonLifecycleService memory address resolver. diff --git a/Dalamud/Game/AddonLifecycle/AddonLifecycleEventListener.cs b/Dalamud/Game/AddonLifecycle/AddonLifecycleEventListener.cs index 0f088362d..12ccf5e8f 100644 --- a/Dalamud/Game/AddonLifecycle/AddonLifecycleEventListener.cs +++ b/Dalamud/Game/AddonLifecycle/AddonLifecycleEventListener.cs @@ -1,6 +1,6 @@ using Dalamud.Plugin.Services; -namespace Dalamud.Game.AddonLifecycle; +namespace Dalamud.Game.Addon; /// /// This class is a helper for tracking and invoking listener delegates. diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index 0298dd203..860750dc7 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -4,8 +4,7 @@ using System.Collections.Generic; using System.Linq; using Dalamud.Configuration.Internal; -using Dalamud.Game.AddonEventManager; -using Dalamud.Game.AddonLifecycle; +using Dalamud.Game.Addon; using Dalamud.Game.Text.SeStringHandling; using Dalamud.IoC; using Dalamud.IoC.Internal; @@ -42,10 +41,10 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar private readonly DalamudConfiguration configuration = Service.Get(); [ServiceManager.ServiceDependency] - private readonly AddonEventManager.AddonEventManager uiEventManager = Service.Get(); + private readonly AddonEventManager uiEventManager = Service.Get(); [ServiceManager.ServiceDependency] - private readonly AddonLifecycle.AddonLifecycle addonLifecycle = Service.Get(); + private readonly AddonLifecycle addonLifecycle = Service.Get(); private readonly AddonLifecycleEventListener dtrPostDrawListener; private readonly AddonLifecycleEventListener dtrPostRequestedUpdateListener; @@ -361,6 +360,9 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar this.uiEventManager.AddEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOverEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseOver, this.DtrEventHandler); this.uiEventManager.AddEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOutEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseOut, this.DtrEventHandler); this.uiEventManager.AddEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseClickEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseClick, this.DtrEventHandler); + 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); var lastChild = dtr->RootNode->ChildNode; while (lastChild->PrevSiblingNode != null) lastChild = lastChild->PrevSiblingNode; @@ -386,6 +388,9 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar this.uiEventManager.RemoveEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOverEventIdOffset); this.uiEventManager.RemoveEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOutEventIdOffset); this.uiEventManager.RemoveEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseClickEventIdOffset); + 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); 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 aa7e71478..f3588f469 100644 --- a/Dalamud/Plugin/Services/IAddonEventManager.cs +++ b/Dalamud/Plugin/Services/IAddonEventManager.cs @@ -1,4 +1,4 @@ -using Dalamud.Game.AddonEventManager; +using Dalamud.Game.Addon; namespace Dalamud.Plugin.Services; diff --git a/Dalamud/Plugin/Services/IAddonLifecycle.cs b/Dalamud/Plugin/Services/IAddonLifecycle.cs index 1dc792660..e455754a1 100644 --- a/Dalamud/Plugin/Services/IAddonLifecycle.cs +++ b/Dalamud/Plugin/Services/IAddonLifecycle.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Runtime.InteropServices; -using Dalamud.Game.AddonLifecycle; +using Dalamud.Game.Addon; namespace Dalamud.Plugin.Services; From b32a3b93856106a723c5f1943bf3254c99877d38 Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Wed, 20 Sep 2023 09:46:38 -0700 Subject: [PATCH 4/8] Fix missed namespace --- .../Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs index 3a1cb0e77..a9948430f 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -using Dalamud.Game.AddonLifecycle; +using Dalamud.Game.Addon; using ImGuiNET; namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps; From 5f86496360c117933c949d41885f994c5d591ed4 Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:55:45 -0700 Subject: [PATCH 5/8] Add missing renames --- Dalamud/Game/Gui/Dtr/DtrBar.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index 860750dc7..f27698e54 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -357,9 +357,6 @@ 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.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOverEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseOver, this.DtrEventHandler); - this.uiEventManager.AddEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOutEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseOut, this.DtrEventHandler); - this.uiEventManager.AddEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseClickEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseClick, this.DtrEventHandler); 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); @@ -385,9 +382,6 @@ 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.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOverEventIdOffset); - this.uiEventManager.RemoveEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseOutEventIdOffset); - this.uiEventManager.RemoveEvent(AddonEventManager.AddonEventManager.DalamudInternalKey, node->AtkResNode.NodeID + MouseClickEventIdOffset); 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); From b26550eea9a15027db05e8f716c1b1e548ed2816 Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:56:07 -0700 Subject: [PATCH 6/8] Fix not applying spacing when set to grow from the right --- Dalamud/Game/Gui/Dtr/DtrBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index f27698e54..48e04e6a3 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -223,7 +223,7 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar if (this.configuration.DtrSwapDirection) { - data.TextNode->AtkResNode.SetPositionFloat(runningXPos, 2); + data.TextNode->AtkResNode.SetPositionFloat(runningXPos + this.configuration.DtrSpacing, 2); runningXPos += elementWidth; } else From 48e60a7a5d3f460a6e8de0c0b138337bf898b491 Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Wed, 20 Sep 2023 11:22:58 -0700 Subject: [PATCH 7/8] Fix hidden nodes preventing native tooltips from propogating --- Dalamud/Game/Gui/Dtr/DtrBar.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index 48e04e6a3..8882088ef 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -232,6 +232,11 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar data.TextNode->AtkResNode.SetPositionFloat(runningXPos, 2); } } + else + { + // If we want the node hidden, shift it up, to prevent collision conflicts + data.TextNode->AtkResNode.SetY(-collisionNode->Height); + } } } From dae105d157b9c4e9ad4f1b1d5c9ef397be3e44d7 Mon Sep 17 00:00:00 2001 From: MidoriKami <9083275+MidoriKami@users.noreply.github.com> Date: Wed, 20 Sep 2023 13:19:06 -0700 Subject: [PATCH 8/8] Account for UiScale when moving nodes --- Dalamud/Game/Gui/Dtr/DtrBar.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index 8882088ef..880bc0625 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -235,7 +235,7 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar else { // If we want the node hidden, shift it up, to prevent collision conflicts - data.TextNode->AtkResNode.SetY(-collisionNode->Height); + data.TextNode->AtkResNode.SetY(-collisionNode->Height * dtr->RootNode->ScaleX); } } } @@ -264,7 +264,7 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar if (!this.configuration.DtrSwapDirection) { var targetSize = (ushort)this.CalculateTotalSize(); - var sizeDelta = targetSize - addon->RootNode->Width; + var sizeDelta = MathF.Round((targetSize - addon->RootNode->Width) * addon->RootNode->ScaleX); if (addon->RootNode->Width != targetSize) {