Decompiled source of ServerSyncTimeout v1.0.0

ServerSyncTimeout.dll

Decompiled 4 days ago
using System;
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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("ServerSyncTimeout")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ServerSyncTimeout")]
[assembly: AssemblyTitle("ServerSyncTimeout")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 ServerSyncTimeout
{
	[BepInPlugin("com.dreich.serversynctimeout", "Server Sync Timeout", "1.0.0")]
	public class ServerSyncTimeoutPlugin : BaseUnityPlugin
	{
		public const string Guid = "com.dreich.serversynctimeout";

		public const string Name = "Server Sync Timeout";

		public const string Version = "1.0.0";

		private const float DefaultTimeout = 300f;

		private const float MinTimeout = 30f;

		private const float MaxTimeout = 3600f;

		internal static ConfigEntry<float> Timeout;

		internal static ConfigEntry<float> ConnectionTimeout;

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Timeout = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Timeout", 300f, new ConfigDescription("Seconds the server keeps trying to send a mod's ServerSync config to a player before giving up and disconnecting them. ServerSync's own limit is 30. Changes apply straight away.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 3600f), Array.Empty<object>()));
			ConnectionTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("Connection", "Timeout", 300f, new ConfigDescription("Seconds without a reply from the other side before the game drops the connection. The game's own limit is 30 while playing and 90 while loading; this only ever makes it longer. Install on the server and every player for it to work both ways. Changes apply straight away.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 3600f), Array.Empty<object>()));
		}

		private void Start()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			int num = SyncTimeoutPatch.PatchAll(new Harmony("com.dreich.serversynctimeout"));
			Log.LogInfo((object)("Raised the config sending timeout in " + num + " mod(s)."));
			if (!ConnectionTimeoutPatch.Setup(new Harmony("com.dreich.serversynctimeout.connection")))
			{
				Log.LogWarning((object)"Valheim has changed; the connection timeout can't be raised.");
			}
		}
	}
	internal static class ConnectionTimeoutPatch
	{
		private const string TimeoutField = "m_timeout";

		private const string SetTimeoutMethod = "SetLongTimeout";

		private static FieldRef<float> gameTimeout;

		private static float gameChoice;

		internal static bool Setup(Harmony harmony)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(ZRpc), "SetLongTimeout", (Type[])null, (Type[])null);
			FieldInfo fieldInfo = AccessTools.Field(typeof(ZRpc), "m_timeout");
			if (methodInfo == null || fieldInfo == null || !fieldInfo.IsStatic || fieldInfo.FieldType != typeof(float))
			{
				return false;
			}
			gameTimeout = AccessTools.StaticFieldRefAccess<float>(fieldInfo);
			gameChoice = gameTimeout.Invoke();
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(ConnectionTimeoutPatch), "AfterSetTimeout", (Type[])null)
			{
				priority = 0
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			ServerSyncTimeoutPlugin.ConnectionTimeout.SettingChanged += delegate
			{
				Apply();
			};
			Apply();
			return true;
		}

		private static void AfterSetTimeout()
		{
			gameChoice = gameTimeout.Invoke();
			Apply();
		}

		private static void Apply()
		{
			gameTimeout.Invoke() = Mathf.Max(gameChoice, ServerSyncTimeoutPlugin.ConnectionTimeout.Value);
		}
	}
	internal static class SyncTimeoutPatch
	{
		private const string ServerSyncNamespace = "ServerSync";

		private const string ConfigSyncTypeName = "ConfigSync";

		private const string WaitMethodMarker = "waitForQueue";

		private const string StateMachineStep = "MoveNext";

		private const float ServerSyncTimeout = 30f;

		private const string ServerSyncTimeoutText = "30 seconds";

		private static readonly MethodInfo GameTime = AccessTools.PropertyGetter(typeof(Time), "time");

		private static readonly MethodInfo CurrentTimeout = AccessTools.Method(typeof(SyncTimeoutPatch), "TimeoutSeconds", (Type[])null, (Type[])null);

		private static readonly MethodInfo RewriteMessage = AccessTools.Method(typeof(SyncTimeoutPatch), "Message", (Type[])null, (Type[])null);

		internal static int PatchAll(Harmony harmony)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			int num = 0;
			foreach (MethodInfo item in WaitMethods())
			{
				try
				{
					harmony.Patch((MethodBase)item, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(SyncTimeoutPatch), "RaiseTimeout", (Type[])null)
					{
						priority = 0
					}, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				catch (Exception ex)
				{
					ServerSyncTimeoutPlugin.Log.LogWarning((object)("Couldn't raise the timeout in " + item.DeclaringType.Assembly.GetName().Name + ": " + ex.Message));
				}
			}
			return num;
		}

		private static IEnumerable<MethodInfo> WaitMethods()
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (assembly.IsDynamic || assembly == typeof(SyncTimeoutPatch).Assembly)
				{
					continue;
				}
				foreach (Type item in TypesIn(assembly))
				{
					if (IsServerSyncWait(item))
					{
						MethodInfo methodInfo = AccessTools.Method(item, "MoveNext", (Type[])null, (Type[])null);
						if (methodInfo != null && Uses30SecondTimeout(methodInfo))
						{
							yield return methodInfo;
						}
					}
				}
			}
		}

		private static IEnumerable<Type> TypesIn(Assembly assembly)
		{
			try
			{
				return assembly.GetTypes();
			}
			catch (ReflectionTypeLoadException ex)
			{
				return ex.Types.Where((Type type) => type != null);
			}
			catch (Exception)
			{
				return Enumerable.Empty<Type>();
			}
		}

		private static bool IsServerSyncWait(Type type)
		{
			if (!type.Name.Contains("waitForQueue"))
			{
				return false;
			}
			Type declaringType = type.DeclaringType;
			while (declaringType != null)
			{
				if (declaringType.Name == "ConfigSync" && declaringType.Namespace == "ServerSync")
				{
					return true;
				}
				declaringType = declaringType.DeclaringType;
			}
			return false;
		}

		private static bool Uses30SecondTimeout(MethodInfo step)
		{
			try
			{
				return PatchProcessor.GetOriginalInstructions((MethodBase)step, (ILGenerator)null).Any(IsServerSyncTimeout);
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static bool IsServerSyncTimeout(CodeInstruction instruction)
		{
			if (instruction.opcode == OpCodes.Ldc_R4 && instruction.operand is float num)
			{
				return num == 30f;
			}
			return false;
		}

		private static IEnumerable<CodeInstruction> RaiseTimeout(IEnumerable<CodeInstruction> instructions)
		{
			bool flag = false;
			foreach (CodeInstruction instruction in instructions)
			{
				if (flag && IsServerSyncTimeout(instruction))
				{
					CodeInstruction val = new CodeInstruction(OpCodes.Call, (object)CurrentTimeout);
					val.labels.AddRange(instruction.labels);
					val.blocks.AddRange(instruction.blocks);
					yield return val;
				}
				else
				{
					yield return instruction;
				}
				if (instruction.opcode == OpCodes.Ldstr && instruction.operand is string text && text.Contains("30 seconds"))
				{
					yield return new CodeInstruction(OpCodes.Call, (object)RewriteMessage);
				}
				flag = CodeInstructionExtensions.Calls(instruction, GameTime);
			}
		}

		private static float TimeoutSeconds()
		{
			return ServerSyncTimeoutPlugin.Timeout.Value;
		}

		private static string Message(string original)
		{
			return original.Replace("30 seconds", Mathf.RoundToInt(TimeoutSeconds()) + " seconds");
		}
	}
}