Decompiled source of PersistentCheckpoint v1.0.0

TheRoughy.PersistentCheckpoint.dll

Decompiled a day ago
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Agents;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CellMenu;
using GTFO.PersistentCheckpoint.Core;
using GTFO.PersistentCheckpoint.Persistence;
using GameData;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Il2CppSystem.Runtime.InteropServices;
using LevelGeneration;
using Localization;
using Microsoft.CodeAnalysis;
using Player;
using SNetwork;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("GTFO Persistent Checkpoint")]
[assembly: AssemblyDescription("Persists GTFO checkpoint capture buffers between game sessions.")]
[assembly: AssemblyCompany("TheRoughy")]
[assembly: AssemblyProduct("GTFO Persistent Checkpoint")]
[assembly: ComVisible(false)]
[assembly: Guid("a11a2774-4115-4e28-a803-8f3fc4848f01")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.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;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace GTFO.PersistentCheckpoint.Persistence
{
	internal readonly record struct CheckpointIdentity(string ExpeditionKey, string RundownName, string ExpeditionName)
	{
		internal bool IsValid
		{
			get
			{
				if (!string.IsNullOrWhiteSpace(ExpeditionKey) && !string.IsNullOrWhiteSpace(RundownName))
				{
					return !string.IsNullOrWhiteSpace(ExpeditionName);
				}
				return false;
			}
		}

		internal string GetFilename()
		{
			if (!IsValid)
			{
				throw new InvalidDataException("Checkpoint mission names are unavailable.");
			}
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
			{
				binaryWriter.Write("GTFO checkpoint identity v1");
				binaryWriter.Write(ExpeditionKey);
				binaryWriter.Write(RundownName);
				binaryWriter.Write(ExpeditionName);
			}
			return Convert.ToHexString(SHA256.HashData(memoryStream.ToArray())) + ".pcp";
		}

		[CompilerGenerated]
		private bool PrintMembers(StringBuilder builder)
		{
			builder.Append("ExpeditionKey = ");
			builder.Append((object?)ExpeditionKey);
			builder.Append(", RundownName = ");
			builder.Append((object?)RundownName);
			builder.Append(", ExpeditionName = ");
			builder.Append((object?)ExpeditionName);
			return true;
		}
	}
	internal sealed record CheckpointSnapshot
	{
		internal int BuildRevision { get; init; }

		internal string ExpeditionKey { get; init; } = string.Empty;

		internal string RundownKey { get; init; } = string.Empty;

		internal string RundownName { get; init; } = string.Empty;

		internal string ExpeditionName { get; init; } = string.Empty;

		internal CheckpointIdentity Identity => new CheckpointIdentity(ExpeditionKey, RundownName, ExpeditionName);

		internal int Tier { get; init; }

		internal int ExpeditionIndex { get; init; }

		internal int HostSeed { get; init; }

		internal int SessionSeed { get; init; }

		internal ulong LevelChecksum { get; init; }

		internal float ProgressionTime { get; init; }

		internal ulong RecallCount { get; init; }

		internal long SavedAtUtcTicks { get; init; }

		internal int RequiredSlotCapacity { get; init; } = 4;

		internal bool LobbyExpansionPresent { get; init; }

		internal string LobbyExpansionVersion { get; init; } = string.Empty;

		internal List<CapturedPlayer> Players { get; init; } = new List<CapturedPlayer>();

		internal List<List<byte[]>> Passes { get; init; } = new List<List<byte[]>>();

		[CompilerGenerated]
		private bool PrintMembers(StringBuilder builder)
		{
			return false;
		}
	}
	internal readonly record struct CapturedPlayer(ulong Lookup, int CharacterIndex, bool IsBot, bool HasResources = false, float HealthRel = 0f, float Infection = 0f, float StandardAmmo = 0f, float SpecialAmmo = 0f, float ClassAmmo = 0f, float ResourcePackAmmo = 0f, float ConsumableAmmo = 0f);
	internal readonly record struct SnapshotLoadResult(CheckpointSnapshot? Snapshot, string? Error)
	{
		internal bool Success => Snapshot != null;

		[CompilerGenerated]
		private bool PrintMembers(StringBuilder builder)
		{
			builder.Append("Snapshot = ");
			builder.Append(Snapshot);
			builder.Append(", Error = ");
			builder.Append((object?)Error);
			return true;
		}
	}
	internal static class CheckpointStore
	{
		private static readonly byte[] Magic = Encoding.ASCII.GetBytes("GTFOCP01");

		private const int FormatVersion = 3;

		private const int HashLength = 32;

		private const int MaxStringBytes = 2048;

		private const int MaxPasses = 32;

		private const int MaxPacketsPerPass = 1000000;

		private const int MaxPacketBytes = 16777216;

		private const long MaxPayloadBytes = 536870912L;

		private static readonly object QueueLock = new object();

		private static Task _pending = Task.CompletedTask;

		private static string _directory = string.Empty;

		internal static void Initialize()
		{
			_directory = Path.Combine(Paths.ConfigPath, "GTFO_PersistentCheckpoint", "checkpoints");
			Directory.CreateDirectory(_directory);
		}

		internal static SnapshotLoadResult TryLoad(CheckpointIdentity identity)
		{
			try
			{
				string path = GetPath(identity);
				if (!File.Exists(path))
				{
					return new SnapshotLoadResult(null, null);
				}
				byte[] array = File.ReadAllBytes(path);
				if (array.LongLength < Magic.Length + 4 + 32 || array.LongLength > 536870912)
				{
					return new SnapshotLoadResult(null, "checkpoint file has an invalid size");
				}
				int num = array.Length - 32;
				if (!CryptographicOperations.FixedTimeEquals(SHA256.HashData(array.AsSpan(0, num)), array.AsSpan(num, 32)))
				{
					return new SnapshotLoadResult(null, "checkpoint integrity check failed");
				}
				using MemoryStream memoryStream = new MemoryStream(array, 0, num, writable: false);
				using BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8, leaveOpen: false);
				if (!binaryReader.ReadBytes(Magic.Length).SequenceEqual(Magic))
				{
					return new SnapshotLoadResult(null, "checkpoint magic is invalid");
				}
				if (binaryReader.ReadInt32() != 3)
				{
					return new SnapshotLoadResult(null, "checkpoint format is unsupported");
				}
				CheckpointSnapshot checkpointSnapshot = ReadSnapshot(binaryReader);
				if (memoryStream.Position != memoryStream.Length)
				{
					return new SnapshotLoadResult(null, "checkpoint contains trailing payload data");
				}
				if (!string.Equals(checkpointSnapshot.ExpeditionKey, identity.ExpeditionKey, StringComparison.Ordinal))
				{
					return new SnapshotLoadResult(null, "checkpoint expedition identity does not match its file");
				}
				if (checkpointSnapshot.Identity != identity)
				{
					return new SnapshotLoadResult(null, "checkpoint mission names do not match the selected mission");
				}
				return new SnapshotLoadResult(checkpointSnapshot, null);
			}
			catch (Exception ex)
			{
				Log.Warning("Failed to load checkpoint for " + identity.ExpeditionKey + ": " + ex.Message);
				return new SnapshotLoadResult(null, ex.Message);
			}
		}

		internal static void EnqueueSave(CheckpointSnapshot snapshot)
		{
			Enqueue(delegate
			{
				WriteAtomic(snapshot);
			});
		}

		internal static void EnqueueDelete(CheckpointIdentity identity)
		{
			Enqueue(delegate
			{
				string path = GetPath(identity);
				if (File.Exists(path))
				{
					File.Delete(path);
				}
			});
		}

		internal static void Flush(TimeSpan timeout)
		{
			Task pending;
			lock (QueueLock)
			{
				pending = _pending;
			}
			try
			{
				if (!pending.Wait(timeout))
				{
					Log.Warning("Timed out while flushing checkpoint writes.");
				}
			}
			catch (Exception exception)
			{
				Log.Error(exception);
			}
		}

		private static void Enqueue(Action operation)
		{
			lock (QueueLock)
			{
				_pending = _pending.ContinueWith(delegate
				{
					try
					{
						operation();
					}
					catch (Exception exception)
					{
						Log.Error(exception);
					}
				}, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default);
			}
		}

		private static void WriteAtomic(CheckpointSnapshot snapshot)
		{
			Directory.CreateDirectory(_directory);
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
			{
				binaryWriter.Write(Magic);
				binaryWriter.Write(3);
				WriteSnapshot(binaryWriter, snapshot);
			}
			if (memoryStream.Length > 536870912)
			{
				throw new InvalidDataException($"Checkpoint payload is too large ({memoryStream.Length} bytes).");
			}
			byte[] array = memoryStream.ToArray();
			byte[] array2 = SHA256.HashData(array);
			string path = GetPath(snapshot.Identity);
			string text = path + ".tmp";
			using (FileStream fileStream = new FileStream(text, FileMode.Create, FileAccess.Write, FileShare.None))
			{
				fileStream.Write(array);
				fileStream.Write(array2);
				fileStream.Flush(flushToDisk: true);
			}
			File.Move(text, path, overwrite: true);
			Log.Info($"Saved checkpoint for {snapshot.ExpeditionKey} ({array.Length + array2.Length:N0} bytes).");
		}

		private static void WriteSnapshot(BinaryWriter writer, CheckpointSnapshot snapshot)
		{
			writer.Write(snapshot.BuildRevision);
			WriteString(writer, snapshot.ExpeditionKey);
			WriteString(writer, snapshot.RundownKey);
			WriteString(writer, snapshot.RundownName);
			WriteString(writer, snapshot.ExpeditionName);
			writer.Write(snapshot.Tier);
			writer.Write(snapshot.ExpeditionIndex);
			writer.Write(snapshot.HostSeed);
			writer.Write(snapshot.SessionSeed);
			writer.Write(snapshot.LevelChecksum);
			writer.Write(snapshot.ProgressionTime);
			writer.Write(snapshot.RecallCount);
			writer.Write(snapshot.SavedAtUtcTicks);
			writer.Write(snapshot.RequiredSlotCapacity);
			writer.Write(snapshot.LobbyExpansionPresent);
			WriteString(writer, snapshot.LobbyExpansionVersion);
			writer.Write(snapshot.Players.Count);
			foreach (CapturedPlayer player in snapshot.Players)
			{
				writer.Write(player.Lookup);
				writer.Write(player.CharacterIndex);
				writer.Write(player.IsBot);
				writer.Write(player.HasResources);
				writer.Write(player.HealthRel);
				writer.Write(player.Infection);
				writer.Write(player.StandardAmmo);
				writer.Write(player.SpecialAmmo);
				writer.Write(player.ClassAmmo);
				writer.Write(player.ResourcePackAmmo);
				writer.Write(player.ConsumableAmmo);
			}
			writer.Write(snapshot.Passes.Count);
			foreach (List<byte[]> pass in snapshot.Passes)
			{
				writer.Write(pass.Count);
				foreach (byte[] item in pass)
				{
					if (item.Length > 16777216)
					{
						throw new InvalidDataException($"Capture packet is too large ({item.Length} bytes).");
					}
					writer.Write(item.Length);
					writer.Write(item);
				}
			}
		}

		private static CheckpointSnapshot ReadSnapshot(BinaryReader reader)
		{
			int buildRevision = reader.ReadInt32();
			string expeditionKey = ReadString(reader);
			string rundownKey = ReadString(reader);
			string rundownName = ReadString(reader);
			string expeditionName = ReadString(reader);
			int tier = reader.ReadInt32();
			int expeditionIndex = reader.ReadInt32();
			int hostSeed = reader.ReadInt32();
			int sessionSeed = reader.ReadInt32();
			ulong levelChecksum = reader.ReadUInt64();
			float progressionTime = reader.ReadSingle();
			ulong recallCount = reader.ReadUInt64();
			long savedAtUtcTicks = reader.ReadInt64();
			int val = reader.ReadInt32();
			bool lobbyExpansionPresent = reader.ReadBoolean();
			string lobbyExpansionVersion = ReadString(reader);
			int num = ReadBoundedCount(reader, 256, "player");
			List<CapturedPlayer> list = new List<CapturedPlayer>(num);
			for (int i = 0; i < num; i++)
			{
				ulong lookup = reader.ReadUInt64();
				int characterIndex = reader.ReadInt32();
				bool isBot = reader.ReadBoolean();
				list.Add(new CapturedPlayer(lookup, characterIndex, isBot, reader.ReadBoolean(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()));
			}
			int num2 = ReadBoundedCount(reader, 32, "capture pass");
			List<List<byte[]>> list2 = new List<List<byte[]>>(num2);
			long num3 = 0L;
			for (int j = 0; j < num2; j++)
			{
				int num4 = ReadBoundedCount(reader, 1000000, "packet");
				List<byte[]> list3 = new List<byte[]>(num4);
				for (int k = 0; k < num4; k++)
				{
					int num5 = ReadBoundedCount(reader, 16777216, "packet byte");
					num3 += num5;
					if (num3 > 536870912)
					{
						throw new InvalidDataException("Checkpoint packet data exceeds the allowed size.");
					}
					byte[] array = reader.ReadBytes(num5);
					if (array.Length != num5)
					{
						throw new EndOfStreamException("Checkpoint packet was truncated.");
					}
					list3.Add(array);
				}
				list2.Add(list3);
			}
			return new CheckpointSnapshot
			{
				BuildRevision = buildRevision,
				ExpeditionKey = expeditionKey,
				RundownKey = rundownKey,
				RundownName = rundownName,
				ExpeditionName = expeditionName,
				Tier = tier,
				ExpeditionIndex = expeditionIndex,
				HostSeed = hostSeed,
				SessionSeed = sessionSeed,
				LevelChecksum = levelChecksum,
				ProgressionTime = progressionTime,
				RecallCount = recallCount,
				SavedAtUtcTicks = savedAtUtcTicks,
				RequiredSlotCapacity = Math.Max(4, val),
				LobbyExpansionPresent = lobbyExpansionPresent,
				LobbyExpansionVersion = lobbyExpansionVersion,
				Players = list,
				Passes = list2
			};
		}

		private static int ReadBoundedCount(BinaryReader reader, int maximum, string name)
		{
			int num = reader.ReadInt32();
			if (num < 0 || num > maximum)
			{
				throw new InvalidDataException($"Invalid {name} count: {num}.");
			}
			return num;
		}

		private static void WriteString(BinaryWriter writer, string value)
		{
			byte[] bytes = Encoding.UTF8.GetBytes(value ?? string.Empty);
			if (bytes.Length > 2048)
			{
				throw new InvalidDataException("Checkpoint string is too long.");
			}
			writer.Write(bytes.Length);
			writer.Write(bytes);
		}

		private static string ReadString(BinaryReader reader)
		{
			int num = ReadBoundedCount(reader, 2048, "string byte");
			byte[] array = reader.ReadBytes(num);
			if (array.Length != num)
			{
				throw new EndOfStreamException("Checkpoint string was truncated.");
			}
			return Encoding.UTF8.GetString(array);
		}

		private static string GetPath(CheckpointIdentity identity)
		{
			return Path.Combine(_directory, identity.GetFilename());
		}
	}
	internal static class DestroyedDoorPreparation
	{
		internal static int Prepare(CheckpointSnapshot snapshot)
		{
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Invalid comparison between Unknown and I4
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Invalid comparison between Unknown and I4
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			if (!SNet.IsMaster)
			{
				return 0;
			}
			Dictionary<(ushort, byte), byte[]> dictionary = new Dictionary<(ushort, byte), byte[]>();
			foreach (List<byte[]> pass in snapshot.Passes)
			{
				foreach (byte[] item in pass)
				{
					if (item.Length >= 3)
					{
						dictionary[(BinaryPrimitives.ReadUInt16LittleEndian(item), item[2])] = item;
					}
				}
			}
			List<(LG_Door_Sync, pDoorState, string)> list = new List<(LG_Door_Sync, pDoorState, string)>();
			foreach (Object item2 in (Il2CppArrayBase<Object>)(object)Resources.FindObjectsOfTypeAll(Il2CppType.Of<LG_WeakDoor>()))
			{
				LG_WeakDoor val = ((Il2CppObjectBase)item2).TryCast<LG_WeakDoor>();
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				try
				{
					if ((Object)(object)val.Gate == (Object)null)
					{
						continue;
					}
					Component syncComp = val.m_syncComp;
					LG_Door_Sync val2 = ((syncComp != null) ? ((Il2CppObjectBase)syncComp).TryCast<LG_Door_Sync>() : null);
					if ((Object)(object)val2 == (Object)null || val2.m_stateReplicator == null)
					{
						continue;
					}
					SNet_Packet<pDoorState> statePacket_Recall = val2.m_stateReplicator.m_statePacket_Recall;
					if (!dictionary.TryGetValue((((SNet_Packet)statePacket_Recall).Replicator.Key, ((SNet_Packet)statePacket_Recall).Index), out var value))
					{
						continue;
					}
					SNet_Marshaller<pDoorState> s_marshaller = SNet_Packet<pDoorState>.s_marshaller;
					if (s_marshaller == null || value.Length != s_marshaller.SizeWithIDs)
					{
						Log.Warning("Skipping door preparation for " + ((Object)val).name + ": saved door packet size does not match the current game.");
						continue;
					}
					pDoorState val3 = DoorStatePacketDecoder.Decode(value, s_marshaller.Size, ((SNet_Marshaller)s_marshaller).m_marshallingType);
					if ((int)val3.status == 11 && (int)val2.GetCurrentSyncState().status != 11)
					{
						list.Add((val2, val3, ((Object)val).name));
					}
				}
				catch (Exception value2)
				{
					Log.Warning($"Skipping door preparation for {((Object)val).name}: {value2}");
				}
			}
			int num = 0;
			foreach (var item3 in list)
			{
				try
				{
					item3.Item1.m_stateReplicator.OnStateChange_Master(item3.Item2);
					num++;
					Log.Info("Broadcast pre-recall destruction for " + item3.Item3 + ".");
				}
				catch (Exception value3)
				{
					Log.Warning($"Pre-recall destruction failed for {item3.Item3}; host recall repair remains available: {value3}");
				}
			}
			return num;
		}
	}
	internal static class DoorStatePacketDecoder
	{
		internal unsafe static pDoorState Decode(byte[] packet, int payloadSize, Type marshallingType)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			if (payloadSize <= 0 || packet.Length < 3 || packet.Length - 3 != payloadSize)
			{
				throw new InvalidDataException("Saved door packet does not match the current door marshaller's size.");
			}
			IntPtr intPtr = Marshal.AllocHGlobal(payloadSize);
			try
			{
				Marshal.Copy(packet, 3, intPtr, payloadSize);
				Object val = Marshal.PtrToStructure(intPtr, marshallingType);
				if (val == null)
				{
					throw new InvalidDataException("The game's door marshaller returned no state.");
				}
				IntPtr intPtr2 = IL2CPP.il2cpp_object_unbox(((Il2CppObjectBase)val).Pointer);
				if (intPtr2 == IntPtr.Zero)
				{
					throw new InvalidDataException("The game's door state could not be unboxed.");
				}
				pDoorState result = Unsafe.Read<pDoorState>((void*)intPtr2);
				GC.KeepAlive(val);
				return result;
			}
			finally
			{
				Marshal.FreeHGlobal(intPtr);
			}
		}
	}
	internal static class LobbyCompatibility
	{
		internal const string PluginGuid = "GTFO.LobbyExpansion";

		internal static bool IsPresent => ((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.ContainsKey("GTFO.LobbyExpansion");

		internal static string Version
		{
			get
			{
				if (!((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.TryGetValue("GTFO.LobbyExpansion", out var value))
				{
					return string.Empty;
				}
				return value.Instance?.GetType().Assembly.GetName().Version?.ToString() ?? "unknown";
			}
		}

		internal static string Description
		{
			get
			{
				if (!IsPresent)
				{
					return "not present";
				}
				return "present (" + Version + ")";
			}
		}

		internal static int CurrentSlotCapacity
		{
			get
			{
				try
				{
					SNet_PlayerSlotManager slots = SNet.Slots;
					return ((slots == null) ? ((int?)null) : ((Il2CppArrayBase<SNet_Slot>)(object)slots.CharacterSlots)?.Length) ?? 4;
				}
				catch
				{
					return 4;
				}
			}
		}

		internal static bool CanRepresent(CheckpointSnapshot snapshot, out string reason)
		{
			int currentSlotCapacity = CurrentSlotCapacity;
			if (currentSlotCapacity < snapshot.RequiredSlotCapacity)
			{
				reason = $"checkpoint needs {snapshot.RequiredSlotCapacity} character slots, current lobby has {currentSlotCapacity}";
				return false;
			}
			reason = string.Empty;
			return true;
		}
	}
	internal static class ResumeCoordinator
	{
		private sealed record PlayerResync(ulong Lookup, string DisplayName);

		private static MethodInfo? _setActiveExpedition;

		private static MethodInfo? _startSinglePlayerRecall;

		private static pActiveExpedition _freshPackage = null;

		private static bool _hasFreshPackage;

		private static bool _applyingPackage;

		private static bool _suppressCheckpointSave;

		private static int _requiredSlotCapacity = 4;

		private static bool _pendingRecallDelayStarted;

		private static float _pendingRecallNotBefore;

		private static bool _pendingDoorPreparationStarted;

		private static float _pendingDoorPreparationNotBefore;

		private static readonly Queue<PlayerResync> PlayerResyncQueue = new Queue<PlayerResync>();

		private static PlayerResync? _activePlayerResync;

		private static CheckpointSnapshot? _playerResyncSnapshot;

		private static float _activePlayerResyncDeadline;

		private static float _nextPlayerResyncTime;

		private static bool _activePlayerResyncSawNativeSync;

		private static bool _activePlayerResyncTimedOut;

		private static CheckpointSnapshot? _bridgeSnapshot;

		private static float _bridgeNotBefore;

		private static float _bridgeDeadline;

		private static CheckpointIdentity? _activeIdentity;

		private const float PendingRecallDelaySeconds = 3f;

		private const float DoorPreparationDelaySeconds = 1f;

		private const float InitialPlayerResyncDelaySeconds = 1.5f;

		private const float PlayerResyncDelaySeconds = 0.25f;

		private const float PlayerResyncTimeoutSeconds = 20f;

		private const float BridgeStabilizationSeconds = 2f;

		private const float BridgeReadyTimeoutSeconds = 20f;

		internal static CheckpointSnapshot? ActiveSnapshot { get; private set; }

		internal static CheckpointSnapshot? PendingRecall { get; private set; }

		internal static string ActiveExpeditionKey { get; private set; } = string.Empty;

		internal static bool ResumeSelected { get; private set; }

		internal static string? LastSelectionError { get; private set; }

		internal static bool HasResumeChoice
		{
			get
			{
				if (ActiveSnapshot != null)
				{
					return !string.IsNullOrEmpty(ActiveExpeditionKey);
				}
				return false;
			}
		}

		internal static bool ShouldShowControl
		{
			get
			{
				if (!HasResumeChoice)
				{
					return LastSelectionError != null;
				}
				return true;
			}
		}

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

		internal static bool CanCreateManualCheckpoint
		{
			get
			{
				if (ShouldShowCreateControl && (Object)(object)CheckpointManager.Current != (Object)null && (Object)(object)SNet.Capture != (Object)null && !SNet.Capture.IsCapturing && !SNet.Capture.IsRecalling && !SNet.MasterManagement.IsMigrating && !_suppressCheckpointSave && !_pendingDoorPreparationStarted && PendingRecall == null && _bridgeSnapshot == null && _playerResyncSnapshot == null && _activePlayerResync == null)
				{
					return PlayerResyncQueue.Count == 0;
				}
				return false;
			}
		}

		internal static void CreateManualCheckpoint()
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if (!CanCreateManualCheckpoint)
			{
				return;
			}
			try
			{
				if (GetUnreadyConnectedPlayers().Count > 0)
				{
					ShowHostWarning("CHECKPOINT NOT CREATED\nWAIT FOR PLAYERS TO FINISH SYNCING");
					return;
				}
				pActiveExpedition activeExpeditionData = RundownManager.GetActiveExpeditionData();
				if (!TryGetIdentity(activeExpeditionData, RundownManager.ActiveExpedition, out CheckpointIdentity _, out string reason))
				{
					throw new InvalidOperationException(reason);
				}
				PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent();
				if ((Object)(object)localPlayerAgent == (Object)null)
				{
					throw new InvalidOperationException("local player agent is unavailable");
				}
				CheckpointManager.StoreCheckpoint(((Agent)localPlayerAgent).EyePosition);
				SNet.Capture.CaptureGameState((eBufferType)3);
				Log.Info("Manually created checkpoint for " + GetExpeditionKey(activeExpeditionData) + ".");
			}
			catch (Exception value)
			{
				Log.Error($"Manual checkpoint creation failed: {value}");
				ShowHostWarning("CHECKPOINT CREATION FAILED\nSEE HOST LOG");
			}
		}

		internal static void OnSelectingExpedition(ref pActiveExpedition package, ExpeditionInTierData expedition)
		{
			if (_applyingPackage || !SNet.IsMaster)
			{
				return;
			}
			ResetSelection();
			if (expedition == null || expedition.SkipLobby)
			{
				if (expedition != null)
				{
					Log.Info("Persistent checkpoints disabled for " + GetExpeditionKey(package) + ": expedition skips the lobby.");
				}
				return;
			}
			string text = (ActiveExpeditionKey = GetExpeditionKey(package));
			if (!TryGetIdentity(package, expedition, out CheckpointIdentity identity, out string reason))
			{
				LastSelectionError = reason;
				Log.Warning("Checkpoint identity for " + text + " is unavailable: " + reason);
				return;
			}
			_activeIdentity = identity;
			_freshPackage = ClonePackage(package);
			_hasFreshPackage = true;
			SnapshotLoadResult snapshotLoadResult = CheckpointStore.TryLoad(identity);
			if (!snapshotLoadResult.Success)
			{
				LastSelectionError = snapshotLoadResult.Error;
				if (snapshotLoadResult.Error != null)
				{
					Log.Warning("Checkpoint for " + text + " is unavailable: " + snapshotLoadResult.Error);
				}
				return;
			}
			CheckpointSnapshot snapshot = snapshotLoadResult.Snapshot;
			if (!ValidateBeforeGeneration(snapshot, package, expedition, out string reason2))
			{
				LastSelectionError = reason2;
				Log.Warning("Checkpoint for " + text + " is incompatible: " + reason2);
				return;
			}
			ActiveSnapshot = snapshot;
			ResumeSelected = true;
			ApplySeeds(ref package, snapshot);
			Log.Info($"Selected persistent checkpoint for {text}; session seed {snapshot.SessionSeed}.");
		}

		internal static void ToggleResume()
		{
			if (SNet.IsMaster && !SkipUnsupportedActiveExpedition() && !(ActiveSnapshot == null) && _hasFreshPackage)
			{
				bool flag = !ResumeSelected;
				pActiveExpedition package = (pActiveExpedition)(flag ? ((object)RundownManager.GetActiveExpeditionData()) : ((object)_freshPackage));
				if (flag)
				{
					ApplySeeds(ref package, ActiveSnapshot);
				}
				if (ApplyActivePackage(package))
				{
					ResumeSelected = flag;
					Log.Info($"Resume checkpoint for {ActiveExpeditionKey}: {(flag ? "enabled" : "disabled")}.");
				}
			}
		}

		internal static void OnStartLevelResult(bool started)
		{
			if (started && SNet.IsMaster && !SkipUnsupportedActiveExpedition() && !string.IsNullOrEmpty(ActiveExpeditionKey) && _activeIdentity.HasValue)
			{
				if (ResumeSelected && ActiveSnapshot != null)
				{
					ResetPendingRecallDelay();
					PendingRecall = ActiveSnapshot;
					Log.Info("Expedition launch will recall checkpoint for " + ActiveExpeditionKey + ".");
				}
				else
				{
					CheckpointStore.EnqueueDelete(_activeIdentity.Value);
					ActiveSnapshot = null;
					Log.Info("Fresh expedition selected; deleted the old checkpoint for " + ActiveExpeditionKey + ".");
				}
			}
		}

		internal static void CaptureCheckpoint()
		{
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Expected I4, but got Unknown
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			if (_suppressCheckpointSave || _pendingDoorPreparationStarted || !SNet.IsMaster)
			{
				return;
			}
			try
			{
				if (SkipUnsupportedActiveExpedition())
				{
					return;
				}
				pActiveExpedition activeExpeditionData = RundownManager.GetActiveExpeditionData();
				string expeditionKey = GetExpeditionKey(activeExpeditionData);
				if (!TryGetIdentity(activeExpeditionData, RundownManager.ActiveExpedition, out CheckpointIdentity identity, out string reason))
				{
					throw new InvalidOperationException(reason);
				}
				SNet_CaptureBuffer val = ((Il2CppArrayBase<SNet_CaptureBuffer>)(object)SNet.Capture.m_buffers)[3];
				if (val == null || !val.isValid)
				{
					return;
				}
				List<List<byte[]>> list = new List<List<byte[]>>(SNet_CaptureBuffer.PassCount);
				for (int i = 0; i < SNet_CaptureBuffer.PassCount; i++)
				{
					List<Il2CppStructArray<byte>> val2 = ((Il2CppArrayBase<List<Il2CppStructArray<byte>>>)(object)val.m_passes)[i];
					List<byte[]> list2 = new List<byte[]>(val2.Count);
					for (int j = 0; j < val2.Count; j++)
					{
						list2.Add(CopyBytes(val2[j]));
					}
					list.Add(list2);
				}
				List<CapturedPlayer> list3 = new List<CapturedPlayer>();
				int num = _requiredSlotCapacity;
				Enumerator<SNet_Player> enumerator = SNet.Slots.SlottedPlayers.GetEnumerator();
				while (enumerator.MoveNext())
				{
					SNet_Player current = enumerator.Current;
					if (!((Object)(object)current == (Object)null))
					{
						list3.Add(CapturePlayerState(current));
						num = Math.Max(num, current.CharacterIndex + 1);
					}
				}
				CheckpointSnapshot obj = new CheckpointSnapshot
				{
					BuildRevision = CellBuildData.GetRevision(),
					ExpeditionKey = expeditionKey,
					RundownKey = activeExpeditionData.rundownKey.data,
					RundownName = identity.RundownName,
					ExpeditionName = identity.ExpeditionName,
					Tier = (int)activeExpeditionData.tier,
					ExpeditionIndex = activeExpeditionData.expeditionIndex,
					HostSeed = activeExpeditionData.hostIDSeed,
					SessionSeed = activeExpeditionData.sessionSeed,
					LevelChecksum = val.data.levelChecksum,
					ProgressionTime = val.data.progressionTime,
					RecallCount = val.data.recallCount,
					SavedAtUtcTicks = DateTime.UtcNow.Ticks,
					RequiredSlotCapacity = num,
					LobbyExpansionPresent = LobbyCompatibility.IsPresent,
					LobbyExpansionVersion = LobbyCompatibility.Version,
					Players = list3,
					Passes = list
				};
				ActiveExpeditionKey = expeditionKey;
				_activeIdentity = identity;
				ActiveSnapshot = obj;
				ResumeSelected = true;
				CheckpointStore.EnqueueSave(obj);
			}
			catch (Exception exception)
			{
				Log.Error(exception);
			}
		}

		internal static bool TryRecallPending()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			CheckpointSnapshot pendingRecall = PendingRecall;
			if (pendingRecall == null || !SNet.IsMaster)
			{
				ResetPendingRecallDelay();
				return false;
			}
			if ((int)GameStateManager.CurrentStateName != 10)
			{
				if (_pendingDoorPreparationStarted)
				{
					PendingRecall = null;
					ResetPendingRecallDelay();
					Log.Warning("Cancelled pending checkpoint recall after leaving InLevel during door preparation.");
				}
				return false;
			}
			if (SkipUnsupportedActiveExpedition())
			{
				return false;
			}
			if (SNet.Capture.IsCapturing || SNet.Capture.IsRecalling || (Object)(object)CheckpointManager.Current == (Object)null)
			{
				return false;
			}
			if (!_pendingRecallDelayStarted)
			{
				if (!GameStateManager.IsEveryoneInState((eGameStateName)10, true, true))
				{
					return false;
				}
				_pendingRecallDelayStarted = true;
				_pendingRecallNotBefore = Time.realtimeSinceStartup + 3f;
				ShowHostWarning("TEMPORAL INSTABILITY DETECTED\nRESTORING TIMELINE...\nEL PSY KONGROO");
				Log.Info($"All players reached InLevel; persistent checkpoint recall will begin in {3f:F1} seconds.");
				return false;
			}
			if (Time.realtimeSinceStartup < _pendingRecallNotBefore)
			{
				return false;
			}
			if (!_pendingDoorPreparationStarted)
			{
				try
				{
					ValidateGeneratedLevel(pendingRecall);
					_pendingDoorPreparationStarted = true;
					int num = DestroyedDoorPreparation.Prepare(pendingRecall);
					_pendingDoorPreparationNotBefore = Time.realtimeSinceStartup + ((num > 0) ? 1f : 0f);
					Log.Info($"Prepared {num} destroyed door(s) before persistent checkpoint recall; callback settling delay: {((num > 0) ? 1f : 0f):F1} seconds.");
					if (num > 0)
					{
						return false;
					}
				}
				catch (Exception value)
				{
					PendingRecall = null;
					ResetPendingRecallDelay();
					Log.Error($"Persistent checkpoint door preparation failed: {value}");
					return false;
				}
			}
			if (Time.realtimeSinceStartup < _pendingDoorPreparationNotBefore)
			{
				return false;
			}
			PendingRecall = null;
			ResetPendingRecallDelay();
			RestoreAndRecall(pendingRecall);
			return true;
		}

		internal static void OnPlayerEvent(SNet_Player player, SNet_PlayerEvent playerEvent)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			if ((int)playerEvent == 3 && !(_activePlayerResync == null) && !((Object)(object)player == (Object)null) && player.Lookup == _activePlayerResync.Lookup)
			{
				_activePlayerResyncSawNativeSync = true;
				Log.Info($"GTFO reported PlayerIsSynced for checkpoint hard-sync target {player.NickName} ({player.Lookup}); {GetPlayerSyncSummary(player)}.");
			}
		}

		internal static void UpdatePlayerResync()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Invalid comparison between Unknown and I4
			if (!SNet.IsMaster || (int)GameStateManager.CurrentStateName != 10)
			{
				ClearPlayerResyncs();
			}
			else
			{
				if ((_activePlayerResync == null && _playerResyncSnapshot == null && PlayerResyncQueue.Count == 0) || SkipUnsupportedActiveExpedition())
				{
					return;
				}
				if (_activePlayerResync != null)
				{
					PlayerResync activePlayerResync = _activePlayerResync;
					SNet_Player val = FindSlottedPlayer(activePlayerResync.Lookup);
					if ((Object)(object)val == (Object)null || !val.IsInSessionHub)
					{
						Log.Warning($"Skipping checkpoint hard-sync for {activePlayerResync.DisplayName} ({activePlayerResync.Lookup}): player disconnected.");
						FinishActivePlayerResync();
					}
					else if (IsPlayerPlayingWithAgent(val) && (_activePlayerResyncSawNativeSync || (int)val.SyncProcessState == 5))
					{
						Log.Info($"Checkpoint hard-sync completed for {val.NickName} ({val.Lookup}); {GetPlayerSyncSummary(val)}.");
						FinishActivePlayerResync();
					}
					else if (!_activePlayerResyncTimedOut && Time.realtimeSinceStartup >= _activePlayerResyncDeadline)
					{
						if (IsPlayerPlayingWithAgent(val))
						{
							Log.Warning($"Checkpoint hard-sync completion signals were stale or missed for {val.NickName} ({val.Lookup}); accepting Playing with a valid agent after {20f:F0} seconds. {GetPlayerSyncSummary(val)}.");
							FinishActivePlayerResync();
						}
						else
						{
							Log.Error($"Checkpoint hard-sync timed out for {val.NickName} ({val.Lookup}). Remaining queued players: {PlayerResyncQueue.Count}. {GetPlayerSyncSummary(val)}. " + "The canonical checkpoint will not be created; that player should leave and rejoin.");
							ShowHostWarning("CHECKPOINT SYNC FAILED: " + val.NickName + "\nLEAVE AND REJOIN");
							_activePlayerResyncTimedOut = true;
						}
						return;
					}
				}
				if (_activePlayerResync != null || Time.realtimeSinceStartup < _nextPlayerResyncTime)
				{
					return;
				}
				while (PlayerResyncQueue.Count > 0)
				{
					PlayerResync playerResync = PlayerResyncQueue.Dequeue();
					SNet_Player val2 = FindSlottedPlayer(playerResync.Lookup);
					if ((Object)(object)val2 == (Object)null || !val2.IsInSessionHub)
					{
						Log.Warning($"Skipping checkpoint hard-sync for {playerResync.DisplayName} ({playerResync.Lookup}): player disconnected.");
						continue;
					}
					try
					{
						_activePlayerResync = playerResync;
						_activePlayerResyncDeadline = Time.realtimeSinceStartup + 20f;
						_activePlayerResyncSawNativeSync = false;
						_activePlayerResyncTimedOut = false;
						StartPlayerHardSync(val2);
						Log.Info($"Started checkpoint hard-sync for {val2.NickName} ({val2.Lookup}); {GetPlayerSyncSummary(val2)}.");
						return;
					}
					catch (Exception value)
					{
						Log.Error($"Failed to start checkpoint hard-sync for {val2.NickName} ({val2.Lookup}): {value}");
						ShowHostWarning("CHECKPOINT SYNC FAILED: " + val2.NickName + "\nLEAVE AND REJOIN");
						ClearPlayerResyncs();
						return;
					}
				}
				if (_playerResyncSnapshot != null)
				{
					CheckpointSnapshot? playerResyncSnapshot = _playerResyncSnapshot;
					ClearPlayerResyncs();
					BeginBridgeRecall(playerResyncSnapshot);
				}
			}
		}

		internal static void UpdateBridgeRecall()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Invalid comparison between Unknown and I4
			CheckpointSnapshot bridgeSnapshot = _bridgeSnapshot;
			if (bridgeSnapshot == null)
			{
				return;
			}
			if (!SNet.IsMaster || (int)GameStateManager.CurrentStateName != 10)
			{
				ClearBridgeRecall();
			}
			else
			{
				if (SkipUnsupportedActiveExpedition() || Time.realtimeSinceStartup < _bridgeNotBefore || SNet.Capture.IsCapturing || SNet.Capture.IsRecalling)
				{
					return;
				}
				List<SNet_Player> unreadyConnectedPlayers = GetUnreadyConnectedPlayers();
				if (unreadyConnectedPlayers.Count > 0 && Time.realtimeSinceStartup < _bridgeDeadline)
				{
					return;
				}
				if (unreadyConnectedPlayers.Count <= 0)
				{
					try
					{
						ApplySavedPlayerResources(bridgeSnapshot);
						LogCurrentPlayerResources("Checkpoint bridge resources");
						bool suppressCheckpointSave = _suppressCheckpointSave;
						try
						{
							_suppressCheckpointSave = true;
							SNet.Capture.CaptureGameState((eBufferType)3);
						}
						finally
						{
							_suppressCheckpointSave = suppressCheckpointSave;
						}
						SNet_CaptureBuffer val = ((Il2CppArrayBase<SNet_CaptureBuffer>)(object)SNet.Capture.m_buffers)[3];
						if (val == null || !val.isValid)
						{
							throw new InvalidOperationException("canonical checkpoint capture was invalid");
						}
						CheckpointManager.ReloadCheckpoint();
						if (!SNet.Sync.SessionCommand((eSessionCommandType)8, 0))
						{
							Log.Warning("GTFO rejected ForceStartPlaying before canonical checkpoint recall; continuing with recall.");
						}
						StartCanonicalAllPlayerRecall();
						Log.Info("Captured and recalled canonical checkpoint for " + bridgeSnapshot.ExpeditionKey + " with GTFO's explicit all-player checkpoint protocol.");
						return;
					}
					catch (Exception value)
					{
						Log.Error($"Canonical checkpoint bridge failed: {value}");
						ShowHostWarning("CHECKPOINT SYNC FAILED\nLEAVE AND REJOIN");
						return;
					}
					finally
					{
						ClearBridgeRecall();
					}
				}
				string text = string.Join(", ", unreadyConnectedPlayers.Select((SNet_Player player) => player.NickName));
				Log.Error($"Timed out waiting for checkpoint bridge players after {20f:F0} seconds: {text}. " + "The canonical checkpoint recall was cancelled to avoid splitting the session again.");
				ShowHostWarning("CHECKPOINT SYNC INCOMPLETE: " + text + "\nLEAVE AND REJOIN");
				ClearBridgeRecall();
			}
		}

		internal static void ClearOnSuccess()
		{
			if (SNet.IsMaster && !SkipUnsupportedActiveExpedition() && !string.IsNullOrEmpty(ActiveExpeditionKey))
			{
				PendingRecall = null;
				ResetPendingRecallDelay();
				ActiveSnapshot = null;
				ResumeSelected = false;
				ClearPlayerResyncs();
				ClearBridgeRecall();
				if (_activeIdentity.HasValue)
				{
					CheckpointStore.EnqueueDelete(_activeIdentity.Value);
				}
				Log.Info("Expedition succeeded; deleted checkpoint for " + ActiveExpeditionKey + ".");
			}
		}

		internal static string GetButtonText()
		{
			if (ActiveSnapshot == null)
			{
				return "<color=#FF8050>CHECKPOINT UNAVAILABLE</color>  <size=55%>" + LastSelectionError + "</size>";
			}
			DateTime value = new DateTime(ActiveSnapshot.SavedAtUtcTicks, DateTimeKind.Utc).ToLocalTime();
			return $"[{(ResumeSelected ? "X" : " ")}] RESUME CHECKPOINT  <size=65%>{value:g}</size>";
		}

		internal static void ObserveCurrentSlots()
		{
			if (!SNet.HasLocalPlayer)
			{
				return;
			}
			try
			{
				Enumerator<SNet_Player> enumerator = SNet.Slots.SlottedPlayers.GetEnumerator();
				while (enumerator.MoveNext())
				{
					SNet_Player current = enumerator.Current;
					if ((Object)(object)current != (Object)null)
					{
						_requiredSlotCapacity = Math.Max(_requiredSlotCapacity, current.CharacterIndex + 1);
					}
				}
			}
			catch
			{
			}
		}

		private static void RestoreAndRecall(CheckpointSnapshot snapshot)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: 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)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			SNet_CaptureBuffer val = null;
			try
			{
				ValidateGeneratedLevel(snapshot);
				List<PlayerResync> players = CaptureConnectedHumans(snapshot);
				_suppressCheckpointSave = true;
				SNet.Capture.CaptureGameState((eBufferType)3);
				val = ((Il2CppArrayBase<SNet_CaptureBuffer>)(object)SNet.Capture.m_buffers)[3];
				ushort bufferID = val.data.bufferID;
				for (int i = 0; i < snapshot.Passes.Count; i++)
				{
					List<Il2CppStructArray<byte>> val2 = ((Il2CppArrayBase<List<Il2CppStructArray<byte>>>)(object)val.m_passes)[i];
					val2.Clear();
					foreach (byte[] item in snapshot.Passes[i])
					{
						val2.Add(new Il2CppStructArray<byte>(item));
					}
				}
				pBufferData data = val.data;
				data.sendingPlayerLookup = SNet.LocalPlayer.Lookup;
				data.levelChecksum = snapshot.LevelChecksum;
				data.bufferID = bufferID;
				data.progressionTime = snapshot.ProgressionTime;
				data.recallCount = snapshot.RecallCount;
				val.data = data;
				val.isValid = true;
				CheckpointManager.ReloadCheckpoint();
				SNet.Capture.InitCheckpointRecall();
				SNet.Capture.RecallGameState((eBufferType)3);
				if (Math.Abs(Clock.ExpeditionProgressionTime - snapshot.ProgressionTime) > 0.01f)
				{
					throw new InvalidOperationException($"Checkpoint recall returned without restoring progression time {snapshot.ProgressionTime:F2} (current {Clock.ExpeditionProgressionTime:F2}).");
				}
				Log.Info($"Recalled persistent checkpoint for {snapshot.ExpeditionKey} at {Clock.ExpeditionProgressionTime:F2} seconds.");
				QueuePlayerResyncs(players, snapshot);
			}
			catch (Exception value)
			{
				if (val != null)
				{
					val.Clear();
				}
				Log.Error($"Persistent checkpoint recall failed: {value}");
			}
			finally
			{
				_suppressCheckpointSave = false;
			}
		}

		private static void ValidateGeneratedLevel(CheckpointSnapshot snapshot)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!TryGetIdentity(RundownManager.GetActiveExpeditionData(), RundownManager.ActiveExpedition, out CheckpointIdentity identity, out string reason))
			{
				throw new InvalidOperationException(reason);
			}
			if (snapshot.Identity != identity)
			{
				throw new InvalidOperationException("Current mission names do not match the saved checkpoint.");
			}
			ulong num = SNet.LocalPlayer.Session.levelChecksum;
			if (num != snapshot.LevelChecksum)
			{
				throw new InvalidOperationException($"Generated level checksum {num} does not match checkpoint {snapshot.LevelChecksum}.");
			}
			if (!LobbyCompatibility.CanRepresent(snapshot, out string reason2))
			{
				throw new InvalidOperationException(reason2);
			}
			if (snapshot.Passes.Count != SNet_CaptureBuffer.PassCount)
			{
				throw new InvalidOperationException($"Checkpoint has {snapshot.Passes.Count} capture passes, game expects {SNet_CaptureBuffer.PassCount}.");
			}
		}

		private static List<PlayerResync> CaptureConnectedHumans(CheckpointSnapshot snapshot)
		{
			HashSet<ulong> hashSet = (from player in snapshot.Players
				where !player.IsBot
				select player.Lookup).ToHashSet();
			List<PlayerResync> list = new List<PlayerResync>();
			Enumerator<SNet_Player> enumerator = SNet.Slots.SlottedPlayers.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SNet_Player current = enumerator.Current;
				if (!((Object)(object)current == (Object)null) && !current.IsMaster && !current.IsBot && current.IsInSessionHub)
				{
					list.Add(new PlayerResync(current.Lookup, current.NickName));
					Log.Info($"Checkpoint player mapping: {current.NickName} ({current.Lookup}), character {current.CharacterIndex}, saved state {(hashSet.Contains(current.Lookup) ? "found" : "not found; vanilla defaults will apply")}, fresh agent key {GetPlayerAgentKey(current)}.");
				}
			}
			return list;
		}

		private static void QueuePlayerResyncs(IEnumerable<PlayerResync> players, CheckpointSnapshot snapshot)
		{
			ClearPlayerResyncs();
			_playerResyncSnapshot = snapshot;
			foreach (PlayerResync player in players)
			{
				PlayerResyncQueue.Enqueue(player);
			}
			if (PlayerResyncQueue.Count == 0)
			{
				CheckpointSnapshot? playerResyncSnapshot = _playerResyncSnapshot;
				ClearPlayerResyncs();
				BeginBridgeRecall(playerResyncSnapshot);
				return;
			}
			_nextPlayerResyncTime = Time.realtimeSinceStartup + 1.5f;
			Log.Info($"Queued {PlayerResyncQueue.Count} connected human player(s) for fresh DropIn hard-sync; the first sync will begin in {1.5f:F1} seconds.");
		}

		private static void StartPlayerHardSync(SNet_Player player)
		{
			if ((object)_startSinglePlayerRecall == null)
			{
				_startSinglePlayerRecall = typeof(SNet_SyncManager).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method)
				{
					ParameterInfo[] parameters = method.GetParameters();
					return method.Name == "StartRecall" && parameters.Length == 1 && parameters[0].ParameterType == typeof(SNet_Player);
				}) ?? throw new MissingMethodException(typeof(SNet_SyncManager).FullName, "StartRecall(SNet_Player)");
			}
			Log.Info($"DropIn capture resources for {player.NickName} ({player.Lookup}): {GetPlayerResourceSummary(player)}.");
			player.SyncProcessState = (eSyncProcessState)4;
			try
			{
				_startSinglePlayerRecall.Invoke(SNet.Sync, new object[1] { player });
			}
			catch
			{
				player.SyncProcessState = (eSyncProcessState)5;
				throw;
			}
		}

		private static void StartCanonicalAllPlayerRecall()
		{
			SNet.Capture.InitCheckpointRecall();
			int num = 0;
			Enumerator<SNet_Player> enumerator = SNet.Slots.PlayersSynchedWithGame.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SNet_Player current = enumerator.Current;
				if (!((Object)(object)current == (Object)null) && !current.IsMaster)
				{
					SNet.Sync.SendPlayerData_Session_SyncAllTo(current);
					num++;
				}
			}
			SNet.Capture.SendBuffer((eBufferType)3, (SNet_Player)null, false);
			SNet.Capture.SendBufferCommand((eBufferType)3, (eBufferOperationType)2, (SNet_Player)null, (ushort)0);
			SNet.Capture.RecallGameState((eBufferType)3);
			Log.Info($"Explicit canonical checkpoint recall sent to {num} synced remote player(s).");
		}

		private static void BeginBridgeRecall(CheckpointSnapshot snapshot)
		{
			_bridgeSnapshot = snapshot;
			_bridgeNotBefore = Time.realtimeSinceStartup + 2f;
			_bridgeDeadline = Time.realtimeSinceStartup + 20f;
			Log.Info($"All checkpoint hard-syncs completed; canonical all-player recall will begin after at least {2f:F1} seconds once current players are capture-ready.");
		}

		private static List<SNet_Player> GetUnreadyConnectedPlayers()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Invalid comparison between Unknown and I4
			List<SNet_Player> list = new List<SNet_Player>();
			Enumerator<SNet_Player> enumerator = SNet.Slots.SlottedPlayers.GetEnumerator();
			PlayerBackpack val = default(PlayerBackpack);
			while (enumerator.MoveNext())
			{
				SNet_Player current = enumerator.Current;
				if ((Object)(object)current == (Object)null || (!current.IsBot && !current.IsInSessionHub))
				{
					continue;
				}
				try
				{
					if ((!current.IsBot && (int)current.Session.mode != 8) || current.IsOutOfSync || !current.HasPlayerAgent || !PlayerBackpackManager.TryGetBackpack(current, ref val))
					{
						list.Add(current);
					}
				}
				catch
				{
					list.Add(current);
				}
			}
			return list;
		}

		private static void ApplySavedPlayerResources(CheckpointSnapshot snapshot)
		{
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			PlayerBackpack val2 = default(PlayerBackpack);
			foreach (CapturedPlayer player in snapshot.Players)
			{
				if (!player.HasResources)
				{
					continue;
				}
				SNet_Player val = FindSlottedPlayer(player.Lookup);
				if ((Object)(object)val == (Object)null || !val.HasPlayerAgent || !PlayerBackpackManager.TryGetBackpack(val, ref val2))
				{
					Log.Warning($"Could not apply saved resources for player {player.Lookup}: current agent or backpack unavailable.");
					continue;
				}
				try
				{
					PlayerAgent val3 = ((Il2CppObjectBase)val.PlayerAgent).TryCast<PlayerAgent>() ?? throw new InvalidOperationException("player agent could not be cast to PlayerAgent");
					((Dam_SyncedDamageBase)val3.Damage).Health = Mathf.Clamp01(player.HealthRel) * ((Dam_SyncedDamageBase)val3.Damage).HealthMax;
					val3.Damage.Infection = Mathf.Clamp01(player.Infection);
					pAmmoStorageData val4 = CreateAmmoDataForCapture(val, val2, player);
					val2.AmmoStorage.SetStorageData(ref val4);
					Log.Info($"Applied saved resources for {val.NickName} ({val.Lookup}): health={player.HealthRel:P0}, infection={player.Infection:P0}, {GetPlayerResourceSummary(val)}.");
				}
				catch (Exception ex)
				{
					Log.Warning($"Could not apply saved resources for {val.NickName} ({val.Lookup}): {ex.Message}");
				}
			}
		}

		private static pAmmoStorageData CreateAmmoDataForCapture(SNet_Player player, PlayerBackpack backpack, CapturedPlayer saved)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			float num = saved.StandardAmmo;
			float num2 = saved.SpecialAmmo;
			if (backpack.IsLocal || (SNet.IsMaster && player.IsBot))
			{
				num -= backpack.AmmoStorage.GetClipAmmoFromSlot((InventorySlot)1) * backpack.AmmoStorage.StandardAmmo.CostOfBullet;
				num2 -= backpack.AmmoStorage.GetClipAmmoFromSlot((InventorySlot)2) * backpack.AmmoStorage.SpecialAmmo.CostOfBullet;
			}
			pAmmoStorageData result = new pAmmoStorageData
			{
				PlayerLookup = player.Lookup,
				hasSetAmmo = true
			};
			((DevFloat32)(ref result.standardAmmo)).Set(Mathf.Max(0f, num), 500f);
			((DevFloat32)(ref result.specialAmmo)).Set(Mathf.Max(0f, num2), 500f);
			((DevFloat32)(ref result.classAmmo)).Set(Mathf.Max(0f, saved.ClassAmmo), 500f);
			((DevFloat32)(ref result.resourcePackAmmo)).Set(Mathf.Max(0f, saved.ResourcePackAmmo), 500f);
			((DevFloat32)(ref result.consumableAmmo)).Set(Mathf.Max(0f, saved.ConsumableAmmo), 500f);
			return result;
		}

		private static CapturedPlayer CapturePlayerState(SNet_Player player)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				PlayerBackpack val = default(PlayerBackpack);
				if (!player.HasPlayerAgent || !PlayerBackpackManager.TryGetBackpack(player, ref val))
				{
					return new CapturedPlayer(player.Lookup, player.CharacterIndex, player.IsBot);
				}
				PlayerAgent val2 = ((Il2CppObjectBase)player.PlayerAgent).TryCast<PlayerAgent>() ?? throw new InvalidOperationException("player agent could not be cast to PlayerAgent");
				pAmmoStorageData storageDataForCapture = val.AmmoStorage.GetStorageDataForCapture();
				return new CapturedPlayer(player.Lookup, player.CharacterIndex, player.IsBot, HasResources: true, ((Dam_SyncedDamageBase)val2.Damage).GetHealthRel(), val2.Damage.Infection, ((DevFloat32)(ref storageDataForCapture.standardAmmo)).Get(500f), ((DevFloat32)(ref storageDataForCapture.specialAmmo)).Get(500f), ((DevFloat32)(ref storageDataForCapture.classAmmo)).Get(500f), ((DevFloat32)(ref storageDataForCapture.resourcePackAmmo)).Get(500f), ((DevFloat32)(ref storageDataForCapture.consumableAmmo)).Get(500f));
			}
			catch (Exception ex)
			{
				Log.Warning($"Could not capture resources for {player.NickName} ({player.Lookup}): {ex.Message}");
				return new CapturedPlayer(player.Lookup, player.CharacterIndex, player.IsBot);
			}
		}

		private static SNet_Player? FindSlottedPlayer(ulong lookup)
		{
			Enumerator<SNet_Player> enumerator = SNet.Slots.SlottedPlayers.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SNet_Player current = enumerator.Current;
				if ((Object)(object)current != (Object)null && current.Lookup == lookup)
				{
					return current;
				}
			}
			return null;
		}

		private static string GetPlayerAgentKey(SNet_Player player)
		{
			try
			{
				if (player.HasPlayerAgent)
				{
					return player.PlayerAgent.GetReplicator().Key.ToString();
				}
			}
			catch
			{
			}
			return "none";
		}

		private static string GetPlayerSyncSummary(SNet_Player player)
		{
			//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_0036: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return $"mode={player.Session.mode}, processState={player.SyncProcessState}, hasAgent={player.HasPlayerAgent}, agent key={GetPlayerAgentKey(player)}, native PlayerIsSynced={_activePlayerResyncSawNativeSync}";
			}
			catch (Exception ex)
			{
				return $"sync state unavailable ({ex.Message}), native PlayerIsSynced={_activePlayerResyncSawNativeSync}";
			}
		}

		private static bool IsPlayerPlayingWithAgent(SNet_Player player)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			try
			{
				return (int)player.Session.mode == 8 && player.HasPlayerAgent;
			}
			catch
			{
				return false;
			}
		}

		private static void LogCurrentPlayerResources(string label)
		{
			Enumerator<SNet_Player> enumerator = SNet.Slots.SlottedPlayers.GetEnumerator();
			while (enumerator.MoveNext())
			{
				SNet_Player current = enumerator.Current;
				if ((Object)(object)current != (Object)null)
				{
					Log.Info($"{label} for {current.NickName} ({current.Lookup}): {GetPlayerResourceSummary(current)}.");
				}
			}
		}

		private static string GetPlayerResourceSummary(SNet_Player player)
		{
			//IL_001b: 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)
			try
			{
				PlayerBackpack val = default(PlayerBackpack);
				if (!PlayerBackpackManager.TryGetBackpack(player, ref val))
				{
					return "backpack unavailable";
				}
				pAmmoStorageData storageDataForCapture = val.AmmoStorage.GetStorageDataForCapture();
				return $"standard={((DevFloat32)(ref storageDataForCapture.standardAmmo)).Get(500f):F1}, special={((DevFloat32)(ref storageDataForCapture.specialAmmo)).Get(500f):F1}, tool={((DevFloat32)(ref storageDataForCapture.classAmmo)).Get(500f):F1}, resourcePack={((DevFloat32)(ref storageDataForCapture.resourcePackAmmo)).Get(500f):F1}, consumable={((DevFloat32)(ref storageDataForCapture.consumableAmmo)).Get(500f):F1}";
			}
			catch (Exception ex)
			{
				return "unavailable (" + ex.Message + ")";
			}
		}

		private static void ShowHostWarning(string message)
		{
			try
			{
				PlayerGuiLayer playerLayer = GuiManager.PlayerLayer;
				if (playerLayer == null || (Object)(object)playerLayer.m_wardenIntel == (Object)null)
				{
					Log.Warning("Could not display checkpoint warning: the player Warden HUD is unavailable.");
				}
				else
				{
					playerLayer.ShowWardenIntel(message, 0f, 8f);
				}
			}
			catch (Exception ex)
			{
				Log.Warning("Could not display checkpoint sync warning: " + ex.Message);
			}
		}

		private static void ClearBridgeRecall()
		{
			_bridgeSnapshot = null;
			_bridgeNotBefore = 0f;
			_bridgeDeadline = 0f;
		}

		private static void ClearPlayerResyncs()
		{
			PlayerResyncQueue.Clear();
			_activePlayerResync = null;
			_playerResyncSnapshot = null;
			_activePlayerResyncDeadline = 0f;
			_nextPlayerResyncTime = 0f;
			_activePlayerResyncSawNativeSync = false;
			_activePlayerResyncTimedOut = false;
		}

		private static void FinishActivePlayerResync()
		{
			_activePlayerResync = null;
			_activePlayerResyncDeadline = 0f;
			_activePlayerResyncSawNativeSync = false;
			_activePlayerResyncTimedOut = false;
			_nextPlayerResyncTime = Time.realtimeSinceStartup + 0.25f;
		}

		private static bool ValidateBeforeGeneration(CheckpointSnapshot snapshot, pActiveExpedition package, ExpeditionInTierData expedition, out string reason)
		{
			if (snapshot.BuildRevision != CellBuildData.GetRevision())
			{
				reason = $"saved build {snapshot.BuildRevision}, current build {CellBuildData.GetRevision()}";
				return false;
			}
			if (snapshot.HostSeed != package.hostIDSeed)
			{
				reason = "checkpoint belongs to a different host";
				return false;
			}
			if (!LobbyCompatibility.CanRepresent(snapshot, out reason))
			{
				return false;
			}
			int num = default(int);
			int num2 = default(int);
			expedition.GetSeeds(ref num, ref num2);
			try
			{
				expedition.SetSeeds(snapshot.HostSeed, snapshot.SessionSeed);
				ulong num3 = expedition.GetChecksum();
				if (num3 != snapshot.LevelChecksum)
				{
					reason = $"saved level checksum {snapshot.LevelChecksum}, current data produces {num3}";
					return false;
				}
			}
			finally
			{
				expedition.SetSeeds(num, num2);
			}
			reason = string.Empty;
			return true;
		}

		private static void ApplySeeds(ref pActiveExpedition package, CheckpointSnapshot snapshot)
		{
			package.hostIDSeed = snapshot.HostSeed;
			package.sessionSeed = snapshot.SessionSeed;
		}

		private static pActiveExpedition ClonePackage(pActiveExpedition source)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_004d: 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_0066: Expected O, but got Unknown
			return new pActiveExpedition
			{
				PlayerData = source.PlayerData,
				rundownType = source.rundownType,
				rundownKey = source.rundownKey,
				tier = source.tier,
				expeditionIndex = source.expeditionIndex,
				hostIDSeed = source.hostIDSeed,
				sessionSeed = source.sessionSeed,
				sessionGUID = source.sessionGUID
			};
		}

		private static bool ApplyActivePackage(pActiveExpedition package)
		{
			try
			{
				if ((object)_setActiveExpedition == null)
				{
					_setActiveExpedition = (from method in (from method in typeof(RundownManager).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
							where method.Name == "SetActiveExpedition"
							select method).Where(delegate(MethodInfo method)
						{
							ParameterInfo[] parameters2 = method.GetParameters();
							int num = parameters2.Length;
							bool flag = (uint)(num - 2) <= 1u;
							return flag && parameters2[0].ParameterType == typeof(pActiveExpedition) && parameters2[1].ParameterType == typeof(ExpeditionInTierData) && (parameters2.Length == 2 || parameters2[2].ParameterType == typeof(bool));
						})
						orderby method.GetParameters().Length descending
						select method).FirstOrDefault() ?? throw new MissingMethodException(typeof(RundownManager).FullName, "SetActiveExpedition(pActiveExpedition, ExpeditionInTierData[, bool])");
				}
				object[] parameters = ((_setActiveExpedition.GetParameters().Length != 3) ? new object[2]
				{
					package,
					RundownManager.ActiveExpedition
				} : new object[3]
				{
					package,
					RundownManager.ActiveExpedition,
					true
				});
				_applyingPackage = true;
				_setActiveExpedition.Invoke(RundownManager.Current, parameters);
				return true;
			}
			catch (Exception value)
			{
				Log.Error($"Failed to switch expedition seed package: {value}");
				return false;
			}
			finally
			{
				_applyingPackage = false;
			}
		}

		private static string GetExpeditionKey(pActiveExpedition package)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			return $"{package.rundownKey.data}_{package.tier}_{package.expeditionIndex}";
		}

		private static bool TryGetIdentity(pActiveExpedition package, ExpeditionInTierData expedition, out CheckpointIdentity identity, out string reason)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			identity = default(CheckpointIdentity);
			try
			{
				RundownDataBlock block = default(RundownDataBlock);
				if ((int)package.rundownType == 5)
				{
					string data = package.rundownKey.data;
					if (data == null || !data.StartsWith("Local_", StringComparison.Ordinal) || !uint.TryParse(data.AsSpan(6), NumberStyles.None, CultureInfo.InvariantCulture, out var result))
					{
						throw new InvalidOperationException("local rundown key is invalid");
					}
					block = GameDataBlockBase<RundownDataBlock>.GetBlock(result);
					if (block == null || RundownManager.GetLocalRundownKey(block) != data)
					{
						throw new InvalidOperationException("local rundown data is unavailable");
					}
				}
				else if (!RundownManager.TryGetRundownData(package.rundownType, ref block) || block == null)
				{
					throw new InvalidOperationException("rundown data is unavailable");
				}
				LocalizedText title = block.StorytellingData.Title;
				string rundownName = ((!string.IsNullOrWhiteSpace(title.UntranslatedText)) ? ("text:" + title.UntranslatedText) : ((title.Id != 0) ? ("loc:" + title.Id.ToString(CultureInfo.InvariantCulture)) : ((!string.IsNullOrWhiteSpace(((GameDataBlockBase<RundownDataBlock>)(object)block).name)) ? ("block:" + ((GameDataBlockBase<RundownDataBlock>)(object)block).name) : string.Empty)));
				identity = new CheckpointIdentity(GetExpeditionKey(package), rundownName, expedition.Descriptive.PublicName);
				if (!identity.IsValid)
				{
					throw new InvalidOperationException("rundown or expedition name is empty");
				}
				reason = string.Empty;
				return true;
			}
			catch (Exception ex)
			{
				reason = ex.Message;
				return false;
			}
		}

		private static bool SkipUnsupportedActiveExpedition()
		{
			ExpeditionInTierData activeExpedition = RundownManager.ActiveExpedition;
			if (activeExpedition != null && !activeExpedition.SkipLobby)
			{
				return false;
			}
			ResetSelection();
			return true;
		}

		private static byte[] CopyBytes(Il2CppStructArray<byte> source)
		{
			byte[] array = new byte[((Il2CppArrayBase<byte>)(object)source).Length];
			for (int i = 0; i < ((Il2CppArrayBase<byte>)(object)source).Length; i++)
			{
				array[i] = ((Il2CppArrayBase<byte>)(object)source)[i];
			}
			return array;
		}

		private static void ResetSelection()
		{
			ActiveSnapshot = null;
			PendingRecall = null;
			ResetPendingRecallDelay();
			ActiveExpeditionKey = string.Empty;
			_activeIdentity = null;
			ResumeSelected = false;
			LastSelectionError = null;
			_hasFreshPackage = false;
			_requiredSlotCapacity = 4;
			ClearPlayerResyncs();
			ClearBridgeRecall();
		}

		private static void ResetPendingRecallDelay()
		{
			_pendingRecallDelayStarted = false;
			_pendingRecallNotBefore = 0f;
			_pendingDoorPreparationStarted = false;
			_pendingDoorPreparationNotBefore = 0f;
		}
	}
}
namespace GTFO.PersistentCheckpoint.Patches
{
	[HarmonyPatch(typeof(RundownManager), "SetActiveExpedition")]
	internal static class SetActiveExpeditionPatch
	{
		private static void Prefix(ref pActiveExpedition __0, ExpeditionInTierData __1)
		{
			ResumeCoordinator.OnSelectingExpedition(ref __0, __1);
		}
	}
	[HarmonyPatch(typeof(SNet_Capture), "CaptureGameState")]
	internal static class CaptureCheckpointPatch
	{
		private static void Postfix(eBufferType __0)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			if ((int)__0 == 3)
			{
				ResumeCoordinator.CaptureCheckpoint();
			}
		}
	}
	[HarmonyPatch(typeof(GS_Lobby), "TryStartLevelTrigger")]
	internal static class StartLevelPatch
	{
		private static void Postfix(bool __result)
		{
			ResumeCoordinator.OnStartLevelResult(__result);
		}
	}
	[HarmonyPatch(typeof(GS_ExpeditionSuccess), "Enter")]
	internal static class ExpeditionSuccessPatch
	{
		private static void Prefix()
		{
			ResumeCoordinator.ClearOnSuccess();
		}
	}
	[HarmonyPatch(typeof(SNet_GlobalManager), "OnPlayerEvent")]
	internal static class PlayerSyncEventPatch
	{
		private static void Postfix(SNet_Player __0, SNet_PlayerEvent __1)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			ResumeCoordinator.OnPlayerEvent(__0, __1);
		}
	}
	[HarmonyPatch(typeof(CM_PageLoadout), "Setup")]
	[HarmonyAfter(new string[] { "GTFO.LobbyExpansion" })]
	internal static class LoadoutUiPatch
	{
		private static void Postfix(CM_PageLoadout __instance)
		{
			PersistentCheckpointController.QueueLoadoutUi(__instance);
		}
	}
	[HarmonyPatch(typeof(LG_WeakDoor), "OnSyncDoorStateChange")]
	internal static class DestroyedDoorRecallPatch
	{
		private static void Postfix(LG_WeakDoor __instance, pDoorState __0, bool __1)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (!SNet.IsMaster || !__1 || (int)__0.status != 11)
			{
				return;
			}
			try
			{
				if ((Object)(object)__instance.Gate != (Object)null)
				{
					__instance.Gate.HasBeenOpenedDuringPlay = true;
				}
				Component animComp = __instance.m_animComp;
				LG_WeakDoor_Anim val = ((animComp != null) ? ((Il2CppObjectBase)animComp).TryCast<LG_WeakDoor_Anim>() : null);
				if ((Object)(object)val != (Object)null)
				{
					val.OnDoorState(__0, false);
				}
				if ((Object)(object)__instance.Gate != (Object)null)
				{
					__instance.Gate.IsTraversable = true;
				}
			}
			catch (Exception value)
			{
				Log.Error($"Failed to repair recalled destroyed-door navigation for {((Object)__instance).name}: {value}");
			}
			try
			{
				Component destructionComp = __instance.m_destructionComp;
				LG_WeakDoor_Destruction val2 = ((destructionComp != null) ? ((Il2CppObjectBase)destructionComp).TryCast<LG_WeakDoor_Destruction>() : null);
				if ((Object)(object)val2 == (Object)null || (Object)(object)val2.m_destructionController == (Object)null)
				{
					Log.Warning("Cannot repair destroyed-door geometry for " + ((Object)__instance).name + ": unsupported destruction component.");
					return;
				}
				val2.EnableSkinnedDoor();
				val2.m_destructionController.SnapToExploded();
				val2.m_isDestroyed = true;
				LG_DoorBladeCuller doorBladeCuller = __instance.m_doorBladeCuller;
				if ((Object)(object)doorBladeCuller != (Object)null)
				{
					doorBladeCuller.SwitchToDestroyedRenderLists();
					doorBladeCuller.ForceUpdate();
				}
				Log.Info($"Repaired recalled destroyed-door geometry for {((Object)__instance).name} on host; traversable: {(Object)(object)__instance.Gate != (Object)null && __instance.Gate.IsTraversable}.");
			}
			catch (Exception value2)
			{
				Log.Error($"Failed to repair recalled destroyed-door geometry for {((Object)__instance).name}: {value2}");
			}
		}
	}
}
namespace GTFO.PersistentCheckpoint.Core
{
	internal static class Log
	{
		private static ManualLogSource? _source;

		internal static void Setup(ManualLogSource source)
		{
			_source = source;
		}

		internal static void Debug(string message)
		{
			ManualLogSource? source = _source;
			if (source != null)
			{
				source.LogDebug((object)message);
			}
		}

		internal static void Info(string message)
		{
			ManualLogSource? source = _source;
			if (source != null)
			{
				source.LogInfo((object)message);
			}
		}

		internal static void Warning(string message)
		{
			ManualLogSource? source = _source;
			if (source != null)
			{
				source.LogWarning((object)message);
			}
		}

		internal static void Error(string message)
		{
			ManualLogSource? source = _source;
			if (source != null)
			{
				source.LogError((object)message);
			}
		}

		internal static void Error(Exception exception)
		{
			ManualLogSource? source = _source;
			if (source != null)
			{
				source.LogError((object)exception);
			}
		}
	}
	internal sealed class PersistentCheckpointController : MonoBehaviour
	{
		private static PersistentCheckpointController? _instance;

		private CM_PageLoadout? _queuedPage;

		private int _uiDelayFrames;

		private CM_TimedButton? _resumeButton;

		private CM_TimedButton? _createButton;

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

		private void Awake()
		{
			if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			_instance = this;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
		}

		private void Update()
		{
			UpdateQueuedUi();
			ResumeCoordinator.ObserveCurrentSlots();
			UpdateCheckpointButtons();
			ResumeCoordinator.TryRecallPending();
			ResumeCoordinator.UpdatePlayerResync();
			ResumeCoordinator.UpdateBridgeRecall();
		}

		internal static void QueueLoadoutUi(CM_PageLoadout page)
		{
			if (!((Object)(object)_instance == (Object)null))
			{
				_instance._queuedPage = page;
				_instance._uiDelayFrames = 2;
			}
		}

		private void UpdateQueuedUi()
		{
			if ((Object)(object)_queuedPage == (Object)null || --_uiDelayFrames > 0)
			{
				return;
			}
			CM_PageLoadout queuedPage = _queuedPage;
			_queuedPage = null;
			try
			{
				_resumeButton = GetOrCreateButton(queuedPage, "PersistentCheckpointResumeButton", (eTimedButtonClickType)0, delegate
				{
					ResumeCoordinator.ToggleResume();
				});
				_createButton = GetOrCreateButton(queuedPage, "PersistentCheckpointCreateButton", (eTimedButtonClickType)3, delegate
				{
					ResumeCoordinator.CreateManualCheckpoint();
				});
				((CM_Item)_createButton).SetText("CREATE CHECKPOINT");
				UpdateCheckpointButtons();
			}
			catch (Exception value)
			{
				Log.Error($"Failed to create checkpoint UI: {value}");
			}
		}

		private static CM_TimedButton GetOrCreateButton(CM_PageLoadout page, string name, eTimedButtonClickType clickType, Action<int> onPress)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			CM_TimedButton val = ((IEnumerable<CM_TimedButton>)((Component)page).GetComponentsInChildren<CM_TimedButton>(true)).FirstOrDefault((Func<CM_TimedButton, bool>)((CM_TimedButton item) => ((Object)((Component)item).gameObject).name == name));
			if ((Object)(object)val != (Object)null)
			{
				((CM_Item)val).OnBtnPressCallback = Action<int>.op_Implicit(onPress);
				return val;
			}
			GameObject obj = Object.Instantiate<GameObject>(page.m_readyButtonPrefab, page.m_readyButtonAlign);
			((Object)obj).name = name;
			obj.transform.localPosition = new Vector3(430f, 100f, 0f);
			obj.transform.localScale = new Vector3(0.75f, 0.75f, 1f);
			CM_TimedButton component = obj.GetComponent<CM_TimedButton>();
			((CM_Item)component).TextMeshRoot = ((Component)page).transform;
			component.m_clickType = clickType;
			((RectTransformComp)component).SetSize(new Vector2(520f, 80f));
			((RectTransformComp)component).Setup();
			((CM_Item)component).SetupCMItem();
			ShiftButtonTextRight(component, 40f);
			((CM_Item)component).OnBtnPressCallback = Action<int>.op_Implicit(onPress);
			((RectTransformComp)component).SetVisible(false);
			return component;
		}

		private static void ShiftButtonTextRight(CM_TimedButton button, float offset)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			foreach (TextMeshPro componentsInChild in ((Component)button).GetComponentsInChildren<TextMeshPro>(true))
			{
				Transform transform = componentsInChild.transform;
				transform.localPosition += new Vector3(offset, 0f, 0f);
			}
		}

		private void UpdateCheckpointButtons()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			if ((Object)(object)_resumeButton != (Object)null)
			{
				bool flag = SNet.IsMaster && (int)GameStateManager.CurrentStateName == 5 && ResumeCoordinator.ShouldShowControl;
				if (((RectTransformComp)_resumeButton).IsVisible != flag)
				{
					((RectTransformComp)_resumeButton).SetVisible(flag);
				}
				if (flag)
				{
					((CM_Item)_resumeButton).SetButtonEnabled(ResumeCoordinator.HasResumeChoice);
					string buttonText = ResumeCoordinator.GetButtonText();
					if (((TMP_Text)_resumeButton.m_btnText).text != buttonText)
					{
						((CM_Item)_resumeButton).SetText(buttonText);
					}
				}
			}
			if ((Object)(object)_createButton != (Object)null)
			{
				bool shouldShowCreateControl = ResumeCoordinator.ShouldShowCreateControl;
				if (((RectTransformComp)_createButton).IsVisible != shouldShowCreateControl)
				{
					((RectTransformComp)_createButton).SetVisible(shouldShowCreateControl);
				}
				if (shouldShowCreateControl)
				{
					((CM_Item)_createButton).SetButtonEnabled(ResumeCoordinator.CanCreateManualCheckpoint);
				}
			}
		}

		private void OnApplicationQuit()
		{
			CheckpointStore.Flush(TimeSpan.FromSeconds(3.0));
		}
	}
	[BepInPlugin("com.Roughy.GTFO.PersistentCheckpoint", "GTFO Persistent Checkpoint", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class PersistentCheckpointPlugin : BasePlugin
	{
		public const string Guid = "com.Roughy.GTFO.PersistentCheckpoint";

		public const string Name = "GTFO Persistent Checkpoint";

		public const string Version = "1.0.0";

		private Harmony? _harmony;

		public override void Load()
		{
			//IL_001a: 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)
			//IL_0025: Expected O, but got Unknown
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			Log.Setup(((BasePlugin)this).Log);
			CheckpointStore.Initialize();
			ClassInjector.RegisterTypeInIl2Cpp<PersistentCheckpointController>();
			GameObject val = new GameObject("GTFO_PersistentCheckpoint");
			Object.DontDestroyOnLoad((Object)val);
			((Object)val).hideFlags = (HideFlags)61;
			val.AddComponent<PersistentCheckpointController>();
			_harmony = new Harmony("com.Roughy.GTFO.PersistentCheckpoint");
			_harmony.PatchAll();
			Log.Info($"Loaded {"GTFO Persistent Checkpoint"} {"1.0.0"}. LobbyExpansion: {LobbyCompatibility.Description}");
		}

		public override bool Unload()
		{
			CheckpointStore.Flush(TimeSpan.FromSeconds(3.0));
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			return true;
		}
	}
}