From 22285ba85158becc275fccb0d8715accb90bf036 Mon Sep 17 00:00:00 2001
From: MS-crew <100300664+MS-crew@users.noreply.github.com>
Date: Tue, 27 Jan 2026 23:11:52 +0300
Subject: [PATCH 1/7] Update Events.cs
Update
---
EXILED/Exiled.Events/Config.cs | 18 +++++
EXILED/Exiled.Events/Events.cs | 24 +++++++
EXILED/Exiled.Events/Features/Event{T}.cs | 82 +++++++++++++++++++++++
3 files changed, 124 insertions(+)
diff --git a/EXILED/Exiled.Events/Config.cs b/EXILED/Exiled.Events/Config.cs
index fd244f699b..46ef02f141 100644
--- a/EXILED/Exiled.Events/Config.cs
+++ b/EXILED/Exiled.Events/Config.cs
@@ -110,5 +110,23 @@ public sealed class Config : IConfig
///
[Description("Whether to log RA commands.")]
public bool LogRaCommands { get; set; } = true;
+
+ ///
+ /// Gets or sets a value indicating whether the Event Profiler is enabled.
+ ///
+ [Description("Indicates whether to enable the event profiler. This detects and logs plugins that cause lag by taking too long to handle events.")]
+ public bool EnableEventProfiler { get; set; } = false;
+
+ ///
+ /// Gets or sets the threshold in milliseconds for the Event Profiler.
+ ///
+ [Description("The threshold in milliseconds. If a plugin takes longer than this to handle an event, a warning will be logged.")]
+ public double EventProfilerThreshold { get; set; } = 16.6;
+
+ ///
+ /// Gets or sets the allocation threshold in bytes.
+ ///
+ [Description("If a plugin allocates more memory than this (bytes) in a single event, it will be logged. Default: 16KB")]
+ public long EventProfilerAllocationThreshold { get; set; } = 16384;
}
}
diff --git a/EXILED/Exiled.Events/Events.cs b/EXILED/Exiled.Events/Events.cs
index c941abd7ca..15e7732fa5 100644
--- a/EXILED/Exiled.Events/Events.cs
+++ b/EXILED/Exiled.Events/Events.cs
@@ -30,6 +30,23 @@ namespace Exiled.Events
///
public sealed class Events : Plugin
{
+#pragma warning disable SA1401
+ ///
+ /// Indicates whether the event profiler is enabled.
+ ///
+ internal static bool IsProfilerEnabled;
+
+ ///
+ /// Execution time threshold (ms) for profiler warnings.
+ ///
+ internal static long AllocationThreshold;
+
+ ///
+ /// Allocation threshold (bytes) for profiler warnings.
+ ///
+ internal static double ProfilerThreshold;
+#pragma warning restore SA1401
+
private static Events instance;
///
@@ -49,6 +66,11 @@ public sealed class Events : Plugin
public override void OnEnabled()
{
instance = this;
+
+ IsProfilerEnabled = Config.EnableEventProfiler;
+ ProfilerThreshold = Config.EventProfilerThreshold;
+ AllocationThreshold = Config.EventProfilerAllocationThreshold;
+
base.OnEnabled();
Stopwatch watch = Stopwatch.StartNew();
@@ -104,6 +126,8 @@ public override void OnEnabled()
///
public override void OnDisabled()
{
+ IsProfilerEnabled = false;
+
base.OnDisabled();
Unpatch();
diff --git a/EXILED/Exiled.Events/Features/Event{T}.cs b/EXILED/Exiled.Events/Features/Event{T}.cs
index 2d6252b91e..2ba0c6c718 100644
--- a/EXILED/Exiled.Events/Features/Event{T}.cs
+++ b/EXILED/Exiled.Events/Features/Event{T}.cs
@@ -9,10 +9,13 @@ namespace Exiled.Events.Features
{
using System;
using System.Collections.Generic;
+ using System.Diagnostics;
using System.Linq;
+ using System.Reflection;
using Exiled.API.Features;
using Exiled.Events.EventArgs.Interfaces;
+
using MEC;
///
@@ -233,6 +236,15 @@ internal void BlendedInvoke(T arg)
for (int i = 0; i < count; i++)
{
+ long startTick = 0;
+ long startBytes = 0;
+
+ if (Events.IsProfilerEnabled)
+ {
+ startTick = Stopwatch.GetTimestamp();
+ startBytes = GC.GetTotalMemory(false);
+ }
+
if (eventIndex < innerEvent.Length && (asyncEventIndex >= innerAsyncEvent.Length || innerEvent[eventIndex].priority >= innerAsyncEvent[asyncEventIndex].priority))
{
try
@@ -244,6 +256,17 @@ internal void BlendedInvoke(T arg)
Log.Error($"Method \"{innerEvent[eventIndex].handler.Method.Name}\" of the class \"{innerEvent[eventIndex].handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}");
}
+ if (Events.IsProfilerEnabled)
+ {
+ double elapsedMs = (Stopwatch.GetTimestamp() - startTick) * 1000.0 / Stopwatch.Frequency;
+ long allocatedBytes = GC.GetTotalMemory(false) - startBytes;
+
+ if (elapsedMs > Events.ProfilerThreshold || allocatedBytes > Events.AllocationThreshold)
+ {
+ LogWarning(innerEvent[eventIndex].handler, elapsedMs, allocatedBytes);
+ }
+ }
+
eventIndex++;
}
else
@@ -268,6 +291,15 @@ internal void InvokeNormal(T arg)
Registration[] innerEvent = this.innerEvent.ToArray();
foreach (Registration registration in innerEvent)
{
+ long startTick = 0;
+ long startBytes = 0;
+
+ if (Events.IsProfilerEnabled)
+ {
+ startTick = Stopwatch.GetTimestamp();
+ startBytes = GC.GetTotalMemory(false);
+ }
+
try
{
registration.handler(arg);
@@ -276,6 +308,17 @@ internal void InvokeNormal(T arg)
{
Log.Error($"Method \"{registration.handler.Method.Name}\" of the class \"{registration.handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}");
}
+
+ if (Events.IsProfilerEnabled)
+ {
+ double elapsedMs = (Stopwatch.GetTimestamp() - startTick) * 1000.0 / Stopwatch.Frequency;
+ long allocatedBytes = GC.GetTotalMemory(false) - startBytes;
+
+ if (elapsedMs > Events.ProfilerThreshold || allocatedBytes > Events.AllocationThreshold)
+ {
+ LogWarning(registration.handler, elapsedMs, allocatedBytes);
+ }
+ }
}
}
@@ -295,5 +338,44 @@ internal void InvokeAsync(T arg)
}
}
}
+
+ private static void LogWarning(Delegate handler, double ms, long bytes)
+ {
+ MethodInfo method = handler.Method;
+ Type targetType = handler.Target?.GetType() ?? method.DeclaringType;
+
+ string pluginName = targetType?.Assembly.GetName().Name;
+ string className = targetType?.Name;
+ string eventName = typeof(T).Name.Replace("EventArgs", string.Empty);
+
+ string[] sizes = { "B", "KB", "MB", "GB" };
+ int order = 0;
+ double len = bytes;
+ while (len >= 1024 && order < sizes.Length - 1)
+ {
+ order++;
+ len /= 1024;
+ }
+
+ string ramResult = $"{len:0.##} {sizes[order]}";
+
+ string triggerPrefix = string.Empty;
+ switch (ms > Events.ProfilerThreshold, bytes > Events.AllocationThreshold)
+ {
+ case (true, false):
+ triggerPrefix = "[CPU]";
+ break;
+
+ case (false, true):
+ triggerPrefix = "[MEMORY]";
+ break;
+
+ case (true, true):
+ triggerPrefix = "[CPU]/[MEMORY]";
+ break;
+ }
+
+ Log.Warn($"[Event Profiler] {triggerPrefix} '{eventName}' | Time: {ms:F2}ms | RAM: {ramResult} | Plugin: {pluginName} | Class: {className} | Method: {method.Name}");
+ }
}
}
From 1141b25de4513856e822e82ab603560bcd937ef2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mustafa=20SAVA=C5=9E?=
Date: Wed, 28 Jan 2026 00:11:52 +0300
Subject: [PATCH 2/7] In some cases, the GC is the one who is guilty
---
EXILED/Exiled.Events/Features/Event{T}.cs | 46 ++++++++++++++++++-----
1 file changed, 36 insertions(+), 10 deletions(-)
diff --git a/EXILED/Exiled.Events/Features/Event{T}.cs b/EXILED/Exiled.Events/Features/Event{T}.cs
index 2ba0c6c718..cbb2a72bc1 100644
--- a/EXILED/Exiled.Events/Features/Event{T}.cs
+++ b/EXILED/Exiled.Events/Features/Event{T}.cs
@@ -238,11 +238,13 @@ internal void BlendedInvoke(T arg)
{
long startTick = 0;
long startBytes = 0;
+ int startGcCount = 0;
if (Events.IsProfilerEnabled)
{
startTick = Stopwatch.GetTimestamp();
startBytes = GC.GetTotalMemory(false);
+ startGcCount = GC.CollectionCount(0);
}
if (eventIndex < innerEvent.Length && (asyncEventIndex >= innerAsyncEvent.Length || innerEvent[eventIndex].priority >= innerAsyncEvent[asyncEventIndex].priority))
@@ -260,10 +262,11 @@ internal void BlendedInvoke(T arg)
{
double elapsedMs = (Stopwatch.GetTimestamp() - startTick) * 1000.0 / Stopwatch.Frequency;
long allocatedBytes = GC.GetTotalMemory(false) - startBytes;
+ bool gcRan = GC.CollectionCount(0) > startGcCount;
if (elapsedMs > Events.ProfilerThreshold || allocatedBytes > Events.AllocationThreshold)
{
- LogWarning(innerEvent[eventIndex].handler, elapsedMs, allocatedBytes);
+ LogWarning(innerEvent[eventIndex].handler, elapsedMs, allocatedBytes, gcRan);
}
}
@@ -293,11 +296,13 @@ internal void InvokeNormal(T arg)
{
long startTick = 0;
long startBytes = 0;
+ int startGcCount = 0;
if (Events.IsProfilerEnabled)
{
startTick = Stopwatch.GetTimestamp();
startBytes = GC.GetTotalMemory(false);
+ startGcCount = GC.CollectionCount(0);
}
try
@@ -313,10 +318,11 @@ internal void InvokeNormal(T arg)
{
double elapsedMs = (Stopwatch.GetTimestamp() - startTick) * 1000.0 / Stopwatch.Frequency;
long allocatedBytes = GC.GetTotalMemory(false) - startBytes;
+ bool gcRan = GC.CollectionCount(0) > startGcCount;
if (elapsedMs > Events.ProfilerThreshold || allocatedBytes > Events.AllocationThreshold)
{
- LogWarning(registration.handler, elapsedMs, allocatedBytes);
+ LogWarning(registration.handler, elapsedMs, allocatedBytes, gcRan);
}
}
}
@@ -339,7 +345,7 @@ internal void InvokeAsync(T arg)
}
}
- private static void LogWarning(Delegate handler, double ms, long bytes)
+ private static void LogWarning(Delegate handler, double ms, long bytes, bool gcRan)
{
MethodInfo method = handler.Method;
Type targetType = handler.Target?.GetType() ?? method.DeclaringType;
@@ -348,6 +354,9 @@ private static void LogWarning(Delegate handler, double ms, long bytes)
string className = targetType?.Name;
string eventName = typeof(T).Name.Replace("EventArgs", string.Empty);
+ if (bytes < 0)
+ bytes = 0;
+
string[] sizes = { "B", "KB", "MB", "GB" };
int order = 0;
double len = bytes;
@@ -360,22 +369,39 @@ private static void LogWarning(Delegate handler, double ms, long bytes)
string ramResult = $"{len:0.##} {sizes[order]}";
string triggerPrefix = string.Empty;
- switch (ms > Events.ProfilerThreshold, bytes > Events.AllocationThreshold)
+
+ switch (gcRan, ms > Events.ProfilerThreshold, bytes > Events.AllocationThreshold)
{
- case (true, false):
- triggerPrefix = "[CPU]";
+ case (true, true, true):
+ triggerPrefix = "[GC] [CPU]/[MEMORY]";
break;
- case (false, true):
- triggerPrefix = "[MEMORY]";
+ case (true, true, false):
+ triggerPrefix = "[GC] [CPU]";
+ break;
+
+ case (true, false, true):
+ triggerPrefix = "[GC] [MEMORY]";
+ break;
+
+ case (true, false, false):
+ triggerPrefix = "[GC]";
break;
- case (true, true):
+ case (false, true, true):
triggerPrefix = "[CPU]/[MEMORY]";
break;
+
+ case (false, true, false):
+ triggerPrefix = "[CPU]";
+ break;
+
+ case (false, false, true):
+ triggerPrefix = "[MEMORY]";
+ break;
}
Log.Warn($"[Event Profiler] {triggerPrefix} '{eventName}' | Time: {ms:F2}ms | RAM: {ramResult} | Plugin: {pluginName} | Class: {className} | Method: {method.Name}");
}
}
-}
+}
\ No newline at end of file
From 4bf5cbce6d9eb66ca08805c20f40f13ce33d0512 Mon Sep 17 00:00:00 2001
From: MS-crew <100300664+MS-crew@users.noreply.github.com>
Date: Wed, 28 Jan 2026 01:56:52 +0300
Subject: [PATCH 3/7] Update Event{T}.cs
---
EXILED/Exiled.Events/Features/Event{T}.cs | 4 ----
1 file changed, 4 deletions(-)
diff --git a/EXILED/Exiled.Events/Features/Event{T}.cs b/EXILED/Exiled.Events/Features/Event{T}.cs
index cbb2a72bc1..f68f352058 100644
--- a/EXILED/Exiled.Events/Features/Event{T}.cs
+++ b/EXILED/Exiled.Events/Features/Event{T}.cs
@@ -384,10 +384,6 @@ private static void LogWarning(Delegate handler, double ms, long bytes, bool gcR
triggerPrefix = "[GC] [MEMORY]";
break;
- case (true, false, false):
- triggerPrefix = "[GC]";
- break;
-
case (false, true, true):
triggerPrefix = "[CPU]/[MEMORY]";
break;
From ebf8a1f28f1bdc96fc9b331a57de4e8d02cbbdb3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mustafa=20SAVA=C5=9E?=
Date: Thu, 29 Jan 2026 19:34:30 +0300
Subject: [PATCH 4/7] Perf update
---
EXILED/Exiled.Events/Config.cs | 4 +-
EXILED/Exiled.Events/Events.cs | 24 --
EXILED/Exiled.Events/Features/Event{T}.cs | 101 -------
.../Patches/Generic/EventProfiler.cs | 256 ++++++++++++++++++
4 files changed, 258 insertions(+), 127 deletions(-)
create mode 100644 EXILED/Exiled.Events/Patches/Generic/EventProfiler.cs
diff --git a/EXILED/Exiled.Events/Config.cs b/EXILED/Exiled.Events/Config.cs
index 46ef02f141..79b78e81a7 100644
--- a/EXILED/Exiled.Events/Config.cs
+++ b/EXILED/Exiled.Events/Config.cs
@@ -115,12 +115,12 @@ public sealed class Config : IConfig
/// Gets or sets a value indicating whether the Event Profiler is enabled.
///
[Description("Indicates whether to enable the event profiler. This detects and logs plugins that cause lag by taking too long to handle events.")]
- public bool EnableEventProfiler { get; set; } = false;
+ public bool EventProfiler { get; set; } = false;
///
/// Gets or sets the threshold in milliseconds for the Event Profiler.
///
- [Description("The threshold in milliseconds. If a plugin takes longer than this to handle an event, a warning will be logged.")]
+ [Description("The threshold in milliseconds. If a plugin takes longer than this to handle an event, a warning will be logged.(For 60 fps 1 frame time is 16.6 ms)")]
public double EventProfilerThreshold { get; set; } = 16.6;
///
diff --git a/EXILED/Exiled.Events/Events.cs b/EXILED/Exiled.Events/Events.cs
index 15e7732fa5..57bb97c6a3 100644
--- a/EXILED/Exiled.Events/Events.cs
+++ b/EXILED/Exiled.Events/Events.cs
@@ -15,7 +15,6 @@ namespace Exiled.Events
using CentralAuth;
using Exiled.API.Features.Core.UserSettings;
using Exiled.Events.Features;
- using HarmonyLib;
using InventorySystem.Items.Pickups;
using InventorySystem.Items.Usables;
using PlayerRoles.Ragdolls;
@@ -30,23 +29,6 @@ namespace Exiled.Events
///
public sealed class Events : Plugin
{
-#pragma warning disable SA1401
- ///
- /// Indicates whether the event profiler is enabled.
- ///
- internal static bool IsProfilerEnabled;
-
- ///
- /// Execution time threshold (ms) for profiler warnings.
- ///
- internal static long AllocationThreshold;
-
- ///
- /// Allocation threshold (bytes) for profiler warnings.
- ///
- internal static double ProfilerThreshold;
-#pragma warning restore SA1401
-
private static Events instance;
///
@@ -67,10 +49,6 @@ public override void OnEnabled()
{
instance = this;
- IsProfilerEnabled = Config.EnableEventProfiler;
- ProfilerThreshold = Config.EventProfilerThreshold;
- AllocationThreshold = Config.EventProfilerAllocationThreshold;
-
base.OnEnabled();
Stopwatch watch = Stopwatch.StartNew();
@@ -126,8 +104,6 @@ public override void OnEnabled()
///
public override void OnDisabled()
{
- IsProfilerEnabled = false;
-
base.OnDisabled();
Unpatch();
diff --git a/EXILED/Exiled.Events/Features/Event{T}.cs b/EXILED/Exiled.Events/Features/Event{T}.cs
index f68f352058..0f489ee4f0 100644
--- a/EXILED/Exiled.Events/Features/Event{T}.cs
+++ b/EXILED/Exiled.Events/Features/Event{T}.cs
@@ -236,17 +236,6 @@ internal void BlendedInvoke(T arg)
for (int i = 0; i < count; i++)
{
- long startTick = 0;
- long startBytes = 0;
- int startGcCount = 0;
-
- if (Events.IsProfilerEnabled)
- {
- startTick = Stopwatch.GetTimestamp();
- startBytes = GC.GetTotalMemory(false);
- startGcCount = GC.CollectionCount(0);
- }
-
if (eventIndex < innerEvent.Length && (asyncEventIndex >= innerAsyncEvent.Length || innerEvent[eventIndex].priority >= innerAsyncEvent[asyncEventIndex].priority))
{
try
@@ -258,18 +247,6 @@ internal void BlendedInvoke(T arg)
Log.Error($"Method \"{innerEvent[eventIndex].handler.Method.Name}\" of the class \"{innerEvent[eventIndex].handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}");
}
- if (Events.IsProfilerEnabled)
- {
- double elapsedMs = (Stopwatch.GetTimestamp() - startTick) * 1000.0 / Stopwatch.Frequency;
- long allocatedBytes = GC.GetTotalMemory(false) - startBytes;
- bool gcRan = GC.CollectionCount(0) > startGcCount;
-
- if (elapsedMs > Events.ProfilerThreshold || allocatedBytes > Events.AllocationThreshold)
- {
- LogWarning(innerEvent[eventIndex].handler, elapsedMs, allocatedBytes, gcRan);
- }
- }
-
eventIndex++;
}
else
@@ -294,17 +271,6 @@ internal void InvokeNormal(T arg)
Registration[] innerEvent = this.innerEvent.ToArray();
foreach (Registration registration in innerEvent)
{
- long startTick = 0;
- long startBytes = 0;
- int startGcCount = 0;
-
- if (Events.IsProfilerEnabled)
- {
- startTick = Stopwatch.GetTimestamp();
- startBytes = GC.GetTotalMemory(false);
- startGcCount = GC.CollectionCount(0);
- }
-
try
{
registration.handler(arg);
@@ -313,18 +279,6 @@ internal void InvokeNormal(T arg)
{
Log.Error($"Method \"{registration.handler.Method.Name}\" of the class \"{registration.handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}");
}
-
- if (Events.IsProfilerEnabled)
- {
- double elapsedMs = (Stopwatch.GetTimestamp() - startTick) * 1000.0 / Stopwatch.Frequency;
- long allocatedBytes = GC.GetTotalMemory(false) - startBytes;
- bool gcRan = GC.CollectionCount(0) > startGcCount;
-
- if (elapsedMs > Events.ProfilerThreshold || allocatedBytes > Events.AllocationThreshold)
- {
- LogWarning(registration.handler, elapsedMs, allocatedBytes, gcRan);
- }
- }
}
}
@@ -344,60 +298,5 @@ internal void InvokeAsync(T arg)
}
}
}
-
- private static void LogWarning(Delegate handler, double ms, long bytes, bool gcRan)
- {
- MethodInfo method = handler.Method;
- Type targetType = handler.Target?.GetType() ?? method.DeclaringType;
-
- string pluginName = targetType?.Assembly.GetName().Name;
- string className = targetType?.Name;
- string eventName = typeof(T).Name.Replace("EventArgs", string.Empty);
-
- if (bytes < 0)
- bytes = 0;
-
- string[] sizes = { "B", "KB", "MB", "GB" };
- int order = 0;
- double len = bytes;
- while (len >= 1024 && order < sizes.Length - 1)
- {
- order++;
- len /= 1024;
- }
-
- string ramResult = $"{len:0.##} {sizes[order]}";
-
- string triggerPrefix = string.Empty;
-
- switch (gcRan, ms > Events.ProfilerThreshold, bytes > Events.AllocationThreshold)
- {
- case (true, true, true):
- triggerPrefix = "[GC] [CPU]/[MEMORY]";
- break;
-
- case (true, true, false):
- triggerPrefix = "[GC] [CPU]";
- break;
-
- case (true, false, true):
- triggerPrefix = "[GC] [MEMORY]";
- break;
-
- case (false, true, true):
- triggerPrefix = "[CPU]/[MEMORY]";
- break;
-
- case (false, true, false):
- triggerPrefix = "[CPU]";
- break;
-
- case (false, false, true):
- triggerPrefix = "[MEMORY]";
- break;
- }
-
- Log.Warn($"[Event Profiler] {triggerPrefix} '{eventName}' | Time: {ms:F2}ms | RAM: {ramResult} | Plugin: {pluginName} | Class: {className} | Method: {method.Name}");
- }
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Generic/EventProfiler.cs b/EXILED/Exiled.Events/Patches/Generic/EventProfiler.cs
new file mode 100644
index 0000000000..1325406cc7
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Generic/EventProfiler.cs
@@ -0,0 +1,256 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) ExMod Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Generic
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Reflection;
+ using System.Reflection.Emit;
+ using System.Runtime.InteropServices;
+
+ using Exiled.API.Features;
+ using Exiled.API.Features.Pools;
+ using Exiled.Events.Features;
+
+ using HarmonyLib;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patch for adding profiler to .
+ ///
+ [HarmonyPatch]
+ internal static class EventProfiler
+ {
+ private static float profilerThreshold;
+
+ private static long allocationThreshold;
+
+ private static Dictionary handlerPropCache;
+
+ private static bool Prepare()
+ {
+ Config config = Exiled.Events.Events.Instance?.Config;
+
+ if (config == null || !config.EventProfiler)
+ return false;
+
+ handlerPropCache = new Dictionary();
+ profilerThreshold = (float)config.EventProfilerThreshold;
+ allocationThreshold = config.EventProfilerAllocationThreshold;
+
+ return true;
+ }
+
+ private static IEnumerable TargetMethods()
+ {
+ Assembly exiledAssembly = typeof(Exiled.Events.Events).Assembly;
+
+ foreach (Type type in exiledAssembly.GetExportedTypes())
+ {
+ foreach (PropertyInfo property in type.GetProperties(BindingFlags.Static | BindingFlags.Public))
+ {
+ Type currentType = property.PropertyType;
+
+ while (currentType != null && currentType != typeof(object))
+ {
+ // if (currentType == typeof(Event) || (currentType.IsGenericType && currentType.GetGenericTypeDefinition() == typeof(Event<>)))
+ if (currentType.IsGenericType && currentType.GetGenericTypeDefinition() == typeof(Event<>))
+ {
+ MethodInfo method = property.PropertyType.GetMethod("BlendedInvoke", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy);
+
+ if (method != null)
+ yield return method;
+
+ break;
+ }
+
+ currentType = currentType.BaseType;
+ }
+ }
+ }
+ }
+
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator, MethodBase originalMethod)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ bool isGenericEvent = originalMethod.DeclaringType.IsGenericType;
+
+ List