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.