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 TimeOutLimit v1.0.1
plugins/TimeOutLimit.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("TimeOutLimit")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Radamanto")] [assembly: AssemblyProduct("TimeOutLimit")] [assembly: AssemblyCopyright("Copyright © 2026 Radamanto")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("8DF044BD-3BA5-4E54-8C7D-9D4CF8D4314C")] [assembly: AssemblyFileVersion("1.0.1")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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; } } } namespace radamanto.TimeOutLimit { [BepInPlugin("radamanto.TimeOutLimit", "TimeOutLimit", "1.0.1")] public class TimeOutLimitPlugin : BaseUnityPlugin { [HarmonyPatch(typeof(ZRpc), "SetLongTimeout", new Type[] { typeof(bool) })] private static class ZRpcSetLongTimeoutPatch { private static void Postfix() { ApplyZRpcTimeout(); Log.LogDebug((object)$"ZRpc timeout set to {ZRpc.m_timeout:0}s"); } } public const string ModGUID = "radamanto.TimeOutLimit"; public const string ModName = "TimeOutLimit"; public const string ModVersion = "1.0.1"; private const float MinTimeout = 30f; private const float MaxTimeout = 600f; private readonly Harmony _harmony = new Harmony("radamanto.TimeOutLimit"); internal static ManualLogSource Log = null; internal static ConfigEntry<float> Timeout = null; private const string WaitForQueueMarker = "waitForQueue"; private const string VanillaDisconnectMessage = "Disconnecting {0} after 30 seconds config sending timeout"; private const float VanillaConfigSyncTimeout = 30f; private static readonly MethodInfo TimeGetter = AccessTools.PropertyGetter(typeof(Time), "time"); private static readonly MethodInfo CurrentTimeoutMethod = SymbolExtensions.GetMethodInfo((Expression<Action>)(() => CurrentTimeout())); private static readonly MethodInfo DisconnectMessageMethod = SymbolExtensions.GetMethodInfo((Expression<Action>)(() => DisconnectMessageFormat(string.Empty))); private static readonly HashSet<string> PatchedTimeouts = new HashSet<string>(); private static readonly HashSet<string> PatchedMessages = new HashSet<string>(); private const string JotunnGUID = "com.jotunn.jotunn"; private static readonly Version JotunnMinVersion = new Version(2, 24, 0); private static FieldInfo? _jotunnTimeoutField; private static bool _jotunnResolved; public static float CurrentTimeout() { return Timeout.Value; } private void Awake() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Timeout = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Timeout", 120f, new ConfigDescription("Timeout in seconds for the vanilla ZRpc ping, ServerSync config sending and Jotunn custom RPCs. Vanilla is 30.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 600f), Array.Empty<object>())); Timeout.SettingChanged += delegate { ApplyRuntimeTimeouts(); }; _harmony.PatchAll(); ApplyZRpcTimeout(); } private void Start() { ApplyJotunnTimeout(); PatchServerSyncTimeouts(); } private static void ApplyRuntimeTimeouts() { ApplyZRpcTimeout(); ApplyJotunnTimeout(); Log.LogInfo((object)$"Timeout changed to {CurrentTimeout():0}s"); } private static void ApplyZRpcTimeout() { ZRpc.m_timeout = CurrentTimeout(); } public static string DisconnectMessageFormat(string source) { return $"[{source}] Disconnecting {{0}} after {CurrentTimeout():0} seconds config sending timeout"; } private void PatchServerSyncTimeouts() { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown HashSet<Assembly> hashSet = new HashSet<Assembly>(); hashSet.Add(typeof(TimeOutLimitPlugin).Assembly); HashSet<Assembly> hashSet2 = hashSet; HarmonyMethod val = new HarmonyMethod(SymbolExtensions.GetMethodInfo((Expression<Action>)(() => WaitForQueueTranspiler(null, null)))) { priority = 0 }; int num = 0; List<string> list = new List<string>(); foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if ((Object)(object)((value != null) ? value.Instance : null) == (Object)null || value.Metadata.GUID == "com.jotunn.jotunn") { continue; } Assembly assembly = ((object)value.Instance).GetType().Assembly; if (!hashSet2.Add(assembly)) { continue; } foreach (MethodInfo item in FindWaitForQueueMethods(assembly)) { num++; if (!CanRewrite(item)) { list.Add(value.Metadata.Name); continue; } try { _harmony.Patch((MethodBase)item, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex) { list.Add(value.Metadata.Name); Log.LogWarning((object)("Failed to patch " + value.Metadata.Name + " [" + value.Metadata.GUID + "] " + item.DeclaringType?.FullName + ": " + ex.GetBaseException().Message)); RemovePatch(item); } } } Log.LogInfo((object)$"ServerSync config sending timeout: {PatchedTimeouts.Count}/{num} methods patched to {CurrentTimeout():0}s, {PatchedMessages.Count} disconnect messages tagged"); if (list.Count > 0) { Log.LogWarning((object)("ServerSync config sending timeout kept at 30s (IL cannot be rewritten, obfuscated): " + string.Join(", ", list.Distinct()))); } } private static bool CanRewrite(MethodInfo method) { try { foreach (CodeInstruction originalInstruction in PatchProcessor.GetOriginalInstructions((MethodBase)method, (ILGenerator)null)) { if (originalInstruction.operand == null && RequiresOperand(originalInstruction.opcode.OperandType)) { return false; } } return true; } catch (Exception) { return false; } } private static bool RequiresOperand(OperandType type) { switch (type) { case OperandType.InlineField: case OperandType.InlineMethod: case OperandType.InlineSig: case OperandType.InlineString: case OperandType.InlineTok: case OperandType.InlineType: return true; default: return false; } } private void RemovePatch(MethodInfo method) { try { _harmony.Unpatch((MethodBase)method, (HarmonyPatchType)0, "radamanto.TimeOutLimit"); } catch (Exception) { } } private static IEnumerable<MethodInfo> FindWaitForQueueMethods(Assembly assembly) { foreach (Type type in GetLoadableTypes(assembly)) { MethodInfo moveNext = GetWaitForQueueMoveNext(type); if (moveNext != null) { yield return moveNext; } } } private static MethodInfo? GetWaitForQueueMoveNext(Type type) { try { if (!type.Name.Contains("waitForQueue") || !type.IsClass || !type.IsNested) { return null; } MethodInfo method = type.GetMethod("MoveNext", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); return (method != null && method.ReturnType == typeof(bool)) ? method : null; } catch (Exception) { return null; } } private static IEnumerable<Type> GetLoadableTypes(Assembly assembly) { try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.Where((Type t) => t != null); } catch (Exception) { return Array.Empty<Type>(); } } private static IEnumerable<CodeInstruction> WaitForQueueTranspiler(IEnumerable<CodeInstruction> instructions, MethodBase original) { //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Expected O, but got Unknown List<CodeInstruction> list = instructions.ToList(); string text = original.DeclaringType?.Assembly.GetName().Name ?? "unknown"; string item = text + ":" + original.DeclaringType?.FullName; int num = FindTimeoutConstant(list); if (num < 0) { Log.LogWarning((object)("[" + text + "] config sending timeout pattern not found, leaving it unchanged")); return list; } list[num].opcode = OpCodes.Call; list[num].operand = CurrentTimeoutMethod; PatchedTimeouts.Add(item); int num2 = list.FindIndex((CodeInstruction c) => c.opcode == OpCodes.Ldstr && c.operand is string text2 && text2 == "Disconnecting {0} after 30 seconds config sending timeout"); if (num2 >= 0) { list[num2].operand = text; list.Insert(num2 + 1, new CodeInstruction(OpCodes.Call, (object)DisconnectMessageMethod)); PatchedMessages.Add(item); } return list; } private static int FindTimeoutConstant(List<CodeInstruction> codes) { for (int i = 0; i + 2 < codes.Count; i++) { if (CodeInstructionExtensions.Calls(codes[i], TimeGetter) && codes[i + 1].opcode == OpCodes.Ldc_R4 && codes[i + 1].operand is float num && Mathf.Approximately(num, 30f) && codes[i + 2].opcode == OpCodes.Add) { return i + 1; } } return -1; } private static void ApplyJotunnTimeout() { FieldInfo fieldInfo = ResolveJotunnTimeoutField(); if (!(fieldInfo == null)) { fieldInfo.SetValue(null, CurrentTimeout()); } } private static FieldInfo? ResolveJotunnTimeoutField() { if (_jotunnResolved) { return _jotunnTimeoutField; } _jotunnResolved = true; if (!Chainloader.PluginInfos.TryGetValue("com.jotunn.jotunn", out var value) || (Object)(object)((value != null) ? value.Instance : null) == (Object)null) { return null; } if (value.Metadata.Version < JotunnMinVersion) { Log.LogInfo((object)$"Skipping Jotunn {value.Metadata.Version}: custom RPC timeout requires {JotunnMinVersion} or newer"); return null; } Type type = ((object)value.Instance).GetType().Assembly.GetType("Jotunn.Entities.CustomRPC"); _jotunnTimeoutField = ((type == null) ? null : AccessTools.Field(type, "Timeout")); if (_jotunnTimeoutField == null || !_jotunnTimeoutField.IsStatic || _jotunnTimeoutField.FieldType != typeof(float)) { _jotunnTimeoutField = null; Log.LogWarning((object)$"Jotunn {value.Metadata.Version}: CustomRPC.Timeout not found, leaving it unchanged"); return null; } Log.LogInfo((object)$"Patching Jotunn {value.Metadata.Version} custom RPC timeout"); return _jotunnTimeoutField; } } }