Decompiled source of BadScoutWatcher v1.1.0

BadScoutWatcher.dll

Decompiled 15 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BadScoutWatcher;
using BepInEx;
using BepInEx.Configuration;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("1.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[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;
		}
	}
}
[HarmonyPatch(typeof(Player), "AddItem")]
public static class AntiCheatInventoryAddPatch
{
	[HarmonyPostfix]
	public static void Postfix(Player __instance, bool __result, ItemInstanceData __1, ref ItemSlot __2)
	{
		BadScoutWatcherPlugin.MonitorInventoryAdd(__instance, __result, __1, __2);
	}
}
[HarmonyPatch(typeof(Player), "SyncInventoryRPC")]
public static class AntiCheatInventorySyncPatch
{
	[HarmonyPrefix]
	public static void Prefix(Player __instance, ref HashSet<Guid> __state)
	{
		__state = BadScoutWatcherPlugin.CaptureInventoryItemGuids(__instance);
	}

	[HarmonyPostfix]
	public static void Postfix(Player __instance, HashSet<Guid> __state)
	{
		BadScoutWatcherPlugin.MonitorInventorySync(__instance, __state);
	}
}
[HarmonyPatch(typeof(Item), "SetItemInstanceDataRPC")]
public static class AntiCheatItemOriginPatch
{
	[HarmonyPostfix]
	public static void Postfix(Item __instance, ItemInstanceData __0)
	{
		BadScoutWatcherPlugin.MonitorItemInstanceData(__instance, __0);
	}
}
[HarmonyPatch]
public static class AntiCheatItemSpawnPatch
{
	private static MethodBase TargetMethod()
	{
		return AccessTools.Method(typeof(PhotonNetwork), "NetworkInstantiate", new Type[2]
		{
			typeof(Hashtable),
			typeof(Player)
		}, (Type[])null);
	}

	[HarmonyPostfix]
	public static void Postfix(Hashtable __0, Player __1, GameObject __result)
	{
		BadScoutWatcherPlugin.MonitorNetworkInstantiation(__1, __result);
	}
}
public struct AntiCheatDropSlotState
{
	public bool HadItem;

	public ItemInstanceData? ItemData;
}
[HarmonyPatch(typeof(CharacterItems), "DropItemRpc")]
public static class AntiCheatDropItemRpcPatch
{
	[HarmonyPrefix]
	public static void Prefix(CharacterItems __instance, ref bool __state)
	{
		__state = BadScoutWatcherPlugin.HasItemToDrop(__instance);
	}

	[HarmonyPostfix]
	public static void Postfix(CharacterItems __instance, bool __state, ItemInstanceData __5)
	{
		BadScoutWatcherPlugin.MonitorDropFromCharacterItems(__instance, __state, __5);
	}
}
[HarmonyPatch(typeof(CharacterItems), "DropItemFromSlotRPC")]
public static class AntiCheatDropItemFromSlotPatch
{
	[HarmonyPrefix]
	public static void Prefix(CharacterItems __instance, byte __0, ref AntiCheatDropSlotState __state)
	{
		__state.HadItem = BadScoutWatcherPlugin.HasItemInDropSlot(__instance, __0);
		__state.ItemData = BadScoutWatcherPlugin.GetItemDataInDropSlot(__instance, __0);
	}

	[HarmonyPostfix]
	public static void Postfix(CharacterItems __instance, AntiCheatDropSlotState __state)
	{
		BadScoutWatcherPlugin.MonitorDropFromCharacterItems(__instance, __state.HadItem, __state.ItemData);
	}
}
[HarmonyPatch]
public static class AntiCheatLandingInjuryPatch
{
	private static MethodBase TargetMethod()
	{
		return AccessTools.Method(typeof(CharacterAfflictions), "AddStatus", (Type[])null, (Type[])null);
	}

	[HarmonyPostfix]
	public static void Postfix(CharacterAfflictions __instance, object[] __args, bool __result)
	{
		try
		{
			if (!__result || (Object)(object)__instance == (Object)null || __args == null || __args.Length < 2 || Convert.ToInt32(__args[0]) != 0 || Convert.ToSingle(__args[1]) <= 0.001f)
			{
				return;
			}
			object obj = AccessTools.Field(typeof(CharacterAfflictions), "character")?.GetValue(__instance);
			Character val = (Character)((obj is Character) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Player val2 = (val.IsLocal ? PhotonNetwork.LocalPlayer : (((Object)(object)((MonoBehaviourPun)val).photonView != (Object)null) ? ((MonoBehaviourPun)val).photonView.Owner : null));
			if (val2 != null && val2.ActorNumber > 0)
			{
				int actorNumber = val2.ActorNumber;
				float time = Time.time;
				if (!BadScoutWatcherPlugin.Players.TryGetValue(actorNumber, out BadScoutWatcherPlugin.PlayerData value) || value == null)
				{
					value = new BadScoutWatcherPlugin.PlayerData
					{
						PlayerName = ((!string.IsNullOrEmpty(val2.NickName)) ? val2.NickName : $"Scout #{actorNumber}"),
						PlayerId = ((!string.IsNullOrEmpty(val2.UserId)) ? val2.UserId : "0"),
						ActorNumber = actorNumber,
						NetPlayer = val2,
						LastDecayTime = time,
						LastCheckTime = time
					};
					BadScoutWatcherPlugin.Players[actorNumber] = value;
				}
				value.InjuryEventCount++;
				if (value.AirborneStartedAt >= 0f)
				{
					value.AirborneInjuryObserved = true;
					value.FlingAirborneSuspected = false;
				}
				if (value.IsAirborneForLanding || value.LandingCheckPending)
				{
					value.LandingDamageObserved = true;
				}
			}
		}
		catch
		{
		}
	}
}
[HarmonyPatch]
public static class AntiCheatRpcShieldPatch
{
	private sealed class RpcRateWindow
	{
		public readonly Queue<float> AcceptedCalls = new Queue<float>();

		public float NextWarningAt;
	}

	private const int MaxRpcCallsPerSecond = 60;

	private const float RpcRateWindowSeconds = 1f;

	private const float BlockLogCooldownSeconds = 5f;

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

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

	private static MethodBase TargetMethod()
	{
		return AccessTools.Method(typeof(PhotonNetwork), "ExecuteRpc", new Type[2]
		{
			typeof(Hashtable),
			typeof(Player)
		}, (Type[])null);
	}

	[HarmonyPrefix]
	public static bool Prefix(Hashtable? __0, Player? __1, ref int __state)
	{
		__state = BadScoutWatcherPlugin.CurrentRpcSenderActorNumber;
		BadScoutWatcherPlugin.CurrentRpcSenderActorNumber = ((__1 != null) ? __1.ActorNumber : 0);
		if (BadScoutWatcherPlugin.ConfigShieldEnabled == null || !BadScoutWatcherPlugin.ConfigShieldEnabled.Value)
		{
			return true;
		}
		try
		{
			if (__1 == null)
			{
				return Reject(0, "unknown sender", "missing sender");
			}
			if (__1.IsLocal)
			{
				return true;
			}
			int actorNumber = __1.ActorNumber;
			string senderName = ((!string.IsNullOrEmpty(__1.NickName)) ? __1.NickName : $"actor {actorNumber}");
			if (actorNumber <= 0)
			{
				return Reject(actorNumber, senderName, "invalid actor number");
			}
			if (__0 == null)
			{
				return Reject(actorNumber, senderName, "missing payload");
			}
			if (!__0.ContainsKey((byte)0) || !(__0[(byte)0] is int num) || num <= 0)
			{
				return Reject(actorNumber, senderName, "invalid view id");
			}
			if (__0.ContainsKey((byte)1) && !(__0[(byte)1] is short))
			{
				return Reject(actorNumber, senderName, "invalid view prefix");
			}
			if (__0.ContainsKey((byte)5))
			{
				if (!(__0[(byte)5] is byte))
				{
					return Reject(actorNumber, senderName, "invalid RPC method index");
				}
			}
			else if (!__0.ContainsKey((byte)3) || !(__0[(byte)3] is string { Length: not 0 }))
			{
				return Reject(actorNumber, senderName, "missing RPC method name");
			}
			if (__0.ContainsKey((byte)4) && __0[(byte)4] != null && !(__0[(byte)4] is object[]))
			{
				return Reject(actorNumber, senderName, "invalid RPC argument list");
			}
			return AllowWithinRate(actorNumber, senderName);
		}
		catch (Exception ex)
		{
			Debug.LogWarning((object)("[BadScoutWatcher] RPC Shield inspection skipped after an error: " + ex.Message));
			return true;
		}
	}

	[HarmonyFinalizer]
	public static Exception? Finalizer(Exception? __exception, int __state)
	{
		BadScoutWatcherPlugin.CurrentRpcSenderActorNumber = __state;
		return __exception;
	}

	private static bool AllowWithinRate(int actorNumber, string senderName)
	{
		float unscaledTime = Time.unscaledTime;
		if (!RateWindows.TryGetValue(actorNumber, out RpcRateWindow value) || value == null)
		{
			value = new RpcRateWindow();
			RateWindows[actorNumber] = value;
		}
		while (value.AcceptedCalls.Count > 0 && unscaledTime - value.AcceptedCalls.Peek() >= 1f)
		{
			value.AcceptedCalls.Dequeue();
		}
		if (value.AcceptedCalls.Count >= 60)
		{
			if (unscaledTime >= value.NextWarningAt)
			{
				Debug.LogWarning((object)$"[BadScoutWatcher] RPC Shield observed a high RPC rate from {senderName} (actor {actorNumber}); valid game RPCs are still being allowed.");
				value.NextWarningAt = unscaledTime + 5f;
			}
			return true;
		}
		value.AcceptedCalls.Enqueue(unscaledTime);
		return true;
	}

	private static bool Reject(int actorNumber, string senderName, string reason)
	{
		float unscaledTime = Time.unscaledTime;
		if (!NextBlockLogAt.TryGetValue(actorNumber, out var value) || unscaledTime >= value)
		{
			Debug.LogWarning((object)$"[BadScoutWatcher] RPC Shield blocked malformed incoming RPC from {senderName} (actor {actorNumber}): {reason}.");
			NextBlockLogAt[actorNumber] = unscaledTime + 5f;
		}
		return false;
	}

	public static void ForgetActor(int actorNumber)
	{
		RateWindows.Remove(actorNumber);
		NextBlockLogAt.Remove(actorNumber);
	}
}
namespace BadScoutWatcher
{
	[BepInPlugin("com.custom.peak.badscoutwatcher", "BadScoutWatcher", "1.1.0")]
	public class BadScoutWatcherPlugin : BaseUnityPlugin, IInRoomCallbacks
	{
		private sealed class ItemOriginRecord
		{
			public int ActorNumber;

			public float LastSeenAt;
		}

		private sealed class AntiFlingCollisionPair
		{
			public Collider? Local;

			public Collider? Remote;
		}

		private sealed class AntiFlingCollisionState
		{
			public readonly List<AntiFlingCollisionPair> Pairs = new List<AntiFlingCollisionPair>();

			public readonly HashSet<long> PairKeys = new HashSet<long>();
		}

		public class Offense
		{
			public string Detector { get; set; } = string.Empty;

			public string Severity { get; set; } = string.Empty;

			public string Description { get; set; } = string.Empty;

			public string Timestamp { get; set; } = string.Empty;

			public int Points { get; set; }
		}

		public class PlayerData
		{
			public int ActorNumber = -1;

			public int ThreatScore = 0;

			public List<Offense> Offenses = new List<Offense>();

			public bool ShowEvidenceHistory;

			public bool IsTrusted = false;

			public bool IsMuted = false;

			public Vector3 LastPosition;

			public float LastCheckTime = 0f;

			public float AirborneTimer = 0f;

			public float StationaryUngroundedTimer = 0f;

			public float AirborneStartedAt = -1f;

			public bool AirborneFlagged = false;

			public bool AirborneInjuryObserved = false;

			public float AirborneStartY = float.NaN;

			public float AirbornePeakY = float.NegativeInfinity;

			public Vector3 LastGroundedPosition;

			public bool HasLastGroundedPosition = false;

			public Vector3 LastObservedPosition;

			public bool HasLastObservedPosition = false;

			public bool FlingAirborneSuspected = false;

			public bool AirborneSnapbackActive = false;

			public bool AirborneSnapbackApplied = false;

			public bool AirborneSnapbackWarned = false;

			public Vector3 AirborneSnapbackPosition;

			public float FlingingUntil = -1f;

			public int SuspiciousFrameBuffer = 0;

			public readonly Queue<float> RecentSpeeds = new Queue<float>();

			public float LastVelocityFlagTime = -999f;

			public Vector3 LastFramePosition;

			public bool HasLastFramePosition = false;

			public float LastAirborneFlagTime = -999f;

			public float LandingPeakY = float.NegativeInfinity;

			public float LandingGroundY = float.NaN;

			public bool HasLandingGroundSample = false;

			public float LandingStartedAt = -1f;

			public float LandingAirborneDuration = 0f;

			public float LandingCandidateHeight = 0f;

			public float LandingCandidatePeakY = float.NegativeInfinity;

			public float LandingCandidateLandingY = float.NaN;

			public float InjuryBeforeLanding = 0f;

			public float LandingMaxInjury = 0f;

			public bool LandingDamageObserved = false;

			public int InjuryEventCount = 0;

			public int InjuryEventsAtLandingStart = 0;

			public float LandingAt = -1f;

			public bool IsAirborneForLanding = false;

			public bool LandingCheckPending = false;

			public bool LandingExempt = false;

			public float LastLandingFlagTime = -999f;

			public int SpawnedItemsCount = 0;

			public int DroppedItemsCount = 0;

			public bool ItemActivityObserved = false;

			public bool InventoryActivityObserved = false;

			public bool InventoryBaselineInitialized = false;

			public bool LocalMovementMonitorLogged = false;

			public readonly Queue<float> ItemSpawnTimes = new Queue<float>();

			public readonly Queue<float> ItemDropTimes = new Queue<float>();

			public readonly Queue<float> InventoryAddTimes = new Queue<float>();

			public readonly Queue<float> UnattributedInventoryTimes = new Queue<float>();

			public readonly Dictionary<int, float> RecentItemInstanceIds = new Dictionary<int, float>();

			public readonly Dictionary<int, float> RecentItemDropIds = new Dictionary<int, float>();

			public int InventoryAddedItemsCount = 0;

			public float LastItemFloodFlagTime = -999f;

			public float LastItemDeleteLogTime = -999f;

			public float LastItemThresholdLogTime = -999f;

			public float LastInventoryThresholdLogTime = -999f;

			public float LastTrustedSuppressionLogTime = -999f;

			public float LastMuteRefreshTime = -999f;

			public float LastDecayTime = 0f;

			public float LastSyncSampleTime = -1f;

			public int LastSpeedSampleFrame = -1;

			public Component? PlayerComponent;

			public bool IsFlinging = false;

			public string PlayerName { get; set; } = string.Empty;

			public string PlayerId { get; set; } = string.Empty;

			public Player? NetPlayer { get; set; }
		}

		public const string PluginGUID = "com.custom.peak.badscoutwatcher";

		public const string PluginName = "BadScoutWatcher";

		public const string PluginVersion = "1.1.0";

		public const float AirborneFlagThresholdSeconds = 10f;

		public const float AntiFlingAirborneRecoverySeconds = 5f;

		public const float SafeLandingMinFallHeightMeters = 22f;

		public const int ItemFloodTriggerCount = 6;

		public const float ItemFloodRapidWindowSeconds = 0.5f;

		private const float ItemFloodFlagCooldownSeconds = 6f;

		private const int BackpackDataEntryKey = 7;

		public static int CurrentRpcSenderActorNumber;

		public static BadScoutWatcherPlugin? Instance;

		public static ConfigEntry<KeyCode>? ConfigToggleKey;

		public static ConfigEntry<Color>? ConfigBgColor;

		public static ConfigEntry<Color>? ConfigBubbleColor;

		public static ConfigEntry<Color>? ConfigHeaderColor;

		public static ConfigEntry<int>? ConfigCornerRadius;

		public static ConfigEntry<bool>? ConfigAutoKick;

		public static ConfigEntry<bool>? ConfigItemDeletion;

		public static ConfigEntry<bool>? ConfigShieldEnabled;

		public static ConfigEntry<bool>? ConfigAntiFlingEnabled;

		public static ConfigEntry<bool>? ConfigOpenOnStartup;

		public static bool ShowUI = true;

		private int currentPage = 0;

		private Rect windowRect = new Rect(40f, 40f, 500f, 360f);

		private Vector2 scrollPos;

		private bool isListeningForKey = false;

		private bool pickerTargetsBackground = true;

		private bool pickerStateInitialized = false;

		private float pickerHue;

		private float pickerSaturation;

		private float pickerBrightness;

		private string pickerHex = "";

		private Texture2D? hueWheelTexture;

		private Texture2D? saturationBrightnessTexture;

		private float saturationBrightnessTextureHue = -1f;

		private static GUIStyle? windowStyle;

		private static GUIStyle? bubbleStyle;

		private static GUIStyle? flatButtonStyle;

		private static GUIStyle? tabButtonStyle;

		private static GUIStyle? warningStyle;

		private static Texture2D? windowBgTexture;

		private static Texture2D? bubbleBgTexture;

		private static Texture2D? buttonNormalTexture;

		private static Texture2D? buttonHoverTexture;

		private static Texture2D? buttonActiveTexture;

		private static Texture2D? tabNormalTexture;

		private static Texture2D? tabHoverTexture;

		private float pollTimer = 0f;

		private static string latestWarning = string.Empty;

		private static float latestWarningUntil = 0f;

		private const float POLL_INTERVAL = 0.2f;

		private static readonly float[] ThreatBarThresholds = new float[4] { 0.2f, 0.45f, 0.7f, 0.95f };

		public static Dictionary<int, PlayerData> Players = new Dictionary<int, PlayerData>();

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

		private static readonly Dictionary<Guid, ItemOriginRecord> ItemOrigins = new Dictionary<Guid, ItemOriginRecord>();

		private static readonly Dictionary<Guid, float> RecentInventoryGuids = new Dictionary<Guid, float>();

		private static readonly Dictionary<Guid, float> BackpackContentIgnoreUntil = new Dictionary<Guid, float>();

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

		private void Awake()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Expected O, but got Unknown
			Instance = this;
			ConfigToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ToggleKey", (KeyCode)283, "Keybind to toggle the UI overlay.");
			ConfigOpenOnStartup = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "OpenOnStartup", true, "Open the UI automatically when the game starts.");
			ConfigBgColor = ((BaseUnityPlugin)this).Config.Bind<Color>("UI Theme", "BackgroundColor", new Color(0.1f, 0.1f, 0.12f, 0.95f), "Window background color.");
			ConfigBubbleColor = ((BaseUnityPlugin)this).Config.Bind<Color>("UI Theme", "BubbleColor", new Color(0.18f, 0.18f, 0.22f, 0.9f), "Item bubble/box background color.");
			ConfigHeaderColor = ((BaseUnityPlugin)this).Config.Bind<Color>("UI Theme", "HeaderColor", new Color(0.25f, 0.25f, 0.3f, 1f), "Top header color.");
			ConfigCornerRadius = ((BaseUnityPlugin)this).Config.Bind<int>("UI Theme", "CornerRadius", 8, "Corner roundness radius (px).");
			ConfigAutoKick = ((BaseUnityPlugin)this).Config.Bind<bool>("Host Rules", "AutoKick", false, "Auto-kick confirmed cheaters when hosting.");
			ConfigItemDeletion = ((BaseUnityPlugin)this).Config.Bind<bool>("Host Rules", "DeleteItems", true, "Delete explosives and item floods when hosting.");
			ConfigShieldEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Protection", "ShieldEnabled", true, "Reject malformed incoming RPCs and warn about unusually high RPC rates without dropping valid game RPCs.");
			ConfigAntiFlingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Protection", "AntiFlingEnabled", true, "Ignore fling collisions and recover after 5 seconds of suspected fling airtime.");
			if (ConfigOpenOnStartup != null)
			{
				ShowUI = ConfigOpenOnStartup.Value;
			}
			UpdateCursorState();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"BadScoutWatcher v1.1.0 loaded!");
			try
			{
				Harmony val = new Harmony("com.custom.peak.badscoutwatcher");
				val.PatchAll();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Harmony patches registered.");
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Harmony patching error: {arg}");
			}
		}

		private void OnEnable()
		{
			PhotonNetwork.AddCallbackTarget((object)this);
		}

		private void OnDisable()
		{
			PhotonNetwork.RemoveCallbackTarget((object)this);
			RestoreAllAntiFlingCollisions();
		}

		public void OnPlayerEnteredRoom(Player newPlayer)
		{
			if (newPlayer != null)
			{
				float time = Time.time;
				int actorNumber = newPlayer.ActorNumber;
				string playerName = ((!string.IsNullOrEmpty(newPlayer.NickName)) ? newPlayer.NickName : $"Scout #{actorNumber}");
				if (!Players.TryGetValue(actorNumber, out PlayerData value) || value == null)
				{
					Players[actorNumber] = new PlayerData
					{
						PlayerName = playerName,
						PlayerId = (string.IsNullOrEmpty(newPlayer.UserId) ? "0" : newPlayer.UserId),
						ActorNumber = actorNumber,
						NetPlayer = newPlayer,
						LastDecayTime = time,
						LastCheckTime = time
					};
				}
			}
		}

		public void OnPlayerLeftRoom(Player otherPlayer)
		{
			if (otherPlayer != null && Players.ContainsKey(otherPlayer.ActorNumber))
			{
				Players.Remove(otherPlayer.ActorNumber);
			}
			if (otherPlayer != null)
			{
				AntiCheatRpcShieldPatch.ForgetActor(otherPlayer.ActorNumber);
			}
		}

		public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged)
		{
		}

		public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps)
		{
		}

		public void OnMasterClientSwitched(Player newMasterClient)
		{
		}

		private void Update()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			UpdateLocalAntiFlingRecovery();
			UpdateAntiFlingCollisionGuard();
			ResetExpiredItemFloodWindows();
			RefreshMutedPlayers();
			if (isListeningForKey)
			{
				return;
			}
			KeyCode val = (KeyCode)((ConfigToggleKey == null) ? 283 : ((int)ConfigToggleKey.Value));
			if (Input.GetKeyDown(val))
			{
				ShowUI = !ShowUI;
				UpdateCursorState();
			}
			pollTimer += Time.deltaTime;
			if (pollTimer >= 0.2f)
			{
				pollTimer = 0f;
				PollPhotonLobbyPlayers();
				CleanupDisconnectedPlayers();
			}
			float time = Time.time;
			foreach (KeyValuePair<int, PlayerData> player in Players)
			{
				PlayerData value = player.Value;
				if (value != null && value.ThreatScore > 0)
				{
					if (value.LastDecayTime <= 0f)
					{
						value.LastDecayTime = time;
					}
					else if (time - value.LastDecayTime >= 60f)
					{
						int num = Mathf.FloorToInt((time - value.LastDecayTime) / 60f);
						value.ThreatScore = Mathf.Max(0, value.ThreatScore - num * 10);
						value.LastDecayTime += (float)num * 60f;
					}
				}
			}
		}

		private void UpdateLocalAntiFlingRecovery()
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0581: Unknown result type (might be due to invalid IL or missing references)
			//IL_0583: Unknown result type (might be due to invalid IL or missing references)
			//IL_0433: Unknown result type (might be due to invalid IL or missing references)
			//IL_040c: Unknown result type (might be due to invalid IL or missing references)
			//IL_041a: Unknown result type (might be due to invalid IL or missing references)
			bool flag = ConfigAntiFlingEnabled != null && ConfigAntiFlingEnabled.Value;
			Character localCharacter = Character.localCharacter;
			if ((Object)(object)localCharacter == (Object)null)
			{
				return;
			}
			CharacterMovement component = ((Component)localCharacter).GetComponent<CharacterMovement>();
			if ((Object)(object)component == (Object)null || !TryGetPeakGrounded((Component)(object)component, out var grounded))
			{
				return;
			}
			Player localPlayer = PhotonNetwork.LocalPlayer;
			int num = ((localPlayer != null && localPlayer.ActorNumber > 0) ? localPlayer.ActorNumber : (-1));
			float time = Time.time;
			Vector3 position = ((Component)localCharacter).transform.position;
			string text = ((localPlayer != null && !string.IsNullOrEmpty(localPlayer.NickName)) ? localPlayer.NickName : "Local player");
			string playerId = ((localPlayer != null && !string.IsNullOrEmpty(localPlayer.UserId)) ? localPlayer.UserId : "0");
			if (!Players.TryGetValue(num, out PlayerData value) || value == null)
			{
				value = new PlayerData
				{
					PlayerName = text,
					PlayerId = playerId,
					ActorNumber = num,
					NetPlayer = localPlayer,
					LastDecayTime = time,
					LastCheckTime = time
				};
				Players[num] = value;
			}
			value.PlayerName = text;
			value.PlayerId = playerId;
			value.NetPlayer = localPlayer;
			if ((Object)(object)value.PlayerComponent != (Object)(object)component)
			{
				value.PlayerComponent = (Component?)(object)component;
				value.LastFramePosition = position;
				value.LastPosition = position;
				value.LastObservedPosition = position;
				value.HasLastFramePosition = true;
				value.HasLastObservedPosition = true;
				value.LastSyncSampleTime = time;
				value.LastGroundedPosition = Vector3.zero;
				value.HasLastGroundedPosition = false;
				value.AirborneStartedAt = -1f;
				value.AirborneTimer = 0f;
				value.AirborneFlagged = false;
				value.AirborneStartY = float.NaN;
				value.AirbornePeakY = float.NegativeInfinity;
				value.StationaryUngroundedTimer = 0f;
				value.AirborneInjuryObserved = false;
				value.FlingAirborneSuspected = false;
				value.AirborneSnapbackActive = false;
				value.AirborneSnapbackApplied = false;
				value.AirborneSnapbackWarned = false;
				value.RecentSpeeds.Clear();
			}
			float injury = 0f;
			bool flag2 = TryGetPeakInjury((Component)(object)component, out injury);
			bool flag3 = IsFlightExempt((Component)(object)component);
			if (!value.LocalMovementMonitorLogged)
			{
				value.LocalMovementMonitorLogged = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Local movement checks active for " + text + ": grounded data available; injury status " + (flag2 ? "available" : "unavailable") + "; Anti-Fling " + (flag ? "enabled" : "disabled") + "."));
			}
			value.LastObservedPosition = position;
			value.HasLastObservedPosition = true;
			if (grounded)
			{
				value.LastGroundedPosition = localCharacter.Center;
				value.HasLastGroundedPosition = true;
				value.AirborneStartedAt = -1f;
				value.AirborneTimer = 0f;
				value.AirborneFlagged = false;
				value.AirborneStartY = float.NaN;
				value.AirbornePeakY = float.NegativeInfinity;
				value.AirborneInjuryObserved = false;
				value.FlingAirborneSuspected = false;
				value.AirborneSnapbackActive = false;
				value.AirborneSnapbackApplied = false;
				value.AirborneSnapbackWarned = false;
			}
			else if (flag3)
			{
				value.AirborneStartedAt = -1f;
				value.AirborneTimer = 0f;
				value.AirborneFlagged = false;
				value.AirborneStartY = float.NaN;
				value.AirbornePeakY = float.NegativeInfinity;
				value.AirborneInjuryObserved = false;
				value.FlingAirborneSuspected = false;
				value.AirborneSnapbackActive = false;
				value.AirborneSnapbackApplied = false;
				value.AirborneSnapbackWarned = false;
			}
			else
			{
				if (value.AirborneStartedAt < 0f)
				{
					value.AirborneStartedAt = time;
					value.AirborneInjuryObserved = false;
					value.InjuryBeforeLanding = (flag2 ? injury : 0f);
					value.AirborneStartY = position.y;
					value.AirbornePeakY = position.y;
				}
				else
				{
					value.AirbornePeakY = Mathf.Max(value.AirbornePeakY, position.y);
					if (flag2 && injury > value.InjuryBeforeLanding + 0.001f)
					{
						value.AirborneInjuryObserved = true;
						value.FlingAirborneSuspected = false;
					}
				}
				value.AirborneTimer = Mathf.Max(0f, time - value.AirborneStartedAt);
				if (PhotonNetwork.InRoom && num > 0 && !value.AirborneFlagged && value.AirborneTimer >= 10f && !value.AirborneInjuryObserved)
				{
					value.AirborneFlagged = true;
					float num2 = (float.IsNaN(value.AirborneStartY) ? 0f : Mathf.Max(0f, value.AirbornePeakY - value.AirborneStartY));
					FlagPlayer(num, value.PlayerName, value.PlayerId, "Airborne", "High risk", $"Non-grounded for {value.AirborneTimer:F1}s without landing; highest point Y {value.AirbornePeakY:F1}m (+{num2:F1}m from start)", 10);
				}
				if (flag && value.HasLastGroundedPosition && value.AirborneTimer >= 5f)
				{
					ActivateAntiFlingSnapback(value);
				}
			}
			value.LastFramePosition = position;
			value.LastSyncSampleTime = time;
		}

		public static void ActivateAntiFlingSnapback(PlayerData player)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			if (player == null || !player.HasLastGroundedPosition || ConfigAntiFlingEnabled == null || !ConfigAntiFlingEnabled.Value)
			{
				return;
			}
			Character val = ResolvePlayerCharacter(player.PlayerComponent);
			if (!((Object)(object)val != (Object)null) || val.IsLocal || PhotonNetwork.IsMasterClient)
			{
				if (!player.AirborneSnapbackActive)
				{
					player.AirborneSnapbackPosition = player.LastGroundedPosition;
					player.AirborneSnapbackApplied = false;
				}
				player.AirborneSnapbackActive = true;
				if (!player.AirborneSnapbackWarned)
				{
					player.AirborneSnapbackWarned = true;
					string arg = (string.IsNullOrEmpty(player.PlayerName) ? "Player" : player.PlayerName);
					ShowNoticeBanner($"Anti-Fling: {arg} returned to last ground point after {player.AirborneTimer:F0}s airborne");
				}
			}
		}

		private static Character? ResolvePlayerCharacter(Component? component)
		{
			if ((Object)(object)component == (Object)null)
			{
				return null;
			}
			Character val = (Character)(object)((component is Character) ? component : null);
			if (val != null)
			{
				return val;
			}
			object obj = AccessTools.Field(((object)component).GetType(), "character")?.GetValue(component);
			Character val2 = (Character)((obj is Character) ? obj : null);
			if (val2 != null)
			{
				return val2;
			}
			return component.GetComponent<Character>() ?? component.GetComponentInParent<Character>();
		}

		private static long GetAntiFlingPairKey(Collider local, Collider remote)
		{
			return ((long)((Object)local).GetInstanceID() << 32) | (uint)((Object)remote).GetInstanceID();
		}

		private static void UpdateAntiFlingCollisionGuard()
		{
			bool flag = ConfigAntiFlingEnabled != null && ConfigAntiFlingEnabled.Value;
			Character val = (flag ? Character.localCharacter : null);
			UpdateAirborneSnapbacks(flag && (Object)(object)val != (Object)null);
			HashSet<int> hashSet = new HashSet<int>();
			if ((Object)(object)val != (Object)null)
			{
				Collider[] componentsInChildren = ((Component)val).GetComponentsInChildren<Collider>(true);
				foreach (KeyValuePair<int, PlayerData> player in Players)
				{
					PlayerData value = player.Value;
					if (value == null)
					{
						continue;
					}
					if (value.IsFlinging && Time.time >= value.FlingingUntil)
					{
						value.IsFlinging = false;
					}
					if (!value.IsFlinging)
					{
						continue;
					}
					Character val2 = ResolvePlayerCharacter(value.PlayerComponent);
					if ((Object)(object)val2 == (Object)null || (Object)(object)val2 == (Object)(object)val || val2.IsLocal)
					{
						continue;
					}
					Collider[] componentsInChildren2 = ((Component)val2).GetComponentsInChildren<Collider>(true);
					if (componentsInChildren2.Length == 0)
					{
						continue;
					}
					hashSet.Add(player.Key);
					if (!AntiFlingCollisionStates.TryGetValue(player.Key, out AntiFlingCollisionState value2) || value2 == null)
					{
						value2 = new AntiFlingCollisionState();
						AntiFlingCollisionStates[player.Key] = value2;
					}
					Collider[] array = componentsInChildren;
					foreach (Collider val3 in array)
					{
						if ((Object)(object)val3 == (Object)null || !val3.enabled || !((Component)val3).gameObject.activeInHierarchy || val3.isTrigger)
						{
							continue;
						}
						Collider[] array2 = componentsInChildren2;
						foreach (Collider val4 in array2)
						{
							if ((Object)(object)val4 == (Object)null || !val4.enabled || !((Component)val4).gameObject.activeInHierarchy || val4.isTrigger)
							{
								continue;
							}
							long antiFlingPairKey = GetAntiFlingPairKey(val3, val4);
							if (!value2.PairKeys.Add(antiFlingPairKey))
							{
								continue;
							}
							try
							{
								if (!Physics.GetIgnoreCollision(val3, val4))
								{
									Physics.IgnoreCollision(val3, val4, true);
									value2.Pairs.Add(new AntiFlingCollisionPair
									{
										Local = val3,
										Remote = val4
									});
								}
							}
							catch
							{
							}
						}
					}
				}
			}
			List<int> list = new List<int>();
			foreach (int key in AntiFlingCollisionStates.Keys)
			{
				if (!hashSet.Contains(key))
				{
					list.Add(key);
				}
			}
			foreach (int item in list)
			{
				RestoreAntiFlingCollisions(item);
			}
		}

		private static void UpdateAirborneSnapbacks(bool enabled)
		{
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			bool grounded = default(bool);
			foreach (PlayerData value in Players.Values)
			{
				if (value == null || !value.AirborneSnapbackActive)
				{
					continue;
				}
				Character val = ResolvePlayerCharacter(value.PlayerComponent);
				if ((Object)(object)val == (Object)null)
				{
					value.AirborneSnapbackActive = false;
					continue;
				}
				try
				{
					if (value.AirborneSnapbackApplied && !val.warping && (Object)(object)value.PlayerComponent != (Object)null && TryGetPeakGrounded(value.PlayerComponent, out grounded) && grounded)
					{
						value.AirborneSnapbackActive = false;
						value.AirborneSnapbackApplied = false;
						value.AirborneSnapbackWarned = false;
						value.AirborneStartedAt = -1f;
						value.AirborneTimer = 0f;
					}
					else if (enabled)
					{
						if (!val.IsLocal && !PhotonNetwork.IsMasterClient)
						{
							value.AirborneSnapbackActive = false;
							value.AirborneSnapbackApplied = false;
							value.AirborneSnapbackWarned = false;
						}
						else
						{
							if (value.AirborneSnapbackApplied)
							{
								continue;
							}
							if (val.IsLocal)
							{
								val.WarpPlayerRPC(value.AirborneSnapbackPosition, false);
							}
							else
							{
								PhotonView photonView = ((MonoBehaviourPun)val).photonView;
								if ((Object)(object)photonView != (Object)null)
								{
									photonView.RPC("WarpPlayerRPC", (RpcTarget)0, new object[2] { value.AirborneSnapbackPosition, false });
								}
								else
								{
									val.WarpPlayerRPC(value.AirborneSnapbackPosition, false);
								}
							}
							value.AirborneSnapbackApplied = true;
							continue;
						}
					}
					else
					{
						value.AirborneSnapbackActive = false;
						value.AirborneSnapbackApplied = false;
						value.AirborneSnapbackWarned = false;
					}
				}
				catch
				{
					value.AirborneSnapbackActive = false;
				}
			}
		}

		private static void RestoreAntiFlingCollisions(int actor)
		{
			if (!AntiFlingCollisionStates.TryGetValue(actor, out AntiFlingCollisionState value) || value == null)
			{
				return;
			}
			foreach (AntiFlingCollisionPair pair in value.Pairs)
			{
				try
				{
					if ((Object)(object)pair.Local != (Object)null && (Object)(object)pair.Remote != (Object)null)
					{
						Physics.IgnoreCollision(pair.Local, pair.Remote, false);
					}
				}
				catch
				{
				}
			}
			AntiFlingCollisionStates.Remove(actor);
		}

		private static void RestoreAllAntiFlingCollisions()
		{
			List<int> list = new List<int>(AntiFlingCollisionStates.Keys);
			foreach (int item in list)
			{
				RestoreAntiFlingCollisions(item);
			}
		}

		private void OnDestroy()
		{
			RestoreAllAntiFlingCollisions();
			if ((Object)(object)hueWheelTexture != (Object)null)
			{
				Object.Destroy((Object)(object)hueWheelTexture);
			}
			if ((Object)(object)saturationBrightnessTexture != (Object)null)
			{
				Object.Destroy((Object)(object)saturationBrightnessTexture);
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private static void PollPhotonLobbyPlayers()
		{
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_066c: Unknown result type (might be due to invalid IL or missing references)
			//IL_066e: Unknown result type (might be due to invalid IL or missing references)
			if (!PhotonNetwork.IsConnected)
			{
				return;
			}
			Player[] playerList = PhotonNetwork.PlayerList;
			if (playerList == null)
			{
				return;
			}
			float time = Time.time;
			Player[] array = playerList;
			foreach (Player val in array)
			{
				if (val == null || (val.IsLocal && val.ActorNumber <= 0))
				{
					continue;
				}
				int actorNumber = val.ActorNumber;
				string nickName = val.NickName;
				string userId = val.UserId;
				string playerName = ((!string.IsNullOrEmpty(nickName)) ? nickName : ((!string.IsNullOrEmpty(userId)) ? userId : $"Scout #{actorNumber}"));
				string playerId = ((!string.IsNullOrEmpty(userId)) ? userId : "0");
				if (!Players.TryGetValue(actorNumber, out PlayerData value) || value == null)
				{
					value = new PlayerData
					{
						PlayerName = playerName,
						PlayerId = playerId,
						ActorNumber = actorNumber,
						NetPlayer = val,
						LastDecayTime = time,
						LastCheckTime = time
					};
					Players[actorNumber] = value;
				}
				else
				{
					if (!string.IsNullOrEmpty(nickName))
					{
						value.PlayerName = nickName;
					}
					value.PlayerId = playerId;
					value.NetPlayer = val;
				}
				if (!((Object)(object)value.PlayerComponent != (Object)null))
				{
					continue;
				}
				Transform transform = value.PlayerComponent.transform;
				Vector3 position = transform.position;
				float num = time - value.LastCheckTime;
				if (!(num >= 0.5f))
				{
					continue;
				}
				float num2 = Vector3.Distance(position, value.LastPosition);
				if (num2 > 50f && num < 2f && !IsRecentlyWarped(value.PlayerComponent))
				{
					FlagPlayer(actorNumber, value.PlayerName, value.PlayerId, "Teleport", "Low risk", $"Warped distance: {num2:F1}m", 5);
				}
				bool grounded;
				bool flag = !TryGetPeakGrounded(value.PlayerComponent, out grounded) || grounded;
				float injury;
				bool flag2 = TryGetPeakInjury(value.PlayerComponent, out injury);
				float y = position.y;
				if (!flag)
				{
					if (!value.IsAirborneForLanding)
					{
						value.IsAirborneForLanding = true;
						value.LandingPeakY = y;
						value.LandingGroundY = float.NaN;
						value.HasLandingGroundSample = false;
						value.LandingStartedAt = time;
						value.LandingAirborneDuration = 0f;
						value.LandingCandidateHeight = 0f;
						value.InjuryBeforeLanding = (flag2 ? injury : 0f);
						value.LandingMaxInjury = value.InjuryBeforeLanding;
						value.LandingDamageObserved = false;
						value.InjuryEventsAtLandingStart = value.InjuryEventCount;
						value.LandingExempt = IsLandingExempt(value.PlayerComponent);
						value.LandingCheckPending = false;
					}
					else if (y > value.LandingPeakY)
					{
						value.LandingPeakY = y;
					}
					if (value.LandingStartedAt >= 0f)
					{
						value.LandingAirborneDuration = time - value.LandingStartedAt;
					}
					if (flag2)
					{
						value.LandingMaxInjury = Mathf.Max(value.LandingMaxInjury, injury);
						if (injury > value.InjuryBeforeLanding + 0.001f)
						{
							value.LandingDamageObserved = true;
							value.AirborneInjuryObserved = true;
						}
					}
					if (value.InjuryEventCount > value.InjuryEventsAtLandingStart)
					{
						value.LandingDamageObserved = true;
					}
				}
				else if (value.IsAirborneForLanding)
				{
					float num3 = ((value.HasLandingGroundSample && !float.IsNaN(value.LandingGroundY)) ? value.LandingGroundY : y);
					float num4 = (float.IsNegativeInfinity(value.LandingPeakY) ? y : value.LandingPeakY);
					float num5 = Mathf.Max(0f, num4 - num3);
					float landingAirborneDuration = ((value.LandingStartedAt >= 0f) ? (time - value.LandingStartedAt) : value.LandingAirborneDuration);
					value.IsAirborneForLanding = false;
					value.LandingStartedAt = -1f;
					value.HasLandingGroundSample = false;
					if (flag2 && injury > value.InjuryBeforeLanding + 0.001f)
					{
						value.LandingDamageObserved = true;
						value.AirborneInjuryObserved = true;
					}
					if (value.InjuryEventCount > value.InjuryEventsAtLandingStart)
					{
						value.LandingDamageObserved = true;
					}
					if (num5 >= 22f && !value.LandingExempt && flag2)
					{
						value.LandingAt = time;
						value.LandingCheckPending = true;
						value.LandingCandidateHeight = num5;
						value.LandingCandidatePeakY = num4;
						value.LandingCandidateLandingY = num3;
						value.LandingAirborneDuration = landingAirborneDuration;
						value.LandingMaxInjury = Mathf.Max(value.LandingMaxInjury, injury);
						BadScoutWatcherPlugin? instance = Instance;
						if (instance != null)
						{
							((BaseUnityPlugin)instance).Logger.LogInfo((object)$"Landing check started for actor {actorNumber}: fall {num5:F1}m; waiting 1.5s to confirm no injury.");
						}
					}
				}
				if (value.LandingCheckPending && flag2)
				{
					value.LandingMaxInjury = Mathf.Max(value.LandingMaxInjury, injury);
					if (injury > value.InjuryBeforeLanding + 0.001f || value.InjuryEventCount > value.InjuryEventsAtLandingStart)
					{
						value.LandingDamageObserved = true;
						value.AirborneInjuryObserved = true;
					}
					if (time - value.LandingAt >= 1.5f)
					{
						if (!value.LandingDamageObserved && value.LandingMaxInjury <= value.InjuryBeforeLanding + 0.001f && time - value.LastLandingFlagTime >= 5f)
						{
							value.LastLandingFlagTime = time;
							FlagPlayer(actorNumber, value.PlayerName, value.PlayerId, "Landing / Height Damageable Fall", "Low risk", $"Landed after {value.LandingAirborneDuration:F1}s; highest point Y {value.LandingCandidatePeakY:F1}m, {value.LandingCandidateHeight:F1}m above landing, without fall injury", 5);
						}
						value.LandingCheckPending = false;
					}
				}
				value.LastPosition = position;
				value.LastCheckTime = time;
			}
		}

		private static bool TryGetCharacterData(Component component, out object? character, out object? data)
		{
			character = null;
			data = null;
			try
			{
				character = ((object)component).GetType().GetField("character", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(component);
				if (character == null)
				{
					return false;
				}
				data = character.GetType().GetField("data", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(character);
				return data != null;
			}
			catch
			{
				character = null;
				data = null;
				return false;
			}
		}

		public static bool TryGetPeakGrounded(Component component, out bool grounded)
		{
			grounded = true;
			try
			{
				if (!TryGetCharacterData(component, out object _, out object data) || data == null)
				{
					return false;
				}
				if (data.GetType().GetField("isGrounded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(data) is bool flag)
				{
					grounded = flag;
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		public static bool IsFlightExempt(Component component)
		{
			try
			{
				if (!TryGetCharacterData(component, out object character, out object data) || character == null || data == null)
				{
					return true;
				}
				string[] array = new string[5] { "isClimbing", "isRopeClimbing", "isVineClimbing", "isCarried", "hasParachute" };
				string[] array2 = array;
				bool flag = default(bool);
				foreach (string name in array2)
				{
					object obj = data.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(data);
					int num;
					if (obj is bool)
					{
						flag = (bool)obj;
						num = 1;
					}
					else
					{
						num = 0;
					}
					if (((uint)num & (flag ? 1u : 0u)) != 0)
					{
						return true;
					}
				}
				if (data.GetType().GetField("timeInAntigrav", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(data) is float num2 && num2 > 0f)
				{
					return true;
				}
				object obj2 = data.GetType().GetField("currentClimbHandle", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(data);
				if (obj2 != null)
				{
					return true;
				}
				object obj3 = character?.GetType().GetField("refs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(character);
				object obj4 = obj3?.GetType().GetField("balloons", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj3);
				if (obj4?.GetType().GetProperty("currentBalloonCount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj4) is int num3 && num3 > 0)
				{
					return true;
				}
				object obj5 = obj4?.GetType().GetField("_isParachuteOpen", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj4);
				bool flag2 = default(bool);
				int num4;
				if (obj5 is bool)
				{
					flag2 = (bool)obj5;
					num4 = 1;
				}
				else
				{
					num4 = 0;
				}
				if (((uint)num4 & (flag2 ? 1u : 0u)) != 0)
				{
					return true;
				}
				object obj6 = data.GetType().GetProperty("currentItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(data);
				if (obj6 != null && (obj6.GetType().Name.Contains("Jetpack") || obj6.GetType().Name.Contains("Parasol")))
				{
					return true;
				}
				object obj7 = data.GetType().GetField("isInvincible", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(data);
				bool flag3 = default(bool);
				int num5;
				if (obj7 is bool)
				{
					flag3 = (bool)obj7;
					num5 = 1;
				}
				else
				{
					num5 = 0;
				}
				if (((uint)num5 & (flag3 ? 1u : 0u)) != 0)
				{
					return true;
				}
				return false;
			}
			catch
			{
				return true;
			}
		}

		internal static bool TryGetPeakInjury(Component component, out float injury)
		{
			injury = 0f;
			try
			{
				if (!TryGetCharacterData(component, out object character, out object _) || character == null)
				{
					return false;
				}
				object obj = character?.GetType().GetField("refs", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(character);
				object obj2 = obj?.GetType().GetField("afflictions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
				MethodInfo methodInfo = obj2?.GetType().GetMethod("GetCurrentStatus", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (methodInfo == null || methodInfo.GetParameters().Length != 1)
				{
					return false;
				}
				Type parameterType = methodInfo.GetParameters()[0].ParameterType;
				object obj3 = Enum.ToObject(parameterType, 0);
				if (methodInfo.Invoke(obj2, new object[1] { obj3 }) is float num)
				{
					injury = num;
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		internal static bool IsLandingExempt(Component component)
		{
			try
			{
				if (IsFlightExempt(component))
				{
					return true;
				}
				if (!TryGetCharacterData(component, out object _, out object data) || data == null)
				{
					return true;
				}
				object obj = data?.GetType().GetField("hasParachute", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(data);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					return true;
				}
				return IsRecentlyWarped(component);
			}
			catch
			{
				return false;
			}
		}

		private static bool IsRecentlyWarped(Component component)
		{
			try
			{
				object obj = ((object)component).GetType().GetField("character", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(component);
				if (obj == null)
				{
					return false;
				}
				object obj2 = obj.GetType().GetProperty("warping", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
				bool flag = default(bool);
				int num;
				if (obj2 is bool)
				{
					flag = (bool)obj2;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					return true;
				}
				return obj.GetType().GetProperty("timeLastWarped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) is float num2 && Time.time - num2 < 2f;
			}
			catch
			{
				return false;
			}
		}

		public static void MonitorPlayerInstantiation(PhotonView pv, GameObject instantiatedObj)
		{
			if (!((Object)(object)pv == (Object)null) && pv.Owner != null)
			{
				MonitorNetworkInstantiation(pv.Owner, instantiatedObj);
			}
		}

		public static void MonitorNetworkInstantiation(Player owner, GameObject instantiatedObj)
		{
			if (owner == null || (Object)(object)instantiatedObj == (Object)null || owner.ActorNumber <= 0 || (Object)(object)instantiatedObj.GetComponentInChildren<Item>(true) == (Object)null)
			{
				return;
			}
			int num = owner.ActorNumber;
			int num2 = ((PhotonNetwork.MasterClient != null) ? PhotonNetwork.MasterClient.ActorNumber : (-1));
			int currentRpcSenderActorNumber = CurrentRpcSenderActorNumber;
			if (num == num2 && currentRpcSenderActorNumber > 0 && currentRpcSenderActorNumber != num2)
			{
				Player netPlayerByActor = GetNetPlayerByActor(currentRpcSenderActorNumber);
				if (netPlayerByActor != null)
				{
					owner = netPlayerByActor;
					num = currentRpcSenderActorNumber;
				}
			}
			Guid itemGuid = GetItemGuid(instantiatedObj);
			RememberItemOrigin(num, itemGuid, overwrite: false);
			float time = Time.time;
			Item[] componentsInChildren = instantiatedObj.GetComponentsInChildren<Item>(true);
			foreach (Item val in componentsInChildren)
			{
				RecentItemInstanceCreators[((Object)val).GetInstanceID()] = new ItemOriginRecord
				{
					ActorNumber = num,
					LastSeenAt = time
				};
				RememberBackpackContents(GetItemInstanceData(val), num, overwrite: false);
			}
			List<int> list = new List<int>();
			foreach (KeyValuePair<int, ItemOriginRecord> recentItemInstanceCreator in RecentItemInstanceCreators)
			{
				if (time - recentItemInstanceCreator.Value.LastSeenAt > 30f)
				{
					list.Add(recentItemInstanceCreator.Key);
				}
			}
			foreach (int item in list)
			{
				RecentItemInstanceCreators.Remove(item);
			}
			if ((Object)(object)Instance != (Object)null)
			{
				((MonoBehaviour)Instance).StartCoroutine(RecordSpawnAfterBackpackData(owner, num, instantiatedObj, time));
			}
			else if (!IsRecentBackpackContent(itemGuid))
			{
				RecordItemActivity(GetOrCreatePlayerData(owner), num, instantiatedObj, scorePlayer: true, time);
			}
		}

		private static IEnumerator RecordSpawnAfterBackpackData(Player owner, int actorNum, GameObject spawnedObject, float eventTime)
		{
			yield return (object)new WaitForSeconds(0.15f);
			if (!((Object)(object)spawnedObject == (Object)null))
			{
				Item[] componentsInChildren = spawnedObject.GetComponentsInChildren<Item>(true);
				foreach (Item item in componentsInChildren)
				{
					RememberBackpackContents(GetItemInstanceData(item), actorNum, overwrite: false);
				}
				if (!IsRecentBackpackContent(GetItemGuid(spawnedObject)))
				{
					PlayerData playerData = GetOrCreatePlayerData(owner);
					RecordItemActivity(playerData, actorNum, spawnedObject, scorePlayer: true, eventTime);
				}
			}
		}

		private static Guid GetItemGuid(ItemInstanceData? instanceData)
		{
			if (instanceData == null)
			{
				return Guid.Empty;
			}
			return (AccessTools.Field(typeof(ItemInstanceData), "guid")?.GetValue(instanceData) is Guid guid) ? guid : Guid.Empty;
		}

		private static Guid GetItemGuid(GameObject? itemObject)
		{
			if ((Object)(object)itemObject == (Object)null)
			{
				return Guid.Empty;
			}
			Item val = itemObject.GetComponent<Item>() ?? itemObject.GetComponentInChildren<Item>(true);
			if ((Object)(object)val == (Object)null)
			{
				return Guid.Empty;
			}
			return GetItemGuid(GetItemInstanceData(val));
		}

		private static ItemInstanceData? GetItemInstanceData(Item item)
		{
			object? obj = AccessTools.Field(typeof(Item), "data")?.GetValue(item);
			return (ItemInstanceData?)((obj is ItemInstanceData) ? obj : null);
		}

		public static void MonitorItemInstanceData(Item item, ItemInstanceData? instanceData)
		{
			if ((Object)(object)item == (Object)null || instanceData == null)
			{
				return;
			}
			try
			{
				Guid itemGuid = GetItemGuid(instanceData);
				int currentRpcSenderActorNumber = CurrentRpcSenderActorNumber;
				int num = ((PhotonNetwork.MasterClient != null) ? PhotonNetwork.MasterClient.ActorNumber : (-1));
				object? obj = AccessTools.Field(typeof(Item), "view")?.GetValue(item);
				PhotonView val = (PhotonView)((obj is PhotonView) ? obj : null);
				int num2 = 0;
				bool overwrite = false;
				ItemOriginRecord value;
				if (currentRpcSenderActorNumber > 0 && currentRpcSenderActorNumber != num)
				{
					num2 = currentRpcSenderActorNumber;
					overwrite = true;
				}
				else if (RecentItemInstanceCreators.TryGetValue(((Object)item).GetInstanceID(), out value) && value != null && Time.time - value.LastSeenAt <= 30f)
				{
					num2 = value.ActorNumber;
					overwrite = true;
				}
				else if (((val != null) ? val.Owner : null) != null)
				{
					num2 = val.Owner.ActorNumber;
				}
				RememberItemOrigin(num2, itemGuid, overwrite);
				RememberBackpackContents(instanceData, num2, overwrite);
			}
			catch
			{
			}
		}

		private static void RememberBackpackContents(ItemInstanceData? instanceData, int originActor, bool overwrite)
		{
			float time = Time.time;
			foreach (Guid backpackContentGuid in GetBackpackContentGuids(instanceData))
			{
				BackpackContentIgnoreUntil[backpackContentGuid] = time + 10f;
				RememberItemOrigin(originActor, backpackContentGuid, overwrite);
			}
		}

		private static HashSet<Guid> GetBackpackContentGuids(Player player)
		{
			return ((Object)(object)player != (Object)null && player.backpackSlot != null) ? GetBackpackContentGuids(((ItemSlot)player.backpackSlot).data) : new HashSet<Guid>();
		}

		private static HashSet<Guid> GetBackpackContentGuids(ItemInstanceData? instanceData)
		{
			HashSet<Guid> hashSet = new HashSet<Guid>();
			if (instanceData == null)
			{
				return hashSet;
			}
			try
			{
				MethodInfo methodInfo = typeof(ItemInstanceData).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "TryGetDataEntry" && method.IsGenericMethodDefinition && method.GetParameters().Length == 2);
				if (methodInfo == null)
				{
					return hashSet;
				}
				Type parameterType = methodInfo.GetParameters()[0].ParameterType;
				object obj = (parameterType.IsEnum ? Enum.ToObject(parameterType, 7) : Convert.ChangeType(7, parameterType));
				object[] array = new object[2] { obj, null };
				MethodInfo methodInfo2 = methodInfo.MakeGenericMethod(typeof(BackpackData));
				object obj2 = methodInfo2.Invoke(instanceData, array);
				if (!(obj2 is bool) || !(bool)obj2 || array[1] == null)
				{
					return hashSet;
				}
				object obj3 = AccessTools.Field(typeof(BackpackData), "itemSlots")?.GetValue(array[1]);
				if (!(obj3 is IEnumerable enumerable))
				{
					return hashSet;
				}
				foreach (object item in enumerable)
				{
					ItemSlot val = (ItemSlot)((item is ItemSlot) ? item : null);
					if (val != null)
					{
						AddInventoryItemGuid(hashSet, val);
					}
				}
			}
			catch
			{
			}
			return hashSet;
		}

		private static bool IsRecentBackpackContent(Guid guid)
		{
			if (guid == Guid.Empty || !BackpackContentIgnoreUntil.TryGetValue(guid, out var value))
			{
				return false;
			}
			if (Time.time <= value)
			{
				return true;
			}
			BackpackContentIgnoreUntil.Remove(guid);
			return false;
		}

		private static Player? GetNetPlayerByActor(int actorNum)
		{
			Player[] playerList = PhotonNetwork.PlayerList;
			if (playerList == null)
			{
				return null;
			}
			Player[] array = playerList;
			foreach (Player val in array)
			{
				if (val != null && val.ActorNumber == actorNum)
				{
					return val;
				}
			}
			return null;
		}

		private static void RememberItemOrigin(int actorNum, Guid guid, bool overwrite)
		{
			if (actorNum <= 0 || guid == Guid.Empty)
			{
				return;
			}
			float time = Time.time;
			if (!overwrite && ItemOrigins.TryGetValue(guid, out ItemOriginRecord value) && value != null)
			{
				value.LastSeenAt = time;
				return;
			}
			ItemOrigins[guid] = new ItemOriginRecord
			{
				ActorNumber = actorNum,
				LastSeenAt = time
			};
			List<Guid> list = new List<Guid>();
			foreach (KeyValuePair<Guid, ItemOriginRecord> itemOrigin in ItemOrigins)
			{
				if (time - itemOrigin.Value.LastSeenAt > 1800f)
				{
					list.Add(itemOrigin.Key);
				}
			}
			foreach (Guid item in list)
			{
				ItemOrigins.Remove(item);
			}
		}

		private static bool TryGetItemOrigin(Guid guid, out int actorNum)
		{
			actorNum = 0;
			if (guid == Guid.Empty || !ItemOrigins.TryGetValue(guid, out ItemOriginRecord value) || value == null)
			{
				return false;
			}
			if (Time.time - value.LastSeenAt > 1800f)
			{
				ItemOrigins.Remove(guid);
				return false;
			}
			actorNum = value.ActorNumber;
			return actorNum > 0;
		}

		public static void MonitorDrop(Player owner, GameObject? droppedObj)
		{
			if (owner == null || owner.ActorNumber <= 0)
			{
				return;
			}
			PlayerData orCreatePlayerData = GetOrCreatePlayerData(owner);
			if ((Object)(object)droppedObj != (Object)null)
			{
				Guid itemGuid = GetItemGuid(droppedObj);
				BackpackContentIgnoreUntil.Remove(itemGuid);
				RememberItemOrigin(owner.ActorNumber, itemGuid, overwrite: true);
			}
			if ((Object)(object)droppedObj == (Object)null && Time.time - orCreatePlayerData.LastItemDeleteLogTime >= 1f)
			{
				orCreatePlayerData.LastItemDeleteLogTime = Time.time;
				BadScoutWatcherPlugin? instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogWarning((object)$"Drop event from actor {owner.ActorNumber} did not expose the item object; the dropper is still tracked, but host item deletion is unavailable for this drop.");
				}
			}
			RecordItemDropActivity(orCreatePlayerData, owner.ActorNumber, droppedObj);
		}

		private static PlayerData GetOrCreatePlayerData(Player owner)
		{
			int actorNumber = owner.ActorNumber;
			if (!Players.TryGetValue(actorNumber, out PlayerData value) || value == null)
			{
				value = new PlayerData
				{
					PlayerName = (string.IsNullOrEmpty(owner.NickName) ? $"Scout #{actorNumber}" : owner.NickName),
					PlayerId = (string.IsNullOrEmpty(owner.UserId) ? "0" : owner.UserId),
					ActorNumber = actorNumber,
					NetPlayer = owner,
					LastDecayTime = Time.time,
					LastCheckTime = Time.time
				};
				Players[actorNumber] = value;
			}
			else
			{
				if (!string.IsNullOrEmpty(owner.NickName))
				{
					value.PlayerName = owner.NickName;
				}
				value.PlayerId = (string.IsNullOrEmpty(owner.UserId) ? "0" : owner.UserId);
				value.NetPlayer = owner;
			}
			return value;
		}

		public static bool HasItemToDrop(object characterItems)
		{
			try
			{
				object obj = characterItems.GetType().GetField("character", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(characterItems);
				object obj2 = obj?.GetType().GetField("data", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
				object obj3 = obj2?.GetType().GetProperty("currentItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj2);
				return obj3 != null;
			}
			catch
			{
				return false;
			}
		}

		public static bool HasItemInDropSlot(object characterItems, byte slotNumber)
		{
			try
			{
				object obj = characterItems.GetType().GetField("character", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(characterItems);
				object obj2 = obj?.GetType().GetProperty("player", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
				object obj3 = (obj2?.GetType().GetMethod("GetItemSlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.Invoke(obj2, new object[1] { slotNumber });
				bool? flag = obj3?.GetType().GetMethod("IsEmpty", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(obj3, null) as bool?;
				return obj3 != null && flag == false;
			}
			catch
			{
				return false;
			}
		}

		public static ItemInstanceData? GetItemDataInDropSlot(object characterItems, byte slotNumber)
		{
			try
			{
				object obj = characterItems.GetType().GetField("character", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(characterItems);
				object obj2 = obj?.GetType().GetProperty("player", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
				object obj3 = (obj2?.GetType().GetMethod("GetItemSlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.Invoke(obj2, new object[1] { slotNumber });
				object? obj4 = obj3?.GetType().GetProperty("data", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj3);
				return (ItemInstanceData?)((obj4 is ItemInstanceData) ? obj4 : null);
			}
			catch
			{
				return null;
			}
		}

		public static void MonitorDropFromCharacterItems(object characterItems, bool validDrop, ItemInstanceData? droppedData = null)
		{
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Expected O, but got Unknown
			if (!validDrop)
			{
				return;
			}
			try
			{
				object obj = characterItems.GetType().GetField("character", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(characterItems);
				Character val = (Character)((obj is Character) ? obj : null);
				PhotonView val2 = (((Object)(object)val != (Object)null) ? ((MonoBehaviourPun)val).photonView : null);
				if ((Object)(object)val2 == (Object)null)
				{
					object? obj2 = obj?.GetType().GetField("view", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj);
					val2 = (PhotonView)((obj2 is PhotonView) ? obj2 : null);
				}
				Player val3 = (((Object)(object)val2 != (Object)null) ? val2.Owner : null);
				if (val3 == null && (Object)(object)val != (Object)null && val.IsLocal)
				{
					val3 = PhotonNetwork.LocalPlayer;
				}
				int currentRpcSenderActorNumber = CurrentRpcSenderActorNumber;
				int num = ((PhotonNetwork.MasterClient != null) ? PhotonNetwork.MasterClient.ActorNumber : (-1));
				if (currentRpcSenderActorNumber > 0 && currentRpcSenderActorNumber != num)
				{
					Player[] playerList = PhotonNetwork.PlayerList;
					if (playerList != null)
					{
						Player[] array = playerList;
						foreach (Player val4 in array)
						{
							if (val4 != null && val4.ActorNumber == currentRpcSenderActorNumber)
							{
								val3 = val4;
								break;
							}
						}
					}
				}
				if (val3 == null)
				{
					return;
				}
				if (droppedData != null)
				{
					RememberItemOrigin(val3.ActorNumber, GetItemGuid(droppedData), overwrite: true);
				}
				GameObject val5 = null;
				object obj3 = characterItems.GetType().GetField("droppedItems", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(characterItems);
				if (obj3 is IEnumerable enumerable)
				{
					{
						IEnumerator enumerator = enumerable.GetEnumerator();
						try
						{
							GameObject val9;
							for (; enumerator.MoveNext(); val5 = val9)
							{
								object current = enumerator.Current;
								object obj4 = current;
								PhotonView val6 = (PhotonView)((obj4 is PhotonView) ? obj4 : null);
								Component val7;
								if (val6 == null)
								{
									val7 = (Component)((obj4 is Component) ? obj4 : null);
									if (val7 == null)
									{
										GameObject val8 = (GameObject)((obj4 is GameObject) ? obj4 : null);
										if (val8 == null || !((Object)(object)val8 != (Object)null))
										{
											goto IL_0240;
										}
										val9 = val8;
										continue;
									}
								}
								else
								{
									if ((Object)(object)val6 != (Object)null)
									{
										val9 = ((Component)val6).gameObject;
										continue;
									}
									val7 = (Component)obj4;
								}
								if (!((Object)(object)val7 != (Object)null))
								{
									goto IL_0240;
								}
								val9 = val7.gameObject;
								continue;
								IL_0240:
								val9 = val5;
							}
						}
						finally
						{
							IDisposable disposable = enumerator as IDisposable;
							if (disposable != null)
							{
								disposable.Dispose();
							}
						}
					}
				}
				MonitorDrop(val3, val5);
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[BadScoutWatcher] Could not inspect dropped item: " + ex.Message));
			}
		}

		public static HashSet<Guid> CaptureInventoryItemGuids(Player player)
		{
			HashSet<Guid> hashSet = new HashSet<Guid>();
			if ((Object)(object)player == (Object)null)
			{
				return hashSet;
			}
			if (player.itemSlots != null)
			{
				ItemSlot[] itemSlots = player.itemSlots;
				foreach (ItemSlot slot in itemSlots)
				{
					AddInventoryItemGuid(hashSet, slot);
				}
			}
			AddInventoryItemGuid(hashSet, player.tempFullSlot);
			AddInventoryItemGuid(hashSet, (ItemSlot?)(object)player.backpackSlot);
			return hashSet;
		}

		private static void AddInventoryItemGuid(HashSet<Guid> guids, ItemSlot? slot)
		{
			if (slot != null && !slot.IsEmpty())
			{
				Guid itemGuid = GetItemGuid(slot.data);
				if (itemGuid != Guid.Empty)
				{
					guids.Add(itemGuid);
				}
			}
		}

		private static Player? GetInventoryOwner(Player player)
		{
			try
			{
				object? obj = typeof(Player).GetField("view", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(player);
				PhotonView val = (PhotonView)((obj is PhotonView) ? obj : null);
				if (((val != null) ? val.Owner : null) != null)
				{
					return val.Owner;
				}
				if ((Object)(object)player.character != (Object)null && player.character.IsLocal)
				{
					return PhotonNetwork.LocalPlayer;
				}
			}
			catch
			{
			}
			return null;
		}

		public static void MonitorInventoryAdd(Player player, bool itemAdded, ItemInstanceData? instanceData, ItemSlot? resultSlot)
		{
			if (!itemAdded || (Object)(object)player == (Object)null || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			Player inventoryOwner = GetInventoryOwner(player);
			if (inventoryOwner == null || inventoryOwner.ActorNumber <= 0)
			{
				return;
			}
			Guid itemGuid = GetItemGuid(instanceData);
			if (itemGuid == Guid.Empty && resultSlot != null)
			{
				itemGuid = GetItemGuid(resultSlot.data);
			}
			if (itemGuid != Guid.Empty && (IsRecentBackpackContent(itemGuid) || GetBackpackContentGuids(player).Contains(itemGuid)))
			{
				return;
			}
			PlayerData orCreatePlayerData = GetOrCreatePlayerData(inventoryOwner);
			int currentRpcSenderActorNumber = CurrentRpcSenderActorNumber;
			int num = ((PhotonNetwork.MasterClient != null) ? PhotonNetwork.MasterClient.ActorNumber : (-1));
			if (currentRpcSenderActorNumber > 0 && currentRpcSenderActorNumber != num)
			{
				PlayerData playerDataByActor = GetPlayerDataByActor(currentRpcSenderActorNumber);
				if (playerDataByActor != null)
				{
					if (!(itemGuid != Guid.Empty) || TryMarkRecentInventoryGuid(itemGuid))
					{
						RecordAttributedInventoryChange(playerDataByActor, currentRpcSenderActorNumber, orCreatePlayerData.PlayerName);
					}
					return;
				}
			}
			RecordInventoryAdditionByOrigin(itemGuid, orCreatePlayerData, inventoryOwner.ActorNumber);
		}

		public static void MonitorInventorySync(Player player, HashSet<Guid>? itemGuidsBeforeSync)
		{
			if ((Object)(object)player == (Object)null || PhotonNetwork.IsMasterClient)
			{
				return;
			}
			Player inventoryOwner = GetInventoryOwner(player);
			if (inventoryOwner == null || inventoryOwner.ActorNumber <= 0)
			{
				return;
			}
			PlayerData orCreatePlayerData = GetOrCreatePlayerData(inventoryOwner);
			if (!orCreatePlayerData.InventoryBaselineInitialized)
			{
				orCreatePlayerData.InventoryBaselineInitialized = true;
				return;
			}
			HashSet<Guid> hashSet = CaptureInventoryItemGuids(player);
			if (itemGuidsBeforeSync == null)
			{
				return;
			}
			foreach (Guid item in hashSet)
			{
				if (!itemGuidsBeforeSync.Contains(item))
				{
					RecordInventoryAdditionByOrigin(item, orCreatePlayerData, inventoryOwner.ActorNumber);
				}
			}
		}

		private static void RecordInventoryAdditionByOrigin(Guid itemGuid, PlayerData recipientData, int recipientActorNum)
		{
			if (IsRecentBackpackContent(itemGuid) || !TryMarkRecentInventoryGuid(itemGuid))
			{
				return;
			}
			if (!TryGetItemOrigin(itemGuid, out var actorNum) || (PhotonNetwork.MasterClient != null && actorNum == PhotonNetwork.MasterClient.ActorNumber))
			{
				RecordUnattributedInventoryChange(recipientData, recipientActorNum);
				return;
			}
			PlayerData playerDataByActor = GetPlayerDataByActor(actorNum);
			if (playerDataByActor == null)
			{
				RecordUnattributedInventoryChange(recipientData, recipientActorNum);
			}
			else
			{
				RecordAttributedInventoryChange(playerDataByActor, actorNum, recipientData.PlayerName);
			}
		}

		private static bool TryMarkRecentInventoryGuid(Guid itemGuid)
		{
			if (itemGuid == Guid.Empty)
			{
				return false;
			}
			float time = Time.time;
			if (RecentInventoryGuids.TryGetValue(itemGuid, out var value) && time - value < 2f)
			{
				return false;
			}
			RecentInventoryGuids[itemGuid] = time;
			List<Guid> list = new List<Guid>();
			foreach (KeyValuePair<Guid, float> recentInventoryGuid in RecentInventoryGuids)
			{
				if (time - recentInventoryGuid.Value > 2f)
				{
					list.Add(recentInventoryGuid.Key);
				}
			}
			foreach (Guid item in list)
			{
				RecentInventoryGuids.Remove(item);
			}
			return true;
		}

		private static PlayerData? GetPlayerDataByActor(int actorNum)
		{
			if (Players.TryGetValue(actorNum, out PlayerData value) && value != null)
			{
				return value;
			}
			Player[] playerList = PhotonNetwork.PlayerList;
			if (playerList == null)
			{
				return null;
			}
			Player[] array = playerList;
			foreach (Player val in array)
			{
				if (val != null && val.ActorNumber == actorNum)
				{
					return GetOrCreatePlayerData(val);
				}
			}
			return null;
		}

		private static void RecordUnattributedInventoryChange(PlayerData recipientData, int recipientActorNum)
		{
			float time = Time.time;
			recipientData.UnattributedInventoryTimes.Enqueue(time);
			PruneItemFloodWindow(recipientData.UnattributedInventoryTimes, time);
			if (recipientData.UnattributedInventoryTimes.Count >= 6 && !(time - recipientData.LastInventoryThresholdLogTime < 1f))
			{
				recipientData.LastInventoryThresholdLogTime = time;
				BadScoutWatcherPlugin? instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogWarning((object)$"Inventory flood observed for recipient {recipientData.PlayerName} (actor {recipientActorNum}), but no non-host source actor could be confirmed. The recipient was not flagged.");
				}
			}
		}

		private static void RecordAttributedInventoryChange(PlayerData sourceData, int sourceActorNum, string recipientName)
		{
			float time = Time.time;
			if (!sourceData.InventoryActivityObserved)
			{
				sourceData.InventoryActivityObserved = true;
				BadScoutWatcherPlugin? instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogInfo((object)$"Inventory flood attribution observed item origins from {sourceData.PlayerName} (actor {sourceActorNum}).");
				}
			}
			sourceData.InventoryAddTimes.Enqueue(time);
			PruneItemFloodWindow(sourceData.InventoryAddTimes, time);
			sourceData.InventoryAddedItemsCount = sourceData.InventoryAddTimes.Count;
			if (sourceData.InventoryAddedItemsCount < 6)
			{
				return;
			}
			if (time - sourceData.LastInventoryThresholdLogTime >= 1f)
			{
				sourceData.LastInventoryThresholdLogTime = time;
				BadScoutWatcherPlugin? instance2 = Instance;
				if (instance2 != null)
				{
					((BaseUnityPlugin)instance2).Logger.LogWarning((object)$"Inventory flood attributed to {sourceData.PlayerName} (actor {sourceActorNum}): {sourceData.InventoryAddedItemsCount} items delivered to {recipientName} within {500f:F0} ms.");
				}
			}
			if (time - sourceData.LastItemFloodFlagTime >= 6f)
			{
				sourceData.LastItemFloodFlagTime = time;
				FlagPlayer(sourceActorNum, sourceData.PlayerName, sourceData.PlayerId, "Item Flood", "High risk", $"{sourceData.InventoryAddedItemsCount} inventory additions within {500f:F0} ms to {recipientName}", 30);
			}
		}

		private static void RecordItemActivity(PlayerData p, int actorNum, GameObject? itemObject, bool scorePlayer = true, float eventTime = -1f)
		{
			if ((Object)(object)itemObject == (Object)null || IsRecentBackpackContent(GetItemGuid(itemObject)))
			{
				return;
			}
			float time = Time.time;
			float num = ((eventTime >= 0f) ? eventTime : time);
			int instanceID = ((Object)itemObject).GetInstanceID();
			if (p.RecentItemInstanceIds.TryGetValue(instanceID, out var value) && time - value < 2f)
			{
				return;
			}
			p.RecentItemInstanceIds[instanceID] = time;
			List<int> list = new List<int>();
			foreach (KeyValuePair<int, float> recentItemInstanceId in p.RecentItemInstanceIds)
			{
				if (time - recentItemInstanceId.Value > 2f)
				{
					list.Add(recentItemInstanceId.Key);
				}
			}
			foreach (int item in list)
			{
				p.RecentItemInstanceIds.Remove(item);
			}
			if (!p.ItemActivityObserved)
			{
				p.ItemActivityObserved = true;
				BadScoutWatcherPlugin? instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogInfo((object)$"Item-spawn monitoring observed a PEAK item for {p.PlayerName} (actor {actorNum}).");
				}
			}
			p.ItemSpawnTimes.Enqueue(num);
			PruneItemFloodWindow(p.ItemSpawnTimes, num);
			p.SpawnedItemsCount = p.ItemSpawnTimes.Count;
			if (p.SpawnedItemsCount >= 6)
			{
				HandleItemFloodResponse(p, actorNum, p.SpawnedItemsCount, $"{p.SpawnedItemsCount} distinct item spawns within {500f:F0} ms", itemObject, $"Item-spam threshold reached for actor {actorNum}", scorePlayer);
			}
		}

		private static void RecordItemDropActivity(PlayerData p, int actorNum, GameObject? droppedObject)
		{
			float time = Time.time;
			if ((Object)(object)droppedObject != (Object)null)
			{
				int instanceID = ((Object)droppedObject).GetInstanceID();
				if (p.RecentItemDropIds.TryGetValue(instanceID, out var value) && time - value < 0.2f)
				{
					return;
				}
				p.RecentItemDropIds[instanceID] = time;
				List<int> list = new List<int>();
				foreach (KeyValuePair<int, float> recentItemDropId in p.RecentItemDropIds)
				{
					if (time - recentItemDropId.Value > 2f)
					{
						list.Add(recentItemDropId.Key);
					}
				}
				foreach (int item in list)
				{
					p.RecentItemDropIds.Remove(item);
				}
			}
			p.ItemDropTimes.Enqueue(time);
			PruneItemFloodWindow(p.ItemDropTimes, time);
			p.DroppedItemsCount = p.ItemDropTimes.Count;
			if (p.DroppedItemsCount >= 6)
			{
				HandleItemFloodResponse(p, actorNum, p.DroppedItemsCount, $"{p.DroppedItemsCount} item drops within {500f:F0} ms", droppedObject, $"Item-drop flood threshold reached for actor {actorNum}", scorePlayer: true);
			}
		}

		private static void HandleItemFloodResponse(PlayerData p, int actorNum, int eventCount, string description, GameObject? itemObject, string logPrefix, bool scorePlayer)
		{
			float time = Time.time;
			if (time - p.LastItemThresholdLogTime >= 1f)
			{
				p.LastItemThresholdLogTime = time;
				BadScoutWatcherPlugin? instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogWarning((object)$"{logPrefix}: {eventCount} item events within {500f:F0} ms.");
				}
			}
			bool isMasterClient = PhotonNetwork.IsMasterClient;
			if (isMasterClient && ConfigItemDeletion != null && ConfigItemDeletion.Value)
			{
				if ((Object)(object)itemObject != (Object)null)
				{
					PhotonView val = itemObject.GetComponent<PhotonView>() ?? itemObject.GetComponentInChildren<PhotonView>(true);
					bool flag = TryDeleteFloodedItem(itemObject);
					if (time - p.LastItemDeleteLogTime >= 1f)
					{
						p.LastItemDeleteLogTime = time;
						BadScoutWatcherPlugin? instance2 = Instance;
						if (instance2 != null)
						{
							((BaseUnityPlugin)instance2).Logger.LogInfo((object)((!flag) ? $"Item flood from actor {actorNum}: item removal could not be requested; see the preceding warning." : (((Object)(object)val == (Object)null) ? $"Item flood from actor {actorNum}: removed the host's local copy; item had no PhotonView." : $"Item flood from actor {actorNum}: requested network removal of the item.")));
						}
					}
				}
				else if (time - p.LastItemDeleteLogTime >= 1f)
				{
					p.LastItemDeleteLogTime = time;
					BadScoutWatcherPlugin? instance3 = Instance;
					if (instance3 != null)
					{
						((BaseUnityPlugin)instance3).Logger.LogWarning((object)$"Item flood from actor {actorNum}: no item object was available to remove.");
					}
				}
			}
			else if (time - p.LastItemDeleteLogTime >= 1f)
			{
				p.LastItemDeleteLogTime = time;
				BadScoutWatcherPlugin? instance4 = Instance;
				if (instance4 != null)
				{
					((BaseUnityPlugin)instance4).Logger.LogWarning((object)((!isMasterClient) ? $"Item flood from actor {actorNum} was detected, but only the room host can remove network items." : $"Item flood from actor {actorNum} was detected, but Delete Items is disabled."));
				}
			}
			if (scorePlayer && time - p.LastItemFloodFlagTime >= 6f)
			{
				p.LastItemFloodFlagTime = time;
				FlagPlayer(actorNum, p.PlayerName, p.PlayerId, "Item Flood", "High risk", description, 30);
			}
		}

		private static void PruneItemFloodWindow(Queue<float> eventTimes, float now)
		{
			while (eventTimes.Count > 0 && now - eventTimes.Peek() >= 0.5f)
			{
				eventTimes.Dequeue();
			}
		}

		private static void ResetExpiredItemFloodWindows()
		{
			float time = Time.time;
			foreach (PlayerData value in Players.Values)
			{
				if (value != null)
				{
					PruneItemFloodWindow(value.ItemSpawnTimes, time);
					value.SpawnedItemsCount = value.ItemSpawnTimes.Count;
					PruneItemFloodWindow(value.ItemDropTimes, time);
					value.DroppedItemsCount = value.ItemDropTimes.Count;
					PruneItemFloodWindow(value.InventoryAddTimes, time);
					value.InventoryAddedItemsCount = value.InventoryAddTimes.Count;
					PruneItemFloodWindow(value.UnattributedInventoryTimes, time);
				}
			}
		}

		private static bool TryDeleteFloodedItem(GameObject itemObject)
		{
			if ((Object)(object)itemObject == (Object)null || !PhotonNetwork.IsMasterClient || !PhotonNetwork.InRoom)
			{
				return false;
			}
			PhotonView val = itemObject.GetComponent<PhotonView>() ?? itemObject.GetComponentInChildren<PhotonView>(true);
			if ((Object)(object)val == (Object)null)
			{
				Object.Destroy((Object)(object)itemObject);
				return true;
			}
			try
			{
				PhotonNetwork.Destroy(((Component)val).gameObject);
				if ((Object)(object)Instance != (Object)null)
				{
					((MonoBehaviour)Instance).StartCoroutine(VerifyFloodItemRemoval(val));
				}
				return true;
			}
			catch (Exception ex)
			{
				BadScoutWatcherPlugin? instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogWarning((object)("Network removal of spam item failed: " + ex.Message));
				}
				return false;
			}
		}

		private static IEnumerator VerifyFloodItemRemoval(PhotonView spawnedView)
		{
			yield return null;
			if ((Object)(object)spawnedView != (Object)null && (Object)(object)((Component)spawnedView).gameObject != (Object)null)
			{
				BadScoutWatcherPlugin? instance = Instance;
				if (instance != null)
				{
					((BaseUnityPlugin)instance).Logger.LogWarning((object)"Photon did not remove the spam item after the destroy request; check the room host and item ownership.");
				}
			}
		}

		private static void UpdateCursorState()
		{
			if (ShowUI)
			{
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
			}
			else
			{
				Cursor.lockState = (CursorLockMode)1;
				Cursor.visible = false;
			}
		}

		private void OnGUI()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0320: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_0343: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Expected O, but got Unknown
			//IL_0361: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			if (isListeningForKey && Event.current.isKey && (int)Event.current.keyCode > 0)
			{
				if (ConfigToggleKey != null)
				{
					ConfigToggleKey.Value = Event.current.keyCode;
				}
				isListeningForKey = false;
				Event.current.Use();
			}
			if (Time.time < latestWarningUntil)
			{
				if (warningStyle == null)
				{
					Color textColor = Color32.op_Implicit(new Color32(byte.MaxValue, (byte)59, (byte)59, byte.MaxValue));
					warningStyle = new GUIStyle(GUI.skin.label)
					{
						alignment = (TextAnchor)3,
						wordWrap = true,
						fontSize = 14,
						fontStyle = (FontStyle)1,
						padding = new RectOffset(0, 0, 0, 0),
						stretchHeight = true
					};
					warningStyle.normal.textColor = textColor;
					warningStyle.onNormal.textColor = textColor;
				}
				GUIContent val = new GUIContent(latestWarning);
				float num = Mathf.Max(1f, Mathf.Min((float)Screen.width - 40f, 700f));
				float num2 = Mathf.Clamp(warningStyle.CalcSize(val).x, Mathf.Min(220f, num), num);
				float num3 = Mathf.Max(1f, num2 - 24f);
				float num4 = warningStyle.CalcHeight(val, num3);
				float num5 = Mathf.Max(42f, num4 + 24f);
				GUI.color = Color.white;
				GUI.Box(new Rect(20f, 20f, num2, num5), GUIContent.none);
				GUI.Label(new Rect(32f, 28f, num3, num5 - 16f), val, warningStyle);
				GUI.color = Color.white;
			}
			if (ShowUI)
			{
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
				InitStyles();
				GUI.BringWindowToFront(8888);
				((Rect)(ref windowRect)).width = Mathf.Min(((Rect)(ref windowRect)).width, Mathf.Max(280f, (float)Screen.width - 20f));
				((Rect)(ref windowRect)).height = Mathf.Min(((Rect)(ref windowRect)).height, Mathf.Max(260f, (float)Screen.height - 20f));
				((Rect)(ref windowRect)).x = Mathf.Clamp(((Rect)(ref windowRect)).x, 0f, Mathf.Max(0f, (float)Screen.width - ((Rect)(ref windowRect)).width));
				((Rect)(ref windowRect)).y = Mathf.Clamp(((Rect)(ref windowRect)).y, 0f, Mathf.Max(0f, (float)Screen.height - ((Rect)(ref windowRect)).height));
				KeyCode val2 = (KeyCode)((ConfigToggleKey == null) ? 283 : ((int)ConfigToggleKey.Value));
				windowRect = GUI.Window(8888, windowRect, new WindowFunction(DrawWindowContent), string.Format("BadScoutWatcher v{0} [Press {1} to Toggle]", "1.1.0", val2), windowStyle ?? GUI.skin.window);
			}
		}

		private void InitStyles()
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Expected O, but got Unknown
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b6: Expected O, but got Unknown
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Expected O, but got Unknown
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f7: Expected O, but got Unknown
			//IL_032a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Expected O, but got Unknown
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0348: Expected O, but got Unknown
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_0370: Expected O, but got Unknown
			//IL_03b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0403: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: Expected O, but got Unknown
			//IL_0417: Unknown result type (might be due to invalid IL or missing references)
			//IL_0421: Expected O, but got Unknown
			//IL_043f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0449: Expected O, but got Unknown
			//IL_0492: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e6: Expected O, but got Unknown
			//IL_04f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fa: Expected O, but got Unknown
			int num = ((ConfigCornerRadius != null) ? ConfigCornerRadius.Value : 8);
			int num2 = Mathf.Clamp(num - 2, 2, 8);
			Color color = (Color)((ConfigBgColor != null) ? ConfigBgColor.Value : new Color(0.1f, 0.1f, 0.12f, 0.95f));
			Color val = (Color)((ConfigBubbleColor != null) ? ConfigBubbleColor.Value : new Color(0.18f, 0.18f, 0.22f, 0.9f));
			if ((Object)(object)windowBgTexture == (Object)null)
			{
				windowBgTexture = CreateRoundedTexture(64, 64, color, num);
			}
			if ((Object)(object)bubbleBgTexture == (Object)null)
			{
				bubbleBgTexture = CreateRoundedTexture(64, 64, val, Mathf.Max(2, num - 2));
			}
			if ((Object)(object)buttonNormalTexture == (Object)null)
			{
				buttonNormalTexture = CreateRoundedTexture(64, 32, new Color(val.r * 1.15f, val.g * 1.15f, val.b * 1.15f, 0.95f), num2);
			}
			if ((Object)(object)buttonHoverTexture == (Object)null)
			{
				buttonHoverTexture = CreateRoundedTexture(64, 32, new Color(val.r * 1.45f, val.g * 1.45f, val.b * 1.45f, 1f), num2);
			}
			if ((Object)(object)buttonActiveTexture == (Object)null)
			{
				buttonActiveTexture = CreateRoundedTexture(64, 32, new Color(val.r * 0.85f, val.g * 0.85f, val.b * 0.85f, 1f), num2);
			}
			if ((Object)(object)tabNormalTexture == (Object)null)
			{
				tabNormalTexture = CreateRoundedTexture(64, 32, new Color(val.r * 1.6f, val.g * 1.6f, val.b * 1.6f, 0.95f), num);
			}
			if ((Object)(object)tabHoverTexture == (Object)null)
			{
				tabHoverTexture = CreateRoundedTexture(64, 32, new Color(val.r * 2f, val.g * 2f, val.b * 2f, 1f), num);
			}
			if (windowStyle == null)
			{
				windowStyle = new GUIStyle(GUI.skin.window);
				windowStyle.normal.background = windowBgTexture;
				windowStyle.onNormal.background = windowBgTexture;
				windowStyle.padding = new RectOffset(10, 10, 24, 10);
				windowStyle.border = new RectOffset(num + 2, num + 2, num + 2, num + 2);
			}
			if (bubbleStyle == null)
			{
				bubbleStyle = new GUIStyle(GUI.skin.box);
				bubbleStyle.normal.background = bubbleBgTexture;
				bubbleStyle.onNormal.background = bubbleBgTexture;
				bubbleStyle.padding = new RectOffset(8, 8, 6, 6);
				bubbleStyle.border = new RectOffset(num, num, num, num);
			}
			if (flatButtonStyle == null)
			{
				flatButtonStyle = new GUIStyle(GUI.skin.button);
				flatButtonStyle.normal.background = buttonNormalTexture;
				flatButtonStyle.hover.background = buttonHoverTexture;
				flatButtonStyle.active.background = buttonActiveTexture;
				flatButtonStyle.normal.textColor = Color.white;
				flatButtonStyle.hover.textColor = Color.white;
				flatButtonStyle.active.textColor = Color.white;
				flatButtonStyle.alignment = (TextAnchor)4;
				flatButtonStyle.border = new RectOffset(num2, num2, num2, num2);
				flatButtonStyle.padding = new RectOffset(6, 6, 1, 1);
			}
			if (tabButtonStyle == null)
			{
				tabButtonStyle = new GUIStyle(GUI.skin.button);
				tabButtonStyle.normal.background = tabNormalTexture;
				tabButtonStyle.hover.background = tabHoverTexture;
				tabButtonStyle.active.background = tabHoverTexture;
				tabButtonStyle.normal.textColor = Color.white;
				tabButtonStyle.hover.textColor = Color.white;
				tabButtonStyle.active.textColor = Color.white;
				tabButtonStyle.alignment = (TextAnchor)4;
				tabButtonStyle.border = new RectOffset(num, num, num, num);
				tabButtonStyle.padding = new RectOffset(8, 8, 2, 2);
			}
		}

		private Texture2D CreateRoundedTexture(int width, int height, Color color, int radius)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(width, height, (TextureFormat)4, false);
			Color[] array = (Color[])(object)new Color[width * height];
			for (int i = 0; i < height; i++)
			{
				for (int j = 0; j < width; j++)
				{
					float num = 0f;
					if (j < radius && i < radius)
					{
						num = Vector2.Distance(new Vector2((float)j, (float)i), new Vector2((float)radius, (float)radius));
					}
					else if (j >= width - radius && i < radius)
					{
						num = Vector2.Distance(new Vector2((float)j, (float)i), new Vector2((float)(width - radius - 1), (float)radius));
					}
					else if (j < radius && i >= height - radius)
					{
						num = Vector2.Distance(new Vector2((float)j, (float)i), new Vector2((float)radius, (float)(height - radius - 1)));
					}
					else if (j >= width - radius && i >= height - radius)
					{
						num = Vector2.Distance(new Vector2((float)j, (float)i), new Vector2((float)(width - radius - 1), (float)(height - radius - 1)));
					}
					if (num > (float)radius)
					{
						array[i * width + j] = Color.clear;
					}
					else if (num > (float)radius - 1f)
					{
						array[i * width + j] = new Color(color.r, color.g, color.b, color.a * (1f - (num - ((float)radius - 1f))));
					}
					else
					{
						array[i * width + j] = color;
					}
				}
			}
			val.SetPixels(array);
			val.Apply();
			return val;
		}

		private void RebuildUIStyles()
		{
			if ((Object)(object)windowBgTexture != (Object)null)
			{
				Object.Destroy((Object)(object)windowBgTexture);
			}
			if ((Object)(object)bubbleBgTexture != (Object)null)
			{
				Object.Destroy((Object)(object)bubbleBgTexture);
			}
			if ((Object)(object)buttonNormalTexture != (Object)null)
			{
				Object.Destroy((Object)(object)buttonNormalTexture);
			}
			if ((Object)(object)buttonHoverTexture != (Object)null)
			{
				Object.Destroy((Object)(object)buttonHoverTexture);
			}
			if ((Object)(object)buttonActiveTexture != (Object)null)
			{
				Object.Destroy((Object)(object)buttonActiveTexture);
			}
			if ((Object)(object)tabNormalTexture != (Object)null)
			{
				Object.Destroy((Object)(object)tabNormalTexture);
			}
			if ((Object)(object)tabHoverTexture != (Object)null)
			{
				Object.Destroy((Object)(object)tabHoverTexture);
			}
			windowBgTexture = null;
			bubbleBgTexture = null;
			buttonNormalTexture = null;
			buttonHoverTexture = null;
			buttonActiveTexture = null;
			tabNormalTexture = null;
			tabHoverTexture = null;
			windowStyle = null;
			bubbleStyle = null;
			flatButtonStyle = null;
			tabButtonStyle = null;
		}

		private bool DrawCustomToggle(bool value, string label)
		{
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			bool result = GUILayout.Toggle(value, "", (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(20f),
				GUILayout.Height(20f)
			});
			if (GUILayout.Button(label, GUI.skin.label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(20f) }))
			{
				result = !value;
			}
			GUILayout.EndHorizontal();
			return result;
		}

		private void DrawWindowContent(int windowID)
		{
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			if (GUILayout.Button("Lobby Watch", tabButtonStyle ?? GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) }))
			{
				currentPage = 0;
			}
			if (GUILayout.Button("Color & Settings", tabButtonStyle ?? GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) }))
			{
				currentPage = 1;
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(6f);
			if (currentPage == 0)
			{
				DrawLobbyPage();
			}
			else if (currentPage == 1)
			{
				DrawSettingsPage();
			}
			GUI.DragWindow();
		}

		private void DrawLobbyPage()
		{
			//IL_0227: Un