Decompiled source of PerfectGuard v1.1.0

plugins/Marioalexsan.PerfectGuard.dll

Decompiled a month ago
using System;
using System.Collections.Concurrent;
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 System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Marioalexsan.PerfectGuard.Patches;
using Marioalexsan.PerfectGuard.SoftDependencies;
using Microsoft.CodeAnalysis;
using Mirror;
using Mirror.RemoteCalls;
using Nessie.ATLYSS.EasySettings;
using Nessie.ATLYSS.EasySettings.UIElements;
using UnityEngine;
using UnityEngine.Events;

[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: IgnoresAccessChecksTo("Mirror")]
[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+dfed091d3d87b00e40690fc3ce3b908627b46efa")]
[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 AbuseDetectorEMA
	{
		private struct TrackedData
		{
			public DateTime TimeSinceLastEvent;

			public TimeSpan TimeBetweenEventsEMA;

			public SuspicionLevel Suspicion;
		}

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

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


		public double SupicionRate { get; }

		public double ConfirmRate { get; }

		public double Factor { get; } = 0.3;


		public TimeSpan SuspicionTimeBetweenEvents { get; }

		public TimeSpan ConfirmTimeBetweenEvents { get; }

		public SuspicionLevel Suspicion { get; private set; } = SuspicionLevel.Normal;


		public event EventHandler<NetworkBehaviour>? OnSuspicionRaised;

		public event EventHandler<NetworkBehaviour>? OnConfirmRaised;

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

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

		public AbuseDetectorEMA(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 void TrackEvent(NetworkBehaviour 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;
			}
			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<NetworkBehaviour, TrackedData> playerData = PlayerData;
			TrackedData value2 = value;
			value2.Suspicion = suspicionLevel;
			playerData[player] = value2;
			Suspicion = suspicionLevel;
		}
	}
	internal class AbuseDetectorTokenBucket
	{
		public struct RateLimitData
		{
			public bool IsRateLimited;

			public DateTime PreviousRateLimitAt;
		}

		private struct TrackedData
		{
			public DateTime LastEventTime;

			public DateTime LastRateLimitedTime;

			public double Tokens;
		}

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

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


		public double RateLimit { get; }

		public double BurstLimit { get; }

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

		public AbuseDetectorTokenBucket(double rateLimit)
			: this(rateLimit, rateLimit * 4.0)
		{
		}

		public AbuseDetectorTokenBucket(double rateLimit, double burstLimit)
		{
			RateLimit = rateLimit;
			BurstLimit = Math.Max(burstLimit, rateLimit);
			if (burstLimit <= rateLimit)
			{
				Logging.LogWarning("A token bucket configuration is incorrect!");
			}
			AllDetectors.Add(this);
		}

		public RateLimitData TrackEvent(NetworkBehaviour player)
		{
			DateTime now = DateTime.Now;
			if (!PlayerData.TryGetValue(player, out var value))
			{
				value = (PlayerData[player] = new TrackedData
				{
					LastEventTime = now,
					LastRateLimitedTime = DateTime.UnixEpoch,
					Tokens = BurstLimit
				});
			}
			double num = Math.Max((now - value.LastEventTime).TotalSeconds, 0.0);
			value.LastEventTime = now;
			value.Tokens = Math.Clamp(value.Tokens + RateLimit * num, 0.0, BurstLimit);
			bool isRateLimited = true;
			DateTime lastRateLimitedTime = value.LastRateLimitedTime;
			if (value.Tokens >= 1.0)
			{
				value.Tokens -= 1.0;
				isRateLimited = false;
			}
			else
			{
				value.LastRateLimitedTime = now;
			}
			PlayerData[player] = value;
			RateLimitData result = default(RateLimitData);
			result.IsRateLimited = isRateLimited;
			result.PreviousRateLimitAt = lastRateLimitedTime;
			return result;
		}
	}
	internal static class Logging
	{
		private static ManualLogSource InternalLogger => PerfectGuard.Plugin.Logger;

		public static void LogFatal(object data, ConfigEntry<bool>? toggle = null)
		{
			Log(data, (LogLevel)1, toggle);
		}

		public static void LogError(object data, ConfigEntry<bool>? toggle = null)
		{
			Log(data, (LogLevel)2, toggle);
		}

		public static void LogWarning(object data, ConfigEntry<bool>? toggle = null)
		{
			Log(data, (LogLevel)4, toggle);
		}

		public static void LogMessage(object data, ConfigEntry<bool>? toggle = null)
		{
			Log(data, (LogLevel)8, toggle);
		}

		public static void LogInfo(object data, ConfigEntry<bool>? toggle = null)
		{
			Log(data, (LogLevel)16, toggle);
		}

		public static void LogDebug(object data, ConfigEntry<bool>? toggle = null)
		{
			Log(data, (LogLevel)32, toggle);
		}

		private static void Log(object data, LogLevel level = 16, ConfigEntry<bool>? toggle = null)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (toggle == null || toggle.Value)
			{
				ManualLogSource internalLogger = InternalLogger;
				if (internalLogger != null)
				{
					internalLogger.Log(level, data);
				}
			}
		}
	}
	public class NetItemObjectTracker : NetworkBehaviour
	{
		public static readonly List<Net_ItemObject> Items = new List<Net_ItemObject>();

		private Net_ItemObject _itemObject = null;

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

		public void OnDestroy()
		{
			Items.Remove(_itemObject);
		}
	}
	[BepInPlugin("Marioalexsan.PerfectGuard", "PerfectGuard", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class PerfectGuard : BaseUnityPlugin
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__33_0;

			internal void <Awake>b__33_0()
			{
				EasySettings.AddHeader("PerfectGuard");
				EasySettings.AddToggle("Enable PerfectGuard (recommended)", EnablePerfectGuard);
				EasySettings.AddToggle("Detailed logging", DetailedLogging);
				EasySettings.AddToggle("Network rate limits (recommended)", NetworkRateLimiting);
				EasySettings.AddToggle("Audio rate limits", AudioRateLimiting);
				EasySettings.AddToggle("Enable item cleanup", ItemCleanup);
				EasySettings.AddSlider("Item cleanup max items", MaxItemsThreshold, wholeNumbers: true);
			}
		}

		private static PerfectGuard? _plugin;

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

		private static ConfigEntry<bool> EnablePerfectGuard;

		private static ConfigEntry<bool> NetworkRateLimiting;

		private static ConfigEntry<bool> ItemCleanup;

		private static ConfigEntry<bool> AudioRateLimiting;

		private static ConfigEntry<float> MaxItemsThreshold;

		private TimeSpan _abuseDetectorCleanupAccumulator;

		private TimeSpan _miscAccumulator;

		public static PerfectGuard Plugin => _plugin ?? throw new InvalidOperationException("PerfectGuard hasn't been initialized yet. Either wait until initialization, or check via ChainLoader instead.");

		internal ManualLogSource Logger { get; private set; }

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

		public static bool NetworkRateLimitingEnabled { get; private set; }

		public static bool AudioRateLimitingEnabled { get; private set; }

		public static bool ItemCleanupEnabled { get; private set; }

		public PerfectGuard()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Expected O, but got Unknown
			_plugin = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			EnablePerfectGuard = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnablePerfectGuard", true, "Enable or disable all features of this mod.");
			DetailedLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DetailedLogging", true, "Log detailed warnings for detected malicious activities.");
			NetworkRateLimiting = ((BaseUnityPlugin)this).Config.Bind<bool>("Protections", "NetworkRateLimiting", true, "Enables rate limits for network spam, such as visual effects and other abuse.");
			AudioRateLimiting = ((BaseUnityPlugin)this).Config.Bind<bool>("Protections", "AudioRateLimiting", true, "Enables audio rate limits to prevent spam.");
			ItemCleanup = ((BaseUnityPlugin)this).Config.Bind<bool>("Protections", "ItemCleanup", true, "Enable cleanup for excessive item drops.");
			MaxItemsThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("Tuning", "MaxItemsThreshold", 200f, new ConfigDescription("The maximum number of items allowed before the items are forcefully cleaned up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(50f, 500f), Array.Empty<object>()));
			UpdateEnableValues();
		}

		private static void UpdateEnableValues()
		{
			NetworkRateLimitingEnabled = EnablePerfectGuard.Value && NetworkRateLimiting.Value;
			AudioRateLimitingEnabled = EnablePerfectGuard.Value && AudioRateLimiting.Value;
			ItemCleanupEnabled = EnablePerfectGuard.Value && ItemCleanup.Value;
		}

		public void Awake()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Expected O, but got Unknown
			_harmony.PatchAll();
			if (!EasySettings.IsAvailable)
			{
				return;
			}
			UnityEvent onInitialized = EasySettings.OnInitialized;
			object obj = <>c.<>9__33_0;
			if (obj == null)
			{
				UnityAction val = delegate
				{
					EasySettings.AddHeader("PerfectGuard");
					EasySettings.AddToggle("Enable PerfectGuard (recommended)", EnablePerfectGuard);
					EasySettings.AddToggle("Detailed logging", DetailedLogging);
					EasySettings.AddToggle("Network rate limits (recommended)", NetworkRateLimiting);
					EasySettings.AddToggle("Audio rate limits", AudioRateLimiting);
					EasySettings.AddToggle("Enable item cleanup", ItemCleanup);
					EasySettings.AddSlider("Item cleanup max items", MaxItemsThreshold, wholeNumbers: true);
				};
				<>c.<>9__33_0 = val;
				obj = (object)val;
			}
			onInitialized.AddListener((UnityAction)obj);
			EasySettings.OnApplySettings.AddListener((UnityAction)delegate
			{
				try
				{
					((BaseUnityPlugin)this).Config.Save();
					UpdateEnableValues();
				}
				catch (Exception ex)
				{
					Logging.LogError("PefectGuard crashed in OnApplySettings! Please report this error to the mod developer:");
					Logging.LogError(ex.ToString());
				}
			});
		}

		public void Update()
		{
			CheckForObjectSpikes();
			CheckAbuseDetectorCleanup();
			CheckAudioCleanup();
		}

		private void CheckAbuseDetectorCleanup()
		{
			_abuseDetectorCleanupAccumulator += TimeSpan.FromSeconds(Time.deltaTime);
			if (_abuseDetectorCleanupAccumulator >= TimeSpan.FromSeconds(60.0))
			{
				_abuseDetectorCleanupAccumulator = TimeSpan.Zero;
				AbuseDetectorEMA.RunActorCleanup();
				AbuseDetectorTokenBucket.RunActorCleanup();
			}
		}

		private void CheckAudioCleanup()
		{
			_miscAccumulator += TimeSpan.FromSeconds(Time.deltaTime);
			if (_miscAccumulator >= TimeSpan.FromSeconds(2.0))
			{
				_abuseDetectorCleanupAccumulator = TimeSpan.Zero;
				PreventAudioSpam.CleanupDeadAudioSources();
			}
		}

		private void CheckForObjectSpikes()
		{
			if (!ItemCleanupEnabled || !NetworkServer.active || !((float)NetItemObjectTracker.Items.Count > MaxItemsThreshold.Value))
			{
				return;
			}
			Logging.LogWarning($"Current item count is above the allowed limit ({MaxItemsThreshold.Value})! Cleaning up excessive items...", DetailedLogging);
			List<Net_ItemObject> items = NetItemObjectTracker.Items;
			int num = items.Count * 85 / 100;
			int num2;
			Renderer val2 = default(Renderer);
			Collider val3 = default(Collider);
			for (num2 = 0; num2 < num; num2++)
			{
				Net_ItemObject val = items[num2];
				if (((Component)val).TryGetComponent<Renderer>(ref val2))
				{
					val2.enabled = false;
				}
				if (((Component)val).TryGetComponent<Collider>(ref val3))
				{
					val3.enabled = false;
				}
				if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val._itemObj))
				{
					val._itemObj.UnspawnObject();
				}
				items.RemoveAt(num2);
				num2--;
				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.SoftDependencies
{
	public static class EasySettings
	{
		private const MethodImplOptions SoftDepend = MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization;

		public const string ModID = "EasySettings";

		public static readonly Version ExpectedVersion = new Version("1.2.0");

		private static BaseUnityPlugin? _plugin;

		private static bool _initialized;

		public static bool IsAvailable
		{
			get
			{
				if (!_initialized)
				{
					_plugin = (Chainloader.PluginInfos.TryGetValue("EasySettings", out var value) ? value.Instance : null);
					_initialized = true;
					if ((Object)(object)_plugin == (Object)null)
					{
						Logging.LogWarning("Soft dependency EasySettings was not found.");
					}
					else if (_plugin.Info.Metadata.Version != ExpectedVersion)
					{
						Logging.LogWarning(string.Format("Soft dependency {0} has a different version than expected (have: {1}, expect: {2}).", "EasySettings", _plugin.Info.Metadata.Version, ExpectedVersion));
					}
				}
				return (Object)(object)_plugin != (Object)null;
			}
		}

		public static UnityEvent OnInitialized
		{
			[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
			get
			{
				return Settings.OnInitialized;
			}
		}

		public static UnityEvent OnCancelSettings
		{
			[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
			get
			{
				return Settings.OnCancelSettings;
			}
		}

		public static UnityEvent OnApplySettings
		{
			[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
			get
			{
				return Settings.OnApplySettings;
			}
		}

		public static UnityEvent OnCloseSettings
		{
			[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
			get
			{
				return Settings.OnCloseSettings;
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static GameObject AddSpace()
		{
			return ((Component)((BaseAtlyssElement)Settings.ModTab.AddSpace()).Root).gameObject;
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static GameObject AddHeader(string label)
		{
			return ((Component)((BaseAtlyssElement)Settings.ModTab.AddHeader(label)).Root).gameObject;
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static GameObject AddButton(string buttonLabel, UnityAction onClick)
		{
			return ((Component)((BaseAtlyssElement)Settings.ModTab.AddButton(buttonLabel, onClick)).Root).gameObject;
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static GameObject AddToggle(string label, ConfigEntry<bool> config)
		{
			return ((Component)((BaseAtlyssElement)Settings.ModTab.AddToggle(label, config)).Root).gameObject;
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static GameObject AddSlider(string label, ConfigEntry<float> config, bool wholeNumbers = false)
		{
			return ((Component)((BaseAtlyssElement)Settings.ModTab.AddSlider(label, config, wholeNumbers)).Root).gameObject;
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static GameObject AddAdvancedSlider(string label, ConfigEntry<float> config, bool wholeNumbers = false)
		{
			return ((Component)((BaseAtlyssElement)Settings.ModTab.AddAdvancedSlider(label, config, wholeNumbers)).Root).gameObject;
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static GameObject AddDropdown<T>(string label, ConfigEntry<T> config) where T : Enum
		{
			return ((Component)((BaseAtlyssElement)Settings.ModTab.AddDropdown<T>(label, config)).Root).gameObject;
		}

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		public static GameObject AddKeyButton(string label, ConfigEntry<KeyCode> config)
		{
			return ((Component)((BaseAtlyssElement)Settings.ModTab.AddKeyButton(label, config)).Root).gameObject;
		}
	}
}
namespace Marioalexsan.PerfectGuard.Patches
{
	[HarmonyPatch]
	internal static class CmdRpcThrottle
	{
		private struct ThrottleData
		{
			public int Limit;

			public string MethodName;

			public ushort FunctionHash;
		}

		private const int Unlimited = 16000;

		private const int DefaultLimit = 400;

		private const int UnspecifiedDefaultLimit = 40;

		private const int VeryHighLimit = 400;

		private const int HighLimit = 100;

		private const int MediumLimit = 40;

		private const int LowLimit = 10;

		private const int VeryLowLimit = 10;

		private static readonly Regex NameRegex;

		private static readonly Dictionary<ushort, ThrottleData> CustomRateLimits;

		private static readonly ConcurrentDictionary<ushort, AbuseDetectorTokenBucket> Detectors;

		static CmdRpcThrottle()
		{
			NameRegex = new Regex("::([^\\(]*)\\(");
			CustomRateLimits = new Dictionary<ushort, ThrottleData>();
			Detectors = new ConcurrentDictionary<ushort, AbuseDetectorTokenBucket>();
			Throttle("System.Void Mirror.NetworkTransformUnreliable::CmdClientToServerSync(System.Nullable`1<UnityEngine.Vector3>,System.Nullable`1<UnityEngine.Quaternion>,System.Nullable`1<UnityEngine.Vector3>)", 16000);
			Throttle("System.Void Mirror.NetworkTransformUnreliable::RpcServerToClientSync(System.Nullable`1<UnityEngine.Vector3>,System.Nullable`1<UnityEngine.Quaternion>,System.Nullable`1<UnityEngine.Vector3>)", 16000);
			Throttle("System.Void BreakableObject::Rpc_Break(UnityEngine.Vector3)");
			Throttle("System.Void ChatBehaviour::Cmd_SetChatChannel(System.String)");
			Throttle("System.Void ChatBehaviour::Cmd_JoinChatRoom(System.String)");
			Throttle("System.Void ChatBehaviour::Cmd_ToggleChatBubble(System.Boolean)");
			Throttle("System.Void ChatBehaviour::Cmd_SendChatMessage(System.String,ChatBehaviour/ChatChannel)");
			Throttle("System.Void ChatBehaviour::Rpc_RecieveChatMessage(System.String,System.Boolean,ChatBehaviour/ChatChannel)");
			Throttle("System.Void ChatBehaviour::Target_RecieveMessage(System.String)");
			Throttle("System.Void ChatBehaviour::Target_GameLogicMessage(System.String)");
			Throttle("System.Void ChatBehaviour::Target_RecieveTriggerMessage(System.String)");
			Throttle("System.Void Creep::Rpc_PlayCreepJumpEffect()");
			Throttle("System.Void Creep::Rpc_PlayAggroIcon(System.Int32)");
			Throttle("System.Void Creep::Rpc_OnCreepSpawn()");
			Throttle("System.Void Creep::Rpc_PlayCreepHurtEffect()");
			Throttle("System.Void Creep::Rpc_InitCreepDeathParams(System.Boolean)");
			Throttle("System.Void Creep::Rpc_TintRender(UnityEngine.Color)");
			Throttle("System.Void Creep::Rpc_InitSkillChargeEffect(System.String,System.Single)");
			Throttle("System.Void Creep::Rpc_InitSkillCastEffect(System.String)");
			Throttle("System.Void Creep::Rpc_CrossFadeAnim(System.String,System.Single,System.Int32)");
			Throttle("System.Void Creep::Rpc_CrossFadeAnim_Timed(System.String,System.Single,System.Int32,System.Single)");
			Throttle("System.Void Creep::Rpc_InitUnspawnEffect(UnityEngine.Vector3,System.Single)");
			Throttle("System.Void CreepSpawner::Rpc_InitSpecialSpawnEffect(UnityEngine.Vector3)");
			Throttle("System.Void Net_ItemObject::Rpc_NetItemObjectPickup(Mirror.NetworkIdentity)");
			Throttle("System.Void Net_ItemObject::Rpc_SpawnPuffcloud()");
			Throttle("System.Void NetNPC::Rpc_CrossFade_Anim(System.String,System.Single,System.Int32)");
			Throttle("System.Void Player::Cmd_SetLatency(System.Int32)");
			Throttle("System.Void Player::Cmd_InitAfkCondition(System.Boolean)");
			Throttle("System.Void Player::Cmd_SetInactive()");
			Throttle("System.Void Player::Cmd_Respawn()");
			Throttle("System.Void Player::Cmd_ReturnToRecalledInstance()");
			Throttle("System.Void Player::Cmd_SceneTransport(System.String,System.String,ZoneDifficulty)");
			Throttle("System.Void Player::Cmd_InviteToParty(Player)");
			Throttle("System.Void Player::Cmd_LeaveParty()");
			Throttle("System.Void Player::Cmd_SetPartyInviteCondition(PartyInviteStatus)");
			Throttle("System.Void Player::Target_ResetCameraPositioning()");
			Throttle("System.Void PlayerCasting::Cmd_QuerySkillStructs(SkillStruct[])");
			Throttle("System.Void PlayerCasting::Cmd_InitSkill(System.String)");
			Throttle("System.Void PlayerCasting::Cmd_SpawnEarlySkillObj()");
			Throttle("System.Void PlayerCasting::Cmd_InitCastInterrupt()");
			Throttle("System.Void PlayerCasting::Cmd_SetReviveEntity(StatusEntity)");
			Throttle("System.Void PlayerCasting::Cmd_CastInit()");
			Throttle("System.Void PlayerCasting::Rpc_QuerySkillStructs(SkillStruct[])");
			Throttle("System.Void PlayerCasting::Rpc_InitSkill(System.String)");
			Throttle("System.Void PlayerCasting::Rpc_SpawnEarlySkillObj()");
			Throttle("System.Void PlayerCasting::Rpc_InterruptCast()");
			Throttle("System.Void PlayerCasting::Rpc_CastSkill(System.String)");
			Throttle("System.Void PlayerCasting::Target_InitSkillLibrary()");
			Throttle("System.Void PlayerCombat::Cmd_ResetHitboxes()");
			Throttle("System.Void PlayerCombat::Cmd_QuickSwapWeapon()");
			Throttle("System.Void PlayerCombat::Cmd_LockWeapon()");
			Throttle("System.Void PlayerCombat::Cmd_SheatheWeapon(WeaponSheatheCondition)");
			Throttle("System.Void PlayerCombat::Cmd_InterruptCombat(System.Boolean)");
			Throttle("System.Void PlayerCombat::Cmd_ApplyBlockCondition(System.Boolean)");
			Throttle("System.Void PlayerCombat::Cmd_WeaponChargeDisplay(System.Boolean)");
			Throttle("System.Void PlayerCombat::Rpc_InterruptCombat(System.Boolean)");
			Throttle("System.Void PlayerCombat::Rpc_WeaponChargeDisplay(System.Boolean)");
			Throttle("System.Void PlayerCombat::Target_CancelBlock()");
			Throttle("System.Void PlayerEquipment::Cmd_SyncEquipStruct(EquipSyncStruct,EquipSfx)");
			Throttle("System.Void PlayerEquipment::Cmd_SyncVanityStruct(EquipSyncStruct)");
			Throttle("System.Void PlayerEquipment::Cmd_RemoveVanity(System.String)");
			Throttle("System.Void PlayerEquipment::Rpc_EquipSfx(EquipSfx)");
			Throttle("System.Void PlayerInteract::Cmd_SetPushblockParent(PushBlock)");
			Throttle("System.Void PlayerInteract::Cmd_SetInteraction(Mirror.NetworkIdentity)");
			Throttle("System.Void PlayerInteract::Cmd_RemoveInteraction(Mirror.NetworkIdentity)");
			Throttle("System.Void PlayerInteract::Cmd_InteractWithTrigger(Mirror.NetworkIdentity)");
			Throttle("System.Void PlayerInteract::Cmd_InteractWithPortal(Portal,ZoneDifficulty)");
			Throttle("System.Void PlayerInteract::Cmd_OpenChest(ItemDropEntity_Chest)");
			Throttle("System.Void PlayerInteract::Cmd_InteractNetItemObject(Net_ItemObject,Mirror.NetworkIdentity)");
			Throttle("System.Void PlayerInteract::Rpc_OpenChest(ItemDropEntity_Chest)");
			Throttle("System.Void PlayerInventory::Cmd_AddCurrency(System.Int32)");
			Throttle("System.Void PlayerInventory::Cmd_SubtractCurrency(System.Int32)");
			Throttle("System.Void PlayerInventory::Cmd_DropCurrency(System.Int32)");
			Throttle("System.Void PlayerInventory::Cmd_PurchaseBuybackItem(NetNPC,System.Int32,System.String)");
			Throttle("System.Void PlayerInventory::Cmd_SellItem(Mirror.NetworkIdentity,ItemData,System.Int32)");
			Throttle("System.Void PlayerInventory::Cmd_InitBuyEffect()", 4);
			Throttle("System.Void PlayerInventory::Cmd_DropItem(ItemData,System.Int32)", 4);
			Throttle("System.Void PlayerInventory::Cmd_UseConsumable(ItemData)");
			Throttle("System.Void PlayerInventory::Rpc_PlayBuyEffect()");
			Throttle("System.Void PlayerInventory::Rpc_PlaySellEffect()");
			Throttle("System.Void PlayerInventory::Rpc_Init_ConsumableObject(Player,System.String)");
			Throttle("System.Void PlayerInventory::Target_RemoveItem(ItemData,System.Int32)");
			Throttle("System.Void PlayerInventory::Target_AddItem(ItemData)");
			Throttle("System.Void PlayerMove::Target_SetRotation(UnityEngine.Quaternion)");
			Throttle("System.Void PlayerMove::Target_LockMovement(MovementLockType,System.Single)");
			Throttle("System.Void PlayerMove::Target_LockLookRotationMidair()");
			Throttle("System.Void PlayerMove::Target_InitJump(System.Single,System.Single,System.Single,UnityEngine.Vector3,System.Single)");
			Throttle("System.Void PlayerPvp::Cmd_FlagPvp(System.Boolean)");
			Throttle("System.Void PlayerQuesting::Cmd_QueryServerQuestData(System.String[],System.String[])");
			Throttle("System.Void PlayerQuesting::Cmd_InitServersideQuestRewards(System.String)");
			Throttle("System.Void PlayerQuesting::Target_Query_CreepKillProgress(System.String)");
			Throttle("System.Void PlayerQuesting::Target_QueryQuestTriggerProgress(System.String)");
			Throttle("System.Void PlayerStats::Cmd_ApplyAttributePoints(System.Int32[],System.Int32[])");
			Throttle("System.Void PlayerStats::Cmd_GainProfessionExp(ResourceEntity,System.Int32)");
			Throttle("System.Void PlayerStats::Cmd_RequestClass(System.String)");
			Throttle("System.Void PlayerStats::Cmd_RequestClassTier(System.Int32)");
			Throttle("System.Void PlayerStats::Rpc_LevelUpEffect()");
			Throttle("System.Void PlayerStats::Rpc_ProfessionLevelUpEffect()");
			Throttle("System.Void PlayerStats::Target_ResetSkillPoints()");
			Throttle("System.Void PlayerStats::Target_DisplayExpFloatText(System.Int32)");
			Throttle("System.Void PlayerTargeting::Cmd_TargetSyncCreep(Mirror.NetworkIdentity)");
			Throttle("System.Void PlayerVisual::Cmd_SendNew_PlayerAppearanceStruct(PlayerAppearanceStruct)");
			Throttle("System.Void PlayerVisual::Cmd_PlayTeleportEffect()", 4);
			Throttle("System.Void PlayerVisual::Cmd_VanitySparkleEffect()", 4);
			Throttle("System.Void PlayerVisual::Cmd_PoofSmokeEffect()", 4);
			Throttle("System.Void PlayerVisual::Cmd_JumpAttackEffect()", 8);
			Throttle("System.Void PlayerVisual::Cmd_CrossFadeAnim(System.String,System.Single,System.Int32)");
			Throttle("System.Void PlayerVisual::Cmd_ShowItemEmote(System.String)");
			Throttle("System.Void PlayerVisual::Cmd_ChangeClimbAnimationSpeed(System.Single)");
			Throttle("System.Void PlayerVisual::Cmd_AltMovementAnimBool(System.Boolean)");
			Throttle("System.Void PlayerVisual::Cmd_ToggleArmorRender(System.Int32,EquipCellTab)");
			Throttle("System.Void PlayerVisual::Cmd_HideWeapon(System.Single)");
			Throttle("System.Void PlayerVisual::Cmd_ResetSpinPlayerModel()");
			Throttle("System.Void PlayerVisual::Rpc_PlayTeleportEffect()", 4);
			Throttle("System.Void PlayerVisual::Rpc_VanitySparkleEffect()", 4);
			Throttle("System.Void PlayerVisual::Rpc_PoofSmokeEffect()", 4);
			Throttle("System.Void PlayerVisual::Rpc_JumpAttackEffect()", 8);
			Throttle("System.Void PlayerVisual::Rpc_CrossFadeAnim(System.String,System.Single,System.Int32)");
			Throttle("System.Void PlayerVisual::IncludeRpc_Crossfade(System.String,System.Single,System.Int32)");
			Throttle("System.Void PlayerVisual::Rpc_ShowItemEmote(System.String)");
			Throttle("System.Void PlayerVisual::Rpc_SetPlaybackSpeed(System.Single)");
			Throttle("System.Void PlayerVisual::Rpc_AltMovementBool(System.Boolean)");
			Throttle("System.Void PlayerVisual::Rpc_HideWeapon(System.Single)");
			Throttle("System.Void PlayerVisual::Rpc_RandomSpinPlayerModel(System.Single)");
			Throttle("System.Void PlayerVisual::Rpc_ResetSpinPlayerModel()");
			Throttle("System.Void PlayerVisual::Rpc_PreventRotationLerp(System.Single)");
			Throttle("System.Void PushBlock::Cmd_RemoveParentPlayer()");
			Throttle("System.Void PushBlock::Request_PushBlockAction(UnityEngine.Vector3,System.Int32,System.Boolean)");
			Throttle("System.Void QuestTrigger::Rpc_InitEffect()");
			Throttle("System.Void StatusEntity::Cmd_AddCondition(System.Int32,System.Int32,System.Int32)");
			Throttle("System.Void StatusEntity::Cmd_ReplenishAll()");
			Throttle("System.Void StatusEntity::Cmd_RevivePlayer(StatusEntity)");
			Throttle("System.Void StatusEntity::Cmd_SubtractMana(System.Int32)");
			Throttle("System.Void StatusEntity::Cmd_SubtractStamina(System.Int32)");
			Throttle("System.Void StatusEntity::Cmd_TakeDamage(System.Int32,System.Single,System.Boolean,System.Boolean,DamageWeight,UnityEngine.Vector3,UnityEngine.Vector3)");
			Throttle("System.Void StatusEntity::Rpc_AngelaTearEffect()");
			Throttle("System.Void StatusEntity::Rpc_DisplayHitEffect(DamageWeight,System.Int32,UnityEngine.Vector3)");
			Throttle("System.Void StatusEntity::Rpc_DisplayBlockHitEffect(StatusEntity,System.Int32,System.Boolean,System.Boolean,UnityEngine.Vector3)");
			Throttle("System.Void StatusEntity::Rpc_DisplayAbsorbHitEffect()");
			Throttle("System.Void StatusEntity::Rpc_DisplayCritHitEffect(StatusEntity,UnityEngine.Vector3)");
			Throttle("System.Void StatusEntity::Rpc_DisplayMissHitEffect(StatusEntity)");
			Throttle("System.Void StatusEntityGUI::Target_Display_FloatingNumber(StatusEntity,FloatTextColor,System.Int32,System.Int32,System.Int32)");
		}

		private static void Throttle(string functionName)
		{
			Throttle(functionName, 400);
		}

		private static void Throttle(string functionName, int rateLimit)
		{
			ushort num = Hash(functionName);
			Match match = NameRegex.Match(functionName);
			CustomRateLimits.Add(num, new ThrottleData
			{
				Limit = rateLimit,
				MethodName = (match.Success ? match.Groups[1].Value : functionName),
				FunctionHash = num
			});
		}

		private static ushort Hash(string functionName)
		{
			return (ushort)((uint)Extensions.GetStableHashCode(functionName) & 0xFFFFu);
		}

		private static AbuseDetectorTokenBucket CreateDetector(ushort functionHash)
		{
			if (!CustomRateLimits.TryGetValue(functionHash, out var value))
			{
				value = (CustomRateLimits[functionHash] = new ThrottleData
				{
					FunctionHash = functionHash,
					Limit = 40,
					MethodName = ""
				});
				Logging.LogDebug("Got an unconfigured CMD/RPC function (" + GetMethodName(functionHash) + ")! Will throttle using default limit for unspecified methods.", PerfectGuard.DetailedLogging);
			}
			return new AbuseDetectorTokenBucket(value.Limit);
		}

		private static bool CheckRateLimits(uint netId, ushort functionHash, byte componentIndex, bool isRpc)
		{
			if (!PerfectGuard.NetworkRateLimitingEnabled)
			{
				return true;
			}
			NetworkIdentity value;
			if (isRpc)
			{
				if (!NetworkClient.active || !NetworkClient.spawned.TryGetValue(netId, out value))
				{
					return true;
				}
			}
			else if (!NetworkServer.active || !NetworkServer.spawned.TryGetValue(netId, out value))
			{
				return true;
			}
			if (0 > componentIndex || componentIndex >= value.NetworkBehaviours.Length)
			{
				return true;
			}
			NetworkBehaviour val = value.NetworkBehaviours[componentIndex];
			AbuseDetectorTokenBucket orAdd = Detectors.GetOrAdd(functionHash, CreateDetector);
			AbuseDetectorTokenBucket.RateLimitData rateLimitData = orAdd.TrackEvent(val);
			if (rateLimitData.IsRateLimited && DateTime.Now - rateLimitData.PreviousRateLimitAt >= TimeSpan.FromSeconds(5.0))
			{
				Logging.LogWarning($"Network spam detected! ({((Object)val).name}, {GetMethodName(functionHash)}, {orAdd.RateLimit}/s, {orAdd.BurstLimit} burst)", PerfectGuard.DetailedLogging);
			}
			return !rateLimitData.IsRateLimited;
		}

		private static string GetMethodName(ushort functionHash)
		{
			if (CustomRateLimits.TryGetValue(functionHash, out var value))
			{
				return value.MethodName;
			}
			if (RemoteProcedureCalls.remoteCallDelegates.TryGetValue(functionHash, out var value2))
			{
				string name = ((Delegate)(object)value2.function).Method.Name;
				Match match = NameRegex.Match(((Delegate)(object)value2.function).Method.Name);
				return match.Success ? match.Groups[1].Value : name;
			}
			return $"<hash {functionHash}>";
		}

		[HarmonyPatch(typeof(NetworkClient), "OnRPCMessage")]
		[HarmonyPrefix]
		private static bool RpcThrottle(ref RpcMessage message)
		{
			return CheckRateLimits(message.netId, message.functionHash, message.componentIndex, isRpc: true);
		}

		[HarmonyPatch(typeof(NetworkServer), "OnCommandMessage")]
		[HarmonyPrefix]
		private static bool CmdThrottle(ref CommandMessage msg)
		{
			return CheckRateLimits(msg.netId, msg.functionHash, msg.componentIndex, isRpc: false);
		}
	}
	[HarmonyPatch]
	public class NetItemObjectTrackerInjector
	{
		[HarmonyPatch(typeof(Net_ItemObject), "Awake")]
		[HarmonyPostfix]
		private static void InjectTracker(Net_ItemObject __instance)
		{
			if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<NetItemObjectTracker>()))
			{
				((Component)__instance).gameObject.AddComponent<NetItemObjectTracker>();
			}
		}
	}
	[HarmonyPatch]
	internal static class PreventAudioSpam
	{
		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>();

		[HarmonyPrefix]
		[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { })]
		public static bool AudioSource_Play_Prefix(AudioSource __instance)
		{
			return CheckAudioCooldown(__instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[]
		{
			typeof(AudioClip),
			typeof(float)
		})]
		public static bool AudioSource_PlayOneShot_Prefix(AudioSource __instance)
		{
			return CheckAudioCooldown(__instance);
		}

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

		internal static void CleanupDeadAudioSources()
		{
			DeadAudioSources.Clear();
			DeadAudioSources.AddRange(AudioCooldowns.Keys.Where((AudioSource audioSource) => (Object)(object)audioSource == (Object)null));
			foreach (AudioSource deadAudioSource in DeadAudioSources)
			{
				AudioCooldowns.Remove(deadAudioSource);
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}