Decompiled source of pause v1.0.1

BepInEx/plugins/GTFOPause/GTFOPause.dll

Decompiled an hour ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using Agents;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppSystem.Collections.Generic;
using LevelGeneration;
using Microsoft.CodeAnalysis;
using Player;
using SNetwork;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
}
namespace HawChow.GTFOPause
{
	internal static class PauseCoordinator
	{
		private sealed class PlayerSnapshot
		{
			internal PlayerAgent Agent { get; }

			internal IntPtr AgentPointer { get; }

			internal IntPtr ControllerPointer { get; }

			internal bool ControlsWereEnabled { get; }

			internal eFocusState InputFilter { get; }

			internal InventorySlot WieldedSlot { get; }

			internal bool PauseApplied { get; set; }

			internal bool PauseApplyWarningLogged { get; set; }

			internal PlayerSnapshot(PlayerAgent agent, IntPtr agentPointer, IntPtr controllerPointer, bool controlsWereEnabled, eFocusState inputFilter, InventorySlot wieldedSlot)
			{
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: 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_002e: Unknown result type (might be due to invalid IL or missing references)
				Agent = agent;
				AgentPointer = agentPointer;
				ControllerPointer = controllerPointer;
				ControlsWereEnabled = controlsWereEnabled;
				InputFilter = inputFilter;
				WieldedSlot = wieldedSlot;
			}
		}

		private const float PlayerRefreshSeconds = 0.25f;

		private const float ReactorRefreshSeconds = 0.5f;

		private const float RemoteControlResendSeconds = 0.25f;

		private const float PendingPlayerWarningSeconds = 5f;

		private static readonly PauseSessionState Session = new PauseSessionState();

		private static readonly RosterSnapshot<ulong> PauseRoster = new RosterSnapshot<ulong>();

		private static readonly BooleanStateLedger<IntPtr> ReactorProgress = new BooleanStateLedger<IntPtr>();

		private static readonly Dictionary<IntPtr, LG_WardenObjective_Reactor> ReactorReferences = new Dictionary<IntPtr, LG_WardenObjective_Reactor>();

		private static readonly Dictionary<ulong, PlayerSnapshot> PlayerSnapshots = new Dictionary<ulong, PlayerSnapshot>();

		private static readonly Dictionary<ulong, float> NextRemoteControlEnforcementAt = new Dictionary<ulong, float>();

		private static bool _hasMigrationSnapshot;

		private static bool _migrationRefreshPending;

		private static string _migrationRefreshReason;

		private static int _rosterReadWarnings;

		private static float _nextPlayerRefreshAt;

		private static float _nextReactorRefreshAt;

		private static float _pendingPlayerSince = -1f;

		private static bool _pendingPlayerWarningLogged;

		internal static bool IsPaused => Session.IsPaused;

		internal static bool IsHostInLevel
		{
			get
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Invalid comparison between Unknown and I4
				try
				{
					return SNet.IsMaster && (int)GameStateManager.CurrentStateName == 10;
				}
				catch
				{
					return false;
				}
			}
		}

		internal static void Toggle()
		{
			if (IsHostInLevel)
			{
				if (Session.IsPaused)
				{
					ForceResume("host hotkey", requestNetworkRecall: true);
				}
				else
				{
					BeginPause();
				}
			}
		}

		internal static void EnforcePause(float unscaledNow)
		{
			if (!Session.TryGetSnapshot(out var snapshot))
			{
				return;
			}
			if (!IsHostInLevel)
			{
				ForceResume("host or in-level state changed", requestNetworkRecall: false);
				return;
			}
			try
			{
				if (!Clock.IsPaused)
				{
					Clock.SetPaused(true);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource pluginLog = Plugin.PluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogWarning((object)("Could not re-apply GTFO Clock pause: " + ex.Message));
				}
			}
			try
			{
				if (Clock.ExpeditionProgressionTime != snapshot.ExpeditionProgressionTime)
				{
					Clock.ExpeditionProgressionTime = snapshot.ExpeditionProgressionTime;
				}
			}
			catch (Exception ex2)
			{
				ManualLogSource pluginLog2 = Plugin.PluginLog;
				if (pluginLog2 != null)
				{
					pluginLog2.LogWarning((object)("Could not re-apply expedition progression snapshot: " + ex2.Message));
				}
			}
			if (float.IsNaN(unscaledNow) || float.IsInfinity(unscaledNow))
			{
				ApplyPauseTimeScale(snapshot);
				return;
			}
			if (unscaledNow >= _nextPlayerRefreshAt)
			{
				CaptureAndReconcilePlayers(pauseEdge: false);
				_nextPlayerRefreshAt = unscaledNow + 0.25f;
				ApplyPauseTimeScale(snapshot);
			}
			if (unscaledNow >= _nextReactorRefreshAt)
			{
				CaptureAndDisableReactors();
				_nextReactorRefreshAt = unscaledNow + 0.5f;
			}
		}

		private static void ApplyPauseTimeScale(PauseStateSnapshot snapshot)
		{
			bool flag = HasPendingDropInOrUncapturedPlayer();
			float num = PauseTimeScalePolicy.SelectTarget(snapshot.TimeScale, flag);
			if (!flag)
			{
				_pendingPlayerSince = -1f;
				_pendingPlayerWarningLogged = false;
			}
			else
			{
				float unscaledTime = Time.unscaledTime;
				if (!float.IsNaN(unscaledTime) && !float.IsInfinity(unscaledTime))
				{
					if (_pendingPlayerSince < 0f || unscaledTime < _pendingPlayerSince)
					{
						_pendingPlayerSince = unscaledTime;
						_pendingPlayerWarningLogged = false;
					}
					if (!_pendingPlayerWarningLogged && unscaledTime - _pendingPlayerSince >= 5f)
					{
						ManualLogSource pluginLog = Plugin.PluginLog;
						if (pluginLog != null)
						{
							pluginLog.LogWarning((object)"Unity time remains live because a slotted player is still in drop-in or lacks an applied pause snapshot.");
						}
						_pendingPlayerWarningLogged = true;
					}
				}
			}
			try
			{
				if (Time.timeScale != num)
				{
					Time.timeScale = num;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource pluginLog2 = Plugin.PluginLog;
				if (pluginLog2 != null)
				{
					pluginLog2.LogWarning((object)("Could not apply paused time-scale mode: " + ex.Message));
				}
			}
		}

		private static bool HasPendingDropInOrUncapturedPlayer()
		{
			try
			{
				SNet_PlayerSlotManager slots = SNet.Slots;
				if ((Object)(object)slots == (Object)null || slots.SlottedPlayers == null)
				{
					return PauseTimeScalePolicy.HasPendingPlayer(null, rosterReadable: false);
				}
				List<PauseRosterMemberState> list = new List<PauseRosterMemberState>();
				Enumerator<SNet_Player> enumerator = slots.SlottedPlayers.GetEnumerator();
				while (enumerator.MoveNext())
				{
					SNet_Player current = enumerator.Current;
					if ((Object)(object)current == (Object)null)
					{
						list.Add(new PauseRosterMemberState(isSlotted: true, hasLookup: false, isInDropIn: false, hasPauseSnapshot: false, pauseApplied: false));
						continue;
					}
					bool isInSlot = current.IsInSlot;
					if (isInSlot)
					{
						ulong lookup = current.Lookup;
						bool flag = lookup != 0;
						bool isInDropIn = current.IsInDropIn;
						bool flag2 = false;
						bool pauseApplied = false;
						if (flag && !isInDropIn && PlayerSnapshots.TryGetValue(lookup, out var value))
						{
							PlayerAgent agent = FindAgent(lookup);
							flag2 = IsSameAgent(value, agent);
							pauseApplied = flag2 && value.PauseApplied;
						}
						list.Add(new PauseRosterMemberState(isInSlot, flag, isInDropIn, flag2, pauseApplied));
					}
				}
				return PauseTimeScalePolicy.HasPendingPlayer(list, rosterReadable: true);
			}
			catch (Exception ex)
			{
				if (_rosterReadWarnings++ < 3)
				{
					ManualLogSource pluginLog = Plugin.PluginLog;
					if (pluginLog != null)
					{
						pluginLog.LogWarning((object)("Could not resolve pending drop-in state; keeping Unity time live: " + ex.Message));
					}
				}
				return true;
			}
		}

		private static PlayerAgent FindAgent(ulong lookup)
		{
			List<PlayerAgent> playerAgentsInLevel = PlayerManager.PlayerAgentsInLevel;
			if (playerAgentsInLevel == null)
			{
				return null;
			}
			Enumerator<PlayerAgent> enumerator = playerAgentsInLevel.GetEnumerator();
			while (enumerator.MoveNext())
			{
				PlayerAgent current = enumerator.Current;
				if ((Object)(object)current != (Object)null && (Object)(object)current.Owner != (Object)null && current.Owner.Lookup == lookup)
				{
					return current;
				}
			}
			return null;
		}

		internal static void FlushPendingMigrationSnapshot()
		{
			if (_migrationRefreshPending)
			{
				if (!Session.IsPaused || !IsHostInLevel)
				{
					_migrationRefreshPending = false;
					_migrationRefreshReason = null;
					return;
				}
				string reason = _migrationRefreshReason ?? "roster changed during pause";
				_migrationRefreshPending = false;
				_migrationRefreshReason = null;
				CaptureMigrationSnapshotNow(reason);
			}
		}

		internal static void ForceResume(string reason, bool requestNetworkRecall)
		{
			if (requestNetworkRecall && _migrationRefreshPending)
			{
				FlushPendingMigrationSnapshot();
			}
			bool hasMigrationSnapshot = _hasMigrationSnapshot;
			if (!Session.TryEnd(out var snapshot))
			{
				if (PlayerSnapshots.Count != 0 || ReactorReferences.Count != 0)
				{
					RestoreReactors();
					RestorePlayers();
					ClearSessionData();
				}
				return;
			}
			bool flag = PauseResumeMigrationPolicy.ShouldCaptureFreshSnapshot(requestNetworkRecall, hasMigrationSnapshot, IsHostInLevel);
			try
			{
				Clock.SetPaused(snapshot.ClockWasPaused);
			}
			catch (Exception ex)
			{
				ManualLogSource pluginLog = Plugin.PluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogError((object)("Could not restore GTFO Clock pause flag: " + ex));
				}
			}
			try
			{
				Clock.ExpeditionProgressionTime = snapshot.ExpeditionProgressionTime;
			}
			catch (Exception ex2)
			{
				ManualLogSource pluginLog2 = Plugin.PluginLog;
				if (pluginLog2 != null)
				{
					pluginLog2.LogError((object)("Could not restore expedition progression time: " + ex2));
				}
			}
			try
			{
				if (Time.timeScale != snapshot.TimeScale)
				{
					Time.timeScale = snapshot.TimeScale;
				}
			}
			catch (Exception ex3)
			{
				ManualLogSource pluginLog3 = Plugin.PluginLog;
				if (pluginLog3 != null)
				{
					pluginLog3.LogError((object)("Could not restore Unity timeScale: " + ex3));
				}
			}
			RestoreReactors();
			RestorePlayers();
			bool flag2 = false;
			if (flag)
			{
				CaptureMigrationSnapshotNow("resume after local state restoration");
				flag2 = _hasMigrationSnapshot;
				if (!flag2)
				{
					ManualLogSource pluginLog4 = Plugin.PluginLog;
					if (pluginLog4 != null)
					{
						pluginLog4.LogWarning((object)"Migration_A resume recall skipped because the fresh post-resume snapshot failed; the stale pause-edge buffer was not replayed.");
					}
				}
			}
			if (PauseResumeMigrationPolicy.ShouldRecall(requestNetworkRecall, flag2, IsHostInLevel))
			{
				try
				{
					SNet.Sync.StartRecallWithAllSyncedPlayers((eBufferType)4, false);
				}
				catch (Exception ex4)
				{
					ManualLogSource pluginLog5 = Plugin.PluginLog;
					if (pluginLog5 != null)
					{
						pluginLog5.LogError((object)("Migration_A resume recall failed: " + ex4));
					}
				}
			}
			ClearSessionData();
			ManualLogSource pluginLog6 = Plugin.PluginLog;
			if (pluginLog6 != null)
			{
				pluginLog6.LogInfo((object)("Game resumed; prior GTFO Clock, reactor progress, player control states and wielded slots restored (" + reason + ")."));
			}
		}

		internal static void HandleRemoteLocomotion(PlayerAgent agent, Vector3 incomingPosition)
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Invalid comparison between Unknown and I4
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			if (!Session.IsPaused || !SNet.IsMaster || (Object)(object)agent == (Object)null || ((Agent)agent).IsLocallyOwned)
			{
				return;
			}
			try
			{
				SNet_Player owner = agent.Owner;
				if ((Object)(object)owner == (Object)null || !owner.IsInSlot || owner.IsOutOfSync || !PlayerSnapshots.ContainsKey(owner.Lookup))
				{
					return;
				}
				float unscaledTime = Time.unscaledTime;
				if (NextRemoteControlEnforcementAt.TryGetValue(owner.Lookup, out var value) && unscaledTime < value)
				{
					return;
				}
				NextRemoteControlEnforcementAt[owner.Lookup] = unscaledTime + 0.25f;
				bool flag = (Object)(object)agent.Inventory != (Object)null && (int)agent.Inventory.WieldedSlot > 0;
				bool flag2 = Vector3.Distance(incomingPosition, ((Agent)agent).Position) >= 2f;
				if (flag || flag2)
				{
					agent.RequestToggleControlsEnabled(false);
					if (flag && (Object)(object)agent.Sync != (Object)null)
					{
						agent.Sync.WantsToWieldSlot((InventorySlot)0, false);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource pluginLog = Plugin.PluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogWarning((object)("Could not hold a remote player at the paused snapshot: " + ex.Message));
				}
			}
		}

		private static void BeginPause()
		{
			PauseStateSnapshot snapshot;
			try
			{
				snapshot = new PauseStateSnapshot(Time.timeScale, Clock.IsPaused, Clock.ExpeditionProgressionTime);
			}
			catch (Exception ex)
			{
				ManualLogSource pluginLog = Plugin.PluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogError((object)("Pause could not snapshot the game clocks: " + ex));
				}
				return;
			}
			if (!Session.TryBegin(snapshot))
			{
				ManualLogSource pluginLog2 = Plugin.PluginLog;
				if (pluginLog2 != null)
				{
					pluginLog2.LogWarning((object)"Pause request ignored: a pause session is already active or clock values are invalid.");
				}
				return;
			}
			_hasMigrationSnapshot = false;
			_migrationRefreshPending = false;
			_migrationRefreshReason = null;
			_nextPlayerRefreshAt = 0f;
			_nextReactorRefreshAt = 0f;
			_pendingPlayerSince = -1f;
			_pendingPlayerWarningLogged = false;
			try
			{
				CaptureMigrationSnapshotNow("pause edge");
				CaptureAndReconcilePlayers(pauseEdge: true);
				PauseRoster.Reset(PlayerSnapshots.Keys);
				CaptureAndDisableReactors();
				Clock.SetPaused(true);
				Clock.ExpeditionProgressionTime = snapshot.ExpeditionProgressionTime;
				ApplyPauseTimeScale(snapshot);
				ManualLogSource pluginLog3 = Plugin.PluginLog;
				if (pluginLog3 != null)
				{
					pluginLog3.LogInfo((object)"Game paused; GTFO Clock and reactor progress are frozen. Unity timeScale is zero when all slotted players are captured; it returns to the pause-edge value while a drop-in or uncaptured player needs to finish joining.");
				}
			}
			catch (Exception ex2)
			{
				ManualLogSource pluginLog4 = Plugin.PluginLog;
				if (pluginLog4 != null)
				{
					pluginLog4.LogError((object)("Pause setup failed; restoring captured state: " + ex2));
				}
				ForceResume("pause setup rollback", requestNetworkRecall: true);
			}
		}

		private static void CaptureAndReconcilePlayers(bool pauseEdge)
		{
			Dictionary<ulong, PlayerAgent> dictionary = new Dictionary<ulong, PlayerAgent>();
			HashSet<ulong> hashSet = new HashSet<ulong>();
			bool flag = TryReadSlottedLookups(hashSet);
			try
			{
				List<PlayerAgent> playerAgentsInLevel = PlayerManager.PlayerAgentsInLevel;
				if (playerAgentsInLevel != null)
				{
					Enumerator<PlayerAgent> enumerator = playerAgentsInLevel.GetEnumerator();
					while (enumerator.MoveNext())
					{
						PlayerAgent current = enumerator.Current;
						if (!((Object)(object)current == (Object)null))
						{
							SNet_Player owner = current.Owner;
							if ((Object)(object)owner != (Object)null && owner.IsInSlot && owner.Lookup != 0L)
							{
								dictionary[owner.Lookup] = current;
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource pluginLog = Plugin.PluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogWarning((object)("Could not enumerate in-level player agents: " + ex.Message));
				}
			}
			bool flag2 = false;
			if (flag && hashSet.Count > 0)
			{
				List<ulong> list = new List<ulong>();
				foreach (ulong key in PlayerSnapshots.Keys)
				{
					if (!hashSet.Contains(key))
					{
						list.Add(key);
					}
				}
				foreach (ulong item in list)
				{
					PlayerSnapshots.Remove(item);
					NextRemoteControlEnforcementAt.Remove(item);
					flag2 = true;
				}
			}
			foreach (KeyValuePair<ulong, PlayerAgent> item2 in dictionary)
			{
				SNet_Player owner2;
				if (PlayerSnapshots.TryGetValue(item2.Key, out var value) && IsSameAgent(value, item2.Value))
				{
					TryApplyPlayerPause(value);
				}
				else if (TryGetReadySlottedOwner(item2.Value, out owner2) && CanCapturePlayer(item2.Value, owner2, pauseEdge))
				{
					if (PlayerSnapshots.TryGetValue(item2.Key, out value))
					{
						PlayerSnapshots.Remove(item2.Key);
						NextRemoteControlEnforcementAt.Remove(item2.Key);
						flag2 = true;
					}
					if (CaptureAndDisableAgent(item2.Value, owner2))
					{
						flag2 = true;
					}
				}
			}
			if (!pauseEdge)
			{
				HashSet<ulong> members = new HashSet<ulong>(PlayerSnapshots.Keys);
				HashSet<ulong> added;
				HashSet<ulong> removed;
				bool flag3 = PauseRoster.Reconcile(members, out added, out removed);
				if (flag2 || flag3)
				{
					QueueMigrationSnapshotRefresh("player roster changed during pause");
				}
			}
		}

		private static bool CanCapturePlayer(PlayerAgent agent, SNet_Player owner, bool pauseEdge)
		{
			try
			{
				PlayerCharacterController val = ((agent != null) ? agent.PlayerCharacterController : null);
				return (Object)(object)val != (Object)null && PausePlayerGate.ShouldCapture(pauseEdge, (Object)(object)owner != (Object)null && owner.IsInDropIn, val.m_isSetup, val.IsGrounded);
			}
			catch
			{
				return false;
			}
		}

		private static bool TryReadSlottedLookups(HashSet<ulong> lookups)
		{
			try
			{
				SNet_PlayerSlotManager slots = SNet.Slots;
				if ((Object)(object)slots == (Object)null || slots.SlottedPlayers == null)
				{
					return false;
				}
				Enumerator<SNet_Player> enumerator = slots.SlottedPlayers.GetEnumerator();
				while (enumerator.MoveNext())
				{
					SNet_Player current = enumerator.Current;
					if ((Object)(object)current != (Object)null && current.IsInSlot && current.Lookup != 0L)
					{
						lookups.Add(current.Lookup);
					}
				}
				return true;
			}
			catch (Exception ex)
			{
				if (_rosterReadWarnings++ < 3)
				{
					ManualLogSource pluginLog = Plugin.PluginLog;
					if (pluginLog != null)
					{
						pluginLog.LogWarning((object)("Could not read SNet.Slots.SlottedPlayers; retaining existing player snapshots: " + ex.Message));
					}
				}
				return false;
			}
		}

		private static bool TryGetReadySlottedOwner(PlayerAgent agent, out SNet_Player owner)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			owner = null;
			if ((Object)(object)agent == (Object)null)
			{
				return false;
			}
			try
			{
				owner = agent.Owner;
				if ((Object)(object)owner == (Object)null || !owner.IsInSlot || owner.IsOutOfSync || owner.Lookup == 0L)
				{
					return false;
				}
				return owner.Load<pReady>().isReady;
			}
			catch
			{
				owner = null;
				return false;
			}
		}

		private static bool HasLanded(PlayerAgent agent)
		{
			try
			{
				PlayerCharacterController val = ((agent != null) ? agent.PlayerCharacterController : null);
				return (Object)(object)val != (Object)null && val.m_isSetup && val.IsGrounded;
			}
			catch
			{
				return false;
			}
		}

		private static bool IsSameAgent(PlayerSnapshot snapshot, PlayerAgent agent)
		{
			try
			{
				PlayerCharacterController val = ((agent != null) ? agent.PlayerCharacterController : null);
				return (Object)(object)snapshot.Agent != (Object)null && (Object)(object)agent != (Object)null && snapshot.AgentPointer == ((Il2CppObjectBase)agent).Pointer && (Object)(object)val != (Object)null && ((Il2CppObjectBase)val).Pointer == snapshot.ControllerPointer;
			}
			catch
			{
				return false;
			}
		}

		private static bool CaptureAndDisableAgent(PlayerAgent agent, SNet_Player owner)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)agent == (Object)null || (Object)(object)owner == (Object)null || owner.Lookup == 0L || PlayerSnapshots.ContainsKey(owner.Lookup))
			{
				return false;
			}
			IntPtr pointer;
			IntPtr pointer2;
			bool updateEnabled;
			eFocusState inputFilter;
			InventorySlot wieldedSlot;
			try
			{
				PlayerCharacterController playerCharacterController = agent.PlayerCharacterController;
				if ((Object)(object)playerCharacterController == (Object)null || !playerCharacterController.m_isSetup)
				{
					return false;
				}
				pointer = ((Il2CppObjectBase)agent).Pointer;
				pointer2 = ((Il2CppObjectBase)playerCharacterController).Pointer;
				if (pointer == IntPtr.Zero || pointer2 == IntPtr.Zero)
				{
					return false;
				}
				updateEnabled = playerCharacterController.m_updateEnabled;
				inputFilter = agent.InputFilter;
				wieldedSlot = (InventorySlot)(((Object)(object)agent.Inventory != (Object)null) ? ((int)agent.Inventory.WieldedSlot) : 0);
			}
			catch (Exception ex)
			{
				ManualLogSource pluginLog = Plugin.PluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogWarning((object)("Could not snapshot a player's initialized control state: " + ex.Message));
				}
				return false;
			}
			PlayerSnapshot playerSnapshot = new PlayerSnapshot(agent, pointer, pointer2, updateEnabled, inputFilter, wieldedSlot);
			PlayerSnapshots.Add(owner.Lookup, playerSnapshot);
			TryApplyPlayerPause(playerSnapshot);
			return true;
		}

		private static bool TryApplyPlayerPause(PlayerSnapshot snapshot)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			if (snapshot == null)
			{
				return false;
			}
			try
			{
				PlayerAgent agent = snapshot.Agent;
				if ((Object)(object)agent == (Object)null || ((Il2CppObjectBase)agent).Pointer != snapshot.AgentPointer)
				{
					snapshot.PauseApplied = false;
					return false;
				}
				PlayerCharacterController playerCharacterController = agent.PlayerCharacterController;
				if ((Object)(object)playerCharacterController == (Object)null || ((Il2CppObjectBase)playerCharacterController).Pointer != snapshot.ControllerPointer || !playerCharacterController.m_isSetup)
				{
					snapshot.PauseApplied = false;
					return false;
				}
				agent.RequestToggleControlsEnabled(false);
				playerCharacterController.SetEnabled(false);
				if ((Object)(object)agent.Inventory != (Object)null && (int)agent.Inventory.WieldedSlot != 0 && (Object)(object)agent.Sync != (Object)null)
				{
					agent.Sync.WantsToWieldSlot((InventorySlot)0, false);
				}
				snapshot.PauseApplied = true;
				snapshot.PauseApplyWarningLogged = false;
				return true;
			}
			catch (Exception ex)
			{
				snapshot.PauseApplied = false;
				if (!snapshot.PauseApplyWarningLogged)
				{
					ManualLogSource pluginLog = Plugin.PluginLog;
					if (pluginLog != null)
					{
						pluginLog.LogWarning((object)("Could not apply a player's pause control lock; retrying on roster refresh: " + ex.Message));
					}
					snapshot.PauseApplyWarningLogged = true;
				}
				return false;
			}
		}

		private static void CaptureAndDisableReactors()
		{
			try
			{
				foreach (LG_WardenObjective_Reactor item in Object.FindObjectsOfType<LG_WardenObjective_Reactor>())
				{
					if ((Object)(object)item == (Object)null)
					{
						continue;
					}
					IntPtr pointer = ((Il2CppObjectBase)item).Pointer;
					if (pointer == IntPtr.Zero)
					{
						continue;
					}
					if (ReactorReferences.TryGetValue(pointer, out var value))
					{
						if ((Object)(object)value != (Object)null)
						{
							ReactorReferences[pointer] = item;
							item.m_progressUpdateEnabled = false;
							continue;
						}
						ReactorReferences.Remove(pointer);
						ReactorProgress.Remove(pointer);
					}
					if (IsActiveProgressReactor(item) && ReactorProgress.TryCapture(pointer, item.m_progressUpdateEnabled))
					{
						ReactorReferences[pointer] = item;
						item.m_progressUpdateEnabled = false;
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource pluginLog = Plugin.PluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogWarning((object)("Could not enumerate Warden objective reactors for pause: " + ex.Message));
				}
			}
		}

		private static bool IsActiveProgressReactor(LG_WardenObjective_Reactor reactor)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//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_0022: Invalid comparison between Unknown and I4
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			if (!reactor.m_isWardenObjective || reactor.ObjectiveItemSolved)
			{
				return false;
			}
			eReactorStatus status = reactor.m_currentState.status;
			if (status - 2 <= 2 || status - 6 <= 2)
			{
				return true;
			}
			return false;
		}

		private static void QueueMigrationSnapshotRefresh(string reason)
		{
			_migrationRefreshPending = true;
			_migrationRefreshReason = reason;
		}

		private static void CaptureMigrationSnapshotNow(string reason)
		{
			_hasMigrationSnapshot = false;
			PauseStateSnapshot snapshot;
			bool flag = Session.TryGetSnapshot(out snapshot);
			try
			{
				if (flag)
				{
					TemporarilyRestorePauseEdgeStateForCapture(snapshot);
				}
				if (SNet.IsMaster && !((Object)(object)SNet.Capture == (Object)null))
				{
					SNet.Capture.CaptureGameState((eBufferType)4);
					_hasMigrationSnapshot = true;
					ManualLogSource pluginLog = Plugin.PluginLog;
					if (pluginLog != null)
					{
						pluginLog.LogInfo((object)("Migration_A pause-edge snapshot captured (" + reason + ")."));
					}
				}
			}
			catch (Exception ex)
			{
				_hasMigrationSnapshot = false;
				ManualLogSource pluginLog2 = Plugin.PluginLog;
				if (pluginLog2 != null)
				{
					pluginLog2.LogWarning((object)("Migration_A pause snapshot failed (" + reason + "): " + ex.Message));
				}
			}
			finally
			{
				if (flag)
				{
					ReapplyPausedState(snapshot);
				}
			}
		}

		private static void TemporarilyRestorePauseEdgeStateForCapture(PauseStateSnapshot snapshot)
		{
			foreach (KeyValuePair<IntPtr, LG_WardenObjective_Reactor> reactorReference in ReactorReferences)
			{
				if ((Object)(object)reactorReference.Value != (Object)null && ReactorProgress.TryGet(reactorReference.Key, out var wasRunning))
				{
					reactorReference.Value.m_progressUpdateEnabled = wasRunning;
				}
			}
			TemporarilyRestorePlayersForCapture();
			Clock.SetPaused(snapshot.ClockWasPaused);
			Clock.ExpeditionProgressionTime = snapshot.ExpeditionProgressionTime;
		}

		private static void TemporarilyRestorePlayersForCapture()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			foreach (PlayerSnapshot value in PlayerSnapshots.Values)
			{
				PlayerAgent agent = value.Agent;
				if (!((Object)(object)agent == (Object)null) && !(((Il2CppObjectBase)agent).Pointer != value.AgentPointer))
				{
					agent.RequestToggleControlsEnabled(value.ControlsWereEnabled);
					PlayerCharacterController playerCharacterController = agent.PlayerCharacterController;
					if ((Object)(object)playerCharacterController != (Object)null && ((Il2CppObjectBase)playerCharacterController).Pointer == value.ControllerPointer)
					{
						playerCharacterController.SetEnabled(value.ControlsWereEnabled);
					}
					agent.InputFilter = value.InputFilter;
					if ((Object)(object)agent.Sync != (Object)null)
					{
						agent.Sync.WantsToWieldSlot(value.WieldedSlot, false);
					}
				}
			}
		}

		private static void ReapplyPausedState(PauseStateSnapshot snapshot)
		{
			try
			{
				Clock.SetPaused(true);
			}
			catch (Exception ex)
			{
				ManualLogSource pluginLog = Plugin.PluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogWarning((object)("Could not reapply Clock pause after snapshot capture: " + ex.Message));
				}
			}
			try
			{
				Clock.ExpeditionProgressionTime = snapshot.ExpeditionProgressionTime;
			}
			catch (Exception ex2)
			{
				ManualLogSource pluginLog2 = Plugin.PluginLog;
				if (pluginLog2 != null)
				{
					pluginLog2.LogWarning((object)("Could not reapply expedition time after snapshot capture: " + ex2.Message));
				}
			}
			foreach (KeyValuePair<IntPtr, LG_WardenObjective_Reactor> reactorReference in ReactorReferences)
			{
				if (!((Object)(object)reactorReference.Value != (Object)null))
				{
					continue;
				}
				try
				{
					reactorReference.Value.m_progressUpdateEnabled = false;
				}
				catch (Exception ex3)
				{
					ManualLogSource pluginLog3 = Plugin.PluginLog;
					if (pluginLog3 != null)
					{
						pluginLog3.LogWarning((object)("Could not re-freeze reactor progress: " + ex3.Message));
					}
				}
			}
			foreach (PlayerSnapshot value in PlayerSnapshots.Values)
			{
				TryApplyPlayerPause(value);
			}
			ApplyPauseTimeScale(snapshot);
		}

		private static void RestorePlayers()
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			foreach (PlayerSnapshot value in PlayerSnapshots.Values)
			{
				PlayerAgent agent = value.Agent;
				if ((Object)(object)agent == (Object)null || ((Il2CppObjectBase)agent).Pointer != value.AgentPointer)
				{
					continue;
				}
				try
				{
					agent.RequestToggleControlsEnabled(value.ControlsWereEnabled);
					PlayerCharacterController playerCharacterController = agent.PlayerCharacterController;
					if ((Object)(object)playerCharacterController != (Object)null && ((Il2CppObjectBase)playerCharacterController).Pointer == value.ControllerPointer)
					{
						playerCharacterController.SetEnabled(value.ControlsWereEnabled);
					}
					agent.InputFilter = value.InputFilter;
					if ((Object)(object)agent.Sync != (Object)null)
					{
						agent.Sync.WantsToWieldSlot(value.WieldedSlot, false);
					}
				}
				catch (Exception ex)
				{
					ManualLogSource pluginLog = Plugin.PluginLog;
					if (pluginLog != null)
					{
						pluginLog.LogWarning((object)("Could not restore a player's pre-pause control/equipment state: " + ex.Message));
					}
				}
			}
			PlayerSnapshots.Clear();
		}

		private static void RestoreReactors()
		{
			foreach (KeyValuePair<IntPtr, LG_WardenObjective_Reactor> reactorReference in ReactorReferences)
			{
				if (!ReactorProgress.TryTake(reactorReference.Key, out var wasRunning) || !((Object)(object)reactorReference.Value != (Object)null))
				{
					continue;
				}
				try
				{
					reactorReference.Value.m_progressUpdateEnabled = wasRunning;
				}
				catch (Exception ex)
				{
					ManualLogSource pluginLog = Plugin.PluginLog;
					if (pluginLog != null)
					{
						pluginLog.LogWarning((object)("Could not restore reactor progress state: " + ex.Message));
					}
				}
			}
			ReactorReferences.Clear();
			ReactorProgress.Clear();
		}

		private static void ClearSessionData()
		{
			Session.Reset();
			_hasMigrationSnapshot = false;
			_migrationRefreshPending = false;
			_migrationRefreshReason = null;
			_nextPlayerRefreshAt = 0f;
			_pendingPlayerSince = -1f;
			_pendingPlayerWarningLogged = false;
			_nextReactorRefreshAt = 0f;
			PlayerSnapshots.Clear();
			PauseRoster.Clear();
			ReactorReferences.Clear();
			ReactorProgress.Clear();
			NextRemoteControlEnforcementAt.Clear();
		}
	}
	[HarmonyPatch(typeof(GameStateManager), "DoChangeState", new Type[] { typeof(eGameStateName) })]
	internal static class PauseGameStatePatch
	{
		[HarmonyPrefix]
		private static void Prefix(eGameStateName __0)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			if ((int)__0 != 10)
			{
				PauseCoordinator.ForceResume("leaving InLevel", requestNetworkRecall: false);
			}
		}
	}
	[HarmonyPatch(typeof(GS_AfterLevel), "CleanupAfterExpedition", new Type[] { })]
	internal static class PauseLevelCleanupPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			PauseCoordinator.ForceResume("expedition cleanup", requestNetworkRecall: false);
		}
	}
	[HarmonyPatch(typeof(PlayerSync), "IncomingLocomotion", new Type[] { typeof(pPlayerLocomotion) })]
	internal static class PauseRemoteLocomotionPatch
	{
		[HarmonyPostfix]
		private static void Postfix(PlayerSync __instance, pPlayerLocomotion __0)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.m_agent == (Object)null))
			{
				PauseCoordinator.HandleRemoteLocomotion(__instance.m_agent, __0.Pos);
			}
		}
	}
	internal sealed class PauseInputGate
	{
		internal const float DefaultHoldSeconds = 0.5f;

		internal const float MinimumHoldSeconds = 0.1f;

		internal const float MaximumHoldSeconds = 3f;

		private bool _holding;

		private bool _triggered;

		private double _holdStartedAt;

		internal bool Tick(bool eligible, bool keyHeld, double unscaledNow, float holdSeconds)
		{
			if (!eligible || !keyHeld || double.IsNaN(unscaledNow) || double.IsInfinity(unscaledNow))
			{
				Reset();
				return false;
			}
			if (!_holding)
			{
				_holding = true;
				_triggered = false;
				_holdStartedAt = unscaledNow;
				return false;
			}
			if (_triggered)
			{
				return false;
			}
			if (unscaledNow < _holdStartedAt)
			{
				_holdStartedAt = unscaledNow;
				return false;
			}
			double num = NormalizeHoldSeconds(holdSeconds);
			if (unscaledNow - _holdStartedAt < num)
			{
				return false;
			}
			_triggered = true;
			return true;
		}

		internal void Reset()
		{
			_holding = false;
			_triggered = false;
			_holdStartedAt = 0.0;
		}

		internal static float NormalizeHoldSeconds(float value)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				return 0.5f;
			}
			return Math.Clamp(value, 0.1f, 3f);
		}
	}
	internal static class PausePlayerGate
	{
		internal static bool ShouldCapture(bool pauseEdge, bool isDropIn, bool controllerIsSetup, bool isGrounded)
		{
			if (controllerIsSetup && !isDropIn)
			{
				return pauseEdge || isGrounded;
			}
			return false;
		}
	}
	internal readonly struct PauseRosterMemberState
	{
		internal bool IsSlotted { get; }

		internal bool HasLookup { get; }

		internal bool IsInDropIn { get; }

		internal bool HasPauseSnapshot { get; }

		internal bool PauseApplied { get; }

		internal PauseRosterMemberState(bool isSlotted, bool hasLookup, bool isInDropIn, bool hasPauseSnapshot, bool pauseApplied)
		{
			IsSlotted = isSlotted;
			HasLookup = hasLookup;
			IsInDropIn = isInDropIn;
			HasPauseSnapshot = hasPauseSnapshot;
			PauseApplied = pauseApplied;
		}
	}
	internal static class PauseTimeScalePolicy
	{
		internal static bool HasPendingPlayer(IEnumerable<PauseRosterMemberState> roster, bool rosterReadable)
		{
			if (!rosterReadable || roster == null)
			{
				return true;
			}
			foreach (PauseRosterMemberState item in roster)
			{
				if (item.IsSlotted && (!item.HasLookup || item.IsInDropIn || !item.HasPauseSnapshot || !item.PauseApplied))
				{
					return true;
				}
			}
			return false;
		}

		internal static float SelectTarget(float pauseEdgeTimeScale, bool hasPendingPlayer)
		{
			if (!hasPendingPlayer)
			{
				return 0f;
			}
			if (!float.IsNaN(pauseEdgeTimeScale) && !float.IsInfinity(pauseEdgeTimeScale) && !(pauseEdgeTimeScale <= 0f))
			{
				return pauseEdgeTimeScale;
			}
			return 1f;
		}
	}
	internal static class PauseResumeMigrationPolicy
	{
		internal static bool ShouldCaptureFreshSnapshot(bool recallRequested, bool pauseEdgeSnapshotAvailable, bool isHostInLevel)
		{
			return recallRequested && pauseEdgeSnapshotAvailable && isHostInLevel;
		}

		internal static bool ShouldRecall(bool recallRequested, bool freshSnapshotCaptured, bool isHostInLevel)
		{
			return recallRequested && freshSnapshotCaptured && isHostInLevel;
		}
	}
	internal readonly struct PauseStateSnapshot
	{
		internal float TimeScale { get; }

		internal bool ClockWasPaused { get; }

		internal float ExpeditionProgressionTime { get; }

		internal bool IsValid
		{
			get
			{
				if (!float.IsNaN(TimeScale) && !float.IsInfinity(TimeScale) && !float.IsNaN(ExpeditionProgressionTime))
				{
					return !float.IsInfinity(ExpeditionProgressionTime);
				}
				return false;
			}
		}

		internal PauseStateSnapshot(float timeScale, bool clockWasPaused, float expeditionProgressionTime)
		{
			TimeScale = timeScale;
			ClockWasPaused = clockWasPaused;
			ExpeditionProgressionTime = expeditionProgressionTime;
		}
	}
	internal sealed class PauseSessionState
	{
		private PauseStateSnapshot _snapshot;

		internal bool IsPaused { get; private set; }

		internal bool TryBegin(PauseStateSnapshot snapshot)
		{
			if (IsPaused || !snapshot.IsValid)
			{
				return false;
			}
			_snapshot = snapshot;
			IsPaused = true;
			return true;
		}

		internal bool TryGetSnapshot(out PauseStateSnapshot snapshot)
		{
			snapshot = _snapshot;
			return IsPaused;
		}

		internal bool TryEnd(out PauseStateSnapshot snapshot)
		{
			if (!IsPaused)
			{
				snapshot = default(PauseStateSnapshot);
				return false;
			}
			snapshot = _snapshot;
			_snapshot = default(PauseStateSnapshot);
			IsPaused = false;
			return true;
		}

		internal void Reset()
		{
			_snapshot = default(PauseStateSnapshot);
			IsPaused = false;
		}
	}
	internal sealed class BooleanStateLedger<TKey> where TKey : notnull
	{
		private readonly Dictionary<TKey, bool> _states = new Dictionary<TKey, bool>();

		internal int Count => _states.Count;

		internal bool TryCapture(TKey key, bool wasRunning)
		{
			if (_states.ContainsKey(key))
			{
				return false;
			}
			_states.Add(key, wasRunning);
			return true;
		}

		internal bool TryGet(TKey key, out bool wasRunning)
		{
			return _states.TryGetValue(key, out wasRunning);
		}

		internal bool TryTake(TKey key, out bool wasRunning)
		{
			if (!_states.TryGetValue(key, out wasRunning))
			{
				return false;
			}
			_states.Remove(key);
			return true;
		}

		internal bool Remove(TKey key)
		{
			return _states.Remove(key);
		}

		internal void Clear()
		{
			_states.Clear();
		}
	}
	internal sealed class RosterSnapshot<TKey> where TKey : notnull
	{
		private readonly HashSet<TKey> _members = new HashSet<TKey>();

		internal int Count => _members.Count;

		internal void Reset(IEnumerable<TKey> members)
		{
			_members.Clear();
			foreach (TKey member in members)
			{
				_members.Add(member);
			}
		}

		internal bool Reconcile(IEnumerable<TKey> members, out HashSet<TKey> added, out HashSet<TKey> removed)
		{
			HashSet<TKey> hashSet = new HashSet<TKey>(members);
			added = new HashSet<TKey>();
			removed = new HashSet<TKey>();
			if (hashSet.Count == 0 && _members.Count != 0)
			{
				return false;
			}
			added.UnionWith(hashSet);
			added.ExceptWith(_members);
			removed.UnionWith(_members);
			removed.ExceptWith(hashSet);
			if (added.Count == 0 && removed.Count == 0)
			{
				return false;
			}
			_members.Clear();
			_members.UnionWith(hashSet);
			return true;
		}

		internal void Clear()
		{
			_members.Clear();
		}
	}
	internal sealed class PauseRuntime : MonoBehaviour
	{
		private readonly PauseInputGate _inputGate = new PauseInputGate();

		public PauseRuntime(IntPtr pointer)
			: base(pointer)
		{
		}

		public void Update()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Plugin.Enabled == null || !Plugin.Enabled.Value)
				{
					_inputGate.Reset();
					if (PauseCoordinator.IsPaused)
					{
						PauseCoordinator.ForceResume("plugin disabled", requestNetworkRecall: true);
					}
					return;
				}
				bool isHostInLevel = PauseCoordinator.IsHostInLevel;
				KeyCode value = Plugin.PauseKey.Value;
				bool keyHeld = (int)value != 0 && Input.GetKey(value);
				if (_inputGate.Tick(isHostInLevel, keyHeld, Time.unscaledTime, Plugin.HoldSeconds.Value))
				{
					PauseCoordinator.Toggle();
				}
				if (PauseCoordinator.IsPaused)
				{
					PauseCoordinator.EnforcePause(Time.unscaledTime);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource pluginLog = Plugin.PluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogError((object)("Pause runtime update failed: " + ex));
				}
				_inputGate.Reset();
				if (PauseCoordinator.IsPaused)
				{
					PauseCoordinator.ForceResume("runtime update exception", requestNetworkRecall: true);
				}
			}
		}

		public void LateUpdate()
		{
			try
			{
				if (PauseCoordinator.IsPaused)
				{
					PauseCoordinator.FlushPendingMigrationSnapshot();
				}
			}
			catch (Exception ex)
			{
				ManualLogSource pluginLog = Plugin.PluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogWarning((object)("Deferred pause snapshot refresh failed: " + ex.Message));
				}
			}
		}

		public void OnDestroy()
		{
			_inputGate.Reset();
			PauseCoordinator.ForceResume("runtime component destroyed", requestNetworkRecall: true);
		}
	}
	[BepInPlugin("HawChow.GTFOPause", "GTFOPause", "1.0.1")]
	public sealed class Plugin : BasePlugin
	{
		internal static ManualLogSource PluginLog { get; private set; }

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<KeyCode> PauseKey { get; private set; }

		internal static ConfigEntry<float> HoldSeconds { get; private set; }

		public override void Load()
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Expected O, but got Unknown
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Expected O, but got Unknown
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			PluginLog = ((BasePlugin)this).Log;
			Enabled = ((BasePlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable host pause and resume hotkey handling.");
			PauseKey = ((BasePlugin)this).Config.Bind<KeyCode>("Input", "PauseKey", (KeyCode)112, "Host-only pause key. None disables the hotkey.");
			HoldSeconds = ((BasePlugin)this).Config.Bind<float>("Input", "HoldSeconds", 0.5f, new ConfigDescription("Continuous hold time before a single pause/resume toggle.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
			ClassInjector.RegisterTypeInIl2Cpp<PauseRuntime>();
			Harmony val = new Harmony("HawChow.GTFOPause");
			try
			{
				val.PatchAll(typeof(Plugin).Assembly);
				RequirePatch(val, typeof(GameStateManager), "DoChangeState");
				RequirePatch(val, typeof(GS_AfterLevel), "CleanupAfterExpedition");
				RequirePatch(val, typeof(PlayerSync), "IncomingLocomotion");
				((BasePlugin)this).AddComponent<PauseRuntime>();
			}
			catch
			{
				val.UnpatchSelf();
				throw;
			}
			ManualLogSource log = ((BasePlugin)this).Log;
			bool flag = default(bool);
			BepInExInfoLogInterpolatedStringHandler val2 = new BepInExInfoLogInterpolatedStringHandler(129, 4, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("GTFOPause");
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("1.0.1");
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" loaded; host-only hold-P toggle=");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<KeyCode>(PauseKey.Value);
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(", hold=");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<float>(HoldSeconds.Value, "0.##");
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("s; GTFO Clock and controlled players pause while Unity physics remains live for drop-in.");
			}
			log.LogInfo(val2);
		}

		private static void RequirePatch(Harmony harmony, Type targetType, string methodName)
		{
			foreach (MethodBase patchedMethod in harmony.GetPatchedMethods())
			{
				if (patchedMethod.DeclaringType == targetType && patchedMethod.Name == methodName)
				{
					return;
				}
			}
			throw new InvalidOperationException("Required Harmony patch was not installed: " + targetType.FullName + "." + methodName);
		}
	}
	internal static class PluginInfo
	{
		internal const string Guid = "HawChow.GTFOPause";

		internal const string Name = "GTFOPause";

		internal const string Version = "1.0.1";
	}
}