diff --git a/Source/Client/Desyncs/StackTraceLogItem.cs b/Source/Client/Desyncs/StackTraceLogItem.cs index b7da27da..d4c46b60 100644 --- a/Source/Client/Desyncs/StackTraceLogItem.cs +++ b/Source/Client/Desyncs/StackTraceLogItem.cs @@ -64,7 +64,7 @@ public override string StackTraceString if (!methodNameCache.TryGetValue(addr, out string method)) methodNameCache[addr] = method = Native.MethodNameFromAddr(raw[i], false); - builder.AppendLine(method != null ? SyncCoordinator.MethodNameWithIL(method) : "Null"); + builder.AppendLine(method != null ? SyncCoordinator.PossiblyCleanedMethodName(method) : "Null"); } return builder.ToString(); diff --git a/Source/Client/Desyncs/SyncCoordinator.cs b/Source/Client/Desyncs/SyncCoordinator.cs index 7fca2911..3fada3a9 100644 --- a/Source/Client/Desyncs/SyncCoordinator.cs +++ b/Source/Client/Desyncs/SyncCoordinator.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; +using LudeonTK; using RimWorld; using Verse; using Multiplayer.Client.Desyncs; @@ -242,6 +244,49 @@ public void TryAddStackTraceForDesyncLogRaw(StackTraceLogItemRaw item, int depth OpinionInBuilding.desyncStackTraceHashes.Add(hash); } + [TweakValue("Multiplayer")] + public static bool cleanMethodNames = false; + + private static readonly Regex RemoveUnknownSourceInfoRegex = new("in <([a-zA-Z0-9]+)>:0"); + public static string PossiblyCleanedMethodName(string rawName) + { + if (!cleanMethodNames) return MethodNameWithIL(rawName); + + // at (wrapper dynamic-method) MonoMod.Utils.DynamicMethodDefinition.RimWorld.UniqueIDsManager.GetNextID_Patch2 ... + // => + // at (wrapper dynamic-method) RimWorld.UniqueIDsManager.GetNextID_Patch2 ... + rawName = rawName.Replace("MonoMod.Utils.DynamicMethodDefinition.", string.Empty); + + // Performs the transformation only if the source file is unknown, meaning random id instead of a path + // at Verse.AI.JobDriver.ReadyForNextToil () [0x00000] in :0 + // => + // at Verse.AI.JobDriver.ReadyForNextToil () [0x00000] + rawName = RemoveUnknownSourceInfoRegex.Replace(rawName, string.Empty); + return ShortenSourceInfo(rawName); + } + + + private static string ShortenSourceInfo(string rawName) + { + // at Multiplayer.Client.Desyncs.DeferredStackTracing.Postfix () [0x00045] in /home/runner/work/Multiplayer/Multiplayer/Source/Client/Desyncs/DeferredStackTracing.cs:41 + // => + // at Multiplayer.Client.Desyncs.DeferredStackTracing.Postfix () [0x00045] in /Client/Desyncs/DeferredStackTracing.cs:41 + const string sourcePrefix = "in "; + const string realSourceStartA = "Multiplayer/Source"; + const string realSourceStartB = "Multiplayer\\Source"; + var startSource2 = rawName.IndexOf(sourcePrefix, StringComparison.Ordinal); + if (startSource2 != -1) + { + var offset = startSource2 + sourcePrefix.Length; + var realSourceStartIndex = rawName.IndexOf(realSourceStartA, offset, StringComparison.Ordinal); + if (realSourceStartIndex == -1) + realSourceStartIndex = rawName.IndexOf(realSourceStartB, offset, StringComparison.Ordinal); + if (realSourceStartIndex != -1) + rawName = rawName.Remove(offset, realSourceStartIndex - offset + realSourceStartA.Length); + } + return rawName; + } + public static string MethodNameWithIL(string rawName) { // Note: The names currently don't include IL locations so the code is commented out