mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
AddonLifecycle Add AddonReceiveEvent (#1473)
This commit is contained in:
parent
8be1e4b8ef
commit
d15e35f3f6
9 changed files with 145 additions and 6 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Addon argument data for Finalize events.
|
/// Addon argument data for Draw events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AddonDrawArgs : AddonArgs
|
public class AddonDrawArgs : AddonArgs
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Addon argument data for Finalize events.
|
/// Addon argument data for ReceiveEvent events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AddonFinalizeArgs : AddonArgs
|
public class AddonFinalizeArgs : AddonArgs
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Addon argument data for ReceiveEvent events.
|
||||||
|
/// </summary>
|
||||||
|
public class AddonReceiveEventArgs : AddonArgs
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override AddonArgsType Type => AddonArgsType.ReceiveEvent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the AtkEventType for this event message.
|
||||||
|
/// </summary>
|
||||||
|
public byte AtkEventType { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the event id for this event message.
|
||||||
|
/// </summary>
|
||||||
|
public int EventParam { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the pointer to an AtkEvent for this event message.
|
||||||
|
/// </summary>
|
||||||
|
public nint AtkEvent { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the pointer to a block of data for this event message.
|
||||||
|
/// </summary>
|
||||||
|
public nint Data { get; init; }
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Addon argument data for Finalize events.
|
/// Addon argument data for Refresh events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AddonRefreshArgs : AddonArgs
|
public class AddonRefreshArgs : AddonArgs
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Addon argument data for Finalize events.
|
/// Addon argument data for OnRequestedUpdate events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AddonRequestedUpdateArgs : AddonArgs
|
public class AddonRequestedUpdateArgs : AddonArgs
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Addon argument data for Finalize events.
|
/// Addon argument data for Update events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AddonUpdateArgs : AddonArgs
|
public class AddonUpdateArgs : AddonArgs
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,9 @@ public enum AddonArgsType
|
||||||
/// Contains argument data for Refresh.
|
/// Contains argument data for Refresh.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Refresh,
|
Refresh,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains argument data for ReceiveEvent.
|
||||||
|
/// </summary>
|
||||||
|
ReceiveEvent,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,4 +59,14 @@ public enum AddonEvent
|
||||||
/// Event that is fired after an addon has finished a refresh.
|
/// Event that is fired after an addon has finished a refresh.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
PostRefresh,
|
PostRefresh,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is fired before an addon begins processing an event.
|
||||||
|
/// </summary>
|
||||||
|
PreReceiveEvent,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is fired after an addon has processed an event.
|
||||||
|
/// </summary>
|
||||||
|
PostReceiveEvent,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using Dalamud.Hooking.Internal;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
|
using Dalamud.Memory;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
|
||||||
|
|
@ -38,6 +39,8 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
||||||
private readonly ConcurrentBag<AddonLifecycleEventListener> removeEventListeners = new();
|
private readonly ConcurrentBag<AddonLifecycleEventListener> removeEventListeners = new();
|
||||||
private readonly List<AddonLifecycleEventListener> eventListeners = new();
|
private readonly List<AddonLifecycleEventListener> eventListeners = new();
|
||||||
|
|
||||||
|
private readonly Dictionary<string, Hook<AddonReceiveEventDelegate>> receiveEventHooks = new();
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private AddonLifecycle(TargetSigScanner sigScanner)
|
private AddonLifecycle(TargetSigScanner sigScanner)
|
||||||
{
|
{
|
||||||
|
|
@ -67,6 +70,8 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
||||||
|
|
||||||
private delegate byte AddonOnRefreshDelegate(AtkUnitManager* unitManager, AtkUnitBase* addon, uint valueCount, AtkValue* values);
|
private delegate byte AddonOnRefreshDelegate(AtkUnitManager* unitManager, AtkUnitBase* addon, uint valueCount, AtkValue* values);
|
||||||
|
|
||||||
|
private delegate void AddonReceiveEventDelegate(AtkUnitBase* addon, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, nint a5);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|
@ -79,6 +84,11 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
||||||
this.onAddonUpdateHook.Dispose();
|
this.onAddonUpdateHook.Dispose();
|
||||||
this.onAddonRefreshHook.Dispose();
|
this.onAddonRefreshHook.Dispose();
|
||||||
this.onAddonRequestedUpdateHook.Dispose();
|
this.onAddonRequestedUpdateHook.Dispose();
|
||||||
|
|
||||||
|
foreach (var (_, hook) in this.receiveEventHooks)
|
||||||
|
{
|
||||||
|
hook.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -104,7 +114,21 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
if (this.newEventListeners.Any())
|
if (this.newEventListeners.Any())
|
||||||
{
|
{
|
||||||
this.eventListeners.AddRange(this.newEventListeners);
|
foreach (var toAddListener in this.newEventListeners)
|
||||||
|
{
|
||||||
|
this.eventListeners.Add(toAddListener);
|
||||||
|
|
||||||
|
// If we want receive event messages have an already active addon, enable the receive event hook.
|
||||||
|
// If the addon isn't active yet, we'll grab the hook when it sets up.
|
||||||
|
if (toAddListener is { EventType: AddonEvent.PreReceiveEvent or AddonEvent.PostReceiveEvent })
|
||||||
|
{
|
||||||
|
if (this.receiveEventHooks.TryGetValue(toAddListener.AddonName, out var hook))
|
||||||
|
{
|
||||||
|
hook.Enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.newEventListeners.Clear();
|
this.newEventListeners.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,6 +166,23 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
||||||
|
|
||||||
private void OnAddonSetup(AtkUnitBase* addon, uint valueCount, AtkValue* values)
|
private void OnAddonSetup(AtkUnitBase* addon, uint valueCount, AtkValue* values)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Hook the addon's ReceiveEvent function here, but only enable the hook if we have an active listener.
|
||||||
|
var addonName = MemoryHelper.ReadStringNullTerminated((nint)addon->Name);
|
||||||
|
var receiveEventHook = Hook<AddonReceiveEventDelegate>.FromAddress((nint)addon->VTable->ReceiveEvent, this.OnReceiveEvent);
|
||||||
|
this.receiveEventHooks.TryAdd(addonName, receiveEventHook);
|
||||||
|
|
||||||
|
if (this.eventListeners.Any(listener => listener.EventType is AddonEvent.PostReceiveEvent or AddonEvent.PreReceiveEvent))
|
||||||
|
{
|
||||||
|
receiveEventHook.Enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "Exception in OnAddonSetup ReceiveEvent Registration.");
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.InvokeListeners(AddonEvent.PreSetup, new AddonSetupArgs
|
this.InvokeListeners(AddonEvent.PreSetup, new AddonSetupArgs
|
||||||
|
|
@ -175,6 +216,21 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
||||||
|
|
||||||
private void OnAddonFinalize(AtkUnitManager* unitManager, AtkUnitBase** atkUnitBase)
|
private void OnAddonFinalize(AtkUnitManager* unitManager, AtkUnitBase** atkUnitBase)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Remove this addons ReceiveEvent Registration
|
||||||
|
var addonName = MemoryHelper.ReadStringNullTerminated((nint)atkUnitBase[0]->Name);
|
||||||
|
if (this.receiveEventHooks.TryGetValue(addonName, out var hook))
|
||||||
|
{
|
||||||
|
hook.Dispose();
|
||||||
|
this.receiveEventHooks.Remove(addonName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "Exception in OnAddonFinalize ReceiveEvent Removal.");
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.InvokeListeners(AddonEvent.PreFinalize, new AddonFinalizeArgs { Addon = (nint)atkUnitBase[0] });
|
this.InvokeListeners(AddonEvent.PreFinalize, new AddonFinalizeArgs { Addon = (nint)atkUnitBase[0] });
|
||||||
|
|
@ -300,6 +356,44 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
||||||
Log.Error(e, "Exception in OnRequestedUpdate post-requestedUpdate invoke.");
|
Log.Error(e, "Exception in OnRequestedUpdate post-requestedUpdate invoke.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnReceiveEvent(AtkUnitBase* addon, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, nint data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.InvokeListeners(AddonEvent.PreReceiveEvent, new AddonReceiveEventArgs
|
||||||
|
{
|
||||||
|
Addon = (nint)addon,
|
||||||
|
AtkEventType = (byte)eventType,
|
||||||
|
EventParam = eventParam,
|
||||||
|
AtkEvent = (nint)atkEvent,
|
||||||
|
Data = data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "Exception in OnReceiveEvent pre-receiveEvent invoke.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var addonName = MemoryHelper.ReadStringNullTerminated((nint)addon->Name);
|
||||||
|
this.receiveEventHooks[addonName].Original(addon, eventType, eventParam, atkEvent, data);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.InvokeListeners(AddonEvent.PostReceiveEvent, new AddonReceiveEventArgs
|
||||||
|
{
|
||||||
|
Addon = (nint)addon,
|
||||||
|
AtkEventType = (byte)eventType,
|
||||||
|
EventParam = eventParam,
|
||||||
|
AtkEvent = (nint)atkEvent,
|
||||||
|
Data = data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "Exception in OnAddonRefresh post-receiveEvent invoke.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue