diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs
index 334542c71..077ca7c93 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonArgs.cs
@@ -14,22 +14,51 @@ public abstract unsafe class AddonArgs
public const string InvalidAddon = "NullAddon";
private string? addonName;
+ private IntPtr addon;
///
/// Gets the name of the addon this args referrers to.
///
public string AddonName => this.GetAddonName();
-
+
///
/// Gets the pointer to the addons AtkUnitBase.
///
- public nint Addon { get; init; }
-
+ public nint Addon
+ {
+ get => this.addon;
+ internal set
+ {
+ if (this.addon == value)
+ return;
+
+ this.addon = value;
+ this.addonName = null;
+ }
+ }
+
///
/// Gets the type of these args.
///
public abstract AddonArgsType Type { get; }
+ ///
+ /// Checks if addon name matches the given span of char.
+ ///
+ /// The name to check.
+ /// Whether it is the case.
+ internal bool IsAddon(ReadOnlySpan name)
+ {
+ if (this.Addon == nint.Zero) return false;
+ if (name.Length is 0 or > 0x20)
+ return false;
+
+ var addonPointer = (AtkUnitBase*)this.Addon;
+ if (addonPointer->Name is null) return false;
+
+ return MemoryHelper.EqualsZeroTerminatedString(name, (nint)addonPointer->Name, null, 0x20);
+ }
+
///
/// Helper method for ensuring the name of the addon is valid.
///
diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonDrawArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonDrawArgs.cs
index 10d46a573..1e1013dd5 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonDrawArgs.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonDrawArgs.cs
@@ -3,8 +3,14 @@
///
/// Addon argument data for Draw events.
///
-public class AddonDrawArgs : AddonArgs
+public class AddonDrawArgs : AddonArgs, ICloneable
{
///
public override AddonArgsType Type => AddonArgsType.Draw;
+
+ ///
+ public AddonDrawArgs Clone() => (AddonDrawArgs)this.MemberwiseClone();
+
+ ///
+ object ICloneable.Clone() => this.Clone();
}
diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFinalizeArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFinalizeArgs.cs
index caf422927..fc26a6c33 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFinalizeArgs.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonFinalizeArgs.cs
@@ -3,8 +3,14 @@ namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
///
/// Addon argument data for ReceiveEvent events.
///
-public class AddonFinalizeArgs : AddonArgs
+public class AddonFinalizeArgs : AddonArgs, ICloneable
{
///
public override AddonArgsType Type => AddonArgsType.Finalize;
+
+ ///
+ public AddonFinalizeArgs Clone() => (AddonFinalizeArgs)this.MemberwiseClone();
+
+ ///
+ object ICloneable.Clone() => this.Clone();
}
diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonReceiveEventArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonReceiveEventArgs.cs
index df75307f1..8f9003b4c 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonReceiveEventArgs.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonReceiveEventArgs.cs
@@ -3,28 +3,34 @@ namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
///
/// Addon argument data for ReceiveEvent events.
///
-public class AddonReceiveEventArgs : AddonArgs
+public class AddonReceiveEventArgs : AddonArgs, ICloneable
{
///
public override AddonArgsType Type => AddonArgsType.ReceiveEvent;
///
- /// Gets the AtkEventType for this event message.
+ /// Gets or sets the AtkEventType for this event message.
///
- public byte AtkEventType { get; init; }
+ public byte AtkEventType { get; set; }
///
- /// Gets the event id for this event message.
+ /// Gets or sets the event id for this event message.
///
- public int EventParam { get; init; }
+ public int EventParam { get; set; }
///
- /// Gets the pointer to an AtkEvent for this event message.
+ /// Gets or sets the pointer to an AtkEvent for this event message.
///
- public nint AtkEvent { get; init; }
+ public nint AtkEvent { get; set; }
///
- /// Gets the pointer to a block of data for this event message.
+ /// Gets or sets the pointer to a block of data for this event message.
///
- public nint Data { get; init; }
+ public nint Data { get; set; }
+
+ ///
+ public AddonReceiveEventArgs Clone() => (AddonReceiveEventArgs)this.MemberwiseClone();
+
+ ///
+ object ICloneable.Clone() => this.Clone();
}
diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRefreshArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRefreshArgs.cs
index b6ac6d8b6..bfcf02544 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRefreshArgs.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRefreshArgs.cs
@@ -5,23 +5,29 @@ namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
///
/// Addon argument data for Refresh events.
///
-public class AddonRefreshArgs : AddonArgs
+public class AddonRefreshArgs : AddonArgs, ICloneable
{
///
public override AddonArgsType Type => AddonArgsType.Refresh;
///
- /// Gets the number of AtkValues.
+ /// Gets or sets the number of AtkValues.
///
- public uint AtkValueCount { get; init; }
+ public uint AtkValueCount { get; set; }
///
- /// Gets the address of the AtkValue array.
+ /// Gets or sets the address of the AtkValue array.
///
- public nint AtkValues { get; init; }
+ public nint AtkValues { get; set; }
///
/// Gets the AtkValues in the form of a span.
///
public unsafe Span AtkValueSpan => new(this.AtkValues.ToPointer(), (int)this.AtkValueCount);
+
+ ///
+ public AddonRefreshArgs Clone() => (AddonRefreshArgs)this.MemberwiseClone();
+
+ ///
+ object ICloneable.Clone() => this.Clone();
}
diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs
index 1b743b31a..219288ccf 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonRequestedUpdateArgs.cs
@@ -3,18 +3,24 @@
///
/// Addon argument data for OnRequestedUpdate events.
///
-public class AddonRequestedUpdateArgs : AddonArgs
+public class AddonRequestedUpdateArgs : AddonArgs, ICloneable
{
///
public override AddonArgsType Type => AddonArgsType.RequestedUpdate;
///
- /// Gets the NumberArrayData** for this event.
+ /// Gets or sets the NumberArrayData** for this event.
///
- public nint NumberArrayData { get; init; }
+ public nint NumberArrayData { get; set; }
///
- /// Gets the StringArrayData** for this event.
+ /// Gets or sets the StringArrayData** for this event.
///
- public nint StringArrayData { get; init; }
+ public nint StringArrayData { get; set; }
+
+ ///
+ public AddonRequestedUpdateArgs Clone() => (AddonRequestedUpdateArgs)this.MemberwiseClone();
+
+ ///
+ object ICloneable.Clone() => this.Clone();
}
diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonSetupArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonSetupArgs.cs
index df2ec26be..bd60879b8 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonSetupArgs.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonSetupArgs.cs
@@ -5,23 +5,29 @@ namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
///
/// Addon argument data for Setup events.
///
-public class AddonSetupArgs : AddonArgs
+public class AddonSetupArgs : AddonArgs, ICloneable
{
///
public override AddonArgsType Type => AddonArgsType.Setup;
///
- /// Gets the number of AtkValues.
+ /// Gets or sets the number of AtkValues.
///
- public uint AtkValueCount { get; init; }
+ public uint AtkValueCount { get; set; }
///
- /// Gets the address of the AtkValue array.
+ /// Gets or sets the address of the AtkValue array.
///
- public nint AtkValues { get; init; }
+ public nint AtkValues { get; set; }
///
/// Gets the AtkValues in the form of a span.
///
public unsafe Span AtkValueSpan => new(this.AtkValues.ToPointer(), (int)this.AtkValueCount);
+
+ ///
+ public AddonSetupArgs Clone() => (AddonSetupArgs)this.MemberwiseClone();
+
+ ///
+ object ICloneable.Clone() => this.Clone();
}
diff --git a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonUpdateArgs.cs b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonUpdateArgs.cs
index 651fbcafb..b087ac15a 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonUpdateArgs.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonArgTypes/AddonUpdateArgs.cs
@@ -3,7 +3,7 @@ namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
///
/// Addon argument data for Update events.
///
-public class AddonUpdateArgs : AddonArgs
+public class AddonUpdateArgs : AddonArgs, ICloneable
{
///
public override AddonArgsType Type => AddonArgsType.Update;
@@ -11,5 +11,11 @@ public class AddonUpdateArgs : AddonArgs
///
/// Gets the time since the last update.
///
- public float TimeDelta { get; init; }
+ public float TimeDelta { get; internal set; }
+
+ ///
+ public AddonUpdateArgs Clone() => (AddonUpdateArgs)this.MemberwiseClone();
+
+ ///
+ object ICloneable.Clone() => this.Clone();
}
diff --git a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs
index 3528de562..decb7a9f4 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs
@@ -1,6 +1,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.CompilerServices;
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Hooking;
@@ -40,6 +41,15 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
private readonly ConcurrentBag newEventListeners = new();
private readonly ConcurrentBag removeEventListeners = new();
+ // Note: these can be sourced from ObjectPool of appropriate types instead, but since we don't import that NuGet
+ // package, and these events are always called from the main thread, this is fine.
+ private readonly AddonSetupArgs recyclingSetupArgs = new();
+ private readonly AddonFinalizeArgs recyclingFinalizeArgs = new();
+ private readonly AddonDrawArgs recyclingDrawArgs = new();
+ private readonly AddonUpdateArgs recyclingUpdateArgs = new();
+ private readonly AddonRefreshArgs recyclingRefreshArgs = new();
+ private readonly AddonRequestedUpdateArgs recyclingRequestedUpdateArgs = new();
+
[ServiceManager.ServiceConstructor]
private AddonLifecycle(TargetSigScanner sigScanner)
{
@@ -132,12 +142,27 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
///
/// Event Type.
/// AddonArgs.
- internal void InvokeListeners(AddonEvent eventType, AddonArgs args)
+ /// What to blame on errors.
+ internal void InvokeListenersSafely(AddonEvent eventType, AddonArgs args, [CallerMemberName] string blame = "")
{
- // Match on string.empty for listeners that want events for all addons.
- foreach (var listener in this.EventListeners.Where(listener => listener.EventType == eventType && (listener.AddonName == args.AddonName || listener.AddonName == string.Empty)))
+ // Do not use linq; this is a high-traffic function, and more heap allocations avoided, the better.
+ foreach (var listener in this.EventListeners)
{
- listener.FunctionDelegate.Invoke(eventType, args);
+ if (listener.EventType != eventType)
+ continue;
+
+ // Match on string.empty for listeners that want events for all addons.
+ if (!string.IsNullOrWhiteSpace(listener.AddonName) && !args.IsAddon(listener.AddonName))
+ continue;
+
+ try
+ {
+ listener.FunctionDelegate.Invoke(eventType, args);
+ }
+ catch (Exception e)
+ {
+ Log.Error(e, $"Exception in {blame} during {eventType} invoke.");
+ }
}
}
@@ -249,20 +274,13 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
{
Log.Error(e, "Exception in OnAddonSetup ReceiveEvent Registration.");
}
-
- try
- {
- this.InvokeListeners(AddonEvent.PreSetup, new AddonSetupArgs
- {
- Addon = (nint)addon,
- AtkValueCount = valueCount,
- AtkValues = (nint)values,
- });
- }
- catch (Exception e)
- {
- Log.Error(e, "Exception in OnAddonSetup pre-setup invoke.");
- }
+
+ this.recyclingSetupArgs.Addon = (nint)addon;
+ this.recyclingSetupArgs.AtkValueCount = valueCount;
+ this.recyclingSetupArgs.AtkValues = (nint)values;
+ this.InvokeListenersSafely(AddonEvent.PreSetup, this.recyclingSetupArgs);
+ valueCount = this.recyclingSetupArgs.AtkValueCount;
+ values = (AtkValue*)this.recyclingSetupArgs.AtkValues;
try
{
@@ -273,19 +291,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
Log.Error(e, "Caught exception when calling original AddonSetup. This may be a bug in the game or another plugin hooking this method.");
}
- try
- {
- this.InvokeListeners(AddonEvent.PostSetup, new AddonSetupArgs
- {
- Addon = (nint)addon,
- AtkValueCount = valueCount,
- AtkValues = (nint)values,
- });
- }
- catch (Exception e)
- {
- Log.Error(e, "Exception in OnAddonSetup post-setup invoke.");
- }
+ this.InvokeListenersSafely(AddonEvent.PostSetup, this.recyclingSetupArgs);
}
private void OnAddonFinalize(AtkUnitManager* unitManager, AtkUnitBase** atkUnitBase)
@@ -299,15 +305,9 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
{
Log.Error(e, "Exception in OnAddonFinalize ReceiveEvent Removal.");
}
-
- try
- {
- this.InvokeListeners(AddonEvent.PreFinalize, new AddonFinalizeArgs { Addon = (nint)atkUnitBase[0] });
- }
- catch (Exception e)
- {
- Log.Error(e, "Exception in OnAddonFinalize pre-finalize invoke.");
- }
+
+ this.recyclingFinalizeArgs.Addon = (nint)atkUnitBase[0];
+ this.InvokeListenersSafely(AddonEvent.PreFinalize, this.recyclingFinalizeArgs);
try
{
@@ -321,14 +321,8 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
private void OnAddonDraw(AtkUnitBase* addon)
{
- try
- {
- this.InvokeListeners(AddonEvent.PreDraw, new AddonDrawArgs { Addon = (nint)addon });
- }
- catch (Exception e)
- {
- Log.Error(e, "Exception in OnAddonDraw pre-draw invoke.");
- }
+ this.recyclingDrawArgs.Addon = (nint)addon;
+ this.InvokeListenersSafely(AddonEvent.PreDraw, this.recyclingDrawArgs);
try
{
@@ -339,26 +333,14 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
Log.Error(e, "Caught exception when calling original AddonDraw. This may be a bug in the game or another plugin hooking this method.");
}
- try
- {
- this.InvokeListeners(AddonEvent.PostDraw, new AddonDrawArgs { Addon = (nint)addon });
- }
- catch (Exception e)
- {
- Log.Error(e, "Exception in OnAddonDraw post-draw invoke.");
- }
+ this.InvokeListenersSafely(AddonEvent.PostDraw, this.recyclingDrawArgs);
}
private void OnAddonUpdate(AtkUnitBase* addon, float delta)
{
- try
- {
- this.InvokeListeners(AddonEvent.PreUpdate, new AddonUpdateArgs { Addon = (nint)addon, TimeDelta = delta });
- }
- catch (Exception e)
- {
- Log.Error(e, "Exception in OnAddonUpdate pre-update invoke.");
- }
+ this.recyclingUpdateArgs.Addon = (nint)addon;
+ this.recyclingUpdateArgs.TimeDelta = delta;
+ this.InvokeListenersSafely(AddonEvent.PreUpdate, this.recyclingUpdateArgs);
try
{
@@ -369,33 +351,19 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
Log.Error(e, "Caught exception when calling original AddonUpdate. This may be a bug in the game or another plugin hooking this method.");
}
- try
- {
- this.InvokeListeners(AddonEvent.PostUpdate, new AddonUpdateArgs { Addon = (nint)addon, TimeDelta = delta });
- }
- catch (Exception e)
- {
- Log.Error(e, "Exception in OnAddonUpdate post-update invoke.");
- }
+ this.InvokeListenersSafely(AddonEvent.PostUpdate, this.recyclingUpdateArgs);
}
private byte OnAddonRefresh(AtkUnitManager* atkUnitManager, AtkUnitBase* addon, uint valueCount, AtkValue* values)
{
byte result = 0;
-
- try
- {
- this.InvokeListeners(AddonEvent.PreRefresh, new AddonRefreshArgs
- {
- Addon = (nint)addon,
- AtkValueCount = valueCount,
- AtkValues = (nint)values,
- });
- }
- catch (Exception e)
- {
- Log.Error(e, "Exception in OnAddonRefresh pre-refresh invoke.");
- }
+
+ this.recyclingRefreshArgs.Addon = (nint)addon;
+ this.recyclingRefreshArgs.AtkValueCount = valueCount;
+ this.recyclingRefreshArgs.AtkValues = (nint)values;
+ this.InvokeListenersSafely(AddonEvent.PreRefresh, this.recyclingRefreshArgs);
+ valueCount = this.recyclingRefreshArgs.AtkValueCount;
+ values = (AtkValue*)this.recyclingRefreshArgs.AtkValues;
try
{
@@ -406,38 +374,18 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
Log.Error(e, "Caught exception when calling original AddonRefresh. This may be a bug in the game or another plugin hooking this method.");
}
- try
- {
- this.InvokeListeners(AddonEvent.PostRefresh, new AddonRefreshArgs
- {
- Addon = (nint)addon,
- AtkValueCount = valueCount,
- AtkValues = (nint)values,
- });
- }
- catch (Exception e)
- {
- Log.Error(e, "Exception in OnAddonRefresh post-refresh invoke.");
- }
-
+ this.InvokeListenersSafely(AddonEvent.PostRefresh, this.recyclingRefreshArgs);
return result;
}
private void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData)
{
- try
- {
- this.InvokeListeners(AddonEvent.PreRequestedUpdate, new AddonRequestedUpdateArgs
- {
- Addon = (nint)addon,
- NumberArrayData = (nint)numberArrayData,
- StringArrayData = (nint)stringArrayData,
- });
- }
- catch (Exception e)
- {
- Log.Error(e, "Exception in OnRequestedUpdate pre-requestedUpdate invoke.");
- }
+ this.recyclingRequestedUpdateArgs.Addon = (nint)addon;
+ this.recyclingRequestedUpdateArgs.NumberArrayData = (nint)numberArrayData;
+ this.recyclingRequestedUpdateArgs.StringArrayData = (nint)stringArrayData;
+ this.InvokeListenersSafely(AddonEvent.PreRequestedUpdate, this.recyclingRequestedUpdateArgs);
+ numberArrayData = (NumberArrayData**)this.recyclingRequestedUpdateArgs.NumberArrayData;
+ stringArrayData = (StringArrayData**)this.recyclingRequestedUpdateArgs.StringArrayData;
try
{
@@ -448,19 +396,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType
Log.Error(e, "Caught exception when calling original AddonRequestedUpdate. This may be a bug in the game or another plugin hooking this method.");
}
- try
- {
- this.InvokeListeners(AddonEvent.PostRequestedUpdate, new AddonRequestedUpdateArgs
- {
- Addon = (nint)addon,
- NumberArrayData = (nint)numberArrayData,
- StringArrayData = (nint)stringArrayData,
- });
- }
- catch (Exception e)
- {
- Log.Error(e, "Exception in OnRequestedUpdate post-requestedUpdate invoke.");
- }
+ this.InvokeListenersSafely(AddonEvent.PostRequestedUpdate, this.recyclingRequestedUpdateArgs);
}
}
diff --git a/Dalamud/Game/Addon/Lifecycle/AddonLifecycleReceiveEventListener.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleReceiveEventListener.cs
index 10171eb16..1c138e447 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonLifecycleReceiveEventListener.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycleReceiveEventListener.cs
@@ -16,6 +16,10 @@ internal unsafe class AddonLifecycleReceiveEventListener : IDisposable
{
private static readonly ModuleLog Log = new("AddonLifecycle");
+ // Note: these can be sourced from ObjectPool of appropriate types instead, but since we don't import that NuGet
+ // package, and these events are always called from the main thread, this is fine.
+ private readonly AddonReceiveEventArgs recyclingReceiveEventArgs = new();
+
///
/// Initializes a new instance of the class.
///
@@ -74,22 +78,17 @@ internal unsafe class AddonLifecycleReceiveEventListener : IDisposable
this.Hook!.Original(addon, eventType, eventParam, atkEvent, data);
return;
}
-
- try
- {
- this.AddonLifecycle.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.");
- }
+
+ this.recyclingReceiveEventArgs.Addon = (nint)addon;
+ this.recyclingReceiveEventArgs.AtkEventType = (byte)eventType;
+ this.recyclingReceiveEventArgs.EventParam = eventParam;
+ this.recyclingReceiveEventArgs.AtkEvent = (IntPtr)atkEvent;
+ this.recyclingReceiveEventArgs.Data = data;
+ this.AddonLifecycle.InvokeListenersSafely(AddonEvent.PreReceiveEvent, this.recyclingReceiveEventArgs);
+ eventType = (AtkEventType)this.recyclingReceiveEventArgs.AtkEventType;
+ eventParam = this.recyclingReceiveEventArgs.EventParam;
+ atkEvent = (AtkEvent*)this.recyclingReceiveEventArgs.AtkEvent;
+ data = this.recyclingReceiveEventArgs.Data;
try
{
@@ -100,20 +99,6 @@ internal unsafe class AddonLifecycleReceiveEventListener : IDisposable
Log.Error(e, "Caught exception when calling original AddonReceiveEvent. This may be a bug in the game or another plugin hooking this method.");
}
- try
- {
- this.AddonLifecycle.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.");
- }
+ this.AddonLifecycle.InvokeListenersSafely(AddonEvent.PostReceiveEvent, this.recyclingReceiveEventArgs);
}
}
diff --git a/Dalamud/Memory/MemoryHelper.cs b/Dalamud/Memory/MemoryHelper.cs
index 3ceecf6a6..552817646 100644
--- a/Dalamud/Memory/MemoryHelper.cs
+++ b/Dalamud/Memory/MemoryHelper.cs
@@ -163,6 +163,38 @@ public static unsafe class MemoryHelper
#region ReadString
+ ///
+ /// Compares if the given char span equals to the null-terminated string at .
+ ///
+ /// The character span.
+ /// The address of null-terminated string.
+ /// The encoding of the null-terminated string.
+ /// The maximum length of the null-terminated string.
+ /// Whether they are equal.
+ public static bool EqualsZeroTerminatedString(
+ ReadOnlySpan charSpan,
+ nint memoryAddress,
+ Encoding? encoding = null,
+ int maxLength = int.MaxValue)
+ {
+ encoding ??= Encoding.UTF8;
+ maxLength = Math.Min(maxLength, charSpan.Length + 4);
+
+ var pmem = ((byte*)memoryAddress)!;
+ var length = 0;
+ while (length < maxLength && pmem[length] != 0)
+ length++;
+
+ var mem = new Span(pmem, length);
+ var memCharCount = encoding.GetCharCount(mem);
+ if (memCharCount != charSpan.Length)
+ return false;
+
+ Span chars = stackalloc char[memCharCount];
+ encoding.GetChars(mem, chars);
+ return charSpan.SequenceEqual(chars);
+ }
+
///
/// Read a UTF-8 encoded string from a specified memory address.
///