Decompiled source of RejoinStateRecovery v1.0.0

Plugins/YenRex/RejoinStateRecovery.dll

Decompiled 7 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using PurrNet;
using PurrNet.Modules;
using PurrNet.Packing;
using PurrNet.Transports;
using PurrNet.Utils;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("RejoinStateRecovery")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.9.0")]
[assembly: AssemblyInformationalVersion("1.0.9")]
[assembly: AssemblyProduct("RejoinStateRecovery")]
[assembly: AssemblyTitle("RejoinStateRecovery")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.9.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 RejoinStateRecovery
{
	[BepInPlugin("io.damon.ontogether.rejoinstaterecovery", "RejoinStateRecovery", "1.0.9")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private enum RejoinMessageType : byte
		{
			SnapshotRequest = 1,
			SnapshotData,
			SnapshotApplyAck
		}

		[Serializable]
		private sealed class NetworkPayloadV1
		{
			public byte[] Data = Array.Empty<byte>();
		}

		[Serializable]
		private sealed class SnapshotRequestV1
		{
			public string Reason = string.Empty;

			public string RequestedAtUtc = string.Empty;

			public ulong ClientPlayerId;

			public string ClientSteamId = string.Empty;
		}

		[Serializable]
		private sealed class SnapshotApplyAckV1
		{
			public long SnapshotVersion;

			public string AppliedAtUtc = string.Empty;

			public string Status = string.Empty;
		}

		[Serializable]
		private sealed class RejoinSnapshotV1
		{
			public long SnapshotVersion;

			public ulong PlayerId;

			public string PlayerSteamId = string.Empty;

			public string PlayerName = string.Empty;

			public SerializableVector3 Position;

			public SerializableQuaternion Rotation;

			public int MovementState;

			public int FocusIndex;

			public int FocusTypeValue = -1;

			public bool IsFocus;

			public int PomodoroMinute;

			public bool PomodoroPaused;

			public int PomodoroType;

			public bool IsMessagePanelOpen;

			public bool IsPomodoroPanelOpen;

			public bool IsTodoPanelOpen;

			public bool WasFishingActive;

			public int FishingStateValue = -1;

			public bool HasFishingHookPosition;

			public SerializableVector3 FishingHookPosition;

			public string SceneName = string.Empty;

			public string SnapshotSentAtUtc = string.Empty;

			public string Reason = string.Empty;

			public long CapturedAtUtcTicks;
		}

		public sealed class ConfigurationManagerAttributes
		{
			public string? DispName { get; set; }

			public int? Order { get; set; }
		}

		[Serializable]
		private struct SerializableVector3
		{
			public float X;

			public float Y;

			public float Z;

			public static implicit operator SerializableVector3(Vector3 value)
			{
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				SerializableVector3 result = default(SerializableVector3);
				result.X = value.x;
				result.Y = value.y;
				result.Z = value.z;
				return result;
			}

			public static implicit operator Vector3(SerializableVector3 value)
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				return new Vector3(value.X, value.Y, value.Z);
			}
		}

		[Serializable]
		private struct SerializableQuaternion
		{
			public float X;

			public float Y;

			public float Z;

			public float W;

			public static implicit operator SerializableQuaternion(Quaternion value)
			{
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				SerializableQuaternion result = default(SerializableQuaternion);
				result.X = value.x;
				result.Y = value.y;
				result.Z = value.z;
				result.W = value.w;
				return result;
			}

			public static implicit operator Quaternion(SerializableQuaternion value)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				return new Quaternion(value.X, value.Y, value.Z, value.W);
			}
		}

		private readonly struct DecodedEnvelope
		{
			public readonly byte Version;

			public readonly byte MessageType;

			public readonly ulong SenderPlayerId;

			public readonly string PayloadJson;

			public DecodedEnvelope(byte version, byte messageType, ulong senderPlayerId, string payloadJson)
			{
				Version = version;
				MessageType = messageType;
				SenderPlayerId = senderPlayerId;
				PayloadJson = payloadJson;
			}
		}

		private static class PacketEnvelopeV1
		{
			private static readonly byte[] Magic = new byte[4] { 79, 84, 81, 49 };

			private const byte Version = 1;

			public static byte[] Encode(byte messageType, ulong senderPlayerId, string payloadJson)
			{
				byte[] bytes = Encoding.UTF8.GetBytes(payloadJson ?? string.Empty);
				using MemoryStream memoryStream = new MemoryStream();
				using BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8);
				binaryWriter.Write(Magic);
				binaryWriter.Write((byte)1);
				binaryWriter.Write(messageType);
				binaryWriter.Write(senderPlayerId);
				binaryWriter.Write(bytes.Length);
				binaryWriter.Write(bytes);
				binaryWriter.Flush();
				return memoryStream.ToArray();
			}

			public static bool TryDecode(byte[]? bytes, out DecodedEnvelope envelope)
			{
				envelope = default(DecodedEnvelope);
				if (bytes == null || bytes.Length < 18)
				{
					return false;
				}
				try
				{
					using MemoryStream input = new MemoryStream(bytes);
					using BinaryReader binaryReader = new BinaryReader(input, Encoding.UTF8);
					byte[] array = binaryReader.ReadBytes(4);
					if (array.Length != 4 || !MatchesMagic(array))
					{
						return false;
					}
					byte b = binaryReader.ReadByte();
					if (b != 1)
					{
						return false;
					}
					byte messageType = binaryReader.ReadByte();
					ulong senderPlayerId = binaryReader.ReadUInt64();
					int num = binaryReader.ReadInt32();
					if (num < 0 || num > bytes.Length)
					{
						return false;
					}
					byte[] bytes2 = binaryReader.ReadBytes(num);
					string @string = Encoding.UTF8.GetString(bytes2);
					envelope = new DecodedEnvelope(b, messageType, senderPlayerId, @string);
					return true;
				}
				catch
				{
					return false;
				}
			}

			private static bool MatchesMagic(byte[] value)
			{
				if (value[0] == Magic[0] && value[1] == Magic[1] && value[2] == Magic[2])
				{
					return value[3] == Magic[3];
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(NetworkManager), "RegisterModules")]
		private static class NetworkManagerRegisterModulesPatch
		{
			private static void Prefix()
			{
				RegisterNetworkPayloadContracts();
			}
		}

		private const string PluginGuid = "io.damon.ontogether.rejoinstaterecovery";

		private const string PluginName = "RejoinStateRecovery";

		private const string PluginVersion = "1.0.9";

		private readonly Harmony _harmony = new Harmony("io.damon.ontogether.rejoinstaterecovery");

		private readonly Dictionary<ulong, RejoinSnapshotV1> _snapshots = new Dictionary<ulong, RejoinSnapshotV1>();

		private readonly Dictionary<string, RejoinSnapshotV1> _snapshotsBySteamId = new Dictionary<string, RejoinSnapshotV1>(StringComparer.Ordinal);

		private readonly Dictionary<ulong, long> _lastAckByPlayer = new Dictionary<ulong, long>();

		private ConfigEntry<float> _snapshotIntervalSeconds;

		private ConfigEntry<bool> _sendOnReconnectOnly;

		private ConfigEntry<bool> _applyUiState;

		private ConfigEntry<bool> _showNotifications;

		private ConfigEntry<bool> _enableLocalFallback;

		private ConfigEntry<float> _localFallbackDelaySeconds;

		private ConfigEntry<float> _localFallbackMaxAgeMinutes;

		private ConfigEntry<float> _localCaptureIntervalSeconds;

		private ConfigEntry<bool> _persistLocalSnapshot;

		private ConfigEntry<bool> _safePositionRestore;

		private ConfigEntry<float> _maxRestoreDistanceMeters;

		private ConfigEntry<float> _groundSnapCastHeight;

		private ConfigEntry<float> _groundSnapCastDistance;

		private ConfigEntry<float> _groundSnapOffsetY;

		private ConfigEntry<float> _groundSnapMaxUpwardAdjust;

		private ConfigEntry<float> _focusRestoreDelaySeconds;

		private ConfigEntry<bool> _resumeFishingOnRestore;

		private NetworkManager? _network;

		private Coroutine? _bootstrapCoroutine;

		private Coroutine? _snapshotCoroutine;

		private Coroutine? _snapshotRequestRetryCoroutine;

		private Coroutine? _applySnapshotCoroutine;

		private Coroutine? _localSnapshotCoroutine;

		private Coroutine? _localFallbackCoroutine;

		private Coroutine? _focusRestoreCoroutine;

		private Coroutine? _fishingResumeCoroutine;

		private bool _snapshotAppliedThisSession;

		private long _lastAppliedSnapshotVersion;

		private long _snapshotVersion;

		private FieldInfo? _focusIndexField;

		private FieldInfo? _focusSimpledTypesField;

		private FieldInfo? _focusTypeField;

		private FieldInfo? _characterControllerField;

		private FieldInfo? _fishingCastIndicatorField;

		private FieldInfo? _fishingCollisionLayersField;

		private MethodInfo? _fishingCastFishingRodMethod;

		private RejoinSnapshotV1? _lastLocalSnapshot;

		private RejoinSnapshotV1? _lastTerminalLocalSnapshot;

		private RejoinSnapshotV1? _preAttachLocalSnapshot;

		private string _localSnapshotStatePath = string.Empty;

		public static ManualLogSource Log;

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			_snapshotIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SnapshotIntervalSeconds", 5f, DescribeSetting("How frequently the host samples and stores per-player snapshot data.", "Snapshot Interval (sec)", 120, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 30f)));
			_sendOnReconnectOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SendOnReconnectOnly", true, DescribeSetting("If true, targeted restore sends only when the player is marked as reconnecting.", "Send On Reconnect Only", 110));
			_applyUiState = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ApplyUiState", true, DescribeSetting("If true, restores key local UI panel states on snapshot apply.", "Apply UI State", 100));
			_showNotifications = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowNotifications", true, DescribeSetting("If true, shows brief in-game notifications when snapshot restore runs.", "Show Notifications", 90));
			_enableLocalFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("Fallback", "EnableLocalFallback", true, DescribeSetting("If true, applies last local pre-disconnect snapshot when host snapshot is unavailable.", "Enable Local Fallback", 120));
			_localFallbackDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Fallback", "LocalFallbackDelaySeconds", 6f, DescribeSetting("Delay before local fallback restore is attempted after connect.", "Fallback Delay (sec)", 110, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 20f)));
			_localFallbackMaxAgeMinutes = ((BaseUnityPlugin)this).Config.Bind<float>("Fallback", "LocalFallbackMaxAgeMinutes", 20f, DescribeSetting("Maximum age for local fallback snapshot.", "Fallback Max Age (min)", 100, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(1f, 120f)));
			_localCaptureIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Fallback", "LocalCaptureIntervalSeconds", 1f, DescribeSetting("How often local fallback snapshots are captured while connected.", "Capture Interval (sec)", 90, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0.25f, 10f)));
			_persistLocalSnapshot = ((BaseUnityPlugin)this).Config.Bind<bool>("Fallback", "PersistLocalSnapshot", true, DescribeSetting("If true, stores the latest local snapshot on disk for cross-session fallback.", "Persist Local Snapshot", 80));
			_safePositionRestore = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "SafePositionRestore", true, DescribeSetting("If true, validates and ground-snaps restored positions.", "Safe Position Restore", 140));
			_maxRestoreDistanceMeters = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "MaxRestoreDistanceMeters", 150f, DescribeSetting("Maximum horizontal distance allowed from current position when restoring.", "Max Restore Distance (m)", 130, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(5f, 1000f)));
			_groundSnapCastHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "GroundSnapCastHeight", 20f, DescribeSetting("Raycast height above desired restore position for ground snap.", "Ground Snap Cast Height", 120, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(2f, 80f)));
			_groundSnapCastDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "GroundSnapCastDistance", 80f, DescribeSetting("Maximum downward raycast distance used by ground snap.", "Ground Snap Cast Distance", 110, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(5f, 200f)));
			_groundSnapOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "GroundSnapOffsetY", 0.15f, DescribeSetting("Vertical offset added above the chosen support hit when restoring.", "Ground Snap Offset Y", 100, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 1f)));
			_groundSnapMaxUpwardAdjust = ((BaseUnityPlugin)this).Config.Bind<float>("Safety", "GroundSnapMaxUpwardAdjust", 0.35f, DescribeSetting("Maximum upward snap (meters) allowed above the saved Y before a lower nearby support is preferred.", "Ground Snap Max Upward (m)", 90, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 3f)));
			_focusRestoreDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Focus", "FocusRestoreDelaySeconds", 2.5f, DescribeSetting("Delay between snapshot apply (position restore) and focus restore attempts.", "Focus Restore Delay (sec)", 120, (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 15f)));
			_resumeFishingOnRestore = ((BaseUnityPlugin)this).Config.Bind<bool>("Fishing", "ResumeFishingOnRestore", true, DescribeSetting("If true, attempts to reopen rod and cast to saved hook position after snapshot apply.", "Resume Fishing On Restore", 120));
			_localSnapshotStatePath = Path.Combine(Paths.ConfigPath, "io.damon.ontogether.rejoinstaterecovery", "local_snapshot.json");
			LoadPersistedLocalSnapshot();
			_harmony.PatchAll();
			SceneManager.sceneLoaded += OnSceneLoaded;
			_bootstrapCoroutine = ((MonoBehaviour)this).StartCoroutine(NetworkBootstrapLoop());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"RejoinStateRecovery loaded (io.damon.ontogether.rejoinstaterecovery)");
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			if (_bootstrapCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_bootstrapCoroutine);
				_bootstrapCoroutine = null;
			}
			if (_snapshotCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_snapshotCoroutine);
				_snapshotCoroutine = null;
			}
			if (_snapshotRequestRetryCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_snapshotRequestRetryCoroutine);
				_snapshotRequestRetryCoroutine = null;
			}
			if (_applySnapshotCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_applySnapshotCoroutine);
				_applySnapshotCoroutine = null;
			}
			if (_localSnapshotCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_localSnapshotCoroutine);
				_localSnapshotCoroutine = null;
			}
			if (_localFallbackCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_localFallbackCoroutine);
				_localFallbackCoroutine = null;
			}
			if (_focusRestoreCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_focusRestoreCoroutine);
				_focusRestoreCoroutine = null;
			}
			if (_fishingResumeCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_fishingResumeCoroutine);
				_fishingResumeCoroutine = null;
			}
			if ((Object)(object)_network != (Object)null)
			{
				CaptureLocalSnapshot("ondestroy-capture", verbose: true);
			}
			UnhookNetwork();
			_harmony.UnpatchSelf();
		}

		private void OnSceneLoaded(Scene _, LoadSceneMode __)
		{
			TryHookNetwork();
		}

		private IEnumerator NetworkBootstrapLoop()
		{
			while (true)
			{
				TryHookNetwork();
				yield return (object)new WaitForSeconds(1f);
			}
		}

		private void TryHookNetwork()
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected O, but got Unknown
			NetworkManager main = NetworkManager.main;
			if (!((Object)(object)main == (Object)null) && main != _network)
			{
				if ((Object)(object)_network != (Object)null)
				{
					CaptureLocalSnapshot("network-switch-capture", verbose: true);
				}
				UnhookNetwork();
				_network = main;
				_preAttachLocalSnapshot = CloneSnapshot(_lastTerminalLocalSnapshot ?? _lastLocalSnapshot);
				RegisterNetworkPayloadContracts();
				_network.Subscribe<NetworkPayloadV1>((PlayerBroadcastDelegate<NetworkPayloadV1>)OnNetworkPayload, true);
				_network.Subscribe<NetworkPayloadV1>((PlayerBroadcastDelegate<NetworkPayloadV1>)OnNetworkPayload, false);
				_network.onPlayerJoined += new OnPlayerJoinedEvent(OnPlayerJoined);
				_network.onClientConnectionState += OnClientConnectionState;
				_network.onNetworkShutdown += new OnSubscribeDelegate(OnNetworkShutdown);
				if (_snapshotCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_snapshotCoroutine);
				}
				_snapshotCoroutine = ((MonoBehaviour)this).StartCoroutine(HostSnapshotLoop());
				if (_localSnapshotCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_localSnapshotCoroutine);
				}
				_localSnapshotCoroutine = ((MonoBehaviour)this).StartCoroutine(LocalSnapshotLoop());
				_snapshotAppliedThisSession = false;
				_lastAppliedSnapshotVersion = 0L;
				if (_network.isClient && !_network.isServer)
				{
					BeginSnapshotRequestRetry("network-attached");
					BeginLocalFallback("network-attached");
				}
			}
		}

		private void UnhookNetwork()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			if (!((Object)(object)_network == (Object)null))
			{
				try
				{
					_network.Unsubscribe<NetworkPayloadV1>((PlayerBroadcastDelegate<NetworkPayloadV1>)OnNetworkPayload, true);
					_network.Unsubscribe<NetworkPayloadV1>((PlayerBroadcastDelegate<NetworkPayloadV1>)OnNetworkPayload, false);
					_network.onPlayerJoined -= new OnPlayerJoinedEvent(OnPlayerJoined);
					_network.onClientConnectionState -= OnClientConnectionState;
					_network.onNetworkShutdown -= new OnSubscribeDelegate(OnNetworkShutdown);
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Network unhook warning: " + ex.Message));
				}
				if (_snapshotRequestRetryCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_snapshotRequestRetryCoroutine);
					_snapshotRequestRetryCoroutine = null;
				}
				if (_applySnapshotCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_applySnapshotCoroutine);
					_applySnapshotCoroutine = null;
				}
				if (_localSnapshotCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_localSnapshotCoroutine);
					_localSnapshotCoroutine = null;
				}
				if (_localFallbackCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_localFallbackCoroutine);
					_localFallbackCoroutine = null;
				}
				if (_focusRestoreCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_focusRestoreCoroutine);
					_focusRestoreCoroutine = null;
				}
				if (_fishingResumeCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_fishingResumeCoroutine);
					_fishingResumeCoroutine = null;
				}
				_snapshotAppliedThisSession = false;
				_lastAppliedSnapshotVersion = 0L;
				_preAttachLocalSnapshot = null;
				_network = null;
			}
		}

		private static void RegisterNetworkPayloadContracts()
		{
			Type typeFromHandle = typeof(NetworkPayloadV1);
			if (Hasher.IsRegistered(typeFromHandle))
			{
				RegisterNetworkPayloadPacker();
				return;
			}
			Hasher.PrepareType(typeFromHandle);
			RegisterNetworkPayloadPacker();
		}

		[RegisterPackers(-10)]
		private static void RegisterNetworkPayloadContractsForPurrNet()
		{
			NetworkRegister.Hash(typeof(NetworkPayloadV1).TypeHandle);
			RegisterNetworkPayloadPacker();
		}

		private static void RegisterNetworkPayloadPacker()
		{
			Packer<NetworkPayloadV1>.RegisterWriter((WriteFunc<NetworkPayloadV1>)WriteNetworkPayload);
			Packer<NetworkPayloadV1>.RegisterReader((ReadFunc<NetworkPayloadV1>)ReadNetworkPayload);
		}

		private static void WriteNetworkPayload(BitPacker packer, NetworkPayloadV1 payload)
		{
			byte[] array = payload?.Data ?? Array.Empty<byte>();
			Packer<byte[]>.Write(packer, array);
		}

		private static void ReadNetworkPayload(BitPacker packer, ref NetworkPayloadV1 payload)
		{
			if (payload == null)
			{
				payload = new NetworkPayloadV1();
			}
			byte[] data = payload.Data;
			Packer<byte[]>.Read(packer, ref data);
			payload.Data = data ?? Array.Empty<byte>();
		}

		private void OnNetworkShutdown(NetworkManager manager, bool _)
		{
			if (manager == _network)
			{
				CaptureLocalSnapshot("shutdown-capture", verbose: true);
				_snapshotAppliedThisSession = false;
				_lastAppliedSnapshotVersion = 0L;
				UnhookNetwork();
			}
		}

		private void OnClientConnectionState(ConnectionState state)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Invalid comparison between Unknown and I4
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Invalid comparison between Unknown and I4
			if (!((Object)(object)_network == (Object)null))
			{
				if (_network.isClient && !_network.isServer && (int)state == 1)
				{
					BeginSnapshotRequestRetry("client-connected");
					BeginLocalFallback("client-connected");
				}
				else if (_network.isClient && ((int)state == 3 || (int)state == 2))
				{
					CaptureLocalSnapshot("disconnect-capture", verbose: true);
				}
			}
		}

		private void BeginSnapshotRequestRetry(string reason)
		{
			if (_snapshotRequestRetryCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_snapshotRequestRetryCoroutine);
			}
			_snapshotRequestRetryCoroutine = ((MonoBehaviour)this).StartCoroutine(SnapshotRequestRetryLoop(reason));
		}

		private IEnumerator SnapshotRequestRetryLoop(string reason)
		{
			for (int attempt = 1; attempt <= 20; attempt++)
			{
				if ((Object)(object)_network == (Object)null || !_network.isClient || _network.isServer || _snapshotAppliedThisSession)
				{
					yield break;
				}
				SendSnapshotRequest($"{reason}-attempt-{attempt}");
				yield return (object)new WaitForSeconds(2f);
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Snapshot request retries exhausted (reason='" + reason + "')"));
		}

		private void BeginLocalFallback(string reason)
		{
			if (_enableLocalFallback.Value)
			{
				if (_localFallbackCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_localFallbackCoroutine);
				}
				_localFallbackCoroutine = ((MonoBehaviour)this).StartCoroutine(LocalFallbackAfterDelay(reason));
			}
		}

		private IEnumerator LocalFallbackAfterDelay(string reason)
		{
			yield return (object)new WaitForSeconds(Mathf.Max(1f, _localFallbackDelaySeconds.Value));
			if (_snapshotAppliedThisSession || (Object)(object)_network == (Object)null || !_network.isClient || _network.isServer)
			{
				_localFallbackCoroutine = null;
				yield break;
			}
			RejoinSnapshotV1 preAttachLocalSnapshot = _preAttachLocalSnapshot;
			if (preAttachLocalSnapshot == null)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Local fallback skipped: no pre-attach snapshot (reason='" + reason + "')"));
				_localFallbackCoroutine = null;
				yield break;
			}
			if (preAttachLocalSnapshot.CapturedAtUtcTicks > 0)
			{
				DateTime dateTime = new DateTime(preAttachLocalSnapshot.CapturedAtUtcTicks, DateTimeKind.Utc);
				if ((DateTime.UtcNow - dateTime).TotalMinutes > (double)Math.Max(1f, _localFallbackMaxAgeMinutes.Value))
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Local fallback skipped: snapshot too old (reason='" + reason + "')"));
					_localFallbackCoroutine = null;
					yield break;
				}
			}
			Vector3 val = preAttachLocalSnapshot.Position;
			if (IsLikelyInvalidLocalCapture(val, preAttachLocalSnapshot.Reason))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Local fallback skipped: snapshot position invalid {val.x:0.00},{val.y:0.00},{val.z:0.00} (reason='{reason}')");
				_localFallbackCoroutine = null;
				yield break;
			}
			string reason2 = preAttachLocalSnapshot.Reason;
			preAttachLocalSnapshot.Reason = "local-fallback:" + reason;
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Applying local fallback snapshot v{preAttachLocalSnapshot.SnapshotVersion} from {preAttachLocalSnapshot.Position.X:0.00},{preAttachLocalSnapshot.Position.Y:0.00},{preAttachLocalSnapshot.Position.Z:0.00} (capturedReason='{reason2}')");
			if (_applySnapshotCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_applySnapshotCoroutine);
			}
			_applySnapshotCoroutine = ((MonoBehaviour)this).StartCoroutine(ApplySnapshotWhenReady(preAttachLocalSnapshot));
			_localFallbackCoroutine = null;
		}

		private IEnumerator LocalSnapshotLoop()
		{
			while (true)
			{
				if ((Object)(object)_network != (Object)null && _network.isClient)
				{
					CaptureLocalSnapshot();
				}
				float num = Mathf.Max(0.25f, _localCaptureIntervalSeconds.Value);
				yield return (object)new WaitForSeconds(num);
			}
		}

		private void OnPlayerJoined(PlayerID player, bool isReconnect, bool asServer)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (asServer && IsHostActive() && (!_sendOnReconnectOnly.Value || isReconnect))
			{
				if (!isReconnect)
				{
					CaptureSnapshots();
					SendSnapshotToPlayer(player, "player-joined");
				}
				else
				{
					SendSnapshotToPlayer(player, "player-rejoined");
				}
			}
		}

		private IEnumerator HostSnapshotLoop()
		{
			while (true)
			{
				if (IsHostActive())
				{
					CaptureSnapshots();
				}
				float num = Mathf.Max(1f, _snapshotIntervalSeconds.Value);
				yield return (object)new WaitForSeconds(num);
			}
		}

		private bool IsHostActive()
		{
			if ((Object)(object)_network != (Object)null)
			{
				if (!_network.isHost)
				{
					return _network.isServer;
				}
				return true;
			}
			return false;
		}

		private ulong LocalPlayerIdOrZero()
		{
			//IL_000e: 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_0016: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				NetworkManager? network = _network;
				long result;
				if (network == null)
				{
					result = 0L;
				}
				else
				{
					PlayerID localPlayer = network.localPlayer;
					result = (long)((PlayerID)(ref localPlayer)).id.value;
				}
				return (ulong)result;
			}
			catch
			{
				return 0uL;
			}
		}

		private void CaptureSnapshots()
		{
			//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)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Expected I4, but got O
			//IL_0169: Expected O, but got I4
			//IL_0171: Expected O, but got I4
			//IL_018b: Expected O, but got I4
			//IL_01a3: Expected O, but got I4
			//IL_01bb: Expected O, but got I4
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Expected O, but got I4
			//IL_01eb: Expected O, but got I4
			//IL_0203: Expected O, but got I4
			//IL_021b: Expected O, but got I4
			//IL_0223: Expected O, but got I4
			//IL_022b: Expected O, but got I4
			//IL_0233: Expected O, but got I4
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Expected O, but got I4
			//IL_0248: Expected O, but got I4
			//IL_0250: Expected O, but got I4
			//IL_0251: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Expected O, but got I4
			//IL_0278: Expected O, but got I4
			//IL_027a: Expected O, but got I4
			//IL_015c->IL015c: Incompatible stack types: O vs I4
			//IL_0155->IL015c: Incompatible stack types: I4 vs O
			//IL_0155->IL015c: Incompatible stack types: O vs I4
			PlayerPanelController i = NetworkSingleton<PlayerPanelController>.I;
			if ((Object)(object)i == (Object)null || i.PlayerIDs == null || i.PlayerTransforms == null)
			{
				return;
			}
			PomodoroController i2 = MonoSingleton<PomodoroController>.I;
			UIManager i3 = MonoSingleton<UIManager>.I;
			int num = Math.Min(i.PlayerIDs.Count, i.PlayerTransforms.Count);
			for (int j = 0; j < num; j++)
			{
				NetworkTransform val = i.PlayerTransforms[j];
				if (!((Object)(object)val == (Object)null))
				{
					PlayerID val2 = i.PlayerIDs[j];
					ulong value = ((PlayerID)(ref val2)).id.value;
					string playerSteamId = GetPlayerSteamId(i, j);
					string playerName = GetPlayerName(i, j);
					PlayerMovementController val3 = ((Component)val).GetComponent<PlayerMovementController>() ?? ((Component)val).GetComponentInChildren<PlayerMovementController>();
					PlayerFocusController val4 = ((Component)val).GetComponent<PlayerFocusController>() ?? ((Component)val).GetComponentInChildren<PlayerFocusController>();
					CaptureFishingSnapshotForPlayer((Component)(object)val, value, out var wasFishingActive, out var fishingStateValue, out var hasFishingHookPosition, out var fishingHookPosition);
					int focusIndex = -1;
					int focusTypeValue = -1;
					if ((Object)(object)val4 != (Object)null)
					{
						focusIndex = GetFocusIndex(val4);
						focusTypeValue = GetFocusTypeValue(val4);
					}
					object obj = new RejoinSnapshotV1
					{
						SnapshotVersion = ++_snapshotVersion,
						PlayerId = value,
						Position = val.position,
						Rotation = val.rotation
					};
					object obj2 = obj;
					int num2;
					if ((Object)(object)val3 != (Object)null)
					{
						obj = val3.MovementState;
						num2 = (int)obj;
					}
					else
					{
						num2 = -1;
						obj = num2;
						num2 = (int)obj;
					}
					((RejoinSnapshotV1)obj2).MovementState = (int)obj;
					((RejoinSnapshotV1)num2).FocusIndex = focusIndex;
					((RejoinSnapshotV1)num2).FocusTypeValue = focusTypeValue;
					((RejoinSnapshotV1)num2).IsFocus = (Object)(object)val4 != (Object)null && val4.IsFocus;
					((RejoinSnapshotV1)num2).PomodoroMinute = (((Object)(object)i2 != (Object)null) ? i2.CurrentMinute : 0);
					((RejoinSnapshotV1)num2).PomodoroPaused = (Object)(object)i2 == (Object)null || i2.IsPaused;
					num2 = (((RejoinSnapshotV1)num2).PomodoroType = (((Object)(object)i2 != (Object)null) ? ((int)i2.PomodoroType) : 0));
					((RejoinSnapshotV1)num2).IsMessagePanelOpen = (Object)(object)i3 != (Object)null && i3.IsMessagePanelActivated;
					((RejoinSnapshotV1)num2).IsPomodoroPanelOpen = (Object)(object)i3 != (Object)null && i3.IsPomodoroOpened;
					((RejoinSnapshotV1)num2).IsTodoPanelOpen = (Object)(object)i3 != (Object)null && i3.IsTodoOpened;
					((RejoinSnapshotV1)num2).WasFishingActive = wasFishingActive;
					((RejoinSnapshotV1)num2).FishingStateValue = fishingStateValue;
					((RejoinSnapshotV1)num2).HasFishingHookPosition = hasFishingHookPosition;
					((RejoinSnapshotV1)num2).FishingHookPosition = fishingHookPosition;
					((RejoinSnapshotV1)num2).PlayerSteamId = playerSteamId;
					((RejoinSnapshotV1)num2).PlayerName = playerName;
					int num3 = num2;
					Scene activeScene = SceneManager.GetActiveScene();
					((RejoinSnapshotV1)num3).SceneName = ((Scene)(ref activeScene)).name;
					((RejoinSnapshotV1)num2).CapturedAtUtcTicks = DateTime.UtcNow.Ticks;
					RejoinSnapshotV1 value2 = (RejoinSnapshotV1)num2;
					_snapshots[value] = value2;
					if (!string.IsNullOrWhiteSpace(playerSteamId))
					{
						_snapshotsBySteamId[playerSteamId] = value2;
					}
				}
			}
		}

		private void CaptureLocalSnapshot(string reason = "local-capture", bool verbose = false)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: 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)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Expected I4, but got O
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Expected O, but got I4
			//IL_014f->IL014f: Incompatible stack types: O vs I4
			//IL_0149->IL014f: Incompatible stack types: I4 vs O
			//IL_0149->IL014f: Incompatible stack types: O vs I4
			TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
			if ((Object)(object)i == (Object)null)
			{
				return;
			}
			PlayerMovementController mainMovementController = i.MainMovementController;
			PlayerFocusController mainFocusController = i.MainFocusController;
			UIManager i2 = MonoSingleton<UIManager>.I;
			PomodoroController i3 = MonoSingleton<PomodoroController>.I;
			ulong playerId = LocalPlayerIdOrZero();
			string playerSteamId = ResolveLocalSteamId();
			CaptureLocalFishingSnapshot(i, out var wasFishingActive, out var fishingStateValue, out var hasFishingHookPosition, out var fishingHookPosition);
			int focusIndex = -1;
			int focusTypeValue = -1;
			if ((Object)(object)mainFocusController != (Object)null)
			{
				focusIndex = GetFocusIndex(mainFocusController);
				focusTypeValue = GetFocusTypeValue(mainFocusController);
			}
			if (!TryResolveLocalPose(i, out Vector3 position, out Quaternion rotation, out string source))
			{
				if (verbose)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Skipped local snapshot capture (" + reason + ") because no local pose source was available."));
				}
				return;
			}
			if (IsLikelyInvalidLocalCapture(position, reason))
			{
				if (verbose)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Skipped local snapshot capture ({reason}) due to invalid position {position.x:0.00},{position.y:0.00},{position.z:0.00}");
				}
				return;
			}
			object obj = this;
			object obj2 = new RejoinSnapshotV1
			{
				SnapshotVersion = ++_snapshotVersion,
				PlayerId = playerId,
				PlayerSteamId = playerSteamId,
				Position = position,
				Rotation = rotation
			};
			obj2 = obj2;
			int num;
			if ((Object)(object)mainMovementController != (Object)null)
			{
				obj = mainMovementController.MovementState;
				num = (int)obj;
			}
			else
			{
				num = -1;
				obj = num;
				num = (int)obj;
			}
			((RejoinSnapshotV1)obj2).MovementState = (int)obj;
			((RejoinSnapshotV1)obj2).FocusIndex = focusIndex;
			((RejoinSnapshotV1)obj2).FocusTypeValue = focusTypeValue;
			((RejoinSnapshotV1)obj2).IsFocus = (Object)(object)mainFocusController != (Object)null && mainFocusController.IsFocus;
			((RejoinSnapshotV1)obj2).PomodoroMinute = (((Object)(object)i3 != (Object)null) ? i3.CurrentMinute : 0);
			((RejoinSnapshotV1)obj2).PomodoroPaused = (Object)(object)i3 == (Object)null || i3.IsPaused;
			obj2 = obj2;
			num = (((RejoinSnapshotV1)obj2).PomodoroType = (((Object)(object)i3 != (Object)null) ? ((int)i3.PomodoroType) : 0));
			((RejoinSnapshotV1)obj2).IsMessagePanelOpen = (Object)(object)i2 != (Object)null && i2.IsMessagePanelActivated;
			((RejoinSnapshotV1)obj2).IsPomodoroPanelOpen = (Object)(object)i2 != (Object)null && i2.IsPomodoroOpened;
			((RejoinSnapshotV1)obj2).IsTodoPanelOpen = (Object)(object)i2 != (Object)null && i2.IsTodoOpened;
			((RejoinSnapshotV1)obj2).WasFishingActive = wasFishingActive;
			((RejoinSnapshotV1)obj2).FishingStateValue = fishingStateValue;
			((RejoinSnapshotV1)obj2).HasFishingHookPosition = hasFishingHookPosition;
			((RejoinSnapshotV1)obj2).FishingHookPosition = fishingHookPosition;
			((RejoinSnapshotV1)obj2).Reason = reason;
			((RejoinSnapshotV1)obj2).SnapshotSentAtUtc = DateTime.UtcNow.ToString("O");
			object obj3 = obj2;
			Scene activeScene = SceneManager.GetActiveScene();
			((RejoinSnapshotV1)obj3).SceneName = ((Scene)(ref activeScene)).name;
			((RejoinSnapshotV1)obj2).CapturedAtUtcTicks = DateTime.UtcNow.Ticks;
			((Plugin)num)._lastLocalSnapshot = (RejoinSnapshotV1?)obj2;
			if (IsTerminalCaptureReason(reason))
			{
				_lastTerminalLocalSnapshot = CloneSnapshot(_lastLocalSnapshot);
			}
			PersistLocalSnapshot(_lastLocalSnapshot);
			if (verbose)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Captured local snapshot v{_lastLocalSnapshot.SnapshotVersion} ({reason}, source={source}) at {_lastLocalSnapshot.Position.X:0.00},{_lastLocalSnapshot.Position.Y:0.00},{_lastLocalSnapshot.Position.Z:0.00}");
			}
		}

		private void CaptureFishingSnapshotForPlayer(Component playerTransform, ulong playerId, out bool wasFishingActive, out int fishingStateValue, out bool hasFishingHookPosition, out Vector3 fishingHookPosition)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected I4, but got Unknown
			//IL_005c: 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_00b0: Invalid comparison between Unknown and I4
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			wasFishingActive = false;
			fishingStateValue = -1;
			hasFishingHookPosition = false;
			fishingHookPosition = default(Vector3);
			FishingController val = playerTransform.GetComponent<FishingController>() ?? playerTransform.GetComponentInChildren<FishingController>();
			if ((Object)(object)val != (Object)null)
			{
				wasFishingActive = (Object)(object)val.FishingRod != (Object)null && val.FishingRod.activeSelf;
				Transform fishHook = val.FishHook;
				if ((Object)(object)fishHook != (Object)null && IsFiniteVector3(fishHook.position))
				{
					hasFishingHookPosition = true;
					fishingHookPosition = fishHook.position;
				}
			}
			if (playerId != LocalPlayerIdOrZero())
			{
				return;
			}
			FishingManager i = MonoSingleton<FishingManager>.I;
			if (!((Object)(object)i == (Object)null))
			{
				fishingStateValue = (int)i.FishingState;
				wasFishingActive = wasFishingActive || i.IsActive || (int)i.FishingState > 0;
				FishingController fishingCont = i.FishingCont;
				if ((Object)(object)((fishingCont != null) ? fishingCont.FishHook : null) != (Object)null && IsFiniteVector3(fishingCont.FishHook.position))
				{
					hasFishingHookPosition = true;
					fishingHookPosition = fishingCont.FishHook.position;
				}
			}
		}

		private static void CaptureLocalFishingSnapshot(TextChannelManager channel, out bool wasFishingActive, out int fishingStateValue, out bool hasFishingHookPosition, out Vector3 fishingHookPosition)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Expected I4, but got Unknown
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Invalid comparison between Unknown and I4
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			wasFishingActive = false;
			fishingStateValue = -1;
			hasFishingHookPosition = false;
			fishingHookPosition = default(Vector3);
			FishingManager i = MonoSingleton<FishingManager>.I;
			object obj = i?.FishingCont;
			if (obj == null)
			{
				Transform mainPlayer = channel.MainPlayer;
				obj = ((mainPlayer != null) ? ((Component)mainPlayer).GetComponent<FishingController>() : null);
				if (obj == null)
				{
					Transform mainPlayer2 = channel.MainPlayer;
					obj = ((mainPlayer2 != null) ? ((Component)mainPlayer2).GetComponentInChildren<FishingController>() : null) ?? (((Object)(object)channel.MainNetTransform != (Object)null) ? ((Component)channel.MainNetTransform).GetComponentInChildren<FishingController>() : null);
				}
			}
			FishingController val = (FishingController)obj;
			if ((Object)(object)val != (Object)null)
			{
				wasFishingActive = (Object)(object)val.FishingRod != (Object)null && val.FishingRod.activeSelf;
				Transform fishHook = val.FishHook;
				if ((Object)(object)fishHook != (Object)null && IsFiniteVector3(fishHook.position))
				{
					hasFishingHookPosition = true;
					fishingHookPosition = fishHook.position;
				}
			}
			if (!((Object)(object)i == (Object)null))
			{
				fishingStateValue = (int)i.FishingState;
				wasFishingActive = wasFishingActive || i.IsActive || (int)i.FishingState > 0;
				FishingController fishingCont = i.FishingCont;
				if ((Object)(object)((fishingCont != null) ? fishingCont.FishHook : null) != (Object)null && IsFiniteVector3(fishingCont.FishHook.position))
				{
					hasFishingHookPosition = true;
					fishingHookPosition = fishingCont.FishHook.position;
				}
			}
		}

		private static bool IsTerminalCaptureReason(string reason)
		{
			if (string.IsNullOrWhiteSpace(reason))
			{
				return false;
			}
			if (reason.IndexOf("disconnect", StringComparison.OrdinalIgnoreCase) < 0 && reason.IndexOf("shutdown", StringComparison.OrdinalIgnoreCase) < 0)
			{
				return reason.IndexOf("network-switch", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		private static bool IsLikelyInvalidLocalCapture(Vector3 position, string reason)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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)
			if (!IsFiniteVector3(position))
			{
				return true;
			}
			if (Mathf.Abs(position.x) < 0.01f && Mathf.Abs(position.y) < 0.01f && Mathf.Abs(position.z) < 0.01f)
			{
				return true;
			}
			return false;
		}

		private static bool TryResolveLocalPose(TextChannelManager channel, out Vector3 position, out Quaternion rotation, out string source)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			position = default(Vector3);
			rotation = Quaternion.identity;
			source = string.Empty;
			Transform mainPlayer = channel.MainPlayer;
			Transform fallback = (((Object)(object)channel.MainNetTransform != (Object)null) ? ((Component)channel.MainNetTransform).transform : null);
			Transform val = PickPreferredTransform(mainPlayer, fallback);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			position = val.position;
			rotation = val.rotation;
			source = ((val == mainPlayer) ? "MainPlayer" : "MainNetTransform");
			return true;
		}

		private static Transform? PickPreferredTransform(Transform? primary, Transform? fallback)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)primary != (Object)null && !IsNearOrigin(primary.position))
			{
				return primary;
			}
			if ((Object)(object)fallback != (Object)null && !IsNearOrigin(fallback.position))
			{
				return fallback;
			}
			return primary ?? fallback;
		}

		private static bool IsNearOrigin(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if (Mathf.Abs(value.x) < 0.01f && Mathf.Abs(value.y) < 0.01f)
			{
				return Mathf.Abs(value.z) < 0.01f;
			}
			return false;
		}

		private static string GetPlayerSteamId(PlayerPanelController panel, int index)
		{
			if (panel.PlayerSteamIDs == null || index < 0 || index >= panel.PlayerSteamIDs.Count)
			{
				return string.Empty;
			}
			return panel.PlayerSteamIDs[index] ?? string.Empty;
		}

		private static string GetPlayerName(PlayerPanelController panel, int index)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (panel.IDInfos == null || index < 0 || index >= panel.IDInfos.Count)
			{
				return string.Empty;
			}
			return DecodeText(panel.IDInfos[index].Name);
		}

		private static string DecodeText(byte[]? value)
		{
			if (value == null || value.Length == 0)
			{
				return string.Empty;
			}
			return Encoding.UTF8.GetString(value).Trim('\0', ' ', '\n', '\r', '\t');
		}

		private string ResolveSteamIdByPlayerId(ulong playerId)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			PlayerPanelController i = NetworkSingleton<PlayerPanelController>.I;
			if ((Object)(object)i == (Object)null || i.PlayerIDs == null)
			{
				return string.Empty;
			}
			int num = ((i.PlayerSteamIDs == null) ? i.PlayerIDs.Count : Math.Min(i.PlayerIDs.Count, i.PlayerSteamIDs.Count));
			for (int j = 0; j < num; j++)
			{
				PlayerID val = i.PlayerIDs[j];
				if (((PlayerID)(ref val)).id.value == playerId)
				{
					return GetPlayerSteamId(i, j);
				}
			}
			return string.Empty;
		}

		private string ResolveLocalSteamId()
		{
			ulong num = LocalPlayerIdOrZero();
			if (num != 0L)
			{
				return ResolveSteamIdByPlayerId(num);
			}
			return string.Empty;
		}

		private bool TryGetLatestSnapshot(out RejoinSnapshotV1 snapshot)
		{
			snapshot = null;
			bool flag = false;
			long num = long.MinValue;
			foreach (KeyValuePair<ulong, RejoinSnapshotV1> snapshot2 in _snapshots)
			{
				RejoinSnapshotV1 value = snapshot2.Value;
				if (value != null && (!flag || value.SnapshotVersion > num))
				{
					snapshot = value;
					num = value.SnapshotVersion;
					flag = true;
				}
			}
			return flag;
		}

		private static RejoinSnapshotV1? CloneSnapshot(RejoinSnapshotV1? source)
		{
			if (source == null)
			{
				return null;
			}
			return new RejoinSnapshotV1
			{
				SnapshotVersion = source.SnapshotVersion,
				PlayerId = source.PlayerId,
				PlayerSteamId = source.PlayerSteamId,
				PlayerName = source.PlayerName,
				Position = source.Position,
				Rotation = source.Rotation,
				MovementState = source.MovementState,
				FocusIndex = source.FocusIndex,
				FocusTypeValue = source.FocusTypeValue,
				IsFocus = source.IsFocus,
				PomodoroMinute = source.PomodoroMinute,
				PomodoroPaused = source.PomodoroPaused,
				PomodoroType = source.PomodoroType,
				IsMessagePanelOpen = source.IsMessagePanelOpen,
				IsPomodoroPanelOpen = source.IsPomodoroPanelOpen,
				IsTodoPanelOpen = source.IsTodoPanelOpen,
				WasFishingActive = source.WasFishingActive,
				FishingStateValue = source.FishingStateValue,
				HasFishingHookPosition = source.HasFishingHookPosition,
				FishingHookPosition = source.FishingHookPosition,
				SnapshotSentAtUtc = source.SnapshotSentAtUtc,
				Reason = source.Reason,
				SceneName = source.SceneName,
				CapturedAtUtcTicks = source.CapturedAtUtcTicks
			};
		}

		private void PersistLocalSnapshot(RejoinSnapshotV1? snapshot)
		{
			if (!_persistLocalSnapshot.Value || snapshot == null || string.IsNullOrWhiteSpace(_localSnapshotStatePath))
			{
				return;
			}
			try
			{
				string directoryName = Path.GetDirectoryName(_localSnapshotStatePath);
				if (!string.IsNullOrWhiteSpace(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				File.WriteAllText(_localSnapshotStatePath, JsonUtility.ToJson((object)snapshot, true));
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Persist local snapshot failed: " + ex.Message));
			}
		}

		private void LoadPersistedLocalSnapshot()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (!_persistLocalSnapshot.Value || string.IsNullOrWhiteSpace(_localSnapshotStatePath) || !File.Exists(_localSnapshotStatePath))
			{
				return;
			}
			try
			{
				RejoinSnapshotV1 rejoinSnapshotV = JsonUtility.FromJson<RejoinSnapshotV1>(File.ReadAllText(_localSnapshotStatePath));
				if (rejoinSnapshotV != null)
				{
					if (IsLikelyInvalidLocalCapture(rejoinSnapshotV.Position, rejoinSnapshotV.Reason))
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)"Persisted local snapshot ignored because position is invalid.");
						return;
					}
					_lastLocalSnapshot = rejoinSnapshotV;
					_lastTerminalLocalSnapshot = CloneSnapshot(rejoinSnapshotV);
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded persisted local snapshot v{rejoinSnapshotV.SnapshotVersion} at {rejoinSnapshotV.Position.X:0.00},{rejoinSnapshotV.Position.Y:0.00},{rejoinSnapshotV.Position.Z:0.00}");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Load persisted local snapshot failed: " + ex.Message));
			}
		}

		private int GetFocusIndex(PlayerFocusController focusController)
		{
			if ((object)_focusIndexField == null)
			{
				_focusIndexField = AccessTools.Field(typeof(PlayerFocusController), "_currentFocusMainIndex");
			}
			if (_focusIndexField == null)
			{
				return -1;
			}
			try
			{
				return (_focusIndexField.GetValue(focusController) is int num) ? num : (-1);
			}
			catch
			{
				return -1;
			}
		}

		private int GetFocusTypeValue(PlayerFocusController focusController)
		{
			if ((object)_focusTypeField == null)
			{
				_focusTypeField = AccessTools.Field(typeof(PlayerFocusController), "_focusType");
			}
			if (_focusTypeField == null)
			{
				return -1;
			}
			try
			{
				object value = _focusTypeField.GetValue(focusController);
				return (value == null) ? (-1) : Convert.ToInt32(value);
			}
			catch
			{
				return -1;
			}
		}

		private void SendSnapshotRequest(string reason)
		{
			if (!((Object)(object)_network == (Object)null) && _network.isClient)
			{
				ulong num = LocalPlayerIdOrZero();
				if (num == 0L)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Skipping snapshot request until local player id is ready (reason='" + reason + "')"));
					return;
				}
				SnapshotRequestV1 snapshotRequestV = new SnapshotRequestV1
				{
					Reason = reason,
					RequestedAtUtc = DateTime.UtcNow.ToString("O"),
					ClientPlayerId = num,
					ClientSteamId = ResolveLocalSteamId()
				};
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Sending snapshot request (reason='{reason}', playerId={snapshotRequestV.ClientPlayerId}, steamId='{snapshotRequestV.ClientSteamId}')");
				SendToServer(RejoinMessageType.SnapshotRequest, snapshotRequestV);
			}
		}

		private void SendSnapshotToPlayer(PlayerID targetPlayer, string reason, string preferredSteamId = "")
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_network == (Object)null)
			{
				return;
			}
			ulong value = ((PlayerID)(ref targetPlayer)).id.value;
			string text = (string.IsNullOrWhiteSpace(preferredSteamId) ? ResolveSteamIdByPlayerId(value) : preferredSteamId);
			RejoinSnapshotV1 value2 = null;
			bool flag = false;
			string text2 = "player-id";
			if (!string.IsNullOrWhiteSpace(text) && _snapshotsBySteamId.TryGetValue(text, out value2))
			{
				flag = true;
				text2 = "steam-id";
			}
			else if (_snapshots.TryGetValue(value, out value2))
			{
				flag = true;
			}
			else if (TryGetLatestSnapshot(out value2))
			{
				flag = true;
				text2 = "latest-fallback";
			}
			if (!flag)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"No snapshot available for player {value} (steamId='{text}', reason='{reason}')");
				return;
			}
			value2.PlayerId = value;
			if (!string.IsNullOrWhiteSpace(text))
			{
				value2.PlayerSteamId = text;
			}
			value2.Reason = reason;
			value2.SnapshotSentAtUtc = DateTime.UtcNow.ToString("O");
			_snapshots[value] = value2;
			if (!string.IsNullOrWhiteSpace(value2.PlayerSteamId))
			{
				_snapshotsBySteamId[value2.PlayerSteamId] = value2;
			}
			byte[] data = PacketEnvelopeV1.Encode(2, LocalPlayerIdOrZero(), JsonUtility.ToJson((object)value2));
			_network.Send<NetworkPayloadV1>(targetPlayer, new NetworkPayloadV1
			{
				Data = data
			}, (Channel)2);
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Sent snapshot v{value2.SnapshotVersion} to player {value} using {text2} (reason='{reason}')");
		}

		private void OnNetworkPayload(PlayerID sender, NetworkPayloadV1 payload, bool asServer)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (!PacketEnvelopeV1.TryDecode(payload?.Data, out var envelope))
			{
				return;
			}
			switch ((RejoinMessageType)envelope.MessageType)
			{
			case RejoinMessageType.SnapshotRequest:
				if (asServer && IsHostActive())
				{
					HandleSnapshotRequest(sender, envelope.PayloadJson);
				}
				break;
			case RejoinMessageType.SnapshotData:
				if (!asServer)
				{
					HandleSnapshotData(envelope.PayloadJson);
				}
				break;
			case RejoinMessageType.SnapshotApplyAck:
				if (asServer && IsHostActive())
				{
					HandleSnapshotAck(sender, envelope.PayloadJson);
				}
				break;
			}
		}

		private void HandleSnapshotRequest(PlayerID sender, string json)
		{
			//IL_0040: 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)
			try
			{
				SnapshotRequestV1 snapshotRequestV = JsonUtility.FromJson<SnapshotRequestV1>(json) ?? new SnapshotRequestV1();
				string text = (string.IsNullOrWhiteSpace(snapshotRequestV.Reason) ? "snapshot-request" : snapshotRequestV.Reason);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Received snapshot request from {((PlayerID)(ref sender)).id.value} (reason='{text}', playerId={snapshotRequestV.ClientPlayerId}, steamId='{snapshotRequestV.ClientSteamId}')");
				SendSnapshotToPlayer(sender, text, snapshotRequestV.ClientSteamId ?? string.Empty);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed handling snapshot request: " + ex.Message));
			}
		}

		private void HandleSnapshotData(string json)
		{
			RejoinSnapshotV1 rejoinSnapshotV = null;
			try
			{
				rejoinSnapshotV = JsonUtility.FromJson<RejoinSnapshotV1>(json);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed deserializing snapshot data: " + ex.Message));
			}
			if (rejoinSnapshotV == null)
			{
				return;
			}
			ulong num = LocalPlayerIdOrZero();
			if (rejoinSnapshotV.PlayerId != 0L && num != 0L && rejoinSnapshotV.PlayerId != num)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Ignoring snapshot v{rejoinSnapshotV.SnapshotVersion} for player {rejoinSnapshotV.PlayerId}; local player is {num}");
				return;
			}
			if (rejoinSnapshotV.SnapshotVersion != 0L && rejoinSnapshotV.SnapshotVersion <= _lastAppliedSnapshotVersion)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Skipping stale snapshot v{rejoinSnapshotV.SnapshotVersion}; last applied is v{_lastAppliedSnapshotVersion}");
				return;
			}
			if (_applySnapshotCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_applySnapshotCoroutine);
			}
			_applySnapshotCoroutine = ((MonoBehaviour)this).StartCoroutine(ApplySnapshotWhenReady(rejoinSnapshotV));
		}

		private IEnumerator ApplySnapshotWhenReady(RejoinSnapshotV1 snapshot)
		{
			bool waitedForMainPlayer = false;
			for (int attempt = 0; attempt < 40; attempt++)
			{
				TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
				if ((Object)(object)i != (Object)null && (Object)(object)i.MainNetTransform != (Object)null)
				{
					break;
				}
				waitedForMainPlayer = true;
				yield return (object)new WaitForSeconds(0.25f);
			}
			if (waitedForMainPlayer)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Applying snapshot v{snapshot.SnapshotVersion} after waiting for local player object.");
			}
			bool flag = ApplySnapshot(snapshot);
			bool flag2 = ShouldRestoreFocus(snapshot);
			if (flag2)
			{
				BeginFocusRestoreRetry(snapshot);
			}
			bool flag3 = ShouldResumeFishing(snapshot);
			if (flag3)
			{
				BeginFishingResumeRetry(snapshot);
			}
			if (flag || flag2 || flag3)
			{
				_snapshotAppliedThisSession = true;
				_lastAppliedSnapshotVersion = Math.Max(_lastAppliedSnapshotVersion, snapshot.SnapshotVersion);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Applied snapshot v{snapshot.SnapshotVersion} (reason='{snapshot.Reason}', focusPending={flag2}, fishingPending={flag3})");
				NotifyLocal($"Rejoin state restored (v{snapshot.SnapshotVersion}).");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Snapshot v{snapshot.SnapshotVersion} did not apply any state (reason='{snapshot.Reason}')");
			}
			SnapshotApplyAckV1 payload = new SnapshotApplyAckV1
			{
				SnapshotVersion = snapshot.SnapshotVersion,
				AppliedAtUtc = DateTime.UtcNow.ToString("O"),
				Status = (flag ? "applied" : ((flag2 || flag3) ? "applied-pending-followup" : "no-op"))
			};
			SendToServer(RejoinMessageType.SnapshotApplyAck, payload);
			_applySnapshotCoroutine = null;
		}

		private void HandleSnapshotAck(PlayerID sender, string json)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				SnapshotApplyAckV1 snapshotApplyAckV = JsonUtility.FromJson<SnapshotApplyAckV1>(json);
				if (snapshotApplyAckV != null)
				{
					_lastAckByPlayer[((PlayerID)(ref sender)).id.value] = snapshotApplyAckV.SnapshotVersion;
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed handling snapshot ack: " + ex.Message));
			}
		}

		private bool ApplySnapshot(RejoinSnapshotV1 snapshot)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			bool result = false;
			TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
			if ((Object)(object)i != (Object)null)
			{
				try
				{
					Transform val = PickPreferredTransform(i.MainPlayer, ((Object)(object)i.MainNetTransform != (Object)null) ? ((Component)i.MainNetTransform).transform : null);
					if ((Object)(object)val != (Object)null)
					{
						Vector3 desiredPosition = snapshot.Position;
						if (TryResolveSafeRestorePosition(snapshot, val, desiredPosition, out Vector3 safePosition, out string reason))
						{
							SetTransformSafely(val, safePosition, snapshot.Rotation, i.MainMovementController);
							MirrorPoseToSecondaryTransform(i, val, safePosition, snapshot.Rotation);
							result = true;
						}
						else
						{
							((BaseUnityPlugin)this).Logger.LogInfo((object)$"Skipped position restore for snapshot v{snapshot.SnapshotVersion}: {reason}");
						}
					}
				}
				catch (Exception arg)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"Position restore failed: {arg}");
				}
				try
				{
					if ((Object)(object)i.MainMovementController != (Object)null && snapshot.MovementState >= 0)
					{
						i.MainMovementController.MovementState = (MovementState)snapshot.MovementState;
						result = true;
					}
				}
				catch (Exception arg2)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"Movement restore failed: {arg2}");
				}
				try
				{
					if ((Object)(object)i.MainFocusController != (Object)null && !snapshot.IsFocus)
					{
						i.MainFocusController.IsFocus = false;
						result = true;
					}
				}
				catch (Exception arg3)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"Focus restore failed: {arg3}");
				}
			}
			try
			{
				PomodoroController i2 = MonoSingleton<PomodoroController>.I;
				if ((Object)(object)i2 != (Object)null)
				{
					SetPropertyNonPublic(i2, "CurrentMinute", snapshot.PomodoroMinute);
					SetPropertyNonPublic(i2, "IsPaused", snapshot.PomodoroPaused);
					if (Enum.IsDefined(typeof(PomodoroType), snapshot.PomodoroType))
					{
						i2.PomodoroType = (PomodoroType)snapshot.PomodoroType;
					}
					result = true;
				}
			}
			catch (Exception arg4)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Pomodoro restore failed: {arg4}");
			}
			if (_applyUiState.Value)
			{
				try
				{
					UIManager i3 = MonoSingleton<UIManager>.I;
					if ((Object)(object)i3 != (Object)null)
					{
						i3.IsMessagePanelActivated = snapshot.IsMessagePanelOpen;
						SetPropertyNonPublic(i3, "IsPomodoroOpened", snapshot.IsPomodoroPanelOpen);
						SetPropertyNonPublic(i3, "IsTodoOpened", snapshot.IsTodoPanelOpen);
						i3.UpdateTimerTextOnOpen();
						result = true;
					}
				}
				catch (Exception arg5)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"UI restore failed: {arg5}");
				}
			}
			return result;
		}

		private static void MirrorPoseToSecondaryTransform(TextChannelManager channel, Transform primary, Vector3 position, Quaternion rotation)
		{
			//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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)channel.MainPlayer != (Object)null && channel.MainPlayer != primary)
				{
					channel.MainPlayer.SetPositionAndRotation(position, rotation);
				}
				if ((Object)(object)channel.MainNetTransform != (Object)null && ((Component)channel.MainNetTransform).transform != primary)
				{
					((Component)channel.MainNetTransform).transform.SetPositionAndRotation(position, rotation);
				}
			}
			catch
			{
			}
		}

		private static bool ShouldRestoreFocus(RejoinSnapshotV1 snapshot)
		{
			return snapshot.IsFocus;
		}

		private void BeginFocusRestoreRetry(RejoinSnapshotV1 snapshot)
		{
			if (ShouldRestoreFocus(snapshot))
			{
				if (_focusRestoreCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_focusRestoreCoroutine);
				}
				_focusRestoreCoroutine = ((MonoBehaviour)this).StartCoroutine(FocusRestoreRetryLoop(snapshot));
			}
		}

		private IEnumerator FocusRestoreRetryLoop(RejoinSnapshotV1 snapshot)
		{
			float num = Mathf.Max(0f, _focusRestoreDelaySeconds.Value);
			if (num > 0f)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Delaying focus restore for snapshot v{snapshot.SnapshotVersion} by {num:0.00}s.");
				yield return (object)new WaitForSeconds(num);
			}
			for (int attempt = 1; attempt <= 40; attempt++)
			{
				PlayerFocusController val = NetworkSingleton<TextChannelManager>.I?.MainFocusController;
				if ((Object)(object)val != (Object)null && TryApplyFocusSnapshotNow(val, snapshot))
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Applied focus restore for snapshot v{snapshot.SnapshotVersion} on attempt {attempt}.");
					_focusRestoreCoroutine = null;
					yield break;
				}
				yield return (object)new WaitForSeconds(0.25f);
			}
			((BaseUnityPlugin)this).Logger.LogWarning((object)$"Focus restore retries exhausted for snapshot v{snapshot.SnapshotVersion}.");
			_focusRestoreCoroutine = null;
		}

		private bool ShouldResumeFishing(RejoinSnapshotV1 snapshot)
		{
			if (!_resumeFishingOnRestore.Value)
			{
				return false;
			}
			if (snapshot.WasFishingActive)
			{
				return true;
			}
			if (snapshot.FishingStateValue != 1 && snapshot.FishingStateValue != 2)
			{
				return snapshot.FishingStateValue == 3;
			}
			return true;
		}

		private void BeginFishingResumeRetry(RejoinSnapshotV1 snapshot)
		{
			if (ShouldResumeFishing(snapshot))
			{
				if (_fishingResumeCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_fishingResumeCoroutine);
				}
				_fishingResumeCoroutine = ((MonoBehaviour)this).StartCoroutine(FishingResumeRetryLoop(snapshot));
			}
		}

		private IEnumerator FishingResumeRetryLoop(RejoinSnapshotV1 snapshot)
		{
			yield return (object)new WaitForSeconds(1.25f);
			for (int attempt = 1; attempt <= 24; attempt++)
			{
				if ((Object)(object)_network == (Object)null || !_network.isClient)
				{
					_fishingResumeCoroutine = null;
					yield break;
				}
				if (TryResumeFishingNow(snapshot, out string detail))
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Fishing resume applied for snapshot v{snapshot.SnapshotVersion} on attempt {attempt} ({detail}).");
					_fishingResumeCoroutine = null;
					yield break;
				}
				if (attempt == 1 || attempt % 8 == 0)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Fishing resume waiting for snapshot v{snapshot.SnapshotVersion} (attempt {attempt}): {detail}");
				}
				yield return (object)new WaitForSeconds(0.35f);
			}
			((BaseUnityPlugin)this).Logger.LogWarning((object)$"Fishing resume retries exhausted for snapshot v{snapshot.SnapshotVersion}.");
			_fishingResumeCoroutine = null;
		}

		private bool TryResumeFishingNow(RejoinSnapshotV1 snapshot, out string detail)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Invalid comparison between Unknown and I4
			//IL_0068: 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_005f: Invalid comparison between Unknown and I4
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			detail = string.Empty;
			FishingManager i = MonoSingleton<FishingManager>.I;
			if ((Object)(object)i == (Object)null)
			{
				detail = "FishingManager unavailable";
				return false;
			}
			if ((int)i.CurrentBait == 0)
			{
				detail = "no bait selected";
				return false;
			}
			if (!i.IsActive)
			{
				i.ButtonFish();
			}
			if (!i.IsActive)
			{
				detail = "rod not active yet";
				return false;
			}
			if ((int)i.FishingState == 2 || (int)i.FishingState == 3)
			{
				detail = $"already in state {i.FishingState}";
				return true;
			}
			if ((int)i.FishingState != 0)
			{
				detail = $"state not ready ({i.FishingState})";
				return false;
			}
			Transform val = NetworkSingleton<TextChannelManager>.I?.MainPlayer;
			if ((Object)(object)val == (Object)null)
			{
				detail = "main player unavailable";
				return false;
			}
			Vector3 desiredTarget = ((snapshot.HasFishingHookPosition && IsFiniteVector3(snapshot.FishingHookPosition)) ? ((Vector3)snapshot.FishingHookPosition) : (val.position + val.forward * 3f));
			if (!TryResolveFishingCastTarget(i, val, desiredTarget, out var castTarget))
			{
				detail = "unable to resolve water cast target";
				return false;
			}
			if (!TryTriggerFishingCast(i, castTarget, out string reason))
			{
				detail = reason;
				return false;
			}
			detail = $"cast triggered at {castTarget.x:0.00},{castTarget.y:0.00},{castTarget.z:0.00}";
			return true;
		}

		private bool TryResolveFishingCastTarget(FishingManager fishingManager, Transform mainPlayer, Vector3 desiredTarget, out Vector3 castTarget)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			castTarget = desiredTarget;
			int collisionMask = ResolveFishingCollisionMask(fishingManager);
			if (TryRaycastFishingWater(desiredTarget, collisionMask, out castTarget))
			{
				return true;
			}
			Vector3 position = mainPlayer.position;
			Vector3 forward = mainPlayer.forward;
			Vector3 val;
			if (!(((Vector3)(ref forward)).sqrMagnitude > 0.001f))
			{
				val = Vector3.forward;
			}
			else
			{
				forward = mainPlayer.forward;
				val = ((Vector3)(ref forward)).normalized;
			}
			Vector3 val2 = val;
			if (TryRaycastFishingWater(position + val2 * 3f, collisionMask, out castTarget))
			{
				return true;
			}
			if (TryRaycastFishingWater(position + Quaternion.Euler(0f, 30f, 0f) * val2 * 3f, collisionMask, out castTarget))
			{
				return true;
			}
			if (TryRaycastFishingWater(position + Quaternion.Euler(0f, -30f, 0f) * val2 * 3f, collisionMask, out castTarget))
			{
				return true;
			}
			return false;
		}

		private int ResolveFishingCollisionMask(FishingManager fishingManager)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if ((object)_fishingCollisionLayersField == null)
			{
				_fishingCollisionLayersField = AccessTools.Field(typeof(FishingManager), "_collisionLayers");
			}
			if (_fishingCollisionLayersField?.GetValue(fishingManager) is LayerMask val && ((LayerMask)(ref val)).value != 0)
			{
				return ((LayerMask)(ref val)).value;
			}
			return -1;
		}

		private static bool TryRaycastFishingWater(Vector3 target, int collisionMask, out Vector3 waterTarget)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: 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_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			waterTarget = target;
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(target + Vector3.up * 80f, Vector3.down, ref val, 160f, collisionMask, (QueryTriggerInteraction)1))
			{
				return false;
			}
			if ((Object)(object)((RaycastHit)(ref val)).transform == (Object)null || ((Component)((RaycastHit)(ref val)).transform).gameObject.layer != LayerMask.NameToLayer("Water"))
			{
				return false;
			}
			waterTarget = ((RaycastHit)(ref val)).point;
			return IsFiniteVector3(waterTarget);
		}

		private bool TryTriggerFishingCast(FishingManager fishingManager, Vector3 castTarget, out string reason)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			reason = string.Empty;
			if ((object)_fishingCastIndicatorField == null)
			{
				_fishingCastIndicatorField = AccessTools.Field(typeof(FishingManager), "_castIndicator");
			}
			if ((object)_fishingCastFishingRodMethod == null)
			{
				_fishingCastFishingRodMethod = AccessTools.Method(typeof(FishingManager), "CastFishingRod", (Type[])null, (Type[])null);
			}
			object? obj = _fishingCastIndicatorField?.GetValue(fishingManager);
			Transform val = (Transform)((obj is Transform) ? obj : null);
			if (val == null)
			{
				reason = "cast indicator unavailable";
				return false;
			}
			if (_fishingCastFishingRodMethod == null)
			{
				reason = "CastFishingRod method unavailable";
				return false;
			}
			bool activeSelf = ((Component)val).gameObject.activeSelf;
			try
			{
				val.position = castTarget;
				((Component)val).gameObject.SetActive(true);
				_fishingCastFishingRodMethod.Invoke(fishingManager, Array.Empty<object>());
				return true;
			}
			catch (Exception ex)
			{
				reason = "cast invocation failed: " + ex.GetBaseException().Message;
				return false;
			}
			finally
			{
				((Component)val).gameObject.SetActive(activeSelf);
			}
		}

		private bool TryApplyFocusSnapshotNow(PlayerFocusController focusController, RejoinSnapshotV1 snapshot)
		{
			if (!ShouldRestoreFocus(snapshot))
			{
				return false;
			}
			if (!IsFocusControllerReady(focusController))
			{
				return false;
			}
			if (!TryPrimeFocusContext(focusController, snapshot, out var focusMainIndex))
			{
				return false;
			}
			try
			{
				focusController.SetFocus(0);
				return focusController.IsFocus;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Focus controller not ready for SetFocus yet (mainIndex={focusMainIndex}, focusIndex={snapshot.FocusIndex}, focusType={snapshot.FocusTypeValue}): {ex}");
				return false;
			}
		}

		private bool IsFocusControllerReady(PlayerFocusController focusController)
		{
			if (focusController.FocusAreaControllers == null || focusController.FocusAreaControllers.Count == 0)
			{
				return false;
			}
			if ((Object)(object)NetworkSingleton<TextChannelManager>.I?.MainAnimationController == (Object)null)
			{
				return false;
			}
			if ((Object)(object)MonoSingleton<UIManager>.I == (Object)null || (Object)(object)ScriptableSingleton<GameSettings>.I == (Object)null)
			{
				return false;
			}
			if ((object)_focusSimpledTypesField == null)
			{
				_focusSimpledTypesField = AccessTools.Field(typeof(PlayerFocusController), "_simpledFocusTypes");
			}
			if ((object)_focusIndexField == null)
			{
				_focusIndexField = AccessTools.Field(typeof(PlayerFocusController), "_currentFocusMainIndex");
			}
			if (_focusSimpledTypesField == null || _focusIndexField == null)
			{
				return false;
			}
			return true;
		}

		private bool TryPrimeFocusContext(PlayerFocusController focusController, RejoinSnapshotV1 snapshot, out int focusMainIndex)
		{
			RejoinSnapshotV1 snapshot2 = snapshot;
			focusMainIndex = -1;
			FocusAreaController val = FindNearestFocusArea(focusController);
			if ((Object)(object)val != (Object)null)
			{
				SetPropertyNonPublic(focusController, "CurrentFocusAreaController", val);
			}
			List<FocusType> list = BuildSimplifiedFocusTypes(focusController);
			if (list == null || list.Count == 0 || _focusSimpledTypesField == null || _focusIndexField == null)
			{
				return false;
			}
			focusMainIndex = snapshot2.FocusIndex;
			if (focusMainIndex < 0 && snapshot2.FocusTypeValue >= 0)
			{
				focusMainIndex = list.FindIndex((FocusType type) => Convert.ToInt32(type) == snapshot2.FocusTypeValue);
			}
			if (focusMainIndex < 0)
			{
				focusMainIndex = 0;
			}
			focusMainIndex = Mathf.Clamp(focusMainIndex, 0, list.Count - 1);
			try
			{
				_focusSimpledTypesField.SetValue(focusController, list);
				_focusIndexField.SetValue(focusController, focusMainIndex);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static FocusAreaController? FindNearestFocusArea(PlayerFocusController focusController)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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)
			if (focusController.FocusAreaControllers == null || focusController.FocusAreaControllers.Count == 0)
			{
				return null;
			}
			Vector3 position = ((Component)focusController).transform.position;
			float num = float.MaxValue;
			FocusAreaController result = null;
			for (int i = 0; i < focusController.FocusAreaControllers.Count; i++)
			{
				FocusAreaController val = focusController.FocusAreaControllers[i];
				if (!((Object)(object)val == (Object)null))
				{
					Vector3 val2 = ((Component)val).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (!(sqrMagnitude >= num))
					{
						num = sqrMagnitude;
						result = val;
					}
				}
			}
			return result;
		}

		private static List<FocusType>? BuildSimplifiedFocusTypes(PlayerFocusController focusController)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			List<FocusType> list = null;
			FocusAreaController currentFocusAreaController = focusController.CurrentFocusAreaController;
			if ((Object)(object)currentFocusAreaController != (Object)null && (Object)(object)currentFocusAreaController.FocusAreaSettings != (Object)null)
			{
				list = currentFocusAreaController.FocusAreaSettings.FocusTypes;
			}
			if (list == null)
			{
				GameSettings i = ScriptableSingleton<GameSettings>.I;
				list = ((i != null) ? i.FocusTypes : null);
			}
			if (list == null || list.Count == 0)
			{
				return null;
			}
			List<FocusType> list2 = new List<FocusType>(list.Count);
			for (int j = 0; j < list.Count; j++)
			{
				FocusType item = list[j];
				if (!list2.Contains(item))
				{
					list2.Add(item);
				}
			}
			return list2;
		}

		private void SendToServer<TPayload>(RejoinMessageType type, TPayload payload)
		{
			if (!((Object)(object)_network == (Object)null) && _network.isClient)
			{
				string payloadJson = JsonUtility.ToJson((object)payload);
				byte[] data = PacketEnvelopeV1.Encode((byte)type, LocalPlayerIdOrZero(), payloadJson);
				_network.SendToServer<NetworkPayloadV1>(new NetworkPayloadV1
				{
					Data = data
				}, (Channel)2);
			}
		}

		private bool TryResolveSafeRestorePosition(RejoinSnapshotV1 snapshot, Transform currentTransform, Vector3 desiredPosition, out Vector3 safePosition, out string reason)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: 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_00ae: 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_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			safePosition = currentTransform.position;
			reason = string.Empty;
			if (!_safePositionRestore.Value)
			{
				safePosition = desiredPosition;
				return true;
			}
			if (!IsFiniteVector3(desiredPosition))
			{
				reason = "desired position is invalid";
				return false;
			}
			if (!string.IsNullOrWhiteSpace(snapshot.SceneName))
			{
				Scene activeScene = SceneManager.GetActiveScene();
				string name = ((Scene)(ref activeScene)).name;
				if (!string.Equals(name, snapshot.SceneName, StringComparison.Ordinal))
				{
					reason = "scene mismatch (snapshot='" + snapshot.SceneName + "', active='" + name + "')";
					return false;
				}
			}
			Vector3 position = currentTransform.position;
			float num = Vector2.Distance(new Vector2(position.x, position.z), new Vector2(desiredPosition.x, desiredPosition.z));
			if (num > Math.Max(5f, _maxRestoreDistanceMeters.Value))
			{
				reason = $"restore distance too large ({num:0.0}m)";
				return false;
			}
			Vector3 val = desiredPosition;
			Vector3 snappedPosition2;
			if (TrySnapToGround(desiredPosition, out var snappedPosition))
			{
				val = snappedPosition;
			}
			else if (desiredPosition.y < -2f && TrySnapToGround(position, out snappedPosition2))
			{
				val = snappedPosition2;
				reason = "desired Y below threshold and ground snap failed; using current ground";
			}
			if (!IsFiniteVector3(val) || val.y < -10f)
			{
				reason = "final candidate failed safety checks";
				return false;
			}
			safePosition = val;
			return true;
		}

		private bool TrySnapToGround(Vector3 originPosition, out Vector3 snappedPosition)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: 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_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: 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)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: 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)
			snappedPosition = originPosition;
			Vector3 val = originPosition + Vector3.up * Math.Max(2f, _groundSnapCastHeight.Value);
			float num = Math.Max(5f, _groundSnapCastDistance.Value);
			RaycastHit[] array = Physics.RaycastAll(val, Vector3.down, num, -1, (QueryTriggerInteraction)1);
			if (array == null || array.Length == 0)
			{
				return false;
			}
			float num2 = Mathf.Max(0f, _groundSnapMaxUpwardAdjust.Value);
			float num3 = float.MaxValue;
			float num4 = float.MaxValue;
			Vector3 val2 = originPosition;
			Vector3 val3 = originPosition;
			bool flag = false;
			bool flag2 = false;
			for (int i = 0; i < array.Length; i++)
			{
				RaycastHit val4 = array[i];
				if (IsFiniteVector3(((RaycastHit)(ref val4)).point) && !(((RaycastHit)(ref val4)).normal.y < 0.2f))
				{
					float num5 = ((RaycastHit)(ref val4)).point.y - originPosition.y;
					float num6 = Mathf.Abs(num5);
					if (num6 < num4)
					{
						num4 = num6;
						val3 = ((RaycastHit)(ref val4)).point;
						flag2 = true;
					}
					if (!(num5 > num2) && !(num6 >= num3))
					{
						num3 = num6;
						val2 = ((RaycastHit)(ref val4)).point;
						flag = true;
					}
				}
			}
			if (!flag && !flag2)
			{
				return false;
			}
			Vector3 val5 = (flag ? val2 : val3);
			snappedPosition = val5 + Vector3.up * Mathf.Max(0f, _groundSnapOffsetY.Value);
			return true;
		}

		private static ConfigDescription DescribeSetting(string description, string displayName, int order, AcceptableValueBase? acceptableValues = null)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			return new ConfigDescription(description, acceptableValues, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					DispName = displayName,
					Order = order
				}
			});
		}

		private static bool IsFiniteVector3(Vector3 value)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (!float.IsNaN(value.x) && !float.IsNaN(value.y) && !float.IsNaN(value.z) && !float.IsInfinity(value.x) && !float.IsInfinity(value.y))
			{
				return !float.IsInfinity(value.z);
			}
			return false;
		}

		private void SetTransformSafely(Transform targetTransform, Vector3 position, Quaternion rotation, PlayerMovementController? movementController)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)movementController == (Object)null)
			{
				targetTransform.SetPositionAndRotation(position, rotation);
				return;
			}
			if ((object)_characterControllerField == null)
			{
				_characterControllerField = AccessTools.Field(typeof(PlayerMovementController), "_characterController");
			}
			object? obj = _characterControllerField?.GetValue(movementController);
			CharacterController val = (CharacterController)((obj is CharacterController) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				targetTransform.SetPositionAndRotation(position, rotation);
				return;
			}
			bool enabled = ((Collider)val).enabled;
			try
			{
				if (enabled)
				{
					((Collider)val).enabled = false;
				}
				targetTransform.SetPositionAndRotation(position, rotation);
			}
			finally
			{
				if (enabled)
				{
					((Collider)val).enabled = true;
				}
			}
		}

		private static void SetPropertyNonPublic(object instance, string propertyName, object value)
		{
			try
			{
				AccessTools.PropertySetter(instance.GetType(), propertyName)?.Invoke(instance, new object[1] { value });
			}
			catch
			{
			}
		}

		private void NotifyLocal(string message)
		{
			if (!_showNotifications.Value)
			{
				return;
			}
			try
			{
				TextChannelManager i = NetworkSingleton<TextChannelManager>.I;
				if (i != null)
				{
					i.AddNotification("[RejoinStateRecovery] " + message);
				}
			}
			catch
			{
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "RejoinStateRecovery";

		public const string PLUGIN_NAME = "RejoinStateRecovery";

		public const string PLUGIN_VERSION = "1.0.9";
	}
}