Decompiled source of LateJoin v1.1.1

LateJoin.dll

Decompiled 17 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using ExitGames.Client.Photon;
using HarmonyLib;
using LateJoin.Patches;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LateJoin")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Allows players to join lobbies that are already in-progress.")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1+66f6370566b62a8b3f3b94e484e4c6ebf9d8e9f9")]
[assembly: AssemblyProduct("LateJoin")]
[assembly: AssemblyTitle("LateJoin")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LateJoin
{
	[BepInPlugin("semimodder-latejoin", "Late Join", "1.1.1")]
	public class Plugin : BaseUnityPlugin
	{
		public static ConfigEntry<bool> CfgEnabled;

		public static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			CfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable mid-game join support (Host keeps lobby open and syncs basic state).");
			new Harmony("semimodder-latejoin").PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Late Join Mod loaded successfully.");
		}

		private void Update()
		{
			if (CfgEnabled.Value)
			{
				LobbyPatches.Update();
				LevelPatches.Update();
				ItemPatches.Update();
				EnemyPatches.Update();
			}
		}
	}
}
namespace LateJoin.Patches
{
	public static class EnemyPatches
	{
		private static readonly FieldInfo EnemyOnScreenCoroutineOwner = AccessTools.Field(AccessTools.TypeByName("EnemyOnScreen+<Logic>d__19"), "4__this") ?? AccessTools.Field(AccessTools.TypeByName("EnemyOnScreen+<Logic>d__19"), "<>4__this");

		private static readonly FieldInfo EnemyVisionCoroutineOwner = AccessTools.Field(AccessTools.TypeByName("EnemyVision+<Vision>d__34"), "4__this") ?? AccessTools.Field(AccessTools.TypeByName("EnemyVision+<Vision>d__34"), "<>4__this");

		private static int lastPlayerSignature = 0;

		public static void Update()
		{
			if (Plugin.CfgEnabled.Value)
			{
				int playerSignature = GetPlayerSignature();
				if (playerSignature != lastPlayerSignature)
				{
					lastPlayerSignature = playerSignature;
					SyncAll();
				}
			}
		}

		public static void SyncAll()
		{
			if (!Plugin.CfgEnabled.Value)
			{
				return;
			}
			List<int> playerViewIds = GetPlayerViewIds();
			if (playerViewIds.Count == 0)
			{
				return;
			}
			try
			{
				EnemyOnScreen[] array = Object.FindObjectsOfType<EnemyOnScreen>();
				foreach (EnemyOnScreen val in array)
				{
					if ((Object)(object)val != (Object)null)
					{
						Sync(val, playerViewIds);
					}
				}
				EnemyVision[] array2 = Object.FindObjectsOfType<EnemyVision>();
				foreach (EnemyVision val2 in array2)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						Sync(val2, playerViewIds);
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[LateJoin] SyncAll enemies failed: " + ex.Message));
			}
		}

		public static void Sync(EnemyOnScreen enemyOnScreen)
		{
			if ((Object)(object)enemyOnScreen != (Object)null)
			{
				Sync(enemyOnScreen, GetPlayerViewIds());
			}
		}

		public static void Sync(EnemyVision enemyVision)
		{
			if ((Object)(object)enemyVision != (Object)null)
			{
				Sync(enemyVision, GetPlayerViewIds());
			}
		}

		public static void SyncEnemyOnScreenCoroutine(object stateMachine)
		{
			if (EnemyOnScreenCoroutineOwner != null && stateMachine != null)
			{
				object? value = EnemyOnScreenCoroutineOwner.GetValue(stateMachine);
				Sync((EnemyOnScreen)((value is EnemyOnScreen) ? value : null));
			}
		}

		public static void SyncEnemyVisionCoroutine(object stateMachine)
		{
			if (EnemyVisionCoroutineOwner != null && stateMachine != null)
			{
				object? value = EnemyVisionCoroutineOwner.GetValue(stateMachine);
				Sync((EnemyVision)((value is EnemyVision) ? value : null));
			}
		}

		private static void Sync(EnemyOnScreen enemyOnScreen, List<int> playerIds)
		{
			if ((Object)(object)enemyOnScreen == (Object)null || playerIds.Count == 0)
			{
				return;
			}
			try
			{
				foreach (int playerId in playerIds)
				{
					enemyOnScreen.PlayerAdded(playerId);
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[LateJoin] Error syncing PlayerAdded on EnemyOnScreen: " + ex.Message));
			}
		}

		private static void Sync(EnemyVision enemyVision, List<int> playerIds)
		{
			if ((Object)(object)enemyVision == (Object)null || playerIds.Count == 0)
			{
				return;
			}
			try
			{
				foreach (int playerId in playerIds)
				{
					enemyVision.PlayerAdded(playerId);
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[LateJoin] Error syncing PlayerAdded on EnemyVision: " + ex.Message));
			}
		}

		private static int GetPlayerSignature()
		{
			List<int> playerViewIds = GetPlayerViewIds();
			if (playerViewIds.Count == 0)
			{
				return 0;
			}
			int num = 17;
			foreach (int item in playerViewIds)
			{
				num = num * 31 + item;
			}
			return num;
		}

		private static List<int> GetPlayerViewIds()
		{
			List<int> list = new List<int>();
			if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null)
			{
				return list;
			}
			foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
			{
				if ((Object)(object)player != (Object)null && (Object)(object)player.photonView != (Object)null && player.photonView.ViewID != 0)
				{
					list.Add(player.photonView.ViewID);
				}
			}
			return list;
		}
	}
	[HarmonyPatch(typeof(EnemyOnScreen), "OnEnable")]
	public static class EnemyOnScreenOnEnablePatch
	{
		[HarmonyPrefix]
		public static void Prefix(EnemyOnScreen __instance)
		{
			if (Plugin.CfgEnabled.Value)
			{
				EnemyPatches.Sync(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(EnemyVision), "OnEnable")]
	public static class EnemyVisionOnEnablePatch
	{
		[HarmonyPrefix]
		public static void Prefix(EnemyVision __instance)
		{
			if (Plugin.CfgEnabled.Value)
			{
				EnemyPatches.Sync(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "Start")]
	public static class PlayerAvatarStartEnemyPatch
	{
		[HarmonyPostfix]
		public static void Postfix()
		{
			if (Plugin.CfgEnabled.Value)
			{
				EnemyPatches.SyncAll();
			}
		}
	}
	[HarmonyPatch]
	public static class EnemyOnScreenLogicMoveNextPatch
	{
		public static MethodBase TargetMethod()
		{
			Type type = AccessTools.TypeByName("EnemyOnScreen+<Logic>d__19");
			if (!(type == null))
			{
				return AccessTools.Method(type, "MoveNext", (Type[])null, (Type[])null);
			}
			return null;
		}

		[HarmonyPrefix]
		public static void Prefix(object __instance)
		{
			if (Plugin.CfgEnabled.Value)
			{
				EnemyPatches.SyncEnemyOnScreenCoroutine(__instance);
			}
		}
	}
	[HarmonyPatch]
	public static class EnemyVisionMoveNextPatch
	{
		public static MethodBase TargetMethod()
		{
			Type type = AccessTools.TypeByName("EnemyVision+<Vision>d__34");
			if (!(type == null))
			{
				return AccessTools.Method(type, "MoveNext", (Type[])null, (Type[])null);
			}
			return null;
		}

		[HarmonyPrefix]
		public static void Prefix(object __instance)
		{
			if (Plugin.CfgEnabled.Value)
			{
				EnemyPatches.SyncEnemyVisionCoroutine(__instance);
			}
		}
	}
	public static class InteractivePatches
	{
		private sealed class StateSyncEntry
		{
			public string TypeName;

			public string RpcName;

			public string FieldName;

			public bool CastToInt;

			public Type ResolvedType;

			public FieldInfo ResolvedField;
		}

		private static readonly List<StateSyncEntry> entries = new List<StateSyncEntry>
		{
			S("ExtractionPoint", "StateSetRPC", castToInt: false),
			S("ShopKeycardDoor", "StateSetRPC"),
			S("UpgradeStand", "StateSetRPC"),
			S("ItemValuableBox", "StateSetRPC"),
			S("ItemMine", "StateSetRPC", castToInt: true, "state"),
			S("ItemMelee", "StateSetRPC"),
			S("ItemGun", "StateSetRPC", castToInt: true, "stateCurrent"),
			S("ItemDrone", "StateSetRPC"),
			S("ItemVehicle", "SetStateRPC"),
			S("ItemCartCannonMain", "StateSetRPC", castToInt: true, "stateCurrent"),
			S("FanTrap", "SetStateRPC", castToInt: false),
			S("CrystalBallValuable", "SetStateRPC", castToInt: false),
			S("BlenderValuable", "SetStateRPC", castToInt: false),
			S("FlamethrowerValuable", "SetStateRPC", castToInt: false),
			S("IceSawValuable", "SetStateRPC", castToInt: false),
			S("FireExtinguisherValuable", "SetStateRPC", castToInt: false),
			S("ScreamDollValuable", "SetStateRPC", castToInt: false),
			S("JackhammerValuable", "SetStateRPC", castToInt: false)
		};

		private static StateSyncEntry S(string type, string rpc, bool castToInt = true, string field = "currentState")
		{
			return new StateSyncEntry
			{
				TypeName = type,
				RpcName = rpc,
				FieldName = field,
				CastToInt = castToInt
			};
		}

		public static void SyncInteractiveObjectsToPlayer(Player target)
		{
			if (Plugin.CfgEnabled.Value && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && target != null)
			{
				((MonoBehaviour)Plugin.Instance).StartCoroutine(SyncCoroutine(target));
			}
		}

		private static IEnumerator SyncCoroutine(Player target)
		{
			yield return (object)new WaitForSeconds(3.5f);
			if (target == null || PhotonNetwork.CurrentRoom == null)
			{
				yield break;
			}
			int count = 0;
			foreach (StateSyncEntry entry in entries)
			{
				if (entry.ResolvedType == null)
				{
					entry.ResolvedType = AccessTools.TypeByName(entry.TypeName);
					if (entry.ResolvedType != null)
					{
						entry.ResolvedField = AccessTools.Field(entry.ResolvedType, entry.FieldName);
					}
				}
				if (entry.ResolvedType == null || entry.ResolvedField == null)
				{
					continue;
				}
				Object[] array = Object.FindObjectsOfType(entry.ResolvedType);
				foreach (Object val in array)
				{
					if (val == (Object)null)
					{
						continue;
					}
					Object obj = ((val is Component) ? val : null);
					PhotonView val2 = ((obj != null) ? ((Component)obj).GetComponent<PhotonView>() : null);
					if (!((Object)(object)val2 != (Object)null) || val2.ViewID == 0)
					{
						continue;
					}
					try
					{
						object value = entry.ResolvedField.GetValue(val);
						if (value != null)
						{
							object obj2 = (entry.CastToInt ? ((object)Convert.ToInt32(value)) : value);
							val2.RPC(entry.RpcName, target, new object[1] { obj2 });
							count++;
						}
					}
					catch (Exception ex)
					{
						Debug.LogWarning((object)$"[LateJoin] Failed to sync state for {entry.TypeName} (ID: {val2.ViewID}): {ex.Message}");
					}
				}
				yield return (object)new WaitForSeconds(0.05f);
			}
			Debug.Log((object)$"[LateJoin] Finished syncing {count} interactive object states to {target.NickName}.");
		}
	}
	[HarmonyPatch(typeof(NetworkManager), "OnPlayerEnteredRoom")]
	public static class NetworkManagerOnPlayerEnteredRoomInteractivePatch
	{
		[HarmonyPostfix]
		public static void Postfix(Player newPlayer)
		{
			if (Plugin.CfgEnabled.Value)
			{
				InteractivePatches.SyncInteractiveObjectsToPlayer(newPlayer);
			}
		}
	}
	public static class ItemPatches
	{
		private sealed class PendingSync
		{
			public Player Target;

			public float NextTime;

			public int Attempts;
		}

		private static readonly FieldInfo BatteryPhotonViewField = AccessTools.Field(typeof(ItemBattery), "photonView");

		private static readonly FieldInfo BatteryLifeIntField = AccessTools.Field(typeof(ItemBattery), "batteryLifeInt");

		private static readonly FieldInfo BatteryCurrentBarsField = AccessTools.Field(typeof(ItemBattery), "currentBars");

		private static readonly FieldInfo InventorySpotBatteryVisualLogicField = AccessTools.Field(typeof(InventorySpot), "batteryVisualLogic");

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

		public static void ClearBatteryPending()
		{
			Pending.Clear();
		}

		public static void QueueBatterySync(PlayerAvatar playerAvatar)
		{
			if (Plugin.CfgEnabled.Value && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)playerAvatar != (Object)null && (Object)(object)playerAvatar.photonView != (Object)null)
			{
				Player owner = playerAvatar.photonView.Owner;
				if (owner != null && PhotonNetwork.LocalPlayer != null && owner.ActorNumber != PhotonNetwork.LocalPlayer.ActorNumber)
				{
					QueueBatterySync(owner, 2f);
				}
			}
		}

		public static void QueueAllBatterySync(float delay)
		{
			if (Plugin.CfgEnabled.Value && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && PhotonNetwork.PlayerListOthers != null)
			{
				Player[] playerListOthers = PhotonNetwork.PlayerListOthers;
				for (int i = 0; i < playerListOthers.Length; i++)
				{
					QueueBatterySync(playerListOthers[i], delay);
				}
			}
		}

		private static void QueueBatterySync(Player target, float delay)
		{
			if (target == null || PhotonNetwork.LocalPlayer == null || target.ActorNumber == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				return;
			}
			for (int num = Pending.Count - 1; num >= 0; num--)
			{
				if (Pending[num].Target != null && Pending[num].Target.ActorNumber == target.ActorNumber)
				{
					Pending.RemoveAt(num);
				}
			}
			Pending.Add(new PendingSync
			{
				Target = target,
				NextTime = Time.unscaledTime + Mathf.Max(0.1f, delay),
				Attempts = 0
			});
		}

		public static void Update()
		{
			if (Pending.Count == 0)
			{
				return;
			}
			for (int num = Pending.Count - 1; num >= 0; num--)
			{
				PendingSync pendingSync = Pending[num];
				if (pendingSync != null && pendingSync.Target != null && !(Time.unscaledTime < pendingSync.NextTime))
				{
					int num2 = SendCurrentBatteryState(pendingSync.Target);
					pendingSync.Attempts++;
					if (pendingSync.Attempts >= 12)
					{
						Pending.RemoveAt(num);
						Debug.Log((object)$"[LateJoin] Battery state sync completed for {pendingSync.Target.NickName}. Synced {num2} batteries.");
					}
					else
					{
						pendingSync.NextTime = Time.unscaledTime + 5f;
					}
				}
			}
		}

		private static int SendCurrentBatteryState(Player target)
		{
			int num = 0;
			ItemBattery[] array = FindBatteries();
			foreach (ItemBattery val in array)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				PhotonView photonView = GetPhotonView(val);
				if ((Object)(object)photonView != (Object)null && photonView.ViewID != 0 && CanReceiveBatteryRpc(photonView, val))
				{
					try
					{
						int batteryLevel = GetBatteryLevel(val);
						bool batteryActive = val.batteryActive;
						photonView.RPC("BatteryToggleRPC", target, new object[1] { false });
						photonView.RPC("BatteryFullPercentChangeRPC", target, new object[2] { batteryLevel, false });
						photonView.RPC("BatteryToggleRPC", target, new object[1] { batteryActive });
						num++;
					}
					catch (Exception ex)
					{
						Debug.LogWarning((object)("[LateJoin] Could not sync battery: " + ex.Message));
					}
				}
			}
			return num;
		}

		private static ItemBattery[] FindBatteries()
		{
			try
			{
				return Object.FindObjectsOfType<ItemBattery>(true);
			}
			catch
			{
				return Object.FindObjectsOfType<ItemBattery>();
			}
		}

		private static PhotonView GetPhotonView(ItemBattery battery)
		{
			if (BatteryPhotonViewField != null)
			{
				object? value = BatteryPhotonViewField.GetValue(battery);
				PhotonView val = (PhotonView)((value is PhotonView) ? value : null);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return ((Component)battery).GetComponent<PhotonView>();
		}

		private static bool CanReceiveBatteryRpc(PhotonView pv, ItemBattery battery)
		{
			if ((Object)(object)pv == (Object)null || (Object)(object)battery == (Object)null)
			{
				return false;
			}
			ItemBattery component = ((Component)pv).GetComponent<ItemBattery>();
			if ((Object)(object)component != (Object)null)
			{
				return component == battery;
			}
			return false;
		}

		private static int GetBatteryLevel(ItemBattery battery)
		{
			int num = Mathf.Max(1, battery.batteryBars);
			int num2 = 0;
			if (BatteryLifeIntField != null && BatteryLifeIntField.GetValue(battery) is int num3)
			{
				num2 = num3;
			}
			if (num2 < 0 || num2 > num || (num2 == 0 && battery.batteryLife > 0f))
			{
				num2 = Mathf.RoundToInt(battery.batteryLife / (100f / (float)num));
			}
			return Mathf.Clamp(num2, 0, num);
		}

		public static void RepairInventorySpot(InventorySpot spot)
		{
			if (!Plugin.CfgEnabled.Value || (Object)(object)spot == (Object)null || !spot.IsOccupied())
			{
				return;
			}
			ItemBattery val = FindBatteryInComponent((Component)(object)spot.CurrentItem);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			int num = RepairBatteryCounters(val);
			if (!(InventorySpotBatteryVisualLogicField != null))
			{
				return;
			}
			object? value = InventorySpotBatteryVisualLogicField.GetValue(spot);
			BatteryVisualLogic val2 = (BatteryVisualLogic)((value is BatteryVisualLogic) ? value : null);
			if (!((Object)(object)val2 != (Object)null))
			{
				return;
			}
			try
			{
				if (!((Component)val2).gameObject.activeSelf)
				{
					((Component)val2).gameObject.SetActive(true);
				}
				if ((Object)(object)val2.itemBattery != (Object)(object)val)
				{
					val2.itemBattery = val;
					val2.BatteryBarsSet();
				}
				else if (val2.batteryBars != val.batteryBars)
				{
					val2.BatteryBarsSet();
				}
				val2.BatteryBarsUpdate(num, true);
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[LateJoin] Could not repair inventory battery UI: " + ex.Message));
			}
		}

		public static void RepairInventoryBatteryResult(Inventory inventory, int index, ref int result)
		{
			if (!Plugin.CfgEnabled.Value || (Object)(object)inventory == (Object)null)
			{
				return;
			}
			InventorySpot spotByIndex = inventory.GetSpotByIndex(index);
			if ((Object)(object)spotByIndex == (Object)null || !spotByIndex.IsOccupied())
			{
				return;
			}
			ItemBattery val = FindBatteryInComponent((Component)(object)spotByIndex.CurrentItem);
			if ((Object)(object)val != (Object)null)
			{
				int num = RepairBatteryCounters(val);
				if (result < 0 || (result == 0 && val.batteryLife > 0f) || result > Mathf.Max(1, val.batteryBars))
				{
					result = num;
				}
			}
		}

		private static ItemBattery FindBatteryInComponent(Component c)
		{
			if ((Object)(object)c == (Object)null)
			{
				return null;
			}
			return c.GetComponent<ItemBattery>() ?? c.GetComponentInChildren<ItemBattery>(true) ?? c.GetComponentInParent<ItemBattery>(true);
		}

		private static int RepairBatteryCounters(ItemBattery battery)
		{
			int batteryLevel = GetBatteryLevel(battery);
			if (BatteryLifeIntField != null)
			{
				BatteryLifeIntField.SetValue(battery, batteryLevel);
			}
			if (BatteryCurrentBarsField != null)
			{
				BatteryCurrentBarsField.SetValue(battery, batteryLevel);
			}
			return batteryLevel;
		}

		private static int GetBatteryBars(ItemBattery battery)
		{
			if (BatteryCurrentBarsField != null)
			{
				object value = BatteryCurrentBarsField.GetValue(battery);
				if (value is int)
				{
					return (int)value;
				}
			}
			return 0;
		}

		public static void TakeoverOrphanedPhysGrabObjects(int leftActor)
		{
			if (!SemiFunc.IsMasterClient())
			{
				return;
			}
			PhysGrabObject[] array = Object.FindObjectsOfType<PhysGrabObject>();
			if (array == null)
			{
				return;
			}
			int num = 0;
			PhysGrabObject[] array2 = array;
			foreach (PhysGrabObject val in array2)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				PhotonView component = ((Component)val).GetComponent<PhotonView>();
				if ((Object)(object)component != (Object)null && component.ViewID != 0 && (component.Owner == null || component.Owner.ActorNumber == leftActor))
				{
					try
					{
						component.TransferOwnership(PhotonNetwork.LocalPlayer);
						num++;
					}
					catch (Exception ex)
					{
						Debug.LogWarning((object)("[LateJoin] Ownership transfer failed: " + ex.Message));
					}
				}
			}
			if (num > 0)
			{
				Debug.Log((object)$"[LateJoin] Took over {num} orphaned grabbed objects from leaving player {leftActor}.");
			}
		}
	}
	[HarmonyPatch(typeof(NetworkManager), "OnPlayerEnteredRoom")]
	public static class NetworkManagerOnPlayerEnteredRoomPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Player newPlayer)
		{
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.CfgEnabled.Value || (Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated)
			{
				return;
			}
			if (SemiFunc.IsMasterClient())
			{
				Debug.Log((object)("[LateJoin] Mid-game join detected: " + newPlayer.NickName + ". Syncing stats..."));
				PunManager.instance.SyncAllDictionaries();
				PhysGrabObject[] array = Object.FindObjectsOfType<PhysGrabObject>();
				foreach (PhysGrabObject val in array)
				{
					if (!((Object)(object)val != (Object)null) || val.playerGrabbing == null || val.playerGrabbing.Count <= 0)
					{
						continue;
					}
					PhotonView component = ((Component)val).GetComponent<PhotonView>();
					if (!((Object)(object)component != (Object)null))
					{
						continue;
					}
					foreach (PhysGrabber item in val.playerGrabbing)
					{
						if ((Object)(object)item != (Object)null && (Object)(object)item.photonView != (Object)null)
						{
							component.RPC("GrabPlayerAddRPC", newPlayer, new object[1] { item.photonView.ViewID });
						}
					}
				}
				ItemPatches.QueueAllBatterySync(2f);
			}
			if (!((Object)(object)PhysGrabber.instance != (Object)null) || !PhysGrabber.instance.grabbed)
			{
				return;
			}
			PhysGrabber instance = PhysGrabber.instance;
			PhysGrabObject fieldValue = instance.GetFieldValue<PhysGrabObject>("grabbedPhysGrabObject");
			if ((Object)(object)fieldValue != (Object)null && (Object)(object)instance.photonView != (Object)null && instance.photonView.IsMine)
			{
				PhotonView component2 = ((Component)fieldValue).GetComponent<PhotonView>();
				if ((Object)(object)component2 != (Object)null)
				{
					int fieldValue2 = instance.GetFieldValue<int>("grabbedPhysGrabObjectColliderID");
					component2.RPC("GrabLinkRPC", newPlayer, new object[5]
					{
						instance.photonView.ViewID,
						fieldValue2,
						instance.physGrabPoint.position,
						instance.cameraRelativeGrabbedForward,
						instance.cameraRelativeGrabbedUp
					});
				}
			}
		}
	}
	[HarmonyPatch(typeof(NetworkManager), "OnPlayerLeftRoom")]
	public static class NetworkManagerOnPlayerLeftRoomPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Player otherPlayer)
		{
			if (Plugin.CfgEnabled.Value && otherPlayer != null)
			{
				ItemPatches.TakeoverOrphanedPhysGrabObjects(otherPlayer.ActorNumber);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "Start")]
	public static class PlayerAvatarStartBatteryPatch
	{
		[HarmonyPostfix]
		public static void Postfix(PlayerAvatar __instance)
		{
			if (Plugin.CfgEnabled.Value)
			{
				ItemPatches.QueueBatterySync(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(InventorySpot), "StateOccupied")]
	public static class InventorySpotStateOccupiedPatch
	{
		[HarmonyPostfix]
		public static void Postfix(InventorySpot __instance)
		{
			if (Plugin.CfgEnabled.Value)
			{
				ItemPatches.RepairInventorySpot(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "GetBatteryStateFromInventorySpot")]
	public static class InventoryGetBatteryStateFromInventorySpotPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Inventory __instance, int index, ref int __result)
		{
			if (Plugin.CfgEnabled.Value)
			{
				ItemPatches.RepairInventoryBatteryResult(__instance, index, ref __result);
			}
		}
	}
	public static class LevelPatches
	{
		private sealed class ModuleConnectionState
		{
			public PhotonView PhotonView;

			public int ViewId;

			public bool Top;

			public bool Bottom;

			public bool Right;

			public bool Left;

			public bool First;
		}

		private sealed class PendingReplay
		{
			public Player Target;

			public float NextTime;

			public int Attempts;
		}

		private static readonly FieldInfo ModulePhotonViewField = AccessTools.Field(typeof(Module), "photonView");

		private static readonly FieldInfo ModuleConnectingTopField = AccessTools.Field(typeof(Module), "ConnectingTop");

		private static readonly FieldInfo ModuleConnectingBottomField = AccessTools.Field(typeof(Module), "ConnectingBottom");

		private static readonly FieldInfo ModuleConnectingRightField = AccessTools.Field(typeof(Module), "ConnectingRight");

		private static readonly FieldInfo ModuleConnectingLeftField = AccessTools.Field(typeof(Module), "ConnectingLeft");

		private static readonly FieldInfo ModuleSetupDoneField = AccessTools.Field(typeof(Module), "SetupDone");

		private static readonly FieldInfo ModuleFirstField = AccessTools.Field(typeof(Module), "First");

		private static readonly FieldInfo ModuleGridXField = AccessTools.Field(typeof(Module), "GridX");

		private static readonly FieldInfo ModuleGridYField = AccessTools.Field(typeof(Module), "GridY");

		private static readonly FieldInfo ModuleStartRoomField = AccessTools.Field(typeof(Module), "StartRoom");

		private static readonly FieldInfo ModulePropSwitchModuleField = AccessTools.Field(typeof(ModulePropSwitch), "Module");

		private static readonly FieldInfo LevelGeneratorModulesSpawnedField = AccessTools.Field(typeof(LevelGenerator), "ModulesSpawned");

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

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

		public static void ClearStates()
		{
			States.Clear();
			Pending.Clear();
		}

		public static bool TrySendBuffered(Module module, bool top, bool bottom, bool right, bool left, bool first)
		{
			if (!Plugin.CfgEnabled.Value)
			{
				return false;
			}
			if ((Object)(object)module == (Object)null || !SemiFunc.IsMultiplayer() || !PhotonNetwork.IsMasterClient)
			{
				return false;
			}
			PhotonView photonView = GetPhotonView(module);
			if ((Object)(object)photonView == (Object)null || photonView.ViewID == 0)
			{
				return false;
			}
			try
			{
				States[photonView.ViewID] = new ModuleConnectionState
				{
					PhotonView = photonView,
					ViewId = photonView.ViewID,
					Top = top,
					Bottom = bottom,
					Right = right,
					Left = left,
					First = first
				};
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[LateJoin] Could not store module connection state: " + ex.Message));
			}
			return false;
		}

		public static void Queue(PlayerAvatar playerAvatar)
		{
			if (Plugin.CfgEnabled.Value && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && (Object)(object)playerAvatar != (Object)null && (Object)(object)playerAvatar.photonView != (Object)null)
			{
				Queue(playerAvatar.photonView.Owner, 3f);
			}
		}

		public static void QueueAllRemote(float delay)
		{
			if (Plugin.CfgEnabled.Value && SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient && PhotonNetwork.PlayerListOthers != null)
			{
				Player[] playerListOthers = PhotonNetwork.PlayerListOthers;
				for (int i = 0; i < playerListOthers.Length; i++)
				{
					Queue(playerListOthers[i], delay);
				}
			}
		}

		private static void Queue(Player target, float delay)
		{
			if (target == null || PhotonNetwork.LocalPlayer == null || target.ActorNumber == PhotonNetwork.LocalPlayer.ActorNumber)
			{
				return;
			}
			for (int num = Pending.Count - 1; num >= 0; num--)
			{
				if (Pending[num].Target != null && Pending[num].Target.ActorNumber == target.ActorNumber)
				{
					Pending.RemoveAt(num);
				}
			}
			Pending.Add(new PendingReplay
			{
				Target = target,
				NextTime = Time.unscaledTime + Mathf.Max(0.1f, delay),
				Attempts = 0
			});
		}

		public static void Update()
		{
			if (Pending.Count == 0)
			{
				return;
			}
			for (int num = Pending.Count - 1; num >= 0; num--)
			{
				PendingReplay pendingReplay = Pending[num];
				if (pendingReplay != null && pendingReplay.Target != null && !(Time.unscaledTime < pendingReplay.NextTime))
				{
					int num2 = SendCurrentState(pendingReplay.Target);
					pendingReplay.Attempts++;
					if (pendingReplay.Attempts >= 16)
					{
						Pending.RemoveAt(num);
						Debug.Log((object)$"[LateJoin] Sent {num2} module connection states to player {pendingReplay.Target.NickName}.");
					}
					else
					{
						pendingReplay.NextTime = Time.unscaledTime + 5f;
					}
				}
			}
		}

		private static int SendCurrentState(Player target)
		{
			if (States.Count == 0)
			{
				return 0;
			}
			int num = 0;
			foreach (ModuleConnectionState value in States.Values)
			{
				if (value == null)
				{
					continue;
				}
				PhotonView val = (((Object)(object)value.PhotonView != (Object)null) ? value.PhotonView : PhotonNetwork.GetPhotonView(value.ViewId));
				if ((Object)(object)val != (Object)null && val.ViewID != 0)
				{
					try
					{
						val.RPC("ModuleConnectionSetRPC", target, new object[5] { value.Top, value.Bottom, value.Right, value.Left, value.First });
						num++;
					}
					catch (Exception ex)
					{
						Debug.LogWarning((object)("[LateJoin] Could not replay module connection: " + ex.Message));
					}
				}
			}
			return num;
		}

		public static void ApplyLocalClientConnections(LevelGenerator levelGenerator)
		{
			if (!Plugin.CfgEnabled.Value || !SemiFunc.IsMultiplayer() || PhotonNetwork.IsMasterClient || (Object)(object)levelGenerator == (Object)null)
			{
				return;
			}
			Module[] array = FindModules();
			if (array == null || array.Length == 0)
			{
				return;
			}
			Dictionary<string, Module> dictionary = new Dictionary<string, Module>();
			Module[] array2 = array;
			foreach (Module val in array2)
			{
				if ((Object)(object)val != (Object)null && !IsStartRoom(val))
				{
					int num = GetInt(ModuleGridXField, val);
					int num2 = GetInt(ModuleGridYField, val);
					string key = $"{num}:{num2}";
					if (!dictionary.ContainsKey(key))
					{
						dictionary.Add(key, val);
					}
				}
			}
			int num3 = 0;
			array2 = array;
			foreach (Module val2 in array2)
			{
				if ((Object)(object)val2 != (Object)null && !IsStartRoom(val2))
				{
					int num4 = GetInt(ModuleGridXField, val2);
					int num5 = GetInt(ModuleGridYField, val2);
					bool flag = IsFirstModule(val2);
					bool top = dictionary.ContainsKey($"{num4}:{num5 + 1}");
					bool bottom = dictionary.ContainsKey($"{num4}:{num5 - 1}") || flag;
					bool right = dictionary.ContainsKey($"{num4 + 1}:{num5}");
					bool left = dictionary.ContainsKey($"{num4 - 1}:{num5}");
					if (ApplyState(val2, top, bottom, right, left, flag, countModuleReady: true))
					{
						num3++;
					}
				}
			}
		}

		public static bool ApplyIncomingRpc(Module module, bool top, bool bottom, bool right, bool left, bool first)
		{
			if ((Object)(object)module == (Object)null || !SemiFunc.IsMultiplayer() || PhotonNetwork.IsMasterClient)
			{
				return true;
			}
			bool flag = GetBool(ModuleSetupDoneField, module);
			if (flag && GetBool(ModuleConnectingTopField, module) == top && GetBool(ModuleConnectingBottomField, module) == bottom && GetBool(ModuleConnectingRightField, module) == right && GetBool(ModuleConnectingLeftField, module) == left && GetBool(ModuleFirstField, module) == first)
			{
				return false;
			}
			ApplyState(module, top, bottom, right, left, first, !flag);
			return false;
		}

		private static PhotonView GetPhotonView(Module module)
		{
			if (ModulePhotonViewField != null)
			{
				object? value = ModulePhotonViewField.GetValue(module);
				PhotonView val = (PhotonView)((value is PhotonView) ? value : null);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return ((Component)module).GetComponent<PhotonView>();
		}

		private static bool ApplyState(Module module, bool top, bool bottom, bool right, bool left, bool first, bool countModuleReady)
		{
			if ((Object)(object)module == (Object)null)
			{
				return false;
			}
			bool flag = GetBool(ModuleSetupDoneField, module);
			SetBool(ModuleConnectingTopField, module, top);
			SetBool(ModuleConnectingBottomField, module, bottom);
			SetBool(ModuleConnectingRightField, module, right);
			SetBool(ModuleConnectingLeftField, module, left);
			SetBool(ModuleFirstField, module, first);
			SetBool(ModuleSetupDoneField, module, value: true);
			try
			{
				ModulePropSwitch[] componentsInChildren = ((Component)module).GetComponentsInChildren<ModulePropSwitch>(true);
				foreach (ModulePropSwitch val in componentsInChildren)
				{
					if ((Object)(object)val != (Object)null)
					{
						if (ModulePropSwitchModuleField != null)
						{
							ModulePropSwitchModuleField.SetValue(val, module);
						}
						val.Setup();
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[LateJoin] ModulePropSwitch setup failed: " + ex.Message));
			}
			if (countModuleReady && !flag && (Object)(object)LevelGenerator.Instance != (Object)null && LevelGeneratorModulesSpawnedField != null)
			{
				int num = GetInt(LevelGeneratorModulesSpawnedField, LevelGenerator.Instance);
				LevelGeneratorModulesSpawnedField.SetValue(LevelGenerator.Instance, num + 1);
			}
			return !flag;
		}

		private static bool IsFirstModule(Module module)
		{
			if ((Object)(object)module != (Object)null)
			{
				if (!GetBool(ModuleFirstField, module) && !IsStartRoom(module) && !((Object)(object)((Component)module).GetComponent<StartRoom>() != (Object)null))
				{
					return (Object)(object)((Component)module).GetComponentInChildren<StartRoom>(true) != (Object)null;
				}
				return true;
			}
			return false;
		}

		private static bool IsStartRoom(Module module)
		{
			if ((Object)(object)module != (Object)null)
			{
				return GetBool(ModuleStartRoomField, module);
			}
			return false;
		}

		private static Module[] FindModules()
		{
			try
			{
				return Object.FindObjectsOfType<Module>(true);
			}
			catch
			{
				return Object.FindObjectsOfType<Module>();
			}
		}

		private static bool GetBool(FieldInfo field, object instance)
		{
			if (field == null || instance == null)
			{
				return false;
			}
			object value = field.GetValue(instance);
			bool flag = default(bool);
			int num;
			if (value is bool)
			{
				flag = (bool)value;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static void SetBool(FieldInfo field, object instance, bool value)
		{
			if (field != null && instance != null)
			{
				field.SetValue(instance, value);
			}
		}

		private static int GetInt(FieldInfo field, object instance)
		{
			if (field == null || instance == null)
			{
				return 0;
			}
			object value = field.GetValue(instance);
			if (value is int)
			{
				return (int)value;
			}
			return 0;
		}
	}
	[HarmonyPatch(typeof(LevelGenerator), "Start")]
	public static class LevelGeneratorStartPatch
	{
		[HarmonyPostfix]
		public static void Postfix()
		{
			if (Plugin.CfgEnabled.Value)
			{
				LevelPatches.ClearStates();
			}
		}
	}
	[HarmonyPatch(typeof(LevelGenerator), "GenerateDone")]
	public static class LevelGeneratorGenerateDonePatch
	{
		[HarmonyPostfix]
		public static void Postfix()
		{
			if (Plugin.CfgEnabled.Value)
			{
				LevelPatches.QueueAllRemote(2f);
			}
		}
	}
	[HarmonyPatch(typeof(LevelGenerator), "GenerateConnectObjects")]
	public static class LevelGeneratorGenerateConnectObjectsPatch
	{
		[HarmonyPrefix]
		public static void Prefix(LevelGenerator __instance)
		{
			if (Plugin.CfgEnabled.Value)
			{
				LevelPatches.ApplyLocalClientConnections(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(LevelGenerator), "GenerateBlockObjects")]
	public static class LevelGeneratorGenerateBlockObjectsPatch
	{
		[HarmonyPrefix]
		public static void Prefix(LevelGenerator __instance)
		{
			if (Plugin.CfgEnabled.Value)
			{
				LevelPatches.ApplyLocalClientConnections(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Module), "ModuleConnectionSet")]
	public static class ModuleConnectionSetPatch
	{
		[HarmonyPrefix]
		public static bool Prefix(Module __instance, bool _top, bool _bottom, bool _right, bool _left, bool _first)
		{
			if (Plugin.CfgEnabled.Value)
			{
				return !LevelPatches.TrySendBuffered(__instance, _top, _bottom, _right, _left, _first);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Module), "ModuleConnectionSetRPC")]
	public static class ModuleConnectionSetRpcPatch
	{
		[HarmonyPrefix]
		public static bool Prefix(Module __instance, bool _top, bool _bottom, bool _right, bool _left, bool _first)
		{
			if (Plugin.CfgEnabled.Value)
			{
				return LevelPatches.ApplyIncomingRpc(__instance, _top, _bottom, _right, _left, _first);
			}
			return true;
		}
	}
	public static class ReflectionHelper
	{
		public static T GetFieldValue<T>(this object obj, string fieldName)
		{
			FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), fieldName);
			if (fieldInfo != null)
			{
				return (T)fieldInfo.GetValue(obj);
			}
			return default(T);
		}

		public static void SetFieldValue(this object obj, string fieldName, object value)
		{
			FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), fieldName);
			if (fieldInfo != null)
			{
				fieldInfo.SetValue(obj, value);
			}
		}
	}
	[HarmonyPatch(typeof(MenuPageLobby), "ButtonStart")]
	public static class MenuPageLobbyButtonStartPatch
	{
		[HarmonyPostfix]
		public static void Postfix()
		{
			if (!Plugin.CfgEnabled.Value || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				if ((Object)(object)SteamManager.instance != (Object)null)
				{
					bool fieldValue = SteamManager.instance.GetFieldValue<bool>("privateLobby");
					SteamManager.instance.UnlockLobby(!fieldValue);
				}
				if (PhotonNetwork.CurrentRoom != null)
				{
					PhotonNetwork.CurrentRoom.IsOpen = true;
					PhotonNetwork.CurrentRoom.IsVisible = true;
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[LateJoin] ButtonStart Postfix unlock failed: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(SteamManager), "LockLobby")]
	public static class SteamManagerLockLobbyPatch
	{
		[HarmonyPrefix]
		public static bool Prefix()
		{
			if (Plugin.CfgEnabled.Value)
			{
				Debug.Log((object)"[LateJoin] Suppressing SteamManager.LockLobby()");
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
	public static class RunManagerChangeLevelPatch
	{
		private static readonly FieldInfo removeFilterFieldInfo = AccessTools.Field(typeof(PhotonNetwork), "removeFilter");

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

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

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

		[HarmonyPrefix]
		public static void Prefix(RunManager __instance, bool _completedLevel, bool _levelFailed, ChangeLevelType _changeLevelType)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.CfgEnabled.Value || _levelFailed || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				object value = AccessTools.Field(typeof(RunManager), "runManagerPUN").GetValue(__instance);
				object? value2 = AccessTools.Field(value.GetType(), "photonView").GetValue(value);
				PhotonView val = (PhotonView)((value2 is PhotonView) ? value2 : null);
				if (val != null)
				{
					PhotonNetwork.RemoveBufferedRPCs(val.ViewID, (string)null, (int[])null);
				}
				PhotonView[] array = Object.FindObjectsOfType<PhotonView>();
				foreach (PhotonView val2 in array)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						Scene scene = ((Component)val2).gameObject.scene;
						if (((Scene)(ref scene)).buildIndex != -1)
						{
							ClearPhotonCache(val2);
						}
					}
				}
				Debug.Log((object)"[LateJoin] Cleared Photon RPC cache for level change.");
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("[LateJoin] Error in ChangeLevel Prefix: " + ex.Message));
			}
		}

		[HarmonyPostfix]
		public static void Postfix()
		{
			if (Plugin.CfgEnabled.Value && PhotonNetwork.IsMasterClient)
			{
				SteamManager.instance.UnlockLobby(true);
				if (PhotonNetwork.CurrentRoom != null)
				{
					PhotonNetwork.CurrentRoom.IsOpen = true;
					PhotonNetwork.CurrentRoom.IsVisible = true;
				}
			}
		}

		private static void ClearPhotonCache(PhotonView pv)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				object? value = removeFilterFieldInfo.GetValue(null);
				Hashtable val = (Hashtable)((value is Hashtable) ? value : null);
				object value2 = keyByteSevenFieldInfo.GetValue(null);
				object? value3 = serverCleanOptionsFieldInfo.GetValue(null);
				RaiseEventOptions val2 = (RaiseEventOptions)((value3 is RaiseEventOptions) ? value3 : null);
				if (val != null && val2 != null)
				{
					val[value2] = pv.InstantiationId;
					val2.CachingOption = (EventCaching)6;
					raiseEventInternalMethodInfo.Invoke(null, new object[4]
					{
						(byte)202,
						val,
						val2,
						SendOptions.SendReliable
					});
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[LateJoin] ClearPhotonCache failed: " + ex.Message));
			}
		}
	}
	public static class LobbyPatches
	{
		private static float nextUnlockTime;

		public static void Update()
		{
			if (!Plugin.CfgEnabled.Value || !PhotonNetwork.IsMasterClient || !(Time.unscaledTime >= nextUnlockTime))
			{
				return;
			}
			nextUnlockTime = Time.unscaledTime + 2f;
			try
			{
				if ((Object)(object)SteamManager.instance != (Object)null)
				{
					bool fieldValue = SteamManager.instance.GetFieldValue<bool>("privateLobby");
					SteamManager.instance.UnlockLobby(!fieldValue);
				}
				if (PhotonNetwork.CurrentRoom != null)
				{
					PhotonNetwork.CurrentRoom.IsOpen = true;
					PhotonNetwork.CurrentRoom.IsVisible = true;
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[LateJoin] Periodic unlock failed: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "Spawn")]
	public static class PlayerAvatarSpawnPatch
	{
		[HarmonyPrefix]
		public static bool Prefix(PlayerAvatar __instance)
		{
			if (!Plugin.CfgEnabled.Value)
			{
				return true;
			}
			return !__instance.GetFieldValue<bool>("spawned");
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "Start")]
	public static class PlayerAvatarStartPatch
	{
		private static bool originalGeneratedState;

		[HarmonyPrefix]
		public static void Prefix(PlayerAvatar __instance)
		{
			if (Plugin.CfgEnabled.Value && (Object)(object)LevelGenerator.Instance != (Object)null)
			{
				originalGeneratedState = LevelGenerator.Instance.Generated;
				if (originalGeneratedState)
				{
					LevelGenerator.Instance.Generated = false;
				}
			}
		}

		[HarmonyPostfix]
		public static void Postfix(PlayerAvatar __instance)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.CfgEnabled.Value || !((Object)(object)LevelGenerator.Instance != (Object)null))
			{
				return;
			}
			if (originalGeneratedState)
			{
				LevelGenerator.Instance.Generated = true;
				if (SemiFunc.IsMasterClient() && (Object)(object)__instance.photonView != (Object)null && !__instance.photonView.IsMine)
				{
					Vector3 val = Vector3.zero;
					Quaternion val2 = Quaternion.identity;
					bool flag = false;
					if ((Object)(object)TruckSafetySpawnPoint.instance != (Object)null)
					{
						val = ((Component)TruckSafetySpawnPoint.instance).transform.position;
						val2 = ((Component)TruckSafetySpawnPoint.instance).transform.rotation;
						flag = true;
					}
					else
					{
						SpawnPoint[] array = Object.FindObjectsOfType<SpawnPoint>();
						if (array.Length != 0)
						{
							SpawnPoint obj = array[Random.Range(0, array.Length)];
							val = ((Component)obj).transform.position;
							val2 = ((Component)obj).transform.rotation;
							flag = true;
						}
					}
					if (flag)
					{
						Debug.Log((object)$"[LateJoin] Teleporting late-joining player {__instance.photonView.Owner.NickName} to Safety Spawnpoint: {val}");
						__instance.Spawn(val, val2);
					}
				}
			}
			if (PhotonNetwork.IsMasterClient && !SemiFunc.RunIsLobby() && (Object)(object)__instance.photonView != (Object)null)
			{
				__instance.photonView.RPC("LoadingLevelAnimationCompletedRPC", (RpcTarget)4, Array.Empty<object>());
			}
			((MonoBehaviour)__instance).StartCoroutine(InitLateJoinPlayer(__instance));
		}

		private static IEnumerator InitLateJoinPlayer(PlayerAvatar player)
		{
			while ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated)
			{
				yield return null;
			}
			yield return (object)new WaitForSeconds(0.5f);
			if (!((Object)(object)player == (Object)null) && !((Object)(object)((Component)player).gameObject == (Object)null) && player.photonView.IsMine && SemiFunc.IsMultiplayer() && !SemiFunc.MenuLevel())
			{
				PlayerDeathHead fieldValue = player.GetFieldValue<PlayerDeathHead>("playerDeathHead");
				Vector3 fieldValue2 = AssetManager.instance.GetFieldValue<Vector3>("physDisabledPosition");
				if (!Object.op_Implicit((Object)(object)fieldValue))
				{
					Debug.Log((object)"[LateJoin] Spawning local DeathHead for self.");
					PlayerDeathHead component = PhotonNetwork.Instantiate(((Object)LevelGenerator.Instance.PlayerDeathHeadPrefab).name, fieldValue2, Quaternion.identity, (byte)0, (object[])null).GetComponent<PlayerDeathHead>();
					component.playerAvatar = player;
					player.SetFieldValue("playerDeathHead", component);
				}
				if (!Object.op_Implicit((Object)(object)player.GetFieldValue<PlayerTumble>("tumble")))
				{
					Debug.Log((object)"[LateJoin] Spawning local Tumble for self.");
					PlayerTumble component2 = PhotonNetwork.Instantiate(((Object)LevelGenerator.Instance.PlayerTumblePrefab).name, fieldValue2, Quaternion.identity, (byte)0, (object[])null).GetComponent<PlayerTumble>();
					component2.playerAvatar = player;
					player.SetFieldValue("tumble", component2);
				}
			}
		}
	}
}