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 01/18] [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 02/18] [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 03/18] 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 04/18] 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 d836a3e55631d6af4489740cbf18c64350064420 Mon Sep 17 00:00:00 2001
From: Haselnussbomber
Date: Wed, 20 Sep 2023 19:14:22 +0200
Subject: [PATCH 05/18] Catch exceptions in Window.Draw
---
Dalamud/Interface/Windowing/Window.cs | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/Dalamud/Interface/Windowing/Window.cs b/Dalamud/Interface/Windowing/Window.cs
index 39c61566b..73a14db79 100644
--- a/Dalamud/Interface/Windowing/Window.cs
+++ b/Dalamud/Interface/Windowing/Window.cs
@@ -1,9 +1,11 @@
+using System;
using System.Numerics;
using Dalamud.Configuration.Internal;
using Dalamud.Game.ClientState.Keys;
using FFXIVClientStructs.FFXIV.Client.UI;
using ImGuiNET;
+using Serilog;
namespace Dalamud.Interface.Windowing;
@@ -284,7 +286,14 @@ public abstract class Window
if (this.ShowCloseButton ? ImGui.Begin(this.WindowName, ref this.internalIsOpen, this.Flags) : ImGui.Begin(this.WindowName, this.Flags))
{
// Draw the actual window contents
- this.Draw();
+ try
+ {
+ this.Draw();
+ }
+ catch (Exception ex)
+ {
+ Log.Error(ex, $"Error during Draw(): {this.WindowName}");
+ }
}
if (wasFocused)
From cec382dfed1fa68f5ee3bb61acd4b0ea309d616f Mon Sep 17 00:00:00 2001
From: Haselnussbomber
Date: Wed, 20 Sep 2023 19:53:45 +0200
Subject: [PATCH 06/18] Switch to ModuleLog in Window
---
Dalamud/Interface/Windowing/Window.cs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/Dalamud/Interface/Windowing/Window.cs b/Dalamud/Interface/Windowing/Window.cs
index 73a14db79..277fb46c8 100644
--- a/Dalamud/Interface/Windowing/Window.cs
+++ b/Dalamud/Interface/Windowing/Window.cs
@@ -3,9 +3,9 @@ using System.Numerics;
using Dalamud.Configuration.Internal;
using Dalamud.Game.ClientState.Keys;
+using Dalamud.Logging.Internal;
using FFXIVClientStructs.FFXIV.Client.UI;
using ImGuiNET;
-using Serilog;
namespace Dalamud.Interface.Windowing;
@@ -14,6 +14,8 @@ namespace Dalamud.Interface.Windowing;
///
public abstract class Window
{
+ private static readonly ModuleLog Log = new("WindowSystem");
+
private static bool wasEscPressedLastFrame = false;
private bool internalLastIsOpen = false;
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 07/18] 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 08/18] 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 09/18] 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 10/18] 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)
{
From b79241902fc8b42e88b64bdb5d451a41787f2832 Mon Sep 17 00:00:00 2001
From: Kaz Wolfe
Date: Wed, 20 Sep 2023 22:41:07 -0700
Subject: [PATCH 11/18] Use multi-platform strategy for Targets
- Intelligently choose the default `DalamudLibPath` depending on the building system's OS.
- Allow overrides with ``$DALAMUD_HOME` env var.
---
targets/Dalamud.Plugin.Bootstrap.targets | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/targets/Dalamud.Plugin.Bootstrap.targets b/targets/Dalamud.Plugin.Bootstrap.targets
index c30a5acba..db4bf6cd7 100644
--- a/targets/Dalamud.Plugin.Bootstrap.targets
+++ b/targets/Dalamud.Plugin.Bootstrap.targets
@@ -1,11 +1,10 @@
- $(appdata)\XIVLauncher\addon\Hooks\dev\
-
-
-
- $(DALAMUD_HOME)/
+ $(appdata)\XIVLauncher\addon\Hooks\dev\
+ $(HOME)/.xlcore/dalamud/Hooks/dev/
+ $(HOME)/Library/Application Support/XIV on Mac/dalamud/Hooks/dev/
+ $(DALAMUD_HOME)/
From 0636a03e41bab46300544bfc00f6643648db1ef8 Mon Sep 17 00:00:00 2001
From: MidoriKami <9083275+MidoriKami@users.noreply.github.com>
Date: Thu, 21 Sep 2023 15:26:08 -0700
Subject: [PATCH 12/18] Include argument data in event information.
---
.../AddonEventManager/AddonEventManager.cs | 2 +-
.../AddonArgTypes/AddonDrawArgs.cs | 13 ++++++
.../AddonArgTypes/AddonFinalizeArgs.cs | 13 ++++++
.../AddonArgTypes/AddonRefreshArgs.cs | 23 ++++++++++
.../AddonArgTypes/AddonRequestedUpdateArgs.cs | 23 ++++++++++
.../AddonArgTypes/AddonSetupArgs.cs | 13 ++++++
.../AddonArgTypes/AddonUpdateArgs.cs | 18 ++++++++
Dalamud/Game/AddonLifecycle/AddonArgs.cs | 22 ---------
Dalamud/Game/AddonLifecycle/AddonArgsType.cs | 37 +++++++++++++++
Dalamud/Game/AddonLifecycle/AddonLifecycle.cs | 45 ++++++++++++++-----
Dalamud/Game/AddonLifecycle/IAddonArgs.cs | 25 +++++++++++
Dalamud/Game/Gui/Dtr/DtrBar.cs | 4 +-
.../AgingSteps/AddonLifecycleAgingStep.cs | 12 ++---
Dalamud/Plugin/Services/IAddonLifecycle.cs | 4 +-
14 files changed, 209 insertions(+), 45 deletions(-)
create mode 100644 Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonDrawArgs.cs
create mode 100644 Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonFinalizeArgs.cs
create mode 100644 Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRefreshArgs.cs
create mode 100644 Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs
create mode 100644 Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs
create mode 100644 Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonUpdateArgs.cs
delete mode 100644 Dalamud/Game/AddonLifecycle/AddonArgs.cs
create mode 100644 Dalamud/Game/AddonLifecycle/AddonArgsType.cs
create mode 100644 Dalamud/Game/AddonLifecycle/IAddonArgs.cs
diff --git a/Dalamud/Game/AddonEventManager/AddonEventManager.cs b/Dalamud/Game/AddonEventManager/AddonEventManager.cs
index 89554074a..730a7a404 100644
--- a/Dalamud/Game/AddonEventManager/AddonEventManager.cs
+++ b/Dalamud/Game/AddonEventManager/AddonEventManager.cs
@@ -160,7 +160,7 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType
///
/// Event type that triggered this call.
/// Addon that triggered this call.
- private void OnAddonFinalize(AddonEvent eventType, AddonArgs addonInfo)
+ private void OnAddonFinalize(AddonEvent eventType, IAddonArgs addonInfo)
{
// It shouldn't be possible for this event to be anything other than PreFinalize.
if (eventType != AddonEvent.PreFinalize) return;
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonDrawArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonDrawArgs.cs
new file mode 100644
index 000000000..614a7ac2a
--- /dev/null
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonDrawArgs.cs
@@ -0,0 +1,13 @@
+namespace Dalamud.Game.Addon.AddonArgTypes;
+
+///
+/// Addon argument data for Finalize events.
+///
+public class AddonDrawArgs : IAddonArgs
+{
+ ///
+ public nint Addon { get; init; }
+
+ ///
+ public AddonArgsType Type => AddonArgsType.Draw;
+}
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonFinalizeArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonFinalizeArgs.cs
new file mode 100644
index 000000000..aa31fb051
--- /dev/null
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonFinalizeArgs.cs
@@ -0,0 +1,13 @@
+namespace Dalamud.Game.Addon.AddonArgTypes;
+
+///
+/// Addon argument data for Finalize events.
+///
+public class AddonFinalizeArgs : IAddonArgs
+{
+ ///
+ public nint Addon { get; init; }
+
+ ///
+ public AddonArgsType Type => AddonArgsType.Finalize;
+}
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRefreshArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRefreshArgs.cs
new file mode 100644
index 000000000..ab4f37c3c
--- /dev/null
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRefreshArgs.cs
@@ -0,0 +1,23 @@
+namespace Dalamud.Game.Addon.AddonArgTypes;
+
+///
+/// Addon argument data for Finalize events.
+///
+public class AddonRefreshArgs : IAddonArgs
+{
+ ///
+ public nint Addon { get; init; }
+
+ ///
+ public AddonArgsType Type => AddonArgsType.Refresh;
+
+ ///
+ /// Gets the number of AtkValues.
+ ///
+ public uint AtkValueCount { get; init; }
+
+ ///
+ /// Gets the address of the AtkValue array.
+ ///
+ public nint AtkValues { get; init; }
+}
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs
new file mode 100644
index 000000000..dfd0dac5e
--- /dev/null
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs
@@ -0,0 +1,23 @@
+namespace Dalamud.Game.Addon.AddonArgTypes;
+
+///
+/// Addon argument data for Finalize events.
+///
+public class AddonRequestedUpdateArgs : IAddonArgs
+{
+ ///
+ public nint Addon { get; init; }
+
+ ///
+ public AddonArgsType Type => AddonArgsType.RequestedUpdate;
+
+ ///
+ /// Gets the NumberArrayData** for this event.
+ ///
+ public nint NumberArrayData { get; init; }
+
+ ///
+ /// Gets the StringArrayData** for this event.
+ ///
+ public nint StringArrayData { get; init; }
+}
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs
new file mode 100644
index 000000000..a73d11ae2
--- /dev/null
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs
@@ -0,0 +1,13 @@
+namespace Dalamud.Game.Addon.AddonArgTypes;
+
+///
+/// Addon argument data for Setup events.
+///
+public class AddonSetupArgs : IAddonArgs
+{
+ ///
+ public nint Addon { get; init; }
+
+ ///
+ public AddonArgsType Type => AddonArgsType.Setup;
+}
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonUpdateArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonUpdateArgs.cs
new file mode 100644
index 000000000..ede588001
--- /dev/null
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonUpdateArgs.cs
@@ -0,0 +1,18 @@
+namespace Dalamud.Game.Addon.AddonArgTypes;
+
+///
+/// Addon argument data for Finalize events.
+///
+public class AddonUpdateArgs : IAddonArgs
+{
+ ///
+ public nint Addon { get; init; }
+
+ ///
+ public AddonArgsType Type => AddonArgsType.Update;
+
+ ///
+ /// Gets the time since the last update.
+ ///
+ public float TimeDelta { get; init; }
+}
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgs.cs
deleted file mode 100644
index 4ae306817..000000000
--- a/Dalamud/Game/AddonLifecycle/AddonArgs.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using Dalamud.Memory;
-using FFXIVClientStructs.FFXIV.Component.GUI;
-
-namespace Dalamud.Game.Addon;
-
-///
-/// Addon argument data for use in event subscribers.
-///
-public unsafe class AddonArgs
-{
- private string? addonName;
-
- ///
- /// Gets the name of the addon this args referrers to.
- ///
- public string AddonName => this.Addon == nint.Zero ? "NullAddon" : this.addonName ??= MemoryHelper.ReadString((nint)((AtkUnitBase*)this.Addon)->Name, 0x20);
-
- ///
- /// Gets the pointer to the addons AtkUnitBase.
- ///
- required public nint Addon { get; init; }
-}
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgsType.cs b/Dalamud/Game/AddonLifecycle/AddonArgsType.cs
new file mode 100644
index 000000000..ac325229d
--- /dev/null
+++ b/Dalamud/Game/AddonLifecycle/AddonArgsType.cs
@@ -0,0 +1,37 @@
+namespace Dalamud.Game.Addon;
+
+///
+/// Enumeration for available AddonLifecycle arg data
+///
+public enum AddonArgsType
+{
+ ///
+ /// Contains argument data for Setup.
+ ///
+ Setup,
+
+ ///
+ /// Contains argument data for Update.
+ ///
+ Update,
+
+ ///
+ /// Contains argument data for Draw.
+ ///
+ Draw,
+
+ ///
+ /// Contains argument data for Finalize.
+ ///
+ Finalize,
+
+ ///
+ /// Contains argument data for RequestedUpdate.
+ ///
+ RequestedUpdate,
+
+ ///
+ /// Contains argument data for Refresh.
+ ///
+ Refresh,
+}
diff --git a/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs b/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs
index 68233eeb8..7f4a4de95 100644
--- a/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs
+++ b/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs
@@ -3,6 +3,7 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
+using Dalamud.Game.Addon.AddonArgTypes;
using Dalamud.Hooking;
using Dalamud.Hooking.Internal;
using Dalamud.IoC;
@@ -127,7 +128,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
this.onAddonRequestedUpdateHook.Enable();
}
- private void InvokeListeners(AddonEvent eventType, AddonArgs args)
+ private void InvokeListeners(AddonEvent eventType, IAddonArgs args)
{
// Match on string.empty for listeners that want events for all addons.
foreach (var listener in this.eventListeners.Where(listener => listener.EventType == eventType && (listener.AddonName == args.AddonName || listener.AddonName == string.Empty)))
@@ -140,7 +141,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
{
try
{
- this.InvokeListeners(AddonEvent.PreSetup, new AddonArgs { Addon = (nint)addon });
+ this.InvokeListeners(AddonEvent.PreSetup, new AddonSetupArgs { Addon = (nint)addon });
}
catch (Exception e)
{
@@ -151,7 +152,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
try
{
- this.InvokeListeners(AddonEvent.PostSetup, new AddonArgs { Addon = (nint)addon });
+ this.InvokeListeners(AddonEvent.PostSetup, new AddonSetupArgs { Addon = (nint)addon });
}
catch (Exception e)
{
@@ -165,7 +166,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
{
try
{
- this.InvokeListeners(AddonEvent.PreFinalize, new AddonArgs { Addon = (nint)atkUnitBase[0] });
+ this.InvokeListeners(AddonEvent.PreFinalize, new AddonFinalizeArgs { Addon = (nint)atkUnitBase[0] });
}
catch (Exception e)
{
@@ -179,7 +180,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
{
try
{
- this.InvokeListeners(AddonEvent.PreDraw, new AddonArgs { Addon = (nint)addon });
+ this.InvokeListeners(AddonEvent.PreDraw, new AddonDrawArgs { Addon = (nint)addon });
}
catch (Exception e)
{
@@ -190,7 +191,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
try
{
- this.InvokeListeners(AddonEvent.PostDraw, new AddonArgs { Addon = (nint)addon });
+ this.InvokeListeners(AddonEvent.PostDraw, new AddonDrawArgs { Addon = (nint)addon });
}
catch (Exception e)
{
@@ -202,7 +203,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
{
try
{
- this.InvokeListeners(AddonEvent.PreUpdate, new AddonArgs { Addon = (nint)addon });
+ this.InvokeListeners(AddonEvent.PreUpdate, new AddonUpdateArgs { Addon = (nint)addon, TimeDelta = delta });
}
catch (Exception e)
{
@@ -213,7 +214,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
try
{
- this.InvokeListeners(AddonEvent.PostUpdate, new AddonArgs { Addon = (nint)addon });
+ this.InvokeListeners(AddonEvent.PostUpdate, new AddonUpdateArgs { Addon = (nint)addon, TimeDelta = delta });
}
catch (Exception e)
{
@@ -225,7 +226,12 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
{
try
{
- this.InvokeListeners(AddonEvent.PreRefresh, new AddonArgs { Addon = (nint)addon });
+ this.InvokeListeners(AddonEvent.PreRefresh, new AddonRefreshArgs
+ {
+ Addon = (nint)addon,
+ AtkValueCount = valueCount,
+ AtkValues = (nint)values,
+ });
}
catch (Exception e)
{
@@ -236,7 +242,12 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
try
{
- this.InvokeListeners(AddonEvent.PostRefresh, new AddonArgs { Addon = (nint)addon });
+ this.InvokeListeners(AddonEvent.PostRefresh, new AddonRefreshArgs
+ {
+ Addon = (nint)addon,
+ AtkValueCount = valueCount,
+ AtkValues = (nint)values,
+ });
}
catch (Exception e)
{
@@ -250,7 +261,12 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
{
try
{
- this.InvokeListeners(AddonEvent.PreRequestedUpdate, new AddonArgs { Addon = (nint)addon });
+ this.InvokeListeners(AddonEvent.PreRequestedUpdate, new AddonRequestedUpdateArgs
+ {
+ Addon = (nint)addon,
+ NumberArrayData = (nint)numberArrayData,
+ StringArrayData = (nint)stringArrayData,
+ });
}
catch (Exception e)
{
@@ -261,7 +277,12 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
try
{
- this.InvokeListeners(AddonEvent.PostRequestedUpdate, new AddonArgs { Addon = (nint)addon });
+ this.InvokeListeners(AddonEvent.PostRequestedUpdate, new AddonRequestedUpdateArgs
+ {
+ Addon = (nint)addon,
+ NumberArrayData = (nint)numberArrayData,
+ StringArrayData = (nint)stringArrayData,
+ });
}
catch (Exception e)
{
diff --git a/Dalamud/Game/AddonLifecycle/IAddonArgs.cs b/Dalamud/Game/AddonLifecycle/IAddonArgs.cs
new file mode 100644
index 000000000..ba77a2c6d
--- /dev/null
+++ b/Dalamud/Game/AddonLifecycle/IAddonArgs.cs
@@ -0,0 +1,25 @@
+using Dalamud.Memory;
+using FFXIVClientStructs.FFXIV.Component.GUI;
+
+namespace Dalamud.Game.Addon;
+
+///
+/// Interface representing the argument data for AddonLifecycle events.
+///
+public unsafe interface IAddonArgs
+{
+ ///
+ /// Gets the name of the addon this args referrers to.
+ ///
+ string AddonName => this.Addon == nint.Zero ? "NullAddon" : MemoryHelper.ReadString((nint)((AtkUnitBase*)this.Addon)->Name, 0x20);
+
+ ///
+ /// Gets the pointer to the addons AtkUnitBase.
+ ///
+ nint Addon { get; init; }
+
+ ///
+ /// Gets the type of these args.
+ ///
+ AddonArgsType Type { get; }
+}
diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs
index 880bc0625..6b74e47cd 100644
--- a/Dalamud/Game/Gui/Dtr/DtrBar.cs
+++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs
@@ -255,7 +255,7 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
}
}
- private void OnDtrPostDraw(AddonEvent eventType, AddonArgs addonInfo)
+ private void OnDtrPostDraw(AddonEvent eventType, IAddonArgs addonInfo)
{
var addon = (AtkUnitBase*)addonInfo.Addon;
@@ -300,7 +300,7 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
}
}
- private void OnAddonRequestedUpdateDetour(AddonEvent eventType, AddonArgs addonInfo)
+ private void OnAddonRequestedUpdateDetour(AddonEvent eventType, IAddonArgs addonInfo)
{
var addon = (AtkUnitBase*)addonInfo.Addon;
diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs
index a9948430f..0821e62de 100644
--- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs
+++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs
@@ -100,32 +100,32 @@ internal class AddonLifecycleAgingStep : IAgingStep
}
}
- private void PostSetup(AddonEvent eventType, AddonArgs addonInfo)
+ private void PostSetup(AddonEvent eventType, IAddonArgs addonInfo)
{
if (this.currentStep is TestStep.CharacterSetup) this.currentStep++;
}
- private void PostUpdate(AddonEvent eventType, AddonArgs addonInfo)
+ private void PostUpdate(AddonEvent eventType, IAddonArgs addonInfo)
{
if (this.currentStep is TestStep.CharacterUpdate) this.currentStep++;
}
- private void PostDraw(AddonEvent eventType, AddonArgs addonInfo)
+ private void PostDraw(AddonEvent eventType, IAddonArgs addonInfo)
{
if (this.currentStep is TestStep.CharacterDraw) this.currentStep++;
}
- private void PostRefresh(AddonEvent eventType, AddonArgs addonInfo)
+ private void PostRefresh(AddonEvent eventType, IAddonArgs addonInfo)
{
if (this.currentStep is TestStep.CharacterRefresh) this.currentStep++;
}
- private void PostRequestedUpdate(AddonEvent eventType, AddonArgs addonInfo)
+ private void PostRequestedUpdate(AddonEvent eventType, IAddonArgs addonInfo)
{
if (this.currentStep is TestStep.CharacterRequestedUpdate) this.currentStep++;
}
- private void PreFinalize(AddonEvent eventType, AddonArgs addonInfo)
+ private void PreFinalize(AddonEvent eventType, IAddonArgs addonInfo)
{
if (this.currentStep is TestStep.CharacterFinalize) this.currentStep++;
}
diff --git a/Dalamud/Plugin/Services/IAddonLifecycle.cs b/Dalamud/Plugin/Services/IAddonLifecycle.cs
index e455754a1..5290395ab 100644
--- a/Dalamud/Plugin/Services/IAddonLifecycle.cs
+++ b/Dalamud/Plugin/Services/IAddonLifecycle.cs
@@ -14,8 +14,8 @@ public interface IAddonLifecycle
/// Delegate for receiving addon lifecycle event messages.
///
/// The event type that triggered the message.
- /// Information about what addon triggered the message.
- public delegate void AddonEventDelegate(AddonEvent eventType, AddonArgs addonInfo);
+ /// Information about what addon triggered the message.
+ public delegate void AddonEventDelegate(AddonEvent eventType, IAddonArgs args);
///
/// Register a listener that will trigger on the specified event and any of the specified addons.
From bd81d230972402d992ced89888aaa5d959ebf700 Mon Sep 17 00:00:00 2001
From: MidoriKami <9083275+MidoriKami@users.noreply.github.com>
Date: Thu, 21 Sep 2023 17:05:27 -0700
Subject: [PATCH 13/18] Use CallHook for AddonSetup
---
.../AddonArgTypes/AddonSetupArgs.cs | 10 +++++++
Dalamud/Game/AddonLifecycle/AddonLifecycle.cs | 26 ++++++++++++-------
.../AddonLifecycleAddressResolver.cs | 2 +-
3 files changed, 28 insertions(+), 10 deletions(-)
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs
index a73d11ae2..4b467deb8 100644
--- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs
@@ -10,4 +10,14 @@ public class AddonSetupArgs : IAddonArgs
///
public AddonArgsType Type => AddonArgsType.Setup;
+
+ ///
+ /// Gets the number of AtkValues.
+ ///
+ public uint AtkValueCount { get; init; }
+
+ ///
+ /// Gets the address of the AtkValue array.
+ ///
+ public nint AtkValues { get; init; }
}
diff --git a/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs b/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs
index 7f4a4de95..75b5b3753 100644
--- a/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs
+++ b/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs
@@ -27,7 +27,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
private readonly Framework framework = Service.Get();
private readonly AddonLifecycleAddressResolver address;
- private readonly Hook onAddonSetupHook;
+ private readonly CallHook onAddonSetupHook;
private readonly Hook onAddonFinalizeHook;
private readonly CallHook onAddonDrawHook;
private readonly CallHook onAddonUpdateHook;
@@ -46,7 +46,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
this.framework.Update += this.OnFrameworkUpdate;
- this.onAddonSetupHook = Hook.FromAddress(this.address.AddonSetup, this.OnAddonSetup);
+ this.onAddonSetupHook = new CallHook(this.address.AddonSetup, this.OnAddonSetup);
this.onAddonFinalizeHook = Hook.FromAddress(this.address.AddonFinalize, this.OnAddonFinalize);
this.onAddonDrawHook = new CallHook(this.address.AddonDraw, this.OnAddonDraw);
this.onAddonUpdateHook = new CallHook(this.address.AddonUpdate, this.OnAddonUpdate);
@@ -54,7 +54,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
this.onAddonRequestedUpdateHook = new CallHook(this.address.AddonOnRequestedUpdate, this.OnRequestedUpdate);
}
- private delegate nint AddonSetupDelegate(AtkUnitBase* addon);
+ private delegate void AddonSetupDelegate(AtkUnitBase* addon, uint valueCount, AtkValue* values);
private delegate void AddonFinalizeDelegate(AtkUnitManager* unitManager, AtkUnitBase** atkUnitBase);
@@ -137,29 +137,37 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
}
}
- private nint OnAddonSetup(AtkUnitBase* addon)
+ private void OnAddonSetup(AtkUnitBase* addon, uint valueCount, AtkValue* values)
{
try
{
- this.InvokeListeners(AddonEvent.PreSetup, new AddonSetupArgs { Addon = (nint)addon });
+ this.InvokeListeners(AddonEvent.PreSetup, new AddonSetupArgs()
+ {
+ Addon = (nint)addon,
+ AtkValueCount = valueCount,
+ AtkValues = (nint)values,
+ });
}
catch (Exception e)
{
Log.Error(e, "Exception in OnAddonSetup pre-setup invoke.");
}
- var result = this.onAddonSetupHook.Original(addon);
+ addon->OnSetup(valueCount, values);
try
{
- this.InvokeListeners(AddonEvent.PostSetup, new AddonSetupArgs { Addon = (nint)addon });
+ this.InvokeListeners(AddonEvent.PostSetup, new AddonSetupArgs()
+ {
+ Addon = (nint)addon,
+ AtkValueCount = valueCount,
+ AtkValues = (nint)values,
+ });
}
catch (Exception e)
{
Log.Error(e, "Exception in OnAddonSetup post-setup invoke.");
}
-
- return result;
}
private void OnAddonFinalize(AtkUnitManager* unitManager, AtkUnitBase** atkUnitBase)
diff --git a/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs b/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs
index d68fee9ed..16fd54832 100644
--- a/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs
+++ b/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs
@@ -41,7 +41,7 @@ internal class AddonLifecycleAddressResolver : BaseAddressResolver
/// The signature scanner to facilitate setup.
protected override void Setup64Bit(SigScanner sig)
{
- this.AddonSetup = sig.ScanText("E8 ?? ?? ?? ?? 8B 83 ?? ?? ?? ?? C1 E8 14");
+ this.AddonSetup = sig.ScanText("FF 90 ?? ?? ?? ?? 48 8B 93 ?? ?? ?? ?? 80 8B");
this.AddonFinalize = sig.ScanText("E8 ?? ?? ?? ?? 48 8B 7C 24 ?? 41 8B C6");
this.AddonDraw = sig.ScanText("FF 90 ?? ?? ?? ?? 83 EB 01 79 C1");
this.AddonUpdate = sig.ScanText("FF 90 ?? ?? ?? ?? 40 88 AF");
From 3b5995e6abc855ad18ff95a6fd1495058976a5d8 Mon Sep 17 00:00:00 2001
From: MidoriKami <9083275+MidoriKami@users.noreply.github.com>
Date: Thu, 21 Sep 2023 20:46:44 -0700
Subject: [PATCH 14/18] Fix DTR Null Reference on first login
---
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 880bc0625..64c3e16b3 100644
--- a/Dalamud/Game/Gui/Dtr/DtrBar.cs
+++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs
@@ -172,7 +172,7 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
this.HandleAddedNodes();
var dtr = this.GetDtr();
- if (dtr == null) return;
+ if (dtr == null || dtr->RootNode == null || dtr->RootNode->ChildNode == null) return;
// The collision node on the DTR element is always the width of its content
if (dtr->UldManager.NodeList == null) return;
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 15/18] 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.
From fd3bd6dc5b9c9db01360016e0b553b50505cb176 Mon Sep 17 00:00:00 2001
From: MidoriKami <9083275+MidoriKami@users.noreply.github.com>
Date: Fri, 22 Sep 2023 12:17:54 -0700
Subject: [PATCH 16/18] Use abstract class instead of interface
---
.../AddonEventManager/AddonEventManager.cs | 2 +-
.../AddonLifecycle/AddonArgTypes/AddonArgs.cs | 46 +++++++++++++++++++
.../AddonArgTypes/AddonDrawArgs.cs | 9 ++--
.../AddonArgTypes/AddonFinalizeArgs.cs | 9 ++--
.../AddonArgTypes/AddonRefreshArgs.cs | 17 ++++---
.../AddonArgTypes/AddonRequestedUpdateArgs.cs | 9 ++--
.../AddonArgTypes/AddonSetupArgs.cs | 18 +++++---
.../AddonArgTypes/AddonUpdateArgs.cs | 9 ++--
Dalamud/Game/AddonLifecycle/AddonArgsType.cs | 2 +-
Dalamud/Game/AddonLifecycle/AddonLifecycle.cs | 7 ++-
Dalamud/Game/AddonLifecycle/IAddonArgs.cs | 25 ----------
Dalamud/Game/Gui/Dtr/DtrBar.cs | 4 +-
.../AgingSteps/AddonLifecycleAgingStep.cs | 12 ++---
Dalamud/Plugin/Services/IAddonLifecycle.cs | 2 +-
14 files changed, 95 insertions(+), 76 deletions(-)
create mode 100644 Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonArgs.cs
delete mode 100644 Dalamud/Game/AddonLifecycle/IAddonArgs.cs
diff --git a/Dalamud/Game/AddonEventManager/AddonEventManager.cs b/Dalamud/Game/AddonEventManager/AddonEventManager.cs
index 730a7a404..89554074a 100644
--- a/Dalamud/Game/AddonEventManager/AddonEventManager.cs
+++ b/Dalamud/Game/AddonEventManager/AddonEventManager.cs
@@ -160,7 +160,7 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType
///
/// Event type that triggered this call.
/// Addon that triggered this call.
- private void OnAddonFinalize(AddonEvent eventType, IAddonArgs addonInfo)
+ 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;
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonArgs.cs
new file mode 100644
index 000000000..949d3fde9
--- /dev/null
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonArgs.cs
@@ -0,0 +1,46 @@
+using Dalamud.Memory;
+using FFXIVClientStructs.FFXIV.Component.GUI;
+
+namespace Dalamud.Game.Addon;
+
+///
+/// Base class for AddonLifecycle AddonArgTypes.
+///
+public abstract unsafe class AddonArgs
+{
+ ///
+ /// Constant string representing the name of an addon that is invalid.
+ ///
+ public const string InvalidAddon = "NullAddon";
+
+ private string? addonName;
+
+ ///
+ /// Gets the name of the addon this args referrers to.
+ ///
+ public string AddonName => this.GetAddonName();
+
+ ///
+ /// Gets the pointer to the addons AtkUnitBase.
+ ///
+ public nint Addon { get; init; }
+
+ ///
+ /// Gets the type of these args.
+ ///
+ public abstract AddonArgsType Type { get; }
+
+ ///
+ /// Helper method for ensuring the name of the addon is valid.
+ ///
+ /// The name of the addon for this object. when invalid.
+ private string GetAddonName()
+ {
+ if (this.Addon == nint.Zero) return InvalidAddon;
+
+ var addonPointer = (AtkUnitBase*)this.Addon;
+ if (addonPointer->Name is null) return InvalidAddon;
+
+ return this.addonName ??= MemoryHelper.ReadString((nint)addonPointer->Name, 0x20);
+ }
+}
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonDrawArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonDrawArgs.cs
index 614a7ac2a..d93001d1c 100644
--- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonDrawArgs.cs
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonDrawArgs.cs
@@ -1,13 +1,10 @@
-namespace Dalamud.Game.Addon.AddonArgTypes;
+namespace Dalamud.Game.Addon;
///
/// Addon argument data for Finalize events.
///
-public class AddonDrawArgs : IAddonArgs
+public class AddonDrawArgs : AddonArgs
{
///
- public nint Addon { get; init; }
-
- ///
- public AddonArgsType Type => AddonArgsType.Draw;
+ public override AddonArgsType Type => AddonArgsType.Draw;
}
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonFinalizeArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonFinalizeArgs.cs
index aa31fb051..ed7aa1b3c 100644
--- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonFinalizeArgs.cs
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonFinalizeArgs.cs
@@ -1,13 +1,10 @@
-namespace Dalamud.Game.Addon.AddonArgTypes;
+namespace Dalamud.Game.Addon;
///
/// Addon argument data for Finalize events.
///
-public class AddonFinalizeArgs : IAddonArgs
+public class AddonFinalizeArgs : AddonArgs
{
///
- public nint Addon { get; init; }
-
- ///
- public AddonArgsType Type => AddonArgsType.Finalize;
+ public override AddonArgsType Type => AddonArgsType.Finalize;
}
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRefreshArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRefreshArgs.cs
index ab4f37c3c..60ccaf8ea 100644
--- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRefreshArgs.cs
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRefreshArgs.cs
@@ -1,15 +1,15 @@
-namespace Dalamud.Game.Addon.AddonArgTypes;
+using System;
+using FFXIVClientStructs.FFXIV.Component.GUI;
+
+namespace Dalamud.Game.Addon;
///
/// Addon argument data for Finalize events.
///
-public class AddonRefreshArgs : IAddonArgs
+public class AddonRefreshArgs : AddonArgs
{
///
- public nint Addon { get; init; }
-
- ///
- public AddonArgsType Type => AddonArgsType.Refresh;
+ public override AddonArgsType Type => AddonArgsType.Refresh;
///
/// Gets the number of AtkValues.
@@ -20,4 +20,9 @@ public class AddonRefreshArgs : IAddonArgs
/// Gets the address of the AtkValue array.
///
public nint AtkValues { get; init; }
+
+ ///
+ /// Gets the AtkValues in the form of a span.
+ ///
+ public unsafe Span AtkValueSpan => new(this.AtkValues.ToPointer(), (int)this.AtkValueCount);
}
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs
index dfd0dac5e..a31369aaf 100644
--- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs
@@ -1,15 +1,12 @@
-namespace Dalamud.Game.Addon.AddonArgTypes;
+namespace Dalamud.Game.Addon;
///
/// Addon argument data for Finalize events.
///
-public class AddonRequestedUpdateArgs : IAddonArgs
+public class AddonRequestedUpdateArgs : AddonArgs
{
///
- public nint Addon { get; init; }
-
- ///
- public AddonArgsType Type => AddonArgsType.RequestedUpdate;
+ public override AddonArgsType Type => AddonArgsType.RequestedUpdate;
///
/// Gets the NumberArrayData** for this event.
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs
index 4b467deb8..17c87967a 100644
--- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonSetupArgs.cs
@@ -1,15 +1,16 @@
-namespace Dalamud.Game.Addon.AddonArgTypes;
+using System;
+
+using FFXIVClientStructs.FFXIV.Component.GUI;
+
+namespace Dalamud.Game.Addon;
///
/// Addon argument data for Setup events.
///
-public class AddonSetupArgs : IAddonArgs
+public class AddonSetupArgs : AddonArgs
{
///
- public nint Addon { get; init; }
-
- ///
- public AddonArgsType Type => AddonArgsType.Setup;
+ public override AddonArgsType Type => AddonArgsType.Setup;
///
/// Gets the number of AtkValues.
@@ -20,4 +21,9 @@ public class AddonSetupArgs : IAddonArgs
/// Gets the address of the AtkValue array.
///
public nint AtkValues { get; init; }
+
+ ///
+ /// Gets the AtkValues in the form of a span.
+ ///
+ public unsafe Span AtkValueSpan => new(this.AtkValues.ToPointer(), (int)this.AtkValueCount);
}
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonUpdateArgs.cs b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonUpdateArgs.cs
index ede588001..993883d77 100644
--- a/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonUpdateArgs.cs
+++ b/Dalamud/Game/AddonLifecycle/AddonArgTypes/AddonUpdateArgs.cs
@@ -1,15 +1,12 @@
-namespace Dalamud.Game.Addon.AddonArgTypes;
+namespace Dalamud.Game.Addon;
///
/// Addon argument data for Finalize events.
///
-public class AddonUpdateArgs : IAddonArgs
+public class AddonUpdateArgs : AddonArgs
{
///
- public nint Addon { get; init; }
-
- ///
- public AddonArgsType Type => AddonArgsType.Update;
+ public override AddonArgsType Type => AddonArgsType.Update;
///
/// Gets the time since the last update.
diff --git a/Dalamud/Game/AddonLifecycle/AddonArgsType.cs b/Dalamud/Game/AddonLifecycle/AddonArgsType.cs
index ac325229d..8a07d445b 100644
--- a/Dalamud/Game/AddonLifecycle/AddonArgsType.cs
+++ b/Dalamud/Game/AddonLifecycle/AddonArgsType.cs
@@ -1,7 +1,7 @@
namespace Dalamud.Game.Addon;
///
-/// Enumeration for available AddonLifecycle arg data
+/// Enumeration for available AddonLifecycle arg data.
///
public enum AddonArgsType
{
diff --git a/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs b/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs
index 75b5b3753..17afbaeac 100644
--- a/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs
+++ b/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs
@@ -3,7 +3,6 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
-using Dalamud.Game.Addon.AddonArgTypes;
using Dalamud.Hooking;
using Dalamud.Hooking.Internal;
using Dalamud.IoC;
@@ -128,7 +127,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
this.onAddonRequestedUpdateHook.Enable();
}
- private void InvokeListeners(AddonEvent eventType, IAddonArgs args)
+ private void InvokeListeners(AddonEvent eventType, AddonArgs args)
{
// Match on string.empty for listeners that want events for all addons.
foreach (var listener in this.eventListeners.Where(listener => listener.EventType == eventType && (listener.AddonName == args.AddonName || listener.AddonName == string.Empty)))
@@ -141,7 +140,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
{
try
{
- this.InvokeListeners(AddonEvent.PreSetup, new AddonSetupArgs()
+ this.InvokeListeners(AddonEvent.PreSetup, new AddonSetupArgs
{
Addon = (nint)addon,
AtkValueCount = valueCount,
@@ -157,7 +156,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
try
{
- this.InvokeListeners(AddonEvent.PostSetup, new AddonSetupArgs()
+ this.InvokeListeners(AddonEvent.PostSetup, new AddonSetupArgs
{
Addon = (nint)addon,
AtkValueCount = valueCount,
diff --git a/Dalamud/Game/AddonLifecycle/IAddonArgs.cs b/Dalamud/Game/AddonLifecycle/IAddonArgs.cs
deleted file mode 100644
index ba77a2c6d..000000000
--- a/Dalamud/Game/AddonLifecycle/IAddonArgs.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using Dalamud.Memory;
-using FFXIVClientStructs.FFXIV.Component.GUI;
-
-namespace Dalamud.Game.Addon;
-
-///
-/// Interface representing the argument data for AddonLifecycle events.
-///
-public unsafe interface IAddonArgs
-{
- ///
- /// Gets the name of the addon this args referrers to.
- ///
- string AddonName => this.Addon == nint.Zero ? "NullAddon" : MemoryHelper.ReadString((nint)((AtkUnitBase*)this.Addon)->Name, 0x20);
-
- ///
- /// Gets the pointer to the addons AtkUnitBase.
- ///
- nint Addon { get; init; }
-
- ///
- /// Gets the type of these args.
- ///
- AddonArgsType Type { get; }
-}
diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs
index 6b74e47cd..880bc0625 100644
--- a/Dalamud/Game/Gui/Dtr/DtrBar.cs
+++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs
@@ -255,7 +255,7 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
}
}
- private void OnDtrPostDraw(AddonEvent eventType, IAddonArgs addonInfo)
+ private void OnDtrPostDraw(AddonEvent eventType, AddonArgs addonInfo)
{
var addon = (AtkUnitBase*)addonInfo.Addon;
@@ -300,7 +300,7 @@ public sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
}
}
- private void OnAddonRequestedUpdateDetour(AddonEvent eventType, IAddonArgs addonInfo)
+ private void OnAddonRequestedUpdateDetour(AddonEvent eventType, AddonArgs addonInfo)
{
var addon = (AtkUnitBase*)addonInfo.Addon;
diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs
index 0821e62de..a9948430f 100644
--- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs
+++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/AddonLifecycleAgingStep.cs
@@ -100,32 +100,32 @@ internal class AddonLifecycleAgingStep : IAgingStep
}
}
- private void PostSetup(AddonEvent eventType, IAddonArgs addonInfo)
+ private void PostSetup(AddonEvent eventType, AddonArgs addonInfo)
{
if (this.currentStep is TestStep.CharacterSetup) this.currentStep++;
}
- private void PostUpdate(AddonEvent eventType, IAddonArgs addonInfo)
+ private void PostUpdate(AddonEvent eventType, AddonArgs addonInfo)
{
if (this.currentStep is TestStep.CharacterUpdate) this.currentStep++;
}
- private void PostDraw(AddonEvent eventType, IAddonArgs addonInfo)
+ private void PostDraw(AddonEvent eventType, AddonArgs addonInfo)
{
if (this.currentStep is TestStep.CharacterDraw) this.currentStep++;
}
- private void PostRefresh(AddonEvent eventType, IAddonArgs addonInfo)
+ private void PostRefresh(AddonEvent eventType, AddonArgs addonInfo)
{
if (this.currentStep is TestStep.CharacterRefresh) this.currentStep++;
}
- private void PostRequestedUpdate(AddonEvent eventType, IAddonArgs addonInfo)
+ private void PostRequestedUpdate(AddonEvent eventType, AddonArgs addonInfo)
{
if (this.currentStep is TestStep.CharacterRequestedUpdate) this.currentStep++;
}
- private void PreFinalize(AddonEvent eventType, IAddonArgs addonInfo)
+ private void PreFinalize(AddonEvent eventType, AddonArgs addonInfo)
{
if (this.currentStep is TestStep.CharacterFinalize) this.currentStep++;
}
diff --git a/Dalamud/Plugin/Services/IAddonLifecycle.cs b/Dalamud/Plugin/Services/IAddonLifecycle.cs
index 5290395ab..e89c57931 100644
--- a/Dalamud/Plugin/Services/IAddonLifecycle.cs
+++ b/Dalamud/Plugin/Services/IAddonLifecycle.cs
@@ -15,7 +15,7 @@ public interface IAddonLifecycle
///
/// The event type that triggered the message.
/// Information about what addon triggered the message.
- public delegate void AddonEventDelegate(AddonEvent eventType, IAddonArgs args);
+ public delegate void AddonEventDelegate(AddonEvent eventType, AddonArgs args);
///
/// Register a listener that will trigger on the specified event and any of the specified addons.
From 74375ec414290ce98d730ec9f94100af859d7c96 Mon Sep 17 00:00:00 2001
From: MidoriKami <9083275+MidoriKami@users.noreply.github.com>
Date: Fri, 22 Sep 2023 20:19:27 -0700
Subject: [PATCH 17/18] Add more ColorHelpers
---
Dalamud/Interface/ColorHelpers.cs | 31 ++++++++++++++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/Dalamud/Interface/ColorHelpers.cs b/Dalamud/Interface/ColorHelpers.cs
index 71f959292..0239ad754 100644
--- a/Dalamud/Interface/ColorHelpers.cs
+++ b/Dalamud/Interface/ColorHelpers.cs
@@ -1,4 +1,5 @@
using System;
+using System.Drawing;
using System.Numerics;
namespace Dalamud.Interface;
@@ -256,6 +257,34 @@ public static class ColorHelpers
/// The faded color.
public static uint Fade(uint color, float amount)
=> RgbaVector4ToUint(Fade(RgbaUintToVector4(color), amount));
-
+
+ ///
+ /// Convert a KnownColor to a RGBA vector with values between 0.0f and 1.0f
+ ///
+ /// Known Color to convert.
+ /// RGBA Vector with values between 0.0f and 1.0f.
+ public static Vector4 Vector(this KnownColor knownColor)
+ {
+ var rgbColor = Color.FromKnownColor(knownColor);
+ return new Vector4(rgbColor.R, rgbColor.G, rgbColor.B, rgbColor.A) / 255.0f;
+ }
+
+ ///
+ /// Normalizes a Vector4 with RGBA 255 color values to values between 0.0f and 1.0f
+ /// If values are out of RGBA 255 range, the original value is returned.
+ ///
+ /// The color vector to convert.
+ /// A vector with values between 0.0f and 1.0f.
+ public static Vector4 NormalizeToUnitRange(this Vector4 color) => color switch
+ {
+ // If any components are out of range, return original value.
+ { W: > 255.0f or < 0.0f } or { X: > 255.0f or < 0.0f } or { Y: > 255.0f or < 0.0f } or { Z: > 255.0f or < 0.0f } => color,
+
+ // If all components are already unit range, return original value.
+ { W: >= 0.0f and <= 1.0f, X: >= 0.0f and <= 1.0f, Y: >= 0.0f and <= 1.0f, Z: >= 0.0f and <= 1.0f } => color,
+
+ _ => color / 255.0f,
+ };
+
public record struct HsvaColor(float H, float S, float V, float A);
}
From c6c28c6e3f8eff1652a2027673907235105ba37d Mon Sep 17 00:00:00 2001
From: MidoriKami <9083275+MidoriKami@users.noreply.github.com>
Date: Fri, 22 Sep 2023 21:12:44 -0700
Subject: [PATCH 18/18] Change default name so auto generate stops complaining
about improper casing.
---
Dalamud/Plugin/Services/IAddonLifecycle.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Dalamud/Plugin/Services/IAddonLifecycle.cs b/Dalamud/Plugin/Services/IAddonLifecycle.cs
index e89c57931..2bc41a366 100644
--- a/Dalamud/Plugin/Services/IAddonLifecycle.cs
+++ b/Dalamud/Plugin/Services/IAddonLifecycle.cs
@@ -13,9 +13,9 @@ public interface IAddonLifecycle
///
/// Delegate for receiving addon lifecycle event messages.
///
- /// The event type that triggered the message.
+ /// The event type that triggered the message.
/// Information about what addon triggered the message.
- public delegate void AddonEventDelegate(AddonEvent eventType, AddonArgs args);
+ public delegate void AddonEventDelegate(AddonEvent type, AddonArgs args);
///
/// Register a listener that will trigger on the specified event and any of the specified addons.