Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of BetterServerRpcErrorLog v0.0.4
NoteBoxz.BetterServerRpcErrorLog.dll
Decompiled 7 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("EasyTextEffects")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: AssemblyCompany("NoteBoxz.BetterServerRpcErrorLog")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("0.0.4.0")] [assembly: AssemblyInformationalVersion("0.0.4+33a4f096e97c4722a442d2058bc0aabadae16216")] [assembly: AssemblyProduct("BetterServerRpcErrorLog")] [assembly: AssemblyTitle("NoteBoxz.BetterServerRpcErrorLog")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.4.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BetterServerRpcErrorLog { [BepInPlugin("NoteBoxz.BetterServerRpcErrorLog", "BetterServerRpcErrorLog", "0.0.4")] public class BetterServerRpcErrorLog : BaseUnityPlugin { [CompilerGenerated] private sealed class <PatchDynamicCoroutine>d__24 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private Task <patchingTask>5__1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <PatchDynamicCoroutine>d__24(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <patchingTask>5__1 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <patchingTask>5__1 = Task.Run(delegate { List<Assembly> list = AppDomain.CurrentDomain.GetAssemblies().ToList(); int count = list.Count; int num = 0; foreach (Assembly item in list) { try { ScanAssemblyToPatch(item); num++; if (num % 5 == 0 || num == count) { Logger.LogInfo((object)($"Patching progress: {num}/{count} assemblies processed " + $"{ServerRpcCount} ServerRpc methods patched, {RpcHandlerCount} RPC handlers patched")); } } catch (Exception ex) { Logger.LogError((object)("Error scanning assembly " + item.FullName + ": " + ex.Message)); } } Logger.LogMessage((object)($"Dynamic patching complete. Processed {count} assemblies. " + $"Patched {ServerRpcCount} ServerRpc methods and {RpcHandlerCount} RPC handlers.")); }); break; case 1: <>1__state = -1; break; } if (!<patchingTask>5__1.IsCompleted) { <>2__current = null; <>1__state = 1; return true; } if (<patchingTask>5__1.Exception != null) { Logger.LogError((object)$"Error during async patching: {<patchingTask>5__1.Exception}"); } Object.Destroy((Object)(object)((Component)SharedCoroutineStarter.Instance).gameObject); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static Harmony Harmony; public static ManualLogSource Logger { get; private set; } public static ConfigEntry<bool> LogAssemblyScanning { get; private set; } public static ConfigEntry<bool> RemoveFirstLineOfStackTrace { get; private set; } public static int ServerRpcCount { get; private set; } public static int RpcHandlerCount { get; private set; } private void Awake() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown Harmony = new Harmony("NoteBoxz.BetterServerRpcErrorLog"); Logger = ((BaseUnityPlugin)this).Logger; Logger.LogInfo((object)"Active, waiting for game to start before dynamic patching..."); LogAssemblyScanning = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LogAssemblyScanning", false, "Enable/disable logging of assembly scanning progress"); RemoveFirstLineOfStackTrace = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RemoveFirstLineOfStackTrace", true, "Enable/disable removing the first line of the stack trace in the error log. This first line is usually from the mod itself and not the actual error."); Patch(); } public static void Patch() { Logger.LogDebug((object)"Patching..."); Harmony.PatchAll(Assembly.GetExecutingAssembly()); Logger.LogDebug((object)"Patching complete!"); } public static void PatchDynamic() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown Logger.LogMessage((object)"Dynamic patching..."); ServerRpcCount = 0; GameObject val = new GameObject("SharedCoroutineStarter"); Object.DontDestroyOnLoad((Object)(object)val); SharedCoroutineStarter.Instance = val.AddComponent<SharedCoroutineStarter>(); ((MonoBehaviour)SharedCoroutineStarter.Instance).StartCoroutine(PatchDynamicCoroutine()); } [IteratorStateMachine(typeof(<PatchDynamicCoroutine>d__24))] private static IEnumerator PatchDynamicCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <PatchDynamicCoroutine>d__24(0); } public static void ScanAssemblyToPatch(Assembly assembly) { //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Expected O, but got Unknown //IL_0553: Unknown result type (might be due to invalid IL or missing references) //IL_055f: Expected O, but got Unknown //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Expected O, but got Unknown List<MethodInfo> list = new List<MethodInfo>(); List<MethodInfo> list2 = new List<MethodInfo>(); List<string> list3 = new List<string>(); if (LogAssemblyScanning.Value) { Logger.LogMessage((object)("----Scanning assembly " + assembly.GetName().Name + " for patch methods----")); } try { Type[] array; try { array = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { int num = ex.Types.Count((Type t) => t == null); if (LogAssemblyScanning.Value) { Logger.LogWarning((object)("Some types in assembly " + assembly.GetName().Name + " couldn't be loaded: " + ex.Message)); Logger.LogWarning((object)$"Failed to load {num} types from {ex.Types.Length} total types"); } array = ex.Types.Where((Type t) => t != null).ToArray(); } Type[] array2 = array; foreach (Type type in array2) { try { list.AddRange(from m in type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where m.Name.EndsWith("ServerRpc") select m); IEnumerable<MethodInfo> enumerable = from m in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where m.Name.StartsWith("__rpc_handler_") && IsRpcHandlerSignatureMatch(m) select m; foreach (MethodInfo item in enumerable) { if (ContainsOwnershipCheckAndErrorLog(item)) { list2.Add(item); if (LogAssemblyScanning.Value) { Logger.LogInfo((object)("Found RPC handler with ownership check: " + GetUniqueMethodSignature(item))); } } } } catch (Exception ex2) { Logger.LogError((object)("Error accessing methods for type " + type.Name + ": " + ex2.Message)); } } foreach (MethodInfo item2 in list) { try { string uniqueMethodSignature = GetUniqueMethodSignature(item2); if (list3.Contains(uniqueMethodSignature)) { if (LogAssemblyScanning.Value) { Logger.LogWarning((object)("Already patched " + uniqueMethodSignature + ", skipping")); } continue; } ServerRpcAttribute val = (ServerRpcAttribute)item2.GetCustomAttribute(typeof(ServerRpcAttribute)); if (val == null) { if (LogAssemblyScanning.Value) { Logger.LogWarning((object)("No ServerRpcAttribute found for " + uniqueMethodSignature + " despite ending with 'ServerRpc', skipping")); } continue; } CustomAttributeData customAttributeData = item2.GetCustomAttributesData().FirstOrDefault((CustomAttributeData x) => x.AttributeType == typeof(ServerRpcAttribute)); if (customAttributeData == null) { goto IL_03eb; } CustomAttributeNamedArgument customAttributeNamedArgument = customAttributeData.NamedArguments.FirstOrDefault((CustomAttributeNamedArgument x) => x.MemberName == "RequireOwnership"); if (customAttributeNamedArgument.TypedValue.Value == null || !(customAttributeNamedArgument.TypedValue.Value is bool flag) || flag) { goto IL_03eb; } if (LogAssemblyScanning.Value) { Logger.LogInfo((object)("ServerRpcAttribute found for " + uniqueMethodSignature + " but RequireOwnership is explicitly set to false, skipping")); } goto end_IL_026b; IL_03eb: try { Harmony.Patch((MethodBase)item2, new HarmonyMethod(typeof(BetterServerRpcErrorLog).GetMethod("ServerRpcPrefix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ServerRpcCount++; if (LogAssemblyScanning.Value) { Logger.LogInfo((object)("Successfully patched ServerRpc " + uniqueMethodSignature)); } } catch (Exception ex3) { Logger.LogError((object)("Failed to patch " + uniqueMethodSignature + ": " + ex3.Message)); } list3.Add(uniqueMethodSignature); end_IL_026b:; } catch (Exception ex4) { Logger.LogError((object)("Error processing patch method " + item2.Name + ": " + ex4.Message)); } } foreach (MethodInfo item3 in list2) { try { string uniqueMethodSignature2 = GetUniqueMethodSignature(item3); if (list3.Contains(uniqueMethodSignature2)) { if (LogAssemblyScanning.Value) { Logger.LogWarning((object)("Already patched RPC handler " + uniqueMethodSignature2 + ", skipping")); } continue; } try { Harmony.Patch((MethodBase)item3, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(BetterServerRpcErrorLog).GetMethod("RpcHandlerTranspiler", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null); RpcHandlerCount++; if (LogAssemblyScanning.Value) { Logger.LogInfo((object)("Successfully patched RPC handler " + uniqueMethodSignature2)); } } catch (Exception ex5) { Logger.LogError((object)("Failed to patch RPC handler " + uniqueMethodSignature2 + ": " + ex5.Message)); } list3.Add(uniqueMethodSignature2); } catch (Exception ex6) { Logger.LogError((object)("Error processing RPC handler " + item3.Name + ": " + ex6.Message)); } } } catch (Exception ex7) { Logger.LogError((object)("Error getting types from assembly " + assembly.GetName().Name + ": " + ex7.Message)); } } private static bool IsRpcHandlerSignatureMatch(MethodInfo method) { ParameterInfo[] parameters = method.GetParameters(); return parameters.Length == 3 && parameters[0].ParameterType == typeof(NetworkBehaviour) && parameters[1].ParameterType.Name == "FastBufferReader" && parameters[2].ParameterType.Name == "__RpcParams"; } private static bool ContainsOwnershipCheckAndErrorLog(MethodInfo method) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) try { MethodBody methodBody = method.GetMethodBody(); if (methodBody == null) { return false; } string location = method.Module.Assembly.Location; if (string.IsNullOrEmpty(location)) { return false; } AssemblyDefinition val = AssemblyDefinition.ReadAssembly(location); try { MethodDefinition val2 = FindMethodDefinition(val, method); if (val2 == null) { return false; } Enumerator<Instruction> enumerator = val2.Body.Instructions.GetEnumerator(); try { while (enumerator.MoveNext()) { Instruction current = enumerator.Current; if (current.OpCode == OpCodes.Ldstr && current.Operand is string text && text == "Only the owner can invoke a ServerRpc that requires ownership!") { return true; } } } finally { ((IDisposable)enumerator).Dispose(); } } finally { ((IDisposable)val)?.Dispose(); } return false; } catch (Exception ex) { Logger.LogError((object)("Error analyzing method " + method.Name + ": " + ex.Message)); return false; } } private static MethodDefinition? FindMethodDefinition(AssemblyDefinition assembly, MethodInfo method) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Enumerator<ModuleDefinition> enumerator = assembly.Modules.GetEnumerator(); try { while (enumerator.MoveNext()) { ModuleDefinition current = enumerator.Current; TypeDefinition type = current.GetType(method.DeclaringType.FullName); if (type == null) { continue; } Enumerator<MethodDefinition> enumerator2 = type.Methods.GetEnumerator(); try { while (enumerator2.MoveNext()) { MethodDefinition current2 = enumerator2.Current; if (!(((MemberReference)current2).Name == method.Name) || ((MethodReference)current2).Parameters.Count != method.GetParameters().Length) { continue; } bool flag = true; for (int i = 0; i < ((MethodReference)current2).Parameters.Count; i++) { if (((MemberReference)((ParameterReference)((MethodReference)current2).Parameters[i]).ParameterType).Name != method.GetParameters()[i].ParameterType.Name) { flag = false; break; } } if (flag) { return current2; } } } finally { ((IDisposable)enumerator2).Dispose(); } } } finally { ((IDisposable)enumerator).Dispose(); } return null; } private static IEnumerable<CodeInstruction> RpcHandlerTranspiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Expected O, but got Unknown //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(instructions); bool flag = false; for (int i = 0; i < list.Count - 2; i++) { if (list[i].opcode == OpCodes.Ldstr && list[i].operand is string text && text == "Only the owner can invoke a ServerRpc that requires ownership!" && list[i + 1].opcode == OpCodes.Call && list[i + 1].operand is MethodInfo methodInfo && methodInfo.Name == "LogError" && methodInfo.DeclaringType.Name == "Debug") { flag = true; list.Insert(i, new CodeInstruction(OpCodes.Ldarg_0, (object)null)); list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_2, (object)null)); list.Insert(i + 2, new CodeInstruction(OpCodes.Call, (object)typeof(BetterServerRpcErrorLog).GetMethod("LogDetailedRpcHandlerError", BindingFlags.Static | BindingFlags.NonPublic))); list[i + 3] = new CodeInstruction(OpCodes.Nop, (object)null); list[i + 4] = new CodeInstruction(OpCodes.Nop, (object)null); break; } } if (!flag) { Logger.LogWarning((object)("RPC handler transpiler couldn't find the error log in " + __originalMethod.Name)); } return list; } private static void LogDetailedRpcHandlerError(NetworkBehaviour target, __RpcParams rpcParams) { //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) try { NetworkManager networkManager = target.NetworkManager; if (networkManager == null) { return; } string text = Environment.StackTrace; if (RemoveFirstLineOfStackTrace.Value) { string[] array = text.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); List<int> indicesToRemove = new List<int>(); for (int i = 0; i < array.Length; i++) { if (array[i].Contains("BetterServerRpcErrorLog.LogDetailedRpcHandlerError") || array[i].Contains("RpcHandlerTranspiler")) { indicesToRemove.Add(i); } } if (indicesToRemove.Count > 0) { text = string.Join(Environment.NewLine, array.Where((string line, int index) => !indicesToRemove.Contains(index))); } } Logger.LogError((object)("[RPC Handler] Only the owner can invoke a ServerRpc that requires ownership!" + $"\nCalled by client {rpcParams.Server.Receive.SenderClientId}, but owner is {target.OwnerClientId}" + "\nObject: " + ((Object)target).name + " (" + ((object)target).GetType().Name + ")\n" + text)); } catch (Exception ex) { Logger.LogError((object)("Error in LogDetailedRpcHandlerError: " + ex.Message)); } } private static void ServerRpcPrefix(MethodBase __originalMethod, NetworkBehaviour __instance) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 try { NetworkManager networkManager = __instance.NetworkManager; if (networkManager == null || !networkManager.IsListening || (int)__instance.__rpc_exec_stage == 1 || (!networkManager.IsClient && !networkManager.IsHost) || __instance.OwnerClientId == networkManager.LocalClientId) { return; } string empty = string.Empty; string text = Environment.StackTrace; if (RemoveFirstLineOfStackTrace.Value) { string[] array = text.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); int indexToRemove = -1; for (int i = 0; i < array.Length; i++) { if (array[i].Contains("BetterServerRpcErrorLog.ServerRpcPrefix") || array[i].Contains("ServerRpcPrefix")) { indexToRemove = i; break; } } if (indexToRemove >= 0 && array.Length > indexToRemove + 1) { text = string.Join(Environment.NewLine, array.Where((string line, int index) => index != indexToRemove)); } } Logger.LogError((object)($"stage({((object)(__RpcExecStage)(ref __instance.__rpc_exec_stage)).ToString()}) Non-owner called ServerRpc that requires ownership: {GetUniqueMethodSignature(__originalMethod)}" + $"\nCalled by client {networkManager.LocalClientId}, but owner is {__instance.OwnerClientId}\n" + text)); } catch (Exception ex) { Logger.LogError((object)$"Error in ServerRpcPrefix for {GetUniqueMethodSignature(__originalMethod)}: {ex.Message}"); } } private static object GetUniqueMethodSignature(MethodBase method) { string arg = string.Join(", ", from p in method.GetParameters() select p.ParameterType.Name); string text = $"{method.DeclaringType}.{method.Name}({arg})"; if (method.IsGenericMethod) { string text2 = string.Join(", ", from t in method.GetGenericArguments() select t.Name); text = text + "<" + text2 + ">"; } return text; } private static string GetUniqueMethodSignature(MethodInfo method) { string arg = string.Join(", ", from p in method.GetParameters() select p.ParameterType.Name); string text = $"{method.DeclaringType}.{method.Name}({arg})"; if (method.IsGenericMethod) { string text2 = string.Join(", ", from t in method.GetGenericArguments() select t.Name); text = text + "<" + text2 + ">"; } return text; } } public class SharedCoroutineStarter : MonoBehaviour { public static SharedCoroutineStarter Instance; } public static class MyPluginInfo { public const string PLUGIN_GUID = "NoteBoxz.BetterServerRpcErrorLog"; public const string PLUGIN_NAME = "BetterServerRpcErrorLog"; public const string PLUGIN_VERSION = "0.0.4"; } } namespace BetterServerRpcErrorLog.Patches { [HarmonyPatch(typeof(GameNetworkManager))] internal class InitPatch { [HarmonyPatch("Start")] [HarmonyPriority(0)] [HarmonyPostfix] private static void Postfix() { try { BetterServerRpcErrorLog.PatchDynamic(); } catch (Exception arg) { BetterServerRpcErrorLog.Logger.LogFatal((object)$"[BetterServerRpcErrorLog] Error during dynamic patching: {arg}"); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }