using System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using CancelJoiningLobby.Controllers; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("CancelJoiningLobby")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("CancelJoiningLobby")] [assembly: AssemblyTitle("CancelJoiningLobby")] [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 CancelJoiningLobby { [BepInPlugin("ZaboomafooW.CancelJoiningLobby", "Cancel Joining Lobby", "1.0.0")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "ZaboomafooW.CancelJoiningLobby"; public const string PluginName = "Cancel Joining Lobby"; public const string PluginVersion = "1.0.0"; private Harmony _harmony; internal static ManualLogSource Log { get; private set; } private void Awake() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; if (ConnectionCancellationController.Initialize()) { _harmony = new Harmony("ZaboomafooW.CancelJoiningLobby"); _harmony.PatchAll(); Log.LogInfo((object)"[CancelJoiningLobby] Loaded v1.0.0"); } } private void OnApplicationQuit() { ConnectionCancellationController.Cleanup(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; Log = null; } } } namespace CancelJoiningLobby.Patches { [HarmonyPatch(typeof(LobbySlot), "JoinLobbyAfterVerifying")] internal static class JoinLobbyAfterVerifyingPatch { private static void Prefix() { ConnectionCancellationController.CaptureJoinAttempt(); } } [HarmonyPatch(typeof(MenuManager), "SetLoadingScreen")] internal static class SetLoadingScreenPatch { private static void Postfix(bool isLoading) { if (!isLoading) { ConnectionCancellationController.OnLoadingScreenClosed(); } } } [HarmonyPatch(typeof(GameNetworkManager), "StartClient")] internal static class StartClientPatch { private static void Prefix() { ConnectionCancellationController.BeginContactingServer(); } private static void Postfix() { ConnectionCancellationController.CompleteStartClient(); } } } namespace CancelJoiningLobby.Controllers { internal static class CancelButtonController { private const string ButtonObjectName = "CancelJoiningLobby.CancelButton"; private const string ButtonText = "[ Cancel ]"; private static GameObject _cancelButtonObject; private static bool _hookLogged; internal static bool Show(MenuManager menuManager) { if (!EnsureButton(menuManager)) { return false; } _cancelButtonObject.SetActive(true); return true; } internal static void Hide() { if ((Object)(object)_cancelButtonObject != (Object)null) { _cancelButtonObject.SetActive(false); } } internal static void Cleanup() { if (!((Object)(object)_cancelButtonObject == (Object)null)) { Object.Destroy((Object)(object)_cancelButtonObject); _cancelButtonObject = null; _hookLogged = false; } } private static bool EnsureButton(MenuManager menuManager) { //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)menuManager == (Object)null || (Object)(object)menuManager.loadingText == (Object)null || (Object)(object)menuManager.loadingScreen == (Object)null || !menuManager.loadingScreen.activeInHierarchy || !((Component)menuManager.loadingText).gameObject.activeInHierarchy) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Loading screen UI was unavailable; cancel button was not created."); return false; } if ((Object)(object)_cancelButtonObject != (Object)null && (Object)(object)_cancelButtonObject.transform.parent != (Object)(object)((TMP_Text)menuManager.loadingText).transform) { Object.Destroy((Object)(object)_cancelButtonObject); _cancelButtonObject = null; } if ((Object)(object)_cancelButtonObject != (Object)null) { return true; } Canvas canvas = ((Graphic)menuManager.loadingText).canvas; if ((Object)(object)canvas == (Object)null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Loading screen canvas was unavailable; cancel button was not created."); return false; } GameObject val = new GameObject("CancelJoiningLobby.CancelButton", new Type[4] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(CancelButtonInputHandler) }); val.transform.SetParent(((TMP_Text)menuManager.loadingText).transform, false); RectTransform component = val.GetComponent(); component.anchorMin = new Vector2(0.5f, 0f); component.anchorMax = new Vector2(0.5f, 0f); component.pivot = new Vector2(0.5f, 1f); component.anchoredPosition = Vector2.zero; Image component2 = val.GetComponent(); ((Graphic)component2).color = Color.clear; ((Graphic)component2).raycastTarget = false; GameObject val2 = new GameObject("CancelJoiningLobby.CancelButton.Text", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI) }); val2.transform.SetParent(val.transform, false); TextMeshProUGUI loadingText = menuManager.loadingText; TextMeshProUGUI component3 = val2.GetComponent(); ((TMP_Text)component3).text = "[ Cancel ]"; ((TMP_Text)component3).font = ((TMP_Text)loadingText).font; ((TMP_Text)component3).fontSharedMaterial = ((TMP_Text)loadingText).fontSharedMaterial; ((TMP_Text)component3).fontSize = ((TMP_Text)loadingText).fontSize; ((TMP_Text)component3).fontStyle = ((TMP_Text)loadingText).fontStyle; ((Graphic)component3).color = ((Graphic)loadingText).color; ((TMP_Text)component3).alignment = (TextAlignmentOptions)514; ((TMP_Text)component3).enableWordWrapping = false; ((Graphic)component3).raycastTarget = false; ((TMP_Text)component3).ForceMeshUpdate(false, false); component.sizeDelta = new Vector2(((TMP_Text)component3).preferredWidth, ((TMP_Text)component3).preferredHeight); RectTransform component4 = val2.GetComponent(); component4.anchorMin = Vector2.zero; component4.anchorMax = Vector2.one; component4.offsetMin = Vector2.zero; component4.offsetMax = Vector2.zero; val.GetComponent().Initialize(component, canvas, component2, component3, ((Graphic)loadingText).color); _cancelButtonObject = val; _cancelButtonObject.SetActive(false); if (!_hookLogged) { _hookLogged = true; Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Hook succeeded: cancel input attached to the v81 loading screen."); } return true; } } internal sealed class CancelButtonInputHandler : MonoBehaviour { private RectTransform _buttonRect; private Canvas _canvas; private Image _background; private TextMeshProUGUI _text; private Color _normalTextColor; private bool _isHighlighted; internal void Initialize(RectTransform buttonRect, Canvas canvas, Image background, TextMeshProUGUI text, Color normalTextColor) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) _buttonRect = buttonRect; _canvas = canvas; _background = background; _text = text; _normalTextColor = normalTextColor; SetHighlighted(highlighted: false); } private void Update() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) if (Keyboard.current != null && ((ButtonControl)Keyboard.current.escapeKey).wasPressedThisFrame) { Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Cancel requested with Escape."); ConnectionCancellationController.CancelConnectionAttempt(); return; } if ((Object)(object)_buttonRect == (Object)null || (Object)(object)_canvas == (Object)null || Mouse.current == null) { SetHighlighted(highlighted: false); return; } Vector2 val = ((InputControl)(object)((Pointer)Mouse.current).position).ReadValue(); Camera val2 = (((int)_canvas.renderMode == 0) ? null : _canvas.worldCamera); bool flag = RectTransformUtility.RectangleContainsScreenPoint(_buttonRect, val, val2); SetHighlighted(flag); if (flag && Mouse.current.leftButton.wasReleasedThisFrame) { Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Cancel clicked."); ConnectionCancellationController.CancelConnectionAttempt(); } } private void OnDisable() { SetHighlighted(highlighted: false); } private void SetHighlighted(bool highlighted) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_background == (Object)null) && !((Object)(object)_text == (Object)null) && _isHighlighted != highlighted) { _isHighlighted = highlighted; ((Graphic)_background).color = (highlighted ? _normalTextColor : Color.clear); ((Graphic)_text).color = (highlighted ? Color.black : _normalTextColor); } } } internal static class ConnectionCancellationController { [CompilerGenerated] private static class <>O { public static TransportEventDelegate <0>__OnTransportEvent; public static Action <1>__OnClientStopped; } private const BindingFlags InstanceNonPublic = BindingFlags.Instance | BindingFlags.NonPublic; private static MethodInfo _setInstanceValuesBackToDefaultMethod; private static NetworkTransport _observedTransport; private static NetworkManager _shutdownNetworkManager; private static MenuManager _capturedMenu; private static GameObject _previouslySelectedObject; private static bool _joinAttemptCaptured; private static bool _contactingServer; private static bool _cancellationInProgress; private static bool _originalMenuButtonsActive; private static bool _originalServerListActive; private static bool _originalLoadingScreenActive; private static float _originalMenuAudioVolume; private static bool _menuAudioCaptured; internal static bool Initialize() { try { _setInstanceValuesBackToDefaultMethod = typeof(GameNetworkManager).GetMethod("SetInstanceValuesBackToDefault", BindingFlags.Instance | BindingFlags.NonPublic); } catch (Exception ex) { Plugin.Log.LogError((object)("[CancelJoiningLobby] Failed to resolve verified vanilla connection cleanup method: " + ex)); return false; } if (_setInstanceValuesBackToDefaultMethod == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Verified vanilla connection cleanup method was not found."); return false; } return true; } internal static void CaptureJoinAttempt() { if (_cancellationInProgress) { return; } GameNetworkManager instance = GameNetworkManager.Instance; if ((Object)(object)instance == (Object)null || instance.disableSteam) { ClearJoinAttempt(); return; } MenuManager val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null || (Object)(object)val.menuButtons == (Object)null || (Object)(object)val.serverListUIContainer == (Object)null || (Object)(object)val.loadingScreen == (Object)null || !val.serverListUIContainer.activeSelf) { ClearJoinAttempt(); return; } _capturedMenu = val; _originalMenuButtonsActive = val.menuButtons.activeSelf; _originalServerListActive = val.serverListUIContainer.activeSelf; _originalLoadingScreenActive = val.loadingScreen.activeSelf; _menuAudioCaptured = (Object)(object)val.MenuAudio != (Object)null; if (_menuAudioCaptured) { _originalMenuAudioVolume = val.MenuAudio.volume; } _previouslySelectedObject = (((Object)(object)EventSystem.current != (Object)null) ? EventSystem.current.currentSelectedGameObject : null); _joinAttemptCaptured = true; _contactingServer = false; CancelButtonController.Hide(); } internal static void BeginContactingServer() { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Expected O, but got Unknown if (!_joinAttemptCaptured || _cancellationInProgress) { return; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)GameNetworkManager.Instance == (Object)null || (Object)(object)singleton == (Object)null || singleton.IsServer || singleton.IsHost) { ClearJoinAttempt(); return; } NetworkTransport val = ((singleton.NetworkConfig != null) ? singleton.NetworkConfig.NetworkTransport : null); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Network transport was unavailable when the server connection attempt started."); ClearJoinAttempt(); return; } UnsubscribeTransport(); _observedTransport = val; NetworkTransport observedTransport = _observedTransport; object obj = <>O.<0>__OnTransportEvent; if (obj == null) { TransportEventDelegate val2 = OnTransportEvent; <>O.<0>__OnTransportEvent = val2; obj = (object)val2; } observedTransport.OnTransportEvent += (TransportEventDelegate)obj; _contactingServer = true; } internal static void CompleteStartClient() { if (_contactingServer && _joinAttemptCaptured && !_cancellationInProgress) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsListening || !singleton.IsClient) { ClearJoinAttempt(); } else if (!CancelButtonController.Show(_capturedMenu)) { ClearJoinAttempt(); } } } internal static void OnLoadingScreenClosed() { if (!_cancellationInProgress) { ClearJoinAttempt(); } } internal static void CancelConnectionAttempt() { if (_contactingServer && _joinAttemptCaptured && !_cancellationInProgress) { GameNetworkManager instance = GameNetworkManager.Instance; NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)instance == (Object)null || (Object)(object)singleton == (Object)null || !singleton.IsListening || !singleton.IsClient) { ClearJoinAttempt(); return; } if (!ResetGameNetworkManagerState(instance)) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Cancellation was aborted because vanilla connection state could not be reset safely."); return; } _cancellationInProgress = true; _contactingServer = false; CancelButtonController.Hide(); UnsubscribeTransport(); instance.localClientWaitingForApproval = false; instance.LeaveCurrentSteamLobby(); instance.steamLobbyName = SteamClient.Name; UnsubscribeClientStopped(); _shutdownNetworkManager = singleton; _shutdownNetworkManager.OnClientStopped += OnClientStopped; _shutdownNetworkManager.Shutdown(true); Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Cancelled server connection attempt; Netcode shutdown started."); } } internal static void Cleanup() { UnsubscribeTransport(); UnsubscribeClientStopped(); CancelButtonController.Cleanup(); ClearCapturedState(); _setInstanceValuesBackToDefaultMethod = null; _cancellationInProgress = false; } private static void OnTransportEvent(NetworkEvent eventType, ulong clientId, ArraySegment payload, float receiveTime) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 if (_contactingServer && !_cancellationInProgress) { if ((int)eventType == 1) { ClearJoinAttempt(); } else if ((int)eventType == 2 || (int)eventType == 3) { ClearJoinAttempt(); } } } private static void OnClientStopped(bool wasServer) { UnsubscribeClientStopped(); GameNetworkManager instance = GameNetworkManager.Instance; if ((Object)(object)instance != (Object)null) { instance.disconnectReason = 0; instance.disconnectionReasonMessage = string.Empty; } RestoreCapturedMenuState(); RefreshServerList(); _cancellationInProgress = false; ClearCapturedState(); Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Returned to the server browser after cancellation."); } private static void RefreshServerList() { SteamLobbyManager val = Object.FindObjectOfType(); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Server list manager was unavailable after cancellation; refresh could not be requested."); return; } val.LoadServerList(); Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Server list refresh requested after cancellation."); } private static bool ResetGameNetworkManagerState(GameNetworkManager gameNetworkManager) { if (_setInstanceValuesBackToDefaultMethod == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Verified vanilla connection cleanup method is unavailable."); return false; } try { _setInstanceValuesBackToDefaultMethod.Invoke(gameNetworkManager, null); return true; } catch (TargetInvocationException ex) { Exception? obj = ex.InnerException ?? ex; Plugin.Log.LogError((object)("[CancelJoiningLobby] Vanilla connection cleanup failed: " + obj)); return false; } catch (Exception ex2) { Plugin.Log.LogError((object)("[CancelJoiningLobby] Vanilla connection cleanup failed: " + ex2)); return false; } } private static void RestoreCapturedMenuState() { if ((Object)(object)_capturedMenu == (Object)null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Captured menu was unavailable after cancellation; UI state could not be restored."); return; } if ((Object)(object)_capturedMenu.loadingScreen != (Object)null) { _capturedMenu.loadingScreen.SetActive(_originalLoadingScreenActive); } if ((Object)(object)_capturedMenu.menuButtons != (Object)null) { _capturedMenu.menuButtons.SetActive(_originalMenuButtonsActive); } if ((Object)(object)_capturedMenu.serverListUIContainer != (Object)null) { _capturedMenu.serverListUIContainer.SetActive(_originalServerListActive); } if (_menuAudioCaptured && (Object)(object)_capturedMenu.MenuAudio != (Object)null) { _capturedMenu.MenuAudio.volume = _originalMenuAudioVolume; } if ((Object)(object)EventSystem.current != (Object)null && (Object)(object)_previouslySelectedObject != (Object)null && _previouslySelectedObject.activeInHierarchy) { EventSystem.current.SetSelectedGameObject(_previouslySelectedObject); } } private static void ClearJoinAttempt() { _contactingServer = false; CancelButtonController.Hide(); UnsubscribeTransport(); ClearCapturedState(); } private static void ClearCapturedState() { _joinAttemptCaptured = false; _capturedMenu = null; _previouslySelectedObject = null; _menuAudioCaptured = false; _originalMenuAudioVolume = 0f; } private static void UnsubscribeTransport() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown if (!((Object)(object)_observedTransport == (Object)null)) { NetworkTransport observedTransport = _observedTransport; object obj = <>O.<0>__OnTransportEvent; if (obj == null) { TransportEventDelegate val = OnTransportEvent; <>O.<0>__OnTransportEvent = val; obj = (object)val; } observedTransport.OnTransportEvent -= (TransportEventDelegate)obj; _observedTransport = null; } } private static void UnsubscribeClientStopped() { if (!((Object)(object)_shutdownNetworkManager == (Object)null)) { _shutdownNetworkManager.OnClientStopped -= OnClientStopped; _shutdownNetworkManager = null; } } } }