Decompiled source of Empress LateJoin v1.1.0

EmpressLateJoin.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Empress")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.0.7.0")]
[assembly: AssemblyInformationalVersion("2.0.7")]
[assembly: AssemblyProduct("EmpressLateJoin")]
[assembly: AssemblyTitle("EmpressLateJoin")]
[assembly: AssemblyVersion("2.0.7.0")]
[module: RefSafetyRules(11)]
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;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace EmpressLateJoin
{
	[BepInPlugin("empress.repo.empresslatejoin", "EmpressLateJoin", "2.0.7")]
	public sealed class EmpressLateJoinPlugin : BaseUnityPlugin
	{
		internal const string PluginGuid = "empress.repo.empresslatejoin";

		internal const string PluginName = "EmpressLateJoin";

		internal const string PluginVersion = "2.0.7";

		private Harmony? _empressHarmony;

		private bool _empressApplicationQuitting;

		internal ConfigEntry<bool> EmpressEnabled;

		internal ConfigEntry<bool> EmpressKeepRoomJoinable;

		internal ConfigEntry<bool> EmpressLiveJoinActiveLevels;

		internal ConfigEntry<bool> EmpressQueueDuringActiveLevels;

		internal ConfigEntry<float> EmpressWaitingLeashRadius;

		internal ConfigEntry<float> EmpressWaitingSnapCooldown;

		internal ConfigEntry<float> EmpressSceneSwitchSettleDelay;

		internal ConfigEntry<float> EmpressSceneSwitchSettleTimeout;

		internal static EmpressLateJoinPlugin? Instance { get; private set; }

		internal static EmpressLateJoinRuntime? Runtime { get; private set; }

		internal ManualLogSource EmpressLog => ((BaseUnityPlugin)this).Logger;

		private void Awake()
		{
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Expected O, but got Unknown
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Expected O, but got Unknown
			((Component)this).gameObject.transform.parent = null;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			Instance = this;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			EmpressEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch for EmpressLateJoin.");
			EmpressKeepRoomJoinable = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "KeepRoomJoinable", true, "Keeps the active multiplayer room joinable after the host starts the run.");
			EmpressLiveJoinActiveLevels = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LiveJoinActiveLevels", true, "Lets mid-level joiners load straight into the active scene and replay the missing live state from the host.");
			EmpressQueueDuringActiveLevels = ((BaseUnityPlugin)this).Config.Bind<bool>("WaitingRoom", "QueueDuringActiveLevels", true, "Parks mid-level late joiners in the Empress holding cell and lets them enter normal play on the next scene.");
			EmpressWaitingLeashRadius = ((BaseUnityPlugin)this).Config.Bind<float>("WaitingRoom", "WaitingLeashRadius", 7f, "How far a waiting player can stray from the holding cell anchor before EmpressLateJoin snaps them back.");
			EmpressWaitingSnapCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("WaitingRoom", "WaitingSnapCooldown", 0.75f, "Minimum delay between holding-cell snapbacks.");
			EmpressSceneSwitchSettleDelay = ((BaseUnityPlugin)this).Config.Bind<float>("WaitingRoom", "SceneSwitchSettleDelay", 1.25f, "Minimum amount of time EmpressLateJoin waits for a late joiner to settle before allowing the next scene switch.");
			EmpressSceneSwitchSettleTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("WaitingRoom", "SceneSwitchSettleTimeout", 8f, "Maximum amount of time EmpressLateJoin will delay a scene switch while a late joiner settles.");
			Runtime = Resources.FindObjectsOfTypeAll<EmpressLateJoinRuntime>().FirstOrDefault();
			if ((Object)(object)Runtime == (Object)null)
			{
				GameObject val = new GameObject("EmpressLateJoin.Runtime")
				{
					hideFlags = (HideFlags)61
				};
				val.transform.parent = null;
				Object.DontDestroyOnLoad((Object)val);
				Runtime = val.AddComponent<EmpressLateJoinRuntime>();
			}
			else
			{
				((Component)Runtime).gameObject.transform.parent = null;
				Object.DontDestroyOnLoad((Object)(object)((Component)Runtime).gameObject);
				((Object)((Component)Runtime).gameObject).hideFlags = (HideFlags)61;
			}
			_empressHarmony = new Harmony("empress.repo.empresslatejoin");
			_empressHarmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"EmpressLateJoin 2.0.7 loaded.");
		}

		private void OnApplicationQuit()
		{
			_empressApplicationQuitting = true;
		}

		private void OnDestroy()
		{
			if (_empressApplicationQuitting)
			{
				Harmony? empressHarmony = _empressHarmony;
				if (empressHarmony != null)
				{
					empressHarmony.UnpatchSelf();
				}
				if (Instance == this && (Object)(object)Runtime != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)Runtime).gameObject);
					Runtime = null;
				}
				if (Instance == this)
				{
					Instance = null;
				}
			}
		}
	}
	internal sealed class EmpressLateJoinRuntime : MonoBehaviour
	{
		private sealed class EmpressDelayedSceneChangeRequest
		{
			internal RunManager RunManager;

			internal bool CompletedLevel;

			internal bool LevelFailed;

			internal ChangeLevelType ChangeLevelType;

			internal float RequestedAt;

			internal bool TimeoutLogged;
		}

		private const float EmpressJoinabilityRefreshInterval = 1f;

		private const float EmpressSupportAuditInterval = 1f;

		private const float EmpressWaitingAnchorRadiusBase = 1.3f;

		private const float EmpressLateJoinReadyHoldTime = 0.2f;

		private const float EmpressLateJoinMinimumIdentityTime = 0.5f;

		private const float EmpressLiveJoinUpdateLevelDelay = 2f;

		private const float EmpressLiveJoinGenerateDoneDelay = 3.5f;

		private const float EmpressLiveJoinReplayDelay = 4.5f;

		private static readonly FieldInfo? EmpressGameManagerConnectRandomField = AccessTools.Field(typeof(GameManager), "connectRandom");

		private static readonly FieldInfo? EmpressPlayerAvatarSpawnedField = AccessTools.Field(typeof(PlayerAvatar), "spawned");

		private static readonly FieldInfo? EmpressPlayerAvatarDeathHeadField = AccessTools.Field(typeof(PlayerAvatar), "playerDeathHead");

		private static readonly FieldInfo? EmpressPlayerAvatarTumbleField = AccessTools.Field(typeof(PlayerAvatar), "tumble");

		private static readonly FieldInfo? EmpressPlayerDeathHeadPhysGrabObjectField = AccessTools.Field(typeof(PlayerDeathHead), "physGrabObject");

		private static readonly FieldInfo? EmpressPlayerDeathHeadSetupField = AccessTools.Field(typeof(PlayerDeathHead), "setup");

		private static readonly FieldInfo? EmpressPlayerTumblePhysGrabObjectField = AccessTools.Field(typeof(PlayerTumble), "physGrabObject");

		private static readonly FieldInfo? EmpressPlayerTumbleSetupField = AccessTools.Field(typeof(PlayerTumble), "setup");

		private static readonly FieldInfo? EmpressPlayerAvatarSteamIdField = AccessTools.Field(typeof(PlayerAvatar), "steamID");

		private static readonly FieldInfo? EmpressPlayerAvatarPlayerNameField = AccessTools.Field(typeof(PlayerAvatar), "playerName");

		private static readonly FieldInfo? EmpressPlayerAvatarLevelAnimationCompletedField = AccessTools.Field(typeof(PlayerAvatar), "levelAnimationCompleted");

		private static readonly FieldInfo? EmpressRunManagerGameOverField = AccessTools.Field(typeof(RunManager), "gameOver");

		private static readonly FieldInfo? EmpressRunManagerPunField = AccessTools.Field(typeof(RunManager), "runManagerPUN");

		private static readonly FieldInfo? EmpressRunManagerPunPhotonViewField = AccessTools.Field(typeof(RunManagerPUN), "photonView");

		private static readonly FieldInfo? EmpressLevelGeneratorPhotonViewField = AccessTools.Field(typeof(LevelGenerator), "PhotonView");

		private static readonly FieldInfo? EmpressLevelGeneratorDeathHeadPrefabField = AccessTools.Field(typeof(LevelGenerator), "PlayerDeathHeadPrefab");

		private static readonly FieldInfo? EmpressLevelGeneratorTumblePrefabField = AccessTools.Field(typeof(LevelGenerator), "PlayerTumblePrefab");

		private static readonly FieldInfo? EmpressPhotonRemoveFilterField = AccessTools.Field(typeof(PhotonNetwork), "removeFilter");

		private static readonly FieldInfo? EmpressPhotonKeyByteSevenField = AccessTools.Field(typeof(PhotonNetwork), "keyByteSeven");

		private static readonly FieldInfo? EmpressPhotonServerCleanOptionsField = AccessTools.Field(typeof(PhotonNetwork), "ServerCleanOptions");

		private static readonly MethodInfo? EmpressPhotonRaiseEventInternalMethod = AccessTools.Method(typeof(PhotonNetwork), "RaiseEventInternal", (Type[])null, (Type[])null);

		private readonly HashSet<int> _empressHostWaitingActors = new HashSet<int>();

		private readonly HashSet<int> _empressDeferredWaitingRoomActors = new HashSet<int>();

		private readonly HashSet<int> _empressEnemyRegisteredActors = new HashSet<int>();

		private readonly HashSet<int> _empressLiveJoinLevelUpdateSentActors = new HashSet<int>();

		private readonly HashSet<int> _empressLiveJoinBootstrapSentActors = new HashSet<int>();

		private readonly HashSet<int> _empressLiveStateReplayedActors = new HashSet<int>();

		private readonly Dictionary<int, float> _empressLateJoinDetectedAt = new Dictionary<int, float>();

		private readonly Dictionary<int, float> _empressLateJoinReadyAt = new Dictionary<int, float>();

		private readonly Dictionary<int, float> _empressNextSnapByActor = new Dictionary<int, float>();

		private EmpressDelayedSceneChangeRequest? _empressDelayedSceneChange;

		private bool _empressRunningDelayedSceneChange;

		private bool _empressLiveJoinCachePrepared;

		private bool _empressSteamLobbyUnlockApplied;

		private bool _empressRunLevelCachePruned;

		private float _empressNextJoinabilityRefreshTime;

		private float _empressNextSupportAuditTime;

		private string _empressSteamLobbyUnlockRoomName = string.Empty;

		private void Awake()
		{
			((Component)this).gameObject.transform.parent = null;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
		}

		private void OnEnable()
		{
			SceneManager.sceneLoaded += EmpressOnSceneLoaded;
		}

		private void OnDisable()
		{
			SceneManager.sceneLoaded -= EmpressOnSceneLoaded;
		}

		private void Update()
		{
			if (!((Object)(object)EmpressLateJoinPlugin.Instance == (Object)null) && EmpressLateJoinPlugin.Instance.EmpressEnabled.Value)
			{
				if (Time.unscaledTime >= _empressNextJoinabilityRefreshTime)
				{
					EmpressRefreshJoinability();
					_empressNextJoinabilityRefreshTime = Time.unscaledTime + 1f;
				}
				EmpressMaybePruneRunLevelCache();
				EmpressMaintainSupportObjects();
				EmpressMaintainLateJoinState();
				EmpressMaintainWaitingPlayers();
				EmpressProcessDelayedSceneChange();
			}
		}

		internal bool EmpressTryHandleLateJoinSpawn()
		{
			if (!EmpressCanHostLateJoinLogic())
			{
				return false;
			}
			List<PlayerAvatar> list = (from avatar in SemiFunc.PlayerGetList()
				where (Object)(object)avatar != (Object)null && (Object)(object)avatar.photonView != (Object)null && !EmpressGetAvatarSpawned(avatar)
				select avatar).ToList();
			if (list.Count == 0)
			{
				return false;
			}
			foreach (PlayerAvatar item in list)
			{
				int ownerActorNr = item.photonView.OwnerActorNr;
				if (EmpressEnsureRunLevelWaitingRoomMode())
				{
					EmpressBackfillLateJoinerIntoEnemySystems(item);
					if (_empressDeferredWaitingRoomActors.Add(ownerActorNr))
					{
						EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)$"EmpressLateJoin is holding late joiner {EmpressGetAvatarDisplayName(item)} ({ownerActorNr}) outside the active level until the next scene.");
					}
					continue;
				}
				EmpressTrackLateJoin(ownerActorNr);
				EmpressEnsureSupportObjects(item);
				if (EmpressShouldQueueLateJoiner())
				{
					EmpressQueueLateJoiner(item);
				}
				else
				{
					EmpressAdmitLateJoiner(item);
				}
			}
			return true;
		}

		internal void EmpressHandlePlayerLeft(Player player)
		{
			_empressHostWaitingActors.Remove(player.ActorNumber);
			_empressDeferredWaitingRoomActors.Remove(player.ActorNumber);
			_empressEnemyRegisteredActors.Remove(player.ActorNumber);
			_empressLiveJoinLevelUpdateSentActors.Remove(player.ActorNumber);
			_empressLiveJoinBootstrapSentActors.Remove(player.ActorNumber);
			_empressLiveStateReplayedActors.Remove(player.ActorNumber);
			_empressNextSnapByActor.Remove(player.ActorNumber);
			EmpressClearLateJoinTracking(player.ActorNumber);
		}

		internal void EmpressHandlePlayerEntered(Player player)
		{
			if (EmpressCanHostLateJoinLogic() && SemiFunc.RunIsLevel())
			{
				if (EmpressEnsureRunLevelWaitingRoomMode())
				{
					EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)$"Late join detected for {player.NickName} ({player.ActorNumber}); EmpressLateJoin is keeping them in the waiting room until the next scene.");
					return;
				}
				EmpressTrackLateJoin(player.ActorNumber);
				string arg = (EmpressShouldQueueLateJoiner() ? "park them in the Empress holding cell" : "drop them into the truck and replay the live run state");
				EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)$"Late join detected for {player.NickName} ({player.ActorNumber}); EmpressLateJoin will {arg}.");
			}
		}

		internal bool EmpressTryDelaySceneChange(RunManager runManager, bool completedLevel, bool levelFailed, ChangeLevelType changeLevelType)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Invalid comparison between Unknown and I4
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Invalid comparison between Unknown and I4
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			if (_empressRunningDelayedSceneChange || !EmpressCanHostRoomControl() || !SemiFunc.RunIsLevel())
			{
				return false;
			}
			if (EmpressEnsureRunLevelWaitingRoomMode())
			{
				return false;
			}
			if (completedLevel && !levelFailed)
			{
				if (_empressLateJoinDetectedAt.Count > 0)
				{
					EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)("EmpressLateJoin is not delaying the completed-run truck departure. Any unfinished live joiners will be carried forward by the normal level switch: " + EmpressDescribeTrackedLateJoiners() + "."));
				}
				return false;
			}
			if (_empressLateJoinDetectedAt.Count == 0)
			{
				return false;
			}
			if ((int)changeLevelType == 4 || (int)changeLevelType == 3)
			{
				return false;
			}
			if (_empressDelayedSceneChange == null)
			{
				_empressDelayedSceneChange = new EmpressDelayedSceneChangeRequest
				{
					RunManager = runManager,
					CompletedLevel = completedLevel,
					LevelFailed = levelFailed,
					ChangeLevelType = changeLevelType,
					RequestedAt = Time.unscaledTime
				};
				EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)("EmpressLateJoin is delaying the scene switch while late joiners finish syncing: " + EmpressDescribeTrackedLateJoiners() + "."));
			}
			else
			{
				_empressDelayedSceneChange.RunManager = runManager;
				_empressDelayedSceneChange.CompletedLevel = completedLevel;
				_empressDelayedSceneChange.LevelFailed = levelFailed;
				_empressDelayedSceneChange.ChangeLevelType = changeLevelType;
			}
			return true;
		}

		internal void EmpressHandleChangeLevelCompleted(RunManager runManager, ChangeLevelType changeLevelType)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			EmpressPrepareLiveJoinLevelDescriptor(runManager, changeLevelType);
			EmpressEnsureRunLevelWaitingRoomMode();
		}

		private void EmpressOnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			_empressHostWaitingActors.Clear();
			_empressDeferredWaitingRoomActors.Clear();
			_empressEnemyRegisteredActors.Clear();
			_empressLiveJoinLevelUpdateSentActors.Clear();
			_empressLiveJoinBootstrapSentActors.Clear();
			_empressLiveStateReplayedActors.Clear();
			_empressLateJoinDetectedAt.Clear();
			_empressLateJoinReadyAt.Clear();
			_empressNextSnapByActor.Clear();
			_empressDelayedSceneChange = null;
			_empressRunningDelayedSceneChange = false;
			_empressLiveJoinCachePrepared = false;
			_empressSteamLobbyUnlockApplied = false;
			_empressRunLevelCachePruned = false;
			_empressNextJoinabilityRefreshTime = 0f;
			_empressNextSupportAuditTime = 0f;
			_empressSteamLobbyUnlockRoomName = string.Empty;
			EmpressRefreshJoinability();
		}

		private void EmpressRefreshJoinability()
		{
			if (!EmpressCanHostRoomControl())
			{
				_empressSteamLobbyUnlockApplied = false;
				_empressSteamLobbyUnlockRoomName = string.Empty;
			}
			else
			{
				EmpressForceJoinabilityRefresh();
			}
		}

		internal void EmpressForceJoinabilityRefresh(bool forceSteamLobbyUnlock = false)
		{
			if (!EmpressCanHostRoomControl() || !EmpressLateJoinPlugin.Instance.EmpressKeepRoomJoinable.Value)
			{
				_empressSteamLobbyUnlockApplied = false;
				_empressSteamLobbyUnlockRoomName = string.Empty;
				return;
			}
			Room currentRoom = PhotonNetwork.CurrentRoom;
			if (currentRoom == null)
			{
				_empressSteamLobbyUnlockApplied = false;
				_empressSteamLobbyUnlockRoomName = string.Empty;
				return;
			}
			if (!string.Equals(_empressSteamLobbyUnlockRoomName, currentRoom.Name, StringComparison.Ordinal))
			{
				_empressSteamLobbyUnlockRoomName = currentRoom.Name;
				_empressSteamLobbyUnlockApplied = false;
			}
			currentRoom.IsOpen = true;
			if (EmpressGetConnectRandom())
			{
				currentRoom.IsVisible = true;
				_empressSteamLobbyUnlockApplied = false;
			}
			if (EmpressGetConnectRandom() || !((Object)(object)SteamManager.instance != (Object)null) || (_empressSteamLobbyUnlockApplied && !forceSteamLobbyUnlock))
			{
				return;
			}
			try
			{
				SteamManager.instance.UnlockLobby(false);
				_empressSteamLobbyUnlockApplied = true;
			}
			catch (Exception ex)
			{
				_empressSteamLobbyUnlockApplied = false;
				EmpressLateJoinPlugin? instance = EmpressLateJoinPlugin.Instance;
				if (instance != null)
				{
					instance.EmpressLog.LogDebug((object)("EmpressLateJoin could not unlock the Steam lobby yet: " + ex.Message));
				}
			}
		}

		private bool EmpressGetCurrentRunGameOver()
		{
			return EmpressGetFieldValue<bool>(EmpressRunManagerGameOverField, RunManager.instance);
		}

		private bool EmpressLiveJoinEnabled()
		{
			return EmpressLateJoinPlugin.Instance?.EmpressLiveJoinActiveLevels.Value ?? false;
		}

		private void EmpressPrepareLiveJoinLevelDescriptor(RunManager runManager, ChangeLevelType changeLevelType)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			if (EmpressLiveJoinEnabled() && EmpressCanHostRoomControl() && !((Object)(object)runManager == (Object)null) && !((Object)(object)runManager.levelCurrent == (Object)null) && SemiFunc.RunIsLevel())
			{
				PhotonView val = EmpressGetRunManagerPhotonView();
				if (!((Object)(object)val == (Object)null) && val.ViewID > 0)
				{
					PhotonNetwork.RemoveBufferedRPCs(val.ViewID, "UpdateLevelRPC", (int[])null);
					EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)$"EmpressLateJoin removed buffered UpdateLevelRPC for {((Object)runManager.levelCurrent).name} ({changeLevelType}) right after the host level switch so late joiners do not consume the stale room handoff on entry.");
				}
			}
		}

		private void EmpressPrepareLiveJoinRoomState()
		{
			if (!_empressLiveJoinCachePrepared && EmpressLiveJoinEnabled() && EmpressCanHostRoomControl() && SemiFunc.RunIsLevel())
			{
				PhotonView val = EmpressGetFieldValue<PhotonView>(EmpressLevelGeneratorPhotonViewField, LevelGenerator.Instance);
				if (!((Object)(object)val == (Object)null) && val.ViewID > 0)
				{
					PhotonNetwork.RemoveBufferedRPCs(val.ViewID, "GenerateDone", (int[])null);
					_empressLiveJoinCachePrepared = true;
					EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)"EmpressLateJoin removed buffered GenerateDone so live joiners can be staged into Start only after the host decides they are ready.");
				}
			}
		}

		private void EmpressMaybePruneRunLevelCache()
		{
			EmpressPrepareLiveJoinRoomState();
			EmpressEnsureRunLevelWaitingRoomMode();
		}

		private void EmpressMaintainSupportObjects()
		{
			if (!EmpressLiveJoinEnabled() || !EmpressCanHostRoomControl() || (Object)(object)LevelGenerator.Instance == (Object)null || SemiFunc.MenuLevel() || Time.unscaledTime < _empressNextSupportAuditTime)
			{
				return;
			}
			_empressNextSupportAuditTime = Time.unscaledTime + 1f;
			foreach (PlayerAvatar item in SemiFunc.PlayerGetList())
			{
				if (!((Object)(object)item == (Object)null))
				{
					EmpressEnsureSupportObjects(item);
				}
			}
		}

		private void EmpressMaintainLateJoinState()
		{
			if (!EmpressCanHostRoomControl() || _empressLateJoinDetectedAt.Count == 0)
			{
				return;
			}
			float num = Mathf.Max(1f, EmpressLateJoinPlugin.Instance.EmpressSceneSwitchSettleTimeout.Value);
			foreach (int item in _empressLateJoinDetectedAt.Keys.ToList())
			{
				if (!EmpressIsActorStillConnected(item))
				{
					EmpressClearLateJoinTracking(item);
					continue;
				}
				float num2 = _empressLateJoinDetectedAt[item];
				PlayerAvatar val = EmpressFindAvatar(item);
				if ((Object)(object)val == (Object)null || (Object)(object)val.photonView == (Object)null)
				{
					if (Time.unscaledTime - num2 >= num)
					{
						EmpressLateJoinPlugin.Instance.EmpressLog.LogWarning((object)$"EmpressLateJoin timed out waiting for actor {item} to finish spawning on the host.");
						EmpressClearLateJoinTracking(item);
					}
					continue;
				}
				EmpressEnsureSupportObjects(val);
				EmpressBackfillLateJoinerIntoEnemySystems(val);
				EmpressAdvanceLiveJoinBootstrap(val, num2);
				if (EmpressIsLateJoinerSettled(val, num2))
				{
					if (!_empressLateJoinReadyAt.ContainsKey(item))
					{
						_empressLateJoinReadyAt[item] = Time.unscaledTime;
						EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)$"Late joiner {EmpressGetAvatarDisplayName(val)} ({item}) has settled on the host.");
					}
					else if (Time.unscaledTime - _empressLateJoinReadyAt[item] >= 0.2f)
					{
						EmpressClearLateJoinTracking(item);
					}
				}
				else
				{
					_empressLateJoinReadyAt.Remove(item);
					if (Time.unscaledTime - num2 >= num)
					{
						EmpressLateJoinPlugin.Instance.EmpressLog.LogWarning((object)$"EmpressLateJoin waited {num:0.0}s for {EmpressGetAvatarDisplayName(val)} ({item}) to settle and will stop delaying scene switches for them.");
						EmpressClearLateJoinTracking(item);
					}
				}
			}
		}

		private void EmpressAdvanceLiveJoinBootstrap(PlayerAvatar avatar, float detectedAt)
		{
			if (!EmpressLiveJoinEnabled() || (Object)(object)avatar.photonView == (Object)null || !EmpressGetAvatarSpawned(avatar))
			{
				return;
			}
			int ownerActorNr = avatar.photonView.OwnerActorNr;
			if (ownerActorNr <= 0 || PhotonNetwork.CurrentRoom == null || !PhotonNetwork.CurrentRoom.Players.TryGetValue(ownerActorNr, out var value))
			{
				return;
			}
			float num = Time.unscaledTime - detectedAt;
			try
			{
				if (!_empressLiveJoinLevelUpdateSentActors.Contains(ownerActorNr) && num >= 2f)
				{
					if (!EmpressHasLateJoinIdentity(avatar, detectedAt))
					{
						return;
					}
					PhotonView val = EmpressGetRunManagerPhotonView();
					if ((Object)(object)val != (Object)null)
					{
						val.RPC("UpdateLevelRPC", value, new object[3]
						{
							((Object)RunManager.instance.levelCurrent).name,
							RunManager.instance.levelsCompleted,
							EmpressGetCurrentRunGameOver()
						});
						_empressLiveJoinLevelUpdateSentActors.Add(ownerActorNr);
						EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)$"EmpressLateJoin pushed the current level descriptor to {EmpressGetAvatarDisplayName(avatar)} ({ownerActorNr}) after a live-join delay.");
					}
				}
				if (_empressLiveJoinLevelUpdateSentActors.Contains(ownerActorNr) && !_empressLiveJoinBootstrapSentActors.Contains(ownerActorNr) && num >= 3.5f)
				{
					PhotonView val2 = EmpressGetFieldValue<PhotonView>(EmpressLevelGeneratorPhotonViewField, LevelGenerator.Instance);
					if ((Object)(object)val2 != (Object)null)
					{
						val2.RPC("GenerateDone", value, Array.Empty<object>());
						_empressLiveJoinBootstrapSentActors.Add(ownerActorNr);
						EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)$"EmpressLateJoin released {EmpressGetAvatarDisplayName(avatar)} ({ownerActorNr}) into the Start state after a live-join bootstrap delay.");
					}
				}
				if (_empressLiveJoinBootstrapSentActors.Contains(ownerActorNr) && !_empressLiveStateReplayedActors.Contains(ownerActorNr) && num >= 4.5f)
				{
					EmpressReplayStatsToJoiner(value);
					EmpressReplayItemsToJoiner(value);
					EmpressReplayPlayerStateToJoiner(value);
					EmpressReplayRoundStateToJoiner(value);
					_empressLiveStateReplayedActors.Add(ownerActorNr);
					EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)$"EmpressLateJoin replayed live run state to {EmpressGetAvatarDisplayName(avatar)} ({ownerActorNr}).");
				}
			}
			catch (Exception ex)
			{
				EmpressLateJoinPlugin? instance = EmpressLateJoinPlugin.Instance;
				if (instance != null)
				{
					instance.EmpressLog.LogWarning((object)$"EmpressLateJoin could not advance live-join bootstrap for actor {ownerActorNr}: {ex.Message}");
				}
			}
		}

		private bool EmpressHasLateJoinIdentity(PlayerAvatar avatar, float detectedAt)
		{
			if (!EmpressGetAvatarSpawned(avatar))
			{
				return false;
			}
			if (string.IsNullOrWhiteSpace(EmpressGetAvatarSteamId(avatar)) || string.IsNullOrWhiteSpace(EmpressGetAvatarPlayerName(avatar)))
			{
				return false;
			}
			EmpressEnsureSupportObjects(avatar);
			if (!EmpressHasSupportObjectsReady(avatar))
			{
				return false;
			}
			int num = (((Object)(object)avatar.photonView != (Object)null) ? avatar.photonView.OwnerActorNr : 0);
			if (num > 0 && !_empressEnemyRegisteredActors.Contains(num))
			{
				return false;
			}
			return Time.unscaledTime - detectedAt >= 0.5f;
		}

		private void EmpressReplayStatsToJoiner(Player targetPlayer)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			if ((Object)(object)PunManager.instance == (Object)null || (Object)(object)PunManager.instance.photonView == (Object)null || (Object)(object)StatsManager.instance == (Object)null)
			{
				return;
			}
			List<KeyValuePair<string, Dictionary<string, int>>> list = StatsManager.instance.dictionaryOfDictionaries.ToList();
			if (list.Count == 0)
			{
				return;
			}
			Hashtable val = new Hashtable();
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				KeyValuePair<string, Dictionary<string, int>> keyValuePair = list[i];
				val[(object)keyValuePair.Key] = EmpressConvertDictionaryToHashtable(keyValuePair.Value);
				num++;
				if (num >= 4 || i >= list.Count - 1)
				{
					bool flag = i >= list.Count - 1;
					PunManager.instance.photonView.RPC("ReceiveSyncData", targetPlayer, new object[2] { val, flag });
					val = new Hashtable();
					num = 0;
				}
			}
		}

		private void EmpressReplayItemsToJoiner(Player targetPlayer)
		{
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Expected I4, but got Unknown
			if ((Object)(object)PunManager.instance == (Object)null || (Object)(object)PunManager.instance.photonView == (Object)null)
			{
				return;
			}
			foreach (ItemAttributes item in from item in Object.FindObjectsOfType<ItemAttributes>()
				where (Object)(object)item != (Object)null && (Object)(object)item.photonView != (Object)null && item.photonView.ViewID > 0
				orderby item.photonView.ViewID
				select item)
			{
				string text = ((!string.IsNullOrWhiteSpace(item.instanceName)) ? item.instanceName : item.itemAssetName);
				if (!string.IsNullOrWhiteSpace(text))
				{
					PunManager.instance.photonView.RPC("SetItemNameRPC", targetPlayer, new object[2]
					{
						text,
						item.photonView.ViewID
					});
				}
				item.photonView.RPC("GetValueRPC", targetPlayer, new object[1] { item.value });
				if (item.disableUI)
				{
					item.photonView.RPC("DisableUIRPC", targetPlayer, new object[1] { true });
				}
				ItemEquippable itemEquippable = item.itemEquippable;
				if ((Object)(object)itemEquippable != (Object)null && (int)itemEquippable.currentState != 0)
				{
					item.photonView.RPC("RPC_UpdateItemState", targetPlayer, new object[3]
					{
						(int)itemEquippable.currentState,
						itemEquippable.inventorySpotIndex,
						itemEquippable.ownerPlayerId
					});
				}
			}
		}

		private void EmpressReplayPlayerStateToJoiner(Player targetPlayer)
		{
			foreach (PlayerAvatar item in from player in SemiFunc.PlayerGetList()
				where (Object)(object)player != (Object)null && (Object)(object)player.photonView != (Object)null
				select player)
			{
				if ((Object)(object)item.playerHealth != (Object)null)
				{
					item.photonView.RPC("UpdateHealthRPC", targetPlayer, new object[3]
					{
						item.playerHealth.health,
						item.playerHealth.maxHealth,
						false
					});
				}
				if (item.deadSet || item.isDisabled || !((Component)item).gameObject.activeSelf)
				{
					item.photonView.RPC("PlayerDeathRPC", targetPlayer, new object[1] { -1 });
				}
				if ((Object)(object)item.tumble != (Object)null && (Object)(object)item.tumble.photonView != (Object)null && item.tumble.isTumbling)
				{
					item.tumble.photonView.RPC("TumbleSetRPC", targetPlayer, new object[2] { true, false });
					if (item.tumble.tumbleOverride)
					{
						item.tumble.photonView.RPC("TumbleOverrideRPC", targetPlayer, new object[1] { true });
					}
				}
			}
		}

		private void EmpressReplayRoundStateToJoiner(Player targetPlayer)
		{
			if ((Object)(object)PunManager.instance?.photonView != (Object)null)
			{
				if (!string.IsNullOrWhiteSpace(SessionManager.instance?.crownedPlayerSteamID))
				{
					PunManager.instance.photonView.RPC("CrownPlayerRPC", targetPlayer, new object[1] { SessionManager.instance.crownedPlayerSteamID });
				}
				if ((Object)(object)RoundDirector.instance != (Object)null)
				{
					PunManager.instance.photonView.RPC("SyncHaul", targetPlayer, new object[1] { RoundDirector.instance.totalHaul });
				}
				if (SemiFunc.RunIsShop() && (Object)(object)ShopManager.instance != (Object)null)
				{
					PunManager.instance.photonView.RPC("UpdateShoppingCostRPC", targetPlayer, new object[1] { ShopManager.instance.totalCost });
				}
			}
			if (!((Object)(object)RoundDirector.instance?.photonView == (Object)null))
			{
				if (RoundDirector.instance.extractionPointActive && (Object)(object)RoundDirector.instance.extractionPointCurrent != (Object)null && (Object)(object)RoundDirector.instance.extractionPointCurrent.photonView != (Object)null)
				{
					RoundDirector.instance.photonView.RPC("ExtractionPointActivateRPC", targetPlayer, new object[1] { RoundDirector.instance.extractionPointCurrent.photonView.ViewID });
				}
				else
				{
					RoundDirector.instance.photonView.RPC("ExtractionPointsUnlockRPC", targetPlayer, Array.Empty<object>());
				}
				if (RoundDirector.instance.allExtractionPointsCompleted)
				{
					RoundDirector.instance.photonView.RPC("ExtractionCompletedAllRPC", targetPlayer, Array.Empty<object>());
				}
			}
		}

		private void EmpressMaintainWaitingPlayers()
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: 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_00e5: Unknown result type (might be due to invalid IL or missing references)
			if (!EmpressCanHostLateJoinLogic() || _empressHostWaitingActors.Count == 0)
			{
				return;
			}
			foreach (int item3 in _empressHostWaitingActors.ToList())
			{
				PlayerAvatar val = EmpressFindAvatar(item3);
				if ((Object)(object)val == (Object)null || (Object)(object)val.photonView == (Object)null)
				{
					_empressHostWaitingActors.Remove(item3);
					_empressNextSnapByActor.Remove(item3);
				}
				else
				{
					if (!SemiFunc.RunIsLevel())
					{
						continue;
					}
					(Vector3 position, Quaternion rotation) tuple = EmpressGetWaitingSpawn(item3);
					Vector3 item = tuple.position;
					Quaternion item2 = tuple.rotation;
					float num = Mathf.Max(1f, EmpressLateJoinPlugin.Instance.EmpressWaitingLeashRadius.Value);
					if (!(Vector3.Distance(((Component)val).transform.position, item) <= num))
					{
						float value;
						float num2 = (_empressNextSnapByActor.TryGetValue(item3, out value) ? value : 0f);
						if (!(Time.unscaledTime < num2))
						{
							val.Spawn(item, item2);
							_empressNextSnapByActor[item3] = Time.unscaledTime + Mathf.Max(0.1f, EmpressLateJoinPlugin.Instance.EmpressWaitingSnapCooldown.Value);
						}
					}
				}
			}
		}

		private void EmpressProcessDelayedSceneChange()
		{
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			if (_empressDelayedSceneChange == null)
			{
				return;
			}
			EmpressDelayedSceneChangeRequest empressDelayedSceneChange = _empressDelayedSceneChange;
			if ((Object)(object)empressDelayedSceneChange.RunManager == (Object)null || ((object)empressDelayedSceneChange.RunManager).Equals((object?)null))
			{
				_empressDelayedSceneChange = null;
				return;
			}
			float num = Mathf.Max(1f, EmpressLateJoinPlugin.Instance.EmpressSceneSwitchSettleTimeout.Value);
			bool flag = Time.unscaledTime - empressDelayedSceneChange.RequestedAt >= num;
			if (_empressLateJoinDetectedAt.Count > 0 && !flag)
			{
				return;
			}
			if (_empressLateJoinDetectedAt.Count > 0 && flag && !empressDelayedSceneChange.TimeoutLogged)
			{
				empressDelayedSceneChange.TimeoutLogged = true;
				EmpressLateJoinPlugin.Instance.EmpressLog.LogWarning((object)$"EmpressLateJoin is forcing the delayed scene switch after {num:0.0}s because these actors never finished settling: {EmpressDescribeTrackedLateJoiners()}.");
			}
			_empressDelayedSceneChange = null;
			EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)"EmpressLateJoin is replaying the delayed scene switch.");
			_empressRunningDelayedSceneChange = true;
			try
			{
				empressDelayedSceneChange.RunManager.ChangeLevel(empressDelayedSceneChange.CompletedLevel, empressDelayedSceneChange.LevelFailed, empressDelayedSceneChange.ChangeLevelType);
			}
			finally
			{
				_empressRunningDelayedSceneChange = false;
			}
		}

		private void EmpressQueueLateJoiner(PlayerAvatar avatar)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			int ownerActorNr = avatar.photonView.OwnerActorNr;
			_empressHostWaitingActors.Add(ownerActorNr);
			var (val, val2) = EmpressGetWaitingSpawn(ownerActorNr);
			avatar.Spawn(val, val2);
			EmpressRefreshAvatarRoomState(avatar);
			EmpressBackfillLateJoinerIntoEnemySystems(avatar);
			EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)$"Parked late joiner {EmpressGetAvatarDisplayName(avatar)} ({ownerActorNr}) in the Empress holding cell.");
		}

		private void EmpressAdmitLateJoiner(PlayerAvatar avatar)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_0065: 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_005d: 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)
			int ownerActorNr = avatar.photonView.OwnerActorNr;
			_empressHostWaitingActors.Remove(ownerActorNr);
			_empressNextSnapByActor.Remove(ownerActorNr);
			Vector3 val;
			Quaternion val2;
			if (EmpressLiveJoinEnabled() && SemiFunc.RunIsLevel())
			{
				(val, val2) = EmpressGetWaitingSpawn(ownerActorNr);
			}
			else
			{
				(val, val2) = EmpressGetAdmissionSpawn(avatar);
			}
			avatar.Spawn(val, val2);
			EmpressRefreshAvatarRoomState(avatar);
			EmpressBackfillLateJoinerIntoEnemySystems(avatar);
			EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)$"Admitted late joiner {EmpressGetAvatarDisplayName(avatar)} ({ownerActorNr}) directly into the current scene at the truck spawn.");
		}

		private void EmpressEnsureSupportObjects(PlayerAvatar avatar)
		{
			//IL_0091: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)avatar == (Object)null || (Object)(object)LevelGenerator.Instance == (Object)null || SemiFunc.MenuLevel() || !EmpressHasAvatarSteamIdentity(avatar))
			{
				return;
			}
			bool flag = false;
			GameObject val = EmpressGetLevelDeathHeadPrefab();
			PlayerDeathHead val2 = EmpressGetAvatarDeathHead(avatar);
			if ((Object)(object)val2 != (Object)null)
			{
				flag |= EmpressRepairDeathHeadSupport(avatar, val2);
			}
			else if ((Object)(object)val != (Object)null)
			{
				val2 = (GameManager.Multiplayer() ? PhotonNetwork.Instantiate(((Object)val).name, new Vector3(0f, 3000f, 0f), Quaternion.identity, (byte)0, (object[])null) : Object.Instantiate<GameObject>(val, new Vector3(0f, 3000f, 0f), Quaternion.identity)).GetComponent<PlayerDeathHead>();
				if ((Object)(object)val2 != (Object)null)
				{
					val2.playerAvatar = avatar;
					EmpressSetAvatarDeathHead(avatar, val2);
					flag = true;
				}
			}
			GameObject val3 = EmpressGetLevelTumblePrefab();
			PlayerTumble val4 = EmpressGetAvatarTumble(avatar);
			if ((Object)(object)val4 != (Object)null)
			{
				flag |= EmpressRepairTumbleSupport(avatar, val4);
			}
			else if ((Object)(object)val3 != (Object)null)
			{
				val4 = (GameManager.Multiplayer() ? PhotonNetwork.Instantiate(((Object)val3).name, new Vector3(0f, 3000f, 0f), Quaternion.identity, (byte)0, (object[])null) : Object.Instantiate<GameObject>(val3, new Vector3(0f, 3000f, 0f), Quaternion.identity)).GetComponent<PlayerTumble>();
				if ((Object)(object)val4 != (Object)null)
				{
					val4.playerAvatar = avatar;
					EmpressSetAvatarTumble(avatar, val4);
					flag = true;
				}
			}
			if (flag)
			{
				EmpressLateJoinPlugin? instance = EmpressLateJoinPlugin.Instance;
				if (instance != null)
				{
					instance.EmpressLog.LogDebug((object)("EmpressLateJoin prepared support objects for " + EmpressGetAvatarDisplayName(avatar) + "."));
				}
			}
		}

		private bool EmpressRepairDeathHeadSupport(PlayerAvatar avatar, PlayerDeathHead deathHead)
		{
			bool result = false;
			if ((Object)(object)deathHead.playerAvatar != (Object)(object)avatar)
			{
				deathHead.playerAvatar = avatar;
				EmpressSetAvatarDeathHead(avatar, deathHead);
				result = true;
			}
			if (!EmpressGetDeathHeadSetup(deathHead) && (Object)(object)EmpressGetDeathHeadPhysGrabObject(deathHead) != (Object)null)
			{
				try
				{
					deathHead.SetupDone();
					EmpressSetDeathHeadSetup(deathHead, setup: true);
					result = true;
				}
				catch (Exception ex)
				{
					EmpressLateJoinPlugin? instance = EmpressLateJoinPlugin.Instance;
					if (instance != null)
					{
						instance.EmpressLog.LogDebug((object)("EmpressLateJoin could not finish death head setup for " + EmpressGetAvatarDisplayName(avatar) + ": " + ex.Message));
					}
				}
			}
			return result;
		}

		private bool EmpressRepairTumbleSupport(PlayerAvatar avatar, PlayerTumble tumble)
		{
			bool result = false;
			if ((Object)(object)tumble.playerAvatar != (Object)(object)avatar)
			{
				tumble.playerAvatar = avatar;
				EmpressSetAvatarTumble(avatar, tumble);
				result = true;
			}
			if (!EmpressGetTumbleSetup(tumble) && (Object)(object)EmpressGetTumblePhysGrabObject(tumble) != (Object)null)
			{
				try
				{
					tumble.SetupDone();
					result = true;
				}
				catch (Exception ex)
				{
					EmpressLateJoinPlugin? instance = EmpressLateJoinPlugin.Instance;
					if (instance != null)
					{
						instance.EmpressLog.LogDebug((object)("EmpressLateJoin could not finish tumble setup for " + EmpressGetAvatarDisplayName(avatar) + ": " + ex.Message));
					}
				}
			}
			return result;
		}

		private (Vector3 position, Quaternion rotation) EmpressGetAdmissionSpawn(PlayerAvatar lateJoiner)
		{
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			List<SpawnPoint> list = EmpressFindSceneObjects<SpawnPoint>();
			if (list.Count == 0)
			{
				return EmpressGetFallbackSpawn();
			}
			SpawnPoint val = list[0];
			float num = float.NegativeInfinity;
			foreach (SpawnPoint item in list)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				float num2 = float.PositiveInfinity;
				foreach (PlayerAvatar item2 in SemiFunc.PlayerGetList())
				{
					if (!((Object)(object)item2 == (Object)null) && !((Object)(object)item2 == (Object)(object)lateJoiner) && EmpressGetAvatarSpawned(item2) && !((Object)(object)item2.photonView == (Object)null) && !_empressHostWaitingActors.Contains(item2.photonView.OwnerActorNr))
					{
						num2 = Mathf.Min(num2, Vector3.Distance(((Component)item).transform.position, ((Component)item2).transform.position));
					}
				}
				float num3 = (float.IsPositiveInfinity(num2) ? 9999f : num2);
				if (num3 > num)
				{
					num = num3;
					val = item;
				}
			}
			return (((Component)val).transform.position, ((Component)val).transform.rotation);
		}

		private (Vector3 position, Quaternion rotation) EmpressGetWaitingSpawn(int actorNumber)
		{
			//IL_0007: 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_000d: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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_0055: 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_005c: Unknown result type (might be due to invalid IL or missing references)
			(Vector3 position, Quaternion rotation) tuple = EmpressGetWaitingAnchor();
			Vector3 item = tuple.position;
			Quaternion item2 = tuple.rotation;
			float num = (float)actorNumber * 47f;
			float num2 = 1.3f + (float)(actorNumber % 3) * 0.45f;
			Vector3 val = Quaternion.Euler(0f, num, 0f) * new Vector3(0f, 0f, num2);
			return (item + val, item2);
		}

		private (Vector3 position, Quaternion rotation) EmpressGetWaitingAnchor()
		{
			//IL_001a: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			TruckSafetySpawnPoint val = EmpressFindSceneObjects<TruckSafetySpawnPoint>().FirstOrDefault();
			if ((Object)(object)val != (Object)null)
			{
				return (((Component)val).transform.position, ((Component)val).transform.rotation);
			}
			List<SpawnPoint> list = EmpressFindSceneObjects<SpawnPoint>();
			if (list.Count > 0 && (Object)(object)list[0] != (Object)null)
			{
				return (((Component)list[0]).transform.position, ((Component)list[0]).transform.rotation);
			}
			return EmpressGetFallbackSpawn();
		}

		private static (Vector3 position, Quaternion rotation) EmpressGetFallbackSpawn()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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)
			if ((Object)(object)PlayerController.instance != (Object)null)
			{
				return (((Component)PlayerController.instance).transform.position, ((Component)PlayerController.instance).transform.rotation);
			}
			if ((Object)(object)Camera.main != (Object)null)
			{
				return (((Component)Camera.main).transform.position, ((Component)Camera.main).transform.rotation);
			}
			return (Vector3.zero, Quaternion.identity);
		}

		private static PlayerAvatar? EmpressFindAvatar(int actorNumber)
		{
			return ((IEnumerable<PlayerAvatar>)SemiFunc.PlayerGetList()).FirstOrDefault((Func<PlayerAvatar, bool>)((PlayerAvatar player) => (Object)(object)player != (Object)null && (Object)(object)player.photonView != (Object)null && player.photonView.OwnerActorNr == actorNumber));
		}

		private bool EmpressShouldQueueLateJoiner()
		{
			if (!SemiFunc.RunIsLevel())
			{
				return false;
			}
			if (EmpressLiveJoinEnabled())
			{
				return false;
			}
			return EmpressLateJoinPlugin.Instance.EmpressQueueDuringActiveLevels.Value;
		}

		private void EmpressTrackLateJoin(int actorNumber)
		{
			if (!_empressLateJoinDetectedAt.ContainsKey(actorNumber))
			{
				_empressLateJoinDetectedAt[actorNumber] = Time.unscaledTime;
			}
			_empressDeferredWaitingRoomActors.Remove(actorNumber);
			_empressEnemyRegisteredActors.Remove(actorNumber);
			_empressLiveJoinLevelUpdateSentActors.Remove(actorNumber);
			_empressLiveJoinBootstrapSentActors.Remove(actorNumber);
			_empressLiveStateReplayedActors.Remove(actorNumber);
			_empressLateJoinReadyAt.Remove(actorNumber);
		}

		private void EmpressClearLateJoinTracking(int actorNumber)
		{
			_empressLateJoinDetectedAt.Remove(actorNumber);
			_empressLateJoinReadyAt.Remove(actorNumber);
		}

		private bool EmpressEnsureRunLevelWaitingRoomMode()
		{
			if (EmpressLiveJoinEnabled())
			{
				return false;
			}
			if (_empressRunLevelCachePruned)
			{
				return true;
			}
			if (!EmpressCanHostRoomControl() || !SemiFunc.RunIsLevel())
			{
				return false;
			}
			if ((Object)(object)EmpressGetRunManagerPhotonView() == (Object)null)
			{
				return false;
			}
			int num = EmpressPruneBufferedRunLevelState();
			if (num <= 0)
			{
				return false;
			}
			_empressRunLevelCachePruned = true;
			_empressHostWaitingActors.Clear();
			_empressLateJoinDetectedAt.Clear();
			_empressLateJoinReadyAt.Clear();
			_empressDelayedSceneChange = null;
			EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)$"EmpressLateJoin pruned buffered active-level state for {num} Photon views. New joiners will wait for the next scene instead of loading into the live run.");
			return true;
		}

		private bool EmpressIsLateJoinerSettled(PlayerAvatar avatar, float detectedAt)
		{
			if (!EmpressGetAvatarSpawned(avatar))
			{
				return false;
			}
			if (string.IsNullOrWhiteSpace(EmpressGetAvatarSteamId(avatar)) || string.IsNullOrWhiteSpace(EmpressGetAvatarPlayerName(avatar)))
			{
				return false;
			}
			if (!EmpressHasSupportObjectsReady(avatar))
			{
				return false;
			}
			int num = (((Object)(object)avatar.photonView != (Object)null) ? avatar.photonView.OwnerActorNr : 0);
			if (num > 0 && !_empressEnemyRegisteredActors.Contains(num))
			{
				return false;
			}
			float num2 = Time.unscaledTime - detectedAt;
			if (num2 < 0.5f)
			{
				return false;
			}
			if (EmpressGetAvatarLevelAnimationCompleted(avatar))
			{
				return true;
			}
			return num2 >= Mathf.Max(0.75f, EmpressLateJoinPlugin.Instance.EmpressSceneSwitchSettleDelay.Value);
		}

		private void EmpressBackfillLateJoinerIntoEnemySystems(PlayerAvatar avatar)
		{
			if ((Object)(object)avatar.photonView == (Object)null)
			{
				return;
			}
			int ownerActorNr = avatar.photonView.OwnerActorNr;
			int viewID = avatar.photonView.ViewID;
			if (ownerActorNr <= 0 || viewID <= 0 || _empressEnemyRegisteredActors.Contains(ownerActorNr))
			{
				return;
			}
			HashSet<Enemy> hashSet = new HashSet<Enemy>(from enemy in EmpressFindSceneObjects<Enemy>()
				where (Object)(object)enemy != (Object)null
				select enemy);
			if (hashSet.Count == 0)
			{
				return;
			}
			int num = 0;
			foreach (Enemy item in hashSet)
			{
				try
				{
					item.PlayerAdded(viewID);
					num++;
				}
				catch (Exception ex)
				{
					EmpressLateJoinPlugin? instance = EmpressLateJoinPlugin.Instance;
					if (instance != null)
					{
						instance.EmpressLog.LogDebug((object)$"EmpressLateJoin could not backfill {EmpressGetAvatarDisplayName(avatar)} ({ownerActorNr}) into enemy {((Object)item).name}: {ex.Message}");
					}
				}
			}
			_empressEnemyRegisteredActors.Add(ownerActorNr);
			EmpressLateJoinPlugin.Instance.EmpressLog.LogInfo((object)$"EmpressLateJoin backfilled {EmpressGetAvatarDisplayName(avatar)} ({ownerActorNr}) into {num} enemy trackers.");
		}

		private static void EmpressRefreshAvatarRoomState(PlayerAvatar avatar)
		{
			try
			{
				RoomVolumeCheck roomVolumeCheck = avatar.RoomVolumeCheck;
				if (roomVolumeCheck != null)
				{
					roomVolumeCheck.CheckSet();
				}
			}
			catch
			{
			}
		}

		private int EmpressPruneBufferedRunLevelState()
		{
			int num = 0;
			PhotonView val = EmpressGetRunManagerPhotonView();
			if ((Object)(object)val != (Object)null)
			{
				PhotonNetwork.RemoveBufferedRPCs(val.ViewID, "UpdateLevelRPC", (int[])null);
				num++;
			}
			PhotonView val2 = EmpressGetFieldValue<PhotonView>(EmpressLevelGeneratorPhotonViewField, LevelGenerator.Instance);
			if ((Object)(object)val2 != (Object)null)
			{
				PhotonNetwork.RemoveBufferedRPCs(val2.ViewID, (string)null, (int[])null);
				num++;
			}
			return num;
		}

		private void EmpressRemoveInstantiationCache(PhotonView photonView)
		{
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: 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_003f: Expected O, but got Unknown
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			if (photonView.InstantiationId == 0 || EmpressPhotonRaiseEventInternalMethod == null)
			{
				return;
			}
			object obj = EmpressPhotonKeyByteSevenField?.GetValue(null);
			if (obj == null)
			{
				return;
			}
			Hashtable val = EmpressGetFieldValue<Hashtable>(EmpressPhotonRemoveFilterField, null);
			Hashtable val2 = new Hashtable();
			if (val != null)
			{
				DictionaryEntryEnumerator enumerator = val.GetEnumerator();
				try
				{
					while (((DictionaryEntryEnumerator)(ref enumerator)).MoveNext())
					{
						DictionaryEntry current = ((DictionaryEntryEnumerator)(ref enumerator)).Current;
						val2[current.Key] = current.Value;
					}
				}
				finally
				{
					((IDisposable)(DictionaryEntryEnumerator)(ref enumerator)).Dispose();
				}
			}
			val2[obj] = photonView.InstantiationId;
			RaiseEventOptions val3 = EmpressGetFieldValue<RaiseEventOptions>(EmpressPhotonServerCleanOptionsField, null);
			EventCaching cachingOption = (EventCaching)((val3 != null) ? ((int)val3.CachingOption) : 0);
			RaiseEventOptions val4 = (RaiseEventOptions)(((object)val3) ?? ((object)new RaiseEventOptions()));
			val4.CachingOption = (EventCaching)6;
			try
			{
				EmpressPhotonRaiseEventInternalMethod.Invoke(null, new object[4]
				{
					(byte)202,
					val2,
					val4,
					SendOptions.SendReliable
				});
			}
			catch (Exception ex)
			{
				EmpressLateJoinPlugin? instance = EmpressLateJoinPlugin.Instance;
				if (instance != null)
				{
					instance.EmpressLog.LogDebug((object)("EmpressLateJoin could not clear instantiation cache for " + ((Object)photonView).name + ": " + ex.Message));
				}
			}
			finally
			{
				if (val3 != null)
				{
					val3.CachingOption = cachingOption;
				}
			}
		}

		private static PhotonView? EmpressGetRunManagerPhotonView()
		{
			RunManagerPUN val = EmpressGetFieldValue<RunManagerPUN>(EmpressRunManagerPunField, RunManager.instance);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			return EmpressGetFieldValue<PhotonView>(EmpressRunManagerPunPhotonViewField, val);
		}

		private static bool EmpressIsActorStillConnected(int actorNumber)
		{
			if (!PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null)
			{
				return false;
			}
			return PhotonNetwork.CurrentRoom.Players.ContainsKey(actorNumber);
		}

		private string EmpressDescribeTrackedLateJoiners()
		{
			List<string> list = new List<string>();
			foreach (int item in _empressLateJoinDetectedAt.Keys.OrderBy((int value) => value))
			{
				PlayerAvatar val = EmpressFindAvatar(item);
				Player value2;
				if ((Object)(object)val != (Object)null)
				{
					list.Add($"{EmpressGetAvatarDisplayName(val)} ({item})");
				}
				else if (PhotonNetwork.CurrentRoom != null && PhotonNetwork.CurrentRoom.Players.TryGetValue(item, out value2))
				{
					list.Add($"{value2.NickName} ({item})");
				}
				else
				{
					list.Add(item.ToString());
				}
			}
			if (list.Count != 0)
			{
				return string.Join(", ", list);
			}
			return "unknown players";
		}

		private static bool EmpressCanHostRoomControl()
		{
			if ((Object)(object)EmpressLateJoinPlugin.Instance == (Object)null || !EmpressLateJoinPlugin.Instance.EmpressEnabled.Value)
			{
				return false;
			}
			if ((Object)(object)GameManager.instance == (Object)null || !PhotonNetwork.InRoom || !GameManager.Multiplayer() || !PhotonNetwork.IsMasterClient)
			{
				return false;
			}
			return true;
		}

		private static bool EmpressCanHostLateJoinLogic()
		{
			if (!EmpressCanHostRoomControl())
			{
				return false;
			}
			if ((Object)(object)GameDirector.instance == (Object)null || (Object)(object)LevelGenerator.Instance == (Object)null)
			{
				return false;
			}
			return SemiFunc.LevelGenDone();
		}

		private static bool EmpressGetConnectRandom()
		{
			return EmpressGetFieldValue<bool>(EmpressGameManagerConnectRandomField, GameManager.instance);
		}

		private static bool EmpressGetAvatarSpawned(PlayerAvatar avatar)
		{
			return EmpressGetFieldValue<bool>(EmpressPlayerAvatarSpawnedField, avatar);
		}

		private static string EmpressGetAvatarSteamId(PlayerAvatar avatar)
		{
			return EmpressGetFieldValue<string>(EmpressPlayerAvatarSteamIdField, avatar) ?? string.Empty;
		}

		private static bool EmpressHasAvatarSteamIdentity(PlayerAvatar avatar)
		{
			if ((Object)(object)avatar != (Object)null)
			{
				return !string.IsNullOrWhiteSpace(EmpressGetAvatarSteamId(avatar));
			}
			return false;
		}

		private static string EmpressGetAvatarPlayerName(PlayerAvatar avatar)
		{
			return EmpressGetFieldValue<string>(EmpressPlayerAvatarPlayerNameField, avatar) ?? string.Empty;
		}

		private static bool EmpressGetAvatarLevelAnimationCompleted(PlayerAvatar avatar)
		{
			return EmpressGetFieldValue<bool>(EmpressPlayerAvatarLevelAnimationCompletedField, avatar);
		}

		private static bool EmpressHasSupportObjectsReady(PlayerAvatar avatar)
		{
			PlayerDeathHead val = EmpressGetAvatarDeathHead(avatar);
			if ((Object)(object)val == (Object)null || (Object)(object)val.playerAvatar != (Object)(object)avatar || !EmpressGetDeathHeadSetup(val))
			{
				return false;
			}
			PlayerTumble val2 = EmpressGetAvatarTumble(avatar);
			if ((Object)(object)val2 != (Object)null && (Object)(object)val2.playerAvatar == (Object)(object)avatar)
			{
				return EmpressGetTumbleSetup(val2);
			}
			return false;
		}

		private static PlayerDeathHead? EmpressGetAvatarDeathHead(PlayerAvatar avatar)
		{
			return EmpressGetFieldValue<PlayerDeathHead>(EmpressPlayerAvatarDeathHeadField, avatar);
		}

		private static void EmpressSetAvatarDeathHead(PlayerAvatar avatar, PlayerDeathHead deathHead)
		{
			EmpressSetFieldValue(EmpressPlayerAvatarDeathHeadField, avatar, deathHead);
		}

		private static PlayerTumble? EmpressGetAvatarTumble(PlayerAvatar avatar)
		{
			return EmpressGetFieldValue<PlayerTumble>(EmpressPlayerAvatarTumbleField, avatar);
		}

		private static void EmpressSetAvatarTumble(PlayerAvatar avatar, PlayerTumble tumble)
		{
			EmpressSetFieldValue(EmpressPlayerAvatarTumbleField, avatar, tumble);
		}

		private static bool EmpressGetDeathHeadSetup(PlayerDeathHead deathHead)
		{
			return EmpressGetFieldValue<bool>(EmpressPlayerDeathHeadSetupField, deathHead);
		}

		private static PhysGrabObject? EmpressGetDeathHeadPhysGrabObject(PlayerDeathHead deathHead)
		{
			return EmpressGetFieldValue<PhysGrabObject>(EmpressPlayerDeathHeadPhysGrabObjectField, deathHead);
		}

		private static void EmpressSetDeathHeadSetup(PlayerDeathHead deathHead, bool setup)
		{
			EmpressSetFieldValue(EmpressPlayerDeathHeadSetupField, deathHead, setup);
		}

		private static PhysGrabObject? EmpressGetTumblePhysGrabObject(PlayerTumble tumble)
		{
			return EmpressGetFieldValue<PhysGrabObject>(EmpressPlayerTumblePhysGrabObjectField, tumble);
		}

		private static bool EmpressGetTumbleSetup(PlayerTumble tumble)
		{
			return EmpressGetFieldValue<bool>(EmpressPlayerTumbleSetupField, tumble);
		}

		private static GameObject? EmpressGetLevelDeathHeadPrefab()
		{
			return EmpressGetFieldValue<GameObject>(EmpressLevelGeneratorDeathHeadPrefabField, LevelGenerator.Instance);
		}

		private static GameObject? EmpressGetLevelTumblePrefab()
		{
			return EmpressGetFieldValue<GameObject>(EmpressLevelGeneratorTumblePrefabField, LevelGenerator.Instance);
		}

		private static List<T> EmpressFindSceneObjects<T>() where T : Object
		{
			//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_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			List<T> list = new List<T>();
			T[] array = Resources.FindObjectsOfTypeAll<T>();
			foreach (T val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				T val2 = val;
				object obj = val2;
				Component val3 = (Component)((obj is Component) ? obj : null);
				Scene scene;
				if (val3 == null)
				{
					object obj2 = val2;
					GameObject val4 = (GameObject)((obj2 is GameObject) ? obj2 : null);
					if (val4 == null)
					{
						continue;
					}
					GameObject val5 = val4;
					scene = val5.scene;
					if (((Scene)(ref scene)).IsValid())
					{
						scene = val5.scene;
						if (!string.IsNullOrWhiteSpace(((Scene)(ref scene)).name))
						{
							list.Add(val);
						}
					}
					continue;
				}
				Component val6 = val3;
				scene = val6.gameObject.scene;
				if (((Scene)(ref scene)).IsValid())
				{
					scene = val6.gameObject.scene;
					if (!string.IsNullOrWhiteSpace(((Scene)(ref scene)).name))
					{
						list.Add(val);
					}
				}
			}
			return list;
		}

		private static string EmpressGetAvatarDisplayName(PlayerAvatar avatar)
		{
			PhotonView photonView = avatar.photonView;
			if (((photonView != null) ? photonView.Owner : null) != null && !string.IsNullOrWhiteSpace(avatar.photonView.Owner.NickName))
			{
				return avatar.photonView.Owner.NickName;
			}
			string text = EmpressGetAvatarPlayerName(avatar);
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			return ((Object)avatar).name;
		}

		private static Hashtable EmpressConvertDictionaryToHashtable(Dictionary<string, int> dictionary)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			Hashtable val = new Hashtable();
			foreach (KeyValuePair<string, int> item in dictionary)
			{
				val[(object)item.Key] = item.Value;
			}
			return val;
		}

		private static T EmpressGetFieldValue<T>(FieldInfo? fieldInfo, object? instance)
		{
			if (fieldInfo == null || (!fieldInfo.IsStatic && instance == null))
			{
				return default(T);
			}
			try
			{
				object value = fieldInfo.GetValue(fieldInfo.IsStatic ? null : instance);
				if (value is T)
				{
					return (T)value;
				}
			}
			catch
			{
			}
			return default(T);
		}

		private static void EmpressSetFieldValue(FieldInfo? fieldInfo, object? instance, object? value)
		{
			if (fieldInfo == null || (!fieldInfo.IsStatic && instance == null))
			{
				return;
			}
			try
			{
				fieldInfo.SetValue(fieldInfo.IsStatic ? null : instance, value);
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(LevelGenerator), "PlayerSpawn")]
	internal static class EmpressLateJoinLevelGeneratorPatch
	{
		private static bool Prefix()
		{
			return !(EmpressLateJoinPlugin.Runtime?.EmpressTryHandleLateJoinSpawn() ?? false);
		}
	}
	[HarmonyPatch(typeof(NetworkManager), "OnPlayerEnteredRoom")]
	internal static class EmpressLateJoinPlayerEnteredPatch
	{
		private static void Postfix(Player newPlayer)
		{
			EmpressLateJoinPlugin.Runtime?.EmpressHandlePlayerEntered(newPlayer);
		}
	}
	[HarmonyPatch(typeof(NetworkManager), "OnPlayerLeftRoom")]
	internal static class EmpressLateJoinPlayerLeftPatch
	{
		private static void Postfix(Player otherPlayer)
		{
			EmpressLateJoinPlugin.Runtime?.EmpressHandlePlayerLeft(otherPlayer);
		}
	}
	[HarmonyPatch(typeof(MenuPageLobby), "ButtonStart")]
	internal static class EmpressLateJoinLobbyStartPatch
	{
		private static void Postfix()
		{
			EmpressLateJoinPlugin.Runtime?.EmpressForceJoinabilityRefresh(forceSteamLobbyUnlock: true);
			EmpressLateJoinPlugin? instance = EmpressLateJoinPlugin.Instance;
			if (instance != null)
			{
				instance.EmpressLog.LogInfo((object)"EmpressLateJoin reopened the room immediately after the host pressed Start.");
			}
		}
	}
	[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
	internal static class EmpressLateJoinChangeLevelPatch
	{
		private static bool Prefix(RunManager __instance, bool _completedLevel, bool _levelFailed, ChangeLevelType _changeLevelType)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			return !(EmpressLateJoinPlugin.Runtime?.EmpressTryDelaySceneChange(__instance, _completedLevel, _levelFailed, _changeLevelType) ?? false);
		}

		private static void Postfix(RunManager __instance, ChangeLevelType _changeLevelType)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			EmpressLateJoinPlugin.Runtime?.EmpressHandleChangeLevelCompleted(__instance, _changeLevelType);
		}
	}
	internal static class EmpressLateJoinGrabGuard
	{
		internal static bool TryResolveGrabber(int playerPhotonId, string rpcName)
		{
			if (playerPhotonId <= 0)
			{
				EmpressLateJoinPlugin? instance = EmpressLateJoinPlugin.Instance;
				if (instance != null)
				{
					instance.EmpressLog.LogDebug((object)("EmpressLateJoin ignored " + rpcName + " because it referenced an invalid player grabber view id."));
				}
				return false;
			}
			PhotonView val = PhotonView.Find(playerPhotonId);
			if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).GetComponent<PhysGrabber>() != (Object)null)
			{
				return true;
			}
			EmpressLateJoinPlugin? instance2 = EmpressLateJoinPlugin.Instance;
			if (instance2 != null)
			{
				instance2.EmpressLog.LogDebug((object)$"EmpressLateJoin ignored {rpcName} because player grabber view {playerPhotonId} is not available on the host yet.");
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "GrabLinkRPC", new Type[]
	{
		typeof(int),
		typeof(int),
		typeof(Vector3),
		typeof(Vector3),
		typeof(Vector3)
	})]
	internal static class EmpressLateJoinPhysGrabObjectGrabLinkPatch
	{
		private static bool Prefix(PhysGrabObject __instance, int playerPhotonID)
		{
			return EmpressLateJoinGrabGuard.TryResolveGrabber(playerPhotonID, "PhysGrabObject.GrabLinkRPC on " + ((Object)__instance).name);
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "GrabPointSyncRPC", new Type[]
	{
		typeof(int),
		typeof(Vector3)
	})]
	internal static class EmpressLateJoinPhysGrabObjectGrabPointSyncPatch
	{
		private static bool Prefix(PhysGrabObject __instance, int playerPhotonID)
		{
			return EmpressLateJoinGrabGuard.TryResolveGrabber(playerPhotonID, "PhysGrabObject.GrabPointSyncRPC on " + ((Object)__instance).name);
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "GrabStartedRPC", new Type[]
	{
		typeof(int),
		typeof(PhotonMessageInfo)
	})]
	internal static class EmpressLateJoinPhysGrabObjectGrabStartedPatch
	{
		private static bool Prefix(PhysGrabObject __instance, int playerPhotonID)
		{
			return EmpressLateJoinGrabGuard.TryResolveGrabber(playerPhotonID, "PhysGrabObject.GrabStartedRPC on " + ((Object)__instance).name);
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "GrabPlayerAddRPC", new Type[] { typeof(int) })]
	internal static class EmpressLateJoinPhysGrabObjectGrabPlayerAddPatch
	{
		private static bool Prefix(PhysGrabObject __instance, int photonViewID)
		{
			return EmpressLateJoinGrabGuard.TryResolveGrabber(photonViewID, "PhysGrabObject.GrabPlayerAddRPC on " + ((Object)__instance).name);
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "GrabPlayerRemoveRPC", new Type[] { typeof(int) })]
	internal static class EmpressLateJoinPhysGrabObjectGrabPlayerRemovePatch
	{
		private static bool Prefix(PhysGrabObject __instance, int photonViewID)
		{
			return EmpressLateJoinGrabGuard.TryResolveGrabber(photonViewID, "PhysGrabObject.GrabPlayerRemoveRPC on " + ((Object)__instance).name);
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "GrabEndedRPC", new Type[]
	{
		typeof(int),
		typeof(PhotonMessageInfo)
	})]
	internal static class EmpressLateJoinPhysGrabObjectGrabEndedPatch
	{
		private static bool Prefix(PhysGrabObject __instance, int playerPhotonID)
		{
			return EmpressLateJoinGrabGuard.TryResolveGrabber(playerPhotonID, "PhysGrabObject.GrabEndedRPC on " + ((Object)__instance).name);
		}
	}
	[HarmonyPatch(typeof(StaticGrabObject), "GrabLinkRPC", new Type[]
	{
		typeof(int),
		typeof(Vector3)
	})]
	internal static class EmpressLateJoinStaticGrabObjectGrabLinkPatch
	{
		private static bool Prefix(StaticGrabObject __instance, int playerPhotonID)
		{
			return EmpressLateJoinGrabGuard.TryResolveGrabber(playerPhotonID, "StaticGrabObject.GrabLinkRPC on " + ((Object)__instance).name);
		}
	}
	[HarmonyPatch(typeof(StaticGrabObject), "GrabPointSyncRPC", new Type[]
	{
		typeof(int),
		typeof(Vector3)
	})]
	internal static class EmpressLateJoinStaticGrabObjectGrabPointSyncPatch
	{
		private static bool Prefix(StaticGrabObject __instance, int playerPhotonID)
		{
			return EmpressLateJoinGrabGuard.TryResolveGrabber(playerPhotonID, "StaticGrabObject.GrabPointSyncRPC on " + ((Object)__instance).name);
		}
	}
	[HarmonyPatch(typeof(StaticGrabObject), "GrabStartedRPC", new Type[] { typeof(int) })]
	internal static class EmpressLateJoinStaticGrabObjectGrabStartedPatch
	{
		private static bool Prefix(StaticGrabObject __instance, int playerPhotonID)
		{
			return EmpressLateJoinGrabGuard.TryResolveGrabber(playerPhotonID, "StaticGrabObject.GrabStartedRPC on " + ((Object)__instance).name);
		}
	}
	[HarmonyPatch(typeof(StaticGrabObject), "GrabEndedRPC", new Type[] { typeof(int) })]
	internal static class EmpressLateJoinStaticGrabObjectGrabEndedPatch
	{
		private static bool Prefix(StaticGrabObject __instance, int playerPhotonID)
		{
			return EmpressLateJoinGrabGuard.TryResolveGrabber(playerPhotonID, "StaticGrabObject.GrabEndedRPC on " + ((Object)__instance).name);
		}
	}
}