diff --git a/GameData/KSPCommunityFixes/Settings.cfg b/GameData/KSPCommunityFixes/Settings.cfg
index 006f839..861c30c 100644
--- a/GameData/KSPCommunityFixes/Settings.cfg
+++ b/GameData/KSPCommunityFixes/Settings.cfg
@@ -547,6 +547,11 @@ KSP_COMMUNITY_FIXES
// the examples and documentation in the patch source.
BaseFieldListUseFieldHost = true
+ // Adds Unity Profiler markers to EventData.Fire() methods, making game event dispatch and
+ // individual subscriber callbacks visible in the Unity Profiler. Useful for identifying
+ // expensive event handlers.
+ EventProfilerMarkers = false
+
// ##########################
// Localization tools
// ##########################
diff --git a/KSPCommunityFixes/BasePatch.cs b/KSPCommunityFixes/BasePatch.cs
index 8b361c2..0bf9bb2 100644
--- a/KSPCommunityFixes/BasePatch.cs
+++ b/KSPCommunityFixes/BasePatch.cs
@@ -191,6 +191,11 @@ protected void AddPatch(PatchType patchType, Type patchedMethodType, string patc
patches.Add(new PatchInfo(patchType, AccessTools.Method(patchedMethodType, patchedMethodName, patchedMethodArgs), patchMethodName, patchPriority));
}
+ protected void AddPatch(PatchInfo patch)
+ {
+ patches.Add(patch);
+ }
+
public bool IsVersionValid => KSPCommunityFixes.KspVersion >= VersionMin && KSPCommunityFixes.KspVersion <= VersionMax;
private bool ApplyHarmonyPatch()
@@ -209,10 +214,8 @@ private bool ApplyHarmonyPatch()
continue;
}
- if (patch.patchMethodName == null)
- patch.patchMethodName = patch.patchedMethod.DeclaringType.Name + "_" + patch.patchedMethod.Name + "_" + patch.patchType;
-
- patch.patchMethod = AccessTools.Method(GetType(), patch.patchMethodName);
+ patch.patchMethodName ??= patch.patchedMethod.DeclaringType.Name + "_" + patch.patchedMethod.Name + "_" + patch.patchType;
+ patch.patchMethod ??= AccessTools.Method(GetType(), patch.patchMethodName);
if (patch.patchMethod == null)
{
diff --git a/KSPCommunityFixes/KSPCommunityFixes.csproj b/KSPCommunityFixes/KSPCommunityFixes.csproj
index 17604dc..a96fdd5 100644
--- a/KSPCommunityFixes/KSPCommunityFixes.csproj
+++ b/KSPCommunityFixes/KSPCommunityFixes.csproj
@@ -222,6 +222,7 @@
+
diff --git a/KSPCommunityFixes/Modding/EventProfilerMarkers.cs b/KSPCommunityFixes/Modding/EventProfilerMarkers.cs
new file mode 100644
index 0000000..0ffa5d5
--- /dev/null
+++ b/KSPCommunityFixes/Modding/EventProfilerMarkers.cs
@@ -0,0 +1,595 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using Contracts;
+using Expansions.Missions;
+using Expansions.Serenity;
+using Expansions.Serenity.DeployedScience.Runtime;
+using FinePrint;
+using HarmonyLib;
+using KSP.UI.Screens;
+using KSP.UI.Screens.Settings.Controls;
+using KSPAchievements;
+using Unity.Profiling;
+using Upgradeables;
+using UnityEngine;
+using UnityEngine.Profiling;
+using static GameEvents;
+using System.Linq.Expressions;
+using System.Linq;
+using System.Reflection.Emit;
+
+namespace KSPCommunityFixes.Modding
+{
+ public class EventProfilerMarkers : BasePatch
+ {
+ protected override void ApplyPatches()
+ {
+ AddPatch(PatchType.Override, typeof(EventVoid), nameof(EventVoid.Fire), nameof(EventVoid_Fire_Override));
+
+ foreach (var type in ValueTypeEventData1)
+ AddPatch(CreatePatch(type));
+
+ foreach (var type in ValueTypeEventData2)
+ AddPatch(CreatePatch(type));
+
+ foreach (var type in ValueTypeEventData3)
+ AddPatch(CreatePatch(type));
+
+ foreach (var type in ValueTypeEventData4)
+ AddPatch(CreatePatch(type));
+ }
+
+ #region CreatePatchOverride
+ static readonly ModuleBuilder PatchModule;
+
+ static EventProfilerMarkers()
+ {
+ var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(
+ new AssemblyName("KSPCommunityFixes_EventProfilerPatches"),
+ AssemblyBuilderAccess.Run);
+ PatchModule = assemblyBuilder.DefineDynamicModule("PatchModule");
+ }
+
+ static int patchCounter;
+
+ static string PatchName(string baseName, Type[] tparams)
+ {
+ return baseName + "<" + string.Join(", ", tparams.Select(t => t.Name)) + ">";
+ }
+
+ static PatchInfo CreatePatch(Type type)
+ {
+ var tparams = type.GenericTypeArguments;
+ var instance = Expression.Parameter(type, "__instance");
+
+ var fparams = new ParameterExpression[tparams.Length + 1];
+ fparams[0] = instance;
+ for (int i = 0; i < tparams.Length; ++i)
+ fparams[i + 1] = Expression.Parameter(tparams[i], $"data{i}");
+
+ MethodInfo generic = tparams.Length switch
+ {
+ 1 => SymbolExtensions.GetMethodInfo(() => EventData1_Fire_Override