Decompiled source of BetterDisconnectReasons v1.0.3

ZaboomafooW-BetterDisconnectReasons-v1.0.3.dll

Decompiled a month ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Netcode.Transports.Facepunch;
using Steamworks;
using Steamworks.Data;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("BetterDisconnectReasons")]
[assembly: AssemblyDescription("Clearer client-side disconnect messages for Lethal Company v81")]
[assembly: AssemblyCompany("ZaboomafooW")]
[assembly: AssemblyProduct("BetterDisconnectReasons")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("1.0.3.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 BetterDisconnectReasons
{
	internal static class ReconnectController
	{
		private const float DirectReconnectSnapshotSeconds = 2f;

		private const string HostShutdownReason = "Disconnected due to host shutting down.";

		internal const string KickedMessage = "You were kicked.";

		internal const string LostConnectionMessage = "You lost connection.";

		internal const string ServerClosedMessage = "Server was closed.";

		internal static ulong SavedHostSteamId { get; private set; }

		internal static Lobby SavedLobby { get; private set; }

		internal static bool HasSavedLobby { get; private set; }

		internal static bool PendingReconnect { get; private set; }

		internal static bool ReconnectInProgress { get; private set; }

		internal static ConnectionState OriginalDisconnectState { get; private set; }

		internal static NetConnectionEnd OriginalDisconnectEndReason { get; private set; }

		internal static string OriginalDisconnectReason { get; private set; }

		private static bool HasOriginalDisconnectInfo { get; set; }

		private static bool DirectReconnectSnapshotActive { get; set; }

		private static bool DirectReconnectSnapshotTimedOut { get; set; }

		private static float DirectReconnectStartedAt { get; set; }

		private static string ForcedReconnectReason { get; set; }

		internal static void Update()
		{
			if (ReconnectInProgress && DirectReconnectSnapshotActive && !(Time.realtimeSinceStartup - DirectReconnectStartedAt < 2f))
			{
				ExpireDirectReconnectSnapshot();
				NetworkManager singleton = NetworkManager.Singleton;
				if ((Object)(object)singleton != (Object)null && singleton.NetworkConfig != null && (Object)(object)singleton.NetworkConfig.NetworkTransport != (Object)null)
				{
					singleton.NetworkConfig.NetworkTransport.DisconnectLocalClient();
				}
				else
				{
					FailReconnect("Server was closed.");
				}
			}
		}

		internal static void RecordServer(SteamId hostSteamId, GameNetworkManager gameNetworkManager)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			SavedHostSteamId = SteamId.op_Implicit(hostSteamId);
			if (!ReconnectInProgress)
			{
				ClearOriginalDisconnectInfo();
				ClearReconnectSnapshot();
			}
			if ((Object)(object)gameNetworkManager != (Object)null && gameNetworkManager.currentLobby.HasValue)
			{
				SavedLobby = gameNetworkManager.currentLobby.Value;
				HasSavedLobby = true;
			}
			else if (!ReconnectInProgress)
			{
				HasSavedLobby = false;
			}
		}

		internal static void RecordSteamDisconnect(ConnectionInfo info)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			if (!ReconnectInProgress && !PendingReconnect && !HasOriginalDisconnectInfo)
			{
				GameNetworkManager instance = GameNetworkManager.Instance;
				if (!((Object)(object)instance == (Object)null) && !instance.isDisconnecting && !instance.localClientWaitingForApproval)
				{
					OriginalDisconnectState = ((ConnectionInfo)(ref info)).State;
					OriginalDisconnectEndReason = ((ConnectionInfo)(ref info)).EndReason;
					HasOriginalDisconnectInfo = true;
					Plugin.Log.LogInfo((object)$"[BetterDisconnectReasons] Saved Steam disconnect: state={OriginalDisconnectState}, endReason={OriginalDisconnectEndReason}.");
				}
			}
		}

		internal static void RecordUnexpectedDisconnect(GameNetworkManager gameNetworkManager, string disconnectReason)
		{
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			OriginalDisconnectReason = disconnectReason ?? string.Empty;
			if (OriginalDisconnectReason == "Disconnected due to host shutting down.")
			{
				PendingReconnect = false;
				ReconnectInProgress = false;
				ClearReconnectSnapshot();
				if ((Object)(object)gameNetworkManager != (Object)null)
				{
					gameNetworkManager.disconnectionReasonMessage = "Server was closed.";
				}
				Plugin.Log.LogInfo((object)"[BetterDisconnectReasons] Server was closed.");
			}
			else if (OriginalDisconnectWasConnectionFailure() && ((Object)(object)StartOfRound.Instance == (Object)null || !StartOfRound.Instance.inShipPhase))
			{
				PendingReconnect = false;
				ReconnectInProgress = false;
				ClearReconnectSnapshot();
				if ((Object)(object)gameNetworkManager != (Object)null)
				{
					gameNetworkManager.disconnectionReasonMessage = "You lost connection.";
				}
				Plugin.Log.LogInfo((object)"[BetterDisconnectReasons] You lost connection.");
			}
			else if (SavedHostSteamId == 0L)
			{
				if (OriginalDisconnectWasConnectionFailure() && (Object)(object)gameNetworkManager != (Object)null)
				{
					gameNetworkManager.disconnectionReasonMessage = "You lost connection.";
					Plugin.Log.LogInfo((object)"[BetterDisconnectReasons] You lost connection.");
				}
				else
				{
					Plugin.Log.LogWarning((object)"[BetterDisconnectReasons] Unexpected disconnect, but no host Steam ID was saved. Using vanilla handling.");
				}
			}
			else
			{
				if ((Object)(object)gameNetworkManager != (Object)null && gameNetworkManager.currentLobby.HasValue)
				{
					SavedLobby = gameNetworkManager.currentLobby.Value;
					HasSavedLobby = true;
				}
				PendingReconnect = true;
				ReconnectInProgress = false;
				Plugin.Log.LogInfo((object)$"[BetterDisconnectReasons] Unexpected disconnect. Reconnecting to host {SavedHostSteamId}.");
			}
		}

		internal static void MarkLobbyClosedForRound()
		{
			HasSavedLobby = false;
		}

		internal static void BeginReconnect()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			if (!PendingReconnect || ReconnectInProgress || SavedHostSteamId == 0L)
			{
				return;
			}
			PendingReconnect = false;
			ReconnectInProgress = true;
			ForcedReconnectReason = null;
			DirectReconnectSnapshotActive = false;
			DirectReconnectSnapshotTimedOut = false;
			Plugin.Log.LogInfo((object)"[BetterDisconnectReasons] Starting reconnect.");
			GameNetworkManager instance = GameNetworkManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				FailReconnect(GetReconnectFailureMessage());
				return;
			}
			if (HasSavedLobby)
			{
				instance.JoinLobby(SavedLobby, SteamId.op_Implicit(SavedHostSteamId));
				return;
			}
			if (OriginalDisconnectWasClosedByPeer() && string.IsNullOrEmpty(OriginalDisconnectReason))
			{
				DirectReconnectSnapshotActive = true;
				DirectReconnectStartedAt = Time.realtimeSinceStartup;
			}
			instance.StartClient(SteamId.op_Implicit(SavedHostSteamId));
		}

		internal static SteamId ResolveReconnectTarget(SteamId requestedTarget)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (!ReconnectInProgress || SavedHostSteamId == 0L)
			{
				return requestedTarget;
			}
			return SteamId.op_Implicit(SavedHostSteamId);
		}

		internal static bool AllowSteamConnection()
		{
			if (DirectReconnectSnapshotTimedOut)
			{
				return false;
			}
			if (!ReconnectInProgress || !DirectReconnectSnapshotActive)
			{
				return true;
			}
			float num = Time.realtimeSinceStartup - DirectReconnectStartedAt;
			if (num >= 2f)
			{
				ExpireDirectReconnectSnapshot();
				return false;
			}
			DirectReconnectSnapshotActive = false;
			Plugin.Log.LogInfo((object)$"[BetterDisconnectReasons] Reconnect reached server in {num:0.00}s.");
			return true;
		}

		internal static string GetReconnectFailureOverride()
		{
			return ForcedReconnectReason;
		}

		internal static void CompleteSuccessfulReconnect()
		{
			if (ReconnectInProgress)
			{
				ReconnectInProgress = false;
				ClearOriginalDisconnectInfo();
				ClearReconnectSnapshot();
				Plugin.Log.LogInfo((object)"[BetterDisconnectReasons] Reconnect accepted.");
			}
		}

		internal static void CompleteInterruptedReconnect(GameNetworkManager gameNetworkManager)
		{
			if (ReconnectInProgress)
			{
				string reconnectFailureMessage = GetReconnectFailureMessage();
				ReconnectInProgress = false;
				PendingReconnect = false;
				ClearReconnectSnapshot();
				if ((Object)(object)gameNetworkManager != (Object)null)
				{
					gameNetworkManager.disconnectionReasonMessage = reconnectFailureMessage;
				}
				Plugin.Log.LogInfo((object)("[BetterDisconnectReasons] Reconnect failed before player validation: " + reconnectFailureMessage));
			}
		}

		internal static string NormalizeDisconnectionReason(string reason)
		{
			if (string.IsNullOrEmpty(reason))
			{
				return reason;
			}
			if (OriginalDisconnectWasConnectionFailure())
			{
				return reason;
			}
			switch (reason)
			{
			case "You cannot rejoin after being kicked.":
				return "You were kicked.";
			case "Game has already started!":
				if (OriginalDisconnectWasClosedByPeer() && string.IsNullOrEmpty(OriginalDisconnectReason))
				{
					return "You were kicked.";
				}
				return "You lost connection.";
			case "Lobby is full!":
				return "You lost connection.";
			case "The host was not accepting connections.":
				return "Server was closed.";
			case "Unknown; please verify your game files.":
				return "You lost connection.";
			default:
				return reason;
			}
		}

		internal static void CompleteFailedReconnect(string displayedReason)
		{
			if (ReconnectInProgress)
			{
				ReconnectInProgress = false;
				DirectReconnectSnapshotActive = false;
				if ((Object)(object)GameNetworkManager.Instance != (Object)null)
				{
					GameNetworkManager.Instance.disconnectionReasonMessage = string.Empty;
				}
				Plugin.Log.LogInfo((object)("[BetterDisconnectReasons] Reconnect failed: " + displayedReason));
			}
		}

		internal static void FailReconnect(string reason)
		{
			ReconnectInProgress = false;
			PendingReconnect = false;
			DirectReconnectSnapshotActive = false;
			if ((Object)(object)GameNetworkManager.Instance != (Object)null)
			{
				GameNetworkManager.Instance.disconnectionReasonMessage = reason;
			}
			Plugin.Log.LogInfo((object)("[BetterDisconnectReasons] Could not reconnect: " + reason));
		}

		internal static void ResetAll()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			SavedHostSteamId = 0uL;
			SavedLobby = default(Lobby);
			HasSavedLobby = false;
			PendingReconnect = false;
			ReconnectInProgress = false;
			ClearOriginalDisconnectInfo();
			ClearReconnectSnapshot();
		}

		private static void ExpireDirectReconnectSnapshot()
		{
			if (!DirectReconnectSnapshotTimedOut)
			{
				DirectReconnectSnapshotActive = false;
				DirectReconnectSnapshotTimedOut = true;
				ForcedReconnectReason = "Server was closed.";
				if ((Object)(object)GameNetworkManager.Instance != (Object)null)
				{
					GameNetworkManager.Instance.disconnectionReasonMessage = "Server was closed.";
				}
				Plugin.Log.LogInfo((object)"[BetterDisconnectReasons] Server was closed.");
			}
		}

		internal static string GetReconnectFailureMessage()
		{
			if (OriginalDisconnectWasConnectionFailure())
			{
				return "You lost connection.";
			}
			return "Server was closed.";
		}

		private static bool OriginalDisconnectWasConnectionFailure()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			return (int)OriginalDisconnectState == 5;
		}

		private static bool OriginalDisconnectWasClosedByPeer()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			return (int)OriginalDisconnectState == 4;
		}

		private static void ClearOriginalDisconnectInfo()
		{
			OriginalDisconnectState = (ConnectionState)0;
			OriginalDisconnectEndReason = (NetConnectionEnd)0;
			OriginalDisconnectReason = string.Empty;
			HasOriginalDisconnectInfo = false;
		}

		private static void ClearReconnectSnapshot()
		{
			DirectReconnectSnapshotActive = false;
			DirectReconnectSnapshotTimedOut = false;
			DirectReconnectStartedAt = 0f;
			ForcedReconnectReason = null;
		}
	}
	[HarmonyPatch]
	internal static class GameNetworkManagerPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(GameNetworkManager), "StartClient")]
		private static void StartClientPrefix(GameNetworkManager __instance, ref SteamId id)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			id = ReconnectController.ResolveReconnectTarget(id);
			ReconnectController.RecordServer(id, __instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(GameNetworkManager), "Singleton_OnClientDisconnectCallback")]
		private static void ClientDisconnectPrefix(GameNetworkManager __instance, ulong clientId)
		{
			NetworkManager singleton = NetworkManager.Singleton;
			if ((Object)(object)singleton == (Object)null || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			if (ReconnectController.ReconnectInProgress)
			{
				if (!__instance.localClientWaitingForApproval && !singleton.IsServer && singleton.IsClient && (clientId == 0L || clientId == singleton.LocalClientId))
				{
					ReconnectController.CompleteInterruptedReconnect(__instance);
				}
			}
			else if (!ReconnectController.PendingReconnect && !__instance.isDisconnecting && !__instance.localClientWaitingForApproval && !singleton.IsServer && singleton.IsClient && (clientId == 0L || clientId == singleton.LocalClientId))
			{
				ReconnectController.RecordUnexpectedDisconnect(__instance, singleton.DisconnectReason);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(GameNetworkManager), "LeaveLobbyAtGameStart")]
		private static void LeaveLobbyAtGameStartPostfix()
		{
			ReconnectController.MarkLobbyClosedForRound();
		}
	}
	[HarmonyPatch]
	internal static class MenuManagerPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(MenuManager), "Start")]
		private static void StartPrefix()
		{
			if (ReconnectController.PendingReconnect && !((Object)(object)GameNetworkManager.Instance == (Object)null))
			{
				GameNetworkManager.Instance.disconnectReason = 0;
				GameNetworkManager.Instance.disconnectionReasonMessage = string.Empty;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(MenuManager), "Start")]
		private static void StartPostfix(MenuManager __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && ReconnectController.PendingReconnect)
			{
				ReconnectController.BeginReconnect();
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(MenuManager), "SetLoadingScreen")]
		private static void SetLoadingScreenPrefix(bool isLoading, ref RoomEnter result, ref string overrideMessage, out string __state)
		{
			__state = null;
			if (isLoading || !ReconnectController.ReconnectInProgress)
			{
				return;
			}
			string reconnectFailureOverride = ReconnectController.GetReconnectFailureOverride();
			if (!string.IsNullOrEmpty(reconnectFailureOverride))
			{
				overrideMessage = reconnectFailureOverride;
				__state = reconnectFailureOverride;
				return;
			}
			GameNetworkManager instance = GameNetworkManager.Instance;
			if ((Object)(object)instance != (Object)null && !string.IsNullOrEmpty(instance.disconnectionReasonMessage))
			{
				instance.disconnectionReasonMessage = ReconnectController.NormalizeDisconnectionReason(instance.disconnectionReasonMessage);
				__state = instance.disconnectionReasonMessage;
			}
			else if (!string.IsNullOrEmpty(overrideMessage))
			{
				overrideMessage = ReconnectController.GetReconnectFailureMessage();
				__state = overrideMessage;
			}
			else if ((int)result == 5 || (int)result == 2)
			{
				overrideMessage = ReconnectController.GetReconnectFailureMessage();
				__state = overrideMessage;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(MenuManager), "SetLoadingScreen")]
		private static void SetLoadingScreenPostfix(bool isLoading, string overrideMessage, string __state)
		{
			if (!isLoading && ReconnectController.ReconnectInProgress)
			{
				string text = __state;
				if (string.IsNullOrEmpty(text))
				{
					text = overrideMessage;
				}
				if (string.IsNullOrEmpty(text) && (Object)(object)GameNetworkManager.Instance != (Object)null)
				{
					text = GameNetworkManager.Instance.disconnectionReasonMessage;
				}
				if (string.IsNullOrEmpty(text))
				{
					text = ReconnectController.GetReconnectFailureMessage();
				}
				ReconnectController.CompleteFailedReconnect(text);
			}
		}
	}
	[HarmonyPatch]
	internal static class PlayerControllerBPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(PlayerControllerB), "SendNewPlayerValuesClientRpc")]
		private static void SendNewPlayerValuesClientRpcPostfix(PlayerControllerB __instance)
		{
			GameNetworkManager instance = GameNetworkManager.Instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.localPlayerController != (Object)(object)__instance))
			{
				ReconnectController.CompleteSuccessfulReconnect();
			}
		}
	}
	[HarmonyPatch]
	internal static class SteamConnectionPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(ConnectionManager), "OnConnected")]
		private static bool OnConnectedPrefix(ConnectionManager __instance)
		{
			if (__instance == null || !(__instance.Interface is FacepunchTransport))
			{
				return true;
			}
			if (ReconnectController.AllowSteamConnection())
			{
				return true;
			}
			__instance.Close();
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ConnectionManager), "OnDisconnected")]
		private static void OnDisconnectedPrefix(ConnectionManager __instance, ConnectionInfo info)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (__instance != null && __instance.Interface is FacepunchTransport)
			{
				ReconnectController.RecordSteamDisconnect(info);
			}
		}
	}
	[BepInPlugin("ZaboomafooW.BetterDisconnectReasons", "BetterDisconnectReasons", "1.0.3")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "ZaboomafooW.BetterDisconnectReasons";

		public const string PluginName = "BetterDisconnectReasons";

		public const string PluginVersion = "1.0.3";

		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("ZaboomafooW.BetterDisconnectReasons");
			_harmony.PatchAll();
			Log.LogInfo((object)"[BetterDisconnectReasons] Loaded v1.0.3");
			Log.LogInfo((object)"[BetterDisconnectReasons] Hook succeeded: disconnect/reconnect patches applied.");
		}

		private void Update()
		{
			ReconnectController.Update();
		}

		private void OnApplicationQuit()
		{
			ReconnectController.ResetAll();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Log = null;
		}
	}
}