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 CancelJoiningLobby v1.0.5
ZaboomafooW-CancelJoiningLobby-v1.0.5.dll
Decompiled 2 weeks agousing System; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading.Tasks; using BepInEx; using BepInEx.Logging; using CancelJoiningLobby.Controllers; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using Steamworks.Data; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.InputSystem; using UnityEngine.SceneManagement; 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.5.0")] [assembly: AssemblyInformationalVersion("1.0.5")] [assembly: AssemblyProduct("CancelJoiningLobby")] [assembly: AssemblyTitle("CancelJoiningLobby")] [assembly: AssemblyVersion("1.0.5.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.5")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "ZaboomafooW.CancelJoiningLobby"; public const string PluginName = "Cancel Joining Lobby"; public const string PluginVersion = "1.0.5"; 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(); if (!ConnectionCancellationController.InstallConnectionLifecycleHooks(_harmony)) { _harmony.UnpatchSelf(); _harmony = null; ConnectionCancellationController.Cleanup(); } else { Log.LogInfo((object)"[CancelJoiningLobby] Loaded v1.0.5"); } } } private void OnApplicationQuit() { ConnectionCancellationController.Cleanup(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } _harmony = null; Log = null; } } } namespace CancelJoiningLobby.Patches { [HarmonyPatch(typeof(MenuManager), "DisplayMenuNotification")] internal static class DisplayMenuNotificationPatch { private static bool Prefix() { return !ConnectionCancellationController.ShouldSuppressMenuNotification; } } [HarmonyPatch(typeof(LobbySlot), "JoinLobbyAfterVerifying")] internal static class JoinLobbyAfterVerifyingPatch { private static void Prefix(Lobby lobby) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) ConnectionCancellationController.CaptureJoinAttempt(lobby); } } [HarmonyPatch(typeof(GameNetworkManager), "JoinLobby")] internal static class JoinLobbyPatch { [HarmonyPriority(0)] private static bool Prefix(GameNetworkManager __instance, Lobby lobby) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return !ConnectionCancellationController.InterceptSteamLobbyJoin(__instance, lobby); } } [HarmonyPatch(typeof(StartOfRound), "OnPlayerConnectedClientRpc")] internal static class OnPlayerConnectedClientRpcPatch { [HarmonyPriority(800)] private static bool Prefix(StartOfRound __instance, ulong clientId) { return ConnectionCancellationController.ShouldRunLocalPlayerAssignment(__instance, clientId); } } [HarmonyPatch(typeof(MenuManager), "SetLoadingScreen")] internal static class SetLoadingScreenPatch { private static void Prefix(MenuManager __instance, out bool __state) { CancelButtonController.EnsureInitialized(__instance); __state = (Object)(object)__instance != (Object)null && (Object)(object)__instance.loadingScreen != (Object)null && __instance.loadingScreen.activeInHierarchy; } private static void Postfix(MenuManager __instance, bool isLoading, bool __state) { if (!(isLoading || __state)) { ConnectionCancellationController.OnLoadingScreenClosed(__instance); } } } [HarmonyPatch(typeof(GameNetworkManager), "StartClient")] internal static class StartClientPatch { private static void Prefix() { ConnectionCancellationController.BeginNetworkConnection(); } } [HarmonyPatch(typeof(StartOfRound), "OnEnable")] internal static class StartOfRoundOnEnablePatch { [HarmonyPriority(800)] private static bool Prefix() { return ConnectionCancellationController.ShouldRunStartOfRoundOnEnable(); } } } namespace CancelJoiningLobby.Controllers { internal static class CancelButtonController { private const string ButtonObjectName = "CancelJoiningLobby.CancelButton"; private const string ButtonText = "[ Cancel ]"; private static GameObject _cancelButtonObject; private static RectTransform _cancelRectTransform; private static TextMeshProUGUI _cancelText; private static Image _hoverImage; private static CancelButtonInputHandler _inputHandler; private static CancelLoadingScreenLifecycleHandler _lifecycleHandler; private static float _verticalGap; private static GameObject _initializedOwner; private static MenuManager _initializedMenuManager; private static bool _hookSuccessLogged; internal static bool EnsureInitialized(MenuManager menuManager) { //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Expected O, but got Unknown //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Expected O, but got Unknown //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)menuManager == (Object)null || (Object)(object)menuManager.loadingScreen == (Object)null || (Object)(object)menuManager.loadingText == (Object)null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Loading screen UI was unavailable; Cancel could not be initialized."); return false; } if ((Object)(object)_initializedOwner == (Object)(object)menuManager.loadingScreen && (Object)(object)_cancelButtonObject != (Object)null && (Object)(object)_cancelRectTransform != (Object)null && (Object)(object)_cancelText != (Object)null && (Object)(object)_hoverImage != (Object)null && (Object)(object)_inputHandler != (Object)null && (Object)(object)_lifecycleHandler != (Object)null) { return true; } SetCancellationAvailable(available: false); ClearCachedReferences(); RectTransform component = menuManager.loadingScreen.GetComponent<RectTransform>(); Canvas componentInParent = menuManager.loadingScreen.GetComponentInParent<Canvas>(); RectTransform rectTransform = ((TMP_Text)menuManager.loadingText).rectTransform; if ((Object)(object)component == (Object)null || (Object)(object)componentInParent == (Object)null || (Object)(object)rectTransform == (Object)null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Loading screen layout was unavailable; Cancel could not be initialized."); return false; } GameObject val = new GameObject("CancelJoiningLobby.CancelButton", new Type[4] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(CancelButtonInputHandler) }); val.transform.SetParent((Transform)(object)component, false); RectTransform component2 = val.GetComponent<RectTransform>(); component2.anchorMin = new Vector2(0.5f, 0.5f); component2.anchorMax = new Vector2(0.5f, 0.5f); component2.pivot = new Vector2(0.5f, 1f); Image component3 = val.GetComponent<Image>(); ((Graphic)component3).color = Color.clear; ((Graphic)component3).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 component4 = val2.GetComponent<TextMeshProUGUI>(); ((TMP_Text)component4).text = "[ Cancel ]"; ((TMP_Text)component4).font = ((TMP_Text)loadingText).font; ((TMP_Text)component4).fontSharedMaterial = ((TMP_Text)loadingText).fontSharedMaterial; ((TMP_Text)component4).fontSize = ((TMP_Text)loadingText).fontSize; ((TMP_Text)component4).fontStyle = ((TMP_Text)loadingText).fontStyle; ((Graphic)component4).color = ((Graphic)loadingText).color; ((TMP_Text)component4).alignment = (TextAlignmentOptions)514; ((TMP_Text)component4).enableWordWrapping = false; ((Graphic)component4).raycastTarget = false; ((TMP_Text)component4).ForceMeshUpdate(false, false); float preferredWidth = ((TMP_Text)component4).preferredWidth; float preferredHeight = ((TMP_Text)component4).preferredHeight; ((TMP_Text)component4).text = "[ Cancel ]\n[ Cancel ]"; ((TMP_Text)component4).ForceMeshUpdate(false, false); float verticalGap = ((TMP_Text)component4).preferredHeight - preferredHeight; ((TMP_Text)component4).text = "[ Cancel ]"; ((TMP_Text)component4).ForceMeshUpdate(false, false); component2.sizeDelta = new Vector2(preferredWidth, preferredHeight); RectTransform component5 = val2.GetComponent<RectTransform>(); component5.anchorMin = Vector2.zero; component5.anchorMax = Vector2.one; component5.offsetMin = Vector2.zero; component5.offsetMax = Vector2.zero; CancelButtonInputHandler component6 = val.GetComponent<CancelButtonInputHandler>(); component6.Initialize(component2, componentInParent, component3, component4, ((Graphic)loadingText).color); val.SetActive(false); _cancelButtonObject = val; _cancelRectTransform = component2; _cancelText = component4; _hoverImage = component3; _inputHandler = component6; _verticalGap = verticalGap; _initializedOwner = menuManager.loadingScreen; _initializedMenuManager = menuManager; CancelLoadingScreenLifecycleHandler cancelLoadingScreenLifecycleHandler = menuManager.loadingScreen.GetComponent<CancelLoadingScreenLifecycleHandler>(); if ((Object)(object)cancelLoadingScreenLifecycleHandler == (Object)null) { cancelLoadingScreenLifecycleHandler = menuManager.loadingScreen.AddComponent<CancelLoadingScreenLifecycleHandler>(); } _lifecycleHandler = cancelLoadingScreenLifecycleHandler; _lifecycleHandler.Initialize(menuManager); if (!_hookSuccessLogged) { _hookSuccessLogged = true; Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Hook succeeded: attached Cancel to the v81 loading screen."); } return true; } internal static bool IsInitializedOwner(MenuManager menuManager) { if ((Object)(object)menuManager != (Object)null && (Object)(object)menuManager.loadingScreen != (Object)null) { return (Object)(object)_initializedOwner == (Object)(object)menuManager.loadingScreen; } return false; } internal static void SetCancellationAvailable(bool available) { if ((Object)(object)_cancelButtonObject == (Object)null || (Object)(object)_inputHandler == (Object)null) { return; } if (available) { if (!PositionForActivation()) { _inputHandler.SetCancellationAvailable(available: false); _cancelButtonObject.SetActive(false); } else { _cancelButtonObject.SetActive(true); _inputHandler.SetCancellationAvailable(available: true); } } else { _inputHandler.SetCancellationAvailable(available: false); _cancelButtonObject.SetActive(false); } } internal static void Cleanup() { SetCancellationAvailable(available: false); if ((Object)(object)_lifecycleHandler != (Object)null) { Object.Destroy((Object)(object)_lifecycleHandler); } if ((Object)(object)_cancelButtonObject != (Object)null) { Object.Destroy((Object)(object)_cancelButtonObject); } ClearCachedReferences(); _hookSuccessLogged = false; } private static bool PositionForActivation() { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_initializedOwner == (Object)null || !_initializedOwner.activeInHierarchy || (Object)(object)_initializedMenuManager == (Object)null || (Object)(object)_initializedMenuManager.loadingText == (Object)null || (Object)(object)_cancelRectTransform == (Object)null) { return false; } RectTransform rectTransform = ((TMP_Text)_initializedMenuManager.loadingText).rectTransform; if ((Object)(object)rectTransform == (Object)null) { return false; } Rect rect = rectTransform.rect; float x = ((Rect)(ref rect)).center.x; rect = rectTransform.rect; Vector3 position = ((Transform)rectTransform).TransformPoint(new Vector3(x, ((Rect)(ref rect)).yMin - _verticalGap, 0f)); ((Transform)_cancelRectTransform).position = position; return true; } private static void ClearCachedReferences() { _cancelButtonObject = null; _cancelRectTransform = null; _cancelText = null; _hoverImage = null; _inputHandler = null; _lifecycleHandler = null; _verticalGap = 0f; _initializedOwner = null; _initializedMenuManager = null; } } internal sealed class CancelButtonInputHandler : MonoBehaviour { private RectTransform _buttonRectTransform; private Canvas _loadingScreenCanvas; private Image _buttonBackground; private TextMeshProUGUI _buttonText; private Color _normalTextColor; private bool _isHighlighted; private InputAction _cancelAction; internal void Initialize(RectTransform buttonRectTransform, Canvas loadingScreenCanvas, Image buttonBackground, TextMeshProUGUI buttonText, 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) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown _buttonRectTransform = buttonRectTransform; _loadingScreenCanvas = loadingScreenCanvas; _buttonBackground = buttonBackground; _buttonText = buttonText; _normalTextColor = normalTextColor; _cancelAction = new InputAction("Cancel", (InputActionType)1, "<Keyboard>/escape", (string)null, (string)null, (string)null); _cancelAction.performed += OnCancelPerformed; SetHighlighted(highlighted: false); } internal void SetCancellationAvailable(bool available) { if (_cancelAction != null) { if (available) { _cancelAction.Enable(); return; } _cancelAction.Disable(); SetHighlighted(highlighted: false); } } private void Update() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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 ((Object)(object)_buttonRectTransform == (Object)null || (Object)(object)_loadingScreenCanvas == (Object)null || Mouse.current == null) { SetHighlighted(highlighted: false); return; } Vector2 val = ((InputControl<Vector2>)(object)((Pointer)Mouse.current).position).ReadValue(); Camera val2 = (((int)_loadingScreenCanvas.renderMode == 0) ? null : _loadingScreenCanvas.worldCamera); bool flag = RectTransformUtility.RectangleContainsScreenPoint(_buttonRectTransform, val, val2); SetHighlighted(flag); if (flag && Mouse.current.leftButton.wasReleasedThisFrame) { Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Cancel clicked."); ConnectionCancellationController.CancelConnectionAttempt(); } } private void OnDestroy() { if (_cancelAction != null) { _cancelAction.Disable(); _cancelAction.performed -= OnCancelPerformed; _cancelAction.Dispose(); _cancelAction = null; } } private void OnCancelPerformed(CallbackContext callbackContext) { Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Cancel requested with Escape."); ConnectionCancellationController.CancelConnectionAttempt(); } 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)_buttonBackground == (Object)null) && !((Object)(object)_buttonText == (Object)null) && _isHighlighted != highlighted) { _isHighlighted = highlighted; ((Graphic)_buttonBackground).color = (highlighted ? _normalTextColor : Color.clear); ((Graphic)_buttonText).color = (highlighted ? Color.black : _normalTextColor); } } } internal sealed class CancelLoadingScreenLifecycleHandler : MonoBehaviour { private MenuManager _menuManager; internal void Initialize(MenuManager menuManager) { _menuManager = menuManager; } private void OnEnable() { if (!((Object)(object)_menuManager == (Object)null)) { ConnectionCancellationController.OnLoadingScreenOpened(_menuManager); } } private void OnDisable() { if (!((Object)(object)_menuManager == (Object)null)) { ConnectionCancellationController.OnLoadingScreenClosed(_menuManager); } } } internal static class ConnectionCancellationController { private enum JoinStage { None, LobbyRefresh, SteamLobbyJoin, NetworkConnecting, JoiningServer } private const BindingFlags InstanceNonPublic = BindingFlags.Instance | BindingFlags.NonPublic; private const BindingFlags InstancePublic = BindingFlags.Instance | BindingFlags.Public; private const BindingFlags StaticNonPublic = BindingFlags.Static | BindingFlags.NonPublic; private static MethodInfo _setInstanceValuesBackToDefaultMethod; private static MethodInfo _startDisconnectMethod; private static MethodInfo _resetGameValuesToDefaultMethod; private static MethodInfo _currentLobbySetterMethod; private static FieldInfo _timeOutLobbyRefreshCoroutineField; private static NetworkManager _joinNetworkManager; private static NetworkManager _networkManagerAwaitingShutdown; private static MenuManager _joinMenuManager; private static GameObject _previousMenuSelection; private static Lobby? _activeJoinLobby; private static JoinStage _joinStage; private static int _nextJoinAttemptId; private static int _activeJoinAttemptId; private static bool _joinAttemptTracked; private static bool _cancellationInProgress; private static bool _postApprovalCancellation; private static bool _steamJoinOperationPending; private static bool _suppressMenuNotification; private static bool _originalMenuButtonsActive; private static bool _originalServerListActive; private static bool _originalLoadingScreenActive; private static float _originalMenuAudioVolume; private static bool _menuAudioStateCaptured; internal static bool ShouldSuppressMenuNotification => _suppressMenuNotification; private static void OnClientStopped(bool wasServer) { UnsubscribeClientStopped(); ResetDisconnectReason(); ReturnToCapturedMenu(); Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Restored the previous menu after cancellation."); FinishCancellationToMenu(); } private static void CompletePostApprovalCancellation() { if (_postApprovalCancellation) { GameNetworkManager instance = GameNetworkManager.Instance; if ((Object)(object)instance == (Object)null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] GameNetworkManager was unavailable after cancelling late in the join; could not return to MainMenu."); ClearJoinAttempt(); _cancellationInProgress = false; _postApprovalCancellation = false; } else if (!ResetGameValuesToDefault(instance)) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Could not reset game state after cancelling late in the join."); ClearJoinAttempt(); _cancellationInProgress = false; _postApprovalCancellation = false; } else { ResetDisconnectReason(); CancelButtonController.SetCancellationAvailable(available: false); StopTrackingJoinNetwork(); SceneManager.sceneLoaded -= OnMainMenuLoadedAfterCancellation; SceneManager.sceneLoaded += OnMainMenuLoadedAfterCancellation; SceneManager.LoadScene("MainMenu"); } } } private static void OnMainMenuLoadedAfterCancellation(Scene scene, LoadSceneMode mode) { if (!string.Equals(((Scene)(ref scene)).name, "MainMenu", StringComparison.Ordinal)) { return; } MenuManager val = Object.FindObjectOfType<MenuManager>(); if ((Object)(object)val != (Object)null) { RestoreMainMenuState(val); if (_originalServerListActive) { RefreshServerList(); } } else { Plugin.Log.LogError((object)"[CancelJoiningLobby] MainMenu loaded without a MenuManager; the previous menu state could not be restored."); } ClearJoinAttempt(); _cancellationInProgress = false; _postApprovalCancellation = false; SceneManager.sceneLoaded -= OnMainMenuLoadedAfterCancellation; Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Cancelled after server approval; restored the previous MainMenu state."); } private static void ReturnToCapturedMenu() { MenuManager joinMenuManager = _joinMenuManager; if ((Object)(object)joinMenuManager == (Object)null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Saved menu state was unavailable after cancellation; could not restore the previous menu."); return; } _suppressMenuNotification = true; try { joinMenuManager.SetLoadingScreen(false, (RoomEnter)5, ""); } finally { _suppressMenuNotification = false; } RestoreJoinMenuState(joinMenuManager); if (_originalServerListActive) { RefreshServerList(); } } private static void RestoreJoinMenuState(MenuManager menuManager) { if ((Object)(object)menuManager.loadingScreen != (Object)null) { menuManager.loadingScreen.SetActive(_originalLoadingScreenActive); } if ((Object)(object)menuManager.menuButtons != (Object)null) { menuManager.menuButtons.SetActive(_originalMenuButtonsActive); } if ((Object)(object)menuManager.serverListUIContainer != (Object)null) { menuManager.serverListUIContainer.SetActive(_originalServerListActive); } if (_menuAudioStateCaptured && (Object)(object)menuManager.MenuAudio != (Object)null) { menuManager.MenuAudio.volume = _originalMenuAudioVolume; } if ((Object)(object)EventSystem.current != (Object)null && (Object)(object)_previousMenuSelection != (Object)null && _previousMenuSelection.activeInHierarchy) { EventSystem.current.SetSelectedGameObject(_previousMenuSelection); } } private static void RestoreMainMenuState(MenuManager menuManager) { if ((Object)(object)menuManager.menuButtons != (Object)null) { menuManager.menuButtons.SetActive(_originalMenuButtonsActive); } if ((Object)(object)menuManager.serverListUIContainer != (Object)null) { menuManager.serverListUIContainer.SetActive(_originalServerListActive); } if ((Object)(object)menuManager.loadingScreen != (Object)null) { menuManager.loadingScreen.SetActive(_originalLoadingScreenActive); } if (_menuAudioStateCaptured && (Object)(object)menuManager.MenuAudio != (Object)null) { menuManager.MenuAudio.volume = _originalMenuAudioVolume; } } private static void RefreshServerList() { SteamLobbyManager val = Object.FindObjectOfType<SteamLobbyManager>(); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Could not find the server list to refresh it after cancellation."); return; } val.LoadServerList(); Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Refreshed the server list after cancellation."); } internal static bool Initialize() { try { _setInstanceValuesBackToDefaultMethod = typeof(GameNetworkManager).GetMethod("SetInstanceValuesBackToDefault", BindingFlags.Instance | BindingFlags.NonPublic); _startDisconnectMethod = typeof(GameNetworkManager).GetMethod("StartDisconnect", BindingFlags.Instance | BindingFlags.NonPublic); _resetGameValuesToDefaultMethod = typeof(GameNetworkManager).GetMethod("ResetGameValuesToDefault", BindingFlags.Instance | BindingFlags.NonPublic); _currentLobbySetterMethod = typeof(GameNetworkManager).GetProperty("currentLobby", BindingFlags.Instance | BindingFlags.Public)?.GetSetMethod(nonPublic: true); _timeOutLobbyRefreshCoroutineField = typeof(LobbySlot).GetField("timeOutLobbyRefreshCoroutine", BindingFlags.Static | BindingFlags.NonPublic); } catch (Exception ex) { Plugin.Log.LogError((object)("[CancelJoiningLobby] Failed to find the Lethal Company join cleanup methods: " + ex)); return false; } if (_setInstanceValuesBackToDefaultMethod == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] GameNetworkManager.SetInstanceValuesBackToDefault was not found."); return false; } if (_startDisconnectMethod == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] GameNetworkManager.StartDisconnect was not found."); return false; } if (_resetGameValuesToDefaultMethod == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] GameNetworkManager.ResetGameValuesToDefault was not found."); return false; } if (_currentLobbySetterMethod == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] GameNetworkManager.currentLobby setter was not found."); return false; } if (_timeOutLobbyRefreshCoroutineField == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] LobbySlot.timeOutLobbyRefreshCoroutine was not found."); return false; } return true; } internal static bool InstallConnectionLifecycleHooks(Harmony harmony) { //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Expected O, but got Unknown //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Expected O, but got Unknown if (harmony == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Harmony was unavailable; connection hooks were not installed."); return false; } try { Type type = typeof(NetworkManager).Assembly.GetType("Unity.Netcode.ConnectionApprovedMessage", throwOnError: false); if (type == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Unity.Netcode.ConnectionApprovedMessage was not found."); return false; } MethodInfo method = type.GetMethod("Handle", BindingFlags.Instance | BindingFlags.Public); MethodInfo method2 = typeof(ConnectionCancellationController).GetMethod("ConnectionApprovedHandlePrefix", BindingFlags.Static | BindingFlags.NonPublic); MethodInfo method3 = typeof(NetworkManager).GetMethod("ShutdownInternal", BindingFlags.Instance | BindingFlags.NonPublic); MethodInfo method4 = typeof(ConnectionCancellationController).GetMethod("NetworkManagerShutdownInternalPostfix", BindingFlags.Static | BindingFlags.NonPublic); if (method == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Unity.Netcode.ConnectionApprovedMessage.Handle was not found."); return false; } if (method2 == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] ConnectionCancellationController.ConnectionApprovedHandlePrefix was not found."); return false; } if (method3 == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Unity.Netcode.NetworkManager.ShutdownInternal was not found."); return false; } if (method4 == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] ConnectionCancellationController.NetworkManagerShutdownInternalPostfix was not found."); return false; } harmony.Patch((MethodBase)method, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)method3, (HarmonyMethod)null, new HarmonyMethod(method4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("[CancelJoiningLobby] Failed to install connection hooks: " + ex)); return false; } } internal static void CaptureJoinAttempt(Lobby lobby) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (!_cancellationInProgress) { GameNetworkManager instance = GameNetworkManager.Instance; MenuManager menuManager = Object.FindObjectOfType<MenuManager>(); if ((Object)(object)instance == (Object)null || !CanCaptureMenuState(menuManager)) { ClearJoinAttempt(); return; } ClearJoinAttempt(); CaptureMenuState(menuManager); _activeJoinLobby = lobby; _activeJoinAttemptId = ++_nextJoinAttemptId; _joinAttemptTracked = true; _joinStage = JoinStage.LobbyRefresh; } } internal static void BeginNetworkConnection() { if (_cancellationInProgress) { return; } if (_joinAttemptTracked) { _joinStage = JoinStage.NetworkConnecting; _joinNetworkManager = NetworkManager.Singleton; return; } MenuManager menuManager = Object.FindObjectOfType<MenuManager>(); if (CanCaptureMenuState(menuManager)) { CaptureMenuState(menuManager); _activeJoinLobby = null; _activeJoinAttemptId = ++_nextJoinAttemptId; _joinAttemptTracked = true; _joinStage = JoinStage.NetworkConnecting; _joinNetworkManager = NetworkManager.Singleton; } } internal static void OnLoadingScreenOpened(MenuManager menuManager) { if (CancelButtonController.IsInitializedOwner(menuManager)) { if (!_joinAttemptTracked || _cancellationInProgress) { CancelButtonController.SetCancellationAvailable(available: false); } else if (_joinStage != JoinStage.LobbyRefresh && _joinStage != JoinStage.SteamLobbyJoin && _joinStage != JoinStage.NetworkConnecting && _joinStage != JoinStage.JoiningServer) { CancelButtonController.SetCancellationAvailable(available: false); } else { CancelButtonController.SetCancellationAvailable(available: true); } } } internal static void OnLoadingScreenClosed(MenuManager menuManager) { if (CancelButtonController.IsInitializedOwner(menuManager)) { CancelButtonController.SetCancellationAvailable(available: false); if (!_cancellationInProgress) { StopTrackingJoinNetwork(); ClearTrackedJoinState(); } } } internal static bool InterceptSteamLobbyJoin(GameNetworkManager gameNetworkManager, Lobby lobby) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) if (_joinAttemptTracked && !_cancellationInProgress && _joinStage == JoinStage.LobbyRefresh && _activeJoinLobby.HasValue) { Lobby value = _activeJoinLobby.Value; if (SteamId.op_Implicit(((Lobby)(ref value)).Id) == SteamId.op_Implicit(((Lobby)(ref lobby)).Id)) { if (_steamJoinOperationPending) { Plugin.Log.LogWarning((object)"[CancelJoiningLobby] A previous Steam lobby request is still finishing. Returned to the previous menu without starting another join."); _cancellationInProgress = true; CancelButtonController.SetCancellationAvailable(available: false); ReturnToCapturedMenu(); FinishCancellationToMenu(); return true; } if (gameNetworkManager.currentLobby.HasValue) { ClearJoinAttempt(); return false; } if (!SetCurrentLobby(gameNetworkManager, lobby)) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Could not set the current Steam lobby. Continuing with the normal game join without cancellation support."); ClearJoinAttempt(); return false; } gameNetworkManager.steamLobbyName = ((Lobby)(ref lobby)).GetData("name"); _joinStage = JoinStage.SteamLobbyJoin; _steamJoinOperationPending = true; int activeJoinAttemptId = _activeJoinAttemptId; JoinSteamLobbyAsync(gameNetworkManager, lobby, activeJoinAttemptId); return true; } } return false; } internal static void CancelConnectionAttempt() { if (_joinAttemptTracked && !_cancellationInProgress) { switch (_joinStage) { case JoinStage.LobbyRefresh: CancelLobbyRefresh(); break; case JoinStage.SteamLobbyJoin: CancelSteamLobbyJoin(); break; case JoinStage.NetworkConnecting: CancelNetworkConnectionBeforeApproval(); break; case JoinStage.JoiningServer: CancelNetworkConnectionAfterApproval(); break; } } } internal static void Cleanup() { UnsubscribeClientStopped(); CancelButtonController.Cleanup(); SceneManager.sceneLoaded -= OnMainMenuLoadedAfterCancellation; StopTrackingJoinNetwork(); ClearTrackedJoinState(); _setInstanceValuesBackToDefaultMethod = null; _startDisconnectMethod = null; _resetGameValuesToDefaultMethod = null; _currentLobbySetterMethod = null; _timeOutLobbyRefreshCoroutineField = null; _cancellationInProgress = false; _postApprovalCancellation = false; _steamJoinOperationPending = false; _suppressMenuNotification = false; } private static bool CanCaptureMenuState(MenuManager menuManager) { if ((Object)(object)menuManager != (Object)null && (Object)(object)menuManager.menuButtons != (Object)null && (Object)(object)menuManager.serverListUIContainer != (Object)null) { return (Object)(object)menuManager.loadingScreen != (Object)null; } return false; } private static void CaptureMenuState(MenuManager menuManager) { _joinMenuManager = menuManager; _originalMenuButtonsActive = menuManager.menuButtons.activeSelf; _originalServerListActive = menuManager.serverListUIContainer.activeSelf; _originalLoadingScreenActive = menuManager.loadingScreen.activeSelf; _menuAudioStateCaptured = (Object)(object)menuManager.MenuAudio != (Object)null; if (_menuAudioStateCaptured) { _originalMenuAudioVolume = menuManager.MenuAudio.volume; } _previousMenuSelection = (((Object)(object)EventSystem.current != (Object)null) ? EventSystem.current.currentSelectedGameObject : null); } private static bool StopLobbyRefreshTimeout(GameNetworkManager gameNetworkManager) { if (_timeOutLobbyRefreshCoroutineField == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] LobbySlot.timeOutLobbyRefreshCoroutine is unavailable."); return false; } try { object? value = _timeOutLobbyRefreshCoroutineField.GetValue(null); Coroutine val = (Coroutine)((value is Coroutine) ? value : null); if (val != null) { ((MonoBehaviour)gameNetworkManager).StopCoroutine(val); _timeOutLobbyRefreshCoroutineField.SetValue(null, null); } return true; } catch (Exception ex) { Plugin.Log.LogError((object)("[CancelJoiningLobby] Failed to stop the lobby refresh timeout: " + ex)); return false; } } private static bool SetCurrentLobby(GameNetworkManager gameNetworkManager, Lobby lobby) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (_currentLobbySetterMethod == null) { Plugin.Log.LogError((object)"[CancelJoiningLobby] GameNetworkManager.currentLobby setter is unavailable."); return false; } try { _currentLobbySetterMethod.Invoke(gameNetworkManager, new object[1] { lobby }); return true; } catch (Exception ex) { Plugin.Log.LogError((object)("[CancelJoiningLobby] Failed to set the current Steam lobby: " + ex)); return false; } } private static void ClearCurrentLobbyIfMatches(GameNetworkManager gameNetworkManager) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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) if (_activeJoinLobby.HasValue && gameNetworkManager.currentLobby.HasValue) { Lobby value = gameNetworkManager.currentLobby.Value; ulong num = SteamId.op_Implicit(((Lobby)(ref value)).Id); value = _activeJoinLobby.Value; if (num == SteamId.op_Implicit(((Lobby)(ref value)).Id)) { gameNetworkManager.SetCurrentLobbyNull(); gameNetworkManager.steamIdsInLobby.Clear(); } } } private static bool ResetGameNetworkManagerState(GameNetworkManager gameNetworkManager) { return InvokeGameNetworkManagerMethod(_setInstanceValuesBackToDefaultMethod, gameNetworkManager, "GameNetworkManager.SetInstanceValuesBackToDefault"); } private static bool StartVanillaDisconnectCleanup(GameNetworkManager gameNetworkManager) { return InvokeGameNetworkManagerMethod(_startDisconnectMethod, gameNetworkManager, "GameNetworkManager.StartDisconnect"); } private static bool ResetGameValuesToDefault(GameNetworkManager gameNetworkManager) { return InvokeGameNetworkManagerMethod(_resetGameValuesToDefaultMethod, gameNetworkManager, "GameNetworkManager.ResetGameValuesToDefault"); } private static bool InvokeGameNetworkManagerMethod(MethodInfo gameNetworkManagerMethod, GameNetworkManager gameNetworkManager, string memberName) { if (gameNetworkManagerMethod == null) { Plugin.Log.LogError((object)("[CancelJoiningLobby] " + memberName + " is unavailable.")); return false; } try { gameNetworkManagerMethod.Invoke(gameNetworkManager, null); return true; } catch (TargetInvocationException ex) { Exception? obj = ex.InnerException ?? ex; Plugin.Log.LogError((object)("[CancelJoiningLobby] " + memberName + " failed: " + obj)); return false; } catch (Exception ex2) { Plugin.Log.LogError((object)("[CancelJoiningLobby] " + memberName + " failed: " + ex2)); return false; } } private static void SubscribeClientStopped(NetworkManager networkManager) { UnsubscribeClientStopped(); _networkManagerAwaitingShutdown = networkManager; _networkManagerAwaitingShutdown.OnClientStopped += OnClientStopped; } private static void UnsubscribeClientStopped() { if (!((Object)(object)_networkManagerAwaitingShutdown == (Object)null)) { _networkManagerAwaitingShutdown.OnClientStopped -= OnClientStopped; _networkManagerAwaitingShutdown = null; } } private static void StopTrackingJoinNetwork() { _joinNetworkManager = null; } private static bool IsActiveJoinAttempt(int joinAttemptId, Lobby lobby) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_003b: Unknown result type (might be due to invalid IL or missing references) if (_joinAttemptTracked && !_cancellationInProgress && _activeJoinAttemptId == joinAttemptId && _activeJoinLobby.HasValue) { Lobby value = _activeJoinLobby.Value; return SteamId.op_Implicit(((Lobby)(ref value)).Id) == SteamId.op_Implicit(((Lobby)(ref lobby)).Id); } return false; } private static void FinishCancellationToMenu() { ClearJoinAttempt(); _cancellationInProgress = false; } private static void ClearJoinAttempt() { CancelButtonController.SetCancellationAvailable(available: false); StopTrackingJoinNetwork(); ClearTrackedJoinState(); } private static void ClearTrackedJoinState() { _joinAttemptTracked = false; _activeJoinAttemptId = 0; _activeJoinLobby = null; _joinStage = JoinStage.None; _joinMenuManager = null; _previousMenuSelection = null; _menuAudioStateCaptured = false; _originalMenuAudioVolume = 0f; } private static void ResetDisconnectReason() { GameNetworkManager instance = GameNetworkManager.Instance; if (!((Object)(object)instance == (Object)null)) { instance.disconnectReason = 0; instance.disconnectionReasonMessage = string.Empty; } } internal static bool ShouldRunStartOfRoundOnEnable() { if (!_cancellationInProgress || !_postApprovalCancellation) { return true; } Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Skipped round callback setup while cancellation was shutting the client down."); return false; } internal static bool ShouldRunLocalPlayerAssignment(StartOfRound startOfRound, ulong clientId) { if (!_joinAttemptTracked || _joinStage != JoinStage.JoiningServer || (Object)(object)startOfRound == (Object)null) { return true; } NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || ((NetworkBehaviour)startOfRound).IsServer || ((NetworkBehaviour)startOfRound).IsHost || clientId != singleton.LocalClientId) { return true; } if (_cancellationInProgress) { Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Skipped local player assignment because cancellation was already in progress."); return false; } Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Local player assignment started; Cancel is now disabled."); ClearJoinAttempt(); return true; } private static async Task JoinSteamLobbyAsync(GameNetworkManager gameNetworkManager, Lobby lobby, int joinAttemptId) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) RoomEnter val; try { val = await ((Lobby)(ref lobby)).Join(); } catch (Exception ex) { _steamJoinOperationPending = false; Plugin.Log.LogError((object)("[CancelJoiningLobby] Steam lobby request failed: " + ex)); if (IsActiveJoinAttempt(joinAttemptId, lobby)) { gameNetworkManager.LeaveCurrentSteamLobby(); gameNetworkManager.steamLobbyName = SteamClient.Name; MenuManager val2 = Object.FindObjectOfType<MenuManager>(); if ((Object)(object)val2 != (Object)null) { val2.SetLoadingScreen(false, (RoomEnter)5, "The host has not loaded or has already landed their ship."); } else { ClearJoinAttempt(); } } return; } _steamJoinOperationPending = false; if (!IsActiveJoinAttempt(joinAttemptId, lobby)) { if ((int)val == 1) { try { ((Lobby)(ref lobby)).Leave(); } catch (Exception ex2) { Plugin.Log.LogError((object)("[CancelJoiningLobby] Failed to leave a Steam lobby that finished after cancellation: " + ex2)); } } } else if ((Object)(object)gameNetworkManager == (Object)null) { ClearJoinAttempt(); } else if ((int)val == 1) { gameNetworkManager.StartClient(((Lobby)(ref lobby)).Owner.Id); } else { gameNetworkManager.LeaveCurrentSteamLobby(); gameNetworkManager.steamLobbyName = SteamClient.Name; MenuManager val3 = Object.FindObjectOfType<MenuManager>(); if ((Object)(object)val3 != (Object)null) { val3.SetLoadingScreen(false, (RoomEnter)5, "The host has not loaded or has already landed their ship."); } else { ClearJoinAttempt(); } } } private static void CancelLobbyRefresh() { _cancellationInProgress = true; CancelButtonController.SetCancellationAvailable(available: false); GameNetworkManager instance = GameNetworkManager.Instance; if ((Object)(object)instance == (Object)null || !StopLobbyRefreshTimeout(instance)) { _cancellationInProgress = false; CancelButtonController.SetCancellationAvailable(available: true); Plugin.Log.LogError((object)"[CancelJoiningLobby] Could not cancel while getting server information because the lobby refresh timeout could not be stopped."); } else { instance.waitingForLobbyDataRefresh = false; SteamMatchmaking.OnLobbyDataChanged -= LobbySlot.OnLobbyDataRefresh; ReturnToCapturedMenu(); Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Cancelled while getting server information; restored the previous menu."); FinishCancellationToMenu(); } } private static void CancelSteamLobbyJoin() { _cancellationInProgress = true; CancelButtonController.SetCancellationAvailable(available: false); GameNetworkManager instance = GameNetworkManager.Instance; if ((Object)(object)instance != (Object)null) { ClearCurrentLobbyIfMatches(instance); instance.steamLobbyName = SteamClient.Name; } ReturnToCapturedMenu(); Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Cancelled while joining the Steam lobby. A late Steam success will be left without starting the game client."); FinishCancellationToMenu(); } private static void CancelNetworkConnectionBeforeApproval() { GameNetworkManager instance = GameNetworkManager.Instance; NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)instance == (Object)null) { ClearJoinAttempt(); return; } if (!ResetGameNetworkManagerState(instance)) { Plugin.Log.LogError((object)"[CancelJoiningLobby] Could not cancel because the pending connection state could not be reset."); return; } _cancellationInProgress = true; CancelButtonController.SetCancellationAvailable(available: false); instance.localClientWaitingForApproval = false; instance.LeaveCurrentSteamLobby(); instance.steamLobbyName = SteamClient.Name; StopTrackingJoinNetwork(); if ((Object)(object)singleton == (Object)null || !singleton.IsClient || !singleton.IsListening) { ReturnToCapturedMenu(); Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Cancelled before server approval; restored the previous menu."); FinishCancellationToMenu(); } else { SubscribeClientStopped(singleton); singleton.Shutdown(true); Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Cancelled before server approval; waiting for Netcode to shut down."); } } private static void CancelNetworkConnectionAfterApproval() { GameNetworkManager instance = GameNetworkManager.Instance; if ((Object)(object)instance == (Object)null) { ClearJoinAttempt(); return; } _cancellationInProgress = true; _postApprovalCancellation = true; CancelButtonController.SetCancellationAvailable(available: false); instance.isDisconnecting = true; if (!StartVanillaDisconnectCleanup(instance)) { instance.isDisconnecting = false; _cancellationInProgress = false; _postApprovalCancellation = false; CancelButtonController.SetCancellationAvailable(available: true); Plugin.Log.LogError((object)"[CancelJoiningLobby] Could not cancel during the final join step because disconnect cleanup failed."); return; } instance.localClientWaitingForApproval = false; StopTrackingJoinNetwork(); NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton != (Object)null && singleton.IsClient && singleton.IsListening) { UnsubscribeClientStopped(); _networkManagerAwaitingShutdown = singleton; singleton.Shutdown(true); Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Cancelled during the final join step; waiting for Netcode to shut down."); } else { Plugin.Log.LogInfo((object)"[CancelJoiningLobby] Cancelled during the final join step after Netcode had already stopped."); CompletePostApprovalCancellation(); } } private static void ConnectionApprovedHandlePrefix() { if (_joinAttemptTracked && !_cancellationInProgress && !((Object)(object)_joinNetworkManager == (Object)null) && !((Object)(object)NetworkManager.Singleton != (Object)(object)_joinNetworkManager) && _joinStage == JoinStage.NetworkConnecting) { _joinStage = JoinStage.JoiningServer; } } private static void NetworkManagerShutdownInternalPostfix(NetworkManager __instance) { if (_cancellationInProgress && _postApprovalCancellation && !((Object)(object)__instance == (Object)null) && !((Object)(object)_networkManagerAwaitingShutdown != (Object)(object)__instance)) { _networkManagerAwaitingShutdown = null; CompletePostApprovalCancellation(); } } } }