mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-02-20 14:57:45 +01:00
[AddonEventManager] Give each plugin their own event listener.
This commit is contained in:
parent
22a381b874
commit
26e138c783
1 changed files with 44 additions and 136 deletions
|
|
@ -18,29 +18,12 @@ namespace Dalamud.Game.AddonEventManager;
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal unsafe class AddonEventManager : IDisposable, IServiceType
|
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 static readonly ModuleLog Log = new("AddonEventManager");
|
||||||
|
|
||||||
private readonly AddonEventManagerAddressResolver address;
|
private readonly AddonEventManagerAddressResolver address;
|
||||||
private readonly Hook<UpdateCursorDelegate> onUpdateCursor;
|
private readonly Hook<UpdateCursorDelegate> onUpdateCursor;
|
||||||
private readonly Dictionary<uint, IAddonEventManager.AddonEventHandler> eventHandlers;
|
|
||||||
private readonly AddonEventListener eventListener;
|
|
||||||
|
|
||||||
private AddonCursorType currentCursor;
|
private AddonCursorType? cursorOverride;
|
||||||
private bool cursorSet;
|
|
||||||
|
|
||||||
private uint currentPluginParamStart = ParamKeyStart;
|
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private AddonEventManager(SigScanner sigScanner)
|
private AddonEventManager(SigScanner sigScanner)
|
||||||
|
|
@ -48,10 +31,7 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType
|
||||||
this.address = new AddonEventManagerAddressResolver();
|
this.address = new AddonEventManagerAddressResolver();
|
||||||
this.address.Setup(sigScanner);
|
this.address.Setup(sigScanner);
|
||||||
|
|
||||||
this.eventListener = new AddonEventListener(this.OnCustomEvent);
|
this.cursorOverride = null;
|
||||||
|
|
||||||
this.eventHandlers = new Dictionary<uint, IAddonEventManager.AddonEventHandler>();
|
|
||||||
this.currentCursor = AddonCursorType.Arrow;
|
|
||||||
|
|
||||||
this.onUpdateCursor = Hook<UpdateCursorDelegate>.FromAddress(this.address.UpdateCursor, this.UpdateCursorDetour);
|
this.onUpdateCursor = Hook<UpdateCursorDelegate>.FromAddress(this.address.UpdateCursor, this.UpdateCursorDetour);
|
||||||
}
|
}
|
||||||
|
|
@ -61,116 +41,38 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
this.eventListener.Dispose();
|
|
||||||
this.onUpdateCursor.Dispose();
|
this.onUpdateCursor.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the start value for a new plugin register.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A unique starting range for event handlers.</returns>
|
|
||||||
/// <exception cref="Exception">Throws when attempting to register too many event handlers.</exception>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attaches an event to a node.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="addon">Addon that contains the node.</param>
|
|
||||||
/// <param name="node">The node that will trigger the event.</param>
|
|
||||||
/// <param name="eventType">The event type to trigger on.</param>
|
|
||||||
/// <param name="param">The unique id for this event.</param>
|
|
||||||
/// <param name="handler">The event handler to be called.</param>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Detaches an event from a node.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="node">The node to remove the event from.</param>
|
|
||||||
/// <param name="eventType">The event type to remove.</param>
|
|
||||||
/// <param name="param">The unique id of the event to remove.</param>
|
|
||||||
public void RemoveEvent(AtkResNode* node, AtkEventType eventType, uint param)
|
|
||||||
{
|
|
||||||
this.eventListener.UnregisterEvent(node, eventType, param);
|
|
||||||
this.eventHandlers.Remove(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a delegate from the managed event handlers.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="param">Unique id of the delegate to remove.</param>
|
|
||||||
public void RemoveHandler(uint param)
|
|
||||||
{
|
|
||||||
this.eventHandlers.Remove(param);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the game cursor.
|
/// Sets the game cursor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cursor">Cursor type to set.</param>
|
/// <param name="cursor">Cursor type to set.</param>
|
||||||
public void SetCursor(AddonCursorType cursor)
|
public void SetCursor(AddonCursorType cursor) => this.cursorOverride = cursor;
|
||||||
{
|
|
||||||
this.currentCursor = cursor;
|
|
||||||
this.cursorSet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resets and un-forces custom cursor.
|
/// Resets and un-forces custom cursor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ResetCursor()
|
public void ResetCursor() => this.cursorOverride = null;
|
||||||
{
|
|
||||||
this.currentCursor = AddonCursorType.Arrow;
|
|
||||||
this.cursorSet = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ServiceManager.CallWhenServicesReady]
|
[ServiceManager.CallWhenServicesReady]
|
||||||
private void ContinueConstruction()
|
private void ContinueConstruction()
|
||||||
{
|
{
|
||||||
this.onUpdateCursor.Enable();
|
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)
|
private nint UpdateCursorDetour(RaptureAtkModule* module)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var atkStage = AtkStage.GetSingleton();
|
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;
|
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;
|
return nint.Zero;
|
||||||
|
|
@ -200,9 +102,10 @@ internal unsafe class AddonEventManagerPluginScoped : IDisposable, IServiceType,
|
||||||
|
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly AddonEventManager baseEventManager = Service<AddonEventManager>.Get();
|
private readonly AddonEventManager baseEventManager = Service<AddonEventManager>.Get();
|
||||||
|
|
||||||
private readonly uint paramKeyStartRange;
|
private readonly AddonEventListener eventListener;
|
||||||
private readonly List<uint> activeParamKeys;
|
private readonly Dictionary<uint, IAddonEventManager.AddonEventHandler> eventHandlers;
|
||||||
|
|
||||||
private bool isForcingCursor;
|
private bool isForcingCursor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -210,62 +113,51 @@ internal unsafe class AddonEventManagerPluginScoped : IDisposable, IServiceType,
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AddonEventManagerPluginScoped()
|
public AddonEventManagerPluginScoped()
|
||||||
{
|
{
|
||||||
this.paramKeyStartRange = this.baseEventManager.GetPluginParamStart();
|
this.eventHandlers = new Dictionary<uint, IAddonEventManager.AddonEventHandler>();
|
||||||
this.activeParamKeys = new List<uint>();
|
this.eventListener = new AddonEventListener(this.PluginAddonEventHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
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 multiple plugins force cursors and dispose without un-forcing them then all forces will be cleared.
|
||||||
if (this.isForcingCursor)
|
if (this.isForcingCursor)
|
||||||
{
|
{
|
||||||
this.baseEventManager.ResetCursor();
|
this.baseEventManager.ResetCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.eventListener.Dispose();
|
||||||
|
this.eventHandlers.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void AddEvent(uint eventId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
|
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 type = (AtkEventType)eventType;
|
||||||
var node = (AtkResNode*)atkResNode;
|
var node = (AtkResNode*)atkResNode;
|
||||||
var addon = (AtkUnitBase*)atkUnitBase;
|
var addon = (AtkUnitBase*)atkUnitBase;
|
||||||
var uniqueId = eventId + this.paramKeyStartRange;
|
|
||||||
|
|
||||||
if (!this.activeParamKeys.Contains(uniqueId))
|
this.eventHandlers.Add(eventId, eventHandler);
|
||||||
{
|
this.eventListener.RegisterEvent(addon, node, type, eventId);
|
||||||
this.baseEventManager.AddEvent(addon, node, type, uniqueId, eventHandler);
|
|
||||||
this.activeParamKeys.Add(uniqueId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.Warning($"Attempted to register already registered eventId: {eventId}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.Warning($"Attempted to register eventId out of range: {eventId}\nMaximum value: {0x10_000}");
|
Log.Warning($"Attempted to register already registered eventId: {eventId}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void RemoveEvent(uint eventId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType)
|
public void RemoveEvent(uint eventId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType)
|
||||||
{
|
{
|
||||||
var type = (AtkEventType)eventType;
|
if (this.eventHandlers.ContainsKey(eventId))
|
||||||
var node = (AtkResNode*)atkResNode;
|
|
||||||
var uniqueId = eventId + this.paramKeyStartRange;
|
|
||||||
|
|
||||||
if (this.activeParamKeys.Contains(uniqueId))
|
|
||||||
{
|
{
|
||||||
this.baseEventManager.RemoveEvent(node, type, uniqueId);
|
var type = (AtkEventType)eventType;
|
||||||
this.activeParamKeys.Remove(uniqueId);
|
var node = (AtkResNode*)atkResNode;
|
||||||
|
|
||||||
|
this.eventListener.UnregisterEvent(node, type, eventId);
|
||||||
|
this.eventHandlers.Remove(eventId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -288,4 +180,20 @@ internal unsafe class AddonEventManagerPluginScoped : IDisposable, IServiceType,
|
||||||
|
|
||||||
this.baseEventManager.ResetCursor();
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue