using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; 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.1.0")] [assembly: AssemblyInformationalVersion("1.0.1")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("1.0.1.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 { 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; } private static bool HasOriginalDisconnectInfo { get; set; } internal static void RecordServer(SteamId hostSteamId, GameNetworkManager gameNetworkManager) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) SavedHostSteamId = SteamId.op_Implicit(hostSteamId); if (!ReconnectInProgress) { ClearOriginalDisconnectInfo(); } 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) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (SavedHostSteamId == 0L) { Plugin.Log.LogWarning((object)"[BetterDisconnectReasons] Unexpected disconnect, but no host Steam ID was saved. Using vanilla handling."); return; } 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (PendingReconnect && !ReconnectInProgress && SavedHostSteamId != 0L) { PendingReconnect = false; ReconnectInProgress = true; Plugin.Log.LogInfo((object)"[BetterDisconnectReasons] Starting reconnect."); GameNetworkManager instance = GameNetworkManager.Instance; if ((Object)(object)instance == (Object)null) { FailReconnect("Server no longer exists."); } else if (HasSavedLobby) { instance.JoinLobby(SavedLobby, SteamId.op_Implicit(SavedHostSteamId)); } else { 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 void CompleteSuccessfulReconnect() { if (ReconnectInProgress) { ReconnectInProgress = false; ClearOriginalDisconnectInfo(); Plugin.Log.LogInfo((object)"[BetterDisconnectReasons] Reconnect accepted."); } } internal static string NormalizeDisconnectionReason(string reason) { if (string.IsNullOrEmpty(reason)) { return reason; } switch (reason) { case "You cannot rejoin after being kicked.": return "You were kicked."; case "Game has already started!": if (OriginalDisconnectWasClosedByPeer()) { return "You were kicked."; } if (OriginalDisconnectWasConnectionFailure()) { return "You disconnected. The game has already started."; } return reason; case "Lobby is full!": if (OriginalDisconnectWasClosedByPeer()) { return "You were kicked."; } if (OriginalDisconnectWasConnectionFailure()) { return "You disconnected. The server is now full."; } return "Server is full."; case "The host was not accepting connections.": return "Server no longer exists."; case "Unknown; please verify your game files.": return "Connection approval request was invalid."; default: return reason; } } internal static void CompleteFailedReconnect(string displayedReason) { if (ReconnectInProgress) { ReconnectInProgress = false; Plugin.Log.LogInfo((object)("[BetterDisconnectReasons] Reconnect failed: " + displayedReason)); } } internal static void FailReconnect(string reason) { ReconnectInProgress = false; PendingReconnect = 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(); } 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 bool OriginalDisconnectWasConnectionFailure() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected I4, but got Unknown if ((int)OriginalDisconnectState == 5) { return true; } int num = (int)OriginalDisconnectEndReason; if (num >= 3000) { return num <= 5999; } return false; } private static void ClearOriginalDisconnectInfo() { OriginalDisconnectState = (ConnectionState)0; OriginalDisconnectEndReason = (NetConnectionEnd)0; HasOriginalDisconnectInfo = false; } } [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) && !ReconnectController.ReconnectInProgress && !ReconnectController.PendingReconnect && !__instance.isDisconnecting && !__instance.localClientWaitingForApproval && !singleton.IsServer && singleton.IsClient && (clientId == 0L || clientId == singleton.LocalClientId)) { ReconnectController.RecordUnexpectedDisconnect(__instance); } } [HarmonyPostfix] [HarmonyPatch(typeof(GameNetworkManager), "Singleton_OnClientConnectedCallback")] private static void ClientConnectedPostfix(ulong clientId) { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && ReconnectController.ReconnectInProgress && clientId == singleton.LocalClientId) { ReconnectController.CompleteSuccessfulReconnect(); } } [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) { if (!isLoading && ReconnectController.ReconnectInProgress) { GameNetworkManager instance = GameNetworkManager.Instance; if ((Object)(object)instance != (Object)null && !string.IsNullOrEmpty(instance.disconnectionReasonMessage)) { instance.disconnectionReasonMessage = ReconnectController.NormalizeDisconnectionReason(instance.disconnectionReasonMessage); } else if (!string.IsNullOrEmpty(overrideMessage)) { overrideMessage = "Server no longer exists."; } else if ((int)result == 5 || (int)result == 2) { overrideMessage = "Server no longer exists."; } } } [HarmonyPostfix] [HarmonyPatch(typeof(MenuManager), "SetLoadingScreen")] private static void SetLoadingScreenPostfix(bool isLoading, string overrideMessage) { if (!isLoading && ReconnectController.ReconnectInProgress) { string text = overrideMessage; if (string.IsNullOrEmpty(text) && (Object)(object)GameNetworkManager.Instance != (Object)null) { text = GameNetworkManager.Instance.disconnectionReasonMessage; } if (string.IsNullOrEmpty(text)) { text = "Server no longer exists."; } ReconnectController.CompleteFailedReconnect(text); } } } [HarmonyPatch(typeof(ConnectionManager), "OnDisconnected")] internal static class SteamConnectionPatches { [HarmonyPrefix] 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.1")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "ZaboomafooW.BetterDisconnectReasons"; public const string PluginName = "BetterDisconnectReasons"; public const string PluginVersion = "1.0.1"; 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.1"); Log.LogInfo((object)"[BetterDisconnectReasons] Hook succeeded: disconnect/reconnect patches applied."); } private void OnApplicationQuit() { ReconnectController.ResetAll(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } Log = null; } } }