Decompiled source of CancelJoiningLobby v1.0.0

BepInEx/plugins/ZaboomafooW-CancelJoiningLobby-v1.0.0.dll

Decompiled 8 hours ago
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<RectTransform>();
			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<Image>();
			((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<TextMeshProUGUI>();
			((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<RectTransform>();
			component4.anchorMin = Vector2.zero;
			component4.anchorMax = Vector2.one;
			component4.offsetMin = Vector2.zero;
			component4.offsetMax = Vector2.zero;
			val.GetComponent<CancelButtonInputHandler>().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<Vector2>)(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<bool> <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<MenuManager>();
			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<byte> 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<SteamLobbyManager>();
			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;
			}
		}
	}
}