mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Refactor IAddonEventManager (#2299)
This commit is contained in:
parent
b1986bd3d1
commit
13306e24ba
5 changed files with 199 additions and 42 deletions
46
Dalamud/Game/Addon/Events/AddonEventData.cs
Normal file
46
Dalamud/Game/Addon/Events/AddonEventData.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
namespace Dalamud.Game.Addon.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Object representing data that is relevant in handling native events.
|
||||
/// </summary>
|
||||
public class AddonEventData
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the AtkEventType for this event.
|
||||
/// </summary>
|
||||
public AddonEventType AtkEventType { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the param field for this event.
|
||||
/// </summary>
|
||||
public uint Param { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the AtkEvent object for this event.
|
||||
/// </summary>
|
||||
/// <remarks>Note: This is not a pointer to the AtkEventData object.<br/><br/></remarks>
|
||||
/// <remarks>Warning: AtkEvent->Node has been modified to be the AtkUnitBase*, and AtkEvent->Target has been modified to be the AtkResNode* that triggered this event.</remarks>
|
||||
public nint AtkEventPointer { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the AtkEventData object for this event.
|
||||
/// </summary>
|
||||
/// <remarks>This field will contain relevant data such as left vs right click, scroll up vs scroll down.</remarks>
|
||||
public nint AtkEventDataPointer { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the AtkUnitBase that is handling this event.
|
||||
/// </summary>
|
||||
public nint AddonPointer { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the AtkResNode that triggered this event.
|
||||
/// </summary>
|
||||
public nint NodeTargetPointer { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a pointer to the AtkEventListener responsible for handling this event.
|
||||
/// Note: As the event listener is dalamud allocated, there's no reason to expose this field.
|
||||
/// </summary>
|
||||
internal nint AtkEventListener { get; set; }
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using Dalamud.Memory;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace Dalamud.Game.Addon.Events;
|
||||
|
|
@ -14,9 +15,9 @@ internal unsafe class AddonEventEntry
|
|||
/// Name of an invalid addon.
|
||||
/// </summary>
|
||||
public const string InvalidAddonName = "NullAddon";
|
||||
|
||||
|
||||
private string? addonName;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pointer to the addons AtkUnitBase.
|
||||
/// </summary>
|
||||
|
|
@ -35,18 +36,25 @@ internal unsafe class AddonEventEntry
|
|||
/// <summary>
|
||||
/// Gets the handler that gets called when this event is triggered.
|
||||
/// </summary>
|
||||
public required IAddonEventManager.AddonEventHandler Handler { get; init; }
|
||||
|
||||
[Obsolete("Use AddonEventDelegate Delegate instead")]
|
||||
public IAddonEventManager.AddonEventHandler Handler { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the delegate that gets called when this event is triggered.
|
||||
/// </summary>
|
||||
[Api13ToDo("Make this field required")]
|
||||
public IAddonEventManager.AddonEventDelegate Delegate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique id for this event.
|
||||
/// </summary>
|
||||
public required uint ParamKey { get; init; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the event type for this event.
|
||||
/// </summary>
|
||||
public required AddonEventType EventType { get; init; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the event handle for this event.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -24,21 +24,21 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
|||
/// PluginName for Dalamud Internal use.
|
||||
/// </summary>
|
||||
public static readonly Guid DalamudInternalKey = Guid.NewGuid();
|
||||
|
||||
|
||||
private static readonly ModuleLog Log = new("AddonEventManager");
|
||||
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly AddonLifecycle addonLifecycle = Service<AddonLifecycle>.Get();
|
||||
|
||||
private readonly AddonLifecycleEventListener finalizeEventListener;
|
||||
|
||||
|
||||
private readonly AddonEventManagerAddressResolver address;
|
||||
private readonly Hook<UpdateCursorDelegate> onUpdateCursor;
|
||||
|
||||
private readonly ConcurrentDictionary<Guid, PluginEventController> pluginEventControllers;
|
||||
|
||||
|
||||
private AddonCursorType? cursorOverride;
|
||||
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private AddonEventManager(TargetSigScanner sigScanner)
|
||||
{
|
||||
|
|
@ -47,7 +47,7 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
|||
|
||||
this.pluginEventControllers = new ConcurrentDictionary<Guid, PluginEventController>();
|
||||
this.pluginEventControllers.TryAdd(DalamudInternalKey, new PluginEventController());
|
||||
|
||||
|
||||
this.cursorOverride = null;
|
||||
|
||||
this.onUpdateCursor = Hook<UpdateCursorDelegate>.FromAddress(this.address.UpdateCursor, this.UpdateCursorDetour);
|
||||
|
|
@ -69,7 +69,7 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
|||
{
|
||||
pluginEventController.Dispose();
|
||||
}
|
||||
|
||||
|
||||
this.addonLifecycle.UnregisterListener(this.finalizeEventListener);
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +92,30 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
|||
{
|
||||
Log.Verbose($"Unable to locate controller for {pluginId}. No event was added.");
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers an event handler for the specified addon, node, and type.
|
||||
/// </summary>
|
||||
/// <param name="pluginId">Unique ID for this plugin.</param>
|
||||
/// <param name="atkUnitBase">The parent addon for this event.</param>
|
||||
/// <param name="atkResNode">The node that will trigger this event.</param>
|
||||
/// <param name="eventType">The event type for this event.</param>
|
||||
/// <param name="eventDelegate">The delegate to call when event is triggered.</param>
|
||||
/// <returns>IAddonEventHandle used to remove the event.</returns>
|
||||
internal IAddonEventHandle? AddEvent(Guid pluginId, nint atkUnitBase, nint atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventDelegate eventDelegate)
|
||||
{
|
||||
if (this.pluginEventControllers.TryGetValue(pluginId, out var controller))
|
||||
{
|
||||
return controller.AddEvent(atkUnitBase, atkResNode, eventType, eventDelegate);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Verbose($"Unable to locate controller for {pluginId}. No event was added.");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +135,7 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
|||
Log.Verbose($"Unable to locate controller for {pluginId}. No event was removed.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Force the game cursor to be the specified cursor.
|
||||
/// </summary>
|
||||
|
|
@ -167,21 +190,21 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
|||
pluginList.Value.RemoveForAddon(addonInfo.AddonName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private nint UpdateCursorDetour(RaptureAtkModule* module)
|
||||
{
|
||||
try
|
||||
{
|
||||
var atkStage = AtkStage.Instance();
|
||||
|
||||
|
||||
if (this.cursorOverride is not null && atkStage is not null)
|
||||
{
|
||||
var cursor = (AddonCursorType)atkStage->AtkCursor.Type;
|
||||
if (cursor != this.cursorOverride)
|
||||
if (cursor != this.cursorOverride)
|
||||
{
|
||||
AtkStage.Instance()->AtkCursor.SetCursorType((AtkCursor.CursorType)this.cursorOverride, 1);
|
||||
}
|
||||
|
||||
|
||||
return nint.Zero;
|
||||
}
|
||||
}
|
||||
|
|
@ -218,7 +241,7 @@ internal class AddonEventManagerPluginScoped : IInternalDisposableService, IAddo
|
|||
public AddonEventManagerPluginScoped(LocalPlugin plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
|
||||
|
||||
this.eventManagerService.AddPluginEventController(plugin.EffectiveWorkingPluginId);
|
||||
}
|
||||
|
||||
|
|
@ -236,28 +259,32 @@ internal class AddonEventManagerPluginScoped : IInternalDisposableService, IAddo
|
|||
this.eventManagerService.RemovePluginEventController(this.plugin.EffectiveWorkingPluginId);
|
||||
}).Wait();
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IAddonEventHandle? AddEvent(IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
|
||||
public IAddonEventHandle? AddEvent(IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
|
||||
=> this.eventManagerService.AddEvent(this.plugin.EffectiveWorkingPluginId, atkUnitBase, atkResNode, eventType, eventHandler);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IAddonEventHandle? AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventDelegate eventDelegate)
|
||||
=> this.eventManagerService.AddEvent(this.plugin.EffectiveWorkingPluginId, atkUnitBase, atkResNode, eventType, eventDelegate);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RemoveEvent(IAddonEventHandle eventHandle)
|
||||
=> this.eventManagerService.RemoveEvent(this.plugin.EffectiveWorkingPluginId, eventHandle);
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void SetCursor(AddonCursorType cursor)
|
||||
{
|
||||
this.isForcingCursor = true;
|
||||
|
||||
|
||||
this.eventManagerService.SetCursor(cursor);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ResetCursor()
|
||||
{
|
||||
this.isForcingCursor = false;
|
||||
|
||||
|
||||
this.eventManagerService.ResetCursor();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using Dalamud.Game.Gui;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
|
|
@ -25,7 +26,7 @@ internal unsafe class PluginEventController : IDisposable
|
|||
}
|
||||
|
||||
private AddonEventListener EventListener { get; init; }
|
||||
|
||||
|
||||
private List<AddonEventEntry> Events { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -36,6 +37,7 @@ internal unsafe class PluginEventController : IDisposable
|
|||
/// <param name="atkEventType">The Event Type.</param>
|
||||
/// <param name="handler">The delegate to call when invoking this event.</param>
|
||||
/// <returns>IAddonEventHandle used to remove the event.</returns>
|
||||
[Obsolete("Use AddEvent that uses AddonEventDelegate instead of AddonEventHandler")]
|
||||
public IAddonEventHandle AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType atkEventType, IAddonEventManager.AddonEventHandler handler)
|
||||
{
|
||||
var node = (AtkResNode*)atkResNode;
|
||||
|
|
@ -43,7 +45,7 @@ internal unsafe class PluginEventController : IDisposable
|
|||
var eventType = (AtkEventType)atkEventType;
|
||||
var eventId = this.GetNextParamKey();
|
||||
var eventGuid = Guid.NewGuid();
|
||||
|
||||
|
||||
var eventHandle = new AddonEventHandle
|
||||
{
|
||||
AddonName = addon->NameString,
|
||||
|
|
@ -51,11 +53,54 @@ internal unsafe class PluginEventController : IDisposable
|
|||
EventType = atkEventType,
|
||||
EventGuid = eventGuid,
|
||||
};
|
||||
|
||||
|
||||
var eventEntry = new AddonEventEntry
|
||||
{
|
||||
Addon = atkUnitBase,
|
||||
Handler = handler,
|
||||
Delegate = null,
|
||||
Node = atkResNode,
|
||||
EventType = atkEventType,
|
||||
ParamKey = eventId,
|
||||
Handle = eventHandle,
|
||||
};
|
||||
|
||||
Log.Verbose($"Adding Event. {eventEntry.LogString}");
|
||||
this.EventListener.RegisterEvent(addon, node, eventType, eventId);
|
||||
this.Events.Add(eventEntry);
|
||||
|
||||
return eventHandle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a tracked event.
|
||||
/// </summary>
|
||||
/// <param name="atkUnitBase">The Parent addon for the event.</param>
|
||||
/// <param name="atkResNode">The Node for the event.</param>
|
||||
/// <param name="atkEventType">The Event Type.</param>
|
||||
/// <param name="eventDelegate">The delegate to call when invoking this event.</param>
|
||||
/// <returns>IAddonEventHandle used to remove the event.</returns>
|
||||
public IAddonEventHandle AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType atkEventType, IAddonEventManager.AddonEventDelegate eventDelegate)
|
||||
{
|
||||
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 = addon->NameString,
|
||||
ParamKey = eventId,
|
||||
EventType = atkEventType,
|
||||
EventGuid = eventGuid,
|
||||
};
|
||||
|
||||
var eventEntry = new AddonEventEntry
|
||||
{
|
||||
Addon = atkUnitBase,
|
||||
Delegate = eventDelegate,
|
||||
Handler = null,
|
||||
Node = atkResNode,
|
||||
EventType = atkEventType,
|
||||
ParamKey = eventId,
|
||||
|
|
@ -91,14 +136,14 @@ internal unsafe class PluginEventController : IDisposable
|
|||
if (this.Events.Where(entry => entry.AddonName == addonName).ToList() is { Count: not 0 } events)
|
||||
{
|
||||
Log.Verbose($"Addon: {addonName} is Finalizing, removing {events.Count} events.");
|
||||
|
||||
|
||||
foreach (var registeredEvent in events)
|
||||
{
|
||||
this.RemoveEvent(registeredEvent.Handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
|
|
@ -106,7 +151,7 @@ internal unsafe class PluginEventController : IDisposable
|
|||
{
|
||||
this.RemoveEvent(registeredEvent.Handle);
|
||||
}
|
||||
|
||||
|
||||
this.EventListener.Dispose();
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +164,7 @@ internal unsafe class PluginEventController : IDisposable
|
|||
|
||||
throw new OverflowException($"uint.MaxValue number of ParamKeys used for this event controller.");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove a tracked event from native UI.
|
||||
/// This method performs several safety checks to only remove events from a still active addon.
|
||||
|
|
@ -153,7 +198,7 @@ internal unsafe class PluginEventController : IDisposable
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If we didn't find the node, we can't remove the event.
|
||||
if (!nodeFound) return;
|
||||
|
||||
|
|
@ -167,33 +212,45 @@ internal unsafe class PluginEventController : IDisposable
|
|||
var paramKeyMatches = currentEvent->Param == eventEntry.ParamKey;
|
||||
var eventListenerAddressMatches = (nint)currentEvent->Listener == this.EventListener.Address;
|
||||
var eventTypeMatches = currentEvent->State.EventType == 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);
|
||||
}
|
||||
|
||||
|
||||
[Api13ToDo("Remove invoke from eventInfo.Handler, and remove nullability from eventInfo.Delegate?.Invoke")]
|
||||
private void PluginEventListHandler(AtkEventListener* self, AtkEventType eventType, uint eventParam, AtkEvent* eventPtr, AtkEventData* eventDataPtr)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (eventPtr 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)eventPtr->Node, (nint)eventPtr->Target);
|
||||
eventInfo.Handler?.Invoke((AddonEventType)eventType, (nint)eventPtr->Node, (nint)eventPtr->Target);
|
||||
|
||||
eventInfo.Delegate?.Invoke((AddonEventType)eventType, new AddonEventData
|
||||
{
|
||||
AddonPointer = (nint)eventPtr->Node,
|
||||
NodeTargetPointer = (nint)eventPtr->Target,
|
||||
AtkEventDataPointer = (nint)eventDataPtr,
|
||||
AtkEventListener = (nint)self,
|
||||
AtkEventType = (AddonEventType)eventType,
|
||||
Param = eventParam,
|
||||
AtkEventPointer = (nint)eventPtr,
|
||||
});
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,8 +13,16 @@ public interface IAddonEventManager
|
|||
/// <param name="atkEventType">Event type for this event handler.</param>
|
||||
/// <param name="atkUnitBase">The parent addon for this event handler.</param>
|
||||
/// <param name="atkResNode">The specific node that will trigger this event handler.</param>
|
||||
[Obsolete("Use AddonEventDelegate instead")]
|
||||
public delegate void AddonEventHandler(AddonEventType atkEventType, nint atkUnitBase, nint atkResNode);
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to be called when an event is received.
|
||||
/// </summary>
|
||||
/// <param name="atkEventType">The AtkEventType that triggered this event.</param>
|
||||
/// <param name="data">The event data object for use in handling this event.</param>
|
||||
public delegate void AddonEventDelegate(AddonEventType atkEventType, AddonEventData data);
|
||||
|
||||
/// <summary>
|
||||
/// Registers an event handler for the specified addon, node, and type.
|
||||
/// </summary>
|
||||
|
|
@ -23,8 +31,19 @@ public interface IAddonEventManager
|
|||
/// <param name="eventType">The event type for this event.</param>
|
||||
/// <param name="eventHandler">The handler to call when event is triggered.</param>
|
||||
/// <returns>IAddonEventHandle used to remove the event. Null if no event was added.</returns>
|
||||
[Obsolete("Use AddEvent with AddonEventDelegate instead of AddonEventHandler")]
|
||||
IAddonEventHandle? AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType eventType, AddonEventHandler eventHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Registers an event handler for the specified addon, node, and type.
|
||||
/// </summary>
|
||||
/// <param name="atkUnitBase">The parent addon for this event.</param>
|
||||
/// <param name="atkResNode">The node that will trigger this event.</param>
|
||||
/// <param name="eventType">The event type for this event.</param>
|
||||
/// <param name="eventDelegate">The handler to call when event is triggered.</param>
|
||||
/// <returns>IAddonEventHandle used to remove the event. Null if no event was added.</returns>
|
||||
IAddonEventHandle? AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType eventType, AddonEventDelegate eventDelegate);
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters an event handler with the specified event id and event type.
|
||||
/// </summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue