AddonLifecycle Add AddonReceiveEvent (#1473)

This commit is contained in:
MidoriKami 2023-10-09 13:10:49 -07:00 committed by GitHub
parent 8be1e4b8ef
commit d15e35f3f6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 145 additions and 6 deletions

View file

@ -1,7 +1,7 @@
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
/// <summary>
/// Addon argument data for Finalize events.
/// Addon argument data for Draw events.
/// </summary>
public class AddonDrawArgs : AddonArgs
{

View file

@ -1,7 +1,7 @@
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
/// <summary>
/// Addon argument data for Finalize events.
/// Addon argument data for ReceiveEvent events.
/// </summary>
public class AddonFinalizeArgs : AddonArgs
{

View file

@ -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; }
}

View file

@ -3,7 +3,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
/// <summary>
/// Addon argument data for Finalize events.
/// Addon argument data for Refresh events.
/// </summary>
public class AddonRefreshArgs : AddonArgs
{

View file

@ -1,7 +1,7 @@
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
/// <summary>
/// Addon argument data for Finalize events.
/// Addon argument data for OnRequestedUpdate events.
/// </summary>
public class AddonRequestedUpdateArgs : AddonArgs
{

View file

@ -1,7 +1,7 @@
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
/// <summary>
/// Addon argument data for Finalize events.
/// Addon argument data for Update events.
/// </summary>
public class AddonUpdateArgs : AddonArgs
{

View file

@ -34,4 +34,9 @@ public enum AddonArgsType
/// Contains argument data for Refresh.
/// </summary>
Refresh,
/// <summary>
/// Contains argument data for ReceiveEvent.
/// </summary>
ReceiveEvent,
}

View file

@ -59,4 +59,14 @@ public enum AddonEvent
/// Event that is fired after an addon has finished a refresh.
/// </summary>
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,
}

View file

@ -8,6 +8,7 @@ using Dalamud.Hooking.Internal;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Logging.Internal;
using Dalamud.Memory;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Component.GUI;
@ -38,6 +39,8 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
private readonly ConcurrentBag<AddonLifecycleEventListener> removeEventListeners = new();
private readonly List<AddonLifecycleEventListener> eventListeners = new();
private readonly Dictionary<string, Hook<AddonReceiveEventDelegate>> receiveEventHooks = new();
[ServiceManager.ServiceConstructor]
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 void AddonReceiveEventDelegate(AtkUnitBase* addon, AtkEventType eventType, int eventParam, AtkEvent* atkEvent, nint a5);
/// <inheritdoc/>
public void Dispose()
{
@ -79,6 +84,11 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
this.onAddonUpdateHook.Dispose();
this.onAddonRefreshHook.Dispose();
this.onAddonRequestedUpdateHook.Dispose();
foreach (var (_, hook) in this.receiveEventHooks)
{
hook.Dispose();
}
}
/// <summary>
@ -104,7 +114,21 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
{
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();
}
@ -142,6 +166,23 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
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
{
this.InvokeListeners(AddonEvent.PreSetup, new AddonSetupArgs
@ -175,6 +216,21 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
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
{
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.");
}
}
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>