diff --git a/Dalamud/Game/AddonEventManager/AddonEventManager.cs b/Dalamud/Game/AddonEventManager/AddonEventManager.cs index 69dc27f3b..4718d4800 100644 --- a/Dalamud/Game/AddonEventManager/AddonEventManager.cs +++ b/Dalamud/Game/AddonEventManager/AddonEventManager.cs @@ -16,13 +16,16 @@ namespace Dalamud.Game.AddonEventManager; /// [InterfaceVersion("1.0")] [ServiceManager.EarlyLoadedService] -internal unsafe class AddonEventManager : IDisposable, IServiceType +internal unsafe class AddonEventManager : IDisposable, IServiceType, IAddonEventManager { private static readonly ModuleLog Log = new("AddonEventManager"); private readonly AddonEventManagerAddressResolver address; private readonly Hook onUpdateCursor; + private readonly AddonEventListener eventListener; + private readonly Dictionary eventHandlers; + private AddonCursorType? cursorOverride; [ServiceManager.ServiceConstructor] @@ -31,28 +34,63 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType this.address = new AddonEventManagerAddressResolver(); this.address.Setup(sigScanner); + this.eventHandlers = new Dictionary(); + this.eventListener = new AddonEventListener(this.DalamudAddonEventHandler); + this.cursorOverride = null; this.onUpdateCursor = Hook.FromAddress(this.address.UpdateCursor, this.UpdateCursorDetour); } 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(); } - /// - /// Sets the game cursor. - /// - /// Cursor type to set. + /// public void SetCursor(AddonCursorType cursor) => this.cursorOverride = cursor; - /// - /// Resets and un-forces custom cursor. - /// + /// public void ResetCursor() => this.cursorOverride = null; [ServiceManager.CallWhenServicesReady] @@ -85,6 +123,22 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType 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."); + } + } + } } /// @@ -149,7 +203,7 @@ internal unsafe class AddonEventManagerPluginScoped : IDisposable, IServiceType, } /// - public void RemoveEvent(uint eventId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType) + public void RemoveEvent(uint eventId, IntPtr atkResNode, AddonEventType eventType) { if (this.eventHandlers.ContainsKey(eventId)) {