Decompiled source of REPO Fixes Antispam Latency Fix v1.2.1

REPOFixesPlus.dll

Decompiled a day 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 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 Steamworks;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.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 REPOFixesPlus
{
	[HarmonyPatch(typeof(GameDirector), "Start")]
	internal static class ConnectionTimeoutPatch
	{
		internal static int TimeoutSeconds = 30;

		private static bool _logged;

		private static void Postfix()
		{
			try
			{
				if (!PhotonNetwork.IsConnected)
				{
					return;
				}
				LoadBalancingClient networkingClient = PhotonNetwork.NetworkingClient;
				LoadBalancingPeer val = ((networkingClient != null) ? networkingClient.LoadBalancingPeer : null);
				if (val != null)
				{
					int num = TimeoutSeconds * 1000;
					if (((PhotonPeer)val).DisconnectTimeout < num)
					{
						((PhotonPeer)val).DisconnectTimeout = num;
					}
					if (((PhotonPeer)val).SentCountAllowance < 10)
					{
						((PhotonPeer)val).SentCountAllowance = 10;
					}
					if (!_logged)
					{
						_logged = true;
						Plugin.Log.LogInfo((object)$"[Connection timeout] Photon disconnect timeout set to {((PhotonPeer)val).DisconnectTimeout / 1000}s, resends to {((PhotonPeer)val).SentCountAllowance}.");
					}
				}
			}
			catch (Exception e)
			{
				Suppressed.Report("Connection timeout", e);
			}
		}
	}
	[HarmonyPatch(typeof(SlowWalkerAttack), "SuckInListPlayerUpdate")]
	internal static class SlowWalkerSuckInFinalizerPatch
	{
		private static Exception Finalizer(Exception __exception)
		{
			if (__exception != null)
			{
				Suppressed.Report("SlowWalkerAttack.SuckInListPlayerUpdate", __exception);
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "Awake")]
	internal static class PlayerAvatarAwakePatch
	{
		private static void Postfix(PlayerAvatar __instance)
		{
			try
			{
				if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.photonView == (Object)null) && !((Object)(object)GameDirector.instance == (Object)null) && GameDirector.instance.PlayerList.Contains(__instance))
				{
					int viewID = __instance.photonView.ViewID;
					int num = RegistrationHelper.RegisterPlayerWithAllTrackers(viewID);
					if (num > 0)
					{
						Plugin.Log.LogInfo((object)$"Registered player (viewID={viewID}) with {num} enemy trackers currently in the scene.");
					}
				}
			}
			catch (Exception e)
			{
				Suppressed.Report("PlayerAvatar.Awake postfix", e);
			}
		}
	}
	[HarmonyPatch(typeof(EnemyVision), "Awake")]
	internal static class EnemyVisionAwakePatch
	{
		private static void Postfix(EnemyVision __instance)
		{
			try
			{
				RegistrationHelper.RegisterAllCurrentPlayers((Action<int>)__instance.PlayerAdded);
			}
			catch (Exception e)
			{
				Suppressed.Report("EnemyVision.Awake postfix", e);
			}
		}
	}
	[HarmonyPatch(typeof(EnemyOnScreen), "Awake")]
	internal static class EnemyOnScreenAwakePatch
	{
		private static void Postfix(EnemyOnScreen __instance)
		{
			try
			{
				RegistrationHelper.RegisterAllCurrentPlayers((Action<int>)__instance.PlayerAdded);
			}
			catch (Exception e)
			{
				Suppressed.Report("EnemyOnScreen.Awake postfix", e);
			}
		}
	}
	[HarmonyPatch(typeof(EnemyOnScreen), "GetOnScreen")]
	internal static class GetOnScreenPatch
	{
		private static void Prefix(EnemyOnScreen __instance, PlayerAvatar _playerAvatar)
		{
			if ((Object)(object)_playerAvatar != (Object)null && (Object)(object)_playerAvatar.photonView != (Object)null)
			{
				__instance.PlayerAdded(_playerAvatar.photonView.ViewID);
			}
		}
	}
	internal static class RegistrationHelper
	{
		public static void RegisterAllCurrentPlayers(Action<int> playerAdded)
		{
			if ((Object)(object)GameDirector.instance == (Object)null || GameDirector.instance.PlayerList == null)
			{
				return;
			}
			foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
			{
				if ((Object)(object)player != (Object)null && (Object)(object)player.photonView != (Object)null)
				{
					playerAdded(player.photonView.ViewID);
				}
			}
		}

		public static int RegisterPlayerWithAllTrackers(int viewID)
		{
			int num = 0;
			EnemyVision[] array = Object.FindObjectsOfType<EnemyVision>(true);
			foreach (EnemyVision val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					val.PlayerAdded(viewID);
					num++;
				}
			}
			EnemyOnScreen[] array2 = Object.FindObjectsOfType<EnemyOnScreen>(true);
			foreach (EnemyOnScreen val2 in array2)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					val2.PlayerAdded(viewID);
					num++;
				}
			}
			return num;
		}
	}
	internal static class ExtraSlotsPersistence
	{
		private const int FirstExtraSpot = 3;

		private const float LocalTimeout = 10f;

		private const float RemoteSettle = 4f;

		private static readonly FieldRef<PlayerAvatar, string> SteamID = AccessTools.FieldRefAccess<PlayerAvatar, string>("steamID");

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

		private static readonly HashSet<string> Taken = new HashSet<string>();

		internal static void Record(string steamID, string itemName, int spot)
		{
			if (spot < 3 || string.IsNullOrEmpty(steamID) || !SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			if (!Records.TryGetValue(steamID, out var value))
			{
				value = new Dictionary<int, int>();
				Records[steamID] = value;
			}
			if (string.IsNullOrEmpty(itemName))
			{
				if (value.Remove(spot))
				{
					Plugin.Log.LogInfo((object)$"[Extra slots] Slot {spot + 1} emptied.");
				}
			}
			else
			{
				value[spot] = itemName.GetHashCode();
				Plugin.Log.LogInfo((object)$"[Extra slots] Remembering '{itemName}' in slot {spot + 1}.");
			}
		}

		internal static void TryReequip(string itemName, int photonViewID, ItemAttributes itemAttributes)
		{
			if (Records.Count == 0 || string.IsNullOrEmpty(itemName) || !SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			if (SemiFunc.IsMultiplayer())
			{
				PhotonView val = PhotonView.Find(photonViewID);
				itemAttributes = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<ItemAttributes>() : null);
			}
			ItemEquippable val2 = (Object.op_Implicit((Object)(object)itemAttributes) ? ((Component)itemAttributes).GetComponent<ItemEquippable>() : null);
			if (!Object.op_Implicit((Object)(object)val2) || val2.IsEquipped())
			{
				return;
			}
			int hashCode = itemName.GetHashCode();
			foreach (PlayerAvatar item2 in SemiFunc.PlayerGetList())
			{
				string text = (Object.op_Implicit((Object)(object)item2) ? SteamID.Invoke(item2) : null);
				if (text == null || !Records.TryGetValue(text, out var value))
				{
					continue;
				}
				foreach (KeyValuePair<int, int> item3 in value)
				{
					string item = text + ":" + item3.Key;
					if (item3.Value == hashCode && !Taken.Contains(item))
					{
						Taken.Add(item);
						if ((Object)(object)PunManager.instance != (Object)null)
						{
							((MonoBehaviour)PunManager.instance).StartCoroutine(ReequipWhenReady(val2, item3.Key, item2, itemName));
						}
						return;
					}
				}
			}
		}

		private static IEnumerator ReequipWhenReady(ItemEquippable equippable, int spot, PlayerAvatar player, string itemName)
		{
			bool local = !SemiFunc.IsMultiplayer() || ((Object)(object)player.photonView != (Object)null && player.photonView.IsMine);
			float start = Time.unscaledTime;
			while (true)
			{
				if (!Object.op_Implicit((Object)(object)equippable) || !Object.op_Implicit((Object)(object)player))
				{
					yield break;
				}
				float num = Time.unscaledTime - start;
				if (local ? LocalSpotExists(spot) : (num >= 4f))
				{
					break;
				}
				if (num >= 10f)
				{
					Plugin.Log.LogWarning((object)$"[Extra slots] Slot {spot + 1} never appeared; leaving '{itemName}' in the truck.");
					yield break;
				}
				yield return null;
			}
			if (!equippable.IsEquipped())
			{
				equippable.RequestEquip(spot, SemiFunc.IsMultiplayer() ? player.photonView.ViewID : (-1));
				Plugin.Log.LogInfo((object)$"[Extra slots] Put '{itemName}' back in slot {spot + 1} (after {Time.unscaledTime - start:0.0}s).");
			}
		}

		private static bool LocalSpotExists(int spot)
		{
			Inventory instance = Inventory.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			List<InventorySpot> allSpots = instance.GetAllSpots();
			if (allSpots != null && spot < allSpots.Count)
			{
				return (Object)(object)allSpots[spot] != (Object)null;
			}
			return false;
		}

		internal static void ClearTaken()
		{
			Taken.Clear();
		}

		internal static void ClearAll()
		{
			Records.Clear();
			Taken.Clear();
		}
	}
	[HarmonyPatch(typeof(StatsManager), "PlayerInventoryUpdate")]
	internal static class ExtraSlotsRecordPatch
	{
		private static void Postfix(string _steamID, string itemName, int spot)
		{
			try
			{
				ExtraSlotsPersistence.Record(_steamID, itemName, spot);
			}
			catch (Exception e)
			{
				Suppressed.Report("Extra slots record", e);
			}
		}
	}
	[HarmonyPatch(typeof(PunManager), "SetItemNameLOGIC")]
	internal static class ExtraSlotsReequipPatch
	{
		private static void Postfix(string _name, int photonViewID, ItemAttributes _itemAttributes)
		{
			try
			{
				ExtraSlotsPersistence.TryReequip(_name, photonViewID, _itemAttributes);
			}
			catch (Exception e)
			{
				Suppressed.Report("Extra slots re-equip", e);
			}
		}
	}
	[HarmonyPatch(typeof(StatsManager), "StuffNeedingResetAtTheEndOfAScene")]
	internal static class ExtraSlotsSceneResetPatch
	{
		private static void Postfix()
		{
			ExtraSlotsPersistence.ClearTaken();
		}
	}
	[HarmonyPatch(typeof(StatsManager), "ResetAllStats")]
	internal static class ExtraSlotsRunResetPatch
	{
		private static void Postfix()
		{
			ExtraSlotsPersistence.ClearAll();
		}
	}
	internal static class GameboyIdlePause
	{
		private const byte FrameSyncEventCode = 192;

		private const int PackedFrameBytes = 5760;

		private static readonly byte[] BlackFrame = CreateBlackFrame();

		private static FieldInfo _hostEmulatorField;

		private static FieldInfo _controllerField;

		private static FieldInfo _lastPackedFrameField;

		private static FieldInfo _broadcastFrameIdField;

		private static MethodInfo _hasHostAuthority;

		private static MethodInfo _broadcastStatus;

		private static MethodInfo _clearDisplay;

		private static MethodInfo _flushSave;

		private static object _pausedEmulator;

		private static double _nextStatus;

		internal static bool TryApply(Harmony harmony)
		{
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Expected O, but got Unknown
			Type type = (Plugin.ModLoaded("com.empress.gameboy") ? AccessTools.TypeByName("EmpressGameboy.EmpressGameboyRuntime") : null);
			if (type == null)
			{
				return false;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "TickHostEmulator", (Type[])null, (Type[])null);
			_hostEmulatorField = AccessTools.Field(type, "_hostEmulator");
			_controllerField = AccessTools.Field(type, "_controllerActorNumber");
			_lastPackedFrameField = AccessTools.Field(type, "_lastPackedFrame");
			_broadcastFrameIdField = AccessTools.Field(type, "_broadcastFrameId");
			_hasHostAuthority = AccessTools.Method(type, "HasHostAuthority", (Type[])null, (Type[])null);
			_broadcastStatus = AccessTools.Method(type, "BroadcastStatus", new Type[1] { typeof(bool) }, (Type[])null);
			_clearDisplay = AccessTools.Method(type, "ClearDisplayTexture", new Type[1] { typeof(Color32) }, (Type[])null);
			_flushSave = ((_hostEmulatorField != null) ? AccessTools.Method(_hostEmulatorField.FieldType, "FlushSave", (Type[])null, (Type[])null) : null);
			if (methodInfo == null || _hostEmulatorField == null || _controllerField?.FieldType != typeof(int) || _lastPackedFrameField?.FieldType != typeof(byte[]) || _broadcastFrameIdField?.FieldType != typeof(int) || _hasHostAuthority == null || _broadcastStatus == null || _clearDisplay == null || _flushSave == null)
			{
				Plugin.Log.LogWarning((object)"[Gameboy idle pause] Empress Gameboy is installed but doesn't have the expected internals (different version?); leaving it alone.");
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(GameboyIdlePause), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static bool Prefix(object __instance)
		{
			try
			{
				object value = _hostEmulatorField.GetValue(__instance);
				if (value == null || !(bool)_hasHostAuthority.Invoke(__instance, null))
				{
					_pausedEmulator = null;
					return true;
				}
				if ((int)_controllerField.GetValue(__instance) != 0)
				{
					_pausedEmulator = null;
					return true;
				}
				if (_pausedEmulator != value)
				{
					_pausedEmulator = value;
					EnterPause(__instance, value);
				}
				double unscaledTimeAsDouble = Time.unscaledTimeAsDouble;
				if (unscaledTimeAsDouble >= _nextStatus)
				{
					_nextStatus = unscaledTimeAsDouble + 1.0;
					_broadcastStatus.Invoke(__instance, new object[1] { false });
				}
				return false;
			}
			catch (Exception e)
			{
				Suppressed.Report("Gameboy idle pause", e);
				return true;
			}
		}

		private static void EnterPause(object runtime, object emulator)
		{
			//IL_0023: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			_flushSave.Invoke(emulator, null);
			_clearDisplay.Invoke(runtime, new object[1] { (object)new Color32((byte)0, (byte)0, (byte)0, byte.MaxValue) });
			_lastPackedFrameField.SetValue(runtime, null);
			if (PhotonNetwork.InRoom)
			{
				int num = (int)_broadcastFrameIdField.GetValue(runtime) + 1;
				_broadcastFrameIdField.SetValue(runtime, num);
				PhotonNetwork.RaiseEvent((byte)192, (object)new object[2] { num, BlackFrame }, new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)0
				}, SendOptions.SendReliable);
			}
			Plugin.Log.LogInfo((object)"[Gameboy idle pause] Nobody is playing; Game Boy paused and screen blanked.");
		}

		private static byte[] CreateBlackFrame()
		{
			byte[] array = new byte[5760];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = byte.MaxValue;
			}
			return array;
		}
	}
	[HarmonyPatch(typeof(PhysGrabBeam), "DrawCurve")]
	internal static class DrawCurveNullGuardPatch
	{
		private static bool Prefix(PhysGrabBeam __instance)
		{
			if (!Object.op_Implicit((Object)(object)__instance.PhysGrabPointPuller) || !Object.op_Implicit((Object)(object)__instance.PhysGrabPointOrigin) || !Object.op_Implicit((Object)(object)__instance.PhysGrabPoint))
			{
				return false;
			}
			return true;
		}

		private static Exception Finalizer(Exception __exception)
		{
			if (__exception != null)
			{
				Suppressed.Report("PhysGrabBeam.DrawCurve", __exception);
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(PlayerEyes), "LookAtTransform")]
	internal static class LookAtTransformFinalizerPatch
	{
		private static Exception Finalizer(Exception __exception)
		{
			if (__exception != null)
			{
				Suppressed.Report("PlayerEyes.LookAtTransform", __exception);
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(PlayerEyes), "ClosestPhysGrabPoint")]
	internal static class ClosestPhysGrabPointFinalizerPatch
	{
		private static Exception Finalizer(Exception __exception)
		{
			if (__exception != null)
			{
				Suppressed.Report("PlayerEyes.ClosestPhysGrabPoint", __exception);
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "PhysicsGrabbingManipulation")]
	internal static class PhysicsGrabbingManipulationFinalizerPatch
	{
		private static Exception Finalizer(Exception __exception)
		{
			if (__exception != null)
			{
				Suppressed.Report("PhysGrabObject.PhysicsGrabbingManipulation", __exception);
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(PhysGrabber), "OverrideDisableGrabLookAt")]
	internal static class OverrideDisableGrabLookAtFinalizerPatch
	{
		private static Exception Finalizer(Exception __exception)
		{
			if (__exception != null)
			{
				Suppressed.Report("PhysGrabber.OverrideDisableGrabLookAt", __exception);
			}
			return null;
		}
	}
	internal static class RpcInstantHelper
	{
		internal static readonly FieldRef<PhysGrabber, bool> BeamActiveRef = AccessTools.FieldRefAccess<PhysGrabber, bool>("physGrabBeamActive");

		internal static readonly MethodInfo ActivateRpcMethod = AccessTools.Method(typeof(PhysGrabber), "PhysGrabBeamActivateRPC", (Type[])null, (Type[])null);

		internal static readonly MethodInfo DeactivateRpcMethod = AccessTools.Method(typeof(PhysGrabber), "PhysGrabBeamDeactivateRPC", (Type[])null, (Type[])null);

		internal static bool ShouldGoInstant(PhysGrabber instance)
		{
			if (SemiFunc.IsMultiplayer() && (Object)(object)instance.photonView != (Object)null)
			{
				return instance.photonView.IsMine;
			}
			return false;
		}

		internal static PhotonMessageInfo BuildSelfInfo(PhysGrabber instance)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			return new PhotonMessageInfo(instance.photonView.Owner, PhotonNetwork.ServerTimestamp, instance.photonView);
		}
	}
	[HarmonyPatch(typeof(PhysGrabber), "PhysGrabBeamActivate")]
	[HarmonyPriority(200)]
	internal static class PhysGrabBeamActivateInstantPatch
	{
		private static bool Prefix(PhysGrabber __instance)
		{
			//IL_001f: 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_0033: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!RpcInstantHelper.ShouldGoInstant(__instance))
				{
					return true;
				}
				if (RpcInstantHelper.BeamActiveRef.Invoke(__instance))
				{
					return false;
				}
				PhotonMessageInfo val = RpcInstantHelper.BuildSelfInfo(__instance);
				RpcInstantHelper.ActivateRpcMethod.Invoke(__instance, new object[1] { val });
				__instance.photonView.RPC("PhysGrabBeamActivateRPC", (RpcTarget)1, Array.Empty<object>());
				return false;
			}
			catch (Exception e)
			{
				Suppressed.Report("instant grab beam activate (fell back to vanilla)", e);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabber), "PhysGrabBeamDeactivate")]
	[HarmonyPriority(200)]
	internal static class PhysGrabBeamDeactivateInstantPatch
	{
		private static bool Prefix(PhysGrabber __instance)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!RpcInstantHelper.ShouldGoInstant(__instance))
				{
					return true;
				}
				PhotonMessageInfo val = RpcInstantHelper.BuildSelfInfo(__instance);
				RpcInstantHelper.DeactivateRpcMethod.Invoke(__instance, new object[1] { val });
				__instance.photonView.RPC("PhysGrabBeamDeactivateRPC", (RpcTarget)1, Array.Empty<object>());
				return false;
			}
			catch (Exception e)
			{
				Suppressed.Report("instant grab beam deactivate (fell back to vanilla)", e);
				return true;
			}
		}
	}
	internal static class LateJoinNowEquipFix
	{
		private static readonly FieldRef<ItemEquippable, bool> IsEquippedRef = AccessTools.FieldRefAccess<ItemEquippable, bool>("isEquipped");

		internal static bool TryApply(Harmony harmony)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			Type type = (Plugin.ModLoaded("zichen.latejoinnow") ? AccessTools.TypeByName("Zichen_LateJoinNow.Plugin") : null);
			MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "BuildPlayerPersistentViewIdSet", (Type[])null, (Type[])null) : null);
			if (methodInfo == null)
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(LateJoinNowEquipFix), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static void Postfix(object __result)
		{
			try
			{
				if (!(__result is HashSet<int> hashSet))
				{
					return;
				}
				ItemEquippable[] array = Object.FindObjectsOfType<ItemEquippable>(true);
				foreach (ItemEquippable val in array)
				{
					if (!((Object)(object)val == (Object)null) && !((Object)(object)((MonoBehaviourPun)val).photonView == (Object)null) && IsEquippedRef.Invoke(val))
					{
						hashSet.Add(((MonoBehaviourPun)val).photonView.ViewID);
					}
				}
			}
			catch (Exception e)
			{
				Suppressed.Report("LateJoinNow equipped-item protection", e);
			}
		}
	}
	[BepInPlugin("ricketywrecked.repo.fixesplus", "R.E.P.O Fixes+", "1.2.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "ricketywrecked.repo.fixesplus";

		internal static ManualLogSource Log;

		internal static bool ModLoaded(string guid)
		{
			return Chainloader.PluginInfos.ContainsKey(guid);
		}

		private void Awake()
		{
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ConfigEntry<bool> obj = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "EnemyTrackerFix", true, "Fixes the KeyNotFoundException spam and stutter when a player joins mid-match, or an enemy spawns after they joined. Restart the game after changing.");
			ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "GrabBeamAntispam", true, "Stops the NullReferenceException log spam from the grab beam and player eye-tracking when a grabbed object is destroyed. Restart the game after changing.");
			ConfigEntry<bool> val2 = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "InstantGrabBeam", true, "Makes your own grab beam turn on and off instantly in multiplayer instead of waiting on a network round-trip. Restart the game after changing.");
			ConfigEntry<bool> val3 = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "ReviveItemFix", true, "Stops the revive item getting stuck (and spamming errors every frame) when the dead player it locked onto leaves the game. Restart the game after changing.");
			ConfigEntry<bool> val4 = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "LateJoinNowEquippedItemFix", true, "Requires LateJoinNow. Stops LateJoinNow's level-change cleanup from destroying items players currently have equipped, which otherwise makes purchased gear vanish when loading into the next level. Does nothing if LateJoinNow isn't installed. Restart the game after changing.");
			ConfigEntry<bool> val5 = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "LevelTeardownErrorGuards", true, "Stops error spam from game code that keeps running for a moment after a level is torn down (currently: the tumble wings upgrade and the room lighting right after you leave a match), and from a player avatar that never finished setting up, which otherwise throws an error every frame (seen when someone in the lobby is on a different game version). Also lets the spectator camera clean up properly when you are revived, so the rest of the revive is not skipped. Restart the game after changing.");
			ConfigEntry<bool> val6 = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "EnemyAttackFix", true, "Stops a Slow Walker (Trudge) getting stuck mid-attack and throwing an error every frame when a player in its vacuum range leaves or is cleaned up. Restart the game after changing.");
			ConfigEntry<bool> val7 = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "OtherModGuards", true, "Skips wasted work in other mods, only if they're installed: CartSpeedSync pushing velocity onto carts this machine doesn't simulate (log spam that grows with level); GameTools re-sending its Prohibit Others room setting every 3 seconds when it hasn't changed, and GameTools letting any player remotely kill/revive/heal you (now only the host can, and a kill + revive at the same time, which loops, is ignored); CrouchAndCrawlSprint running while the level loads; Toggle Mute's MUTED indicator missing; and Hauler Unlimited Storage destroying non-valuables. Restart the game after changing.");
			ConfigEntry<int> val8 = ((BaseUnityPlugin)this).Config.Bind<int>("Fixes", "ConnectionTimeoutSeconds", 30, new ConfigDescription("How long a network hiccup can last before Photon disconnects you (the game's default is 10). Raising it keeps short Wi-Fi/internet blips from kicking you out of a lobby - including your own. Only affects your own connection. Set to 10 to keep the game's default. Restart the game after changing.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 60), Array.Empty<object>()));
			ConfigEntry<bool> val9 = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance", "NoStackTracesOnInfoAndWarnings", true, "Stops Unity capturing a full stack trace every time the game or a mod writes an ordinary log line or warning. That capture allocates memory on every call and is a common source of garbage-collection stutter in heavily modded games. Errors keep their stack traces, so crash logs stay just as useful. Restart the game after changing.");
			ConfigEntry<bool> val10 = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "ExtraSlotsKeepItems", true, "For mods that add inventory slots (e.g. ExtraSlots): the game only remembers slots 1-3 between levels, so items in slot 4+ end up loose in the truck. This remembers the extra slots too and puts those items back. Only matters on the host. Restart the game after changing.");
			ConfigEntry<bool> val11 = ((BaseUnityPlugin)this).Config.Bind<bool>("Fixes", "GameboyPauseWhenIdle", true, "Requires Empress Gameboy Emulator. Pauses the host's Game Boy when nobody is at the controls instead of emulating and streaming its screen to every player all session. Resumes exactly where it left off when someone presses E. Does nothing if that mod isn't installed. Restart the game after changing.");
			Harmony harmony = new Harmony("ricketywrecked.repo.fixesplus");
			int applied = 0;
			int failed = 0;
			if (obj.Value)
			{
				Apply(harmony, "Enemy tracker fix", ref applied, ref failed, typeof(PlayerAvatarAwakePatch), typeof(EnemyVisionAwakePatch), typeof(EnemyOnScreenAwakePatch), typeof(GetOnScreenPatch));
			}
			if (val.Value)
			{
				Apply(harmony, "Grab beam antispam", ref applied, ref failed, typeof(DrawCurveNullGuardPatch), typeof(LookAtTransformFinalizerPatch), typeof(ClosestPhysGrabPointFinalizerPatch), typeof(PhysicsGrabbingManipulationFinalizerPatch), typeof(OverrideDisableGrabLookAtFinalizerPatch));
			}
			if (val2.Value)
			{
				Apply(harmony, "Instant grab beam", ref applied, ref failed, typeof(PhysGrabBeamActivateInstantPatch), typeof(PhysGrabBeamDeactivateInstantPatch));
			}
			if (val3.Value)
			{
				Apply(harmony, "Revive item fix", ref applied, ref failed, typeof(ReviveItemUpdatePatch), typeof(ReviveItemFixedUpdatePatch));
			}
			if (val4.Value)
			{
				try
				{
					if (LateJoinNowEquipFix.TryApply(harmony))
					{
						applied++;
						Log.LogInfo((object)"[LateJoinNow equipped-item fix] LateJoinNow found; equipped gear will survive level changes.");
					}
					else
					{
						Log.LogInfo((object)"[LateJoinNow equipped-item fix] LateJoinNow not installed; nothing to do.");
					}
				}
				catch (Exception ex)
				{
					failed++;
					Log.LogError((object)("[LateJoinNow equipped-item fix] could not apply: " + ex.Message));
				}
			}
			if (val5.Value)
			{
				Apply(harmony, "Level teardown error guards", ref applied, ref failed, typeof(TumbleWingsFixedUpdateFinalizerPatch), typeof(PlayerHealthUpdateFinalizerPatch), typeof(StopSpectateFinalizerPatch), typeof(AmbientLightLogicFinalizerPatch));
			}
			if (val6.Value)
			{
				Apply(harmony, "Enemy attack fix", ref applied, ref failed, typeof(SlowWalkerSuckInFinalizerPatch));
			}
			if (val7.Value)
			{
				try
				{
					applied += ThirdPartyModGuards.TryApply(harmony);
				}
				catch (Exception ex2)
				{
					failed++;
					Log.LogError((object)("[Mod guards] could not apply: " + ex2.Message));
				}
			}
			if (val9.Value)
			{
				Application.SetStackTraceLogType((LogType)3, (StackTraceLogType)0);
				Application.SetStackTraceLogType((LogType)2, (StackTraceLogType)0);
			}
			if (val8.Value > 10)
			{
				ConnectionTimeoutPatch.TimeoutSeconds = val8.Value;
				Apply(harmony, "Connection timeout", ref applied, ref failed, typeof(ConnectionTimeoutPatch));
			}
			if (val10.Value)
			{
				Apply(harmony, "Extra slots keep items", ref applied, ref failed, typeof(ExtraSlotsRecordPatch), typeof(ExtraSlotsReequipPatch), typeof(ExtraSlotsSceneResetPatch), typeof(ExtraSlotsRunResetPatch));
			}
			if (val11.Value)
			{
				try
				{
					if (GameboyIdlePause.TryApply(harmony))
					{
						applied++;
						Log.LogInfo((object)"[Gameboy idle pause] Empress Gameboy found; it will pause when nobody is playing.");
					}
				}
				catch (Exception ex3)
				{
					failed++;
					Log.LogError((object)("[Gameboy idle pause] could not apply: " + ex3.Message));
				}
			}
			Log.LogInfo((object)$"R.E.P.O Fixes+ loaded: {applied} patches applied, {failed} failed.");
		}

		private void Apply(Harmony harmony, string group, ref int applied, ref int failed, params Type[] patchClasses)
		{
			foreach (Type type in patchClasses)
			{
				try
				{
					harmony.PatchAll(type);
					applied++;
				}
				catch (Exception ex)
				{
					failed++;
					Log.LogError((object)("[" + group + "] could not apply " + type.Name + ": " + ex.Message));
				}
			}
		}
	}
	internal static class Suppressed
	{
		private static readonly Dictionary<string, int> Counts = new Dictionary<string, int>();

		internal static void Report(string site, Exception e)
		{
			int value;
			lock (Counts)
			{
				Counts.TryGetValue(site, out value);
				value = (Counts[site] = value + 1);
			}
			if (value == 1 || value % 1000 == 0)
			{
				Plugin.Log.LogWarning((object)$"Suppressed exception in {site} ({value} so far): {e.GetType().Name}: {e.Message}");
			}
		}
	}
	internal static class ReviveItemTarget
	{
		private static readonly FieldRef<ItemReviveItem, bool> HasTarget = AccessTools.FieldRefAccess<ItemReviveItem, bool>("hasTarget");

		private static readonly FieldRef<ItemReviveItem, PlayerAvatar> Target = AccessTools.FieldRefAccess<ItemReviveItem, PlayerAvatar>("playerTarget");

		private static readonly FieldRef<ItemReviveItem, PhotonView> View = AccessTools.FieldRefAccess<ItemReviveItem, PhotonView>("photonView");

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

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

		internal static void DropIfGone(ItemReviveItem item)
		{
			if ((Object)(object)item == (Object)null || !HasTarget.Invoke(item) || StillThere(Target.Invoke(item)))
			{
				return;
			}
			Target.Invoke(item) = null;
			HasTarget.Invoke(item) = false;
			Plugin.Log.LogInfo((object)"Revive item's target left the game; cleared it so it can find another dead player.");
			if (SemiFunc.IsMultiplayer() && SemiFunc.IsMasterClientOrSingleplayer())
			{
				PhotonView val = View.Invoke(item);
				if ((Object)(object)val != (Object)null)
				{
					val.RPC("UpdatePlayerTargetRPC", (RpcTarget)0, new object[1] { -1 });
				}
			}
		}

		private static bool StillThere(PlayerAvatar player)
		{
			if (!Object.op_Implicit((Object)(object)player))
			{
				return false;
			}
			PlayerDeathHead val = DeathHead.Invoke(player);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return false;
			}
			PhysGrabObject val2 = HeadGrabObject.Invoke(val);
			if (Object.op_Implicit((Object)(object)val2))
			{
				return Object.op_Implicit((Object)(object)val2.rb);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(ItemReviveItem), "Update")]
	internal static class ReviveItemUpdatePatch
	{
		private static void Prefix(ItemReviveItem __instance)
		{
			try
			{
				ReviveItemTarget.DropIfGone(__instance);
			}
			catch (Exception e)
			{
				Suppressed.Report("revive item target check", e);
			}
		}
	}
	[HarmonyPatch(typeof(ItemReviveItem), "FixedUpdate")]
	internal static class ReviveItemFixedUpdatePatch
	{
		private static void Prefix(ItemReviveItem __instance)
		{
			try
			{
				ReviveItemTarget.DropIfGone(__instance);
			}
			catch (Exception e)
			{
				Suppressed.Report("revive item target check", e);
			}
		}
	}
	[HarmonyPatch(typeof(ItemUpgradePlayerTumbleWingsLogic), "FixedUpdate")]
	internal static class TumbleWingsFixedUpdateFinalizerPatch
	{
		private static Exception Finalizer(Exception __exception)
		{
			if (__exception != null)
			{
				Suppressed.Report("ItemUpgradePlayerTumbleWingsLogic.FixedUpdate", __exception);
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(PlayerHealth), "Update")]
	internal static class PlayerHealthUpdateFinalizerPatch
	{
		private static readonly FieldRef<PlayerAvatar, string> PlayerName = AccessTools.FieldRefAccess<PlayerAvatar, string>("playerName");

		private static bool Prefix(PlayerHealth __instance, ref PlayerAvatar ___playerAvatar)
		{
			if (Object.op_Implicit((Object)(object)___playerAvatar))
			{
				return true;
			}
			if (__instance.isMenuAvatar && Object.op_Implicit((Object)(object)PlayerAvatar.instance))
			{
				___playerAvatar = PlayerAvatar.instance;
				return true;
			}
			return false;
		}

		private static Exception Finalizer(Exception __exception, PlayerAvatar ___playerAvatar)
		{
			if (__exception != null)
			{
				string text = "unknown player";
				try
				{
					if ((Object)(object)___playerAvatar != (Object)null)
					{
						text = PlayerName.Invoke(___playerAvatar) ?? text;
					}
				}
				catch
				{
				}
				Suppressed.Report("PlayerHealth.Update (" + text + ")", __exception);
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(SpectateCamera), "StopSpectate")]
	internal static class StopSpectateFinalizerPatch
	{
		private static Exception Finalizer(Exception __exception, SpectateCamera __instance)
		{
			if (__exception != null)
			{
				try
				{
					if ((Object)(object)__instance != (Object)null)
					{
						Object.Destroy((Object)(object)((Component)__instance).gameObject);
					}
				}
				catch
				{
				}
				Suppressed.Report("SpectateCamera.StopSpectate", __exception);
			}
			return null;
		}
	}
	[HarmonyPatch(typeof(EnvironmentDirector), "AmbientLightLogic")]
	internal static class AmbientLightLogicFinalizerPatch
	{
		private static Exception Finalizer(Exception __exception)
		{
			if (__exception != null)
			{
				Suppressed.Report("EnvironmentDirector.AmbientLightLogic", __exception);
			}
			return null;
		}
	}
	internal static class ThirdPartyModGuards
	{
		internal static int TryApply(Harmony harmony)
		{
			int num = 0;
			if (CartSpeedSyncKinematicGuard.TryApply(harmony))
			{
				num++;
				Plugin.Log.LogInfo((object)"[Mod guards] CartSpeedSync found; skipping its cart steering on carts this machine doesn't simulate.");
			}
			if (GameToolsRoomSyncGuard.TryApply(harmony))
			{
				num++;
				Plugin.Log.LogInfo((object)"[Mod guards] GameTools found; ProhibitOthers is only re-sent to the room when it actually changes.");
			}
			if (CrouchCrawlLoadingGuard.TryApply(harmony))
			{
				num++;
				Plugin.Log.LogInfo((object)"[Mod guards] CrouchAndCrawlSprint found; its crawl is held while the level is loading.");
			}
			if (ToggleMuteHudRetry.TryApply(harmony))
			{
				num++;
				Plugin.Log.LogInfo((object)"[Mod guards] Toggle Mute found; its MUTED indicator is set up again once the HUD is ready.");
			}
			if (GameToolsRemoteControlGuard.TryApply(harmony))
			{
				num++;
				Plugin.Log.LogInfo((object)"[Mod guards] GameTools found; only the host can remotely kill, revive or heal you, and a kill + revive requested at the same time is ignored.");
			}
			if (HaulerValuablesOnlyGuard.TryApply(harmony))
			{
				num++;
				Plugin.Log.LogInfo((object)"[Mod guards] Hauler Unlimited Storage found; the hauler only takes valuables again (non-valuables were being destroyed for $0).");
			}
			return num;
		}
	}
	internal static class HaulerValuablesOnlyGuard
	{
		private static readonly FieldRef<ItemValuableBox, List<PhysGrabObject>> ValidObjects = AccessTools.FieldRefAccess<ItemValuableBox, List<PhysGrabObject>>("validObjectsInBox");

		internal static bool TryApply(Harmony harmony)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			MethodInfo methodInfo = (Plugin.ModLoaded("repo.hauler.unlimited") ? AccessTools.Method(typeof(ItemValuableBox), "OnObjectsFound", (Type[])null, (Type[])null) : null);
			if (methodInfo == null)
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(HaulerValuablesOnlyGuard), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static void Postfix(ItemValuableBox __instance)
		{
			try
			{
				ValidObjects.Invoke(__instance)?.RemoveAll((PhysGrabObject o) => (Object)(object)o == (Object)null || (Object)(object)((Component)o).GetComponent<ValuableObject>() == (Object)null);
			}
			catch (Exception e)
			{
				Suppressed.Report("Hauler valuables-only guard", e);
			}
		}
	}
	internal static class CrouchCrawlLoadingGuard
	{
		internal static bool TryApply(Harmony harmony)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			Type type = (Plugin.ModLoaded("TechedCrayon413.CrouchAndCrawlSprint") ? AccessTools.TypeByName("CrouchAndCrawlSprint.Patch_PlayerController") : null);
			MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "Postfix", (Type[])null, (Type[])null) : null);
			if (methodInfo == null)
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(CrouchCrawlLoadingGuard), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static bool Prefix(PlayerController __0)
		{
			try
			{
				return (Object)(object)__0 != (Object)null && (Object)(object)__0.playerAvatarScript != (Object)null && (Object)(object)PlayerCollisionStand.instance != (Object)null && (Object)(object)CameraCrouchPosition.instance != (Object)null && !SemiFunc.MenuLevel();
			}
			catch (Exception e)
			{
				Suppressed.Report("CrouchAndCrawlSprint guard", e);
				return false;
			}
		}
	}
	internal static class ToggleMuteHudRetry
	{
		private static MethodInfo _setup;

		private static bool _reported;

		internal static bool TryApply(Harmony harmony)
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			Type type = (Plugin.ModLoaded("soundedsquash.togglemute") ? AccessTools.TypeByName("MuteToggle.Patches.HUDCanvasPatches") : null);
			_setup = ((type != null) ? AccessTools.Method(type, "HUDCanvasAwakePostfix", (Type[])null, (Type[])null) : null);
			MethodInfo methodInfo = AccessTools.Method(typeof(EnergyUI), "Start", (Type[])null, (Type[])null);
			if (_setup == null || _setup.GetParameters().Length != 0 || methodInfo == null)
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(ToggleMuteHudRetry), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static void Postfix()
		{
			try
			{
				if (!((Object)(object)GameObject.Find("MutedText") != (Object)null))
				{
					bool flag = (Object)(object)GameObject.Find("EnergyMax") != (Object)null;
					if (flag)
					{
						_setup.Invoke(null, null);
					}
					if (!_reported)
					{
						_reported = true;
						Plugin.Log.LogInfo((object)(((Object)(object)GameObject.Find("MutedText") != (Object)null) ? "[Mod guards] Toggle Mute's MUTED indicator set up (its own attempt at HUD start runs too early; the error it logs just before this can be ignored)." : $"[Mod guards] Toggle Mute's MUTED indicator still couldn't be set up (EnergyMax text found: {flag})."));
					}
				}
			}
			catch (Exception e)
			{
				Suppressed.Report("Toggle Mute HUD retry", e);
			}
		}
	}
	internal static class CartSpeedSyncKinematicGuard
	{
		private static readonly FieldRef<PhysGrabCart, Rigidbody> CartRb = AccessTools.FieldRefAccess<PhysGrabCart, Rigidbody>("rb");

		internal static bool TryApply(Harmony harmony)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			Type type = (Plugin.ModLoaded("discjenny.CartSpeedSync") ? AccessTools.TypeByName("CartSpeedSync.PhysGrabCartPatches") : null);
			MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "CartSteerPostfix", (Type[])null, (Type[])null) : null);
			if (methodInfo == null)
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(CartSpeedSyncKinematicGuard), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static bool Prefix(PhysGrabCart __0)
		{
			try
			{
				if ((Object)(object)__0 == (Object)null || !SemiFunc.IsMasterClientOrSingleplayer())
				{
					return false;
				}
				Rigidbody val = CartRb.Invoke(__0);
				return (Object)(object)val != (Object)null && !val.isKinematic;
			}
			catch (Exception e)
			{
				Suppressed.Report("CartSpeedSync guard", e);
				return true;
			}
		}
	}
	internal static class GameToolsRoomSyncGuard
	{
		private const string Key = "GT_ProhibitOthers";

		private static Func<bool> _isEnabled;

		internal static bool TryApply(Harmony harmony)
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			Type type = (Plugin.ModLoaded("zichen.gametools") ? AccessTools.TypeByName("Zichen_GameTools.Plugin") : null);
			MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "SyncProhibitOthersToRoom", (Type[])null, (Type[])null) : null);
			MethodInfo methodInfo2 = ((type != null) ? AccessTools.Method(type, "IsProhibitOthersEnabled", (Type[])null, (Type[])null) : null);
			if (methodInfo == null || methodInfo2 == null || methodInfo2.ReturnType != typeof(bool))
			{
				return false;
			}
			_isEnabled = (Func<bool>)Delegate.CreateDelegate(typeof(Func<bool>), methodInfo2);
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(GameToolsRoomSyncGuard), "Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static bool Prefix()
		{
			try
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (currentRoom == null || ((RoomInfo)currentRoom).CustomProperties == null)
				{
					return true;
				}
				object value;
				return !((Dictionary<object, object>)(object)((RoomInfo)currentRoom).CustomProperties).TryGetValue((object)"GT_ProhibitOthers", out value) || !(value is bool flag) || flag != _isEnabled();
			}
			catch (Exception e)
			{
				Suppressed.Report("GameTools room sync guard", e);
				return true;
			}
		}
	}
	internal static class GameToolsRemoteControlGuard
	{
		private static readonly string[] Kinds = new string[3] { "GT_ReqKill_", "GT_ReqRevive_", "GT_ReqHeal_" };

		private static bool _loggedBlock;

		private static bool _loggedLoop;

		internal static bool TryApply(Harmony harmony)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			if (!Plugin.ModLoaded("zichen.gametools"))
			{
				return false;
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(Player), "InternalCacheProperties", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(GameToolsRemoteControlGuard), "Postfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static void Postfix(Player __instance)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (__instance == null || __instance.IsLocal || __instance.CustomProperties == null)
				{
					return;
				}
				Player localPlayer = PhotonNetwork.LocalPlayer;
				string text = ((localPlayer != null) ? localPlayer.ActorNumber.ToString() : null);
				string text2 = (SteamClient.IsValid ? ((object)SteamClient.SteamId/*cast due to .constrained prefix*/).ToString() : null);
				Hashtable customProperties = __instance.CustomProperties;
				List<object> list = new List<object>();
				bool flag = false;
				bool flag2 = false;
				foreach (object key in ((Dictionary<object, object>)(object)customProperties).Keys)
				{
					if (!(key is string text3))
					{
						continue;
					}
					string[] kinds = Kinds;
					foreach (string text4 in kinds)
					{
						if (text3.StartsWith(text4, StringComparison.Ordinal))
						{
							string text5 = text3.Substring(text4.Length);
							if ((text != null && text5 == text) || (text2 != null && text5 == text2))
							{
								list.Add(key);
								flag |= text4 == "GT_ReqKill_";
								flag2 |= text4 == "GT_ReqRevive_";
							}
						}
					}
				}
				if (list.Count == 0)
				{
					return;
				}
				if (!__instance.IsMasterClient)
				{
					foreach (object item in list)
					{
						((Dictionary<object, object>)(object)customProperties).Remove(item);
					}
					if (!_loggedBlock)
					{
						_loggedBlock = true;
						Plugin.Log.LogInfo((object)("[Mod guards] Ignored a GameTools kill/revive/heal request aimed at you from " + __instance.NickName + " (not the host)."));
					}
				}
				else
				{
					if (!(flag && flag2))
					{
						return;
					}
					foreach (object item2 in list)
					{
						if (item2 is string text6 && !text6.StartsWith("GT_ReqHeal_", StringComparison.Ordinal))
						{
							((Dictionary<object, object>)(object)customProperties).Remove(item2);
						}
					}
					if (!_loggedLoop)
					{
						_loggedLoop = true;
						Plugin.Log.LogInfo((object)("[Mod guards] Ignored a GameTools kill + revive requested at the same time by the host (" + __instance.NickName + "); that combination loops."));
					}
				}
			}
			catch (Exception e)
			{
				Suppressed.Report("GameTools remote control guard", e);
			}
		}
	}
}