Decompiled source of LateJoin v2.8.0

RepoLateJoin.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
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 Photon.Voice;
using Photon.Voice.PUN;
using Photon.Voice.Unity;
using RepoLateJoin.Client;
using RepoLateJoin.Core;
using RepoLateJoin.Guard;
using RepoLateJoin.Join;
using RepoLateJoin.Lobby;
using RepoLateJoin.Menu;
using Steamworks;
using Steamworks.Data;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[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("RepoLateJoin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Unified late-join mod for R.E.P.O.")]
[assembly: AssemblyFileVersion("2.8.0.0")]
[assembly: AssemblyInformationalVersion("2.8.0")]
[assembly: AssemblyProduct("RepoLateJoin")]
[assembly: AssemblyTitle("RepoLateJoin")]
[assembly: AssemblyVersion("2.8.0.0")]
[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 RepoLateJoin
{
	[BepInPlugin("repolatejoin.unified", "RepoLateJoin", "2.8.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class Runner : MonoBehaviour
		{
			private void Update()
			{
				if (PunGate.PhotonInitialised)
				{
					Handshake.Tick();
					VoiceWatchdog.Tick();
					CartRescue.Tick();
					if (PunGate.Ready)
					{
						LobbyGate.Tick();
						ModuleEstimate.Tick();
						LocalCatchUp.Tick();
						LevelSnapshot.Tick();
						TruckScreen.Tick();
					}
				}
			}

			private void OnGUI()
			{
				StatusOverlay.Draw();
			}
		}

		internal const string Guid = "repolatejoin.unified";

		internal const string Version = "2.8.0";

		private static readonly Dictionary<string, string> Superseded = new Dictionary<string, string>
		{
			{ "ozoka.latejoin", "LateJoin (Ozoka Gaming)" },
			{ "chaos.holz.laterepo", "LateRepo (Chaosholz)" },
			{ "laterepo.fix.runtime", "LateRepo Fix (AST Studios)" },
			{ "jp.laterepo.introfix", "LateRepo Intro Fix (CapackMods)" },
			{ "knightmike.latejoin", "Late Join (knightmike)" },
			{ "zichen.latejoinnow", "LateJoinNow (Zichen)" },
			{ "com.repo.latejoinfixed", "LateJoinFixed (Scorpio)" },
			{ "com.sunwu.anti_Item_teleport", "AntiItemTeleport (sunwu)" }
		};

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			Log.Attach(((BaseUnityPlugin)this).Logger);
			ModConfig.Bind(((BaseUnityPlugin)this).Config);
			WarnAboutSupersededMods();
			Application.SetStackTraceLogType((LogType)3, (StackTraceLogType)0);
			Application.SetStackTraceLogType((LogType)2, (StackTraceLogType)0);
			TeleportGuard.Active = !Chainloader.PluginInfos.ContainsKey("com.sunwu.anti_Item_teleport");
			PlayerLimit.Watch();
			if (ModConfig.MaxPlayersEnabled.Value && Chainloader.PluginInfos.ContainsKey("Linkoid.Repo.RoboUnion"))
			{
				Log.Warn("RoboUnion also sets max players. Use either its MaxPlayers or ours (MorePlayer), not both - they overwrite each other. Turn MorePlayer / Enabled off to let RoboUnion decide.");
			}
			GameObject val = new GameObject("RepoLateJoin")
			{
				hideFlags = (HideFlags)61
			};
			Object.DontDestroyOnLoad((Object)val);
			JoinQueue.Attach((MonoBehaviour)(object)val.AddComponent<Runner>());
			_harmony = new Harmony("repolatejoin.unified");
			int num = ApplyPatches("RepoLateJoin.Patches");
			PunGate.Initialised += delegate
			{
				ApplyPatches("RepoLateJoin.Patches.Late");
				Handshake.Listen();
				LevelSnapshot.Listen();
				HostReports.Listen();
			};
			Log.Info("RepoLateJoin 2.8.0 loaded. Late join " + (ModConfig.On ? "on" : "off") + ", AntiCheat " + (TeleportGuard.Active ? "on" : "off (standalone AntiItemTeleport loaded)") + ", debug logging " + (Log.DebugOn ? "on" : "off") + ".");
			Log.Debug(num + " patch group(s) applied before Photon start.");
		}

		private int ApplyPatches(string ns)
		{
			Type[] array;
			try
			{
				array = (from t in typeof(Plugin).Assembly.GetTypes()
					where t.Namespace == ns && t.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0
					select t).ToArray();
			}
			catch (Exception ex)
			{
				Log.Error("Could not enumerate patch classes: " + ex);
				return 0;
			}
			int num = 0;
			Type[] array2 = array;
			foreach (Type type in array2)
			{
				try
				{
					_harmony.CreateClassProcessor(type).Patch();
					num++;
					Log.Debug("Applied patch group " + type.Name + ".");
				}
				catch (Exception ex2)
				{
					Log.Warn("Patch group " + type.Name + " could not be applied (" + ex2.GetType().Name + ": " + ex2.Message + "). That feature is disabled; everything else still works.");
				}
			}
			return num;
		}

		private void WarnAboutSupersededMods()
		{
			List<string> list = new List<string>();
			try
			{
				foreach (KeyValuePair<string, string> item in Superseded)
				{
					if (Chainloader.PluginInfos.ContainsKey(item.Key))
					{
						list.Add(item.Value);
					}
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not inspect the loaded plugin list: " + ex);
				return;
			}
			if (list.Count != 0)
			{
				Log.Warn("RepoLateJoin already contains the features of: " + string.Join(", ", list) + ". Remove " + ((list.Count == 1) ? "it" : "them") + " - running both leaves two mods fighting over the same lobby state.");
			}
		}
	}
}
namespace RepoLateJoin.Patches
{
	[HarmonyPatch(typeof(PlayerDeathHead), "UpdateParticleColors")]
	internal static class DeathHeadColorPatch
	{
		private static Exception Finalizer(Exception __exception)
		{
			if (__exception == null)
			{
				return null;
			}
			if (!ModConfig.On)
			{
				return __exception;
			}
			Log.Debug("Suppressed a death head colour update that ran before the head finished arriving over the network (" + __exception.GetType().Name + "). Particle colours stay default until the next cosmetics change.");
			return null;
		}
	}
	[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
	internal static class RunManagerChangeLevelPatch
	{
		private static readonly FieldInfo RestartingField = Reflect.Field(typeof(RunManager), "restarting");

		private static void Prefix()
		{
			if (!PunGate.Ready)
			{
				return;
			}
			try
			{
				if ((!SemiFunc.MenuLevel() && !SemiFunc.IsMasterClientOrSingleplayer()) || ((Object)(object)RunManager.instance != (Object)null && Reflect.Get(RestartingField, RunManager.instance, fallback: false)))
				{
					return;
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not evaluate the change-level guard: " + ex);
				return;
			}
			try
			{
				LobbyGate.BeginTransition();
				PhotonCacheCleaner.ClearSceneCache();
				PhotonCacheCleaner.ClearStaleLevelUpdateRpc();
				LevelStateSync.Clear();
				LevelEndWait.Begin(JoinQueue.StillLoading());
				JoinQueue.Reset();
			}
			catch (Exception ex2)
			{
				Log.Warn("Level-change housekeeping failed; the level change continues. " + ex2);
			}
		}
	}
	[HarmonyPatch(typeof(RunManager), "RestartScene")]
	internal static class RunManagerRestartScenePatch
	{
		private static void Prefix(RunManager __instance)
		{
			try
			{
				LevelEndWait.Check(__instance);
			}
			catch (Exception ex)
			{
				Log.Debug("Level-end wait check failed: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(ItemEquippable), "RPC_RequestEquip")]
	internal static class ItemRequestEquipPatch
	{
		private static bool Prefix(int physGrabberPhotonViewID)
		{
			try
			{
				return !InventoryGuard.HoldBack(physGrabberPhotonViewID);
			}
			catch (Exception ex)
			{
				Log.Debug("Equip request check failed: " + ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(ItemEquippable), "RPC_UpdateItemState")]
	internal static class ItemUpdateStatePatch
	{
		private static bool Prefix(ItemEquippable __instance, int state)
		{
			try
			{
				if (state == 2)
				{
					InventorySlotPatch.Register();
				}
				return InventoryGuard.AllowEquipState(__instance, state);
			}
			catch (Exception ex)
			{
				Log.Debug("Item state check failed: " + ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(MenuPageServerListCreateNew), "ButtonConfirm")]
	internal static class ServerNameTypedPatch
	{
		[HarmonyPriority(800)]
		private static void Prefix(MenuPageServerListCreateNew __instance)
		{
			try
			{
				RoomName.Typed(__instance);
			}
			catch (Exception ex)
			{
				Log.Debug("Could not read the typed server name: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(TruckScreenText), "MessageSendCustomRPC")]
	internal static class TruckScreenMessagePatch
	{
		private static bool Prefix(string message, PhotonMessageInfo _info)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return TruckScreen.AllowMessage(message, _info);
			}
			catch (Exception ex)
			{
				Log.Debug("Truck screen message check failed: " + ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(StaticGrabObject), "GrabStartedRPC")]
	internal static class StaticGrabStartedPatch
	{
		private static bool Prefix(int playerPhotonID)
		{
			try
			{
				return TruckScreen.AllowGrabReport(playerPhotonID);
			}
			catch (Exception ex)
			{
				Log.Debug("Grab report check failed: " + ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(StaticGrabObject), "GrabEndedRPC")]
	internal static class StaticGrabEndedPatch
	{
		private static bool Prefix(int playerPhotonID)
		{
			try
			{
				return TruckScreen.AllowGrabReport(playerPhotonID);
			}
			catch (Exception ex)
			{
				Log.Debug("Grab report check failed: " + ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(LevelGenerator), "Start")]
	internal static class LevelGeneratorStartPatch
	{
		private static void Prefix(LevelGenerator __instance)
		{
			PunGate.Mark();
			IntroSkip.Reset();
			ModuleEstimate.Reset();
			LocalCatchUp.Reset();
			JoinStatus.Clear();
			EnemyAwareness.ForgetDestroyed();
			if (!ModConfig.On)
			{
				return;
			}
			try
			{
				PhotonCacheCleaner.ClearStaleGenerationRpcs(__instance);
			}
			catch (Exception ex)
			{
				Log.Debug("Could not clear stale generation RPCs: " + ex);
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			if (__exception == null)
			{
				return null;
			}
			if (!IsSingleplayerPoolMiss(__exception))
			{
				return __exception;
			}
			Log.Debug("Suppressed an outdated RunManager.singleplayerPool access during level generation.");
			return null;
		}

		private static bool IsSingleplayerPoolMiss(Exception e)
		{
			while (e != null)
			{
				if (e is MissingFieldException && e.ToString().IndexOf("singleplayerPool", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
				e = e.InnerException;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Module), "ModuleConnectionSetRPC")]
	internal static class ModuleConnectionSetRpcPatch
	{
		private static bool Prefix(Module __instance, bool _top, bool _bottom, bool _right, bool _left, bool _first, PhotonMessageInfo _info)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return ModuleEstimate.OnHostData(__instance, new bool[5] { _top, _bottom, _right, _left, _first }, _info);
			}
			catch (Exception ex)
			{
				Log.Debug("Room connection comparison failed: " + ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(LoadingUI), "LevelAnimationStart")]
	internal static class LoadingUiAnimationStartPatch
	{
		private static bool Prefix(LoadingUI __instance)
		{
			return IntroSkip.Prefix(__instance);
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "Spawn")]
	internal static class PlayerAvatarSpawnPatch
	{
		private static readonly FieldInfo SpawnedField = Reflect.Field(typeof(PlayerAvatar), "spawned");

		private static bool Prefix(PlayerAvatar __instance, ref Vector3 position, ref Quaternion rotation)
		{
			if (!ModConfig.On || (Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!Reflect.Get(SpawnedField, __instance, fallback: false))
			{
				JoinSpawn.Choose(__instance, ref position, ref rotation);
				return true;
			}
			Log.Debug("Blocked a re-spawn of an already-spawned player so late joins do not teleport the party.");
			return false;
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "Awake")]
	internal static class PlayerAvatarAwakePatch
	{
		private static void Postfix(PlayerAvatar __instance)
		{
			try
			{
				if (ModConfig.On && (Object)(object)__instance != (Object)null)
				{
					EnemyAwareness.RegisterAllPlayers();
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Enemy registration at Awake failed: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "Start")]
	internal static class PlayerAvatarStartPatch
	{
		private static void Postfix(PlayerAvatar __instance)
		{
			if (PunGate.Ready && !((Object)(object)__instance == (Object)null) && !PhotonNetwork.IsMasterClient && !((Object)(object)__instance.photonView == (Object)null) && __instance.photonView.IsMine)
			{
				IntroSkip.OnLocalAvatarStart(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(SteamManager), "LockLobby")]
	internal static class SteamManagerLockLobbyPatch
	{
		private static bool Prefix()
		{
			if (!PunGate.Ready || LobbyGate.SelfLocking)
			{
				return true;
			}
			if (!LobbyGate.JoinAllowedNow())
			{
				return true;
			}
			Log.Debug("Kept the lobby unlocked because late joining is currently allowed.");
			LobbyGate.Apply("lock suppressed", force: true);
			return false;
		}
	}
	[HarmonyPatch(typeof(SteamManager), "OnLobbyEntered")]
	internal static class SteamLobbyEnteredPatch
	{
		private static void Prefix(Lobby _lobby, ref Lobby ___currentLobby)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			SteamLobbyJoin.StayIn(_lobby, ref ___currentLobby);
		}
	}
	[HarmonyPatch(typeof(RunManagerPUN), "SendJoinSteamLobby")]
	internal static class SteamLobbySendPatch
	{
		private static bool Prefix()
		{
			try
			{
				return !SteamLobbyJoin.SendJoin();
			}
			catch (Exception ex)
			{
				Log.Debug("Steam lobby send failed, using the game's own: " + ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(NetworkManager), "OnEnable")]
	internal static class NetworkManagerOnEnablePatch
	{
		private static void Postfix()
		{
			PunGate.Mark();
		}
	}
	[HarmonyPatch(typeof(NetworkManager), "OnPlayerEnteredRoom")]
	internal static class NetworkManagerPlayerEnteredPatch
	{
		private static void Postfix(Player newPlayer)
		{
			if (PunGate.PhotonInitialised)
			{
				SteamLobbyJoin.OnPlayerEntered(newPlayer);
			}
			if (PunGate.Ready)
			{
				JoinQueue.OnPlayerEntered(newPlayer);
			}
		}
	}
	[HarmonyPatch(typeof(NetworkManager), "OnPlayerLeftRoom")]
	internal static class NetworkManagerPlayerLeftPatch
	{
		private static void Postfix(Player otherPlayer)
		{
			if (PunGate.Ready)
			{
				JoinQueue.OnPlayerLeft(otherPlayer);
			}
		}
	}
	[HarmonyPatch(typeof(EnemyOnScreen), "OnEnable")]
	internal static class EnemyOnScreenEnablePatch
	{
		private static void Prefix(EnemyOnScreen __instance)
		{
			EnemyAwareness.EnsureFor(__instance);
		}
	}
	[HarmonyPatch(typeof(EnemyVision), "OnEnable")]
	internal static class EnemyVisionEnablePatch
	{
		private static void Prefix(EnemyVision __instance)
		{
			EnemyAwareness.EnsureFor(__instance);
		}
	}
	[HarmonyPatch(typeof(EnemyOnScreen), "GetOnScreen")]
	internal static class EnemyGetOnScreenPatch
	{
		private static void Prefix(EnemyOnScreen __instance, PlayerAvatar _playerAvatar)
		{
			if ((Object)(object)_playerAvatar != (Object)null && (Object)(object)_playerAvatar.photonView != (Object)null && _playerAvatar.photonView.ViewID != 0)
			{
				__instance.PlayerAdded(_playerAvatar.photonView.ViewID);
			}
		}
	}
	[HarmonyPatch]
	internal static class GrabObjectDestroyPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			string[] array = new string[2] { "PhysGrabObject", "StaticGrabObject" };
			for (int i = 0; i < array.Length; i++)
			{
				MethodInfo methodInfo = Reflect.Method(Reflect.TypeByName(array[i]), "DestroyPhysGrabObjectRPC");
				if (methodInfo != null)
				{
					yield return methodInfo;
				}
			}
		}

		private static void Prefix(object __instance)
		{
			if (PunGate.Ready && PhotonNetwork.IsMasterClient)
			{
				Component val = (Component)((__instance is Component) ? __instance : null);
				if (val != null && (Object)(object)val != (Object)null)
				{
					LevelStateSync.RememberDestroyed(val.GetComponent<PhotonView>());
				}
			}
		}
	}
	[HarmonyPatch(typeof(GameManager), "Awake")]
	internal static class GameManagerAwakePatch
	{
		private static void Postfix()
		{
			PlayerLimit.Apply();
		}
	}
	[HarmonyPatch(typeof(MenuPageEsc), "Start")]
	internal static class MenuPageEscStartPatch
	{
		private static void Postfix(MenuPageEsc __instance)
		{
			InviteButton.TryAdd(__instance);
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "Teleport", new Type[]
	{
		typeof(Vector3),
		typeof(Quaternion)
	})]
	internal static class TeleportDepthPatch
	{
		private static void Prefix()
		{
			TeleportGuard.TeleportEnter();
		}

		private static Exception Finalizer(Exception __exception)
		{
			TeleportGuard.TeleportLeave();
			return __exception;
		}
	}
	[HarmonyPatch(typeof(NetworkManager), "OnEventReceivedCustom")]
	internal static class FakeKickPatch
	{
		private static bool Prefix(EventData photonEvent)
		{
			if (photonEvent == null || photonEvent.Code != 199 || photonEvent.Sender <= 0)
			{
				return true;
			}
			Player masterClient = PhotonNetwork.MasterClient;
			if (masterClient != null && photonEvent.Sender == masterClient.ActorNumber)
			{
				return true;
			}
			Room currentRoom = PhotonNetwork.CurrentRoom;
			Player sender = ((currentRoom != null) ? currentRoom.GetPlayer(photonEvent.Sender, false) : null);
			HostReports.Warn("fake-kick:" + photonEvent.Sender, () => "[Blocked] a fake kick from " + ((sender != null) ? TeleportGuard.Describe(sender) : ("#" + photonEvent.Sender)) + " - only the host can kick.");
			return false;
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "SetPositionRPC")]
	internal static class SetPositionRpcPatch
	{
		private static bool Prefix(PhysGrabObject __instance)
		{
			try
			{
				return TeleportGuard.AllowSetPosition(__instance);
			}
			catch (Exception ex)
			{
				Log.Debug("SetPositionRPC check failed: " + ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "Update")]
	internal static class VoidRescuePatch
	{
		private const float VoidY = -50f;

		private static void Prefix(PhysGrabObject __instance)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (!(((Component)__instance).transform.position.y >= -50f) && TeleportGuard.Active && TeleportGuard.CanMoveObjects && !__instance.dead && __instance.spawned && !((Object)(object)((Component)__instance).GetComponent<ValuableObject>() == (Object)null) && TeleportGuard.MoveToTruck(__instance))
			{
				HostReports.Share("rescue", "[Rescued] " + HostReports.NameOf((Object)(object)__instance) + " -> truck");
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "DestroyObject")]
	internal static class CartRescuePatch
	{
		private static bool Prefix(PhysGrabObjectImpactDetector __instance)
		{
			if (CartRescue.On && CartRescue.Protects(__instance))
			{
				return !CartRescue.Rescue(((Component)__instance).GetComponent<PhysGrabObject>());
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(GumballValuable), "UpdateHypnosisLinesList")]
	internal static class GumballValuableHypnosisPatch
	{
		private static readonly FieldInfo LinesField = Reflect.Field(typeof(GumballValuable), "hypnosisLines");

		private static readonly FieldInfo InSightField = Reflect.Field(typeof(GumballValuable), "playerInSight");

		private static readonly FieldInfo SeenField = Reflect.Field(typeof(GumballValuable), "previouslySeenList");

		private static void Prefix(GumballValuable __instance)
		{
			if (!ModConfig.On || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			try
			{
				List<PlayerAvatar> list = SemiFunc.PlayerGetList();
				if (list != null)
				{
					GrowFlags(InSightField, __instance, list.Count);
					GrowFlags(SeenField, __instance, list.Count);
					GrowLines(__instance, list);
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not grow the gumball hypnosis lists: " + ex);
			}
		}

		private static void GrowFlags(FieldInfo field, GumballValuable instance, int target)
		{
			if (field?.GetValue(instance) is List<bool> list)
			{
				while (list.Count < target)
				{
					list.Add(item: false);
				}
			}
		}

		private static void GrowLines(GumballValuable instance, List<PlayerAvatar> players)
		{
			//IL_00c6: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			if (!(LinesField?.GetValue(instance) is List<GameObject> list) || list.Count >= players.Count)
			{
				return;
			}
			GameObject hypnosisLine = instance.hypnosisLine;
			Transform hypnosisLinesParenTransform = instance.HypnosisLinesParenTransform;
			if ((Object)(object)hypnosisLine == (Object)null)
			{
				return;
			}
			int num = 0;
			for (int i = list.Count; i < players.Count; i++)
			{
				PlayerAvatar val = players[i];
				if ((Object)(object)val == (Object)null)
				{
					list.Add(null);
					continue;
				}
				GameObject val2 = Object.Instantiate<GameObject>(hypnosisLine, ((Component)instance).transform.position, Quaternion.identity);
				Transform val3 = null;
				try
				{
					PlayerLocalCamera localCamera = val.localCamera;
					val3 = ((localCamera != null) ? localCamera.GetOverrideTransform() : null);
				}
				catch
				{
				}
				if ((Object)(object)val3 != (Object)null)
				{
					val2.transform.SetParent(val3);
					val2.transform.localPosition = Vector3.zero;
					val2.transform.localRotation = Quaternion.identity;
				}
				EyeLines component = val2.GetComponent<EyeLines>();
				if ((Object)(object)component != (Object)null)
				{
					component.InitializeLine(val);
				}
				if ((Object)(object)hypnosisLinesParenTransform != (Object)null)
				{
					val2.transform.SetParent(hypnosisLinesParenTransform);
				}
				val2.SetActive(false);
				list.Add(val2);
				num++;
			}
			if (num > 0)
			{
				Log.Debug("Grew a gumball valuable's hypnosis line list by " + num + " so it stops throwing once somebody joins mid-level.");
			}
		}
	}
	[HarmonyPatch(typeof(RunManager), "UpdateLevel")]
	internal static class InventorySlotPatch
	{
		private static void Postfix()
		{
			Register();
		}

		internal static void Register()
		{
			if (!ModConfig.On)
			{
				return;
			}
			try
			{
				Inventory instance = Inventory.instance;
				if ((Object)(object)instance == (Object)null || !instance.GetAllSpots().Exists((InventorySpot s) => (Object)(object)s == (Object)null) || SemiFunc.MenuLevel() || SemiFunc.RunIsArena())
				{
					return;
				}
				int num = 0;
				InventorySpot[] array = Object.FindObjectsOfType<InventorySpot>(true);
				foreach (InventorySpot val in array)
				{
					if (!((Object)(object)instance.GetSpotByIndex(val.inventorySpotIndex) != (Object)null))
					{
						instance.InventorySpotAddAtIndex(val, val.inventorySpotIndex);
						num++;
					}
				}
				if (num > 0)
				{
					Log.Debug("Registered " + num + " inventory slot(s) the join skipped.");
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not register inventory slots: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerVoiceChat), "ToggleMixer")]
	internal static class VoiceMixerGuardPatch
	{
		private static bool Prefix(AudioSource ___audioSource)
		{
			return (Object)(object)___audioSource != (Object)null;
		}
	}
	[HarmonyPatch(typeof(PlayerVoiceChat), "Start")]
	internal static class VoiceStartPatch
	{
		private static void Postfix(PlayerVoiceChat __instance, PlayerAvatar ___playerAvatar)
		{
			try
			{
				if (!((Object)(object)___playerAvatar == (Object)null) && !SemiFunc.MenuLevel())
				{
					__instance.ToggleMixer(false, false);
					Log.Debug("Linked " + ((Object)___playerAvatar).name + "'s voice that was claimed before it started.");
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not link an early voice: " + ex);
			}
		}
	}
	[HarmonyPatch]
	internal static class SteamIdWaitPatch
	{
		private static readonly FieldInfo GrabberAvatarField = Reflect.Field(typeof(PhysGrabber), "playerAvatar");

		private static IEnumerable<MethodBase> TargetMethods()
		{
			MethodInfo[] array = new MethodInfo[4]
			{
				Reflect.Method(typeof(PlayerAvatar), "LateStart"),
				Reflect.Method(typeof(PhysGrabber), "LateStart"),
				Reflect.Method(typeof(PlayerTumble), "Setup"),
				Reflect.Method(typeof(PlayerDeathHead), "Setup")
			};
			foreach (MethodInfo methodInfo in array)
			{
				if (methodInfo != null)
				{
					yield return methodInfo;
				}
			}
		}

		private static void Postfix(object __instance, ref IEnumerator __result)
		{
			PlayerAvatar val = (PlayerAvatar)((__instance is PlayerAvatar) ? __instance : null);
			Func<PlayerAvatar> owner;
			if (val == null)
			{
				PhysGrabber val2 = (PhysGrabber)((__instance is PhysGrabber) ? __instance : null);
				if (val2 == null)
				{
					PlayerTumble val3 = (PlayerTumble)((__instance is PlayerTumble) ? __instance : null);
					if (val3 == null)
					{
						PlayerDeathHead val4 = (PlayerDeathHead)((__instance is PlayerDeathHead) ? __instance : null);
						if (val4 == null || !SemiFunc.IsMasterClient())
						{
							return;
						}
						owner = () => val4.playerAvatar;
					}
					else
					{
						if (!SemiFunc.IsMasterClient())
						{
							return;
						}
						owner = () => val3.playerAvatar;
					}
				}
				else
				{
					owner = () => Reflect.Get<PlayerAvatar>(GrabberAvatarField, val2);
				}
			}
			else
			{
				owner = () => val;
			}
			__result = AfterSteamId(owner, __result);
		}

		private static IEnumerator AfterSteamId(Func<PlayerAvatar> owner, IEnumerator original)
		{
			float until = Time.time + 10f;
			while (Time.time < until && string.IsNullOrEmpty(Read(owner)))
			{
				yield return (object)new WaitForSeconds(0.2f);
			}
			while (original.MoveNext())
			{
				yield return original.Current;
			}
		}

		private static string Read(Func<PlayerAvatar> owner)
		{
			try
			{
				PlayerAvatar val = owner();
				return ((Object)(object)val != (Object)null) ? SemiFunc.PlayerGetSteamID(val) : null;
			}
			catch
			{
				return null;
			}
		}
	}
	[HarmonyPatch(typeof(TruckScreenText), "NextLineRPC")]
	internal static class TruckScreenLinePatch
	{
		private static readonly FieldInfo PageField = Reflect.Field(typeof(TruckScreenText), "currentPageIndex");

		private static bool Prefix(TruckScreenText __instance, int index, int _currentLineIndex)
		{
			int num = Reflect.Get(PageField, __instance, -1);
			if (num >= 0 && num < __instance.pages.Count)
			{
				List<TextLine> textLines = __instance.pages[num].textLines;
				if (_currentLineIndex >= 0 && _currentLineIndex < textLines.Count && index >= 0 && index < textLines[_currentLineIndex].textLines.Count)
				{
					return true;
				}
			}
			Log.Debug("Skipped truck screen line " + _currentLineIndex + "/" + index + " not on our page " + num + ".");
			return false;
		}
	}
	[HarmonyPatch(typeof(EnemyBirthdayBoy), "RemoveBalloonRPC")]
	internal static class BalloonRemovePatch
	{
		private static bool Prefix(EnemyBirthdayBoy __instance, Vector3 _position)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return __instance.balloons.ContainsKey(_position);
		}
	}
	[HarmonyPatch(typeof(EnemyBirthdayBoy), "SpawnBalloonRPC")]
	internal static class BalloonSpawnPatch
	{
		private static bool Prefix(EnemyBirthdayBoy __instance, Vector3 _keyPos)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (__instance.balloons.TryGetValue(_keyPos, out var value))
			{
				return (Object)(object)value == (Object)null;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(ExtractionPoint), "ActivateTheFirstExtractionPointAutomaticallyWhenAPlayerLeaveTruck")]
	internal static class ExtractionAutoPressPatch
	{
		private static bool Prefix()
		{
			if (!IntroSkip.LateJoined || PhotonNetwork.IsMasterClient)
			{
				return true;
			}
			Log.Debug("Left the first extraction point alone: we joined this level late.");
			return false;
		}
	}
	[HarmonyPatch(typeof(LevelPoint), "Start")]
	internal static class LevelPointLinkPatch
	{
		private static void Postfix(LevelPoint __instance)
		{
			if (ModConfig.On && __instance.ModuleConnect && !((Object)(object)LevelGenerator.Instance == (Object)null) && LevelGenerator.Instance.Generated)
			{
				((MonoBehaviour)__instance).StartCoroutine(Relink(__instance));
			}
		}

		private static IEnumerator Relink(LevelPoint point)
		{
			yield return null;
			if ((Object)(object)point == (Object)null || (Object)(object)LevelGenerator.Instance == (Object)null)
			{
				yield break;
			}
			Transform transform = ((Component)point).transform;
			foreach (LevelPoint levelPathPoint in LevelGenerator.Instance.LevelPathPoints)
			{
				if ((Object)(object)levelPathPoint == (Object)null || (Object)(object)levelPathPoint == (Object)(object)point || !levelPathPoint.ModuleConnect || point.ConnectedPoints.Contains(levelPathPoint))
				{
					continue;
				}
				Transform transform2 = ((Component)levelPathPoint).transform;
				if (Vector3.Distance(transform.position, transform2.position) < 15f && Vector3.Dot(transform2.forward, transform.forward) <= -0.8f)
				{
					Vector3 forward = transform2.forward;
					Vector3 val = transform.position - transform2.position;
					if (Vector3.Dot(forward, ((Vector3)(ref val)).normalized) > 0.8f)
					{
						point.ConnectedPoints.Add(levelPathPoint);
					}
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class LightManagerActivationPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			Type type = Reflect.TypeByName("LightManager");
			string[] array = new string[2] { "HandleLightActivation", "HandleEmissionActivation" };
			foreach (string name in array)
			{
				MethodInfo methodInfo = Reflect.Method(type, name);
				if (methodInfo != null)
				{
					yield return methodInfo;
				}
			}
		}

		private static Exception Finalizer(Exception __exception, MethodBase __originalMethod)
		{
			if (__exception == null)
			{
				return null;
			}
			if (!ModConfig.On)
			{
				return __exception;
			}
			Log.Debug("Suppressed a " + __exception.GetType().Name + " in LightManager." + (__originalMethod?.Name ?? "?") + " so the rest of the level's lights still turn on.");
			return null;
		}
	}
	[HarmonyPatch]
	internal static class ReviveItemTargetPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(ItemReviveItem), "Update", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(ItemReviveItem), "FixedUpdate", (Type[])null, (Type[])null);
		}

		private static void Prefix(ItemReviveItem __instance, ref PlayerAvatar ___playerTarget, ref bool ___hasTarget)
		{
			if (!___hasTarget || (Object)(object)___playerTarget != (Object)null)
			{
				return;
			}
			___playerTarget = null;
			___hasTarget = false;
			if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient)
			{
				PhotonView component = ((Component)__instance).GetComponent<PhotonView>();
				if (component != null)
				{
					component.RPC("UpdatePlayerTargetRPC", (RpcTarget)0, new object[1] { -1 });
				}
			}
		}
	}
	[HarmonyPatch(typeof(SpectateCamera), "StopSpectate")]
	internal static class SpectateStopPatch
	{
		private static Exception Finalizer(SpectateCamera __instance, Exception __exception)
		{
			if (__exception == null)
			{
				return null;
			}
			if ((Object)(object)__instance != (Object)null)
			{
				if ((Object)(object)__instance.normalTransformPivot != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)__instance.normalTransformPivot).gameObject);
				}
				Object.Destroy((Object)(object)((Component)__instance).gameObject);
			}
			Log.Debug("Spectate camera cleanup threw (" + __exception.Message + "); finished it so the revive completes.");
			return null;
		}
	}
	[HarmonyPatch(typeof(StaticGrabObject), "Update")]
	internal static class StaticGrabLeaverPatch
	{
		private static void Prefix(StaticGrabObject __instance)
		{
			if (__instance.playerGrabbing.Count > 0)
			{
				__instance.playerGrabbing.RemoveAll((PhysGrabber p) => (Object)(object)p == (Object)null || (Object)(object)p.physGrabPoint == (Object)null);
			}
		}
	}
	[HarmonyPatch]
	internal static class TornDownPlayerPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			(Type, string)[] array = new(Type, string)[7]
			{
				(typeof(PlayerEyes), "LookAtTransform"),
				(typeof(PlayerEyes), "ClosestPhysGrabPoint"),
				(typeof(PhysGrabBeam), "DrawCurve"),
				(typeof(PhysGrabber), "ObjectTurning"),
				(typeof(PhysGrabber), "OverrideDisableGrabLookAt"),
				(typeof(ItemUpgradePlayerTumbleWingsLogic), "FixedUpdate"),
				(typeof(SlowWalkerAttack), "SuckInListPlayerUpdate")
			};
			for (int i = 0; i < array.Length; i++)
			{
				(Type, string) tuple = array[i];
				Type item = tuple.Item1;
				string item2 = tuple.Item2;
				MethodInfo methodInfo = Reflect.Method(item, item2);
				if (methodInfo != null)
				{
					yield return methodInfo;
				}
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			if (!(__exception is NullReferenceException) && !(__exception is MissingReferenceException))
			{
				return __exception;
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(PlayerHealth), "Update")]
	internal static class PlayerHealthUpdatePatch
	{
		private static bool Prefix(PlayerHealth __instance, ref PlayerAvatar ___playerAvatar)
		{
			if ((Object)(object)___playerAvatar != (Object)null)
			{
				return true;
			}
			if (!__instance.isMenuAvatar || (Object)(object)PlayerAvatar.instance == (Object)null)
			{
				return false;
			}
			___playerAvatar = PlayerAvatar.instance;
			return true;
		}
	}
	[HarmonyPatch]
	internal static class AmbientLightAfterLeavePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			string[] array = new string[2] { "AmbientLightLogic", "FogLogic" };
			foreach (string name in array)
			{
				MethodInfo methodInfo = Reflect.Method(typeof(EnvironmentDirector), name);
				if (methodInfo != null)
				{
					yield return methodInfo;
				}
			}
		}

		private static bool Prefix()
		{
			if ((Object)(object)PlayerController.instance != (Object)null)
			{
				return (Object)(object)PlayerController.instance.playerAvatarScript != (Object)null;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "PhysicsGrabbingManipulation")]
	internal static class GrabberLeaverPatch
	{
		private static void Prefix(PhysGrabObject __instance)
		{
			if (__instance.playerGrabbing.Count > 0)
			{
				__instance.playerGrabbing.RemoveAll((PhysGrabber g) => (Object)(object)g == (Object)null || (Object)(object)g.playerAvatar == (Object)null);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "Awake")]
	internal static class GrabberViewPatch
	{
		private static void Postfix(PlayerAvatar __instance)
		{
			PhysGrabber component = ((Component)__instance).GetComponent<PhysGrabber>();
			if (!((Object)(object)component == (Object)null))
			{
				if ((Object)(object)component.photonView == (Object)null)
				{
					component.photonView = __instance.photonView;
				}
				if ((Object)(object)component.playerAvatar == (Object)null)
				{
					component.playerAvatar = __instance;
				}
			}
		}
	}
	[HarmonyPatch(typeof(PlayerDeathEffects), "GetColors")]
	internal static class DeathColorsPatch
	{
		private static Exception Finalizer(Exception __exception, ref GradientColorKey[] __result)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			if (__exception == null)
			{
				return null;
			}
			__result = (GradientColorKey[])(object)new GradientColorKey[2]
			{
				new GradientColorKey(Color.white, 0f),
				new GradientColorKey(Color.gray, 1f)
			};
			Log.Debug("Death effect colours were not there yet (" + __exception.GetType().Name + "); used plain ones.");
			return null;
		}
	}
	[HarmonyPatch(typeof(EnemySlowMouthAttached), "Update")]
	internal static class SpewerMouthPatch
	{
		private static readonly FieldInfo EnemyField = Reflect.Field(typeof(EnemySlowMouthAttached), "enemySlowMouth");

		private static Exception Finalizer(EnemySlowMouthAttached __instance, Exception __exception)
		{
			if (!(__exception is NullReferenceException) && !(__exception is MissingReferenceException))
			{
				return __exception;
			}
			if ((Object)(object)__instance == (Object)null || (Object)(object)Reflect.Get<EnemySlowMouth>(EnemyField, __instance) != (Object)null)
			{
				return null;
			}
			Transform[] array = (Transform[])(object)new Transform[3] { __instance.otherTopJawTransform, __instance.otherBotJawTransform, __instance.localParentTransform };
			foreach (Transform val in array)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
			}
			Object.Destroy((Object)(object)((Component)__instance).gameObject);
			Log.Debug("Removed a Spewer mouth left on a player after its enemy was gone.");
			return null;
		}
	}
	[HarmonyPatch]
	internal static class ExtractionActivationPatch
	{
		private static readonly FieldInfo StateField = Reflect.Field(typeof(ExtractionPoint), "currentState");

		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(RoundDirector), "RequestExtractionPointActivationRPC", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(RoundDirector), "ExtractionPointActivate", (Type[])null, (Type[])null);
		}

		private static bool Prefix(int photonViewID)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Invalid comparison between Unknown and I4
			if (!SemiFunc.IsMultiplayer() || !PhotonNetwork.IsMasterClient)
			{
				return true;
			}
			PhotonView val = PhotonView.Find(photonViewID);
			ExtractionPoint val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<ExtractionPoint>() : null);
			if ((Object)(object)val2 != (Object)null && !val2.isLocked && (int)Reflect.Get<State>(StateField, val2, (State)1) == 1)
			{
				return true;
			}
			Log.Debug("Ignored a request to start extraction point " + photonViewID + ": " + (((Object)(object)val2 == (Object)null) ? "no such point" : (val2.isLocked ? "locked" : "not ready")) + ".");
			return false;
		}
	}
	[HarmonyPatch(typeof(UnityLogSource), "OnUnityLogMessageReceived")]
	internal static class KinematicWarningPatch
	{
		private static bool Prefix(string message)
		{
			if (message != null)
			{
				if (message.IndexOf("velocity of a kinematic body is not supported", StringComparison.Ordinal) < 0)
				{
					return message.IndexOf("BoxCollider does not support negative scale or size", StringComparison.Ordinal) < 0;
				}
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(PlayerNameChecker), "Update")]
	internal static class PlayerNameTagPatch
	{
		private const float Grace = 5f;

		private static readonly FieldInfo TagField = Reflect.Field(typeof(PlayerAvatar), "worldSpaceUIPlayerName");

		private static readonly FieldInfo LocalField = Reflect.Field(typeof(PlayerAvatar), "isLocal");

		private static readonly Dictionary<PlayerAvatar, float> MissingSince = new Dictionary<PlayerAvatar, float>();

		private static float _next;

		private static bool Prefix()
		{
			if ((Object)(object)Camera.main == (Object)null)
			{
				return false;
			}
			if (Time.unscaledTime < _next)
			{
				return true;
			}
			_next = Time.unscaledTime + 1f;
			try
			{
				GiveMissingTags();
			}
			catch (Exception ex)
			{
				Log.Debug("Could not give a player their name tag: " + ex);
			}
			return true;
		}

		private static void GiveMissingTags()
		{
			if ((Object)(object)WorldSpaceUIParent.instance == (Object)null || (Object)(object)GameDirector.instance == (Object)null || SemiFunc.MenuLevel())
			{
				return;
			}
			HashSet<PlayerAvatar> hashSet = new HashSet<PlayerAvatar>();
			foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
			{
				if (!((Object)(object)player == (Object)null) && !Reflect.Get(LocalField, player, fallback: true) && !((Object)(object)Reflect.Get<WorldSpaceUIPlayerName>(TagField, player) != (Object)null) && !string.IsNullOrEmpty(SemiFunc.PlayerGetName(player)))
				{
					hashSet.Add(player);
					if (!MissingSince.TryGetValue(player, out var value))
					{
						value = (MissingSince[player] = Time.unscaledTime);
					}
					if (!(Time.unscaledTime - value < 5f))
					{
						WorldSpaceUIParent.instance.PlayerName(player);
						Log.Debug("Gave " + SemiFunc.PlayerGetName(player) + " the name tag their start never made.");
					}
				}
			}
			foreach (PlayerAvatar item in new List<PlayerAvatar>(MissingSince.Keys))
			{
				if (!hashSet.Contains(item))
				{
					MissingSince.Remove(item);
				}
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			if (!(__exception is NullReferenceException) && !(__exception is MissingReferenceException))
			{
				return __exception;
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(EnemyOogly), "StateCeilingRoam")]
	internal static class OoglyCeilingRoamPatch
	{
		private static readonly FieldInfo PointField = Reflect.Field(typeof(EnemyOogly), "currentLevelPoint");

		private static readonly FieldInfo StartRoomField = Reflect.Field(typeof(LevelPoint), "inStartRoom");

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			if (PointField == null || StartRoomField == null)
			{
				return list;
			}
			for (int i = 0; i + 1 < list.Count; i++)
			{
				if (CodeInstructionExtensions.LoadsField(list[i], PointField, false) && CodeInstructionExtensions.LoadsField(list[i + 1], StartRoomField, false))
				{
					list[i].opcode = OpCodes.Call;
					list[i].operand = AccessTools.Method(typeof(OoglyCeilingRoamPatch), "InStartRoom", (Type[])null, (Type[])null);
					list.RemoveAt(i + 1);
				}
			}
			return list;
		}

		public static bool InStartRoom(EnemyOogly oogly)
		{
			LevelPoint val = Reflect.Get<LevelPoint>(PointField, oogly);
			if ((Object)(object)val != (Object)null)
			{
				return Reflect.Get(StartRoomField, val, fallback: false);
			}
			return false;
		}
	}
}
namespace RepoLateJoin.Patches.Late
{
	[HarmonyPatch(typeof(NetworkConnect), "OnJoinedRoom")]
	internal static class NetworkConnectJoinedRoomPatch
	{
		private static void Postfix()
		{
			JoinRetry.Joined();
			TeleportGuard.NewRoom();
			Handshake.Greet();
		}
	}
	[HarmonyPatch(typeof(NetworkConnect), "OnJoinRoomFailed")]
	internal static class JoinRetryPatch
	{
		private static bool Prefix(NetworkConnect __instance, short returnCode, string _cause)
		{
			try
			{
				return JoinRetry.OnJoinFailed(__instance, returnCode, _cause);
			}
			catch (Exception ex)
			{
				Log.Debug("Join retry check failed: " + ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(NetworkConnect), "OnConnectedToMaster")]
	internal static class ConnectionTimeoutPatch
	{
		private static void Prefix()
		{
			try
			{
				LoadBalancingClient networkingClient = PhotonNetwork.NetworkingClient;
				LoadBalancingPeer val = ((networkingClient != null) ? networkingClient.LoadBalancingPeer : null);
				if (val != null)
				{
					((PhotonPeer)val).DisconnectTimeout = Math.Max(((PhotonPeer)val).DisconnectTimeout, 30000);
					((PhotonPeer)val).SentCountAllowance = Math.Max(((PhotonPeer)val).SentCountAllowance, 10);
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not raise the connection timeout: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(LoadBalancingClient), "OpCreateRoom")]
	internal static class RoomNameCreatePatch
	{
		[HarmonyPriority(0)]
		private static void Prefix(EnterRoomParams enterRoomParams)
		{
			try
			{
				RoomName.Create(enterRoomParams?.RoomOptions?.CustomRoomProperties);
			}
			catch (Exception ex)
			{
				Log.Debug("Could not set the server name: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(Room), "SetCustomProperties")]
	internal static class RoomNameKeepPatch
	{
		[HarmonyPriority(0)]
		private static void Prefix(Hashtable propertiesToSet)
		{
			try
			{
				RoomName.Keep(propertiesToSet);
			}
			catch (Exception ex)
			{
				Log.Debug("Could not keep the server name: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(PhotonNetwork), "ExecuteRpc")]
	internal static class RpcSenderPatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix(Hashtable rpcData, Player sender)
		{
			RpcContext.Enter(sender);
			try
			{
				return !TeleportGuard.ShouldDrop(rpcData, sender) && !ForeignRpc.Drop(rpcData);
			}
			catch (Exception ex)
			{
				Log.Debug("RPC check failed: " + ex);
				return true;
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			RpcContext.Leave();
			return __exception;
		}
	}
	[HarmonyPatch]
	internal static class PublishUserIdPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(LoadBalancingClient), "OpCreateRoom", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(LoadBalancingClient), "OpJoinOrCreateRoom", (Type[])null, (Type[])null);
		}

		private static void Prefix(EnterRoomParams enterRoomParams)
		{
			TeleportGuard.PublishUserId(enterRoomParams);
		}
	}
	[HarmonyPatch(typeof(PhotonView), "OnDestroy")]
	internal static class DestroyedViewCachePatch
	{
		private static readonly FieldInfo RemovedField = Reflect.Field(typeof(PhotonView), "removedFromLocalViewList");

		private static void Prefix(PhotonView __instance)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)__instance == (Object)null || !PunGate.Ready || !PhotonNetwork.IsMasterClient || !PhotonNetwork.InRoom || !PhotonNetwork.IsMessageQueueRunning || __instance.InstantiationId < 1 || __instance.ViewID != __instance.InstantiationId || !__instance.IsMine || Reflect.Get(RemovedField, __instance, fallback: true))
				{
					return;
				}
				Scene scene = ((Component)__instance).gameObject.scene;
				if (((Scene)(ref scene)).buildIndex != -1)
				{
					if (PhotonCacheCleaner.ClearOne(__instance))
					{
						Log.Debug("Removed destroyed " + ((Object)((Component)__instance).gameObject).name + " (view " + __instance.ViewID + ") from the room cache.");
					}
					PhotonCacheCleaner.ClearBufferedRpcs(__instance);
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch(typeof(VoiceConnection), "OnRemoteVoiceInfo")]
	internal static class VoiceArrivingPatch
	{
		private static void Prefix()
		{
			VoiceWatchdog.NewStreamArriving = true;
		}

		private static Exception Finalizer(Exception __exception)
		{
			VoiceWatchdog.NewStreamArriving = false;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Speaker), "Link")]
	internal static class VoiceRelinkPatch
	{
		private static void Prefix(Speaker __instance, RemoteVoiceLink stream)
		{
			try
			{
				VoiceWatchdog.BeforeLink(__instance, stream);
			}
			catch (Exception ex)
			{
				Log.Debug("Could not replace a voice stream: " + ex);
			}
		}
	}
}
namespace RepoLateJoin.Menu
{
	internal static class InviteButton
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static UnityAction <0>__OpenInviteOverlay;
		}

		private const string CloneName = "RepoLateJoin_InviteButton";

		private static readonly FieldInfo OriginalPositionField = Reflect.Field(typeof(MenuButton), "originalButtonPosition");

		internal static void TryAdd(MenuPageEsc page)
		{
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			if (!PunGate.Ready || !ModConfig.On || (Object)(object)page == (Object)null)
			{
				return;
			}
			if (!PhotonNetwork.InRoom || !LobbyGate.SceneAllowsJoin())
			{
				Log.Debug("Skipping the invite button: this scene does not accept joins.");
				return;
			}
			try
			{
				MenuButton val = FindQuitButton(page);
				if ((Object)(object)val == (Object)null)
				{
					Log.Debug("Could not find the quit button to anchor the invite button to.");
					return;
				}
				Transform parent = ((Component)val).transform.parent;
				if ((Object)(object)parent == (Object)null || (Object)(object)parent.Find("RepoLateJoin_InviteButton") != (Object)null)
				{
					return;
				}
				GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, parent);
				((Object)val2).name = "RepoLateJoin_InviteButton";
				MenuButtonPopUp component = val2.GetComponent<MenuButtonPopUp>();
				if ((Object)(object)component != (Object)null)
				{
					Object.Destroy((Object)(object)component);
				}
				Relabel(val2, "INVITE");
				Button component2 = val2.GetComponent<Button>();
				if ((Object)(object)component2 != (Object)null)
				{
					((Behaviour)component2).enabled = true;
					((Selectable)component2).interactable = true;
					((UnityEventBase)component2.onClick).RemoveAllListeners();
					ButtonClickedEvent onClick = component2.onClick;
					object obj = <>O.<0>__OpenInviteOverlay;
					if (obj == null)
					{
						UnityAction val3 = OpenInviteOverlay;
						<>O.<0>__OpenInviteOverlay = val3;
						obj = (object)val3;
					}
					((UnityEvent)onClick).AddListener((UnityAction)obj);
				}
				val2.transform.SetSiblingIndex(((Component)val).transform.GetSiblingIndex());
				((MonoBehaviour)page).StartCoroutine(PlaceNextFrame(val2, ((Component)val).gameObject));
				Log.Debug("Added an INVITE button to the escape menu.");
			}
			catch (Exception ex)
			{
				Log.Debug("Could not add the invite button: " + ex);
			}
		}

		private static void OpenInviteOverlay()
		{
			try
			{
				SteamManager instance = SteamManager.instance;
				if (instance != null)
				{
					instance.OpenSteamOverlayToInvite();
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not open the Steam invite overlay: " + ex);
			}
		}

		private static IEnumerator PlaceNextFrame(GameObject clone, GameObject anchor)
		{
			yield return null;
			if ((Object)(object)clone == (Object)null || (Object)(object)anchor == (Object)null)
			{
				yield break;
			}
			RectTransform component = clone.GetComponent<RectTransform>();
			RectTransform component2 = anchor.GetComponent<RectTransform>();
			if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null)
			{
				yield break;
			}
			component.anchorMin = component2.anchorMin;
			component.anchorMax = component2.anchorMax;
			component.pivot = component2.pivot;
			component.sizeDelta = component2.sizeDelta;
			((Transform)component).localScale = ((Transform)component2).localScale;
			((Transform)component).localPosition = FreeSlotBelow(clone, component2);
			Relabel(clone, "INVITE");
			MenuButton component3 = clone.GetComponent<MenuButton>();
			if (!((Object)(object)component3 != (Object)null))
			{
				yield break;
			}
			Reflect.Set(OriginalPositionField, component3, Vector2.op_Implicit(((Transform)component).localPosition));
			try
			{
				component3.ResizeToText();
			}
			catch (Exception ex)
			{
				Log.Debug("Invite button resize failed: " + ex);
			}
		}

		private static Vector3 FreeSlotBelow(GameObject clone, RectTransform anchor)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			List<float> list = new List<float>();
			foreach (Transform item in ((Transform)anchor).parent)
			{
				Transform val = item;
				if ((Object)(object)((Component)val).gameObject != (Object)(object)clone && ((Component)val).gameObject.activeSelf && (Object)(object)((Component)val).GetComponent<MenuButton>() != (Object)null)
				{
					list.Add(val.localPosition.y);
				}
			}
			list.Sort();
			float num = float.MaxValue;
			for (int i = 1; i < list.Count; i++)
			{
				float num2 = list[i] - list[i - 1];
				if (num2 > 1f && num2 < num)
				{
					num = num2;
				}
			}
			if (num == float.MaxValue)
			{
				num = 32f;
			}
			float num3 = ((list.Count > 0) ? list[0] : ((Transform)anchor).localPosition.y);
			return new Vector3(((Transform)anchor).localPosition.x, num3 - num, ((Transform)anchor).localPosition.z);
		}

		private static void Relabel(GameObject buttonObject, string label)
		{
			if ((Object)(object)buttonObject == (Object)null)
			{
				return;
			}
			MenuButton component = buttonObject.GetComponent<MenuButton>();
			if ((Object)(object)component != (Object)null)
			{
				component.buttonTextString = label;
			}
			TextMeshProUGUI[] componentsInChildren = buttonObject.GetComponentsInChildren<TextMeshProUGUI>(true);
			foreach (TextMeshProUGUI val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null))
				{
					((TMP_Text)val).SetText(label, true);
					((TMP_Text)val).ForceMeshUpdate(false, false);
				}
			}
		}

		private static MenuButton FindQuitButton(MenuPageEsc page)
		{
			MenuButton[] componentsInChildren = ((Component)page).GetComponentsInChildren<MenuButton>(true);
			MenuButton val = null;
			MenuButton[] array = componentsInChildren;
			foreach (MenuButton val2 in array)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					string text = (val2.buttonTextString ?? string.Empty).ToUpperInvariant().Replace(" ", string.Empty);
					if (text == "QUITGAME")
					{
						return val2;
					}
					if ((Object)(object)val == (Object)null && text.Contains("QUIT"))
					{
						val = val2;
					}
				}
			}
			return val;
		}
	}
	internal static class StatusOverlay
	{
		private const float LineHeight = 22f;

		private const float Margin = 12f;

		private const float Edge = 10f;

		private const float Width = 700f;

		private static readonly Color Green = new Color(0.65f, 0.95f, 0.65f);

		private static readonly Color Red = new Color(1f, 0.66f, 0.66f);

		private static readonly Color Yellow = new Color(1f, 0.91f, 0.63f);

		private static readonly Regex ColorTags = new Regex("</?color[^>]*>");

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

		private static GUIStyle _style;

		private static GUIStyle _shadow;

		internal static void Draw()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Invalid comparison between Unknown and I4
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			//IL_0097: 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_00b0: Expected O, but got Unknown
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			//IL_0375: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			bool flag = ModConfig.ShowStatus != null && ModConfig.ShowStatus.Value;
			bool flag2 = ModConfig.ShowJoinStatus != null && ModConfig.ShowJoinStatus.Value && ModConfig.On;
			if ((!flag && !flag2) || Event.current == null || (int)Event.current.type != 7 || !PunGate.PhotonInitialised || !PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null)
			{
				return;
			}
			if (_style == null)
			{
				_style = new GUIStyle(GUI.skin.label)
				{
					fontSize = 16,
					richText = true
				};
				_style.normal.textColor = Color.white;
				_shadow = new GUIStyle(_style);
				_shadow.normal.textColor = new Color(0f, 0f, 0f, 0.7f);
			}
			ModConfig.Corner corner = ((ModConfig.InfoPosition != null) ? ModConfig.InfoPosition.Value : ModConfig.Corner.TopLeft);
			bool flag3 = corner == ModConfig.Corner.TopRight || corner == ModConfig.Corner.BottomRight;
			bool flag4 = corner == ModConfig.Corner.BottomLeft || corner == ModConfig.Corner.BottomRight;
			Lines.Clear();
			if (flag2)
			{
				foreach (var (text, phase) in JoinStatus.Current())
				{
					Lines.Add(text + ": " + Paint(phase.ToString(), PhaseColor(phase)));
				}
			}
			if (flag)
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				bool flag5 = currentRoom.IsOpen && (currentRoom.MaxPlayers == 0 || currentRoom.PlayerCount < currentRoom.MaxPlayers);
				bool flag6 = LobbyGate.IsPrivateLobby();
				string text2 = (flag6 ? "Private" : (LobbyGate.IsMatchmaking() ? "Matchmaking" : "Public"));
				int index = (flag4 ? Lines.Count : 0);
				Lines.Insert(index, "Name: " + (flag6 ? "Private Game" : RoomName(currentRoom)));
				Lines.Insert(index, "Room: " + text2 + " - " + (flag5 ? Paint("Open", Green) : Paint("Closed", Red)));
				Lines.Insert(index, "Players: " + currentRoom.PlayerCount + "/" + ((currentRoom.MaxPlayers > 0) ? currentRoom.MaxPlayers : currentRoom.PlayerCount));
			}
			GUIStyle style = _style;
			GUIStyle shadow = _shadow;
			int num = (flag3 ? 2 : 0);
			TextAnchor alignment = (TextAnchor)num;
			shadow.alignment = (TextAnchor)num;
			style.alignment = alignment;
			float num2 = (flag3 ? ((float)Screen.width - 12f - 700f) : 12f);
			float num3 = (flag4 ? ((float)Screen.height - 10f - 4f - (float)Lines.Count * 22f) : 10f);
			foreach (string line in Lines)
			{
				GUI.Label(new Rect(num2 + 1f, num3 + 1f, 700f, 26f), ColorTags.Replace(line, ""), _shadow);
				GUI.Label(new Rect(num2, num3, 700f, 26f), line, _style);
				num3 += 22f;
			}
		}

		private static Color PhaseColor(JoinStatus.Phase phase)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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)
			return (Color)(phase switch
			{
				JoinStatus.Phase.Left => Red, 
				JoinStatus.Phase.Joined => Green, 
				_ => Yellow, 
			});
		}

		private static string Paint(string text, Color color)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			return "<color=#" + ColorUtility.ToHtmlStringRGB(color) + ">" + text + "</color>";
		}

		private static string RoomName(Room room)
		{
			try
			{
				if (((Dictionary<object, object>)(object)((RoomInfo)room).CustomProperties).TryGetValue((object)"server_name", out object value) && value is string text && !string.IsNullOrEmpty(text))
				{
					return text;
				}
			}
			catch (Exception)
			{
			}
			return room.Name;
		}
	}
}
namespace RepoLateJoin.Lobby
{
	internal static class JoinRetry
	{
		private const float Every = 5f;

		private const int Tries = 6;

		private static readonly FieldInfo ConnectRandomField = Reflect.Field(typeof(GameManager), "connectRandom");

		private static readonly FieldInfo JoinNameField = Reflect.Field(typeof(DataDirector), "networkJoinServerName");

		private static readonly MethodInfo JoinPrivate = Reflect.Method(typeof(NetworkConnect), "TryJoiningRoom");

		private static int _tries;

		private static bool _givingUp;

		internal static bool OnJoinFailed(NetworkConnect connect, short code, string cause)
		{
			if (_givingUp || (Object)(object)connect == (Object)null || (Object)(object)JoinQueue.Runner == (Object)null)
			{
				return true;
			}
			if ((code != 32764 && code != 32765) || !CanRetry())
			{
				return true;
			}
			if (_tries >= 6)
			{
				Log.Info("Still could not join the room (" + cause + ") after " + 6 + " tries.");
				_tries = 0;
				return true;
			}
			_tries++;
			Log.Info("Could not join the room (" + cause + "); trying again in " + 5f + " s (" + _tries + "/" + 6 + ").");
			JoinQueue.Runner.StartCoroutine(Retry(connect, code, cause));
			return false;
		}

		internal static void Joined()
		{
			if (_tries > 0)
			{
				Log.Info("Joined the room after " + _tries + " retr" + ((_tries == 1) ? "y." : "ies."));
			}
			_tries = 0;
		}

		private static IEnumerator Retry(NetworkConnect connect, short code, string cause)
		{
			yield return (object)new WaitForSecondsRealtime(5f);
			if ((Object)(object)connect == (Object)null || PhotonNetwork.InRoom)
			{
				yield break;
			}
			bool flag = false;
			try
			{
				ClientState networkClientState = PhotonNetwork.NetworkClientState;
				if (PhotonNetwork.IsConnectedAndReady && ((int)networkClientState == 15 || (int)networkClientState == 4))
				{
					flag = Send(connect);
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not retry the join: " + ex);
			}
			if (flag)
			{
				yield break;
			}
			_tries = 0;
			_givingUp = true;
			try
			{
				((MonoBehaviourPunCallbacks)connect).OnJoinRoomFailed(code, cause);
			}
			finally
			{
				_givingUp = false;
			}
		}

		private static bool Send(NetworkConnect connect)
		{
			string text = ServerName();
			if (text != null)
			{
				if (text.Length > 0)
				{
					return PhotonNetwork.JoinRoom(text, (string[])null);
				}
				return false;
			}
			if (JoinPrivate == null)
			{
				return false;
			}
			JoinPrivate.Invoke(connect, null);
			return true;
		}

		private static bool CanRetry()
		{
			string text = ServerName();
			if (text == null)
			{
				return JoinPrivate != null;
			}
			return text.Length > 0;
		}

		private static string ServerName()
		{
			if ((Object)(object)GameManager.instance == (Object)null || !Reflect.Get(ConnectRandomField, GameManager.instance, fallback: false))
			{
				return null;
			}
			return (((Object)(object)DataDirector.instance != (Object)null) ? Reflect.Get<string>(JoinNameField, DataDirector.instance) : null) ?? "";
		}
	}
	internal static class LobbyGate
	{
		private static readonly FieldInfo PrivateLobbyField = Reflect.Field(typeof(SteamManager), "privateLobby");

		private static readonly FieldInfo LobbyTypeField = Reflect.Field(typeof(GameManager), "lobbyType");

		private const float TransitionCooldown = 4f;

		private static float _cooldownUntil;

		private static LevelGenerator _staleGenerator;

		private static float _nextTick;

		private static bool? _appliedState;

		internal static bool SelfLocking;

		private static string _seenRoom;

		private static LobbyTypes _seenType;

		internal static bool InTransition => Time.unscaledTime < _cooldownUntil;

		internal static bool SceneAllowsJoin()
		{
			try
			{
				if (SemiFunc.RunIsLobbyMenu())
				{
					return true;
				}
				if (SemiFunc.RunIsLobby())
				{
					return ModConfig.JoinInLobby.Value;
				}
				if (SemiFunc.RunIsShop())
				{
					return ModConfig.JoinInShop.Value;
				}
				if (SemiFunc.RunIsLevel())
				{
					return ModConfig.JoinInLevel.Value;
				}
				return false;
			}
			catch (Exception ex)
			{
				Log.Debug("Scene gate could not be evaluated: " + ex);
				return false;
			}
		}

		internal static bool ReadyForJoiners()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Invalid comparison between Unknown and I4
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Invalid comparison between Unknown and I4
			try
			{
				if ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated)
				{
					return false;
				}
				if ((Object)(object)LevelGenerator.Instance == (Object)(object)_staleGenerator)
				{
					return false;
				}
				if ((Object)(object)GameDirector.instance == (Object)null)
				{
					return false;
				}
				gameState currentState = GameDirector.instance.currentState;
				return (int)currentState == 2 || (int)currentState == 7;
			}
			catch (Exception ex)
			{
				Log.Debug("Ready gate could not be evaluated: " + ex);
				return false;
			}
		}

		internal static bool JoinAllowedNow()
		{
			if (PunGate.Ready && PhotonNetwork.IsMasterClient && PhotonNetwork.InRoom && !InTransition && SceneAllowsJoin())
			{
				return ReadyForJoiners();
			}
			return false;
		}

		internal static void BeginTransition()
		{
			_appliedState = null;
			_staleGenerator = LevelGenerator.Instance;
			_cooldownUntil = Time.unscaledTime + 4f;
			Apply("level transition started", force: true);
		}

		internal static void Tick()
		{
			if (PunGate.Ready && !(Time.unscaledTime < _nextTick))
			{
				_nextTick = Time.unscaledTime + 1f;
				Apply("periodic refresh");
				if (_appliedState == true && PhotonNetwork.IsMasterClient && PhotonNetwork.CurrentRoom != null)
				{
					EnsureSceneProperty(PhotonNetwork.CurrentRoom);
				}
			}
		}

		private static void EnsureSceneProperty(Room room, bool force = false)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_005b: Expected O, but got Unknown
			try
			{
				Scene activeScene = SceneManager.GetActiveScene();
				string name = ((Scene)(ref activeScene)).name;
				((Dictionary<object, object>)(object)((RoomInfo)room).CustomProperties).TryGetValue((object)"curScn", out object value);
				bool flag = value is string text && text == name;
				if (!flag || force)
				{
					Hashtable val = new Hashtable();
					((Dictionary<object, object>)val).Add((object)"curScn", (object)name);
					room.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
					if (flag)
					{
						Log.Debug("Re-wrote the room scene property '" + name + "' before opening.");
						return;
					}
					Log.Info("Room scene property was '" + (value ?? "unset")?.ToString() + "' while we are in '" + name + "'; corrected so late joiners load the right scene.");
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not check the room scene property: " + ex);
			}
		}

		internal static void Apply(string reason, bool force = false)
		{
			if (!PunGate.Ready || !PhotonNetwork.IsMasterClient || !PhotonNetwork.InRoom)
			{
				return;
			}
			bool flag = JoinAllowedNow();
			if (force || !_appliedState.HasValue || _appliedState.Value != flag)
			{
				_appliedState = flag;
				if (flag)
				{
					OpenRoom(reason);
				}
				else
				{
					CloseRoom(reason);
				}
			}
		}

		private static void OpenRoom(string reason)
		{
			bool flag = !IsPrivateLobby();
			try
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (currentRoom != null)
				{
					EnsureSceneProperty(currentRoom, force: true);
					currentRoom.IsOpen = true;
					currentRoom.IsVisible = flag;
				}
				if ((Object)(object)SteamManager.instance != (Object)null)
				{
					SteamManager.instance.UnlockLobby(flag);
				}
				if (flag && IsMatchmaking() && (Object)(object)GameManager.instance != (Object)null)
				{
					GameManager.instance.SetConnectRandom(true);
				}
				Log.Debug("Lobby open for late joins (" + reason + "), advertised=" + flag + ".");
			}
			catch (Exception ex)
			{
				Log.Debug("Could not open the lobby after " + reason + ": " + ex);
			}
		}

		private static void CloseRoom(string reason)
		{
			try
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (currentRoom != null)
				{
					currentRoom.IsOpen = false;
					currentRoom.IsVisible = false;
				}
				if (IsMatchmaking() && (Object)(object)GameManager.instance != (Object)null)
				{
					GameManager.instance.SetConnectRandom(false);
				}
				if ((Object)(object)SteamManager.instance != (Object)null)
				{
					SelfLocking = true;
					try
					{
						SteamManager.instance.LockLobby();
					}
					finally
					{
						SelfLocking = false;
					}
				}
				Log.Debug("Lobby closed to new joins (" + reason + ").");
			}
			catch (Exception ex)
			{
				Log.Debug("Could not close the lobby after " + reason + ": " + ex);
			}
		}

		internal static bool IsPrivateLobby()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			LobbyTypes? val = LobbyType();
			if (val.HasValue)
			{
				return (int)val.Value == 0;
			}
			if ((Object)(object)SteamManager.instance != (Object)null && PrivateLobbyField != null)
			{
				return Reflect.Get(PrivateLobbyField, SteamManager.instance, fallback: true);
			}
			return true;
		}

		internal static bool IsMatchmaking()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			LobbyTypes? val = LobbyType();
			if (val.HasValue)
			{
				return (int)val.Value == 2;
			}
			return false;
		}

		private static LobbyTypes? LobbyType()
		{
			//IL_00e1: 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_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			Room val = (PhotonNetwork.InRoom ? PhotonNetwork.CurrentRoom : null);
			if (val != null)
			{
				object value;
				LobbyTypes? result = ((((Dictionary<object, object>)(object)((RoomInfo)val).CustomProperties).TryGetValue((object)"server_name", out value) && value is string { Length: >0 }) ? new LobbyTypes?((LobbyTypes)1) : (val.IsVisible ? new LobbyTypes?((LobbyTypes)2) : ((LobbyTypes?)null)));
				if (result.HasValue)
				{
					_seenRoom = val.Name;
					_seenType = result.Value;
					return result;
				}
				if (val.Name == _seenRoom)
				{
					return _seenType;
				}
			}
			if ((Object)(object)GameManager.instance == (Object)null || LobbyTypeField == null)
			{
				return null;
			}
			try
			{
				object value2 = LobbyTypeField.GetValue(GameManager.instance);
				if (value2 is LobbyTypes)
				{
					return (LobbyTypes)value2;
				}
			}
			catch
			{
			}
			return null;
		}
	}
	internal static class PhotonCacheCleaner
	{
		private static readonly FieldInfo RemoveFilterField = Reflect.Field(typeof(PhotonNetwork), "removeFilter");

		private static readonly FieldInfo KeyByteSevenField = Reflect.Field(typeof(PhotonNetwork), "keyByteSeven");

		private static readonly FieldInfo ServerCleanOptionsField = Reflect.Field(typeof(PhotonNetwork), "ServerCleanOptions");

		private static readonly MethodInfo RaiseEventInternalMethod = Reflect.Method(typeof(PhotonNetwork), "RaiseEventInternal");

		internal static bool Available
		{
			get
			{
				if (RemoveFilterField != null && KeyByteSevenField != null && ServerCleanOptionsField != null)
				{
					return RaiseEventInternalMethod != null;
				}
				return false;
			}
		}

		internal static void ClearSceneCache()
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			if (!PunGate.Ready || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			if (!Available)
			{
				Log.Debug("Photon cache clearing is unavailable on this Photon build; skipping.");
				return;
			}
			PhotonView[] array;
			try
			{
				array = Object.FindObjectsOfType<PhotonView>(true);
			}
			catch (Exception ex)
			{
				Log.Debug("Could not enumerate PhotonViews: " + ex);
				return;
			}
			int num = 0;
			int num2 = 0;
			PhotonView val = RunManagerView();
			PhotonView[] array2 = array;
			foreach (PhotonView val2 in array2)
			{
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Scene scene = ((Component)val2).gameObject.scene;
				if (((Scene)(ref scene)).buildIndex == -1)
				{
					num2++;
					continue;
				}
				if (ClearOne(val2))
				{
					num++;
				}
				if ((Object)(object)val2 != (Object)(object)val)
				{
					ClearBufferedRpcs(val2);
				}
			}
			if (num > 0)
			{
				Log.Debug("Cleared " + num + " buffered instantiation(s) from the finished level, left " + num2 + " persistent object(s) alone.");
			}
			else
			{
				Log.Debug("No buffered Photon instantiations needed clearing.");
			}
		}

		internal static bool ClearOne(PhotonView view)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)view == (Object)null || !Available)
			{
				return false;
			}
			if (!view.isRuntimeInstantiated || view.InstantiationId < 1)
			{
				return false;
			}
			try
			{
				object? value = RemoveFilterField.GetValue(null);
				Hashtable val = (Hashtable)((value is Hashtable) ? value : null);
				object value2 = KeyByteSevenField.GetValue(null);
				object? value3 = ServerCleanOptionsField.GetValue(null);
				RaiseEventOptions val2 = (RaiseEventOptions)((value3 is RaiseEventOptions) ? value3 : null);
				if (val == null || value2 == null || val2 == null)
				{
					return false;
				}
				val[value2] = view.InstantiationId;
				val2.CachingOption = (EventCaching)6;
				return RaiseInternal(202, val, val2);
			}
			catch (Exception ex)
			{
				Log.Debug("Could not clear Photon cache for view " + view.ViewID + ": " + ex);
				return false;
			}
		}

		internal static bool RaiseInternal(byte code, object data, RaiseEventOptions options)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (RaiseEventInternalMethod == null)
			{
				return false;
			}
			try
			{
				object obj = RaiseEventInternalMethod.Invoke(null, new object[4]
				{
					code,
					data,
					options,
					SendOptions.SendReliable
				});
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch (Exception ex)
			{
				Log.Debug("Photon internal event " + code + " failed: " + ex);
				return false;
			}
		}

		internal static PhotonView RunManagerView()
		{
			try
			{
				object obj = Reflect.Field(typeof(RunManager), "runManagerPUN")?.GetValue(RunManager.instance);
				object result;
				if (obj != null)
				{
					object? obj2 = Reflect.Field(obj.GetType(), "photonView")?.GetValue(obj);
					PhotonView val = (PhotonView)((obj2 is PhotonView) ? obj2 : null);
					if (val != null)
					{
						result = val;
						goto IL_0053;
					}
				}
				result = null;
				goto IL_0053;
				IL_0053:
				return (PhotonView)result;
			}
			catch
			{
				return null;
			}
		}

		internal static void ClearBufferedRpcs(PhotonView view)
		{
			if ((Object)(object)view == (Object)null || view.ViewID == 0)
			{
				return;
			}
			try
			{
				PhotonNetwork.RemoveBufferedRPCs(view.ViewID, (string)null, (int[])null);
			}
			catch (Exception ex)
			{
				Log.Debug("Could not clear buffered RPCs for view " + view.ViewID + ": " + ex);
			}
		}

		internal static void ClearStaleLevelUpdateRpc()
		{
			if (!PunGate.Ready || !PhotonNetwork.IsMasterClient || (Object)(object)RunManager.instance == (Object)null)
			{
				return;
			}
			try
			{
				PhotonView val = RunManagerView();
				if (!((Object)(object)val == (Object)null))
				{
					PhotonNetwork.RemoveBufferedRPCs(val.ViewID, "UpdateLevelRPC", (int[])null);
					PhotonNetwork.RemoveBufferedRPCs(val.ViewID, "AllPlayersDeadRPC", (int[])null);
					Log.Debug("Removed the previous level's buffered UpdateLevelRPC and AllPlayersDeadRPC.");
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not remove the stale level update RPC: " + ex);
			}
		}

		internal static void ClearStaleGenerationRpcs(LevelGenerator generator)
		{
			if (!PunGate.Ready || !PhotonNetwork.IsMasterClient || (Object)(object)generator == (Object)null)
			{
				return;
			}
			if (generator.Generated)
			{
				Log.Debug("Generation already reported done; leaving its buffered RPCs alone.");
				return;
			}
			try
			{
				PhotonView photonView = generator.PhotonView;
				if (!((Object)(object)photonView == (Object)null) && photonView.ViewID != 0)
				{
					PhotonNetwork.RemoveBufferedRPCs(photonView.ViewID, (string)null, (int[])null);
					Log.Debug("Removed the previous level's buffered generation RPCs.");
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not remove stale generation RPCs: " + ex);
			}
		}
	}
	internal static class PlayerLimit
	{
		internal static void Watch()
		{
			ModConfig.MaxPlayers.SettingChanged += delegate
			{
				Apply();
			};
			ModConfig.MaxPlayersEnabled.SettingChanged += delegate
			{
				Apply();
			};
		}

		internal static void Apply()
		{
			if (!ModConfig.MaxPlayersEnabled.Value || (Object)(object)GameManager.instance == (Object)null)
			{
				return;
			}
			int value = ModConfig.MaxPlayers.Value;
			try
			{
				GameManager.instance.SetMaxPlayers(value);
				Log.Debug("Max players set to " + GameManager.instance.maxPlayers + ".");
			}
			catch (Exception ex)
			{
				Log.Debug("Could not set max players: " + ex);
			}
		}
	}
	internal static class RoomName
	{
		private const string Key = "server_name";

		private static readonly FieldInfo TextField = Reflect.Field(typeof(MenuTextInput), "textCurrent");

		private static string _typed;

		private static string _created;

		internal static void Typed(MenuPageServerListCreateNew page)
		{
			string text = (((Object)(object)page != (Object)null && (Object)(object)page.menuTextInput != (Object)null) ? Reflect.Get<string>(TextField, page.menuTextInput) : null);
			_typed = (string.IsNullOrWhiteSpace(text) ? null : text.Trim());
		}

		internal static void Create(Hashtable properties)
		{
			_created = null;
			if (ModConfig.On && properties != null && ((Dictionary<object, object>)(object)properties).ContainsKey((object)"server_name") && _typed != null)
			{
				_created = ModConfig.RoomPrefix() + " " + _typed;
				if (properties[(object)"server_name"] as string != _created)
				{
					Log.Debug("Server name '" + properties[(object)"server_name"]?.ToString() + "' set to '" + _created + "'.");
				}
				properties[(object)"server_name"] = _created;
			}
		}

		internal static void Keep(Hashtable properties)
		{
			if (_created != null && properties != null && ((Dictionary<object, object>)(object)properties).ContainsKey((object)"server_name") && PhotonNetwork.IsMasterClient && !(properties[(object)"server_name"] as string == _created))
			{
				Log.Debug("Kept the server name '" + _created + "' instead of '" + properties[(object)"server_name"]?.ToString() + "'.");
				properties[(object)"server_name"] = _created;
			}
		}
	}
	internal static class SteamLobbyJoin
	{
		private const string Rpc = "JoinSteamLobbyRPC";

		private const float WaitForId = 60f;

		private const float Recheck = 15f;

		private static readonly FieldInfo ConnectRandomField = Reflect.Field(typeof(GameManager), "connectRandom");

		private static FieldRef<SteamManager, Lobby> _currentLobby;

		internal static Lobby? Current()
		{
			//IL_0044: 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_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_0066: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)SteamManager.instance == (Object)null || !SteamClient.IsValid)
			{
				return null;
			}
			try
			{
				if (_currentLobby == null)
				{
					_currentLobby = AccessTools.FieldRefAccess<SteamManager, Lobby>("currentLobby");
				}
				Lobby value = _currentLobby.Invoke(SteamManager.instance);
				SteamId id = ((Lobby)(ref value)).Id;
				return ((SteamId)(ref id)).IsValid ? new Lobby?(value) : ((Lobby?)null);
			}
			catch
			{
				return null;
			}
		}

		internal static void StayIn(Lobby entered, ref Lobby current)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			SteamId id = ((Lobby)(ref current)).Id;
			if (((SteamId)(ref id)).IsValid && ((Lobby)(ref current)).Id.Value == ((Lobby)(ref entered)).Id.Value)
			{
				current = default(Lobby);
				Log.Debug("Asked to join the Steam lobby we are already in; stayed in it.");
			}
		}

		internal static bool SendJoin()
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if (!PhotonNetwork.IsMasterClient || !PhotonNetwork.InRoom)
			{
				return false;
			}
			Lobby? val = Current();
			PhotonView val2 = PhotonCacheCleaner.RunManagerView();
			if (!val.HasValue || (Object)(object)val2 == (Object)null || val2.ViewID < 1)
			{
				return false;
			}
			SteamManager.instance.UnlockLobby(true);
			PhotonNetwork.RemoveBufferedRPCs(val2.ViewID, "JoinSteamLobbyRPC", (int[])null);
			int num = 0;
			Player[] playerListOthers = PhotonNetwork.PlayerListOthers;
			foreach (Player player in playerListOthers)
			{
				if (!InLobby(val.Value, player) && Send(val2, val.Value, player))
				{
					num++;
				}
			}
			Log.Debug("Steam lobby ready; sent it to " + num + " player(s) not in it yet.");
			return true;
		}

		internal static void OnPlayerEntered(Player player)
		{
			if (player != null && !player.IsLocal && PhotonNetwork.IsMasterClient && !((Object)(object)JoinQueue.Runner == (Object)null) && !((Object)(object)GameManager.instance == (Object)null) && Reflect.Get(ConnectRandomField, GameManager.instance, fallback: false))
			{
				JoinQueue.Runner.StartCoroutine(Welcome(player));
			}
		}

		private static IEnumerator Welcome(Player player)
		{
			float until = Time.unscaledTime + 60f;
			while (SteamIdOf(player) == 0L)
			{
				if (Time.unscaledTime > until || !Here(player))
				{
					yield break;
				}
				yield return (object)new WaitForSecondsRealtime(2f);
			}
			for (int attempt = 0; attempt < 2; attempt++)
			{
				if (!Here(player))
				{
					break;
				}
				Lobby? val = Current();
				PhotonView val2 = PhotonCacheCleaner.RunManagerView();
				if (!val.HasValue || (Object)(object)val2 == (Object)null || val2.ViewID < 1 || InLobby(val.Value, player))
				{
					break;
				}
				if (Send(val2, val.Value, player))
				{
					Log.Debug(JoinQueue.Name(player) + " was not in our Steam lobby; sent it to them.");
				}
				yield return (object)new WaitForSecondsRealtime(15f);
			}
		}

		private static bool Here(Player player)
		{
			if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient)
			{
				return PhotonNetwork.CurrentRoom.GetPlayer(player.ActorNumber, false) != null;
			}
			return false;
		}

		private static bool Send(PhotonView view, Lobby lobby, Player player)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				view.RPC("JoinSteamLobbyRPC", player, new object[1] { ((object)((Lobby)(ref lobby)).Id/*cast due to .constrained prefix*/).ToString() });
				return true;
			}
			catch (Exception ex)
			{
				Log.Debug("Could not send the Steam lobby to " + JoinQueue.Name(player) + ": " + ex);
				return false;
			}
		}

		private static bool InLobby(Lobby lobby, Player player)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			ulong num = SteamIdOf(player);
			if (num == 0L)
			{
				return true;
			}
			try
			{
				foreach (Friend member in ((Lobby)(ref lobby)).Members)
				{
					if (member.Id.Value == num)
					{
						return true;
					}
				}
			}
			catch
			{
				return true;
			}
			return false;
		}

		private static ulong SteamIdOf(Player player)
		{
			string text = player.UserId;
			if (string.IsNullOrEmpty(text))
			{
				PlayerAvatar val = null;
				try
				{
					val = SemiFunc.PlayerAvatarGetFromPhotonPlayer(player);
				}
				catch
				{
				}
				text = (((Object)(object)val != (Object)null) ? SemiFunc.PlayerGetSteamID(val) : null);
			}
			if (!ulong.TryParse(text, out var result))
			{
				return 0uL;
			}
			return result;
		}
	}
}
namespace RepoLateJoin.Join
{
	internal static class EnemyAwareness
	{
		private static readonly List<int> Scratch = new List<int>();

		private static readonly HashSet<EnemyOnScreen> OnScreens = new HashSet<EnemyOnScreen>();

		private static readonly HashSet<EnemyVision> Visions = new HashSet<EnemyVision>();

		private static bool Wanted => ModConfig.On;

		private static List<int> PlayerViewIds()
		{
			Scratch.Clear();
			if (GameDirector.instance?.PlayerList == null)
			{
				return Scratch;
			}
			foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
			{
				if (!((Object)(object)player == (Object)null) && !((Object)(object)player.photonView == (Object)null) && player.photonView.ViewID != 0)
				{
					Scratch.Add(player.photonView.ViewID);
				}
			}
			return Scratch;
		}

		internal static void ForgetDestroyed()
		{
			OnScreens.RemoveWhere((EnemyOnScreen t) => (Object)(object)t == (Object)null);
			Visions.RemoveWhere((EnemyVision t) => (Object)(object)t == (Object)null);
		}

		internal static void EnsureFor(EnemyOnScreen onScreen)
		{
			if ((Object)(object)onScreen == (Object)null)
			{
				return;
			}
			OnScreens.Add(onScreen);
			if (!Wanted)
			{
				return;
			}
			try
			{
				List<int> list = PlayerViewIds();
				for (int i = 0; i < list.Count; i++)
				{
					onScreen.PlayerAdded(list[i]);
				}
			}
			catch (Exception ex)
			{
				Log.Debug("EnemyOnScreen registration failed: " + ex);
			}
		}

		internal static void EnsureFor(EnemyVision vision)
		{
			if ((Object)(object)vision == (Object)null)
			{
				return;
			}
			Visions.Add(vision);
			if (!Wanted)
			{
				return;
			}
			try
			{
				List<int> list = PlayerViewIds();
				for (int i = 0; i < list.Count; i++)
				{
					vision.PlayerAdded(list[i]);
				}
			}
			catch (Exception ex)
			{
				Log.Debug("EnemyVision registration failed: " + ex);
			}
		}

		internal static void RegisterAllPlayers()
		{
			if (!Wanted)
			{
				return;
			}
			List<int> list = PlayerViewIds();
			if (list.Count == 0)
			{
				return;
			}
			int num = 0;
			try
			{
				OnScreens.RemoveWhere((EnemyOnScreen t) => (Object)(object)t == (Object)null);
				Visions.RemoveWhere((EnemyVision t) => (Object)(object)t == (Object)null);
				foreach (EnemyOnScreen onScreen in OnScreens)
				{
					for (int num2 = 0; num2 < list.Count; num2++)
					{
						onScreen.PlayerAdded(list[num2]);
					}
					num++;
				}
				foreach (EnemyVision vision in Visions)
				{
					for (int num3 = 0; num3 < list.Count; num3++)
					{
						vision.PlayerAdded(list[num3]);
					}
					num++;
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not register players with the enemies: " + ex);
				return;
			}
			if (num > 0)
			{
				Log.Debug("Registered " + list.Count + " player(s) with " + num + " enemy tracker(s) so enemies can see late joiners.");
			}
		}
	}
	internal static class ExtractionSync
	{
		private static readonly FieldInfo RoundHaulGoalField = Reflect.Field(typeof(RoundDirector), "haulGoal");

		private static readonly FieldInfo PointListField = Reflect.Field(typeof(RoundDirector), "extractionPointList");

		private static readonly FieldInfo PointStateField = Reflect.Field(typeof(ExtractionPoint), "currentState");

		private static readonly FieldInfo PointViewField = Reflect.Field(typeof(ExtractionPoint), "photonView");

		private static readonly FieldInfo PointHaulFetchedField = Reflect.Field(typeof(ExtractionPoint), "haulGoalFetched");

		private static readonly FieldInfo AllCompletedField = Reflect.Field(typeof(RoundDirector), "allExtractionPointsCompleted");

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

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

		internal static void Clear()
		{
			AtEntry.Clear();
			AllDoneAtEntry.Clear();
		}

		internal static void RememberAtEntry(Player player)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			if (player == null || !PhotonNetwork.IsMasterClient || (Object)(object)RoundDirector.instance == (Object)null)
			{
				return;
			}
			try
			{
				Dictionary<int, State> dictionary = new Dictionary<int, State>();
				foreach (ExtractionPoint item in Points())
				{
					PhotonView val = ViewOf(item);
					if ((Object)(object)val != (Object)null && val.ViewID != 0)
					{
						dictionary[val.ViewID] = StateOf(item);
					}
				}
				AtEntry[player.ActorNumber] = dictionary;
				if (Reflect.Get(AllCompletedField, RoundDirector.instance, fallback: false))
				{
					AllDoneAtEntry.Add(player.ActorNumber);
				}
				else
				{
					AllDoneAtEntry.Remove(player.ActorNumber);
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Could not record the extraction points for a joiner: " + ex);
			}
		}

		internal static int SendTo(Player target)
		{
			if (!PunGate.Ready)
			{
				return 0;
			}
			if (target == null || !PhotonNetwork.IsMasterClient)
			{
				return 0;
			}
			if ((Object)(object)RoundDirector.instance == (Object)null)
			{
				return 0;
			}
			int num = 0 + SendHaulGoal(target) + SendActivation(target) + SendPointStates(target) + SendAllCompleted(target);
			if (num > 0)
			{
				Log.Debug("Sent extraction point state to late joiners.");
			}
			return num;
		}

		private static int SendAllCompleted(Player target)
		{
			if (!Reflect.Get(AllCompletedField, RoundDirector.instance, fallback: false))
			{
				return 0;
			}
			if (Known(target) && !AllDoneAtEntry.Contains(target.ActorNumber))
			{
				return 0;
			}
			PhotonView val = ViewOf(RoundDirector.instance);
			if ((Object)(object)val == (Object)null)
			{
				return 0;
			}
			try
			{
				val.RPC("ExtractionCompletedAllRPC", target, Array.Empty<object>());
				return 1;
			}
			catch (Exception ex)
			{
				Log.Debug("Could not send that every extraction point is done: " + ex);
				return 0;
			}
		}

		private static int SendHaulGoal(Player target)
		{
			int num = Reflect.Get(RoundHaulGoalField, RoundDirector.instance, 0);
			if (num <= 0)
			{
				return 0;
			}
			PhotonView val = ViewOf(RoundDirector.instance);
			if ((Object)(object)val == (Object)null)
			{
				return 0;
			}
			try
			{
				val.RPC("StartRoundRPC", target, new object[1] { num });
				return 1;
			}
			catch (Exception ex)
			{
				Log.Debug("Could not send the haul goal: " + ex);
				return 0;
			}
		}

		private static int SendActivation(Player target)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			PhotonView val = ViewOf(RoundDirector.instance);
			if ((Object)(object)val == (Object)null)
			{
				return 0;
			}
			try
			{
				ExtractionPoint val2 = null;
				foreach (ExtractionPoint item in Points())
				{
					if (InProgress(StateOf(item)))
					{
						val2 = item;
						break;
					}
				}
				if ((Object)(object)val2 == (Object)null)
				{
					val.RPC("ExtractionPointsUnlockRPC", target, Array.Empty<object>());
					return 1;
				}
				PhotonView val3 = ViewOf(val2);
				if ((Object)(object)val3 == (Object)null || val3.ViewID == 0)
				{
					return 0;
				}
				val.RPC("ExtractionPointActivateRPC", target, new object[1] { val3.ViewID });
				if (Known(target) && !InProgress(EntryState(target, val3)))
				{
					Log.Debug("Extraction point " + val3.ViewID + " started after the joiner entered; they have it.");
					return 1;
				}
				if (val2.haulGoal > 0 && Reflect.Get(PointHaulFetchedField, val2, fallback: false))
				{
					val3.RPC("HaulGoalSetRPC", target, new object[1] { val2.haulGoal });
				}
				val3.RPC("StateSetRPC", target, new object[1] { (object)(State)2 });
				Log.Debug("Showed extraction point " + val3.ViewID + " (" + ((object)StateOf(val2)/*cast due to .constrained prefix*/).ToString() + ") to the joiner as active.");
				return 2;
			}
			catch (Exception ex)
			{
				Log.Debug("Could not send the extraction activation state: " + ex);
				return 0;
			}
		}

		private static int SendPointStates(Player target)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Invalid comparison between Unknown and I4
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Invalid comparison between Unknown and I4
			int num = 0;
			foreach (ExtractionPoint item in Points())
			{
				PhotonView val = ViewOf(item);
				if ((Object)(object)val == (Object)null || val.ViewID == 0)
				{
					continue;
				}
				try
				{
					if (item.haulGoal > 0 && Reflect.Get(PointHaulFetchedField, item, fallback: false))
					{
						val.RPC("HaulGoalSetRPC", target, new object[1] { item.haulGoal });
						num++;
					}
					if ((int)StateOf(item) == 7)
					{
						if (Known(target) && (int)EntryState(target, val) != 7)
						{
							Log.Debug("Extraction point " + val.ViewID + " finished after the joiner entered; they have it.");
							continue;
						}
						val.RPC("StateSetRPC", target, new object[1] { (object)(State)7 });
						num++;
						Log.Debug("Sent extraction point " + val.ViewID + " as complete.");
					}
				}
				catch (Exception ex)
				{
					Log.Debug("Could not send state for extraction point " + val.ViewID + ": " + ex);
				}
			}
			return num;
		}

		private static bool InProgress(State state)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Invalid comparison between Unknown and I4
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			if ((int)state != 0 && (int)state != 1)
			{
				return (int)state != 7;
			}
			return false;
		}

		private static State StateOf(ExtractionPoint point)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			return Reflect.Get<State>(PointStateField, point, (State)0);
		}

		private static bool Known(Player target)
		{
			return AtEntry.ContainsKey(target.ActorNumber);
		}

		private static State EntryState(Player target, PhotonView view)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (!AtEntry.TryGetValue(target.ActorNumber, out var value) || !value.TryGetValue(view.ViewID, out var value2))
			{
				return (State)0;
			}
			return value2;
		}

		private static IEnumerable<ExtractionPoint> Points()
		{
			if (PointListField?.GetValue(RoundDirector.instance) is List<GameObject> list)
			{
				foreach (GameObject item in list)
				{
					if (!((Object)(object)item == (Object)null))
					{
						ExtractionPoint component = item.GetComponent<ExtractionPoint>();
						if ((Object)(object)component != (Object)null)
						{
							yield return component;
						}
					}
				}
				yield break;
			}
			ExtractionPoint[] array;
			try
			{
				array = Object.FindObjectsOfType<ExtractionPoint>(true);
			}
			catch
			{
				yield break;
			}
			ExtractionPoint[] array2 = array;
			foreach (ExtractionPoint val in array2)
			{
				if ((Object)(object)val != (Object)null)
				{
					yield return val;
				}
			}
		}

		private static PhotonView ViewOf(RoundDirector director)
		{
			if ((Object)(object)director == (Object)null)
			{
				return null;
			}
			return ((Component)director).GetComponent<PhotonView>();
		}

		private static PhotonView ViewOf(ExtractionPoint point)
		{
			if ((Object)(object)point == (Object)null)
			{
				return null;
			}
			object? obj = PointViewField?.GetValue(point);
			PhotonView val = (PhotonView)((obj is PhotonView) ? obj : null);
			if (val != null && (Object)(object)val != (Object)null)
			{
				return val;
			}
			return ((Component)point).GetComponent<PhotonView>();
		}
	}
	internal static class Handshake
	{
		private const byte Event = 173;

		private const string Tag = "RepoLateJoin";

		private const float ReplyTimeout = 15f;

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

		private static float _greetedAt;

		internal static bool HostHasMod
		{
			get
			{
				if (PhotonNetwork.MasterClient != null)
				{
					return HasMod(PhotonNetwork.MasterClient.ActorNumber);
				}
				return false;
			}
		}

		internal static bool HasMod(int actor)
		{
			return ModdedActors.Contains(actor);
		}

		internal static void Listen()
		{
			PhotonNetwork.NetworkingClient.EventReceived += OnEvent;
		}

		internal static void Greet()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			ModdedActors.Clear();
			_greetedAt = 0f;
			if (!PhotonNetwork.IsMasterClient && Send(new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)0
			}))
			{
				_greetedAt = Time.unscaledTime;
				Log.Debug("Asked the host whether it runs RepoLateJoin.");
			}
		}

		internal static void Tick()
		{
			if (_greetedAt == 0f)
			{
				return;
			}
			if (!PhotonNetwork.IsMessageQueueRunning)
			{
				_greetedAt = Time.unscaledTime;
			}
			if (!(Time.unscaledTime - _greetedAt < 15f))
			{
				_greetedAt = 0f;
				if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient)
				{
					Log.Info("The host did not answer within " + 15f + " s: the host is not running RepoLateJoin 2.8.0 or newer, so joining mid-run relies on the game alone.");
				}
			}
		}

		private static bool Send(RaiseEventOptions options)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return PhotonNetwork.RaiseEvent((byte)173, (object)new object[3]
				{
					"RepoLateJoin",
					"2.8.0",
					ModConfig.On
				}, options, SendOptions.SendReliable);
			}
			catch (Exception ex)
			{
				Log.Debug("Could not send the RepoLateJoin handshake: " + ex);
				return false;
			}
		}

		private static void OnEvent(Even