From 7d88fe082f5a93df65f13aba62813afb24583409 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 14:52:41 -0500 Subject: [PATCH 01/16] update BasePlayer_PlayerInit_Patch --- .../BasePlayer_PlayerInit_Patch.cs | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/BasePlayer_PlayerInit_Patch.cs b/src/RustServerMetrics/HarmonyPatches/BasePlayer_PlayerInit_Patch.cs index 9a28850..6e2c682 100644 --- a/src/RustServerMetrics/HarmonyPatches/BasePlayer_PlayerInit_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/BasePlayer_PlayerInit_Patch.cs @@ -1,7 +1,5 @@ using HarmonyLib; -using System; using System.Collections.Generic; -using System.Reflection; using System.Reflection.Emit; // ReSharper disable InconsistentNaming @@ -12,26 +10,24 @@ namespace RustServerMetrics.HarmonyPatches; public class BasePlayer_PlayerInit_Patch { [HarmonyTranspiler] - public static IEnumerable Transpile(IEnumerable originalInstructions) + public static IEnumerable Transpiler(IEnumerable instructions) { - var retList = new List(originalInstructions); - - var fieldInfo = typeof(SingletonComponent) - .GetField(nameof(SingletonComponent.Instance), BindingFlags.Static | BindingFlags.Public); - - var methodInfo = typeof(MetricsLogger) - .GetMethod(nameof(MetricsLogger.OnPlayerInit), BindingFlags.Instance | BindingFlags.NonPublic); - - var idx = retList.FindIndex(x => x.opcode == OpCodes.Call && x.operand is MethodInfo methodInfo1 && methodInfo1.DeclaringType.Name == "EACServer" && methodInfo1.Name == "OnStartLoading"); - - if (idx < 0) throw new Exception("Failed to find the insertion index for PlayerInit"); - - retList.InsertRange(idx, [ - new CodeInstruction(OpCodes.Ldsfld, fieldInfo), - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Call, methodInfo) - ]); - - return retList; + var matcher = new CodeMatcher(instructions) + .MatchEndForward( + new CodeMatch(OpCodes.Call, + AccessTools.Method(typeof(EACServer), + nameof(EACServer.OnStartLoading)))) + .ThrowIfInvalid("Failed to find insertion point for BasePlayer.PlayerInit") + .Advance(1) + .InsertAndAdvance( + new CodeInstruction(OpCodes.Ldsfld, + AccessTools.Field(typeof(SingletonComponent), + nameof(SingletonComponent.Instance))), + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Call, + AccessTools.Method(typeof(MetricsLogger), + nameof(MetricsLogger.OnPlayerInit)))); + + return matcher.Instructions(); } } \ No newline at end of file From c5b019a261551a03e81a1e75f58993ecdaa06f86 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 14:58:50 -0500 Subject: [PATCH 02/16] update BasePlayer_OnDisconnected_Patch --- .../BasePlayer_OnDisconnected_Patch.cs | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/BasePlayer_OnDisconnected_Patch.cs b/src/RustServerMetrics/HarmonyPatches/BasePlayer_OnDisconnected_Patch.cs index 1e45cf0..bb2d0a8 100644 --- a/src/RustServerMetrics/HarmonyPatches/BasePlayer_OnDisconnected_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/BasePlayer_OnDisconnected_Patch.cs @@ -1,6 +1,5 @@ using HarmonyLib; using System.Collections.Generic; -using System.Reflection; using System.Reflection.Emit; // ReSharper disable InconsistentNaming @@ -11,22 +10,19 @@ namespace RustServerMetrics.HarmonyPatches; public class BasePlayer_OnDisconnected_Patch { [HarmonyTranspiler] - public static IEnumerable Transpile(IEnumerable originalInstructions) + public static IEnumerable Transpiler(IEnumerable instructions) { - var retList = new List(originalInstructions); + var matcher = new CodeMatcher(instructions) + .Start() + .InsertAndAdvance( + new CodeInstruction(OpCodes.Ldsfld, + AccessTools.Field(typeof(SingletonComponent), + nameof(SingletonComponent.Instance))), + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Call, + AccessTools.Method(typeof(MetricsLogger), + nameof(MetricsLogger.OnPlayerDisconnected)))); - var fieldInfo = typeof(SingletonComponent) - .GetField(nameof(SingletonComponent.Instance), BindingFlags.Static | BindingFlags.Public); - - var methodInfo = typeof(MetricsLogger) - .GetMethod(nameof(MetricsLogger.OnPlayerDisconnected), BindingFlags.Instance | BindingFlags.NonPublic); - - retList.InsertRange(0, [ - new CodeInstruction(OpCodes.Ldsfld, fieldInfo), - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Call, methodInfo) - ]); - - return retList; + return matcher.Instructions(); } } \ No newline at end of file From 4f810af66c56b23271b00022aec14e70f3310754 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 15:42:52 -0500 Subject: [PATCH 03/16] update BasePlayer_PerformanceReport_Patch --- .../BasePlayer_PerformanceReport_Patch.cs | 56 ++++++++----------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/BasePlayer_PerformanceReport_Patch.cs b/src/RustServerMetrics/HarmonyPatches/BasePlayer_PerformanceReport_Patch.cs index 5fb7eeb..1a4cdb5 100644 --- a/src/RustServerMetrics/HarmonyPatches/BasePlayer_PerformanceReport_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/BasePlayer_PerformanceReport_Patch.cs @@ -1,10 +1,10 @@ using HarmonyLib; using System; using System.Collections.Generic; -using System.Linq; using System.Reflection.Emit; // ReSharper disable InconsistentNaming +// ReSharper disable PossibleMultipleEnumeration namespace RustServerMetrics.HarmonyPatches; @@ -12,45 +12,37 @@ namespace RustServerMetrics.HarmonyPatches; public class BasePlayer_PerformanceReport_Patch { [HarmonyTranspiler] - public static IEnumerable Transpile(IEnumerable originalInstructions, - ILGenerator ilGenerator) + public static IEnumerable Transpiler(IEnumerable instructions, + ILGenerator ilGenerator) { - var instructionsList = originalInstructions.ToList(); - var jumpLabel = ilGenerator.DefineLabel(); - - CodeMatch[] needle = - [ - new(OpCodes.Ldloc_0), - new(OpCodes.Ldstr, "legacy"), - new(OpCodes.Call, AccessTools.Method(typeof(String), "op_Equality")), - new(OpCodes.Brfalse) - ]; - - CodeInstruction[] injection = - [ - new(OpCodes.Ldsfld, AccessTools.Field(typeof(SingletonComponent), nameof(SingletonComponent.Instance))), - new(OpCodes.Ldloc_1), - new(OpCodes.Call, AccessTools.Method(typeof(MetricsLogger), nameof(MetricsLogger.OnClientPerformanceReport))), - new(OpCodes.Brtrue, jumpLabel) - ]; - try { - var codeMatcher = new CodeMatcher(instructionsList); - - codeMatcher.MatchEndForward(needle) - .ThrowIfInvalid("Unable to find the expected injection point") - .Advance(1) - .InsertAndAdvance(injection) - .End() - .AddLabels([jumpLabel]); + var matcher = new CodeMatcher(instructions, ilGenerator) + .End() + .CreateLabel(out var retLabel) + .MatchEndBackwards( + new CodeMatch(OpCodes.Ldloc_0), + new CodeMatch(OpCodes.Ldstr, "legacy"), + new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(string), "op_Equality")), + new CodeMatch(OpCodes.Brfalse)) + .ThrowIfInvalid("Unable to find the expected injection point") + .Advance(1) + .InsertAndAdvance( + new CodeInstruction(OpCodes.Ldsfld, + AccessTools.Field(typeof(SingletonComponent), + nameof(SingletonComponent.Instance))), + new CodeInstruction(OpCodes.Ldloc_1), + new CodeInstruction(OpCodes.Call, + AccessTools.Method(typeof(MetricsLogger), + nameof(MetricsLogger.OnClientPerformanceReport))), + new CodeInstruction(OpCodes.Brtrue, retLabel)); - return codeMatcher.Instructions(); + return matcher.Instructions(); } catch (Exception e) { UnityEngine.Debug.LogError($"[ServerMetrics] {nameof(BasePlayer_PerformanceReport_Patch)}: " + e.Message); - return instructionsList; + return instructions; } } } \ No newline at end of file From 00c91d8efefe3cbbc8e6c2f6219bd1562481d7b5 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 15:45:04 -0500 Subject: [PATCH 04/16] add missing try-catch blocks --- .../BasePlayer_OnDisconnected_Patch.cs | 17 +++++-- .../BasePlayer_PlayerInit_Patch.cs | 45 ++++++++++++------- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/BasePlayer_OnDisconnected_Patch.cs b/src/RustServerMetrics/HarmonyPatches/BasePlayer_OnDisconnected_Patch.cs index bb2d0a8..bfa3559 100644 --- a/src/RustServerMetrics/HarmonyPatches/BasePlayer_OnDisconnected_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/BasePlayer_OnDisconnected_Patch.cs @@ -1,8 +1,11 @@ -using HarmonyLib; +using System; +using HarmonyLib; using System.Collections.Generic; using System.Reflection.Emit; +using UnityEngine; // ReSharper disable InconsistentNaming +// ReSharper disable PossibleMultipleEnumeration namespace RustServerMetrics.HarmonyPatches; @@ -12,7 +15,9 @@ public class BasePlayer_OnDisconnected_Patch [HarmonyTranspiler] public static IEnumerable Transpiler(IEnumerable instructions) { - var matcher = new CodeMatcher(instructions) + try + { + var matcher = new CodeMatcher(instructions) .Start() .InsertAndAdvance( new CodeInstruction(OpCodes.Ldsfld, @@ -23,6 +28,12 @@ public static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable instructions) { - var matcher = new CodeMatcher(instructions) - .MatchEndForward( - new CodeMatch(OpCodes.Call, - AccessTools.Method(typeof(EACServer), - nameof(EACServer.OnStartLoading)))) - .ThrowIfInvalid("Failed to find insertion point for BasePlayer.PlayerInit") - .Advance(1) - .InsertAndAdvance( - new CodeInstruction(OpCodes.Ldsfld, - AccessTools.Field(typeof(SingletonComponent), - nameof(SingletonComponent.Instance))), - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(MetricsLogger), - nameof(MetricsLogger.OnPlayerInit)))); + try + { + var matcher = new CodeMatcher(instructions) + .MatchEndForward( + new CodeMatch(OpCodes.Call, + AccessTools.Method(typeof(EACServer), + nameof(EACServer.OnStartLoading)))) + .ThrowIfInvalid("Failed to find insertion point for BasePlayer.PlayerInit") + .Advance(1) + .InsertAndAdvance( + new CodeInstruction(OpCodes.Ldsfld, + AccessTools.Field(typeof(SingletonComponent), + nameof(SingletonComponent.Instance))), + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Call, + AccessTools.Method(typeof(MetricsLogger), + nameof(MetricsLogger.OnPlayerInit)))); - return matcher.Instructions(); + return matcher.Instructions(); + } + catch (Exception e) + { + Debug.LogError($"[ServerMetrics] {nameof(BasePlayer_PlayerInit_Patch)}: " + e.Message); + return instructions; + } } } \ No newline at end of file From 28e24ac40ea3661bb0d08011d2c2008766afde80 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 15:49:35 -0500 Subject: [PATCH 05/16] update Bootstrap_StartServer_Patch --- .../Bootstrap_StartServer_Patch.cs | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/Bootstrap_StartServer_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Bootstrap_StartServer_Patch.cs index c90c957..04d2e3f 100644 --- a/src/RustServerMetrics/HarmonyPatches/Bootstrap_StartServer_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Bootstrap_StartServer_Patch.cs @@ -1,9 +1,11 @@ -using HarmonyLib; +using System; +using HarmonyLib; using System.Collections.Generic; -using System.Reflection; using System.Reflection.Emit; +using UnityEngine; // ReSharper disable InconsistentNaming +// ReSharper disable PossibleMultipleEnumeration namespace RustServerMetrics.HarmonyPatches; @@ -11,17 +13,22 @@ namespace RustServerMetrics.HarmonyPatches; public class Bootstrap_StartServer_Patch { [HarmonyTranspiler] - public static IEnumerable Transpile(IEnumerable originalInstructions) + public static IEnumerable Transpiler(IEnumerable instructions) { - var retList = new List(originalInstructions); - - var methodInfo = typeof(MetricsLogger) - .GetMethod(nameof(MetricsLogger.Initialize), BindingFlags.Static | BindingFlags.NonPublic); - - retList.InsertRange(0, [ - new CodeInstruction(OpCodes.Call, methodInfo) - ]); - - return retList; + try + { + var matcher = new CodeMatcher(instructions) + .InsertAndAdvance( + new CodeInstruction(OpCodes.Call, + AccessTools.Method(typeof(MetricsLogger), + nameof(MetricsLogger.Initialize)))); + + return matcher.Instructions(); + } + catch (Exception e) + { + Debug.LogError($"[ServerMetrics] {nameof(Bootstrap_StartServer_Patch)}: " + e.Message); + return instructions; + } } } \ No newline at end of file From 427a1deb16454669f39d826ca402c2d5de10021b Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 16:11:48 -0500 Subject: [PATCH 06/16] use standard indentions --- .../BasePlayer_OnDisconnected_Patch.cs | 22 +++++---- .../BasePlayer_PerformanceReport_Patch.cs | 45 ++++++++++--------- .../BasePlayer_PlayerInit_Patch.cs | 34 ++++++++------ .../Bootstrap_StartServer_Patch.cs | 10 +++-- 4 files changed, 64 insertions(+), 47 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/BasePlayer_OnDisconnected_Patch.cs b/src/RustServerMetrics/HarmonyPatches/BasePlayer_OnDisconnected_Patch.cs index bfa3559..dbd2129 100644 --- a/src/RustServerMetrics/HarmonyPatches/BasePlayer_OnDisconnected_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/BasePlayer_OnDisconnected_Patch.cs @@ -18,15 +18,19 @@ public static IEnumerable Transpiler(IEnumerable), - nameof(SingletonComponent.Instance))), - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(MetricsLogger), - nameof(MetricsLogger.OnPlayerDisconnected)))); + .Start() + .InsertAndAdvance( + new CodeInstruction( + OpCodes.Ldsfld, + AccessTools.Field( + typeof(SingletonComponent), + nameof(SingletonComponent.Instance))), + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction( + OpCodes.Call, + AccessTools.Method( + typeof(MetricsLogger), + nameof(MetricsLogger.OnPlayerDisconnected)))); return matcher.Instructions(); } diff --git a/src/RustServerMetrics/HarmonyPatches/BasePlayer_PerformanceReport_Patch.cs b/src/RustServerMetrics/HarmonyPatches/BasePlayer_PerformanceReport_Patch.cs index 1a4cdb5..11eb8c3 100644 --- a/src/RustServerMetrics/HarmonyPatches/BasePlayer_PerformanceReport_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/BasePlayer_PerformanceReport_Patch.cs @@ -12,30 +12,35 @@ namespace RustServerMetrics.HarmonyPatches; public class BasePlayer_PerformanceReport_Patch { [HarmonyTranspiler] - public static IEnumerable Transpiler(IEnumerable instructions, - ILGenerator ilGenerator) + public static IEnumerable Transpiler( + IEnumerable instructions, + ILGenerator ilGenerator) { try { var matcher = new CodeMatcher(instructions, ilGenerator) - .End() - .CreateLabel(out var retLabel) - .MatchEndBackwards( - new CodeMatch(OpCodes.Ldloc_0), - new CodeMatch(OpCodes.Ldstr, "legacy"), - new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(string), "op_Equality")), - new CodeMatch(OpCodes.Brfalse)) - .ThrowIfInvalid("Unable to find the expected injection point") - .Advance(1) - .InsertAndAdvance( - new CodeInstruction(OpCodes.Ldsfld, - AccessTools.Field(typeof(SingletonComponent), - nameof(SingletonComponent.Instance))), - new CodeInstruction(OpCodes.Ldloc_1), - new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(MetricsLogger), - nameof(MetricsLogger.OnClientPerformanceReport))), - new CodeInstruction(OpCodes.Brtrue, retLabel)); + .End() + .CreateLabel(out var retLabel) + .MatchEndBackwards( + new CodeMatch(OpCodes.Ldloc_0), + new CodeMatch(OpCodes.Ldstr, "legacy"), + new CodeMatch(OpCodes.Call, AccessTools.Method(typeof(string), "op_Equality")), + new CodeMatch(OpCodes.Brfalse)) + .ThrowIfInvalid("Unable to find the expected injection point") + .Advance(1) + .InsertAndAdvance( + new CodeInstruction( + OpCodes.Ldsfld, + AccessTools.Field( + typeof(SingletonComponent), + nameof(SingletonComponent.Instance))), + new CodeInstruction(OpCodes.Ldloc_1), + new CodeInstruction( + OpCodes.Call, + AccessTools.Method( + typeof(MetricsLogger), + nameof(MetricsLogger.OnClientPerformanceReport))), + new CodeInstruction(OpCodes.Brtrue, retLabel)); return matcher.Instructions(); } diff --git a/src/RustServerMetrics/HarmonyPatches/BasePlayer_PlayerInit_Patch.cs b/src/RustServerMetrics/HarmonyPatches/BasePlayer_PlayerInit_Patch.cs index 1004bba..4ac8a29 100644 --- a/src/RustServerMetrics/HarmonyPatches/BasePlayer_PlayerInit_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/BasePlayer_PlayerInit_Patch.cs @@ -18,20 +18,26 @@ public static IEnumerable Transpiler(IEnumerable), - nameof(SingletonComponent.Instance))), - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Call, - AccessTools.Method(typeof(MetricsLogger), - nameof(MetricsLogger.OnPlayerInit)))); + .MatchEndForward( + new CodeMatch( + OpCodes.Call, + AccessTools.Method( + typeof(EACServer), + nameof(EACServer.OnStartLoading)))) + .ThrowIfInvalid("Failed to find insertion point for BasePlayer.PlayerInit") + .Advance(1) + .InsertAndAdvance( + new CodeInstruction( + OpCodes.Ldsfld, + AccessTools.Field( + typeof(SingletonComponent), + nameof(SingletonComponent.Instance))), + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction( + OpCodes.Call, + AccessTools.Method( + typeof(MetricsLogger), + nameof(MetricsLogger.OnPlayerInit)))); return matcher.Instructions(); } diff --git a/src/RustServerMetrics/HarmonyPatches/Bootstrap_StartServer_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Bootstrap_StartServer_Patch.cs index 04d2e3f..aabaf28 100644 --- a/src/RustServerMetrics/HarmonyPatches/Bootstrap_StartServer_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Bootstrap_StartServer_Patch.cs @@ -19,10 +19,12 @@ public static IEnumerable Transpiler(IEnumerable Date: Fri, 8 May 2026 16:19:09 -0500 Subject: [PATCH 07/16] cleanup --- .../Delayed/ConsoleSystem_Internal_Patch.cs | 10 +++++----- .../Delayed/InvokeHandlerBase_DoTick_Patch.cs | 11 +++++------ .../Delayed/ObjectWorkQueue_RunJob_Patch.cs | 10 +++++----- .../Delayed/RPCServer_Attribute_Method_Patch.cs | 2 +- .../Delayed/ServerMgr_Metrics_Patches.cs | 10 +++++----- .../HarmonyPatches/NetWrite_PacketID_Patch.cs | 6 +++++- .../HarmonyPatches/NetWrite_Send_Patch.cs | 6 +++++- 7 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/ConsoleSystem_Internal_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/ConsoleSystem_Internal_Patch.cs index 4b47724..449cedc 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/ConsoleSystem_Internal_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/ConsoleSystem_Internal_Patch.cs @@ -18,13 +18,13 @@ internal class ConsoleSystem_Internal_Patch [HarmonyPrepare] public static bool Prepare() { - if (!RustServerMetricsLoader.__serverStarted) + if (RustServerMetricsLoader.__serverStarted) { - Debug.Log("Note: Cannot patch ConsoleSystem_Internal_Patch yet. We will patch it upon server start."); - return false; + return true; } - - return true; + + Debug.Log($"Note: Cannot patch {nameof(ConsoleSystem_Internal_Patch)} yet. We will patch it upon server start."); + return false; } [HarmonyTargetMethods] diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/InvokeHandlerBase_DoTick_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/InvokeHandlerBase_DoTick_Patch.cs index 30e2adb..e9a4c52 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/InvokeHandlerBase_DoTick_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/InvokeHandlerBase_DoTick_Patch.cs @@ -37,14 +37,13 @@ internal static class InvokeHandlerBase_DoTick_Patch [HarmonyPrepare] public static bool Prepare() { - // ReSharper disable once InvertIf - if (!RustServerMetricsLoader.__serverStarted) + if (RustServerMetricsLoader.__serverStarted) { - UnityEngine.Debug.Log("Note: Cannot patch InvokeHandlerBase_DoTick_Patch yet. We will patch it upon server start."); - return false; + return true; } - - return true; + + UnityEngine.Debug.Log($"Note: Cannot patch {nameof(InvokeHandlerBase_DoTick_Patch)} yet. We will patch it upon server start."); + return false; } [HarmonyTargetMethods] diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/ObjectWorkQueue_RunJob_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/ObjectWorkQueue_RunJob_Patch.cs index 9c29eab..abe74b5 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/ObjectWorkQueue_RunJob_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/ObjectWorkQueue_RunJob_Patch.cs @@ -21,13 +21,13 @@ internal static class ObjectWorkQueue_RunJob_Patch [HarmonyPrepare] public static bool Prepare() { - if (!RustServerMetricsLoader.__serverStarted) + if (RustServerMetricsLoader.__serverStarted) { - Debug.Log("Note: Cannot patch ObjectWorkQueue_RunJob_Patch yet. We will patch it upon server start."); - return false; + return true; } - - return true; + + Debug.Log($"Note: Cannot patch {nameof(ObjectWorkQueue_RunJob_Patch)} yet. We will patch it upon server start."); + return false; } [HarmonyTargetMethods] diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/RPCServer_Attribute_Method_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/RPCServer_Attribute_Method_Patch.cs index dfa640b..4226321 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/RPCServer_Attribute_Method_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/RPCServer_Attribute_Method_Patch.cs @@ -26,7 +26,7 @@ public static bool Prepare() return true; } - Debug.Log("Note: Cannot patch RPCServer_Attribute_Method_Patch yet. We will patch it upon server start."); + Debug.Log($"Note: Cannot patch {nameof(RPCServer_Attribute_Method_Patch)} yet. We will patch it upon server start."); return false; } diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/ServerMgr_Metrics_Patches.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/ServerMgr_Metrics_Patches.cs index 99b7d38..176462a 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/ServerMgr_Metrics_Patches.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/ServerMgr_Metrics_Patches.cs @@ -20,13 +20,13 @@ internal static class ServerMgr_Metrics_Patches [HarmonyPrepare] public static bool Prepare() { - if (!RustServerMetricsLoader.__serverStarted) + if (RustServerMetricsLoader.__serverStarted) { - Debug.Log("Note: Cannot patch ServerMgr_Metrics_Patches yet. We will patch it upon server start."); - return false; + return true; } - - return true; + + Debug.Log($"Note: Cannot patch {nameof(ServerMgr_Metrics_Patches)} yet. We will patch it upon server start."); + return false; } [HarmonyTargetMethods] diff --git a/src/RustServerMetrics/HarmonyPatches/NetWrite_PacketID_Patch.cs b/src/RustServerMetrics/HarmonyPatches/NetWrite_PacketID_Patch.cs index dee2fed..1b91b93 100644 --- a/src/RustServerMetrics/HarmonyPatches/NetWrite_PacketID_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/NetWrite_PacketID_Patch.cs @@ -11,7 +11,11 @@ public class NetWrite_PacketID_Patch [HarmonyPostfix] public static void Postfix(Message.Type val) { - if (!MetricsLogger.IsReady) return; + if (!MetricsLogger.IsReady) + { + return; + } + SingletonComponent.Instance.OnNetWritePacketID(val); } } \ No newline at end of file diff --git a/src/RustServerMetrics/HarmonyPatches/NetWrite_Send_Patch.cs b/src/RustServerMetrics/HarmonyPatches/NetWrite_Send_Patch.cs index f6b394b..cfc971f 100644 --- a/src/RustServerMetrics/HarmonyPatches/NetWrite_Send_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/NetWrite_Send_Patch.cs @@ -11,7 +11,11 @@ public class NetWrite_Send_Patch [HarmonyPrefix] public static void Prefix(NetWrite __instance, SendInfo info) { - if (!MetricsLogger.IsReady) return; + if (!MetricsLogger.IsReady) + { + return; + } + SingletonComponent.Instance.OnNetWriteSend(__instance, info); } } \ No newline at end of file From 4ef4d5c075f99f196cd98f9a7bfde01bb8664908 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 17:28:52 -0500 Subject: [PATCH 08/16] fix matcher not being initiated --- .../HarmonyPatches/Bootstrap_StartServer_Patch.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/RustServerMetrics/HarmonyPatches/Bootstrap_StartServer_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Bootstrap_StartServer_Patch.cs index aabaf28..7ad9e4f 100644 --- a/src/RustServerMetrics/HarmonyPatches/Bootstrap_StartServer_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Bootstrap_StartServer_Patch.cs @@ -18,6 +18,7 @@ public static IEnumerable Transpiler(IEnumerable Date: Fri, 8 May 2026 17:31:04 -0500 Subject: [PATCH 09/16] add plugin name to all produced log lines --- .../Delayed/ConsoleSystem_Internal_Patch.cs | 2 +- .../Delayed/InvokeHandlerBase_DoTick_Patch.cs | 2 +- .../Delayed/ObjectWorkQueue_RunJob_Patch.cs | 2 +- .../Delayed/RPCServer_Attribute_Method_Patch.cs | 2 +- src/RustServerMetrics/ModTimeWarnings.cs | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/ConsoleSystem_Internal_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/ConsoleSystem_Internal_Patch.cs index 449cedc..d21a82b 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/ConsoleSystem_Internal_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/ConsoleSystem_Internal_Patch.cs @@ -23,7 +23,7 @@ public static bool Prepare() return true; } - Debug.Log($"Note: Cannot patch {nameof(ConsoleSystem_Internal_Patch)} yet. We will patch it upon server start."); + Debug.Log($"[ServerMetrics] Note: Cannot patch {nameof(ConsoleSystem_Internal_Patch)} yet. We will patch it upon server start."); return false; } diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/InvokeHandlerBase_DoTick_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/InvokeHandlerBase_DoTick_Patch.cs index e9a4c52..1032c74 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/InvokeHandlerBase_DoTick_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/InvokeHandlerBase_DoTick_Patch.cs @@ -42,7 +42,7 @@ public static bool Prepare() return true; } - UnityEngine.Debug.Log($"Note: Cannot patch {nameof(InvokeHandlerBase_DoTick_Patch)} yet. We will patch it upon server start."); + UnityEngine.Debug.Log($"[ServerMetrics] Note: Cannot patch {nameof(InvokeHandlerBase_DoTick_Patch)} yet. We will patch it upon server start."); return false; } diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/ObjectWorkQueue_RunJob_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/ObjectWorkQueue_RunJob_Patch.cs index abe74b5..e8a1e9d 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/ObjectWorkQueue_RunJob_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/ObjectWorkQueue_RunJob_Patch.cs @@ -26,7 +26,7 @@ public static bool Prepare() return true; } - Debug.Log($"Note: Cannot patch {nameof(ObjectWorkQueue_RunJob_Patch)} yet. We will patch it upon server start."); + Debug.Log($"[ServerMetrics] Note: Cannot patch {nameof(ObjectWorkQueue_RunJob_Patch)} yet. We will patch it upon server start."); return false; } diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/RPCServer_Attribute_Method_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/RPCServer_Attribute_Method_Patch.cs index 4226321..1c15c0c 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/RPCServer_Attribute_Method_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/RPCServer_Attribute_Method_Patch.cs @@ -26,7 +26,7 @@ public static bool Prepare() return true; } - Debug.Log($"Note: Cannot patch {nameof(RPCServer_Attribute_Method_Patch)} yet. We will patch it upon server start."); + Debug.Log($"[ServerMetrics] Note: Cannot patch {nameof(RPCServer_Attribute_Method_Patch)} yet. We will patch it upon server start."); return false; } diff --git a/src/RustServerMetrics/ModTimeWarnings.cs b/src/RustServerMetrics/ModTimeWarnings.cs index 25776e1..f0f0fa9 100644 --- a/src/RustServerMetrics/ModTimeWarnings.cs +++ b/src/RustServerMetrics/ModTimeWarnings.cs @@ -21,13 +21,13 @@ public static class ModTimeWarnings [HarmonyPrepare] public static bool Prepare() { - if (!RustServerMetricsLoader.__serverStarted) + if (RustServerMetricsLoader.__serverStarted) { - Debug.Log("Note: Cannot patch any time warnings yet. We will patch it upon server start."); - return false; + return true; } - - return true; + + Debug.Log("[ServerMetrics] Note: Cannot patch any time warnings yet. We will patch it upon server start."); + return false; } [HarmonyTargetMethods] From 695dad869e2ce81d6351c9ce05a9f22756b03fe8 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 17:36:23 -0500 Subject: [PATCH 10/16] update ServerMgr_Metrics_Patches --- .../Delayed/ServerMgr_Metrics_Patches.cs | 70 ++++++++++++------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/ServerMgr_Metrics_Patches.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/ServerMgr_Metrics_Patches.cs index 176462a..cb2148b 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/ServerMgr_Metrics_Patches.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/ServerMgr_Metrics_Patches.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using System.Linq; +using System; +using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using HarmonyLib; @@ -8,6 +8,7 @@ using Stopwatch = System.Diagnostics.Stopwatch; // ReSharper disable InconsistentNaming +// ReSharper disable PossibleMultipleEnumeration namespace RustServerMetrics.HarmonyPatches.Delayed; @@ -25,59 +26,80 @@ public static bool Prepare() return true; } - Debug.Log($"Note: Cannot patch {nameof(ServerMgr_Metrics_Patches)} yet. We will patch it upon server start."); + Debug.Log($"[ServerMetrics] Note: Cannot patch {nameof(ServerMgr_Metrics_Patches)} yet. We will patch it upon server start."); return false; } [HarmonyTargetMethods] - public static IEnumerable TargetMethods(Harmony harmonyInstance) + public static IEnumerable TargetMethods() { yield return AccessTools.Method(typeof(ServerMgr), nameof(ServerMgr.Update)); yield return AccessTools.Method(typeof(ServerBuildingManager), nameof(ServerBuildingManager.Cycle)); yield return AccessTools.Method(typeof(ServerBuildingManager), nameof(ServerBuildingManager.Merge)); yield return AccessTools.Method(typeof(ServerBuildingManager), nameof(ServerBuildingManager.Split)); - yield return AccessTools.Method(typeof(BasePlayer), nameof(BasePlayer.ServerCycle)); yield return AccessTools.Method(typeof(ConnectionQueue), nameof(ConnectionQueue.Cycle)); - yield return AccessTools.Method(typeof(AIThinkManager), nameof(AIThinkManager.ProcessQueue)); yield return AccessTools.Method(typeof(IOEntity), nameof(IOEntity.ProcessQueue)); - yield return AccessTools.Method(typeof(BasePet), nameof(BasePet.ProcessMovementQueue)); yield return AccessTools.Method(typeof(BaseMountable), nameof(BaseMountable.FixedUpdateCycle)); yield return AccessTools.Method(typeof(Buoyancy), nameof(Buoyancy.Cycle)); - yield return AccessTools.Method(typeof(BaseEntity), nameof(BaseEntity.Kill)); yield return AccessTools.Method(typeof(BaseEntity), nameof(BaseEntity.Spawn)); - yield return AccessTools.Method(typeof(Facepunch.Network.Raknet.Server), nameof(Facepunch.Network.Raknet.Server.Cycle)); } [HarmonyTranspiler] - public static IEnumerable Transpile(IEnumerable originalInstructions, MethodBase methodBase, ILGenerator ilGenerator) + public static IEnumerable Transpiler( + IEnumerable instructions, + MethodBase methodBase, + ILGenerator ilGenerator) { - var ret = originalInstructions.ToList(); - var local = ilGenerator.DeclareLocal(typeof(long)); - - ret.InsertRange(0, [ - new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Stopwatch), nameof(Stopwatch.GetTimestamp))), - new CodeInstruction(OpCodes.Stloc, local) - ]); - - return Helpers.Postfix(ret, - CustomPostfix, - new CodeInstruction(OpCodes.Ldstr, $"{methodBase.DeclaringType?.Name}.{methodBase.Name}"), - new CodeInstruction(OpCodes.Ldloc, local)); + try + { + var invokedMethod = $"{methodBase.DeclaringType?.Name}.{methodBase.Name}"; + + var matcher = new CodeMatcher(instructions, ilGenerator) + .Start() + .DeclareLocal(typeof(long), out var timestampLocal) + .InsertAndAdvance( + new CodeInstruction(OpCodes.Call, + AccessTools.Method( + typeof(Stopwatch), + nameof(Stopwatch.GetTimestamp))), + new CodeInstruction(OpCodes.Stloc, timestampLocal)) + .MatchStartForward(new CodeMatch(OpCodes.Ret)) + .Repeat(repeatingMatcher => + { + CodeInstruction[] toInsert = + [ + new(OpCodes.Ldstr, invokedMethod), + new(OpCodes.Ldloc, timestampLocal), + new(OpCodes.Call, AccessTools.Method(typeof(ServerMgr_Metrics_Patches), nameof(RecordInvokeTime))) + ]; + + repeatingMatcher.Instruction.MoveLabelsTo(toInsert[0]); + repeatingMatcher.InsertAndAdvance(toInsert); + repeatingMatcher.Advance(1); + }); + + return matcher.Instructions(); + } + catch (Exception e) + { + Debug.LogError($"[ServerMetrics] {nameof(ServerMgr_Metrics_Patches)}: " + e.Message); + return instructions; + } } - private static void CustomPostfix(string methodName, long __state) + private static void RecordInvokeTime(string methodName, long startTimestamp) { if (!MetricsLogger.IsReady) { return; } - var ms = (Stopwatch.GetTimestamp() - __state) * TicksToMs; + var ms = (Stopwatch.GetTimestamp() - startTimestamp) * TicksToMs; MetricsLogger.Instance.ServerUpdate.LogTime(methodName, ms); } } \ No newline at end of file From db7eda2844a7ed8db8a9b68065e2f3bb80e8767e Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 17:41:18 -0500 Subject: [PATCH 11/16] cleanup --- .../Delayed/InvokeHandlerBase_DoTick_Patch.cs | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/InvokeHandlerBase_DoTick_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/InvokeHandlerBase_DoTick_Patch.cs index 1032c74..3e6c362 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/InvokeHandlerBase_DoTick_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/InvokeHandlerBase_DoTick_Patch.cs @@ -3,11 +3,11 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Reflection; using System.Reflection.Emit; // ReSharper disable once InconsistentNaming +// ReSharper disable PossibleMultipleEnumeration namespace RustServerMetrics.HarmonyPatches.Delayed; @@ -19,17 +19,6 @@ internal static class InvokeHandlerBase_DoTick_Patch private static readonly double TicksToMs = 1000.0 / Stopwatch.Frequency; - private static readonly CodeMatch[] NeedleSequenceToFind = - [ - CodeMatch.LoadsField(AccessTools.Field(typeof(InvokeAction), nameof(InvokeAction.action))), - CodeMatch.Calls(AccessTools.Method(typeof(Action), nameof(Action.Invoke))) - ]; - - private static readonly CodeInstruction[] SequenceToInject = - [ - new(OpCodes.Call, AccessTools.Method(typeof(InvokeHandlerBase_DoTick_Patch), nameof(InvokeWrapper))) - ]; - #endregion #region Patching @@ -41,7 +30,7 @@ public static bool Prepare() { return true; } - + UnityEngine.Debug.Log($"[ServerMetrics] Note: Cannot patch {nameof(InvokeHandlerBase_DoTick_Patch)} yet. We will patch it upon server start."); return false; } @@ -49,29 +38,41 @@ public static bool Prepare() [HarmonyTargetMethods] public static IEnumerable TargetMethods() { - yield return AccessTools.DeclaredMethod(typeof(InvokeHandlerBase), nameof(InvokeHandlerBase.DoTick)); + yield return AccessTools.DeclaredMethod( + typeof(InvokeHandlerBase), + nameof(InvokeHandlerBase.DoTick)); } [HarmonyTranspiler] - public static IEnumerable Transpiler(IEnumerable originalInstructions) + public static IEnumerable Transpiler(IEnumerable instructions) { - var instructionsList = originalInstructions.ToList(); - try { - var codeMatcher = new CodeMatcher(instructionsList); - - codeMatcher.MatchStartForward(NeedleSequenceToFind) - .ThrowIfInvalid("Unable to find the expected injection point") - .RemoveInstructions(2) - .InsertAndAdvance(SequenceToInject); + var codeMatcher = new CodeMatcher(instructions) + .MatchStartForward( + CodeMatch.LoadsField( + AccessTools.Field( + typeof(InvokeAction), + nameof(InvokeAction.action))), + CodeMatch.Calls( + AccessTools.Method( + typeof(Action), + nameof(Action.Invoke)))) + .ThrowIfInvalid("Unable to find the expected injection point") + .RemoveInstructions(2) + .InsertAndAdvance( + new CodeInstruction( + OpCodes.Call, + AccessTools.Method( + typeof(InvokeHandlerBase_DoTick_Patch), + nameof(InvokeWrapper)))); return codeMatcher.Instructions(); } catch (Exception e) { UnityEngine.Debug.LogError($"[ServerMetrics] {nameof(InvokeHandlerBase_DoTick_Patch)}: " + e.Message); - return instructionsList; + return instructions; } } From 268c0062b203fc184281c5443e406c69da7578e7 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 17:52:10 -0500 Subject: [PATCH 12/16] update ObjectWorkQueue_RunJob_Patch --- .../Delayed/ObjectWorkQueue_RunJob_Patch.cs | 74 +++++++++++++------ 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/ObjectWorkQueue_RunJob_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/ObjectWorkQueue_RunJob_Patch.cs index e8a1e9d..e70c38c 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/ObjectWorkQueue_RunJob_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/ObjectWorkQueue_RunJob_Patch.cs @@ -2,13 +2,13 @@ using RustServerMetrics.HarmonyPatches.Utility; using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Reflection.Emit; using UnityEngine; using Stopwatch = System.Diagnostics.Stopwatch; // ReSharper disable InconsistentNaming +// ReSharper disable PossibleMultipleEnumeration namespace RustServerMetrics.HarmonyPatches.Delayed; @@ -31,20 +31,23 @@ public static bool Prepare() } [HarmonyTargetMethods] - public static IEnumerable TargetMethods(Harmony harmonyInstance) + public static IEnumerable TargetMethods() { var assemblyCSharp = typeof(BaseNetworkable).Assembly; var typesToScan = new Stack(assemblyCSharp.GetTypes()); - HashSet yielded = []; - + var yielded = new HashSet(); + while (typesToScan.TryPop(out var type)) { - var subTypes = type.GetNestedTypes(); - foreach (var t in subTypes) + foreach (var t in type.GetNestedTypes()) + { typesToScan.Push(t); + } if (type.BaseType == null || !type.BaseType.Name.Contains("ObjectWorkQueue")) + { continue; + } if (yielded.Add(type.FullName)) { @@ -54,32 +57,57 @@ public static IEnumerable TargetMethods(Harmony harmonyInstance) } [HarmonyTranspiler] - public static IEnumerable Transpile(IEnumerable originalInstructions, - MethodBase methodBase, - ILGenerator ilGenerator) + public static IEnumerable Transpiler( + IEnumerable instructions, + MethodBase methodBase, + ILGenerator ilGenerator) { - var ret = originalInstructions.ToList(); - var local = ilGenerator.DeclareLocal(typeof(long)); - - ret.InsertRange(0, [ - new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Stopwatch), nameof(Stopwatch.GetTimestamp))), - new CodeInstruction(OpCodes.Stloc, local) - ]); - - return Helpers.Postfix(ret, - CustomPostfix, - new CodeInstruction(OpCodes.Ldstr, $"{methodBase.DeclaringType?.Name}.{methodBase.Name}"), - new CodeInstruction(OpCodes.Ldloc, local)); + try + { + var invokedMethod = $"{methodBase.DeclaringType?.Name}.{methodBase.Name}"; + + var matcher = new CodeMatcher(instructions, ilGenerator) + .Start() + .DeclareLocal(typeof(long), out var timestampLocal) + .InsertAndAdvance( + new CodeInstruction( + OpCodes.Call, + AccessTools.Method( + typeof(Stopwatch), + nameof(Stopwatch.GetTimestamp))), + new CodeInstruction(OpCodes.Stloc, timestampLocal)) + .MatchStartForward(new CodeMatch(OpCodes.Ret)) + .Repeat(repeatingMatcher => + { + CodeInstruction[] toInsert = + [ + new(OpCodes.Ldstr, invokedMethod), + new(OpCodes.Ldloc, timestampLocal), + new(OpCodes.Call, AccessTools.Method(typeof(ObjectWorkQueue_RunJob_Patch), nameof(RecordInvokeTime))) + ]; + + repeatingMatcher.Instruction.MoveLabelsTo(toInsert[0]); + repeatingMatcher.InsertAndAdvance(toInsert); + repeatingMatcher.Advance(1); + }); + + return matcher.Instructions(); + } + catch (Exception e) + { + Debug.LogError($"[ServerMetrics] {nameof(ObjectWorkQueue_RunJob_Patch)}: " + e.Message); + return instructions; + } } - private static void CustomPostfix(string methodName, long __state) + private static void RecordInvokeTime(string methodName, long startTimestamp) { if (!MetricsLogger.IsReady) { return; } - var ms = (Stopwatch.GetTimestamp() - __state) * TicksToMs; + var ms = (Stopwatch.GetTimestamp() - startTimestamp) * TicksToMs; MetricsLogger.Instance.WorkQueueTimes.LogTime(methodName, ms); } } \ No newline at end of file From 7f416f8e79283ece052a5184ff4228748a341a38 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 18:10:50 -0500 Subject: [PATCH 13/16] remove unused time warning junk --- src/RustServerMetrics/ModTimeWarnings.cs | 64 ------------------- .../RustServerMetricsLoader.cs | 19 ------ 2 files changed, 83 deletions(-) delete mode 100644 src/RustServerMetrics/ModTimeWarnings.cs diff --git a/src/RustServerMetrics/ModTimeWarnings.cs b/src/RustServerMetrics/ModTimeWarnings.cs deleted file mode 100644 index f0f0fa9..0000000 --- a/src/RustServerMetrics/ModTimeWarnings.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; -using HarmonyLib; -using RustServerMetrics.HarmonyPatches.Utility; -using UnityEngine; -using Stopwatch = System.Diagnostics.Stopwatch; - -// ReSharper disable InconsistentNaming - -namespace RustServerMetrics; - -[HarmonyPatch] -public static class ModTimeWarnings -{ - public static readonly List Methods = new(); - - private static readonly double TicksToMs = 1000.0 / Stopwatch.Frequency; - - [HarmonyPrepare] - public static bool Prepare() - { - if (RustServerMetricsLoader.__serverStarted) - { - return true; - } - - Debug.Log("[ServerMetrics] Note: Cannot patch any time warnings yet. We will patch it upon server start."); - return false; - } - - [HarmonyTargetMethods] - public static IEnumerable TargetMethods(Harmony harmonyInstance) => Methods; - - [HarmonyTranspiler] - public static IEnumerable Transpile(IEnumerable originalInstructions, MethodBase methodBase, ILGenerator ilGenerator) - { - var ret = originalInstructions.ToList(); - var local = ilGenerator.DeclareLocal(typeof(long)); - - ret.InsertRange(0, new CodeInstruction [] - { - new (OpCodes.Call, AccessTools.Method(typeof(Stopwatch), nameof(Stopwatch.GetTimestamp))), - new (OpCodes.Stloc, local) - }); - - return Helpers.Postfix(ret, - CustomPostfix, - new CodeInstruction(OpCodes.Ldstr, $"{methodBase.DeclaringType?.Name}.{methodBase.Name}"), - new CodeInstruction(OpCodes.Ldloc, local)); - } - - private static void CustomPostfix(string methodName, long __state) - { - if (!MetricsLogger.IsReady) - { - return; - } - - var ms = (Stopwatch.GetTimestamp() - __state) * TicksToMs; - MetricsLogger.Instance.TimeWarnings.LogTime(methodName, ms); - } -} \ No newline at end of file diff --git a/src/RustServerMetrics/RustServerMetricsLoader.cs b/src/RustServerMetrics/RustServerMetricsLoader.cs index 12d76d0..8081f64 100644 --- a/src/RustServerMetrics/RustServerMetricsLoader.cs +++ b/src/RustServerMetrics/RustServerMetricsLoader.cs @@ -43,23 +43,4 @@ public void OnUnloaded(OnHarmonyModUnloadedArgs args) Object.DestroyImmediate(MetricsLogger.Instance); } } - - public void AddModTimeWarnings(List methods) - { - var instance = new Harmony($"RustServerMetrics.ModTimeWarnings.{__modTimeWarningsHarmonyInstances.Count}"); - __modTimeWarningsHarmonyInstances.Add(instance); - - ModTimeWarnings.Methods.Clear(); - ModTimeWarnings.Methods.AddRange(methods); - - var patchProcessor = new PatchClassProcessor(instance, typeof(ModTimeWarnings)); - patchProcessor.Patch(); - - foreach (var method in methods) - { - Debug.Log($"{method.DeclaringType?.Name}.{method.Name}"); - } - - Debug.Log($"[ServerMetrics]: Added {methods.Count} ModTimeWarnings"); - } } \ No newline at end of file From af9e0bc90d53d1b46fdf5ae927148578644c021d Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 18:15:28 -0500 Subject: [PATCH 14/16] update RPCServer_Attribute_Method_Patch --- .../RPCServer_Attribute_Method_Patch.cs | 64 +++++++++++++------ 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/Delayed/RPCServer_Attribute_Method_Patch.cs b/src/RustServerMetrics/HarmonyPatches/Delayed/RPCServer_Attribute_Method_Patch.cs index 1c15c0c..35e0e50 100644 --- a/src/RustServerMetrics/HarmonyPatches/Delayed/RPCServer_Attribute_Method_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/Delayed/RPCServer_Attribute_Method_Patch.cs @@ -2,13 +2,13 @@ using RustServerMetrics.HarmonyPatches.Utility; using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Reflection.Emit; using UnityEngine; using Stopwatch = System.Diagnostics.Stopwatch; // ReSharper disable InconsistentNaming +// ReSharper disable PossibleMultipleEnumeration namespace RustServerMetrics.HarmonyPatches.Delayed; @@ -31,7 +31,7 @@ public static bool Prepare() } [HarmonyTargetMethods] - public static IEnumerable TargetMethods(Harmony harmonyInstance) + public static IEnumerable TargetMethods() { var baseNetworkableType = typeof(BaseNetworkable); var baseNetworkableAssembly = baseNetworkableType.Assembly; @@ -55,31 +55,57 @@ public static IEnumerable TargetMethods(Harmony harmonyInstance) } [HarmonyTranspiler] - public static IEnumerable Transpile(IEnumerable originalInstructions, MethodBase methodBase, ILGenerator ilGenerator) + public static IEnumerable Transpiler( + IEnumerable instructions, + MethodBase methodBase, + ILGenerator ilGenerator) { - var ret = originalInstructions.ToList(); - var local = ilGenerator.DeclareLocal(typeof(long)); - - ret.InsertRange(0, [ - new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(Stopwatch), nameof(Stopwatch.GetTimestamp))), - new CodeInstruction(OpCodes.Stloc, local) - ]); - - return Helpers.Postfix(ret, - CustomPostfix, - new CodeInstruction(OpCodes.Ldstr, $"{methodBase.DeclaringType?.Name}.{methodBase.Name}"), - new CodeInstruction(OpCodes.Ldloc, local)); + try + { + var invokedMethod = $"{methodBase.DeclaringType?.Name}.{methodBase.Name}"; + + var matcher = new CodeMatcher(instructions, ilGenerator) + .Start() + .DeclareLocal(typeof(long), out var timestampLocal) + .InsertAndAdvance( + new CodeInstruction( + OpCodes.Call, + AccessTools.Method( + typeof(Stopwatch), + nameof(Stopwatch.GetTimestamp))), + new CodeInstruction(OpCodes.Stloc, timestampLocal)) + .MatchStartForward(new CodeMatch(OpCodes.Ret)) + .Repeat(repeatingMatcher => + { + CodeInstruction[] toInsert = + [ + new(OpCodes.Ldstr, invokedMethod), + new(OpCodes.Ldloc, timestampLocal), + new(OpCodes.Call, AccessTools.Method(typeof(RPCServer_Attribute_Method_Patch), nameof(RecordInvokeTime))) + ]; + + repeatingMatcher.Instruction.MoveLabelsTo(toInsert[0]); + repeatingMatcher.InsertAndAdvance(toInsert); + repeatingMatcher.Advance(1); + }); + + return matcher.Instructions(); + } + catch (Exception e) + { + Debug.LogError($"[ServerMetrics] {nameof(RPCServer_Attribute_Method_Patch)}: " + e.Message); + return instructions; + } } - - - private static void CustomPostfix(string methodName, long __state) + + private static void RecordInvokeTime(string methodName, long startTimestamp) { if (!MetricsLogger.IsReady) { return; } - var ms = (Stopwatch.GetTimestamp() - __state) * TicksToMs; + var ms = (Stopwatch.GetTimestamp() - startTimestamp) * TicksToMs; MetricsLogger.Instance.ServerRpcCalls.LogTime(methodName, ms); } } \ No newline at end of file From 3902f956ac9094f2c1eb1d2b97285c8ba77bbb42 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 18:15:32 -0500 Subject: [PATCH 15/16] Delete Helpers.cs --- .../HarmonyPatches/Utility/Helpers.cs | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 src/RustServerMetrics/HarmonyPatches/Utility/Helpers.cs diff --git a/src/RustServerMetrics/HarmonyPatches/Utility/Helpers.cs b/src/RustServerMetrics/HarmonyPatches/Utility/Helpers.cs deleted file mode 100644 index e891e04..0000000 --- a/src/RustServerMetrics/HarmonyPatches/Utility/Helpers.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection.Emit; -using HarmonyLib; - -namespace RustServerMetrics.HarmonyPatches.Utility; - -public static class Helpers -{ - public static IEnumerable Postfix(IEnumerable originalInstructions, Delegate postfix, params CodeInstruction[] loads) - { - var ret = originalInstructions.ToList(); - - for (var i = ret.Count - 1; i >= 0; i--) - { - var code = ret[i]; - if (code.opcode != OpCodes.Ret) - { - continue; - } - - ret.Insert(i, new CodeInstruction(OpCodes.Call, postfix.Method)); - - if (loads is { Length: > 0 }) - { - for (var j = loads.Length - 1; j >= 0; j--) - { - ret.Insert(i, new CodeInstruction(loads[j])); - } - } - - if (code == ret[i] || code.labels is not { Count: > 0 }) - { - continue; - } - - while (code.labels.Count > 0) - { - var label = code.labels[0]; - ret[i].labels.Add(label); - code.labels.RemoveAt(0); - } - } - - return ret; - } -} \ No newline at end of file From 66307f9ab3de9777c2252e345b016446ebe11df5 Mon Sep 17 00:00:00 2001 From: JJ Date: Fri, 8 May 2026 18:39:58 -0500 Subject: [PATCH 16/16] cleanup oxide patch setup --- .../HarmonyPatches/OxideMod_OnFrame_Patch.cs | 56 ++++++++----------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/src/RustServerMetrics/HarmonyPatches/OxideMod_OnFrame_Patch.cs b/src/RustServerMetrics/HarmonyPatches/OxideMod_OnFrame_Patch.cs index 307adc8..e428649 100644 --- a/src/RustServerMetrics/HarmonyPatches/OxideMod_OnFrame_Patch.cs +++ b/src/RustServerMetrics/HarmonyPatches/OxideMod_OnFrame_Patch.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Reflection.Emit; @@ -13,7 +14,6 @@ namespace RustServerMetrics.HarmonyPatches; public static class OxideMod_OnFrame_Patch { private const string OxideCore_AssemblyName = "Oxide.Core"; - private const string OxidePluginType_FullName = "Oxide.Core.Plugins.Plugin"; private const string OxideInterfaceType_FullName = "Oxide.Core.Interface"; private const string OxideOxideModType_FullName = "Oxide.Core.OxideMod"; @@ -23,49 +23,37 @@ public static class OxideMod_OnFrame_Patch public static float nextTick = 0f; [HarmonyPrepare] - public static bool Prepare() + public static bool Prepare(MethodBase original) { - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - foreach (var assembly in assemblies) + // If the original method is not null, then we've already loaded the oxide assembly. + if (original != null) { - if (!string.Equals(assembly.GetName().Name, OxideCore_AssemblyName, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - _oxideCoreAssembly = assembly; - - break; + return true; } - - return _oxideCoreAssembly != null; + + _oxideCoreAssembly = AppDomain.CurrentDomain + .GetAssemblies() + .FirstOrDefault(x => string.Equals(x.GetName().Name, OxideCore_AssemblyName, StringComparison.OrdinalIgnoreCase)); + + return _oxideCoreAssembly is not null; } [HarmonyTargetMethods] - public static IEnumerable TargetMethods(Harmony harmonyInstance) + public static IEnumerable TargetMethods() { - foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) - { - if (!string.Equals(assembly.GetName().Name, OxideCore_AssemblyName, StringComparison.OrdinalIgnoreCase)) continue; - _oxideCoreAssembly = assembly; - break; - } - - if (_oxideCoreAssembly == null) - { - return []; - } - var oxideModType = _oxideCoreAssembly.GetType(OxideOxideModType_FullName, false); - - if (oxideModType == null) + if (oxideModType is null) { return []; } - var targetMethod = oxideModType.GetMethod("OnFrame", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, [typeof(float)], null); - - if (targetMethod == null) + var targetMethod = oxideModType.GetMethod( + "OnFrame", + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, + null, + [typeof(float)], + null); + if (targetMethod is null) { return []; } @@ -74,7 +62,9 @@ public static IEnumerable TargetMethods(Harmony harmonyInstance) } [HarmonyTranspiler] - public static IEnumerable Transpile(IEnumerable originalInstructions, ILGenerator ilGenerator) + public static IEnumerable Transpiler( + IEnumerable originalInstructions, + ILGenerator ilGenerator) { var skipProcessingLabel = ilGenerator.DefineLabel(); var loopHeadLabel = ilGenerator.DefineLabel();