mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-03 14:23:40 +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]
|
||||
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<UpdateCursorDelegate> onUpdateCursor;
|
||||
private readonly Dictionary<uint, IAddonEventManager.AddonEventHandler> 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<uint, IAddonEventManager.AddonEventHandler>();
|
||||
this.currentCursor = AddonCursorType.Arrow;
|
||||
this.cursorOverride = null;
|
||||
|
||||
this.onUpdateCursor = Hook<UpdateCursorDelegate>.FromAddress(this.address.UpdateCursor, this.UpdateCursorDetour);
|
||||
}
|
||||
|
|
@ -61,116 +41,38 @@ internal unsafe class AddonEventManager : IDisposable, IServiceType
|
|||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
this.eventListener.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>
|
||||
/// Sets the game cursor.
|
||||
/// </summary>
|
||||
/// <param name="cursor">Cursor type to set.</param>
|
||||
public void SetCursor(AddonCursorType cursor)
|
||||
{
|
||||
this.currentCursor = cursor;
|
||||
this.cursorSet = true;
|
||||
}
|
||||
public void SetCursor(AddonCursorType cursor) => this.cursorOverride = cursor;
|
||||
|
||||
/// <summary>
|
||||
/// Resets and un-forces custom cursor.
|
||||
/// </summary>
|
||||
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<AddonEventManager>.Get();
|
||||
|
||||
private readonly uint paramKeyStartRange;
|
||||
private readonly List<uint> activeParamKeys;
|
||||
|
||||
private readonly AddonEventListener eventListener;
|
||||
private readonly Dictionary<uint, IAddonEventManager.AddonEventHandler> eventHandlers;
|
||||
|
||||
private bool isForcingCursor;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -210,62 +113,51 @@ internal unsafe class AddonEventManagerPluginScoped : IDisposable, IServiceType,
|
|||
/// </summary>
|
||||
public AddonEventManagerPluginScoped()
|
||||
{
|
||||
this.paramKeyStartRange = this.baseEventManager.GetPluginParamStart();
|
||||
this.activeParamKeys = new List<uint>();
|
||||
this.eventHandlers = new Dictionary<uint, IAddonEventManager.AddonEventHandler>();
|
||||
this.eventListener = new AddonEventListener(this.PluginAddonEventHandler);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue