Decompiled source of LateJoinFixed v1.3.0

LateJoinFixed.dll

Decompiled 13 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using Zichen_LateJoinNow.Common;
using Zichen_LateJoinNow.I18N;

[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("LateJoinFixed")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyInformationalVersion("1.3.0")]
[assembly: AssemblyProduct("LateJoinFixed")]
[assembly: AssemblyTitle("LateJoinFixed")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Zichen_LateJoinNow
{
	internal static class CompatibilityReport
	{
		public static FieldInfo PhotonRemoveFilter;

		public static FieldInfo PhotonKeyByteSeven;

		public static FieldInfo PhotonServerCleanOptions;

		public static MethodInfo PhotonRaiseEventInternal;

		public static FieldInfo GameManagerConnectRandomField;

		public static FieldInfo GameManagerLobbyTypeField;

		public static FieldRef<GameManager, LobbyTypes> GameManagerLobbyTypeRef;

		public static FieldRef<GameManager, bool> GameManagerConnectRandomRef;

		public static FieldRef<RunManager, RunManagerPUN> RunManagerPunRef;

		public static FieldRef<RunManagerPUN, PhotonView> RunManagerPunPhotonViewRef;

		public static FieldRef<PlayerAvatar, bool> PlayerAvatarSpawnedRef;

		public static FieldRef<MenuManager, bool> MenuPagePopUpScheduledRef;

		public static FieldRef<MenuManager, MenuPageIndex> MenuCurrentMenuPageIndexRef;

		public static FieldRef<MenuManager, MenuPage> MenuCurrentMenuPageRef;

		public static bool CanCleanPhotonCache;

		public static bool CanRefreshLevelGenerator;

		public static bool CanCatchupLateJoiner;

		public static bool CanShowMenuPopup;

		public static bool CanReadRunManagerPun;

		public static bool CanReadGameManagerLobbyState;

		public static bool CanAddRoomNamePrefix;

		public static bool CanFixGumballOverflow;

		public static bool CanGuardLoadingAnimation;

		public static bool CanEnsurePlayerCorpse;

		public static bool CanSyncModuleState;

		public static bool CanSyncValuableObjectState;

		private static readonly BindingFlags NPS = BindingFlags.Static | BindingFlags.NonPublic;

		private static readonly BindingFlags NPI = BindingFlags.Instance | BindingFlags.NonPublic;

		private static readonly BindingFlags ALL = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		public static bool HasIssues { get; private set; }

		public static bool RuntimeReady { get; private set; }

		public static List<string> AffectedFeatures { get; } = new List<string>();

		public static List<string> Details { get; } = new List<string>();

		public static void Build()
		{
			HasIssues = false;
			RuntimeReady = false;
			AffectedFeatures.Clear();
			Details.Clear();
			CanCleanPhotonCache = TryAll(() => RF(typeof(PhotonNetwork), "removeFilter", NPS, ref PhotonRemoveFilter), () => RF(typeof(PhotonNetwork), "keyByteSeven", NPS, ref PhotonKeyByteSeven), () => RF(typeof(PhotonNetwork), "ServerCleanOptions", NPS, ref PhotonServerCleanOptions), () => RM(typeof(PhotonNetwork), "RaiseEventInternal", new Type[4]
			{
				typeof(byte),
				typeof(object),
				typeof(RaiseEventOptions),
				typeof(SendOptions)
			}, NPS, ref PhotonRaiseEventInternal));
			if (!CanCleanPhotonCache)
			{
				AffectedFeatures.Add("Photon instantiation cache cleanup");
			}
			RF(typeof(GameManager), "connectRandom", NPI, ref GameManagerConnectRandomField);
			RF(typeof(GameManager), "lobbyType", NPI, ref GameManagerLobbyTypeField);
			try
			{
				if (GameManagerLobbyTypeField != null)
				{
					GameManagerLobbyTypeRef = AccessTools.FieldRefAccess<GameManager, LobbyTypes>("lobbyType");
				}
				if (GameManagerConnectRandomField != null)
				{
					GameManagerConnectRandomRef = AccessTools.FieldRefAccess<GameManager, bool>("connectRandom");
				}
			}
			catch (Exception ex)
			{
				Details.Add("GameManager FieldRef failed: " + ex.Message);
			}
			CanReadRunManagerPun = TryAll(() => RFR<RunManager, RunManagerPUN>("runManagerPUN", out RunManagerPunRef), () => RFR<RunManagerPUN, PhotonView>("photonView", out RunManagerPunPhotonViewRef));
			if (!CanReadRunManagerPun)
			{
				AffectedFeatures.Add("RunManager buffered RPC cleanup");
			}
			CanRefreshLevelGenerator = typeof(LevelGenerator) != null;
			CanCatchupLateJoiner = TryAll(() => RFR<PlayerAvatar, bool>("spawned", out PlayerAvatarSpawnedRef), () => RME(typeof(PlayerAvatar), "LoadingLevelAnimationCompletedRPC"));
			if (!CanCatchupLateJoiner)
			{
				AffectedFeatures.Add("Late joiner load-animation catch-up");
			}
			CanShowMenuPopup = TryAll(() => RFR<MenuManager, bool>("pagePopUpScheduled", out MenuPagePopUpScheduledRef), () => RFR<MenuManager, MenuPageIndex>("currentMenuPageIndex", out MenuCurrentMenuPageIndexRef), () => RFR<MenuManager, MenuPage>("currentMenuPage", out MenuCurrentMenuPageRef));
			if (!CanShowMenuPopup)
			{
				AffectedFeatures.Add("Menu popup notifications");
			}
			CanReadGameManagerLobbyState = GameManagerConnectRandomField != null && GameManagerLobbyTypeField != null;
			if (!CanReadGameManagerLobbyState)
			{
				AffectedFeatures.Add("Room type detection");
			}
			CanAddRoomNamePrefix = Check(typeof(DataDirector), "networkServerName", "Public room name [Late Join] prefix");
			CanFixGumballOverflow = HasField(typeof(GumballValuable), "hypnosisLines") && HasField(typeof(GumballValuable), "hypnosisLine");
			if (!CanFixGumballOverflow)
			{
				AffectedFeatures.Add("GumballValuable overflow fix");
			}
			CanGuardLoadingAnimation = HasField(typeof(LoadingUI), "levelAnimationStarted") && HasField(typeof(LoadingUI), "levelAnimationCompleted") && HasField(typeof(PlayerAvatar), "levelAnimationCompleted");
			if (!CanGuardLoadingAnimation)
			{
				AffectedFeatures.Add("Late join loading screen guard");
			}
			CanEnsurePlayerCorpse = HasField(typeof(PlayerAvatar), "playerDeathHead") && HasField(typeof(PlayerAvatar), "tumble");
			if (!CanEnsurePlayerCorpse)
			{
				AffectedFeatures.Add("Late joiner death head / tumble fallback");
			}
			CanSyncModuleState = HasField(typeof(Module), "ConnectingTop") && HasField(typeof(Module), "ConnectingBottom") && HasField(typeof(Module), "ConnectingRight") && HasField(typeof(Module), "ConnectingLeft") && HasField(typeof(Module), "First") && HasField(typeof(Module), "SetupDone");
			if (!CanSyncModuleState)
			{
				AffectedFeatures.Add("Late joiner walls / room interior sync");
			}
			CanSyncValuableObjectState = HasField(typeof(ValuableObject), "dollarValueSet") && HasField(typeof(ValuableObject), "dollarValueCurrent") && HasField(typeof(ValuableObject), "discovered") && HasField(typeof(ValuableObject), "inStartRoom");
			if (!CanSyncValuableObjectState)
			{
				AffectedFeatures.Add("Late joiner valuable state sync");
			}
			HasIssues = AffectedFeatures.Count > 0;
			RuntimeReady = true;
		}

		private static bool HasField(Type t, string name)
		{
			return AccessTools.Field(t, name) != null;
		}

		private static bool Check(Type t, string name, string feature)
		{
			if (HasField(t, name))
			{
				return true;
			}
			Details.Add("Missing field: " + t.Name + "." + name);
			AffectedFeatures.Add(feature);
			return false;
		}

		private static bool TryAll(params Func<bool>[] checks)
		{
			bool result = true;
			foreach (Func<bool> func in checks)
			{
				try
				{
					if (!func())
					{
						result = false;
					}
				}
				catch (Exception ex)
				{
					Details.Add(ex.Message);
					result = false;
				}
			}
			return result;
		}

		private static bool RF(Type t, string name, BindingFlags flags, ref FieldInfo into)
		{
			into = t.GetField(name, flags);
			if (into != null)
			{
				return true;
			}
			Details.Add("Missing field: " + t.Name + "." + name);
			return false;
		}

		private static bool RM(Type t, string name, Type[] pTypes, BindingFlags flags, ref MethodInfo into)
		{
			into = t.GetMethod(name, flags, null, pTypes, null);
			if (into != null)
			{
				return true;
			}
			Details.Add("Missing method: " + t.Name + "." + name);
			return false;
		}

		private static bool RME(Type t, string name)
		{
			if (t.GetMethod(name, ALL) != null)
			{
				return true;
			}
			Details.Add("Missing method: " + t.Name + "." + name);
			return false;
		}

		private static bool RFR<TType, TField>(string name, out FieldRef<TType, TField> fieldRef)
		{
			fieldRef = null;
			if (!HasField(typeof(TType), name))
			{
				Details.Add("Missing field: " + typeof(TType).Name + "." + name);
				return false;
			}
			try
			{
				fieldRef = AccessTools.FieldRefAccess<TType, TField>(name);
				return true;
			}
			catch (Exception ex)
			{
				Details.Add("FieldRef binding failed " + typeof(TType).Name + "." + name + ": " + ex.Message);
				return false;
			}
		}
	}
	internal static class ConflictDetect
	{
		internal static readonly List<string> DetectedConflictMods = new List<string>();

		private static readonly string[] ConflictKeywords = new string[2] { "late", "join" };

		private static readonly string[] SelfKeywords = new string[3] { "latejoinnow", "zichenlatejoinnow", "latejoinfixed" };

		internal static bool HasConflict => DetectedConflictMods.Count > 0;

		internal static void Detect()
		{
			DetectedConflictMods.Clear();
			try
			{
				foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
				{
					string text = pluginInfo.Key ?? string.Empty;
					PluginInfo value = pluginInfo.Value;
					object obj;
					if (value == null)
					{
						obj = null;
					}
					else
					{
						BepInPlugin metadata = value.Metadata;
						obj = ((metadata != null) ? metadata.Name : null);
					}
					if (obj == null)
					{
						obj = string.Empty;
					}
					string text2 = (string)obj;
					if (!string.Equals(text, "zichen.latejoinnow", StringComparison.OrdinalIgnoreCase) && !IsSelf(text) && !IsSelf(text2) && (IsConflict(text) || IsConflict(text2)))
					{
						string text3 = ((!string.IsNullOrWhiteSpace(text2)) ? text2 : text);
						if (!DetectedConflictMods.Contains(text3))
						{
							DetectedConflictMods.Add(text3);
							Plugin.LogWarning("Detected similar/conflicting mod: " + text3);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Verbose("Mod conflict detection failed: " + ex.Message);
			}
		}

		private static string Normalize(string v)
		{
			if (!string.IsNullOrWhiteSpace(v))
			{
				return v.Replace("_", "").Replace("-", "").Replace(" ", "")
					.Replace(".", "")
					.ToLowerInvariant();
			}
			return string.Empty;
		}

		private static bool IsSelf(string v)
		{
			string text = Normalize(v);
			string[] selfKeywords = SelfKeywords;
			foreach (string value in selfKeywords)
			{
				if (text.IndexOf(value, StringComparison.Ordinal) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private static bool IsConflict(string v)
		{
			string text = Normalize(v);
			string[] conflictKeywords = ConflictKeywords;
			foreach (string value in conflictKeywords)
			{
				if (text.IndexOf(value, StringComparison.Ordinal) >= 0)
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class EnemySync
	{
		internal static bool Enabled = true;

		private static FieldRef<EnemyParent, bool> _spawned;

		private static FieldRef<EnemyParent, bool> SpawnedRef => _spawned ?? (_spawned = AccessTools.FieldRefAccess<EnemyParent, bool>("Spawned"));

		internal static void OnRemotePlayerAvatarStart(PlayerAvatar avatar)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			try
			{
				if (Enabled && PhotonNetwork.IsMasterClient && SemiFunc.RunIsLevel() && (Object)avatar != (Object)null && (Object)avatar.photonView != (Object)null && !avatar.photonView.IsMine && avatar.photonView.Owner != null && (Object)LevelGenerator.Instance != (Object)null && LevelGenerator.Instance.Generated)
				{
					GameDirector instance = GameDirector.instance;
					if ((Object)instance != (Object)null)
					{
						((MonoBehaviour)instance).StartCoroutine(HostSend(avatar.photonView.Owner.ActorNumber));
					}
				}
			}
			catch (Exception e)
			{
				Suppressed.Report("Late-join enemy sync (host)", e);
			}
		}

		private static IEnumerator HostSend(int actor)
		{
			float[] array = new float[2] { 4f, 6f };
			float[] array2 = array;
			foreach (float delay in array2)
			{
				yield return (object)new WaitForSeconds(delay);
				if (!PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient || !SemiFunc.RunIsLevel())
				{
					break;
				}
				Player val = ((PhotonNetwork.CurrentRoom != null) ? PhotonNetwork.CurrentRoom.GetPlayer(actor, false) : null);
				if (val == null || (Object)EnemyDirector.instance == (Object)null)
				{
					break;
				}
				int num = 0;
				int num2 = 0;
				if (EnemyDirector.instance.enemiesSpawned != null)
				{
					EnemyParent[] array3 = EnemyDirector.instance.enemiesSpawned.ToArray();
					foreach (EnemyParent val2 in array3)
					{
						try
						{
							if (!((Object)val2 == (Object)null) && !((Object)((MonoBehaviourPun)val2).photonView == (Object)null))
							{
								num2++;
								if (SpawnedRef != null && !SpawnedRef.Invoke(val2))
								{
									((MonoBehaviourPun)val2).photonView.RPC("DespawnRPC", val, Array.Empty<object>());
									num++;
								}
							}
						}
						catch (Exception e)
						{
							Suppressed.Report("Late-join enemy sync (send)", e);
						}
					}
				}
				if (delay == 4f)
				{
					Plugin.LogInfo($"[Late-join enemy sync] {num2} enemies in the level; told a player who joined mid-level that {num} of them are resting (otherwise they'd see them frozen in place).");
				}
			}
		}
	}
	internal static class LateJoinNowHooks
	{
		private class InitialSide
		{
			public Connection Side;

			public InitialSide(Connection s)
			{
				//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)
				Side = s;
			}
		}

		private static bool _changeLevelInProgress;

		private static readonly Dictionary<int, int> _trackedItems = new Dictionary<int, int>();

		private static readonly List<GameObject> _itemsToTrack = new List<GameObject>(128);

		private static bool _itemTrackingCoroutineRunning;

		internal static readonly FieldRef<PlayerAvatar, bool> _avDisabledRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isDisabled");

		internal static readonly FieldRef<PlayerAvatar, bool> _avDeadSetRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("deadSet");

		private static readonly FieldRef<GumballValuable, List<GameObject>> _gumLines = AccessTools.FieldRefAccess<GumballValuable, List<GameObject>>("hypnosisLines");

		private static readonly FieldRef<GumballValuable, GameObject> _gumPrefab = TFR<GumballValuable, GameObject>("hypnosisLine");

		private static readonly FieldRef<GumballValuable, Transform> _gumParent = TFR<GumballValuable, Transform>("HypnosisLinesParenTransform");

		private static readonly FieldRef<GumballValuable, List<bool>> _gumPlayerInSight = TFR<GumballValuable, List<bool>>("playerInSight");

		private static readonly FieldRef<GumballValuable, List<bool>> _gumPreviouslySeen = TFR<GumballValuable, List<bool>>("previouslySeenList");

		private static readonly FieldRef<GumballValuable, int> _gumPlayerCount = TFR<GumballValuable, int>("playerCount");

		private static readonly FieldRef<PlayerAvatar, bool> _animCompleted = AccessTools.FieldRefAccess<PlayerAvatar, bool>("levelAnimationCompleted");

		private static readonly FieldRef<LoadingUI, bool> _uiStarted = TFR<LoadingUI, bool>("levelAnimationStarted");

		private static readonly FieldRef<LoadingUI, bool> _uiCompleted = TFR<LoadingUI, bool>("levelAnimationCompleted");

		private static readonly FieldRef<PlayerAvatarTalkAnimation, PlayerAvatarVisuals> _talkAnimVisualsRef = TFR<PlayerAvatarTalkAnimation, PlayerAvatarVisuals>("playerAvatarVisuals");

		private static readonly FieldRef<PlayerAvatarOverchargeVisuals, PlayerAvatar> _overchargePlayerAvatarRef = TFR<PlayerAvatarOverchargeVisuals, PlayerAvatar>("playerAvatar");

		private static readonly FieldRef<PlayerAvatarOverchargeVisuals, Transform> _overchargeBeamOriginRef = TFR<PlayerAvatarOverchargeVisuals, Transform>("physGrabBeamOrigin");

		private static readonly FieldRef<PlayerAvatarGrabDisableVisuals, PlayerAvatar> _grabDisablePlayerAvatarRef = TFR<PlayerAvatarGrabDisableVisuals, PlayerAvatar>("playerAvatar");

		private static readonly FieldRef<PlayerAvatarGrabDisableVisuals, Transform> _grabDisableBeamOriginRef = TFR<PlayerAvatarGrabDisableVisuals, Transform>("physGrabBeamOrigin");

		private static readonly FieldRef<PlayerAvatarLeftArm, PlayerAvatarVisuals> _leftArmVisualsRef = TFR<PlayerAvatarLeftArm, PlayerAvatarVisuals>("playerAvatarVisuals");

		private static readonly FieldRef<Inventory, bool> _inventorySpotsFeched = TFR<Inventory, bool>("spotsFeched");

		private static readonly FieldRef<Inventory, List<InventorySpot>> _inventorySpotsRef = TFR<Inventory, List<InventorySpot>>("inventorySpots");

		private static readonly FieldRef<Inventory, PhysGrabber> _inventoryPhysGrabberRef = TFR<Inventory, PhysGrabber>("physGrabber");

		private static readonly FieldRef<Inventory, PlayerAvatar> _inventoryPlayerAvatarRef = TFR<Inventory, PlayerAvatar>("playerAvatar");

		internal static readonly FieldRef<ItemEquippable, InventorySpot> _itemEquippedSpotRef = TFR<ItemEquippable, InventorySpot>("equippedSpot");

		internal static readonly FieldRef<ItemEquippable, int> _itemEquippableOwnerIdRef = TFR<ItemEquippable, int>("ownerPlayerId");

		internal static readonly FieldRef<ItemEquippable, int> _itemInventorySpotIndexRef = TFR<ItemEquippable, int>("inventorySpotIndex");

		private static readonly FieldRef<PhysGrabber, PhysGrabObject> _physGrabbedObjectRef = TFR<PhysGrabber, PhysGrabObject>("grabbedPhysGrabObject");

		private static readonly FieldRef<ItemEquippable, ItemState> _itemEquippableStateRef = TFR<ItemEquippable, ItemState>("currentState");

		private static readonly FieldRef<InventorySpot, SpotState> _spotStateRef = TFR<InventorySpot, SpotState>("currentState");

		private static readonly MethodInfo _spotSetCurrentItemProp = AccessTools.PropertySetter(typeof(InventorySpot), "CurrentItem");

		private static MethodInfo _rpcUpdateItemStateMethod;

		private static readonly Dictionary<int, bool> _epLastLockedState = new Dictionary<int, bool>(8);

		private static readonly FieldRef<ExtractionPoint, bool> _epIsLockedRef = TFR<ExtractionPoint, bool>("isLocked");

		private static float _restartSceneTimer = 0f;

		private static readonly FieldRef<RunManager, bool> _rmRestartingRef = TFR<RunManager, bool>("restarting");

		private static readonly FieldRef<RunManager, bool> _rmRestartingDoneRef = TFR<RunManager, bool>("restartingDone");

		private static readonly FieldRef<RunManager, bool> _rmGameOverRef = TFR<RunManager, bool>("gameOver");

		private static readonly FieldRef<RunManager, bool> _rmLobbyJoinRef = TFR<RunManager, bool>("lobbyJoin");

		private static readonly FieldRef<RunManager, bool> _rmWaitToChangeSceneRef = TFR<RunManager, bool>("waitToChangeScene");

		private static readonly FieldRef<PlayerAvatar, bool> _avatarOutroDoneRef = TFR<PlayerAvatar, bool>("outroDone");

		private static readonly ConditionalWeakTable<ModulePropSwitch, InitialSide> _initialPropSwitchSides = new ConditionalWeakTable<ModulePropSwitch, InitialSide>();

		internal static void SetSpotItem(InventorySpot spot, ItemEquippable item)
		{
			if ((Object)(object)spot == (Object)null)
			{
				return;
			}
			try
			{
				if (_spotSetCurrentItemProp != null)
				{
					_spotSetCurrentItemProp.Invoke(spot, new object[1] { item });
				}
				if (_spotStateRef != null)
				{
					_spotStateRef.Invoke(spot) = (SpotState)(((Object)(object)item != (Object)null) ? 1 : 0);
				}
				spot.UpdateUI();
			}
			catch (Exception ex)
			{
				Plugin.Verbose("[SetSpotItem] Error: " + ex.Message);
			}
		}

		private static bool IsMainState()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			GameDirector instance = GameDirector.instance;
			if (instance == null)
			{
				return false;
			}
			return (int)instance.currentState == 2;
		}

		internal static void ClearTrackedItemInstantiationCaches()
		{
			if (_trackedItems.Count == 0)
			{
				return;
			}
			int num = 0;
			List<int> list = new List<int>(_trackedItems.Values);
			_trackedItems.Clear();
			foreach (int item in list)
			{
				if (Plugin.ClearInstantiationCacheById(item))
				{
					num++;
				}
			}
			Plugin.Verbose($"Level transition cleaned {num} item cache entries.");
		}

		internal static void RunManagerChangeLevelPrefix(bool _completedLevel, bool _levelFailed, ChangeLevelType _changeLevelType)
		{
			if (!Plugin.IsHookActive() || _changeLevelInProgress || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			_changeLevelInProgress = true;
			try
			{
				if (_animCompleted != null && GameDirector.instance?.PlayerList != null)
				{
					foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
					{
						if (!((Object)(object)player == (Object)null))
						{
							try
							{
								_animCompleted.Invoke(player) = true;
							}
							catch
							{
							}
						}
					}
				}
				Plugin.ClearRunManagerPunBufferedRpcs();
				HashSet<int> keep = Plugin.BuildPlayerPersistentViewIdSet();
				Plugin.DestroyAllSceneValuablesAndItemsWithKeepPublic(keep);
				Plugin.ClearAllScenePhotonCacheWithKeepPublic(keep, clearPersistent: true);
				ClearTrackedItemInstantiationCaches();
				Plugin.ClearLateJoinQueue();
				Plugin.HideRoomForChangeLevelCooldown();
				_epLastLockedState.Clear();
			}
			catch
			{
			}
			finally
			{
				_changeLevelInProgress = false;
			}
		}

		internal static void RunManagerChangeLevelPostfix(bool _completedLevel, bool _levelFailed, ChangeLevelType _changeLevelType)
		{
			if (!Plugin.IsHookActive() || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				Plugin.EnforceLobbyState();
			}
			catch
			{
			}
		}

		internal static void LevelGeneratorStartPrefix(LevelGenerator __instance)
		{
			if (!PhotonNetwork.IsMasterClient)
			{
				try
				{
					Plugin.ResetClientCoopState();
					return;
				}
				catch
				{
					return;
				}
			}
			if (!Plugin.IsHookActive() || !CompatibilityReport.CanRefreshLevelGenerator)
			{
				return;
			}
			bool flag = false;
			try
			{
				flag = SemiFunc.RunIsLobby();
			}
			catch
			{
			}
			if (!flag || (Object)(object)__instance?.PhotonView == (Object)null)
			{
				return;
			}
			try
			{
				PhotonNetwork.RemoveBufferedRPCs(__instance.PhotonView.ViewID, (string)null, (int[])null);
			}
			catch
			{
			}
		}

		internal static void PlayerAvatarStartPostfix(PlayerAvatar __instance)
		{
			if (!Plugin.IsHookActive() || !CompatibilityReport.CanCatchupLateJoiner || (Object)(object)__instance?.photonView == (Object)null || !IsMainState())
			{
				return;
			}
			if (!PhotonNetwork.IsMasterClient)
			{
				if (__instance.photonView.IsMine)
				{
					Plugin.TryRunClientLateJoinFixes(__instance);
				}
				return;
			}
			if (!__instance.photonView.IsMine && __instance.photonView.Owner != null)
			{
				MapSync.OnRemotePlayerAvatarStart(__instance);
				EnemySync.OnRemotePlayerAvatarStart(__instance);
			}
			if (_animCompleted != null)
			{
				try
				{
					_animCompleted.Invoke(__instance) = true;
				}
				catch
				{
				}
			}
			try
			{
				__instance.photonView.RPC("LoadingLevelAnimationCompletedRPC", (RpcTarget)0, Array.Empty<object>());
			}
			catch
			{
			}
			try
			{
				int viewID = __instance.photonView.ViewID;
				EnemyOnScreen[] array = Object.FindObjectsOfType<EnemyOnScreen>();
				foreach (EnemyOnScreen val in array)
				{
					if ((Object)(object)val != (Object)null)
					{
						val.PlayerAdded(viewID);
					}
				}
			}
			catch
			{
			}
		}

		internal static bool LevelGeneratorPlayerSpawnPrefix(LevelGenerator __instance)
		{
			//IL_0046: 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)
			//IL_0051: 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_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: 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_00c0: 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_00d2: 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_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: 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_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.IsHookActive() || (Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!PhotonNetwork.IsMasterClient)
			{
				return true;
			}
			if (!__instance.Generated)
			{
				return true;
			}
			try
			{
				if (GameDirector.instance?.PlayerList == null)
				{
					return false;
				}
				bool teleportToHostOnJoinEnabled = Plugin.TeleportToHostOnJoinEnabled;
				Vector3 val = Vector3.zero;
				Quaternion val2 = Quaternion.identity;
				bool flag = false;
				if (teleportToHostOnJoinEnabled)
				{
					PlayerAvatar val3 = PickAliveAnchorAvatar();
					if ((Object)(object)val3 != (Object)null)
					{
						Transform val4 = (((Object)(object)val3.playerTransform != (Object)null) ? val3.playerTransform : ((Component)val3).transform);
						if ((Object)(object)val4 != (Object)null)
						{
							Vector3 forward = val4.forward;
							forward.y = 0f;
							if (((Vector3)(ref forward)).sqrMagnitude < 0.001f)
							{
								forward = Vector3.forward;
							}
							else
							{
								((Vector3)(ref forward)).Normalize();
							}
							val = val4.position + forward * 3f;
							val2 = Quaternion.LookRotation(-forward);
							flag = true;
						}
					}
				}
				SpawnPoint[] array = null;
				int num = 0;
				int num2 = 0;
				foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
				{
					object obj;
					if (player == null)
					{
						obj = null;
					}
					else
					{
						PhotonView photonView = player.photonView;
						obj = ((photonView != null) ? photonView.Owner : null);
					}
					if (obj == null || !Plugin.TryGetLateJoinEntryByActor(player.photonView.Owner.ActorNumber, out var entry))
					{
						continue;
					}
					Plugin.LateJoinState state = entry.State;
					if (state != Plugin.LateJoinState.Pending && state != Plugin.LateJoinState.Queued && state != Plugin.LateJoinState.Processing)
					{
						continue;
					}
					Vector3 val5;
					Quaternion val6;
					if (flag)
					{
						val5 = val;
						val6 = val2;
					}
					else
					{
						if (array == null)
						{
							array = Object.FindObjectsOfType<SpawnPoint>();
						}
						if (array.Length == 0)
						{
							return false;
						}
						SpawnPoint obj2 = array[num % array.Length];
						val5 = ((Component)obj2).transform.position;
						val6 = ((Component)obj2).transform.rotation;
						num++;
					}
					try
					{
						player.Spawn(val5, val6);
						entry.SpawnPosition = val5;
						num2++;
					}
					catch
					{
					}
				}
				Plugin.Verbose(string.Format("Late joiner spawn: {0} player(s), {1}.", num2, flag ? "teleported to anchor" : "spawn point"));
			}
			catch
			{
			}
			return false;
		}

		private static bool IsAvatarAlive(PlayerAvatar av)
		{
			if ((Object)(object)av == (Object)null)
			{
				return false;
			}
			return !Plugin.IsAvatarReallyDead(av);
		}

		private static PlayerAvatar PickAliveAnchorAvatar()
		{
			if (GameDirector.instance?.PlayerList == null)
			{
				return null;
			}
			Player masterClient = PhotonNetwork.MasterClient;
			int num = ((masterClient != null) ? masterClient.ActorNumber : (-1));
			PlayerAvatar val = null;
			PlayerAvatar val2 = null;
			foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
			{
				object obj;
				if (player == null)
				{
					obj = null;
				}
				else
				{
					PhotonView photonView = player.photonView;
					obj = ((photonView != null) ? photonView.Owner : null);
				}
				if (obj != null && IsAvatarAlive(player))
				{
					if (player.photonView.Owner.ActorNumber == num)
					{
						val = player;
					}
					else if ((Object)(object)val2 == (Object)null)
					{
						val2 = player;
					}
				}
			}
			return val ?? val2;
		}

		internal static void NetworkManagerOnPlayerEnteredRoomPostfix(Player newPlayer)
		{
			Plugin.NetworkManagerOnPlayerEnteredRoomPostfix(newPlayer);
		}

		internal static void NetworkManagerOnPlayerLeftRoomPostfix(Player otherPlayer)
		{
			Plugin.NetworkManagerOnPlayerLeftRoomPostfix(otherPlayer);
		}

		internal static void NetworkConnectOnConnectedToMasterPrefix()
		{
			Plugin.NetworkConnectOnConnectedToMasterPrefix();
		}

		internal static void NetworkManagerPlayerSpawnedRPCPostfix(PhotonMessageInfo _info)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			Plugin.NetworkManagerPlayerSpawnedRPCPostfix(_info);
		}

		internal static void NetworkConnectOnJoinedRoomPostfix()
		{
			try
			{
				MapSync.OnJoinedRoom();
				Plugin.AnnouncePresenceAndRescan();
			}
			catch
			{
			}
			Plugin.TryJoinSteamLobbyFromPhotonRoom();
		}

		internal static void NetworkManagerOnDisconnectedPostfix()
		{
			try
			{
				Plugin.ResetCoopState();
			}
			catch
			{
			}
		}

		private static FieldRef<T, F> TFR<T, F>(string name)
		{
			try
			{
				return (AccessTools.Field(typeof(T), name) != null) ? AccessTools.FieldRefAccess<T, F>(name) : null;
			}
			catch
			{
				return null;
			}
		}

		internal static bool GumballValuableUpdateHypnosisLinesListPrefix(GumballValuable __instance)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.IsHookActive() || (Object)(object)__instance == (Object)null || _gumLines == null || _gumPrefab == null)
			{
				return true;
			}
			try
			{
				List<GameObject> list = _gumLines.Invoke(__instance);
				List<PlayerAvatar> list2 = SemiFunc.PlayerGetList();
				if (list == null || list2 == null)
				{
					return true;
				}
				int count = list2.Count;
				int count2 = list.Count;
				if (count <= count2)
				{
					return true;
				}
				GameObject val = _gumPrefab.Invoke(__instance);
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				Transform val2 = ((_gumParent != null) ? _gumParent.Invoke(__instance) : null);
				for (int i = count2; i < count; i++)
				{
					GameObject val3 = Object.Instantiate<GameObject>(val, ((Component)__instance).transform.position, Quaternion.identity);
					if ((Object)(object)val2 != (Object)null)
					{
						val3.transform.SetParent(val2);
					}
					val3.SetActive(false);
					list.Add(val3);
				}
				if (_gumPlayerInSight != null)
				{
					List<bool> list3 = _gumPlayerInSight.Invoke(__instance);
					if (list3 != null)
					{
						while (list3.Count < count)
						{
							list3.Add(item: false);
						}
					}
				}
				if (_gumPreviouslySeen != null)
				{
					List<bool> list4 = _gumPreviouslySeen.Invoke(__instance);
					if (list4 != null)
					{
						while (list4.Count < count)
						{
							list4.Add(item: false);
						}
					}
				}
			}
			catch
			{
			}
			return true;
		}

		internal static bool GumballValuableClientEffectsLoopPrefix(GumballValuable __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			try
			{
				if (_gumLines == null || _gumPlayerCount == null)
				{
					return true;
				}
				List<GameObject> list = _gumLines.Invoke(__instance);
				int num = _gumPlayerCount.Invoke(__instance);
				if (list != null && num > list.Count)
				{
					return false;
				}
				if (_gumPlayerInSight != null)
				{
					List<bool> list2 = _gumPlayerInSight.Invoke(__instance);
					if (list2 != null && num > list2.Count)
					{
						return false;
					}
				}
			}
			catch
			{
			}
			return true;
		}

		internal static bool SemiFuncOwnerOnlyRPCPrefix(PhotonMessageInfo _info, PhotonView _photonView, ref bool __result)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.IsHookActive())
			{
				return true;
			}
			try
			{
				Player sender = _info.Sender;
				if (sender != null)
				{
					if (sender.IsMasterClient)
					{
						__result = true;
						return false;
					}
					if ((Object)(object)_photonView != (Object)null && (_photonView.OwnerActorNr == sender.ActorNumber || _photonView.CreatorActorNr == sender.ActorNumber))
					{
						__result = true;
						return false;
					}
				}
			}
			catch
			{
			}
			return true;
		}

		internal static bool PlayerAvatarLoadingLevelAnimationCompletedRPCPrefix(PlayerAvatar __instance)
		{
			if (!Plugin.IsHookActive() || (Object)(object)__instance == (Object)null || _animCompleted == null)
			{
				return true;
			}
			if (!PhotonNetwork.IsMasterClient)
			{
				return true;
			}
			if (!IsMainState())
			{
				return true;
			}
			try
			{
				if (!_animCompleted.Invoke(__instance))
				{
					_animCompleted.Invoke(__instance) = true;
				}
			}
			catch
			{
			}
			return false;
		}

		internal static bool LoadingUILevelAnimationStartPrefix(LoadingUI __instance)
		{
			if (!Plugin.IsHookActive() || (Object)(object)__instance == (Object)null || (Object)(object)LevelGenerator.Instance?.Level != (Object)null)
			{
				return true;
			}
			if (PhotonNetwork.CurrentRoom == null)
			{
				return true;
			}
			try
			{
				if (_uiStarted != null)
				{
					_uiStarted.Invoke(__instance) = true;
				}
				if (_uiCompleted != null)
				{
					_uiCompleted.Invoke(__instance) = true;
				}
				try
				{
					PlayerController instance = PlayerController.instance;
					if (instance != null)
					{
						PlayerAvatar playerAvatarScript = instance.playerAvatarScript;
						if (playerAvatarScript != null)
						{
							playerAvatarScript.LoadingLevelAnimationCompleted();
						}
					}
				}
				catch
				{
				}
				return false;
			}
			catch
			{
			}
			return true;
		}

		internal static void PlayerAvatarAwakeSetOwnershipPostfix(PlayerAvatar __instance)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance?.photonView == (Object)null || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				__instance.photonView.OwnershipTransfer = (OwnershipOption)1;
			}
			catch
			{
			}
		}

		internal static void PlayerAvatarOnDestroyPostfix(PlayerAvatar __instance)
		{
			try
			{
				if ((Object)(object)PlayerAvatar.instance == (Object)(object)__instance)
				{
					PlayerAvatar.instance = null;
				}
			}
			catch
			{
			}
			if (!PhotonNetwork.IsMasterClient || !PhotonNetwork.InRoom || !((Object)(object)__instance != (Object)null))
			{
				return;
			}
			try
			{
				if (Plugin._playerDeathHeadRef != null)
				{
					PlayerDeathHead val = Plugin._playerDeathHeadRef.Invoke(__instance);
					if ((Object)(object)val != (Object)null)
					{
						PhotonView component = ((Component)val).GetComponent<PhotonView>();
						if ((Object)(object)component != (Object)null)
						{
							if (component.IsMine)
							{
								PhotonNetwork.Destroy(((Component)val).gameObject);
								Plugin.Verbose("[AvatarOnDestroy] Physically destroyed PlayerDeathHead associated with Avatar.");
							}
							else
							{
								PhotonNetwork.RemoveBufferedRPCs(component.ViewID, (string)null, (int[])null);
							}
						}
					}
				}
				if (Plugin._playerTumbleRef != null)
				{
					PlayerTumble val2 = Plugin._playerTumbleRef.Invoke(__instance);
					if ((Object)(object)val2 != (Object)null)
					{
						PhotonView component2 = ((Component)val2).GetComponent<PhotonView>();
						if ((Object)(object)component2 != (Object)null)
						{
							if (component2.IsMine)
							{
								PhotonNetwork.Destroy(((Component)val2).gameObject);
								Plugin.Verbose("[AvatarOnDestroy] Physically destroyed PlayerTumble associated with Avatar.");
							}
							else
							{
								PhotonNetwork.RemoveBufferedRPCs(component2.ViewID, (string)null, (int[])null);
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Verbose("[AvatarOnDestroy] Failed to destroy fallback corpse component: " + ex.Message);
			}
			try
			{
				PhotonView photonView = __instance.photonView;
				if ((Object)(object)photonView != (Object)null && photonView.ViewID != 0)
				{
					PhotonNetwork.RemoveBufferedRPCs(photonView.ViewID, (string)null, (int[])null);
					Plugin.Verbose($"[AvatarOnDestroy] Cleared buffered RPCs for Avatar ViewID {photonView.ViewID}.");
				}
				PlayerCosmetics componentInChildren = ((Component)__instance).GetComponentInChildren<PlayerCosmetics>();
				PhotonView val3 = ((componentInChildren != null) ? ((Component)componentInChildren).GetComponent<PhotonView>() : null);
				if ((Object)(object)val3 != (Object)null && val3.ViewID != 0)
				{
					PhotonNetwork.RemoveBufferedRPCs(val3.ViewID, (string)null, (int[])null);
					Plugin.Verbose($"[AvatarOnDestroy] Cleared buffered RPCs for Cosmetics ViewID {val3.ViewID}.");
				}
			}
			catch (Exception ex2)
			{
				Plugin.Verbose("[AvatarOnDestroy] Failed to clear buffered RPCs: " + ex2.Message);
			}
		}

		internal static bool EnergyUIUpdatePrefix()
		{
			if ((Object)(object)PlayerController.instance != (Object)null)
			{
				return (Object)(object)PlayerAvatar.instance != (Object)null;
			}
			return false;
		}

		internal static bool PlayerAvatarVisualsUpdatePrefix(PlayerAvatarVisuals __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return false;
			}
			if (!__instance.isMenuAvatar && (Object)(object)LevelGenerator.Instance == (Object)null)
			{
				return false;
			}
			if (__instance.isMenuAvatar && (Object)(object)__instance.playerAvatar == (Object)null && (Object)(object)PlayerAvatar.instance == (Object)null)
			{
				return false;
			}
			if (!__instance.isMenuAvatar && (Object)(object)__instance.playerAvatar == (Object)null)
			{
				return false;
			}
			PlayerAvatar val = (((Object)(object)__instance.playerAvatar != (Object)null) ? __instance.playerAvatar : PlayerAvatar.instance);
			if ((Object)(object)val != (Object)null && (Object)(object)val.localCamera == (Object)null)
			{
				return false;
			}
			return true;
		}

		internal static bool PlayerExpressionUpdatePrefix(PlayerExpression __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return false;
			}
			if ((Object)(object)LevelGenerator.Instance == (Object)null)
			{
				bool flag = false;
				try
				{
					flag = SemiFunc.MenuLevel();
				}
				catch
				{
				}
				if (!flag)
				{
					return false;
				}
			}
			if (!Object.op_Implicit((Object)(object)__instance.playerAvatar) && (Object)(object)PlayerAvatar.instance == (Object)null)
			{
				return false;
			}
			return true;
		}

		internal static bool PlayerAvatarTalkAnimationUpdatePrefix(PlayerAvatarTalkAnimation __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return false;
			}
			if ((Object)(object)__instance.playerAvatar == (Object)null && (Object)(object)PlayerAvatar.instance == (Object)null)
			{
				return false;
			}
			if (_talkAnimVisualsRef != null)
			{
				try
				{
					if ((Object)(object)_talkAnimVisualsRef.Invoke(__instance) == (Object)null)
					{
						return false;
					}
				}
				catch
				{
					return false;
				}
			}
			return true;
		}

		internal static bool PlayerLocalCameraGetOverrideTransformPrefix(PlayerLocalCamera __instance, ref Transform __result)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				__result = null;
				return false;
			}
			if ((Object)(object)__instance.playerAvatar == (Object)null)
			{
				__result = ((Component)__instance).transform;
				return false;
			}
			if ((Object)(object)CameraPosition.instance == (Object)null)
			{
				__result = ((Component)__instance).transform;
				return false;
			}
			if ((Object)(object)__instance.playerAvatar.PlayerVisionTarget == (Object)null)
			{
				__result = ((Component)__instance).transform;
				return false;
			}
			return true;
		}

		internal static bool PlayerAvatarOverchargeVisualsUpdatePrefix(PlayerAvatarOverchargeVisuals __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return false;
			}
			try
			{
				if (_overchargePlayerAvatarRef != null && (Object)(object)_overchargePlayerAvatarRef.Invoke(__instance) == (Object)null)
				{
					return false;
				}
				if (_overchargeBeamOriginRef != null && (Object)(object)_overchargeBeamOriginRef.Invoke(__instance) == (Object)null)
				{
					return false;
				}
			}
			catch
			{
				return false;
			}
			return true;
		}

		internal static bool PlayerAvatarGrabDisableVisualsUpdatePrefix(PlayerAvatarGrabDisableVisuals __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return false;
			}
			try
			{
				if (_grabDisablePlayerAvatarRef != null && (Object)(object)_grabDisablePlayerAvatarRef.Invoke(__instance) == (Object)null)
				{
					return false;
				}
				if (_grabDisableBeamOriginRef != null && (Object)(object)_grabDisableBeamOriginRef.Invoke(__instance) == (Object)null)
				{
					return false;
				}
			}
			catch
			{
				return false;
			}
			return true;
		}

		internal static bool PhysGrabberUpdatePrefix(PhysGrabber __instance)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.playerAvatar == (Object)null)
			{
				return false;
			}
			if ((Object)(object)__instance.playerAvatar.healthGrab == (Object)null)
			{
				return false;
			}
			if ((Object)(object)__instance.playerAvatar.localCamera == (Object)null)
			{
				return false;
			}
			if (__instance.isLocal)
			{
				if ((Object)(object)PlayerController.instance == (Object)null)
				{
					return false;
				}
				if ((Object)(object)LevelGenerator.Instance == (Object)null)
				{
					return false;
				}
			}
			return true;
		}

		internal static bool PlayerAvatarLeftArmUpdatePrefix(PlayerAvatarLeftArm __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return false;
			}
			if ((Object)(object)__instance.playerAvatar == (Object)null)
			{
				return false;
			}
			if (_leftArmVisualsRef != null)
			{
				try
				{
					if ((Object)(object)_leftArmVisualsRef.Invoke(__instance) == (Object)null)
					{
						return false;
					}
				}
				catch
				{
					return false;
				}
			}
			if ((Object)(object)__instance.flashlightController == (Object)null)
			{
				return false;
			}
			if ((Object)(object)__instance.playerAvatar.localCamera == (Object)null)
			{
				return false;
			}
			if ((Object)(object)__instance.playerAvatar.playerHealth == (Object)null)
			{
				return false;
			}
			if ((Object)(object)__instance.playerAvatar.playerAvatarVisuals == (Object)null)
			{
				return false;
			}
			return true;
		}

		internal static bool EnsureInventoryReady()
		{
			try
			{
				Inventory val = Inventory.instance;
				if ((Object)(object)val == (Object)null)
				{
					val = Object.FindObjectOfType<Inventory>(true);
					if ((Object)(object)val != (Object)null)
					{
						Inventory.instance = val;
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					return false;
				}
				List<InventorySpot> list = ((_inventorySpotsRef != null) ? _inventorySpotsRef.Invoke(val) : val.GetAllSpots());
				if (list == null)
				{
					list = new List<InventorySpot>();
					if (_inventorySpotsRef != null)
					{
						_inventorySpotsRef.Invoke(val) = list;
					}
				}
				while (list.Count < 3)
				{
					list.Add(null);
				}
				bool flag = false;
				for (int i = 0; i < 3; i++)
				{
					if ((Object)(object)list[i] == (Object)null)
					{
						flag = true;
						break;
					}
				}
				if (flag)
				{
					InventorySpot[] array = Object.FindObjectsOfType<InventorySpot>(true);
					if (array != null && array.Length != 0)
					{
						InventorySpot[] array2 = array;
						foreach (InventorySpot val2 in array2)
						{
							if ((Object)(object)val2 != (Object)null && val2.inventorySpotIndex >= 0 && val2.inventorySpotIndex < 3)
							{
								list[val2.inventorySpotIndex] = val2;
							}
						}
					}
				}
				if (_inventoryPhysGrabberRef != null && (Object)(object)_inventoryPhysGrabberRef.Invoke(val) == (Object)null && (Object)(object)PhysGrabber.instance != (Object)null)
				{
					_inventoryPhysGrabberRef.Invoke(val) = PhysGrabber.instance;
				}
				if (_inventoryPlayerAvatarRef != null && (Object)(object)_inventoryPlayerAvatarRef.Invoke(val) == (Object)null && (Object)(object)PlayerAvatar.instance != (Object)null)
				{
					_inventoryPlayerAvatarRef.Invoke(val) = PlayerAvatar.instance;
				}
				bool flag2 = true;
				for (int k = 0; k < 3; k++)
				{
					if ((Object)(object)list[k] == (Object)null)
					{
						flag2 = false;
						break;
					}
				}
				if (flag2 && _inventorySpotsFeched != null)
				{
					_inventorySpotsFeched.Invoke(val) = true;
				}
				return flag2;
			}
			catch (Exception ex)
			{
				Plugin.Verbose("[EnsureInventoryReady] Error: " + ex.Message);
				return false;
			}
		}

		private static bool IsInventoryReady()
		{
			if ((Object)(object)Inventory.instance == (Object)null)
			{
				return false;
			}
			return EnsureInventoryReady();
		}

		internal static bool ItemEquippableUpdateItemStatePrefix(ItemEquippable __instance, int state, int spotIndex, int ownerId, PhotonMessageInfo _info)
		{
			if ((Object)(object)__instance == (Object)null || !SemiFunc.IsMultiplayer())
			{
				return true;
			}
			EnsureInventoryReady();
			if (state == 2 && (Object)(object)PhysGrabber.instance != (Object)null && (Object)(object)PhysGrabber.instance.photonView != (Object)null && PhysGrabber.instance.photonView.ViewID == ownerId && spotIndex >= 0 && spotIndex < 3)
			{
				Inventory instance = Inventory.instance;
				InventorySpot val = ((instance != null) ? instance.GetSpotByIndex(spotIndex) : null);
				if ((Object)(object)val == (Object)null)
				{
					InventorySpot[] array = Object.FindObjectsOfType<InventorySpot>(true);
					if (array != null)
					{
						InventorySpot[] array2 = array;
						foreach (InventorySpot val2 in array2)
						{
							if (!((Object)(object)val2 != (Object)null) || val2.inventorySpotIndex != spotIndex)
							{
								continue;
							}
							val = val2;
							if (!((Object)(object)Inventory.instance != (Object)null))
							{
								break;
							}
							List<InventorySpot> list = ((_inventorySpotsRef != null) ? _inventorySpotsRef.Invoke(Inventory.instance) : null);
							if (list != null)
							{
								while (list.Count < 3)
								{
									list.Add(null);
								}
								list[spotIndex] = val;
							}
							break;
						}
					}
				}
				if ((Object)(object)val != (Object)null && (Object)(object)val.CurrentItem != (Object)(object)__instance)
				{
					if (_spotStateRef != null)
					{
						_spotStateRef.Invoke(val) = (SpotState)0;
					}
					if (_spotSetCurrentItemProp != null)
					{
						_spotSetCurrentItemProp.Invoke(val, new object[1]);
					}
				}
			}
			return true;
		}

		internal static void ItemEquippableUpdateItemStatePostfix(ItemEquippable __instance, int state, int spotIndex, int ownerId)
		{
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || !SemiFunc.IsMultiplayer() || !((Object)(object)PhysGrabber.instance != (Object)null) || !((Object)(object)PhysGrabber.instance.photonView != (Object)null) || PhysGrabber.instance.photonView.ViewID != ownerId || state != 2 || spotIndex < 0 || spotIndex >= 3)
			{
				return;
			}
			EnsureInventoryReady();
			Inventory instance = Inventory.instance;
			InventorySpot val = ((instance != null) ? instance.GetSpotByIndex(spotIndex) : null);
			if ((Object)(object)val == (Object)null)
			{
				InventorySpot[] array = Object.FindObjectsOfType<InventorySpot>(true);
				if (array != null)
				{
					InventorySpot[] array2 = array;
					foreach (InventorySpot val2 in array2)
					{
						if ((Object)(object)val2 != (Object)null && val2.inventorySpotIndex == spotIndex)
						{
							val = val2;
							break;
						}
					}
				}
			}
			InventorySpot val3 = ((_itemEquippedSpotRef != null) ? _itemEquippedSpotRef.Invoke(__instance) : null);
			if ((Object)(object)val != (Object)null)
			{
				if ((Object)(object)val3 == (Object)null || (Object)(object)val.CurrentItem != (Object)(object)__instance)
				{
					Plugin.LogInfo($"[ItemEquippable] Safety net: Binding equippedSpot for {((Object)__instance).name} on slot {spotIndex}!");
					if (_itemEquippedSpotRef != null)
					{
						_itemEquippedSpotRef.Invoke(__instance) = val;
					}
					SetSpotItem(val, __instance);
				}
				PlayerAvatar instance2 = PlayerAvatar.instance;
				if ((Object)(object)instance2 != (Object)null && (Object)(object)instance2.physGrabber != (Object)null && _physGrabbedObjectRef != null)
				{
					PhysGrabObject component = ((Component)__instance).GetComponent<PhysGrabObject>();
					if ((Object)(object)component != (Object)null && (Object)(object)_physGrabbedObjectRef.Invoke(instance2.physGrabber) == (Object)(object)component)
					{
						instance2.physGrabber.OverrideGrab((PhysGrabObject)null, 0f, false);
						instance2.physGrabber.ReleaseObject(-1, 0.1f);
					}
				}
			}
			else
			{
				Plugin.LogError($"[ItemEquippable] CRITICAL: Spot {spotIndex} not found! Recovering item {((Object)__instance).name} so it does not vanish!");
				if (_itemEquippableStateRef != null)
				{
					_itemEquippableStateRef.Invoke(__instance) = (ItemState)0;
				}
				PhysGrabObject component2 = ((Component)__instance).GetComponent<PhysGrabObject>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.OverrideDeactivateReset();
					((Component)component2).transform.localScale = Vector3.one;
				}
			}
		}

		internal static bool ItemEquippableCompleteUnequipPrefix(ItemEquippable __instance, int physGrabberPhotonViewID)
		{
			if (!SemiFunc.IsMultiplayer())
			{
				return true;
			}
			if (physGrabberPhotonViewID <= 0)
			{
				return true;
			}
			PhotonView val = PhotonView.Find(physGrabberPhotonViewID);
			if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).GetComponent<PhysGrabber>() == (Object)null)
			{
				Plugin.Verbose($"[ItemEquippable] CompleteUnequip skipped: invalid physGrabberPhotonViewID {physGrabberPhotonViewID}");
				return false;
			}
			return true;
		}

		internal static bool InventoryUIUpdatePrefix(InventoryUI __instance)
		{
			if ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated)
			{
				return false;
			}
			return true;
		}

		internal static void InventorySpotHandleInputPrefix(InventorySpot __instance)
		{
			EnsureInventoryReady();
		}

		internal static void InventorySpotAttemptEquipItemPrefix(InventorySpot __instance)
		{
			EnsureInventoryReady();
		}

		internal static void InventorySpotStartPostfix(InventorySpot __instance)
		{
			EnsureInventoryReady();
		}

		internal static void PlayerVoiceChatStartPostfix(PlayerVoiceChat __instance)
		{
			if (!Plugin.IsHookActive() || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			try
			{
				PhotonView component = ((Component)__instance).GetComponent<PhotonView>();
				if (!((Object)(object)component == (Object)null) && !component.IsMine)
				{
					bool flag = true;
					try
					{
						flag = SemiFunc.MenuLevel();
					}
					catch
					{
					}
					bool flag2 = false;
					try
					{
						flag2 = SemiFunc.RunIsLobby() || SemiFunc.RunIsShop() || SemiFunc.RunIsLevel();
					}
					catch
					{
					}
					if (!flag && flag2)
					{
						__instance.ToggleMixer(false, false);
					}
				}
			}
			catch
			{
			}
		}

		internal static void EnemyOnScreenAwakePostfix(EnemyOnScreen __instance)
		{
			if (!Plugin.IsHookActive() || (Object)(object)__instance == (Object)null || GameDirector.instance?.PlayerList == null || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
				{
					if ((Object)(object)player?.photonView != (Object)null)
					{
						__instance.PlayerAdded(player.photonView.ViewID);
					}
				}
			}
			catch
			{
			}
		}

		internal static void ExtractionPointUpdateLockPostfix(ExtractionPoint __instance, bool locked)
		{
			//IL_007d: 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_0084: 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_0093: Expected O, but got Unknown
			if (!Plugin.IsHookActive() || (Object)(object)__instance == (Object)null || !PhotonNetwork.IsMasterClient || PhotonNetwork.CurrentRoom == null)
			{
				return;
			}
			try
			{
				PhotonView component = ((Component)__instance).GetComponent<PhotonView>();
				if (!((Object)(object)component == (Object)null) && component.ViewID != 0)
				{
					int viewID = component.ViewID;
					if (!_epLastLockedState.TryGetValue(viewID, out var value) || value != locked)
					{
						_epLastLockedState[viewID] = locked;
						object[] array = new object[2] { viewID, locked };
						PhotonNetwork.RaiseEvent((byte)171, (object)array, new RaiseEventOptions
						{
							Receivers = (ReceiverGroup)0
						}, SendOptions.SendReliable);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Verbose("ExtractionPoint lock broadcast failed: " + ex.Message);
			}
		}

		internal static void ModuleAwakePostfix(Module __instance)
		{
		}

		internal static bool ModuleStartPrefix(Module __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return false;
			}
			try
			{
				if ((Object)(object)((Component)__instance).GetComponent<StartRoom>() != (Object)null)
				{
					Plugin.SetModuleStartRoom(__instance, val: true);
					return false;
				}
				if ((Object)(object)LevelGenerator.Instance != (Object)null && (Object)(object)LevelGenerator.Instance.LevelParent != (Object)null)
				{
					((Component)__instance).transform.parent = LevelGenerator.Instance.LevelParent.transform;
				}
			}
			catch
			{
			}
			return false;
		}

		internal static void ModuleStartPostfix(Module __instance)
		{
			if (PhotonNetwork.IsMasterClient || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			try
			{
				Plugin.OnClientModuleSpawned(__instance);
			}
			catch
			{
			}
		}

		internal static bool ModulePropSwitchSetupPrefix(ModulePropSwitch __instance)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			//IL_009d: 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_00a6: 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_00d1: 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_00d9: 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_00dd: 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_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Invalid comparison between Unknown and I4
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Invalid comparison between Unknown and I4
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Invalid comparison between Unknown and I4
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Expected O, but got Unknown
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Invalid comparison between Unknown and I4
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Expected O, but got Unknown
			if ((Object)(object)__instance == (Object)null)
			{
				return false;
			}
			try
			{
				Module val = ((Plugin._propSwitchModuleRef != null) ? Plugin._propSwitchModuleRef.Invoke(__instance) : null);
				if ((Object)val == (Object)null)
				{
					val = ((Component)__instance).GetComponentInParent<Module>();
					if (Plugin._propSwitchModuleRef != null && (Object)val != (Object)null)
					{
						Plugin._propSwitchModuleRef.Invoke(__instance) = val;
					}
				}
				if ((Object)val == (Object)null)
				{
					return false;
				}
				if (!_initialPropSwitchSides.TryGetValue(__instance, out var value))
				{
					value = new InitialSide(__instance.ConnectionSide);
					_initialPropSwitchSides.Add(__instance, value);
				}
				Quaternion localRotation = ((Component)val).transform.localRotation;
				int num = Mathf.RoundToInt(((Quaternion)(ref localRotation)).eulerAngles.y) % 360;
				if (num < 0)
				{
					num += 360;
				}
				int num2 = (num + 45) / 90;
				Connection val2 = (__instance.ConnectionSide = (Connection)((value.Side + num2) % 4));
				bool flag = Plugin._modTop != null && Plugin._modTop.Invoke(val);
				bool flag2 = Plugin._modRight != null && Plugin._modRight.Invoke(val);
				bool flag3 = Plugin._modBottom != null && Plugin._modBottom.Invoke(val);
				bool flag4 = Plugin._modLeft != null && Plugin._modLeft.Invoke(val);
				bool flag5 = false;
				if ((int)val2 == 0 && flag)
				{
					flag5 = true;
				}
				else if ((int)val2 == 1 && flag2)
				{
					flag5 = true;
				}
				else if ((int)val2 == 2 && flag3)
				{
					flag5 = true;
				}
				else if ((int)val2 == 3 && flag4)
				{
					flag5 = true;
				}
				if ((Object)__instance.NotConnectedParent != (Object)null)
				{
					__instance.NotConnectedParent.SetActive(!flag5);
				}
				if ((Object)__instance.ConnectedParent != (Object)null)
				{
					__instance.ConnectedParent.SetActive(flag5);
				}
			}
			catch (Exception ex)
			{
				Plugin.Verbose("[ModuleFix] ModulePropSwitch.SetupPrefix error: " + ex.Message);
			}
			return false;
		}

		internal static void PhysGrabObjectAwakePostfix(PhysGrabObject __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			if (PhotonNetwork.IsMasterClient && Plugin.IsHookActive())
			{
				_itemsToTrack.Add(((Component)__instance).gameObject);
				if (!_itemTrackingCoroutineRunning)
				{
					_itemTrackingCoroutineRunning = true;
					try
					{
						((MonoBehaviour)Plugin.Instance).StartCoroutine(TrackItemsDelayedBatch());
					}
					catch
					{
					}
				}
			}
			try
			{
				Plugin.TryForceSpawnSingle(__instance);
			}
			catch
			{
			}
		}

		internal static bool PlayerAvatarUpdateMyPlayerVoiceChatPrefix(PlayerAvatar __instance, int photonViewID)
		{
			if (!Plugin.IsHookActive())
			{
				return true;
			}
			try
			{
				PhotonView val = PhotonView.Find(photonViewID);
				if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).GetComponent<PlayerVoiceChat>() == (Object)null)
				{
					Plugin.Verbose($"[UpdateMyPlayerVoiceChat] Local Voice ViewID {photonViewID} not found, intercepting and waiting for retry.");
					return false;
				}
			}
			catch
			{
				return false;
			}
			return true;
		}

		internal static bool RunManagerRestartScenePrefix(RunManager __instance)
		{
			if ((Object)(object)__instance == (Object)null || !Plugin.IsHookActive())
			{
				return true;
			}
			if (!PhotonNetwork.IsMasterClient || !GameManager.Multiplayer())
			{
				return true;
			}
			bool num = _rmRestartingRef.Invoke(__instance);
			bool flag = _rmRestartingDoneRef.Invoke(__instance);
			if (!num)
			{
				_rmRestartingRef.Invoke(__instance) = true;
				_restartSceneTimer = 0f;
				if ((Object)(object)GameDirector.instance == (Object)null)
				{
					return true;
				}
				foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
				{
					if ((Object)(object)player != (Object)null)
					{
						try
						{
							player.OutroStart();
						}
						catch
						{
						}
					}
				}
				return false;
			}
			if (flag)
			{
				return false;
			}
			_restartSceneTimer += Time.deltaTime;
			bool flag2 = true;
			if ((Object)(object)GameDirector.instance == (Object)null)
			{
				flag2 = false;
			}
			else
			{
				foreach (PlayerAvatar player2 in GameDirector.instance.PlayerList)
				{
					if (!((Object)(object)player2 == (Object)null) && (!((Object)(object)player2.photonView != (Object)null) || player2.photonView.Owner == null || !Plugin.TryGetLateJoinEntryByActor(player2.photonView.Owner.ActorNumber, out var entry) || entry.State == Plugin.LateJoinState.Done) && !_avatarOutroDoneRef.Invoke(player2))
					{
						flag2 = false;
						break;
					}
				}
			}
			if (!flag2 && _restartSceneTimer >= 15f)
			{
				Plugin.LogInfo($"Stuck in Outro state for {_restartSceneTimer:F1}s, forcing level transition!");
				flag2 = true;
			}
			if (!flag2)
			{
				return false;
			}
			_rmRestartingDoneRef.Invoke(__instance) = true;
			bool flag3 = false;
			if (_rmGameOverRef.Invoke(__instance))
			{
				try
				{
					NetworkManager.instance.DestroyAll();
				}
				catch
				{
				}
				_rmGameOverRef.Invoke(__instance) = false;
				flag3 = true;
			}
			bool num2 = _rmLobbyJoinRef.Invoke(__instance);
			bool flag4 = _rmWaitToChangeSceneRef.Invoke(__instance);
			if (num2)
			{
				_rmLobbyJoinRef.Invoke(__instance) = false;
				try
				{
					SceneManager.LoadSceneAsync("LobbyJoin");
				}
				catch
				{
				}
			}
			else if (!flag4)
			{
				if (!flag3 && SemiFunc.RunIsLobbyMenu())
				{
					try
					{
						NetworkManager.instance.DestroyAll();
					}
					catch
					{
					}
				}
				if (!GameManager.Multiplayer())
				{
					try
					{
						SceneManager.LoadSceneAsync("Main");
					}
					catch
					{
					}
				}
				else if (PhotonNetwork.IsMasterClient)
				{
					try
					{
						PhotonNetwork.LoadLevel("Reload");
					}
					catch
					{
					}
				}
			}
			return false;
		}

		internal static IEnumerable<CodeInstruction> SlowProjectileDestroyRPCTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			MethodInfo methodInfo = AccessTools.Method(typeof(Object), "Destroy", new Type[1] { typeof(Object) }, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Plugin), "SafeDestroySlowProjectile", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				Plugin.Verbose("SlowProjectile.DestroyRPC Transpiler: Target or replacement method not found!");
				return list;
			}
			bool flag = false;
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Call && list[i].operand is MethodInfo methodInfo3 && methodInfo3 == methodInfo)
				{
					list[i].operand = methodInfo2;
					flag = true;
					Plugin.Verbose("SlowProjectile.DestroyRPC Transpiler: Replaced Object.Destroy call at index " + i);
				}
			}
			if (!flag)
			{
				Plugin.Verbose("SlowProjectile.DestroyRPC Transpiler: Target call not found!");
			}
			return list;
		}

		internal static void ItemAttributesAwakePostfix(ItemAttributes __instance)
		{
			if ((Object)(object)__instance == (Object)null || !Plugin.IsHookActive() || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			_itemsToTrack.Add(((Component)__instance).gameObject);
			if (_itemTrackingCoroutineRunning)
			{
				return;
			}
			_itemTrackingCoroutineRunning = true;
			try
			{
				((MonoBehaviour)Plugin.Instance).StartCoroutine(TrackItemsDelayedBatch());
			}
			catch
			{
			}
		}

		internal static void ValuableObjectAwakePostfix(ValuableObject __instance)
		{
			if ((Object)(object)__instance == (Object)null || !Plugin.IsHookActive() || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			_itemsToTrack.Add(((Component)__instance).gameObject);
			if (_itemTrackingCoroutineRunning)
			{
				return;
			}
			_itemTrackingCoroutineRunning = true;
			try
			{
				((MonoBehaviour)Plugin.Instance).StartCoroutine(TrackItemsDelayedBatch());
			}
			catch
			{
			}
		}

		internal static IEnumerator TrackItemsDelayedBatch()
		{
			yield return null;
			_itemTrackingCoroutineRunning = false;
			if (!Plugin.IsHookActive() || !PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient)
			{
				_itemsToTrack.Clear();
				yield break;
			}
			int num = 0;
			try
			{
				for (int i = 0; i < _itemsToTrack.Count; i++)
				{
					GameObject val = _itemsToTrack[i];
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					try
					{
						PhotonView component = val.GetComponent<PhotonView>();
						if ((Object)(object)component != (Object)null && component.InstantiationId != 0)
						{
							_trackedItems[component.ViewID] = component.InstantiationId;
							num++;
						}
					}
					catch
					{
					}
				}
			}
			catch
			{
			}
			finally
			{
				_itemsToTrack.Clear();
			}
			if (num > 0)
			{
				Plugin.Verbose($"[TrackItemsDelayedBatch] Batch registered tracking for {num} scene item(s).");
			}
		}

		internal static void PhysGrabObjectDestroyPhysGrabObjectRPCPrefix(PhysGrabObject __instance, PhotonMessageInfo _info)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((Object)(object)RunManager.instance == (Object)null) && !((Object)(object)__instance == (Object)null) && Plugin.IsHookActive() && PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && SemiFunc.MasterOnlyRPC(_info))
				{
					PhotonView component = ((Component)__instance).GetComponent<PhotonView>();
					if ((Object)(object)component != (Object)null && component.InstantiationId != 0)
					{
						_trackedItems.Remove(component.ViewID);
						Plugin.ClearInstantiationCacheById(component.InstantiationId);
						Plugin.Verbose($"[DestroyPhysGrabObjectRPC] Cleared cache on destroy: ViewID={component.ViewID}, InstantiationId={component.InstantiationId}, Name={((Object)((Component)__instance).gameObject).name}");
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Verbose("[DestroyPhysGrabObjectRPC] Failed to clean up cache on destroy: " + ex.Message);
			}
		}

		internal static void PlayerAvatarJumpRPCPrefix(PlayerAvatar __instance)
		{
			if ((Object)(object)__instance == (Object)null || !Plugin.IsHookActive() || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				PhotonView photonView = __instance.photonView;
				if ((Object)(object)photonView != (Object)null && photonView.Owner != null)
				{
					Plugin.NotifyPlayerJumped(photonView.Owner.ActorNumber);
				}
			}
			catch
			{
			}
		}

		internal static void RunManagerUpdateLevelPostfix()
		{
			MapSync.OnUpdateLevel();
		}

		internal static void LevelGeneratorGenerateDonePostfix(LevelGenerator __instance)
		{
			MapSync.OnGenerateDone(__instance);
		}

		internal static bool ModuleConnectionSetRPCPrefix(Module __instance)
		{
			return LevelRepair.ModuleConnectionSetRPCPrefix(__instance);
		}
	}
	internal static class LevelRepair
	{
		internal static bool Enabled = true;

		private const float Spacing = 15f;

		private const float Tolerance = 2f;

		internal static bool ModuleConnectionSetRPCPrefix(Module __instance)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			if (!Enabled)
			{
				return true;
			}
			try
			{
				return (Object)__instance == (Object)null || Plugin._modDone == null || !Plugin._modDone.Invoke(__instance);
			}
			catch
			{
				return true;
			}
		}

		internal static IEnumerator Routine()
		{
			yield return (object)new WaitForSeconds(6f);
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			try
			{
				List<Module> list = new List<Module>();
				List<Vector3> list2 = new List<Vector3>();
				Module[] array = Object.FindObjectsOfType<Module>(true);
				foreach (Module val in array)
				{
					if (!((Object)val == (Object)null))
					{
						if ((Object)((Component)val).GetComponent<StartRoom>() != (Object)null)
						{
							list2.Add(((Component)val).transform.position);
						}
						else
						{
							list.Add(val);
						}
					}
				}
				foreach (Module item in list)
				{
					num++;
					if (Plugin._modDone != null && Plugin._modDone.Invoke(item))
					{
						num2++;
						continue;
					}
					Vector3 position = ((Component)item).transform.position;
					bool flag = Mathf.Abs(position.x) < 2f && Mathf.Abs(position.z - 7.5f) < 2f;
					foreach (Vector3 item2 in list2)
					{
						if (Mathf.Abs(item2.x - position.x) < 2f && item2.z < position.z && position.z - item2.z < 23f)
						{
							flag = true;
						}
					}
					if (Plugin._modTop != null)
					{
						Plugin._modTop.Invoke(item) = HasNeighbour(list, item, position + new Vector3(0f, 0f, 15f));
					}
					if (Plugin._modBottom != null)
					{
						Plugin._modBottom.Invoke(item) = HasNeighbour(list, item, position - new Vector3(0f, 0f, 15f)) || flag;
					}
					if (Plugin._modRight != null)
					{
						Plugin._modRight.Invoke(item) = HasNeighbour(list, item, position + new Vector3(15f, 0f, 0f));
					}
					if (Plugin._modLeft != null)
					{
						Plugin._modLeft.Invoke(item) = HasNeighbour(list, item, position - new Vector3(15f, 0f, 0f));
					}
					if (Plugin._modFirst != null)
					{
						Plugin._modFirst.Invoke(item) = flag;
					}
					if (Plugin._modDone != null)
					{
						Plugin._modDone.Invoke(item) = true;
					}
					ModulePropSwitch[] componentsInChildren = ((Component)item).GetComponentsInChildren<ModulePropSwitch>(true);
					foreach (ModulePropSwitch val2 in componentsInChildren)
					{
						if (Plugin._propSwitchModuleRef != null)
						{
							Plugin._propSwitchModuleRef.Invoke(val2) = item;
						}
						val2.Setup();
						num4++;
					}
					num3++;
				}
			}
			catch (Exception e)
			{
				Suppressed.Report("Late-join level repair", e);
			}
			Plugin.LogInfo((num3 > 0) ? $"[Late-join level repair] Set up {num3} of {num} level pieces the host's setup never reached ({num4} wall/floor switches); {num2} were already set up." : $"[Late-join level repair] All {num} level pieces were already set up; nothing to repair.");
		}

		private static bool HasNeighbour(List<Module> all, Module self, Vector3 at)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			//IL_0022: Expected O, but got Unknown
			//IL_002a: 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)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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)
			foreach (Module item in all)
			{
				if (!((Object)item == (Object)self))
				{
					Vector3 position = ((Component)item).transform.position;
					if (Mathf.Abs(position.x - at.x) < 2f && Mathf.Abs(position.z - at.z) < 2f)
					{
						return true;
					}
				}
			}
			return false;
		}
	}
	internal static class MapSync
	{
		internal static bool RevealAllFallback;

		internal static bool MapSyncEnabled = true;

		internal static bool LevelRepairEnabled = true;

		private const byte EventCode = 163;

		private const string Tag = "RW.MapSync";

		private const float MatchDistance = 0.25f;

		private static readonly FieldRef<RoomVolume, bool> Explored = AccessTools.FieldRefAccess<RoomVolume, bool>("Explored");

		private static bool _subscribed;

		private static string _lastRoomName;

		private static LevelGenerator _lastLevel;

		private static bool _lateJoinPending;

		private static float _joinedRoomAt = -1000f;

		private static bool _receivedThisLevel;

		private static bool _joinerRoutinesStarted;

		private static readonly List<Vector3> Pending = new List<Vector3>();

		internal static void OnJoinedRoom()
		{
			_joinedRoomAt = Time.realtimeSinceStartup;
		}

		internal static void OnUpdateLevel()
		{
			try
			{
				if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient)
				{
					Room currentRoom = PhotonNetwork.CurrentRoom;
					string obj = ((currentRoom != null) ? currentRoom.Name : null);
					bool flag = obj != _lastRoomName;
					_lastRoomName = obj;
					if (flag && SemiFunc.RunIsLevel() && Time.realtimeSinceStartup - _joinedRoomAt < 15f)
					{
						_lateJoinPending = true;
					}
				}
			}
			catch (Exception e)
			{
				Suppressed.Report("Late-join map sync (join detect)", e);
			}
		}

		internal static void OnGenerateDone(LevelGenerator __instance)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_0023: Expected O, but got Unknown
			try
			{
				if (PhotonNetwork.InRoom && SemiFunc.IsMultiplayer() && !((Object)__instance == (Object)_lastLevel))
				{
					_lastLevel = __instance;
					_joinerRoutinesStarted = false;
					EnsureSubscribed();
					_receivedThisLevel = false;
					Pending.Clear();
					if (_lateJoinPending && !PhotonNetwork.IsMasterClient && SemiFunc.RunIsLevel())
					{
						_lateJoinPending = false;
						StartJoinerRoutines((MonoBehaviour)(object)__instance);
					}
				}
			}
			catch (Exception e)
			{
				Suppressed.Report("Late-join map sync (level done)", e);
			}
		}

		internal static void EnsureJoinerRoutinesStarted(MonoBehaviour runner)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			if (!_joinerRoutinesStarted && !PhotonNetwork.IsMasterClient && SemiFunc.RunIsLevel() && (Object)runner != (Object)null)
			{
				_lateJoinPending = false;
				StartJoinerRoutines(runner);
			}
		}

		private static void StartJoinerRoutines(MonoBehaviour runner)
		{
			if (!_joinerRoutinesStarted)
			{
				_joinerRoutinesStarted = true;
				EnsureSubscribed();
				Plugin.LogInfo("[Late-join map sync] Joined mid-level" + (MapSyncEnabled ? "; waiting for the host's explored rooms." : "."));
				if (MapSyncEnabled)
				{
					runner.StartCoroutine(JoinerRoutine());
				}
				if (LevelRepairEnabled)
				{
					runner.StartCoroutine(LevelRepair.Routine());
				}
			}
		}

		private static void EnsureSubscribed()
		{
			if (!_subscribed)
			{
				PhotonNetwork.NetworkingClient.EventReceived += OnEvent;
				_subscribed = true;
			}
		}

		internal static void OnRemotePlayerAvatarStart(PlayerAvatar avatar)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			try
			{
				if (MapSyncEnabled && PhotonNetwork.IsMasterClient && SemiFunc.RunIsLevel() && (Object)avatar != (Object)null && (Object)avatar.photonView != (Object)null && !avatar.photonView.IsMine && avatar.photonView.Owner != null && (Object)LevelGenerator.Instance != (Object)null && LevelGenerator.Instance.Generated)
				{
					GameDirector instance = GameDirector.instance;
					if ((Object)instance != (Object)null)
					{
						((MonoBehaviour)instance).StartCoroutine(HostSend(avatar.photonView.Owner.ActorNumber));
					}
				}
			}
			catch (Exception e)
			{
				Suppressed.Report("Late-join map sync (host)", e);
			}
		}

		private static IEnumerator HostSend(int actor)
		{
			float[] array = new float[2] { 4f, 8f };
			float[] array2 = array;
			foreach (float delay in array2)
			{
				yield return (object)new WaitForSeconds(delay);
				if (!PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient || PhotonNetwork.CurrentRoom == null || PhotonNetwork.CurrentRoom.GetPlayer(actor, false) == null)
				{
					break;
				}
				try
				{
					List<float> list = new List<float>();
					RoomVolume[] array3 = Object.FindObjectsOfType<RoomVolume>(true);
					foreach (RoomVolume val in array3)
					{
						if ((Object)val != (Object)null && Explored != null && Explored.Invoke(val))
						{
							Vector3 position = ((Component)val).transform.position;
							list.Add(position.x);
							list.Add(position.y);
							list.Add(position.z);
						}
					}
					object[] array4 = new object[2]
					{
						"RW.MapSync",
						list.ToArray()
					};
					RaiseEventOptions val2 = new RaiseEventOptions();
					val2.TargetActors = new int[1] { actor };
					PhotonNetwork.RaiseEvent((byte)163, (object)array4, val2, SendOptions.SendReliable);
					if (delay == 4f)
					{
						Plugin.LogInfo($"[Late-join map sync] Sent {list.Count / 3} explored rooms to a player who joined mid-level.");
					}
				}
				catch (Exception e)
				{
					Suppressed.Report("Late-join map sync (send)", e);
				}
			}
		}

		private static void OnEvent(EventData ev)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (ev.Code != 163 || PhotonNetwork.IsMasterClient)
				{
					return;
				}
				Player masterClient = PhotonNetwork.MasterClient;
				if (masterClient != null && ev.Sender == masterClient.ActorNumber && ev.CustomData is object[] array && array.Length == 2 && array[0] is string text && !(text != "RW.MapSync") && array[1] is float[] array2)
				{
					_receivedThisLevel = true;
					for (int i = 0; i + 2 < array2.Length; i += 3)
					{
						Pending.Add(new Vector3(array2[i], array2[i + 1], array2[i + 2]));
					}
				}
			}
			catch (Exception e)
			{
				Suppressed.Report("Late-join map sync (receive)", e);
			}
		}

		private static IEnumerator JoinerRoutine()
		{
			float waited = 0f;
			while (((Object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated) && waited < 60f)
			{
				yield return (object)new WaitForSeconds(0.5f);
				waited += 0.5f;
			}
			yield return (object)new WaitForSeconds(3f);
			int uncovered = 0;
			for (float t = 0f; t < 30f; t += 1f)
			{
				uncovered += ApplyPending();
				if (!_receivedThisLevel && t >= 15f)
				{
					break;
				}
				yield return (object)new WaitForSeconds(1f);
			}
			if (_receivedThisLevel)
			{
				Plugin.LogInfo($"[Late-join map sync] Joined mid-level; the host's map was synced ({uncovered} rooms uncovered).");
				yield break;
			}
			if (!RevealAllFallback)
			{
				Plugin.LogInfo("[Late-join map sync] Joined mid-level, but the host doesn't run Map Sync Fix, so its explored rooms can't be synced (RevealAllRoomsWhenHostHasNoMod is off).");
				yield break;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			RoomVolume[] array = Object.FindObjectsOfType<RoomVolume>(true);
			foreach (RoomVolume val in array)
			{
				if (!((Object)val == (Object)null))
				{
					num++;
					if (Explored != null && Explored.Invoke(val))
					{
						num2++;
					}
					else if ((Object)val.MapModule == (Object)null)
					{
						num3++;
					}
					else if (val.SetExplored())
					{
						num4++;
					}
				}
			}
			Plugin.LogInfo($"[Late-join map sync] Joined mid-level and the host doesn't run Map Sync Fix; uncovered the rest of the map (RevealAllRoomsWhenHostHasNoMod): {num4} rooms uncovered now, {num2} already uncovered, {num3} with no map cover (truck/extraction/start), {num} room volumes in total.");
		}

		private static int ApplyPending()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0053: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Expected O, but got Unknown
			if (Pending.Count == 0)
			{
				return 0;
			}
			int num = 0;
			RoomVolume[] array = Object.FindObjectsOfType<RoomVolume>(true);
			for (int num2 = Pending.Count - 1; num2 >= 0; num2--)
			{
				bool flag = false;
				bool flag2 = true;
				RoomVolume[] array2 = array;
				foreach (RoomVolume val in array2)
				{
					if ((Object)val == (Object)null || Vector3.Distance(((Component)val).transform.position, Pending[num2]) > 0.25f)
					{
						continue;
					}
					flag = true;
					if (Explored != null && !Explored.Invoke(val))
					{
						if ((Object)val.MapModule == (Object)null)
						{
							flag2 = false;
						}
						else if (val.SetExplored())
						{
							num++;
						}
					}
				}
				if (flag && flag2)
				{
					Pending.RemoveAt(num2);
				}
			}
			return num;
		}
	}
	internal sealed class PeerCoopCallbackListener : IInRoomCallbacks, IOnEventCallback
	{
		private const byte ExtractionLockEventCode = 171;

		private const byte DeadPlayersSyncEventCode = 172;

		private const byte ModuleSyncEventCode = 173;

		public void OnEvent(EventData photonEvent)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Expected O, but got Unknown
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Expected O, but got Unknown
			if (photonEvent.Code == 173 && !PhotonNetwork.IsMasterClient)
			{
				try
				{
					if (photonEvent.CustomData is int[] data)
					{
						Plugin.HandleModuleSyncEventClient(data);
					}
					return;
				}
				catch (Exception ex)
				{
					Plugin.Verbose("Failed to synchronize module connection data: " + ex.Message);
					return;
				}
			}
			if (photonEvent.Code == 172 && !PhotonNetwork.IsMasterClient)
			{
				try
				{
					if (photonEvent.CustomData is int[] array)
					{
						for (int i = 0; i < array.Length; i++)
						{
							PhotonView val = PhotonView.Find(array[i]);
							if ((Object)val != (Object)null)
							{
								PlayerAvatar component = ((Component)val).GetComponent<PlayerAvatar>();
								if ((Object)component != (Object)null)
								{
									Plugin.MarkPlayerAvatarDeadClient(component);
								}
							}
						}
					}
					return;
				}
				catch (Exception ex2)
				{
					Plugin.Verbose("Failed to synchronize dead players: " + ex2.Message);
					return;
				}
			}
			if (photonEvent.Code != 171 || PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				if (!(photonEvent.CustomData is object[] array2) || array2.Length < 2)
				{
					return;
				}
				int num = (int)array2[0];
				bool flag = (bool)array2[1];
				PhotonView val2 = PhotonView.Find(num);
				if (!((Object)val2 == (Object)null))
				{
					ExtractionPoint component2 = ((Component)val2).GetComponent<ExtractionPoint>();
					if (!((Object)component2 == (Object)null))
					{
						component2.UpdateLock(flag);
						Plugin.Verbose($"ExtractionPoint {num} lock state sync: {flag}");
					}
				}
			}
			catch (Exception ex3)
			{
				Plugin.Verbose("ExtractionPoint lock sync failed: " + ex3.Message);
			}
		}

		public void OnPlayerEnteredRoom(Player newPlayer)
		{
			if (newPlayer != null)
			{
				Plugin.UpdateCoopStateFor(newPlayer);
			}
		}

		public void OnPlayerLeftRoom(Player otherPlayer)
		{
			if (otherPlayer != null)
			{
				Plugin.RemoveCoopActor(otherPlayer.ActorNumber);
			}
		}

		public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
		{
			if (targetPlayer == null || changedProps == null)
			{
				return;
			}
			bool flag = ((Dictionary<object, object>)(object)changedProps).ContainsKey("LateJoinNow.Version") || ((Dictionary<object, object>)(object)changedProps).ContainsKey("LateJoinNow.Protocol");
			bool flag2 = ((Dictionary<object, object>)(object)changedProps).ContainsKey("LateJoinNow.Ready") || ((Dictionary<object, object>)(object)changedProps).ContainsKey("LateJoinNow.ModuleCount");
			if (flag || flag2)
			{
				if (flag)
				{
					Plugin.UpdateCoopStateFor(targetPlayer);
				}
				if (flag2 && PhotonNetwork.IsMasterClient && targetPlayer.ActorNumber != PhotonNetwork.LocalPlayer.ActorNumber)
				{
					Plugin.RecordCoopReadyForListener(targetPlayer);
				}
			}
		}

		public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
		{
		}

		public void OnMasterClientSwitched(Player newMasterClient)
		{
			try
			{
				Plugin.HandleMasterClientSwitched(newMasterClient);
			}
			catch
			{
			}
		}
	}
	[BepInPlugin("com.repo.latejoinfixed", "LateJoinFixed", "1.3.0")]
	public class Plugin : BaseUnityPlugin
	{
		private sealed class StateSyncEntry
		{
			public string TypeName;

			public string FieldName;

			public string RpcName;

			public bool CastToInt;

			public Type ResolvedType;

			public FieldInfo ResolvedField;

			public bool ResolveAttempted;
		}

		internal enum LateJoinState
		{
			Pending,
			Queued,
			Processing,
			Finalizing,
			Done,
			Aborted
		}

		internal sealed class LateJoinEntry
		{
			public Player Target;

			public int ActorNumber;

			public string NickName;

			public LateJoinState State;

			public string StartScene;

			public Vector3 SpawnPosition;
		}

		private delegate void OwnershipUpdateDelegate(int[] viewOwnerPairs, int targetActor);

		internal struct ModuleSyncData
		{
			public bool top;

			public bool bottom;

			public bool right;

			public bool left;

			public bool first;

			public ModuleSyncData(bool top, bool bottom, bool right, bool left, bool first)
			{
				this.top = top;
				this.bottom = bottom;
				this.right = right;
				this.left = left;
				this.first = first;
			}
		}

		public const string PluginGuid = "com.repo.latejoinfixed";

		public const string PluginName = "LateJoinFixed";

		public const string PluginVersion = "1.3.0";

		internal static Plugin Instance;

		internal static Harmony harmony;

		private I18NRegistry _i18n;

		private I18NSwitcher _i18nSwitcher;

		private static ConfigEntry<bool> _cfgModEnabled;

		private static ConfigEntry<bool> _cfgPublicRoomPrefix;

		private static ConfigEntry<bool> _cfgTeleportToHostOnJoin;

		private static ConfigEntry<bool> _cfgCoopMode;

		private static ConfigEntry<bool> _cfgAllowLobby;

		private static ConfigEntry<bool> _cfgAllowShop;

		private static ConfigEntry<bool> _cfgAllowLevel;

		private static ConfigEntry<bool> _cfgHudEnabled;

		private static ConfigEntry<bool> _cfgShowJoinStatus;

		private static ConfigEntry<bool> _cfgHudShowRoomType;

		private static ConfigEntry<bool> _cfgHudShowRoomName;

		private static ConfigEntry<bool> _cfgHudShowPlayerCount;

		private static ConfigEntry<int> _cfgHudFontSize;

		private static ConfigEntry<int> _cfgHudOpacity;

		private static ConfigEntry<bool> _cfgVerboseLog;

		private static ConfigEntry<bool> _cfgShowConflictWarning;

		private static ConfigEntry<bool> _cfgMapSync;

		private static ConfigEntry<bool> _cfgRevealAllRoomsWhenHostHasNoMod;

		private static ConfigEntry<bool> _cfgLevelRepair;

		private static ConfigEntry<bool> _cfgEnemySync;

		private float _enforceTimer;

		private float _clientDeadCheckTimer;

		private const float EnforceInterval = 1f;

		private static readonly FieldRef<RoundDirector, bool> _roundAllExtractionPointsCompletedRef = TryFieldRef<RoundDirector, bool>("allExtractionPointsCompleted");

		private const float SceneAllowedCacheSeconds = 0.5f;

		private static float _sceneAllowedCacheUntil;

		private static bool _sceneAllowedCachedValue;

		private static float _inGameCacheUntil;

		private static bool _inGameCachedValue;

		private static bool _clientLateJoinFixesDone;

		private static bool _clientLateJoinCoroutineRunning;

		private static FieldInfo _physGrabSpawnedField;

		private static Type _physGrabHingeType;

		private static GUIStyle _hudLabelStyle;

		private static GUIStyle _hudShadowStyle;

		private static int _cachedFontSize = -1;

		private static int _cachedOpacity = -1;

		private static readonly List<string> _hudLines = new List<string>(3);

		private const float HudRefreshInterval = 1f;

		private static float _hudNextRefreshAt;

		private static bool _hudCacheUseChinese;

		private static bool _hudCacheShowPlayerCount;

		private static bool _hudCacheShowRoomType;

		private static bool _hudCacheShowRoomName;

		private static int _hudCachePlayerCount;

		private static int _hudCacheMaxPlayers;

		private static bool _hudCacheLobbyTypeKnown;

		private static LobbyTypes _hudCacheLobbyType;

		private static string _hudCacheServerName;

		private static bool _hudCacheRoomOpen;

		private static readonly List<string> _emptyList = new List<string>(0);

		private static readonly List<string> _lateJoinStatusLines = new List<string>(4);

		private static readonly List<LateJoinEntry> _hudEntriesScratch = new List<LateJoinEntry>(8);

		private static readonly Comparison<LateJoinEntry> _entrySortDesc = (LateJoinEntry a, LateJoinEntry b) => b.ActorNumber.CompareTo(a.ActorNumber);

		private const float StatusLinesRefreshSeconds = 0.25f;

		private static float _statusLinesNextRefreshAt;

		private static int _statusLinesLastEntryCount = -1;

		private static int _statusLinesLastCoopMask;

		private static readonly FieldRef<PlayerAvatar, string> _avatarPlayerNameRef = TryAvatarPlayerNameRef();

		private static readonly StateSyncEntry[] _stateSyncEntries = new StateSyncEntry[22]
		{
			S("ExtractionPoint", "StateSetRPC", castToInt: false),
			S("ShopKeycardDoor", "StateSetRPC"),
			S("UpgradeStand", "StateSetRPC"),
			S("ItemValuableBox", "StateSetRPC"),
			S("ItemMine", "StateSetRPC", castToInt: true, "state"),
			S("ItemMelee", "StateSetRPC"),
			S("ItemGun", "StateSetRPC", castToInt: true, "stateCurrent"),
			S("ItemDrone", "StateSetRPC"),
			S("ItemVehicle", "SetStateRPC"),
			S("ItemCartCannonMain", "StateSetRPC", castToInt: true, "stateCurrent"),
			S("FanTrap", "SetStateRPC", castToInt: false),
			S("CrystalBallValuable", "SetStateRPC", castToInt: false),
			S("BlenderValuable", "SetStateRPC", castToInt: false),
			S("FlamethrowerValuable", "SetStateRPC", castToInt: false),
			S("IceSawValuable", "SetStateRPC", castToInt: false),
			S("FireExtinguisherValuable", "SetStateRPC", castToInt: false),
			S("ScreamDollValuable", "SetStateRPC", castToInt: false),
			S("JackhammerValuable", "SetStateRPC", castToInt: false),
			S("TrafficLightValuable", "SetStateRPC", castToInt: false),
			S("ValuableWizardTimeGlass", "SetStateRPC", castToInt: false),
			S("ValuableArcticSnowBike", "SetStateRPC"),
			S("ValuableEgg", "SetStateRPC", castToInt: false)
		};

		private static FieldInfo _epIsLockedFieldInfo;

		private static readonly Dictionary<int, LateJoinEntry> _lateJoinEntries = new Dictionary<int, LateJoinEntry>();

		private static readonly Queue<int> _lateJoinQueue = new Queue<int>();

		private static bool _schedulerRunning;

		private static readonly Dictionary<int, bool> _playerJumpedFlags = new Dictionary<int, bool>();

		internal static readonly FieldRef<PlayerAvatar, PlayerDeathHead> _playerDeathHeadRef = AccessTools.FieldRefAccess<PlayerAvatar, PlayerDeathHead>("playerDeathHead");

		internal static readonly FieldRef<PlayerAvatar, PlayerTumble> _playerTumbleRef = AccessTools.FieldRefAccess<PlayerAvatar, PlayerTumble>("tumble");

		internal static readonly FieldRef<PlayerDeathHead, bool> _deathHeadTriggeredRef = AccessTools.FieldRefAccess<PlayerDeathHead, bool>("triggered");

		internal static readonly FieldRef<PlayerHealth, int> _playerHealthRef = AccessTools.FieldRefAccess<PlayerHealth, int>("health");

		internal static readonly FieldRef<PlayerDeathHead, PhysGrabObject> _deathHeadPhysGrabRef = AccessTools.FieldRefAccess<PlayerDeathHead, PhysGrabObject>("physGrabObject");

		private static readonly FieldRef<PlayerAvatar, bool> _avatarIsMovingRef = AccessTools.FieldRefAccess<PlayerAvatar, bool>("isMoving");

		private static readonly FieldRef<PlayerAvatar, Vector3> _avatarInputDirectionRawRef = AccessTools.FieldRefAccess<PlayerAvatar, Vector3>("InputDirectionRaw");

		private static readonly FieldRef<PlayerAvatar, bool> _avatarAnimCompleted = TryAvatarAnimCompletedRef();

		internal static readonly FieldRef<Module, bool> _modTop = AccessTools.FieldRefAccess<Module, bool>("ConnectingTop");

		internal static readonly FieldRef<Module, bool> _modBottom = AccessTools.FieldRefAccess<Module, bool>("ConnectingBottom");

		internal static readonly FieldRef<Module, bool> _modRight = AccessTools.FieldRefAccess<Module, bool>("ConnectingRight");

		internal static readonly FieldRef<Module, bool> _modLeft = AccessTools.FieldRefAccess<Module, bool>("ConnectingLeft");

		internal static readonly FieldRef<Module, bool> _modFirst = AccessTools.FieldRefAccess<Module, bool>("First");

		internal static readonly FieldRef<Module, bool> _modDone = AccessTools.FieldRefAccess<Module, bool>("SetupDone");

		private static readonly Dictionary<int, ModuleSyncData> _cachedModuleSync = new Dictionary<int, ModuleSyncData>(64);

		private static readonly FieldRef<LevelGenerator, int> _lgModulesReadyPlayersRef = AccessTools.FieldRefAccess<LevelGenerator, int>("ModulesReadyPlayers");

		private static readonly FieldRef<LevelGenerator, int> _lgModulesSpawnedRef = AccessTools.FieldRefAccess<LevelGenerator, int>("ModulesSpawned");

		private static readonly FieldRef<LevelGenerator, bool> _lgAllPlayersReadyRef = AccessTools.FieldRefAccess<LevelGenerator, bool>("AllPlayersReady");

		private static readonly FieldRef<LevelGenerator, int> _lgModuleAmountRef = AccessTools.FieldRefAccess<LevelGenerator, int>("ModuleAmount");

		internal static readonly FieldRef<Module, bool> _modStartRoomRef = AccessTools.FieldRefAccess<Module, bool>("StartRoom");

		internal static readonly FieldRef<ModulePropSwitch, Module> _propSwitchModuleRef = AccessTools.FieldRefAccess<ModulePropSwitch, Module>("Module");

		private static readonly FieldRef<ValuableObject, bool> _valSet = AccessTools.FieldRefAccess<ValuableObject, bool>("dollarValueSet");

		private static readonly FieldRef<ValuableObject, float> _valCurrent = AccessTools.FieldRefAccess<ValuableObject, float>("dollarValueCurrent");

		private static readonly FieldRef<ValuableObject, bool> _valDisc = AccessTools.FieldRefAccess<ValuableObject, bool>("discovered");

		private static readonly FieldRef<ValuableObject, bool> _valHaul = AccessTools.FieldRefAccess<ValuableObject, bool>("inStartRoom");

		private static readonly FieldRef<MenuPageLobby, List<string>> _menuLobbyJoiningPlayersRef = TryMenuLobbyListRef("joiningPlayers");

		private static readonly FieldRef<MenuPageLobby, float> _menuLobbyJoiningTimerRef = TryMenuLobbyFloatRef("joiningPlayersTimer");

		private static readonly FieldRef<MenuPageLobby, float> _menuLobbyJoiningEndTimerRef = TryMenuLobbyFloatRef("joiningPlayersEndTimer");

		private static readonly MethodInfo _ownershipUpdateMethod = typeof(PhotonNetwork).GetMethod("OwnershipUpdate", BindingFlags.Static | BindingFlags.NonPublic);

		private static readonly OwnershipUpdateDelegate _ownershipUpdate = ((_ownershipUpdateMethod != null) ? ((OwnershipUpdateDelegate)Delegate.CreateDelegate(typeof(OwnershipUpdateDelegate), _ownershipUpdateMethod)) : null);

		private static readonly int[] _ownershipScratch = new int[2];

		private static bool _refreshOldPlayersBusy;

		private static readonly List<(PlayerAvatar av, int ownerActor)> _oldAvatarsScratch = new List<(PlayerAvatar, int)>(8);

		private static readonly WaitForSecondsRealtime _wait02 = new WaitForSecondsRealtime(0.2f);

		private static readonly WaitForSecondsRealtime _wait05 = new WaitForSecondsRealtime(0.5f);

		private static readonly WaitForSecondsRealtime _wait1 = new WaitForSecondsRealtime(1f);

		private static readonly WaitForSecondsRealtime _wait2 = new WaitForSecondsRealtime(2f);

		private static readonly WaitForSecondsRealtime _wait25 = new WaitForSecondsRealtime(2.5f);

		private static readonly WaitForSecondsRealtime _wait15 = new WaitForSecondsRealtime(1.5f);

		private const int ModuleBatchSize = 8;

		private const int ValuableBatchSize = 6;

		private static readonly FieldRef<ItemAttributes, int> _itemValueRef = TryItemValueRef();

		private const int ItemBatchSize = 8;

		private static readonly int[] _projectileOwnershipScratch = new int[2];

		private const float ChangeLevelCooldownSeconds = 0f;

		private const float WaitGeneratedTimeoutSeconds = 60f;

		private const float WaitGeneratedPollInterval = 0.2f;

		private const float CooldownLockedSentinelSeconds = 600f;

		private const string RoomNamePrefixCN = "[中途加入 (fix by Scorpio)] ";

		private const string RoomNamePrefixEN = "[Late Join (fix by Scorpio)] ";

		private static readonly FieldRef<DataDirector, string> _networkServerNameRef = TryFieldRefRoom();

		private static readonly FieldRef<RunManager, List<PlayerVoiceChat>> _runManagerVoiceChatsRef = TryFieldRefRM();

		private static readonly HashSet<int> _scenePersistentViewIds = new HashSet<int>(64);

		private static float _changeLevelCooldownUntil;

		private static bool _changeLevelCooldownRunning;

		private static bool? _lastSceneAllowed;

		private static readonly WaitForSecondsRealtime _waitGeneratedPoll = new WaitForSecondsRealtime(0.2f);

		private static bool _steamLobbyLocked;

		private static float _steamUnlockEnsureNextAt;

		private const float SteamUnlockEnsureInter