diff --git a/Dalamud/Game/AddonEventManager/AddonEventManager.cs b/Dalamud/Game/AddonEventManager/AddonEventManager.cs index b88c64253..69dc27f3b 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventManager.cs +++ b/Dalamud/Game/AddonEventManager/AddonEventManager.cs @@ -18,29 +18,12 @@ namespace Dalamud.Game.AddonEventManager; [ServiceManager.EarlyLoadedService] internal unsafe class AddonEventManager : IDisposable, IServiceType { - // The starting value for param key ranges. - // Reserve the first 0x10_000 for dalamud internal use. - private const uint ParamKeyStart = 0x0010_0000; - - // The range each plugin is allowed to use. - // 1,048,576 per plugin should be reasonable. - private const uint ParamKeyPluginRange = 0x10_0000; - - // The maximum range allowed to be given to a plugin. - // 1,048,576 maximum plugins should be reasonable. - private const uint ParamKeyMax = 0xFFF0_0000; - private static readonly ModuleLog Log = new("AddonEventManager"); private readonly AddonEventManagerAddressResolver address; private readonly Hook onUpdateCursor; - private readonly Dictionary eventHandlers; - private readonly AddonEventListener eventListener; - private AddonCursorType currentCursor; - private bool cursorSet; - - private uint currentPluginParamStart = ParamKeyStart; + private AddonCursorType? cursorOverride; [ServiceManager.ServiceConstructor] private AddonEventManager(SigScanner sigScanner) @@ -48,10 +31,7 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType this.address = new AddonEventManagerAddressResolver(); this.address.Setup(sigScanner); - this.eventListener = new AddonEventListener(this.OnCustomEvent); - - this.eventHandlers = new Dictionary(); - this.currentCursor = AddonCursorType.Arrow; + this.cursorOverride = null; this.onUpdateCursor = Hook.FromAddress(this.address.UpdateCursor, this.UpdateCursorDetour); } @@ -61,116 +41,38 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType /// public void Dispose() { - this.eventListener.Dispose(); this.onUpdateCursor.Dispose(); } - /// - /// Get the start value for a new plugin register. - /// - /// A unique starting range for event handlers. - /// Throws when attempting to register too many event handlers. - public uint GetPluginParamStart() - { - if (this.currentPluginParamStart >= ParamKeyMax) - { - throw new Exception("Maximum number of event handlers reached."); - } - - var result = this.currentPluginParamStart; - - this.currentPluginParamStart += ParamKeyPluginRange; - return result; - } - - /// - /// Attaches an event to a node. - /// - /// Addon that contains the node. - /// The node that will trigger the event. - /// The event type to trigger on. - /// The unique id for this event. - /// The event handler to be called. - public void AddEvent(AtkUnitBase* addon, AtkResNode* node, AtkEventType eventType, uint param, IAddonEventManager.AddonEventHandler handler) - { - this.eventListener.RegisterEvent(addon, node, eventType, param); - this.eventHandlers.TryAdd(param, handler); - } - - /// - /// Detaches an event from a node. - /// - /// The node to remove the event from. - /// The event type to remove. - /// The unique id of the event to remove. - public void RemoveEvent(AtkResNode* node, AtkEventType eventType, uint param) - { - this.eventListener.UnregisterEvent(node, eventType, param); - this.eventHandlers.Remove(param); - } - - /// - /// Removes a delegate from the managed event handlers. - /// - /// Unique id of the delegate to remove. - public void RemoveHandler(uint param) - { - this.eventHandlers.Remove(param); - } - /// /// Sets the game cursor. /// /// Cursor type to set. - public void SetCursor(AddonCursorType cursor) - { - this.currentCursor = cursor; - this.cursorSet = true; - } + public void SetCursor(AddonCursorType cursor) => this.cursorOverride = cursor; /// /// Resets and un-forces custom cursor. /// - public void ResetCursor() - { - this.currentCursor = AddonCursorType.Arrow; - this.cursorSet = false; - } - + public void ResetCursor() => this.cursorOverride = null; + [ServiceManager.CallWhenServicesReady] private void ContinueConstruction() { this.onUpdateCursor.Enable(); } - private void OnCustomEvent(AtkEventListener* self, AtkEventType eventType, uint eventParam, AtkEvent* eventData, nint 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 OnCustomEvent custom event invoke."); - } - } - } - private nint UpdateCursorDetour(RaptureAtkModule* module) { try { var atkStage = AtkStage.GetSingleton(); - if (this.cursorSet && atkStage is not null) + if (this.cursorOverride is not null && atkStage is not null) { var cursor = (AddonCursorType)atkStage->AtkCursor.Type; - if (cursor != this.currentCursor) + if (cursor != this.cursorOverride) { - AtkStage.GetSingleton()->AtkCursor.SetCursorType((AtkCursor.CursorType)this.currentCursor, 1); + AtkStage.GetSingleton()->AtkCursor.SetCursorType((AtkCursor.CursorType)this.cursorOverride, 1); } return nint.Zero; @@ -200,9 +102,10 @@ internal unsafe class AddonEventManagerPluginScoped : IDisposable, IServiceType, [ServiceManager.ServiceDependency] private readonly AddonEventManager baseEventManager = Service.Get(); - - private readonly uint paramKeyStartRange; - private readonly List activeParamKeys; + + private readonly AddonEventListener eventListener; + private readonly Dictionary eventHandlers; + private bool isForcingCursor; /// @@ -210,62 +113,51 @@ internal unsafe class AddonEventManagerPluginScoped : IDisposable, IServiceType, /// public AddonEventManagerPluginScoped() { - this.paramKeyStartRange = this.baseEventManager.GetPluginParamStart(); - this.activeParamKeys = new List(); + this.eventHandlers = new Dictionary(); + this.eventListener = new AddonEventListener(this.PluginAddonEventHandler); } - + /// public void Dispose() { - foreach (var activeKey in this.activeParamKeys) - { - this.baseEventManager.RemoveHandler(activeKey); - } - // if multiple plugins force cursors and dispose without un-forcing them then all forces will be cleared. if (this.isForcingCursor) { this.baseEventManager.ResetCursor(); } + + this.eventListener.Dispose(); + this.eventHandlers.Clear(); } /// public void AddEvent(uint eventId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler) { - if (eventId < 0x10_000) + if (!this.eventHandlers.ContainsKey(eventId)) { var type = (AtkEventType)eventType; var node = (AtkResNode*)atkResNode; var addon = (AtkUnitBase*)atkUnitBase; - var uniqueId = eventId + this.paramKeyStartRange; - if (!this.activeParamKeys.Contains(uniqueId)) - { - this.baseEventManager.AddEvent(addon, node, type, uniqueId, eventHandler); - this.activeParamKeys.Add(uniqueId); - } - else - { - Log.Warning($"Attempted to register already registered eventId: {eventId}"); - } + this.eventHandlers.Add(eventId, eventHandler); + this.eventListener.RegisterEvent(addon, node, type, eventId); } else { - Log.Warning($"Attempted to register eventId out of range: {eventId}\nMaximum value: {0x10_000}"); + Log.Warning($"Attempted to register already registered eventId: {eventId}"); } } /// public void RemoveEvent(uint eventId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType) { - var type = (AtkEventType)eventType; - var node = (AtkResNode*)atkResNode; - var uniqueId = eventId + this.paramKeyStartRange; - - if (this.activeParamKeys.Contains(uniqueId)) + if (this.eventHandlers.ContainsKey(eventId)) { - this.baseEventManager.RemoveEvent(node, type, uniqueId); - this.activeParamKeys.Remove(uniqueId); + var type = (AtkEventType)eventType; + var node = (AtkResNode*)atkResNode; + + this.eventListener.UnregisterEvent(node, type, eventId); + this.eventHandlers.Remove(eventId); } else { @@ -288,4 +180,20 @@ internal unsafe class AddonEventManagerPluginScoped : IDisposable, IServiceType, 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."); + } + } + } }