Decompiled source of PerfectAnti v1.1.0

plugins/Marioalexsan.PerfectGuard.dll

Decompiled 8 months 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 System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Marioalexsan.PerfectGuard")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+5d35e5ecf46843db90d9219fbaebfe5ad12611f0")]
[assembly: AssemblyProduct("PerfectGuard")]
[assembly: AssemblyTitle("Marioalexsan.PerfectGuard")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Marioalexsan.PerfectGuard
{
	public enum SuspicionLevel
	{
		Normal,
		Suspicious,
		Confirmed
	}
	internal class AbuseDetector
	{
		private struct TrackedData
		{
			public DateTime TimeSinceLastEvent;

			public TimeSpan TimeBetweenEventsEMA;

			public SuspicionLevel Suspicion;
		}

		private readonly Dictionary<Player, TrackedData> PlayerData = new Dictionary<Player, TrackedData>();

		public static List<AbuseDetector> AllDetectors { get; } = new List<AbuseDetector>();


		public double SupicionRate { get; }

		public double ConfirmRate { get; }

		public double Factor { get; } = 0.3;


		public TimeSpan SuspicionTimeBetweenEvents { get; }

		public TimeSpan ConfirmTimeBetweenEvents { get; }

		public event EventHandler<Player>? OnSuspicionRaised;

		public event EventHandler<Player>? OnConfirmRaised;

		public static void RunActorCleanup()
		{
			foreach (AbuseDetector allDetector in AllDetectors)
			{
				Dictionary<Player, TrackedData> playerData = allDetector.PlayerData;
				KeyValuePair<Player, TrackedData>[] array = playerData.ToArray();
				for (int i = 0; i < array.Length; i++)
				{
					KeyValuePair<Player, TrackedData> keyValuePair = array[i];
					if (!Object.op_Implicit((Object)(object)keyValuePair.Key))
					{
						playerData.Remove(keyValuePair.Key);
					}
				}
			}
		}

		public AbuseDetector(double suspicionRate)
			: this(suspicionRate, suspicionRate * 4.0)
		{
		}

		public AbuseDetector(double suspicionRate, double confirmRate)
		{
			SupicionRate = suspicionRate;
			ConfirmRate = Math.Max(confirmRate, suspicionRate);
			SuspicionTimeBetweenEvents = TimeSpan.FromSeconds(1.0 / SupicionRate);
			ConfirmTimeBetweenEvents = TimeSpan.FromSeconds(1.0 / ConfirmRate);
			AllDetectors.Add(this);
		}

		public bool TrackIfServerAndCheckBehaviour(Player player)
		{
			return NetworkServer.active ? TrackEventAndCheckBehaviour(player) : CheckBehaviour(player);
		}

		public bool CheckBehaviour(Player player)
		{
			TrackedData value;
			return !PlayerData.TryGetValue(player, out value) || value.Suspicion == SuspicionLevel.Normal;
		}

		public bool TrackEventAndCheckBehaviour(Player player)
		{
			DateTime now = DateTime.Now;
			if (!PlayerData.TryGetValue(player, out var value))
			{
				PlayerData[player] = new TrackedData
				{
					TimeSinceLastEvent = now,
					TimeBetweenEventsEMA = SuspicionTimeBetweenEvents * 2.0,
					Suspicion = SuspicionLevel.Normal
				};
				return true;
			}
			TimeSpan timeSpan = now - value.TimeSinceLastEvent;
			TimeSpan timeSpan2 = Factor * timeSpan + (1.0 - Factor) * value.TimeBetweenEventsEMA;
			value.TimeSinceLastEvent = now;
			value.TimeBetweenEventsEMA = timeSpan2;
			SuspicionLevel suspicionLevel = SuspicionLevel.Normal;
			if (timeSpan2 <= ConfirmTimeBetweenEvents)
			{
				suspicionLevel = SuspicionLevel.Confirmed;
				if (suspicionLevel > value.Suspicion)
				{
					this.OnConfirmRaised?.Invoke(this, player);
				}
			}
			else if (timeSpan2 <= SuspicionTimeBetweenEvents)
			{
				suspicionLevel = SuspicionLevel.Suspicious;
				if (suspicionLevel > value.Suspicion)
				{
					this.OnSuspicionRaised?.Invoke(this, player);
				}
			}
			Dictionary<Player, TrackedData> playerData = PlayerData;
			TrackedData value2 = value;
			value2.Suspicion = suspicionLevel;
			playerData[player] = value2;
			return suspicionLevel == SuspicionLevel.Normal;
		}
	}
	public static class NetItemObjectManager
	{
		public static readonly List<Net_ItemObject> AllItemObjects = new List<Net_ItemObject>();

		public static int Count => AllItemObjects.Count;
	}
	public class NetItemObjectTracker : NetworkBehaviour
	{
		private Net_ItemObject _itemObject;

		public void Awake()
		{
			_itemObject = ((Component)this).GetComponent<Net_ItemObject>();
		}

		public override void OnStartClient()
		{
			((NetworkBehaviour)this).OnStartClient();
			if ((Object)(object)_itemObject != (Object)null && !NetItemObjectManager.AllItemObjects.Contains(_itemObject))
			{
				NetItemObjectManager.AllItemObjects.Add(_itemObject);
			}
		}

		public override void OnStopClient()
		{
			((NetworkBehaviour)this).OnStopClient();
			if ((Object)(object)_itemObject != (Object)null)
			{
				NetItemObjectManager.AllItemObjects.Remove(_itemObject);
			}
		}
	}
	[BepInPlugin("Marioalexsan.PerfectGuard", "PerfectGuard", "1.1.0")]
	public class PerfectGuard : BaseUnityPlugin
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static NetworkMessageDelegate <0>__ThrottledMessageHandler;
		}

		[CompilerGenerated]
		private sealed class <EngagePanicCleanupCoroutine>d__71 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			private List<Net_ItemObject> <allItems>5__1;

			private int <i>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <EngagePanicCleanupCoroutine>d__71(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<allItems>5__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					_isPanicModeActive = true;
					<allItems>5__1 = NetItemObjectManager.AllItemObjects.ToList();
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					Logger.LogMessage((object)$"[Panic] Beginning staggered destruction of {<allItems>5__1.Count} items.");
					<i>5__2 = 0;
					goto IL_00f9;
				case 2:
					{
						<>1__state = -1;
						goto IL_00e8;
					}
					IL_00f9:
					if (<i>5__2 < <allItems>5__1.Count)
					{
						if ((Object)(object)<allItems>5__1[<i>5__2] != (Object)null)
						{
							Object.Destroy((Object)(object)((Component)<allItems>5__1[<i>5__2]).gameObject);
						}
						if (<i>5__2 % 50 == 0)
						{
							<>2__current = null;
							<>1__state = 2;
							return true;
						}
						goto IL_00e8;
					}
					Logger.LogMessage((object)"[Panic] Item cleanup complete.");
					_isPanicModeActive = false;
					_lastNetworkObjectCount = NetItemObjectManager.Count;
					return false;
					IL_00e8:
					<i>5__2++;
					goto IL_00f9;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <PeriodicChecksCoroutine>d__62 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public PerfectGuard <>4__this;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <PeriodicChecksCoroutine>d__62(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: Expected O, but got Unknown
				//IL_0098: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a2: Expected O, but got Unknown
				//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ee: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSeconds(3f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					Logger.LogInfo((object)"Periodic (slow) checks started.");
					if (NetworkClient.isConnected)
					{
						_lastNetworkObjectCount = NetItemObjectManager.Count;
					}
					break;
				case 2:
					<>1__state = -1;
					break;
				case 3:
					<>1__state = -1;
					break;
				}
				if (!EnableMasterSwitch.Value)
				{
					<>2__current = (object)new WaitForSeconds(5f);
					<>1__state = 2;
					return true;
				}
				if (EnableServerHealthCheck.Value && NetworkClient.isConnected)
				{
					PerformLatencyCheck();
				}
				<>4__this.CleanupDeadAudioSources();
				<>2__current = (object)new WaitForSeconds(2f);
				<>1__state = 3;
				return true;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <ResetMessageCountersCoroutine>d__68 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ResetMessageCountersCoroutine>d__68(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					break;
				case 1:
					<>1__state = -1;
					RpcMessageCounts.Clear();
					break;
				}
				<>2__current = (object)new WaitForSeconds(0.25f);
				<>1__state = 1;
				return true;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		internal static ManualLogSource Logger = null;

		private readonly Harmony _harmony = new Harmony("Marioalexsan.PerfectGuard");

		private ConfigEntry<KeyCode> _windowKey;

		private Rect _windowRect;

		private bool _windowShown;

		private TimeSpan _abuseDetectorCleanupAccumulator;

		private const float AudioSpamCooldownSeconds = 0.1f;

		private static readonly Dictionary<AudioSource, float> AudioCooldowns = new Dictionary<AudioSource, float>();

		private static readonly List<AudioSource> DeadAudioSources = new List<AudioSource>();

		private static bool _isShieldActive;

		private static Coroutine _shieldResetCoroutine;

		private const int RpcSpamThreshold = 40;

		private const float ShieldResetInterval = 0.25f;

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

		private static readonly Dictionary<ushort, NetworkMessageDelegate> OriginalHandlers = new Dictionary<ushort, NetworkMessageDelegate>();

		private static readonly HashSet<ushort> RpcWhitelist = new HashSet<ushort> { 63450, 15647, 28859 };

		private Coroutine _periodicChecksCoroutine;

		private static int _lastNetworkObjectCount;

		private static bool _isPanicModeActive;

		private const float StuckThresholdSeconds = 25f;

		private const float HighLatencyThreshold = 300f;

		private static float _previousLatency;

		private static float _stuckDuration;

		private static bool _serverStuckReportSent;

		public static ConfigEntry<bool> EnableMasterSwitch { get; private set; }

		public static ConfigEntry<bool> EnableGlobalRpcShield { get; private set; }

		public static ConfigEntry<bool> EnableObjectSpikeProtection { get; private set; }

		public static ConfigEntry<bool> EnableAudioSpamProtection { get; private set; }

		public static ConfigEntry<bool> EnableServerHealthCheck { get; private set; }

		public static ConfigEntry<bool> EnableDetailedLogging { get; private set; }

		public static ConfigEntry<int> NetworkObjectSpikeThreshold { get; private set; }

		public static string DeadServerStatus { get; private set; } = "N/A";


		public PerfectGuard()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			_windowKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("1. General", "WindowKey", (KeyCode)290, "Key to open the configuration window.");
			EnableMasterSwitch = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "MasterSwitch", true, "Enable or disable all protections globally.");
			EnableDetailedLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "DetailedLogging", true, "Log detailed warnings for detected malicious activities.");
			EnableGlobalRpcShield = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Protections", "GlobalRpcShield", true, "Enables a low-level network shield to block all types of excessive RPC spam.");
			EnableObjectSpikeProtection = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Protections", "ObjectSpikeProtection", true, "Automatically detects and cleans up mass item drop spam.");
			EnableAudioSpamProtection = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Protections", "AudioSpamProtection", true, "Prevents crashes from excessive audio source plays.");
			EnableServerHealthCheck = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Protections", "ServerHealthCheck", true, "Monitors server latency to detect high lag or server freezes.");
			NetworkObjectSpikeThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("3. Tuning", "ObjectSpikeThreshold", 150, "The number of new items created in a single frame required to trigger a panic cleanup.");
		}

		public void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			_harmony.PatchAll();
			Logger.LogMessage((object)"PerfectGuard v1.1.0 has loaded!");
			EnableMasterSwitch.SettingChanged += delegate
			{
				ToggleSystems();
			};
			ToggleSystems();
		}

		private void ToggleSystems()
		{
			if (EnableMasterSwitch.Value)
			{
				Logger.LogMessage((object)"PerfectGuard is now ACTIVE.");
				if (_periodicChecksCoroutine == null)
				{
					_periodicChecksCoroutine = ((MonoBehaviour)this).StartCoroutine(PeriodicChecksCoroutine());
				}
				if (EnableGlobalRpcShield.Value)
				{
					InitializeShield();
				}
			}
			else
			{
				Logger.LogWarning((object)"PerfectGuard is now INACTIVE.");
				if (_periodicChecksCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_periodicChecksCoroutine);
					_periodicChecksCoroutine = null;
				}
				ShutdownShield();
			}
		}

		public void Update()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(_windowKey.Value))
			{
				_windowShown = !_windowShown;
			}
			if (EnableMasterSwitch.Value && EnableObjectSpikeProtection.Value && !_isPanicModeActive && NetworkClient.isConnected)
			{
				CheckForObjectSpikes();
			}
			_abuseDetectorCleanupAccumulator += TimeSpan.FromSeconds(Time.deltaTime);
			if (_abuseDetectorCleanupAccumulator >= TimeSpan.FromSeconds(60.0))
			{
				_abuseDetectorCleanupAccumulator = TimeSpan.Zero;
				AbuseDetector.RunActorCleanup();
			}
		}

		public void OnGUI()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (_windowShown)
			{
				_windowRect = GUILayout.Window(GUIUtility.GetControlID((FocusType)2), new Rect((float)Screen.width * 0.1f, (float)Screen.height * 0.1f, (float)Screen.width * 0.3f, (float)Screen.height * 0.5f), new WindowFunction(DrawWindow), "PerfectGuard v1.1.0", Array.Empty<GUILayoutOption>());
			}
		}

		private void DrawWindow(int windowID)
		{
			EnableMasterSwitch.Value = GUILayout.Toggle(EnableMasterSwitch.Value, "Enable All Protections (Master Switch)", Array.Empty<GUILayoutOption>());
			GUILayout.Space(10f);
			EnableGlobalRpcShield.Value = GUILayout.Toggle(EnableGlobalRpcShield.Value, "Global RPC Spam Shield", Array.Empty<GUILayoutOption>());
			EnableObjectSpikeProtection.Value = GUILayout.Toggle(EnableObjectSpikeProtection.Value, "Item Drop Spike Protection", Array.Empty<GUILayoutOption>());
			EnableAudioSpamProtection.Value = GUILayout.Toggle(EnableAudioSpamProtection.Value, "Audio Spam Protection", Array.Empty<GUILayoutOption>());
			EnableServerHealthCheck.Value = GUILayout.Toggle(EnableServerHealthCheck.Value, "Server Health/Lag Check", Array.Empty<GUILayoutOption>());
			GUILayout.Space(10f);
			EnableDetailedLogging.Value = GUILayout.Toggle(EnableDetailedLogging.Value, "Enable Detailed Logging", Array.Empty<GUILayoutOption>());
			GUILayout.FlexibleSpace();
			GUILayout.Label("Server Status: " + DeadServerStatus, Array.Empty<GUILayoutOption>());
			GUI.DragWindow();
		}

		[IteratorStateMachine(typeof(<PeriodicChecksCoroutine>d__62))]
		private IEnumerator PeriodicChecksCoroutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <PeriodicChecksCoroutine>d__62(0)
			{
				<>4__this = this
			};
		}

		internal static bool CheckAudioCooldown(AudioSource instance)
		{
			if (!EnableMasterSwitch.Value || !EnableAudioSpamProtection.Value || (Object)(object)instance == (Object)null)
			{
				return true;
			}
			if (!((Component)instance).gameObject.activeInHierarchy)
			{
				return false;
			}
			lock (AudioCooldowns)
			{
				if (AudioCooldowns.TryGetValue(instance, out var value) && Time.time < value)
				{
					return false;
				}
				AudioCooldowns[instance] = Time.time + 0.1f;
			}
			return true;
		}

		private void CleanupDeadAudioSources()
		{
			lock (AudioCooldowns)
			{
				DeadAudioSources.Clear();
				DeadAudioSources.AddRange(AudioCooldowns.Keys.Where((AudioSource audioSource) => (Object)(object)audioSource == (Object)null));
				foreach (AudioSource deadAudioSource in DeadAudioSources)
				{
					AudioCooldowns.Remove(deadAudioSource);
				}
			}
		}

		private void InitializeShield()
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			if (_isShieldActive || !NetworkClient.active)
			{
				return;
			}
			try
			{
				FieldInfo field = typeof(NetworkClient).GetField("handlers", BindingFlags.Static | BindingFlags.NonPublic);
				if (field == null)
				{
					Logger.LogError((object)"[Shield] Could not find NetworkClient.handlers field!");
					return;
				}
				Dictionary<ushort, NetworkMessageDelegate> dictionary = field.GetValue(null) as Dictionary<ushort, NetworkMessageDelegate>;
				ushort id = NetworkMessageId<RpcMessage>.Id;
				if (dictionary != null && dictionary.TryGetValue(id, out var value))
				{
					OriginalHandlers[id] = value;
					object obj = <>O.<0>__ThrottledMessageHandler;
					if (obj == null)
					{
						NetworkMessageDelegate val = ThrottledMessageHandler;
						<>O.<0>__ThrottledMessageHandler = val;
						obj = (object)val;
					}
					dictionary[id] = (NetworkMessageDelegate)obj;
					if (_shieldResetCoroutine != null)
					{
						((MonoBehaviour)this).StopCoroutine(_shieldResetCoroutine);
					}
					_shieldResetCoroutine = ((MonoBehaviour)this).StartCoroutine(ResetMessageCountersCoroutine());
					_isShieldActive = true;
					Logger.LogMessage((object)"[Shield] Global RPC Network Shield is ACTIVE.");
				}
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"[Shield] Failed to initialize Network Shield: {arg}");
			}
		}

		private void ShutdownShield()
		{
			if (!_isShieldActive)
			{
				return;
			}
			try
			{
				Dictionary<ushort, NetworkMessageDelegate> dictionary = typeof(NetworkClient).GetField("handlers", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as Dictionary<ushort, NetworkMessageDelegate>;
				ushort id = NetworkMessageId<RpcMessage>.Id;
				if (dictionary != null && OriginalHandlers.TryGetValue(id, out NetworkMessageDelegate value))
				{
					dictionary[id] = value;
				}
				if (_shieldResetCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(_shieldResetCoroutine);
				}
				_shieldResetCoroutine = null;
				OriginalHandlers.Clear();
				RpcMessageCounts.Clear();
				_isShieldActive = false;
				Logger.LogMessage((object)"[Shield] Global RPC Network Shield is INACTIVE.");
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"[Shield] Failed to shutdown Network Shield: {arg}");
			}
		}

		private static void ThrottledMessageHandler(NetworkConnection conn, NetworkReader reader, int channelId)
		{
			if (!OriginalHandlers.TryGetValue(NetworkMessageId<RpcMessage>.Id, out NetworkMessageDelegate value))
			{
				return;
			}
			int position = reader.Position;
			if (reader.Remaining < 7)
			{
				value.Invoke(conn, reader, channelId);
				return;
			}
			uint num = NetworkReaderExtensions.ReadUInt(reader);
			reader.ReadByte();
			ushort num2 = NetworkReaderExtensions.ReadUShort(reader);
			reader.Position = position;
			if (RpcWhitelist.Contains(num2))
			{
				value.Invoke(conn, reader, channelId);
				return;
			}
			RpcMessageCounts.TryGetValue(num2, out var value2);
			value2++;
			RpcMessageCounts[num2] = value2;
			if (value2 > 40)
			{
				if (value2 == 41 && EnableDetailedLogging.Value)
				{
					string arg = $"netId:{num}";
					if (NetworkClient.spawned.TryGetValue(num, out var value3) && (Object)(object)value3 != (Object)null)
					{
						Player componentInParent = ((Component)value3).GetComponentInParent<Player>();
						arg = (((Object)(object)componentInParent != (Object)null) ? componentInParent._nickname : ((Object)value3).name);
					}
					Logger.LogError((object)$"[Shield] RPC SPAM DETECTED! Blocking excessive calls (Hash: {num2}) from sender: {arg}.");
				}
			}
			else
			{
				value.Invoke(conn, reader, channelId);
			}
		}

		[IteratorStateMachine(typeof(<ResetMessageCountersCoroutine>d__68))]
		private static IEnumerator ResetMessageCountersCoroutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ResetMessageCountersCoroutine>d__68(0);
		}

		private void CheckForObjectSpikes()
		{
			int count = NetItemObjectManager.Count;
			int num = count - _lastNetworkObjectCount;
			if (num > NetworkObjectSpikeThreshold.Value)
			{
				Logger.LogError((object)$"[Panic] Item Spike Detected! {num} new items in a single frame. Engaging Panic Cleanup.");
				NeutralizeAllItemsImmediately();
				((MonoBehaviour)this).StartCoroutine(EngagePanicCleanupCoroutine());
			}
			_lastNetworkObjectCount = count;
		}

		private static void NeutralizeAllItemsImmediately()
		{
			List<Net_ItemObject> list = NetItemObjectManager.AllItemObjects.ToList();
			if (EnableDetailedLogging.Value)
			{
				Logger.LogMessage((object)$"[Panic] Neutralizing {list.Count} items immediately.");
			}
			Renderer val = default(Renderer);
			Collider val2 = default(Collider);
			foreach (Net_ItemObject item in list)
			{
				if (!((Object)(object)item == (Object)null) && !((Object)(object)((Component)item).gameObject == (Object)null))
				{
					if (((Component)item).TryGetComponent<Renderer>(ref val))
					{
						val.enabled = false;
					}
					if (((Component)item).TryGetComponent<Collider>(ref val2))
					{
						val2.enabled = false;
					}
				}
			}
		}

		[IteratorStateMachine(typeof(<EngagePanicCleanupCoroutine>d__71))]
		private static IEnumerator EngagePanicCleanupCoroutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <EngagePanicCleanupCoroutine>d__71(0);
		}

		private static void PerformLatencyCheck()
		{
			Player mainPlayer = Player._mainPlayer;
			if ((Object)(object)mainPlayer == (Object)null)
			{
				return;
			}
			if (mainPlayer.Network_isHostPlayer)
			{
				DeadServerStatus = "<color=cyan>Host</color>";
				return;
			}
			float num = mainPlayer.Network_latency;
			if (Mathf.Approximately(num, _previousLatency))
			{
				_stuckDuration += 2f;
				if (_stuckDuration >= 25f && !_serverStuckReportSent)
				{
					_serverStuckReportSent = true;
					DeadServerStatus = "<color=red>Frozen</color>";
					Logger.LogError((object)"[HealthCheck] Server is unresponsive (latency is frozen).");
				}
			}
			else
			{
				if (_serverStuckReportSent)
				{
					Logger.LogMessage((object)"[HealthCheck] Server has recovered and is responsive again.");
				}
				_stuckDuration = 0f;
				_serverStuckReportSent = false;
			}
			if (!_serverStuckReportSent)
			{
				DeadServerStatus = ((num > 300f) ? $"<color=orange>Lagging ({num}ms)</color>" : $"<color=#2bff00>Stable ({num}ms)</color>");
			}
			_previousLatency = num;
		}
	}
	internal static class ModInfo
	{
		public const string GUID = "Marioalexsan.PerfectGuard";

		public const string NAME = "PerfectGuard";

		public const string VERSION = "1.1.0";
	}
}
namespace Marioalexsan.PerfectGuard.Patches
{
	[HarmonyPatch]
	internal static class CommandRateLimiter
	{
		private static readonly AbuseDetector DropCommandDetector;

		static CommandRateLimiter()
		{
			DropCommandDetector = new AbuseDetector(5.0);
			DropCommandDetector.OnConfirmRaised += delegate(object sender, Player player)
			{
				if (PerfectGuard.EnableDetailedLogging.Value)
				{
					PerfectGuard.Logger.LogWarning((object)("Player " + ((Object)player).name + " is spamming the drop item command! Blocking requests."));
				}
			};
		}

		[HarmonyPatch(typeof(PlayerInventory), "UserCode_Cmd_DropItem__ItemData__Int32")]
		[HarmonyPrefix]
		private static bool RateLimitDropCommand(PlayerInventory __instance)
		{
			if (!NetworkServer.active)
			{
				return true;
			}
			return DropCommandDetector.TrackIfServerAndCheckBehaviour(__instance._player);
		}
	}
	[HarmonyPatch]
	public class NetItemObjectTrackerInjector
	{
		[HarmonyPatch(typeof(Net_ItemObject), "Awake")]
		[HarmonyPostfix]
		private static void InjectTracker(Net_ItemObject __instance)
		{
			if ((Object)(object)((Component)__instance).GetComponent<NetItemObjectTracker>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<NetItemObjectTracker>();
			}
		}
	}
	[HarmonyPatch]
	internal static class AudioSpamPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { })]
		public static bool AudioSource_Play_Prefix(AudioSource __instance)
		{
			return PerfectGuard.CheckAudioCooldown(__instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[]
		{
			typeof(AudioClip),
			typeof(float)
		})]
		public static bool AudioSource_PlayOneShot_Prefix(AudioSource __instance)
		{
			return PerfectGuard.CheckAudioCooldown(__instance);
		}
	}
	[HarmonyPatch]
	internal static class EffectSpamPatches
	{
		private static readonly AbuseDetector TeleportDetector;

		private static readonly AbuseDetector SparkleDetector;

		private static readonly AbuseDetector PoofDetector;

		private static readonly AbuseDetector JumpDetector;

		static EffectSpamPatches()
		{
			TeleportDetector = new AbuseDetector(4.0);
			SparkleDetector = new AbuseDetector(4.0);
			PoofDetector = new AbuseDetector(4.0);
			JumpDetector = new AbuseDetector(4.0);
			TeleportDetector.OnConfirmRaised += delegate(object s, Player p)
			{
				LogConfirmation(p, "teleport effects");
			};
			SparkleDetector.OnConfirmRaised += delegate(object s, Player p)
			{
				LogConfirmation(p, "vanity sparkle effects");
			};
			PoofDetector.OnConfirmRaised += delegate(object s, Player p)
			{
				LogConfirmation(p, "poof smoke effects");
			};
			JumpDetector.OnConfirmRaised += delegate(object s, Player p)
			{
				LogConfirmation(p, "jump attack effects");
			};
		}

		private static void LogConfirmation(Player player, string effectName)
		{
			if (PerfectGuard.EnableDetailedLogging.Value)
			{
				PerfectGuard.Logger.LogWarning((object)("Player " + ((Object)player).name + " is malicious! Trying to crash the server using " + effectName + "."));
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerVisual), "Cmd_PlayTeleportEffect")]
		private static bool RateLimitTeleportCommand(PlayerVisual __instance)
		{
			return TeleportDetector.TrackIfServerAndCheckBehaviour(__instance._player);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerVisual), "Rpc_PlayTeleportEffect")]
		private static bool CheckServerTeleport(PlayerVisual __instance)
		{
			return TeleportDetector.CheckBehaviour(__instance._player);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerVisual), "Cmd_VanitySparkleEffect")]
		private static bool RateLimitSparkleCommand(PlayerVisual __instance)
		{
			return SparkleDetector.TrackIfServerAndCheckBehaviour(__instance._player);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerVisual), "Rpc_VanitySparkleEffect")]
		private static bool CheckServerSparkle(PlayerVisual __instance)
		{
			return SparkleDetector.CheckBehaviour(__instance._player);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerVisual), "Cmd_PoofSmokeEffect")]
		private static bool RateLimitPoofCommand(PlayerVisual __instance)
		{
			return PoofDetector.TrackIfServerAndCheckBehaviour(__instance._player);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerVisual), "Rpc_PoofSmokeEffect")]
		private static bool CheckServerPoof(PlayerVisual __instance)
		{
			return PoofDetector.CheckBehaviour(__instance._player);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerVisual), "Cmd_JumpAttackEffect")]
		private static bool RateLimitJumpCommand(PlayerVisual __instance)
		{
			return JumpDetector.TrackIfServerAndCheckBehaviour(__instance._player);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerVisual), "Rpc_JumpAttackEffect")]
		private static bool CheckServerJump(PlayerVisual __instance)
		{
			return JumpDetector.CheckBehaviour(__instance._player);
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}