Decompiled source of HardAntiCheat v2.6.0

HardAntiCheat.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using CodeTalker;
using CodeTalker.Networking;
using CodeTalker.Packets;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using Mirror.FizzySteam;
using Nessie.ATLYSS.EasySettings;
using Nessie.ATLYSS.EasySettings.UIElements;
using Newtonsoft.Json;
using Steamworks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.HardAntiCheat.sftwre")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("2.6.0.0")]
[assembly: AssemblyInformationalVersion("2.6.0+e5e7aa6f84c86d3766fca359b4a1e4e0990d25ce")]
[assembly: AssemblyProduct("HardAntiCheat")]
[assembly: AssemblyTitle("HardAntiCheat")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.6.0.0")]
[module: UnverifiableCode]
[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;
		}
	}
}
namespace HardAntiCheat
{
	[BepInPlugin("com.HardAntiCheat.sftwre", "HardAntiCheat", "2.6.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Main : BaseUnityPlugin
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static BinaryPacketListener <0>__OnClientReceivedRequest;

			public static BinaryPacketListener <1>__OnReceivedResponse;

			public static BinaryPacketListener <2>__OnClientReceivedKickMessage;
		}

		public static ConfigEntry<bool> EnableAntiCheat;

		public static ConfigEntry<bool> DisableForHost;

		public static ConfigEntry<string> TrustedSteamIDs;

		public static ConfigEntry<string> BannedSteamIDs;

		public static ConfigEntry<bool> EnableRequiredMods;

		public static ConfigEntry<string> RequiredModGUIDs;

		public static ConfigEntry<bool> EnableModBlacklist;

		public static ConfigEntry<string> BlacklistedModGUIDs;

		public static ConfigEntry<bool> EnableModWhitelist;

		public static ConfigEntry<string> WhitelistedModGUIDs;

		public static ConfigEntry<bool> EnableRemoteDatabase;

		public static ConfigEntry<string> RemoteDatabaseURL;

		public static ConfigEntry<bool> EnableThunderstoreWhitelist;

		public static ConfigEntry<string> ThunderstoreCommunity;

		public static ConfigEntry<bool> BlockDeprecatedMods;

		public static ConfigEntry<float> ThunderstoreRefreshInterval;

		public static ConfigEntry<float> ThunderstoreCacheTimeout;

		public static ConfigEntry<bool> AutoWhitelistNewMods;

		public static ConfigEntry<bool> AutoBlacklistUnknownMods;

		public static ConfigEntry<float> UnknownModGracePeriod;

		public static ConfigEntry<float> VerificationTimeout;

		public static ConfigEntry<int> MaxLogFileSizeMB;

		public static ConfigEntry<bool> EnableMovementChecks;

		public static ConfigEntry<float> MaxEffectiveSpeed;

		public static ConfigEntry<float> MovementGraceBuffer;

		public static ConfigEntry<float> MovementTimeThreshold;

		public static ConfigEntry<float> TeleportDistanceThreshold;

		public static ConfigEntry<bool> EnableAirborneChecks;

		public static ConfigEntry<bool> EnableSpeedChecks;

		public static ConfigEntry<float> SpeedHackDetectionCooldown;

		public static ConfigEntry<float> JumpHackDetectionCooldown;

		public static ConfigEntry<float> AirborneHackDetectionCooldown;

		public static ConfigEntry<bool> EnableExperienceChecks;

		public static ConfigEntry<int> JumpThreshold;

		public static ConfigEntry<int> MaxPlausibleXPGain;

		public static ConfigEntry<int> MaxXPGainPerWindow;

		public static ConfigEntry<float> XPGainWindowSeconds;

		public static ConfigEntry<bool> EnableCooldownChecks;

		public static ConfigEntry<bool> EnableReviveChecks;

		public static ConfigEntry<bool> EnableSpamChecks;

		public static ConfigEntry<float> MinConsumableInterval;

		public static ConfigEntry<float> MinWeaponSwapInterval;

		public static ConfigEntry<float> MinBlockInterval;

		public static ConfigEntry<bool> EnablePunishmentSystem;

		public static ConfigEntry<int> WarningsUntilAction;

		public static ConfigEntry<string> ActionType;

		public static ConfigEntry<bool> EnableDetailedLogs;

		public static ConfigEntry<bool> LogPlayerName;

		public static ConfigEntry<bool> LogPlayerID;

		public static ConfigEntry<bool> LogInfractionCount;

		public static ConfigEntry<bool> LogInfractionDetails;

		private static readonly HashSet<ulong> WhitelistedUsers = new HashSet<ulong>();

		private static readonly HashSet<ulong> ServerBannedUsers = new HashSet<ulong>();

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

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

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

		private static readonly Dictionary<string, ThunderstoreModCache> ThunderstoreCache = new Dictionary<string, ThunderstoreModCache>();

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

		private static readonly Dictionary<string, DateTime> PendingUnknownMods = new Dictionary<string, DateTime>();

		private static Coroutine thunderstoreRefreshCoroutine;

		private static bool isThunderstoreInitialized = false;

		internal static readonly Dictionary<ulong, Coroutine> PendingVerification = new Dictionary<ulong, Coroutine>();

		internal static readonly HashSet<ulong> VerifiedSteamIDs = new HashSet<ulong>();

		public static readonly Dictionary<uint, Dictionary<string, float>> ServerRemainingCooldowns = new Dictionary<uint, Dictionary<string, float>>();

		public static readonly Dictionary<uint, PlayerPositionData> ServerPlayerPositions = new Dictionary<uint, PlayerPositionData>();

		public static readonly Dictionary<uint, PlayerStatsData> ServerPlayerStats = new Dictionary<uint, PlayerStatsData>();

		public static readonly Dictionary<uint, List<(float Timestamp, int Amount)>> XpGainHistory = new Dictionary<uint, List<(float, int)>>();

		public static readonly Dictionary<uint, int> ServerRunningXpTotal = new Dictionary<uint, int>();

		public static readonly Dictionary<uint, PlayerAirborneData> ServerPlayerAirborneStates = new Dictionary<uint, PlayerAirborneData>();

		public static readonly Dictionary<uint, float> ServerPlayerInitialSpeeds = new Dictionary<uint, float>();

		public static readonly Dictionary<uint, PlayerMovementStats> ServerPlayerMovementStats = new Dictionary<uint, PlayerMovementStats>();

		public static readonly Dictionary<uint, float> ServerPlayerMovementTimers = new Dictionary<uint, float>();

		public static readonly Dictionary<uint, int> ServerPlayerInfractionCount = new Dictionary<uint, int>();

		public static readonly Dictionary<uint, float> ServerPlayerGracePeriod = new Dictionary<uint, float>();

		public static readonly Dictionary<uint, float> ServerPunishmentCooldown = new Dictionary<uint, float>();

		public static readonly Dictionary<uint, float> ServerSpeedCheckCooldowns = new Dictionary<uint, float>();

		public static readonly Dictionary<uint, float> ServerJumpCheckCooldowns = new Dictionary<uint, float>();

		public static readonly Dictionary<uint, float> ServerAirborneCheckCooldowns = new Dictionary<uint, float>();

		public static readonly Dictionary<uint, float> AuthorizedSelfRevives = new Dictionary<uint, float>();

		public static readonly Dictionary<uint, float> LastConsumableTime = new Dictionary<uint, float>();

		public static readonly Dictionary<uint, float> LastWeaponSwapTime = new Dictionary<uint, float>();

		public static readonly Dictionary<uint, float> LastBlockTime = new Dictionary<uint, float>();

		public static readonly Dictionary<uint, float> StaminaCheckTimer = new Dictionary<uint, float>();

		private static readonly Dictionary<string, string> KnownModAliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "CodeTalker", "CodeYapper" } };

		private readonly Harmony harmony = new Harmony("com.HardAntiCheat.sftwre");

		internal static ManualLogSource Log;

		internal static string InfractionLogPath;

		public static Main Instance { get; private set; }

		private void InitConfig()
		{
			//IL_063b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0645: Expected O, but got Unknown
			EnableAntiCheat = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "Enable AntiCheat", true, "Master switch.");
			DisableForHost = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "Disable Detections for Host", true, "If true, the host is exempt.");
			TrustedSteamIDs = ((BaseUnityPlugin)this).Config.Bind<string>("1. General", "Trusted SteamIDs", "", "Comma-separated list of exempt SteamIDs.");
			BannedSteamIDs = ((BaseUnityPlugin)this).Config.Bind<string>("1. General", "Banned SteamIDs", "", "Comma-separated list of SteamIDs that are banned from joining.");
			MaxLogFileSizeMB = ((BaseUnityPlugin)this).Config.Bind<int>("1. General", "Max Log File Size (MB)", 5, "If the infraction log exceeds this size, it will be archived on startup.");
			EnableRequiredMods = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Mod Lists", "Enable Required Mods", false, "If true, clients MUST have the mods listed in Required Mod GUIDs.");
			RequiredModGUIDs = ((BaseUnityPlugin)this).Config.Bind<string>("2. Mod Lists", "Required Mod GUIDs", "", "Comma-separated list of Mod GUIDs that clients must have.");
			EnableModBlacklist = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Mod Lists", "Enable Mod Blacklist", false, "If true, clients sending a mod GUID found in the Blacklist will be kicked.");
			BlacklistedModGUIDs = ((BaseUnityPlugin)this).Config.Bind<string>("2. Mod Lists", "Blacklisted Mod GUIDs", "com.cheat.menu,another.bad.mod", "Comma-separated list of Mod GUIDs that are strictly forbidden.");
			EnableModWhitelist = ((BaseUnityPlugin)this).Config.Bind<bool>("2. Mod Lists", "Enable Mod Whitelist", false, "If true, ONLY mods in the Whitelist are allowed.");
			WhitelistedModGUIDs = ((BaseUnityPlugin)this).Config.Bind<string>("2. Mod Lists", "Whitelisted Mod GUIDs", "", "Comma-separated list of Mod GUIDs that are permitted.");
			EnableRemoteDatabase = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Remote Database", "Enable Remote Blacklist DB", false, "Fetch a list of blacklisted mods from a URL.");
			RemoteDatabaseURL = ((BaseUnityPlugin)this).Config.Bind<string>("3. Remote Database", "Remote Blacklist DB URL", "", "URL to a raw text file containing blacklisted Mod GUIDs.");
			EnableThunderstoreWhitelist = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Thunderstore", "Enable Thunderstore Whitelist", false, "Automatically whitelist all mods published on Thunderstore.");
			ThunderstoreCommunity = ((BaseUnityPlugin)this).Config.Bind<string>("4. Thunderstore", "Community Identifier", "atlyss", "Thunderstore community to fetch packages from.");
			BlockDeprecatedMods = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Thunderstore", "Block Deprecated Mods", true, "Automatically block mods that are deprecated on Thunderstore.");
			ThunderstoreRefreshInterval = ((BaseUnityPlugin)this).Config.Bind<float>("4. Thunderstore", "Refresh Interval (Minutes)", 30f, "How often to refresh the Thunderstore package list.");
			ThunderstoreCacheTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("4. Thunderstore", "Cache Timeout (Hours)", 6f, "How long to cache individual package verification results.");
			AutoWhitelistNewMods = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Thunderstore", "Auto-Whitelist New Mods", true, "Automatically add newly discovered Thunderstore mods to the whitelist.");
			AutoBlacklistUnknownMods = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Thunderstore", "Auto-Blacklist Unknown Mods", true, "Automatically blacklist mods that cannot be verified on Thunderstore. Persists across restarts.");
			UnknownModGracePeriod = ((BaseUnityPlugin)this).Config.Bind<float>("4. Thunderstore", "Unknown Mod Grace Period", 30f, "Seconds to wait before kicking for unknown mods.");
			VerificationTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("5. Handshake", "Verification Timeout", 25f, "Seconds to wait for client mod list before kicking.");
			EnableMovementChecks = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Movement", "Enable Teleport Checks", true, "Checks movement distance.");
			MaxEffectiveSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("6. Movement", "Max Effective Speed", 100f, "Max plausible speed.");
			MovementGraceBuffer = ((BaseUnityPlugin)this).Config.Bind<float>("6. Movement", "Movement Grace Buffer", 10f, "Distance buffer.");
			MovementTimeThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("6. Movement", "Movement Time Threshold", 5.5f, "Time between checks.");
			TeleportDistanceThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("6. Movement", "Teleport Threshold", 50f, "Distance to log as teleport.");
			EnableAirborneChecks = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Movement", "Enable Fly Checks", true, "Checks airtime.");
			EnableSpeedChecks = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Movement", "Enable Speed Stat Checks", true, "Checks base move speed.");
			JumpThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("6. Movement", "Jump Threshold", 8, "Max jumps allowed.");
			SpeedHackDetectionCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("6. Movement", "Speed Check Cooldown", 2f, "Log cooldown.");
			JumpHackDetectionCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("6. Movement", "Jump Check Cooldown", 2f, "Log cooldown.");
			AirborneHackDetectionCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("6. Movement", "Airborne Check Cooldown", 10f, "Log cooldown.");
			EnableExperienceChecks = ((BaseUnityPlugin)this).Config.Bind<bool>("7. Stats", "Enable XP Checks", true, "Checks XP gain.");
			MaxPlausibleXPGain = ((BaseUnityPlugin)this).Config.Bind<int>("7. Stats", "Max XP Gain", 1000000, "Max XP in one go.");
			MaxXPGainPerWindow = ((BaseUnityPlugin)this).Config.Bind<int>("7. Stats", "Max XP Rate", 5000000, "Max XP over time.");
			XPGainWindowSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("7. Stats", "XP Window", 30f, "Time window for XP rate.");
			EnableCooldownChecks = ((BaseUnityPlugin)this).Config.Bind<bool>("8. Combat", "Enable Cooldown Checks", true, "Enforce skill cooldowns.");
			EnableReviveChecks = ((BaseUnityPlugin)this).Config.Bind<bool>("8. Combat", "Enable Revive Checks", true, "Prevent self-revive.");
			EnableSpamChecks = ((BaseUnityPlugin)this).Config.Bind<bool>("8. Combat", "Enable Spam Checks", true, "Prevents macro-spamming items/swaps/blocking.");
			MinConsumableInterval = ((BaseUnityPlugin)this).Config.Bind<float>("8. Combat", "Min Consumable Interval", 0.4f, "Min seconds between using items.");
			MinWeaponSwapInterval = ((BaseUnityPlugin)this).Config.Bind<float>("8. Combat", "Min Weapon Swap Interval", 0.25f, "Min seconds between switching weapons.");
			MinBlockInterval = ((BaseUnityPlugin)this).Config.Bind<float>("8. Combat", "Min Block Interval", 0.1f, "Min seconds between block inputs.");
			EnablePunishmentSystem = ((BaseUnityPlugin)this).Config.Bind<bool>("9. Punishment", "Enable Auto-Punish", true, "Kick/Ban on infractions.");
			WarningsUntilAction = ((BaseUnityPlugin)this).Config.Bind<int>("9. Punishment", "Infractions Limit", 5, "Warnings before action.");
			ActionType = ((BaseUnityPlugin)this).Config.Bind<string>("9. Punishment", "Action Type", "Kick", new ConfigDescription("Action to take", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[2] { "Kick", "Ban" }), Array.Empty<object>()));
			EnableDetailedLogs = ((BaseUnityPlugin)this).Config.Bind<bool>("10. Logging", "Enable Detailed Logs", true, "Log details.");
			LogPlayerName = ((BaseUnityPlugin)this).Config.Bind<bool>("10. Logging", "Log Name", true, "");
			LogPlayerID = ((BaseUnityPlugin)this).Config.Bind<bool>("10. Logging", "Log ID", true, "");
			LogInfractionCount = ((BaseUnityPlugin)this).Config.Bind<bool>("10. Logging", "Log Count", true, "");
			LogInfractionDetails = ((BaseUnityPlugin)this).Config.Bind<bool>("10. Logging", "Log Details", true, "");
			ConnectionLimiter.BindConfig(((BaseUnityPlugin)this).Config);
			StatTracking.BindConfig(((BaseUnityPlugin)this).Config);
		}

		private void InitEasySettingsSafe()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			Settings.OnInitialized.AddListener(new UnityAction(AddSettings));
			Settings.OnApplySettings.AddListener((UnityAction)delegate
			{
				((BaseUnityPlugin)this).Config.Reload();
				((BaseUnityPlugin)this).Config.Save();
				ParseTrustedSteamIDs();
				ParseBannedSteamIDs();
				ParseRequiredMods();
				ParseBlacklistedMods();
				ParseWhitelistedMods();
				if (EnableRemoteDatabase.Value)
				{
					FetchRemoteDatabase();
				}
				if (EnableThunderstoreWhitelist.Value)
				{
					StartThunderstoreIntegration();
				}
				Log.LogInfo((object)"[HAC] Settings applied and reloaded successfully.");
			});
		}

		private void AddSettings()
		{
			SettingsTab orAddCustomTab = Settings.GetOrAddCustomTab("HardAntiCheat");
			orAddCustomTab.AddHeader("General");
			orAddCustomTab.AddToggle(EnableAntiCheat);
			orAddCustomTab.AddToggle(DisableForHost);
			orAddCustomTab.AddTextField(TrustedSteamIDs, "Text...");
			orAddCustomTab.AddTextField(BannedSteamIDs, "Text...");
			orAddCustomTab.AddSlider(VerificationTimeout, false);
			orAddCustomTab.AddHeader("Mod Lists");
			orAddCustomTab.AddToggle(EnableRequiredMods);
			orAddCustomTab.AddTextField(RequiredModGUIDs, "Text...");
			orAddCustomTab.AddToggle(EnableModBlacklist);
			orAddCustomTab.AddTextField(BlacklistedModGUIDs, "Text...");
			orAddCustomTab.AddToggle(EnableModWhitelist);
			orAddCustomTab.AddTextField(WhitelistedModGUIDs, "Text...");
			orAddCustomTab.AddHeader("Thunderstore Integration");
			orAddCustomTab.AddToggle(EnableThunderstoreWhitelist);
			orAddCustomTab.AddTextField(ThunderstoreCommunity, "Text...");
			orAddCustomTab.AddToggle(BlockDeprecatedMods);
			orAddCustomTab.AddToggle(AutoWhitelistNewMods);
			orAddCustomTab.AddToggle(AutoBlacklistUnknownMods);
			orAddCustomTab.AddAdvancedSlider(ThunderstoreRefreshInterval, false);
			orAddCustomTab.AddAdvancedSlider(UnknownModGracePeriod, false);
			orAddCustomTab.AddHeader("Remote Database");
			orAddCustomTab.AddToggle(EnableRemoteDatabase);
			orAddCustomTab.AddTextField(RemoteDatabaseURL, "Text...");
			orAddCustomTab.AddHeader("Movement Detection");
			orAddCustomTab.AddToggle(EnableMovementChecks);
			orAddCustomTab.AddSlider(MaxEffectiveSpeed, false);
			orAddCustomTab.AddSlider(TeleportDistanceThreshold, false);
			orAddCustomTab.AddToggle(EnableAirborneChecks);
			orAddCustomTab.AddToggle(EnableSpeedChecks);
			orAddCustomTab.AddAdvancedSlider(JumpThreshold);
			orAddCustomTab.AddHeader("Combat & Stats");
			orAddCustomTab.AddToggle(EnableExperienceChecks);
			orAddCustomTab.AddToggle(EnableCooldownChecks);
			orAddCustomTab.AddToggle(EnableReviveChecks);
			orAddCustomTab.AddToggle(EnableSpamChecks);
			orAddCustomTab.AddSlider(MinBlockInterval, false);
			StatTracking.AddSettings(orAddCustomTab);
			orAddCustomTab.AddHeader("Punishment");
			orAddCustomTab.AddToggle(EnablePunishmentSystem);
			orAddCustomTab.AddAdvancedSlider(WarningsUntilAction);
			string[] actions = new string[2] { "Kick", "Ban" };
			int num = ((ActionType.Value == "Ban") ? 1 : 0);
			AtlyssDropdown val = orAddCustomTab.AddDropdown("Action Type", actions, num);
			val.OnValueChanged.AddListener((UnityAction<int>)delegate(int num2)
			{
				ActionType.Value = actions[num2];
			});
			orAddCustomTab.AddHeader("Logging");
			orAddCustomTab.AddToggle(EnableDetailedLogs);
			ConnectionLimiter.AddSettings(orAddCustomTab);
		}

		private static void ParseBlacklistedMods()
		{
			ServerBlacklistedGUIDs.Clear();
			string value = BlacklistedModGUIDs.Value;
			if (!string.IsNullOrWhiteSpace(value))
			{
				string[] array = value.Split(',');
				string[] array2 = array;
				foreach (string text in array2)
				{
					string text2 = text.Trim();
					if (!string.IsNullOrEmpty(text2))
					{
						ServerBlacklistedGUIDs.Add(text2.ToLower());
					}
				}
			}
			Log.LogInfo((object)$"[HAC] Blacklist updated. {ServerBlacklistedGUIDs.Count} mods banned.");
		}

		private static void ParseWhitelistedMods()
		{
			ServerWhitelistedGUIDs.Clear();
			string value = WhitelistedModGUIDs.Value;
			if (!string.IsNullOrWhiteSpace(value))
			{
				string[] array = value.Split(',');
				string[] array2 = array;
				foreach (string text in array2)
				{
					string text2 = text.Trim();
					if (!string.IsNullOrEmpty(text2))
					{
						ServerWhitelistedGUIDs.Add(text2.ToLower());
					}
				}
			}
			Log.LogInfo((object)$"[HAC] Whitelist updated. {ServerWhitelistedGUIDs.Count} mods permitted.");
		}

		private static void ParseRequiredMods()
		{
			ServerRequiredGUIDs.Clear();
			string value = RequiredModGUIDs.Value;
			if (!string.IsNullOrWhiteSpace(value))
			{
				string[] array = value.Split(',');
				string[] array2 = array;
				foreach (string text in array2)
				{
					string text2 = text.Trim();
					if (!string.IsNullOrEmpty(text2))
					{
						ServerRequiredGUIDs.Add(text2.ToLower());
					}
				}
			}
			Log.LogInfo((object)$"[HAC] Required mods updated. {ServerRequiredGUIDs.Count} mods required.");
		}

		private static void ParseTrustedSteamIDs()
		{
			WhitelistedUsers.Clear();
			string[] array = TrustedSteamIDs.Value.Split(',');
			string[] array2 = array;
			foreach (string text in array2)
			{
				if (ulong.TryParse(text.Trim(), out var result))
				{
					WhitelistedUsers.Add(result);
				}
			}
		}

		private static void ParseBannedSteamIDs()
		{
			ServerBannedUsers.Clear();
			string value = BannedSteamIDs.Value;
			if (!string.IsNullOrWhiteSpace(value))
			{
				string[] array = value.Split(',');
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (ulong.TryParse(text.Trim(), out var result))
					{
						ServerBannedUsers.Add(result);
					}
				}
			}
			Log.LogInfo((object)$"[HAC] Banned users updated. {ServerBannedUsers.Count} users banned.");
		}

		public static bool IsPlayerExempt(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (DisableForHost.Value && player._isHostPlayer)
			{
				return true;
			}
			if (!string.IsNullOrEmpty(player._steamID) && ulong.TryParse(player._steamID, out var result) && WhitelistedUsers.Contains(result))
			{
				return true;
			}
			return false;
		}

		public static bool IsSteamIdTrusted(ulong steamId)
		{
			return WhitelistedUsers.Contains(steamId);
		}

		public static bool IsPlayerBanned(ulong steamId)
		{
			return ServerBannedUsers.Contains(steamId);
		}

		private static Player GetPlayerBySteamID(ulong steamId)
		{
			Player val = default(Player);
			foreach (NetworkConnectionToClient value in NetworkServer.connections.Values)
			{
				if ((Object)(object)((NetworkConnection)value).identity != (Object)null && ((Component)((NetworkConnection)value).identity).TryGetComponent<Player>(ref val) && ulong.TryParse(val._steamID, out var result) && result == steamId)
				{
					return val;
				}
			}
			return null;
		}

		private void CheckAndArchiveLogFile()
		{
			try
			{
				if (File.Exists(InfractionLogPath))
				{
					FileInfo fileInfo = new FileInfo(InfractionLogPath);
					long num = (long)MaxLogFileSizeMB.Value * 1024L * 1024;
					if (fileInfo.Length > num)
					{
						File.Move(InfractionLogPath, Path.Combine(Path.GetDirectoryName(InfractionLogPath), $"{Path.GetFileNameWithoutExtension(InfractionLogPath)}_ARCHIVED_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.txt"));
					}
				}
			}
			catch
			{
			}
		}

		public static void LogInfraction(NetworkBehaviour instance, string cheatType, string details)
		{
			uint netId = instance.netId;
			if (ServerPunishmentCooldown.TryGetValue(netId, out var value) && Time.time < value)
			{
				return;
			}
			ServerPunishmentCooldown.Remove(netId);
			if (!ServerPlayerInfractionCount.ContainsKey(netId))
			{
				ServerPlayerInfractionCount[netId] = 0;
			}
			ServerPlayerInfractionCount[netId]++;
			int num = ServerPlayerInfractionCount[netId];
			int value2 = WarningsUntilAction.Value;
			Player component = ((Component)instance).GetComponent<Player>();
			string text = component?._nickname ?? "Unknown";
			string text2 = component?._steamID ?? $"netId:{netId}";
			if (EnablePunishmentSystem.Value && !AtlyssNetworkManager._current._soloMode && num >= value2)
			{
				if (!NetworkServer.active || !((Object)(object)component != (Object)null) || ((NetworkBehaviour)component).connectionToClient == null)
				{
					return;
				}
				ServerPunishmentCooldown[netId] = Time.time + 60f;
				string text3 = ActionType.Value.ToLower();
				string text4 = $"Player {text} (ID: {text2}) was automatically {text3.ToUpper()}ed for reaching {num}/{value2} infractions.";
				BroadcastServerMessage($"{text} was {text3}ed for {num} infractions");
				Log.LogWarning((object)text4);
				string capturedPunishment = text4;
				Task.Run(delegate
				{
					try
					{
						File.AppendAllText(InfractionLogPath, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [PUNISHMENT] " + capturedPunishment + Environment.NewLine);
					}
					catch
					{
					}
				});
				if ((Object)(object)HostConsole._current != (Object)null)
				{
					HostConsole._current.Init_ServerMessage("[HAC]: " + text4);
				}
				if (text3 == "kick")
				{
					((MonoBehaviour)Instance).StartCoroutine(Instance.DelayedKick(component, text4));
				}
				else
				{
					HC_PeerListEntry val = null;
					if ((Object)(object)HostConsole._current != (Object)null)
					{
						foreach (HC_PeerListEntry peerListEntry in HostConsole._current._peerListEntries)
						{
							if ((Object)(object)peerListEntry._netId != (Object)null && peerListEntry._netId.netId == netId)
							{
								val = peerListEntry;
								break;
							}
						}
					}
					if ((Object)(object)val != (Object)null)
					{
						CodeTalkerNetwork.SendNetworkPacket(component, (BinaryPacketBase)(object)new HAC_KickMessage
						{
							Reason = text4
						}, (CompressionType)0, CompressionLevel.Fastest);
						HostConsole._current._selectedPeerEntry = val;
						HostConsole._current.Ban_Peer();
					}
					else
					{
						((MonoBehaviour)Instance).StartCoroutine(Instance.DelayedKick(component, text4));
						Log.LogError((object)$"Could not find PeerListEntry for player {text} (netId: {netId}) to BAN, kicked instead.");
					}
				}
				ServerPlayerInfractionCount.Remove(netId);
			}
			else
			{
				if (!EnableDetailedLogs.Value)
				{
					return;
				}
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}]");
				List<string> list = new List<string>();
				if (LogPlayerName.Value)
				{
					list.Add("Player: " + text);
				}
				if (LogPlayerID.Value)
				{
					list.Add("ID: " + text2);
				}
				if (list.Any())
				{
					stringBuilder.Append(" " + string.Join(" | ", list));
				}
				stringBuilder.Append(" | Type: " + cheatType);
				if (LogInfractionDetails.Value)
				{
					stringBuilder.Append(" | Details: " + details);
				}
				if (LogInfractionCount.Value)
				{
					stringBuilder.Append($" | Warning {num}/{value2}");
				}
				string logMessage = stringBuilder.ToString();
				Log.LogWarning((object)logMessage);
				Task.Run(delegate
				{
					try
					{
						File.AppendAllText(InfractionLogPath, logMessage + Environment.NewLine);
					}
					catch
					{
					}
				});
			}
		}

		public static void ClearAllPlayerData(uint netId, ulong steamId)
		{
			ServerRemainingCooldowns.Remove(netId);
			ServerPlayerPositions.Remove(netId);
			ServerPlayerStats.Remove(netId);
			XpGainHistory.Remove(netId);
			ServerRunningXpTotal.Remove(netId);
			ServerPlayerAirborneStates.Remove(netId);
			ServerPlayerInitialSpeeds.Remove(netId);
			ServerPlayerMovementStats.Remove(netId);
			ServerPlayerMovementTimers.Remove(netId);
			ServerPlayerInfractionCount.Remove(netId);
			ServerPlayerGracePeriod.Remove(netId);
			ServerPunishmentCooldown.Remove(netId);
			ServerSpeedCheckCooldowns.Remove(netId);
			ServerJumpCheckCooldowns.Remove(netId);
			ServerAirborneCheckCooldowns.Remove(netId);
			LastConsumableTime.Remove(netId);
			LastWeaponSwapTime.Remove(netId);
			LastBlockTime.Remove(netId);
			StaminaCheckTimer.Remove(netId);
			if (PendingVerification.TryGetValue(steamId, out Coroutine value))
			{
				if ((Object)(object)Instance != (Object)null && value != null)
				{
					((MonoBehaviour)Instance).StopCoroutine(value);
				}
				PendingVerification.Remove(steamId);
			}
			VerifiedSteamIDs.Remove(steamId);
		}

		public static void OnClientReceivedRequest(PacketHeader header, BinaryPacketBase packet)
		{
			if (!(packet is HAC_ModListRequest hAC_ModListRequest) || !ulong.TryParse(Player._mainPlayer?._steamID, out var result) || hAC_ModListRequest.TargetSteamID != result)
			{
				return;
			}
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			List<string> list3 = new List<string>();
			foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
			{
				list.Add(pluginInfo.Key);
				list2.Add(pluginInfo.Value.Metadata.Name);
				list3.Add(pluginInfo.Value.Metadata.Version.ToString());
			}
			HAC_ModListResponse hAC_ModListResponse = new HAC_ModListResponse
			{
				ClientModGUIDs = list,
				ClientModNames = list2,
				ClientModVersions = list3
			};
			CodeTalkerNetwork.SendNetworkPacket(header.SenderID, (BinaryPacketBase)(object)hAC_ModListResponse, (CompressionType)0, CompressionLevel.Fastest);
		}

		public static void OnReceivedResponse(PacketHeader header, BinaryPacketBase packet)
		{
			if (!(packet is HAC_ModListResponse hAC_ModListResponse))
			{
				return;
			}
			ulong senderID = header.SenderID;
			if (NetworkServer.active)
			{
				if (PendingVerification.TryGetValue(senderID, out Coroutine value))
				{
					((MonoBehaviour)Instance).StartCoroutine(Instance.VerifyClientModsWithThunderstore(senderID, hAC_ModListResponse.ClientModGUIDs ?? new List<string>(), hAC_ModListResponse.ClientModNames ?? new List<string>(), hAC_ModListResponse.ClientModVersions ?? new List<string>(), value));
				}
			}
			else
			{
				if (!EnableModBlacklist.Value)
				{
					return;
				}
				Player val = ((IEnumerable<Player>)Object.FindObjectsOfType<Player>()).FirstOrDefault((Func<Player, bool>)((Player p) => p._isHostPlayer));
				if (!((Object)(object)val != (Object)null) || !(val._steamID == senderID.ToString()))
				{
					return;
				}
				List<string> source = hAC_ModListResponse.ClientModGUIDs ?? new List<string>();
				List<string> list = source.Where((string m) => ServerBlacklistedGUIDs.Contains(m)).ToList();
				if (list.Any())
				{
					Log.LogWarning((object)("[HAC] Host is using blacklisted mods: " + string.Join(", ", list) + ". Disconnecting for safety."));
					if ((Object)(object)ChatBehaviour._current != (Object)null)
					{
						ChatBehaviour._current.New_ChatMessage("<color=red><b>[HAC] Disconnected:</b> Host is using blacklisted mods!</color>");
					}
					if (NetworkClient.active)
					{
						NetworkClient.Disconnect();
					}
				}
			}
		}

		private IEnumerator VerifyClientModsWithThunderstore(ulong steamId, List<string> guids, List<string> names, List<string> versions, Coroutine timeoutCoroutine)
		{
			if (guids == null || guids.Count == 0)
			{
				Log.LogInfo((object)$"[HAC] Client {steamId} reported 0 mods. Vanilla client authorized.");
				AuthorizePlayer(steamId, timeoutCoroutine);
				yield break;
			}
			Dictionary<string, (string name, string version)> modInfoMap = new Dictionary<string, (string, string)>();
			for (int i = 0; i < guids.Count; i++)
			{
				if (!string.IsNullOrEmpty(guids[i]))
				{
					modInfoMap[guids[i].ToLower()] = (names[i], versions[i]);
				}
			}
			List<string> unauthorizedMods = new List<string>();
			List<string> unknownMods = new List<string>();
			foreach (string guid in guids)
			{
				string lowerGuid = guid.ToLower();
				if (string.IsNullOrEmpty(lowerGuid) || ServerWhitelistedGUIDs.Contains(lowerGuid) || ServerRequiredGUIDs.Contains(guid))
				{
					continue;
				}
				(string name, string version) info;
				string displayName = (modInfoMap.TryGetValue(lowerGuid, out info) ? (info.name + " (" + guid + ") v" + info.version) : guid);
				if (EnableModBlacklist.Value && ServerBlacklistedGUIDs.Contains(lowerGuid))
				{
					unauthorizedMods.Add(displayName);
					continue;
				}
				if (EnableThunderstoreWhitelist.Value)
				{
					if ((isThunderstoreInitialized && ThunderstoreKnownPackages.Contains(lowerGuid)) || !isThunderstoreInitialized)
					{
						continue;
					}
					unknownMods.Add(displayName);
				}
				else if (EnableModWhitelist.Value && !ServerWhitelistedGUIDs.Contains(lowerGuid))
				{
					unauthorizedMods.Add(displayName);
				}
				info = default((string, string));
			}
			if (unauthorizedMods.Any())
			{
				PunishVerificationFailure(steamId, "Unauthorized/Blacklisted Mods Detected: " + string.Join(", ", unauthorizedMods.Take(3)));
				yield break;
			}
			if (EnableRequiredMods.Value)
			{
				List<string> missingMods = ServerRequiredGUIDs.Where((string req) => !guids.Contains(req)).ToList();
				if (missingMods.Any())
				{
					PunishVerificationFailure(steamId, "Missing Required Mods: " + string.Join(", ", missingMods.Take(3)));
					yield break;
				}
			}
			if (unknownMods.Any() && isThunderstoreInitialized)
			{
				List<(string guid, string name, string version)> parsedMods = new List<(string, string, string)>();
				foreach (string unknownDisplay in unknownMods)
				{
					string guid2 = unknownDisplay;
					string name = "Unknown";
					string version = "?";
					int start = unknownDisplay.IndexOf('(');
					if (start > 0)
					{
						name = unknownDisplay.Substring(0, start).Trim();
						int end = unknownDisplay.IndexOf(')', start);
						if (end > start)
						{
							guid2 = unknownDisplay.Substring(start + 1, end - start - 1);
							int vIdx = unknownDisplay.IndexOf(" v", end);
							if (vIdx > end)
							{
								version = unknownDisplay.Substring(vIdx + 2).Trim();
							}
						}
					}
					parsedMods.Add((guid2, name, version));
				}
				Log.LogInfo((object)$"[HAC] Player authorized. {parsedMods.Count} mods queued for background Thunderstore verification.");
				((MonoBehaviour)Instance).StartCoroutine(StaggeredBackgroundVerify(parsedMods));
			}
			AuthorizePlayer(steamId, timeoutCoroutine);
		}

		public static void OnClientReceivedKickMessage(PacketHeader header, BinaryPacketBase packet)
		{
			if (packet is HAC_KickMessage hAC_KickMessage)
			{
				Log.LogWarning((object)("[HAC] Kicked by server. Reason: " + hAC_KickMessage.Reason));
				if ((Object)(object)ChatBehaviour._current != (Object)null)
				{
					ChatBehaviour._current.New_ChatMessage("<color=red><b>[HAC] Kicked by Server:</b> " + hAC_KickMessage.Reason + "</color>");
				}
			}
		}

		private static void AuthorizePlayer(ulong steamId, Coroutine coroutine)
		{
			if ((Object)(object)Instance != (Object)null && coroutine != null)
			{
				((MonoBehaviour)Instance).StopCoroutine(coroutine);
			}
			PendingVerification.Remove(steamId);
			VerifiedSteamIDs.Add(steamId);
		}

		public IEnumerator SendRequestWithDelay(Player player, ulong steamId)
		{
			yield return (object)new WaitForSeconds(3f);
			if (!((Object)(object)player == (Object)null))
			{
				HAC_ModListRequest req = new HAC_ModListRequest
				{
					TargetSteamID = steamId
				};
				CodeTalkerNetwork.SendNetworkPacket(player, (BinaryPacketBase)(object)req, (CompressionType)0, CompressionLevel.Fastest);
				Coroutine kickRoutine = ((MonoBehaviour)this).StartCoroutine(KickClientAfterDelay(player));
				PendingVerification[steamId] = kickRoutine;
			}
		}

		public IEnumerator KickClientAfterDelay(Player player)
		{
			if ((Object)(object)player == (Object)null || !ulong.TryParse(player._steamID, out var steamId))
			{
				yield break;
			}
			yield return (object)new WaitForSeconds(VerificationTimeout.Value);
			if ((Object)(object)player != (Object)null && !VerifiedSteamIDs.Contains(steamId) && PendingVerification.ContainsKey(steamId))
			{
				if (EnableRequiredMods.Value && ServerRequiredGUIDs.Count > 0)
				{
					PunishVerificationFailure(steamId, "Handshake Timeout. Required mods are missing.");
					yield break;
				}
				Log.LogInfo((object)$"[HAC] Client {steamId} timed out. Assumed Vanilla. Allowing join.");
				AuthorizePlayer(steamId, PendingVerification[steamId]);
			}
		}

		public IEnumerator DelayedKick(Player player, string reason)
		{
			if ((Object)(object)player != (Object)null)
			{
				CodeTalkerNetwork.SendNetworkPacket(player, (BinaryPacketBase)(object)new HAC_KickMessage
				{
					Reason = reason
				}, (CompressionType)0, CompressionLevel.Fastest);
				NetworkConnectionToClient conn = ((NetworkBehaviour)player).connectionToClient;
				yield return (object)new WaitForSeconds(0.5f);
				if (conn != null)
				{
					((NetworkConnection)conn).Disconnect();
				}
			}
		}

		public static void BroadcastServerMessage(string message)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			if (NetworkServer.active)
			{
				string text = ((message.Length > 120) ? (message.Substring(0, 120) + "...") : message);
				text = text.Replace("<", "").Replace(">", "");
				ServerMessage val = new ServerMessage
				{
					servMsg = "[HAC] " + text
				};
				NetworkServer.SendToAll<ServerMessage>(val, 0, false);
			}
		}

		private static void PunishVerificationFailure(ulong steamId, string reason)
		{
			if (PendingVerification.TryGetValue(steamId, out Coroutine value))
			{
				if ((Object)(object)Instance != (Object)null && value != null)
				{
					((MonoBehaviour)Instance).StopCoroutine(value);
				}
				PendingVerification.Remove(steamId);
			}
			VerifiedSteamIDs.Remove(steamId);
			Player playerBySteamID = GetPlayerBySteamID(steamId);
			if ((Object)(object)playerBySteamID != (Object)null && ((NetworkBehaviour)playerBySteamID).connectionToClient != null)
			{
				string text = playerBySteamID._nickname ?? "Unknown";
				Log.LogWarning((object)("[HAC] Kicking " + text + ". Reason: " + reason));
				BroadcastServerMessage(text + " was kicked: " + reason);
				if ((Object)(object)HostConsole._current != (Object)null)
				{
					HostConsole._current.Init_ServerMessage("[HAC]: Kicked " + text + ". " + reason);
				}
				((MonoBehaviour)Instance).StartCoroutine(Instance.DelayedKick(playerBySteamID, reason));
			}
		}

		public void FetchRemoteDatabase()
		{
			if (EnableRemoteDatabase.Value && !string.IsNullOrWhiteSpace(RemoteDatabaseURL.Value))
			{
				((MonoBehaviour)this).StartCoroutine(FetchRemoteDatabaseCoroutine());
			}
		}

		private IEnumerator FetchRemoteDatabaseCoroutine()
		{
			UnityWebRequest webRequest = UnityWebRequest.Get(RemoteDatabaseURL.Value);
			try
			{
				yield return webRequest.SendWebRequest();
				if (webRequest.isNetworkError || webRequest.isHttpError)
				{
					Log.LogError((object)("[HAC] Failed to fetch remote database: " + webRequest.error));
					yield break;
				}
				string content = webRequest.downloadHandler.text;
				string[] lines = content.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
				int added = 0;
				string[] array = lines;
				foreach (string line in array)
				{
					string trimmed = line.Trim();
					if (!string.IsNullOrEmpty(trimmed) && !trimmed.StartsWith("//") && !trimmed.StartsWith("#"))
					{
						ServerBlacklistedGUIDs.Add(trimmed);
						added++;
					}
				}
				Log.LogInfo((object)$"[HAC] Remote database loaded. Added {added} blacklisted mods.");
			}
			finally
			{
				((IDisposable)webRequest)?.Dispose();
			}
		}

		private void StartThunderstoreIntegration()
		{
			if (EnableThunderstoreWhitelist.Value)
			{
				if (thunderstoreRefreshCoroutine != null)
				{
					((MonoBehaviour)this).StopCoroutine(thunderstoreRefreshCoroutine);
				}
				thunderstoreRefreshCoroutine = ((MonoBehaviour)this).StartCoroutine(InitializeThunderstoreDatabase());
				((MonoBehaviour)this).StartCoroutine(AutoRefreshThunderstoreDatabase());
			}
		}

		private IEnumerator InitializeThunderstoreDatabase()
		{
			Log.LogInfo((object)"[HAC] Initializing Thunderstore package database...");
			yield return ((MonoBehaviour)this).StartCoroutine(FetchThunderstorePackageList());
			isThunderstoreInitialized = true;
			Log.LogInfo((object)$"[HAC] Thunderstore database initialized with {ThunderstoreKnownPackages.Count} known packages.");
		}

		private IEnumerator AutoRefreshThunderstoreDatabase()
		{
			while (true)
			{
				yield return (object)new WaitForSeconds(ThunderstoreRefreshInterval.Value * 60f);
				if (EnableThunderstoreWhitelist.Value)
				{
					Log.LogInfo((object)"[HAC] Auto-refreshing Thunderstore database...");
					yield return ((MonoBehaviour)this).StartCoroutine(FetchThunderstorePackageList());
				}
			}
		}

		private IEnumerator FetchThunderstorePackageList()
		{
			string communityUrl = "https://thunderstore.io/c/" + ThunderstoreCommunity.Value + "/api/v1/package/";
			UnityWebRequest request = UnityWebRequest.Get(communityUrl);
			try
			{
				request.SetRequestHeader("Accept", "application/json");
				request.timeout = 15;
				yield return request.SendWebRequest();
				if ((int)request.result == 1)
				{
					try
					{
						List<ThunderstorePackageV1> packages = JsonConvert.DeserializeObject<List<ThunderstorePackageV1>>(request.downloadHandler.text);
						if (packages == null)
						{
							yield break;
						}
						ThunderstoreKnownPackages.Clear();
						int deprecatedCount = 0;
						foreach (ThunderstorePackageV1 pkg in packages)
						{
							string fullName = pkg.full_name ?? (pkg.namespace_name + "-" + pkg.name);
							ThunderstoreKnownPackages.Add(fullName);
							List<string> possibleGUIDs = GeneratePossibleGUIDs(pkg);
							foreach (string guid in possibleGUIDs)
							{
								ThunderstoreKnownPackages.Add(guid.ToLower());
							}
							if (pkg.is_deprecated && BlockDeprecatedMods.Value)
							{
								foreach (string guid2 in possibleGUIDs)
								{
									ServerBlacklistedGUIDs.Add(guid2.ToLower());
								}
								ServerBlacklistedGUIDs.Add(fullName.ToLower());
								deprecatedCount++;
							}
							if (!AutoWhitelistNewMods.Value || pkg.is_deprecated || pkg.is_flagged)
							{
								continue;
							}
							foreach (string guid3 in possibleGUIDs)
							{
								ServerWhitelistedGUIDs.Add(guid3.ToLower());
							}
						}
						Log.LogInfo((object)$"[HAC] Thunderstore: {packages.Count} packages found, {deprecatedCount} deprecated mods auto-blacklisted.");
					}
					catch (Exception ex)
					{
						Exception ex2 = ex;
						Log.LogError((object)("[HAC] Failed to parse Thunderstore package list: " + ex2.Message));
					}
				}
				else
				{
					Log.LogWarning((object)("[HAC] Failed to fetch Thunderstore package list: " + request.error + ". Will retry on next refresh."));
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		private List<string> GeneratePossibleGUIDs(ThunderstorePackageV1 pkg)
		{
			List<string> list = new List<string>();
			string text = (pkg.namespace_name ?? pkg.owner ?? "").Replace("-", ".").Replace(" ", "");
			string text2 = (pkg.name ?? "").Replace("-", ".").Replace(" ", "");
			if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2))
			{
				return list;
			}
			list.Add(text + "." + text2);
			list.Add("com." + text + "." + text2);
			list.Add("org." + text + "." + text2);
			list.Add((text + "." + text2).ToLower());
			list.Add(text + "-" + text2);
			if (!string.IsNullOrEmpty(pkg.full_name))
			{
				list.Add(pkg.full_name);
				list.Add(pkg.full_name.Replace("-", "."));
			}
			return list.Distinct().ToList();
		}

		private IEnumerator StaggeredBackgroundVerify(List<(string guid, string name, string version)> mods)
		{
			foreach (var (guid, name, version) in mods)
			{
				yield return ((MonoBehaviour)this).StartCoroutine(BackgroundVerifyModAsync(guid, name, version));
				yield return (object)new WaitForSeconds(0.75f);
			}
		}

		private static string NormalizeForMatch(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			return s.ToLower().Replace(" ", "").Replace("_", "")
				.Replace("-", "")
				.Replace(".", "");
		}

		private static bool PackageMatchesMod(ThunderstorePackageV1 pkg, string guid, string displayName)
		{
			string text = NormalizeForMatch(displayName);
			string text2 = NormalizeForMatch(guid);
			string text3 = NormalizeForMatch(pkg.name ?? "");
			string text4 = NormalizeForMatch(pkg.full_name ?? "");
			string text5 = NormalizeForMatch(pkg.namespace_name ?? pkg.owner ?? "");
			if (!string.IsNullOrEmpty(text) && text == text3)
			{
				return true;
			}
			if (!string.IsNullOrEmpty(text) && text.Length >= 4 && (text4.Contains(text) || (text.Contains(text3) && text3.Length >= 4)))
			{
				return true;
			}
			if (!string.IsNullOrEmpty(text3) && text3.Length >= 4 && text2.Contains(text3))
			{
				return true;
			}
			if (!string.IsNullOrEmpty(text5) && text5.Length >= 4 && text2.Contains(text5))
			{
				return true;
			}
			if ((KnownModAliases.TryGetValue(displayName, out string value) || KnownModAliases.TryGetValue(guid, out value)) && NormalizeForMatch(value) == text3)
			{
				return true;
			}
			return false;
		}

		private IEnumerator BackgroundVerifyModAsync(string guid, string name, string version)
		{
			string lowerGuid = guid.ToLower();
			if (ThunderstoreKnownPackages.Contains(lowerGuid))
			{
				yield break;
			}
			string searchTerm = ((!string.IsNullOrEmpty(name) && name != "Unknown") ? name : guid);
			if (KnownModAliases.TryGetValue(name, out string aliasName) || KnownModAliases.TryGetValue(guid, out aliasName))
			{
				Log.LogDebug((object)("[HAC] [Background] '" + name + "' (" + guid + ") matched known alias -> searching as '" + aliasName + "'."));
				searchTerm = aliasName;
			}
			string searchUrl = "https://thunderstore.io/c/" + ThunderstoreCommunity.Value + "/api/v1/package/?search=" + Uri.EscapeDataString(searchTerm);
			UnityWebRequest request = UnityWebRequest.Get(searchUrl);
			try
			{
				request.SetRequestHeader("Accept", "application/json");
				request.timeout = 10;
				yield return request.SendWebRequest();
				if ((int)request.result == 1)
				{
					try
					{
						string json = request.downloadHandler.text;
						List<ThunderstorePackageV1> results = ((!json.TrimStart().StartsWith("{")) ? JsonConvert.DeserializeObject<List<ThunderstorePackageV1>>(json) : JsonConvert.DeserializeObject<ThunderstoreSearchResponse>(json)?.results);
						ThunderstorePackageV1 matchedPkg = null;
						if (results != null)
						{
							foreach (ThunderstorePackageV1 pkg in results)
							{
								if (PackageMatchesMod(pkg, guid, name))
								{
									matchedPkg = pkg;
									break;
								}
							}
						}
						if (matchedPkg != null)
						{
							ThunderstoreKnownPackages.Add(lowerGuid);
							foreach (string g in GeneratePossibleGUIDs(matchedPkg))
							{
								ThunderstoreKnownPackages.Add(g.ToLower());
							}
							if (AutoWhitelistNewMods.Value && !matchedPkg.is_deprecated && !matchedPkg.is_flagged)
							{
								ServerWhitelistedGUIDs.Add(lowerGuid);
								foreach (string g2 in GeneratePossibleGUIDs(matchedPkg))
								{
									ServerWhitelistedGUIDs.Add(g2.ToLower());
								}
							}
							Log.LogDebug((object)("[HAC] [Background] Verified and cached " + name + " (" + guid + ") v" + version + " on Thunderstore."));
							yield break;
						}
						Log.LogWarning((object)("[HAC] [Background] '" + name + "' (" + guid + ") v" + version + " was NOT found on Thunderstore. Treating as unknown."));
						if (AutoBlacklistUnknownMods.Value)
						{
							string lowerGuidBL = guid.ToLower();
							if (ServerBlacklistedGUIDs.Add(lowerGuidBL))
							{
								string existing = BlacklistedModGUIDs.Value?.Trim() ?? "";
								BlacklistedModGUIDs.Value = (string.IsNullOrEmpty(existing) ? lowerGuidBL : (existing + "," + lowerGuidBL));
								Log.LogWarning((object)("[HAC] [Background] '" + name + "' (" + guid + ") auto-blacklisted and saved to config."));
							}
						}
					}
					catch (Exception ex)
					{
						Exception ex2 = ex;
						Log.LogWarning((object)("[HAC] [Background] Parse error for '" + guid + "': " + ex2.Message));
					}
				}
				else
				{
					Log.LogWarning((object)("[HAC] [Background] Failed to verify '" + name + "' (" + guid + ") against Thunderstore: " + request.error));
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			string fullName = Directory.GetParent(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location)).FullName;
			string text = Path.Combine(fullName, "HardAntiCheat");
			Directory.CreateDirectory(text);
			InfractionLogPath = Path.Combine(text, "HardAntiCheat_InfractionLog.txt");
			InitConfig();
			CheckAndArchiveLogFile();
			ParseTrustedSteamIDs();
			ParseBannedSteamIDs();
			ParseRequiredMods();
			ParseBlacklistedMods();
			ParseWhitelistedMods();
			if (EnableRemoteDatabase.Value)
			{
				FetchRemoteDatabase();
			}
			TrustedSteamIDs.SettingChanged += delegate
			{
				ParseTrustedSteamIDs();
			};
			BannedSteamIDs.SettingChanged += delegate
			{
				ParseBannedSteamIDs();
			};
			RequiredModGUIDs.SettingChanged += delegate
			{
				ParseRequiredMods();
			};
			BlacklistedModGUIDs.SettingChanged += delegate
			{
				ParseBlacklistedMods();
			};
			WhitelistedModGUIDs.SettingChanged += delegate
			{
				ParseWhitelistedMods();
			};
			RemoteDatabaseURL.SettingChanged += delegate
			{
				if (EnableRemoteDatabase.Value)
				{
					FetchRemoteDatabase();
				}
			};
			EnableThunderstoreWhitelist.SettingChanged += delegate
			{
				if (EnableThunderstoreWhitelist.Value)
				{
					StartThunderstoreIntegration();
				}
			};
			InitEasySettingsSafe();
			Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(Assembly.GetExecutingAssembly());
			foreach (Type type in typesFromAssembly)
			{
				try
				{
					harmony.CreateClassProcessor(type).Patch();
				}
				catch (Exception ex)
				{
					Log.LogError((object)("[HardAntiCheat] Patch class " + type.Name + " failed and was skipped: " + ex.Message));
				}
			}
			Log.LogInfo((object)string.Format("[{0}] loaded. ReqList: {1} | Blacklist: {2} | Whitelist: {3} | Thunderstore: {4}", "HardAntiCheat", EnableRequiredMods.Value, EnableModBlacklist.Value, EnableModWhitelist.Value, EnableThunderstoreWhitelist.Value));
		}

		private void Start()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			object obj = <>O.<0>__OnClientReceivedRequest;
			if (obj == null)
			{
				BinaryPacketListener val = OnClientReceivedRequest;
				<>O.<0>__OnClientReceivedRequest = val;
				obj = (object)val;
			}
			CodeTalkerNetwork.RegisterBinaryListener<HAC_ModListRequest>((BinaryPacketListener)obj);
			object obj2 = <>O.<1>__OnReceivedResponse;
			if (obj2 == null)
			{
				BinaryPacketListener val2 = OnReceivedResponse;
				<>O.<1>__OnReceivedResponse = val2;
				obj2 = (object)val2;
			}
			CodeTalkerNetwork.RegisterBinaryListener<HAC_ModListResponse>((BinaryPacketListener)obj2);
			object obj3 = <>O.<2>__OnClientReceivedKickMessage;
			if (obj3 == null)
			{
				BinaryPacketListener val3 = OnClientReceivedKickMessage;
				<>O.<2>__OnClientReceivedKickMessage = val3;
				obj3 = (object)val3;
			}
			CodeTalkerNetwork.RegisterBinaryListener<HAC_KickMessage>((BinaryPacketListener)obj3);
			if (EnableThunderstoreWhitelist.Value && !isThunderstoreInitialized)
			{
				Log.LogInfo((object)"[HAC] Pre-warming Thunderstore cache...");
				StartThunderstoreIntegration();
			}
			((MonoBehaviour)this).StartCoroutine(ConnectionLimiter.Run());
		}
	}
	[HarmonyPatch(typeof(NextServer), "OnConnectionStatusChanged")]
	public static class NextServerAcceptGuardPatch
	{
		private sealed class Live
		{
			public ulong SteamId;

			public float Time;

			public bool Connected;
		}

		private static readonly Dictionary<uint, Live> Table;

		private static readonly Dictionary<ulong, float> LastLog;

		private const float PendingTimeoutSeconds = 15f;

		private const float LogCooldownSeconds = 5f;

		private static readonly bool Ready;

		private static readonly FieldRef<NextServer, BidirectionalDictionary<HSteamNetConnection, int>> ConnMap;

		private static readonly FieldRef<NextServer, HSteamListenSocket> ListenSocket;

		static NextServerAcceptGuardPatch()
		{
			Table = new Dictionary<uint, Live>();
			LastLog = new Dictionary<ulong, float>();
			try
			{
				ConnMap = AccessTools.FieldRefAccess<NextServer, BidirectionalDictionary<HSteamNetConnection, int>>("connToMirrorID");
				ListenSocket = AccessTools.FieldRefAccess<NextServer, HSteamListenSocket>("listenSocket");
				Ready = true;
			}
			catch (Exception ex)
			{
				Main.Log.LogError((object)("[HAC] [AcceptGuard] Could not bind NextServer fields, accept-time guard disabled: " + ex.Message));
			}
		}

		public static bool Prefix(NextServer __instance, SteamNetConnectionStatusChangedCallback_t param)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Invalid comparison between Unknown and I4
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Invalid comparison between Unknown and I4
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Invalid comparison between Unknown and I4
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Invalid comparison between Unknown and I4
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			if (!Ready)
			{
				return true;
			}
			try
			{
				ESteamNetworkingConnectionState eState = param.m_info.m_eState;
				uint handle = param.m_hConn.m_HSteamNetConnection;
				if ((int)eState != 1)
				{
					if ((int)eState == 3)
					{
						if (Table.TryGetValue(handle, out Live value))
						{
							value.Connected = true;
						}
					}
					else if ((int)eState == 4 || (int)eState == 5)
					{
						Table.Remove(handle);
					}
					return true;
				}
				if (!Main.EnableAntiCheat.Value || !ConnectionLimiter.EnableConnectionLimit.Value || !ConnectionLimiter.EnableAcceptGuard.Value)
				{
					return true;
				}
				if (!((HSteamListenSocket)(ref param.m_info.m_hListenSocket)).Equals(ListenSocket.Invoke(__instance)))
				{
					return true;
				}
				ulong steamId = ((SteamNetworkingIdentity)(ref param.m_info.m_identityRemote)).GetSteamID64();
				if (steamId == 0L || Main.IsSteamIdTrusted(steamId))
				{
					return true;
				}
				float unscaledTime = Time.unscaledTime;
				Prune(__instance, unscaledTime);
				int num = Math.Max(1, ConnectionLimiter.MaxConnectionsPerSteamId.Value);
				List<uint> list = (from kv in Table
					where kv.Key != handle && kv.Value.SteamId == steamId
					select kv.Key).ToList();
				Live value2 = new Live
				{
					SteamId = steamId,
					Time = unscaledTime
				};
				if (list.Count < num)
				{
					Table[handle] = value2;
					return true;
				}
				bool value3 = ConnectionLimiter.KickAllOnViolation.Value;
				bool value4 = ConnectionLimiter.LogOnly.Value;
				string text = (value4 ? "Log-only, allowing it" : (value3 ? "Refusing it and dropping all of this Steam ID's connections" : "Refusing the new connection"));
				LogThrottled(steamId, $"[HAC] [AcceptGuard] SteamID {steamId} already has {list.Count} connection(s) (max {num}). {text}.");
				if (value4)
				{
					Table[handle] = value2;
					return true;
				}
				if (value3)
				{
					foreach (uint item in list)
					{
						DropExisting(__instance, item);
					}
				}
				SteamNetworkingSockets.CloseConnection(param.m_hConn, 0, "Connection limit", false);
				return false;
			}
			catch (Exception ex)
			{
				Main.Log.LogWarning((object)("[HAC] [AcceptGuard] Error, falling back to vanilla accept: " + ex.Message));
				return true;
			}
		}

		private static void DropExisting(NextServer server, uint handle)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			Table.Remove(handle);
			HSteamNetConnection val = default(HSteamNetConnection);
			((HSteamNetConnection)(ref val))..ctor(handle);
			int num = default(int);
			if (ConnMap.Invoke(server).TryGetValue(val, ref num))
			{
				server.Disconnect(num);
			}
			else
			{
				SteamNetworkingSockets.CloseConnection(val, 0, "Connection limit", false);
			}
		}

		private static void Prune(NextServer server, float now)
		{
			if (Table.Count == 0)
			{
				return;
			}
			BidirectionalDictionary<HSteamNetConnection, int> map = ConnMap.Invoke(server);
			int num = default(int);
			foreach (uint item in (from kv in Table
				where kv.Value.Connected ? (!map.TryGetValue(new HSteamNetConnection(kv.Key), ref num)) : (now - kv.Value.Time > 15f)
				select kv.Key).ToList())
			{
				Table.Remove(item);
			}
		}

		private static void LogThrottled(ulong steamId, string message)
		{
			float unscaledTime = Time.unscaledTime;
			if (!LastLog.TryGetValue(steamId, out var value) || !(unscaledTime - value < 5f))
			{
				LastLog[steamId] = unscaledTime;
				Main.Log.LogWarning((object)message);
			}
		}
	}
	public static class ConnectionLimiter
	{
		private sealed class Tracked
		{
			public ulong SteamId;
		}

		public static ConfigEntry<bool> EnableConnectionLimit;

		public static ConfigEntry<int> MaxConnectionsPerSteamId;

		public static ConfigEntry<bool> KickAllOnViolation;

		public static ConfigEntry<float> ScanIntervalSeconds;

		public static ConfigEntry<bool> LogOnly;

		public static ConfigEntry<bool> EnableAcceptGuard;

		private static readonly Dictionary<int, Tracked> Known = new Dictionary<int, Tracked>();

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

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

		private static readonly Dictionary<ulong, float> LastViolationLog = new Dictionary<ulong, float>();

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

		private const int MaxAddressAttempts = 3;

		private const float CleanupDelaySeconds = 2f;

		private const float ViolationLogCooldown = 5f;

		public static void BindConfig(ConfigFile config)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			EnableConnectionLimit = config.Bind<bool>("11. Connection Limit", "Enable Connection Limit", true, "Disconnect Steam IDs that hold more simultaneous connections than allowed (stops multi-client cloner mods).");
			MaxConnectionsPerSteamId = config.Bind<int>("11. Connection Limit", "Max Connections Per Steam ID", 1, new ConfigDescription("Max simultaneous connections allowed per Steam ID.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 16), Array.Empty<object>()));
			KickAllOnViolation = config.Bind<bool>("11. Connection Limit", "Kick All Connections On Violation", true, "When a Steam ID goes over the limit, disconnect every connection it has, including the original one. If false, only the newest extras are dropped and the oldest connections are kept.");
			ScanIntervalSeconds = config.Bind<float>("11. Connection Limit", "Scan Interval (Seconds)", 0.25f, new ConfigDescription("How often connections are checked while hosting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), Array.Empty<object>()));
			LogOnly = config.Bind<bool>("11. Connection Limit", "Log Only (Test Mode)", false, "If true, violations are only logged and nobody is disconnected. Use this to test first.");
			EnableAcceptGuard = config.Bind<bool>("11. Connection Limit", "Enforce Limit At Accept", true, "Enforce the limit the moment a connection arrives, before the game sees it. A connection over the limit is refused, and (if Kick All is on) the Steam ID's other connections are dropped too.");
		}

		public static void AddSettings(SettingsTab tab)
		{
			tab.AddHeader("Connection Limit");
			tab.AddToggle(EnableConnectionLimit);
			tab.AddAdvancedSlider(MaxConnectionsPerSteamId);
			tab.AddToggle(KickAllOnViolation);
			tab.AddToggle(LogOnly);
			tab.AddToggle(EnableAcceptGuard);
		}

		public static IEnumerator Run()
		{
			while (true)
			{
				yield return (object)new WaitForSeconds(Mathf.Max(0.05f, ScanIntervalSeconds.Value));
				if (!NetworkServer.active || !Main.EnableAntiCheat.Value || !EnableConnectionLimit.Value)
				{
					ResetState();
					continue;
				}
				try
				{
					Scan();
				}
				catch (Exception ex)
				{
					Exception e = ex;
					Main.Log.LogWarning((object)("[HAC] [ConnLimit] Scan error: " + e.Message));
				}
			}
		}

		private static void ResetState()
		{
			if (Known.Count != 0 || ResolveAttempts.Count != 0 || PendingCleanup.Count != 0 || LastViolationLog.Count != 0 || WarnedAddresses.Count != 0)
			{
				Known.Clear();
				ResolveAttempts.Clear();
				PendingCleanup.Clear();
				LastViolationLog.Clear();
				WarnedAddresses.Clear();
			}
		}

		private static Player GetPlayer(NetworkConnectionToClient conn)
		{
			return (conn != null && (Object)(object)((NetworkConnection)conn).identity != (Object)null) ? ((Component)((NetworkConnection)conn).identity).GetComponent<Player>() : null;
		}

		private static void Scan()
		{
			Dictionary<int, NetworkConnectionToClient> connections = NetworkServer.connections;
			foreach (int item in Known.Keys.Where((int k) => !connections.ContainsKey(k)).ToList())
			{
				Known.Remove(item);
			}
			foreach (int item2 in ResolveAttempts.Keys.Where((int k) => !connections.ContainsKey(k)).ToList())
			{
				ResolveAttempts.Remove(item2);
			}
			foreach (int item3 in PendingCleanup.Keys.Where((int k) => !connections.ContainsKey(k)).ToList())
			{
				PendingCleanup.Remove(item3);
			}
			foreach (NetworkConnectionToClient item4 in connections.Values.ToList())
			{
				if (item4 != null && (object)item4 != NetworkServer.localConnection && !Known.ContainsKey(((NetworkConnection)item4).connectionId) && TryResolveSteamId(item4, out var steamId))
				{
					Known[((NetworkConnection)item4).connectionId] = new Tracked
					{
						SteamId = steamId
					};
					Main.Log.LogInfo((object)$"[HAC] [ConnLimit] New connection {((NetworkConnection)item4).connectionId} from SteamID {steamId}");
				}
			}
			RunCleanup(connections);
			Dictionary<ulong, List<NetworkConnectionToClient>> dictionary = new Dictionary<ulong, List<NetworkConnectionToClient>>();
			HashSet<ulong> hashSet = new HashSet<ulong>();
			foreach (KeyValuePair<int, Tracked> item5 in Known)
			{
				if (!PendingCleanup.ContainsKey(item5.Key) && connections.TryGetValue(item5.Key, out var value) && value != null)
				{
					ulong steamId2 = item5.Value.SteamId;
					Player player = GetPlayer(value);
					if ((Object)(object)player != (Object)null && Main.IsPlayerExempt(player))
					{
						hashSet.Add(steamId2);
					}
					if (!dictionary.TryGetValue(steamId2, out var value2))
					{
						value2 = (dictionary[steamId2] = new List<NetworkConnectionToClient>());
					}
					value2.Add(value);
				}
			}
			int value3 = MaxConnectionsPerSteamId.Value;
			bool value4 = KickAllOnViolation.Value;
			foreach (KeyValuePair<ulong, List<NetworkConnectionToClient>> item6 in dictionary)
			{
				if (hashSet.Contains(item6.Key) || item6.Value.Count <= value3)
				{
					continue;
				}
				List<NetworkConnectionToClient> list2 = item6.Value.OrderBy((NetworkConnectionToClient c) => ((NetworkConnection)c).connectionId).ToList();
				List<NetworkConnectionToClient> list3 = (value4 ? list2 : list2.Skip(value3).ToList());
				if (ShouldLog(item6.Key))
				{
					string text = string.Join(", ", list2.Select((NetworkConnectionToClient c) => ((NetworkConnection)c).connectionId.ToString()).ToArray());
					string text2 = (LogOnly.Value ? "Log-only, would disconnect" : "Disconnecting");
					string text3 = (value4 ? "all" : "the newest extra");
					Main.Log.LogWarning((object)$"[HAC] [ConnLimit] SteamID {item6.Key} has {item6.Value.Count} connections (max {value3}; ids {text}). {text2} {text3} connection(s).");
				}
				if (LogOnly.Value)
				{
					continue;
				}
				foreach (NetworkConnectionToClient item7 in list3)
				{
					Kick(item7);
				}
			}
		}

		private static bool TryResolveSteamId(NetworkConnectionToClient conn, out ulong steamId)
		{
			steamId = 0uL;
			ResolveAttempts.TryGetValue(((NetworkConnection)conn).connectionId, out var value);
			if (value < 3)
			{
				ResolveAttempts[((NetworkConnection)conn).connectionId] = value + 1;
				string address = conn.address;
				if (!string.IsNullOrEmpty(address))
				{
					if (ulong.TryParse(address, out steamId) && steamId != 0)
					{
						return true;
					}
					if (WarnedAddresses.Add(address))
					{
						Main.Log.LogWarning((object)("[HAC] [ConnLimit] Connection address '" + address + "' is not a SteamID64; trying the player's Steam ID instead."));
					}
				}
			}
			Player player = GetPlayer(conn);
			if ((Object)(object)player != (Object)null && ulong.TryParse(player._steamID, out steamId) && steamId != 0)
			{
				return true;
			}
			steamId = 0uL;
			return false;
		}

		private static bool ShouldLog(ulong steamId)
		{
			float unscaledTime = Time.unscaledTime;
			if (LastViolationLog.TryGetValue(steamId, out var value) && unscaledTime - value < 5f)
			{
				return false;
			}
			LastViolationLog[steamId] = unscaledTime;
			return true;
		}

		private static void Kick(NetworkConnectionToClient conn)
		{
			PendingCleanup[((NetworkConnection)conn).connectionId] = Time.unscaledTime;
			try
			{
				((NetworkConnection)conn).Disconnect();
			}
			catch (Exception ex)
			{
				Main.Log.LogWarning((object)$"[HAC] [ConnLimit] Disconnect failed for connection {((NetworkConnection)conn).connectionId}: {ex.Message}");
			}
		}

		private static void RunCleanup(Dictionary<int, NetworkConnectionToClient> connections)
		{
			foreach (int item in PendingCleanup.Keys.ToList())
			{
				float num = PendingCleanup[item];
				if (num == float.MaxValue || Time.unscaledTime - num < 2f)
				{
					continue;
				}
				if (connections.TryGetValue(item, out NetworkConnectionToClient value) && value != null)
				{
					try
					{
						NetworkServer.DestroyPlayerForConnection(value);
						Main.Log.LogInfo((object)$"[HAC] [ConnLimit] Connection {item} was still listed after being kicked; removed its player object.");
					}
					catch (Exception ex)
					{
						Main.Log.LogWarning((object)$"[HAC] [ConnLimit] Cleanup failed for connection {item}: {ex.Message}");
					}
				}
				PendingCleanup[item] = float.MaxValue;
			}
		}
	}
	public struct PlayerPositionData
	{
		public Vector3 Position;

		public float Timestamp;
	}
	public struct PlayerStatsData
	{
		public int Level;

		public int Experience;
	}
	public struct PlayerAirborneData
	{
		public float AirTime;

		public Vector3 LastGroundedPosition;

		public int ServerSideJumpCount;

		public float LastVerticalPosition;

		public float VerticalStallTime;
	}
	public struct PlayerMovementStats
	{
		public List<float> RecentSpeeds;

		public float TimeAtMaxSpeed;
	}
	public class ThunderstorePackageV1
	{
		public string uuid4 { get; set; }

		[JsonProperty("namespace")]
		public string namespace_name { get; set; }

		public string name { get; set; }

		public string full_name { get; set; }

		public string owner { get; set; }

		[JsonIgnore]
		public string latest { get; set; }

		public bool is_deprecated { get; set; }

		[JsonProperty("is_flagged")]
		public bool is_flagged { get; set; }

		public string date_created { get; set; }

		public string date_updated { get; set; }

		public int downloads { get; set; }
	}
	public class ThunderstorePackageDetail
	{
		public string uuid4 { get; set; }

		[JsonProperty("namespace")]
		public string namespace_name { get; set; }

		public string name { get; set; }

		public string full_name { get; set; }

		public string owner { get; set; }

		[JsonIgnore]
		public string latest { get; set; }

		public bool is_deprecated { get; set; }

		[JsonProperty("is_flagged")]
		public bool is_flagged { get; set; }

		public List<string> dependencies { get; set; }

		public string date_created { get; set; }

		public string date_updated { get; set; }
	}
	public class ThunderstoreModCache
	{
		public string BepInExGUID { get; set; }

		public string ThunderstoreUUID { get; set; }

		public string Namespace { get; set; }

		public string Name { get; set; }

		public bool IsDeprecated { get; set; }

		public bool IsFlagged { get; set; }

		public DateTime LastVerified { get; set; }

		public DateTime FirstSeen { get; set; }
	}
	public class ThunderstoreSearchResponse
	{
		public string next { get; set; }

		public string previous { get; set; }

		public List<ThunderstorePackageV1> results { get; set; }
	}
	public class HAC_ModListRequest : BinaryPacketBase
	{
		public override string PacketSignature => "HAC_Request_V6";

		public ulong TargetSteamID { get; set; }

		public override byte[] Serialize()
		{
			using MemoryStream memoryStream = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			binaryWriter.Write(TargetSteamID);
			return memoryStream.ToArray();
		}

		public override void Deserialize(byte[] data)
		{
			using MemoryStream input = new MemoryStream(data);
			using BinaryReader binaryReader = new BinaryReader(input);
			TargetSteamID = binaryReader.ReadUInt64();
		}
	}
	public class HAC_ModListResponse : BinaryPacketBase
	{
		public override string PacketSignature => "HAC_Response_V6";

		public List<string> ClientModGUIDs { get; set; }

		public List<string> ClientModNames { get; set; }

		public List<string> ClientModVersions { get; set; }

		public override byte[] Serialize()
		{
			using MemoryStream memoryStream = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			int num = ClientModGUIDs?.Count ?? 0;
			binaryWriter.Write(num);
			for (int i = 0; i < num; i++)
			{
				binaryWriter.Write(ClientModGUIDs[i] ?? string.Empty);
				binaryWriter.Write(ClientModNames[i] ?? string.Empty);
				binaryWriter.Write(ClientModVersions[i] ?? string.Empty);
			}
			return memoryStream.ToArray();
		}

		public override void Deserialize(byte[] data)
		{
			using MemoryStream input = new MemoryStream(data);
			using BinaryReader binaryReader = new BinaryReader(input);
			int num = binaryReader.ReadInt32();
			ClientModGUIDs = new List<string>(num);
			ClientModNames = new List<string>(num);
			ClientModVersions = new List<string>(num);
			for (int i = 0; i < num; i++)
			{
				ClientModGUIDs.Add(binaryReader.ReadString());
				ClientModNames.Add(binaryReader.ReadString());
				ClientModVersions.Add(binaryReader.ReadString());
			}
		}
	}
	public class HAC_KickMessage : BinaryPacketBase
	{
		public override string PacketSignature => "HAC_Kick_V6";

		public string Reason { get; set; }

		public override byte[] Serialize()
		{
			using MemoryStream memoryStream = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			binaryWriter.Write(Reason ?? string.Empty);
			return memoryStream.ToArray();
		}

		public override void Deserialize(byte[] data)
		{
			using MemoryStream input = new MemoryStream(data);
			using BinaryReader binaryReader = new BinaryReader(input);
			Reason = binaryReader.ReadString();
		}
	}
	[HarmonyPatch(typeof(PlayerStats), "UserCode_Cmd_ApplyAttributePoints__Int32[]__Int32[]")]
	public static class AttributePointValidationPatch
	{
		public static bool Prefix(PlayerStats __instance, int[] _attributeValues, int[] _pointsSpent)
		{
			if (!NetworkServer.active || !Main.EnableAntiCheat.Value || !StatTracking.EnableAttributeChecks.Value)
			{
				return true;
			}
			Player component = ((Component)__instance).GetComponent<Player>();
			if ((Object)(object)component == (Object)null || Main.IsPlayerExempt(component))
			{
				return true;
			}
			if (__instance._currentAttributePoints <= 0)
			{
				return true;
			}
			string text = FindProblem(__instance, _attributeValues, _pointsSpent);
			if (text == null)
			{
				return true;
			}
			Main.LogInfraction((NetworkBehaviour)(object)__instance, "Stat Manipulation (Attribute Points)", text + " Request blocked.");
			return false;
		}

		private static string FindProblem(PlayerStats stats, int[] values, int[] spent)
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			if (values == null || spent == null)
			{
				return "Attribute request had no data.";
			}
			if (values.Length != spent.Length)
			{
				return $"Attribute request had mismatched lists ({values.Length} values, {spent.Length} costs).";
			}
			SyncList<AttributeStruct> syncAttributes = stats._syncAttributes;
			if (values.Length > syncAttributes.Count)
			{
				return $"Attribute request listed {values.Length} stats but only {syncAttributes.Count} exist.";
			}
			long num = 0L;
			for (int i = 0; i < values.Length; i++)
			{
				int attributeValue = syncAttributes[i]._attributeValue;
				int num2 = spent[i];
				int num3 = values[i] - attributeValue;
				if (num2 < 0)
				{
					return $"Negative point cost ({num2}) for attribute {i}.";
				}
				if (num3 < 0)
				{
					return $"Tried to lower attribute {i} from {attributeValue} to {values[i]}.";
				}
				if (num3 > num2)
				{
					return $"Raised attribute {i} by {num3} but only paid {num2} point(s).";
				}
				num += num2;
			}
			if (num > stats._currentAttributePoints)
			{
				return $"Spent {num} point(s) but only had {stats._currentAttributePoints}.";
			}
			return null;
		}
	}
	public static class AttributeBudget
	{
		private sealed class State
		{
			public float FirstSeen;

			public float LastSeen;

			public float NextCheck;

			public float NextReport;
		}

		private static readonly Dictionary<uint, State> States = new Dictionary<uint, State>();

		private const float CheckIntervalSeconds = 2f;

		private const float ReportCooldownSeconds = 30f;

		private const int PruneThreshold = 64;

		private const float PruneAgeSeconds = 300f;

		public static int GetBudget(PlayerStats stats)
		{
			StatLogics statLogics = GameManager._current._statLogics;
			int num = Mathf.Max(1, stats._currentLevel);
			if (statLogics._maxMainLevel > 0)
			{
				num = Mathf.Min(num, statLogics._maxMainLevel);
			}
			return (num == 1) ? 6 : ((num - 1) * statLogics._attributePointsEachLevel + 6);
		}

		public static long GetUsed(PlayerStats stats, out int unspent)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			unspent = Math.Max(0, stats._currentAttributePoints);
			long num = unspent;
			SyncList<AttributeStruct> syncAttributes = stats._syncAttributes;
			for (int i = 0; i < syncAttributes.Count; i++)
			{
				num += Math.Max(0, syncAttributes[i]._attributeValue - 1);
			}
			return num;
		}

		public static void Evaluate(PlayerStats stats, bool force)
		{
			if (!NetworkServer.active || !Main.EnableAntiCheat.Value || !StatTracking.EnableAttributeBudget.Value)
			{
				return;
			}
			try
			{
				uint netId = ((NetworkBehaviour)stats).netId;
				float time = Time.time;
				if (!States.TryGetValue(netId, out State value))
				{
					Prune(time);
					Dictionary<uint, State> states = States;
					State obj = new State
					{
						FirstSeen = time
					};
					value = obj;
					states[netId] = obj;
				}
				value.LastSeen = time;
				if (time - value.FirstSeen < StatTracking.JoinWindowSeconds.Value || (Main.ServerPlayerGracePeriod.TryGetValue(netId, out var value2) && time < value2) || (!force && time < value.NextCheck))
				{
					return;
				}
				value.NextCheck = time + 2f;
				Player player = stats._player;
				if ((Object)(object)player == (Object)null || Main.IsPlayerExempt(player))
				{
					return;
				}
				int budget = GetBudget(stats);
				int unspent;
				long used = GetUsed(stats, out unspent);
				if (used > budget + StatTracking.AttributeBudgetTolerance.Value && !(time < value.NextReport))
				{
					value.NextReport = time + 30f;
					string text = $"Attribute points total {used} but level {stats._currentLevel} allows {budget} (unspent {unspent}).";
					string value3 = StatTracking.AttributeBudgetAction.Value;
					if (value3 == "Log Only")
					{
						Main.Log.LogWarning((object)("[HAC] [AttrBudget] " + player._nickname + " (" + player._steamID + "): " + text + " Log-only, no action taken."));
					}
					else if (value3 == "Reset")
					{
						Main.LogInfraction((NetworkBehaviour)(object)stats, "Stat Manipulation (Attribute Budget)", text + " Attributes reset.");
						stats.Server_ResetAttributePoints();
						value.NextReport = 0f;
					}
					else
					{
						Main.LogInfraction((NetworkBehaviour)(object)stats, "Stat Manipulation (Attribute Budget)", text);
					}
				}
			}
			catch (Exception ex)
			{
				Main.Log.LogWarning((object)("[HAC] [AttrBudget] Check failed: " + ex.Message));
			}
		}

		private static void Prune(float now)
		{
			if (States.Count < 64)
			{
				return;
			}
			foreach (uint item in (from kv in States
				where now - kv.Value.LastSeen > 300f
				select kv.Key).ToList())
			{
				States.Remove(item);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerStats), "Update")]
	public static class AttributeBudgetSweepPatch
	{
		public static void Postfix(PlayerStats __instance)
		{
			AttributeBudget.Evaluate(__instance, force: false);
		}
	}
	[HarmonyPatch(typeof(PlayerStats), "UserCode_Cmd_ApplyAttributePoints__Int32[]__Int32[]")]
	public static class AttributeBudgetApplyPatch
	{
		public static void Postfix(PlayerStats __instance)
		{
			AttributeBudget.Evaluate(__instance, force: true);
		}
	}
	[HarmonyPatch]
	public static class CombatValidationPatch
	{
		[HarmonyPatch(typeof(PlayerCasting), "Update")]
		[HarmonyPostfix]
		public static void CooldownUpdate(PlayerCasting __instance)
		{
			if (!NetworkServer.active || !Main.EnableAntiCheat.Value || !Main.EnableCooldownChecks.Value)
			{
				return;
			}
			uint netId = ((NetworkBehaviour)__instance).netId;
			if (!Main.ServerRemainingCooldowns.TryGetValue(netId, out Dictionary<string, float> value))
			{
				return;
			}
			List<string> list = new List<string>();
			foreach (string item in new List<string>(value.Keys))
			{
				float num = value[item] - (Time.deltaTime + Time.deltaTime * __instance._cooldownMod);
				if (num <= 0f)
				{
					list.Add(item);
				}
				else
				{
					value[item] = num;
				}
			}
			foreach (string item2 in list)
			{
				value.Remove(item2);
			}
		}

		[HarmonyPatch(typeof(PlayerCasting), "Server_CastSkill")]
		[HarmonyPrefix]
		public static bool UnifiedCooldownValidation(PlayerCasting __instance)
		{
			Player component = ((Component)__instance).GetComponent<Player>();
			if (!NetworkServer.active || !Main.EnableAntiCheat.Value || !Main.EnableCooldownChecks.Value || Main.IsPlayerExempt(component))
			{
				return true;
			}
			ScriptableSkill currentCastSkill = __instance._currentCastSkill;
			if ((Object)(object)currentCastSkill == (Object)null)
			{
				return true;
			}
			uint netId = ((NetworkBehaviour)__instance).netId;
			if (Main.ServerRemainingCooldowns.TryGetValue(netId, out Dictionary<string, float> value) && value.ContainsKey(((Object)currentCastSkill).name))
			{
				__instance.Server_InterruptCast();
				Main.LogInfraction((NetworkBehaviour)(object)__instance, "Skill Manipulation", "Attempted to cast " + ((Object)currentCastSkill).name + " while on cooldown.");
				return false;
			}
			if (!Main.ServerRemainingCooldowns.ContainsKey(netId))
			{
				Main.ServerRemainingCooldowns[netId] = new Dictionary<string, float>();
			}
			Main.ServerRemainingCooldowns[netId][((Object)currentCastSkill).name] = currentCastSkill._skillRankParams._baseCooldown;
			return true;
		}
	}
	[HarmonyPatch]
	public static class ExperienceValidationPatch
	{
		[HarmonyPatch(typeof(PlayerStats), "set_Network_currentExp")]
		[HarmonyPrefix]
		public static bool ValidateExperienceChange(PlayerStats __instance, ref int value)
		{
			Player component = ((Component)__instance).GetComponent<Player>();
			if (!NetworkServer.active || !Main.EnableAntiCheat.Value || !Main.EnableExperienceChecks.Value || Main.IsPlayerExempt(component))
			{
				return true;
			}
			uint netId = ((NetworkBehaviour)__instance).netId;
			if (StatTracking.InJoinWindow(netId))
			{
				Main.ServerPlayerStats[netId] = new PlayerStatsData
				{
					Experience = value,
					Level = __instance.Network_currentLevel
				};
				return true;
			}
			if (!Main.ServerPlayerStats.TryGetValue(netId, out var value2))
			{
				Main.ServerPlayerStats[netId] = new PlayerStatsData
				{
					Experience = value,
					Level = __instance.Network_currentLevel
				};
				return true;
			}
			int experience = value2.Experience;
			int num = value - experience;
			if (num <= 0)
			{
				PlayerStatsData value3 = Main.ServerPlayerStats[netId];
				value3.Experience = value;
				Main.ServerPlayerStats[netId] = value3;
				return true;
			}
			if (num > Main.MaxPlausibleXPGain.Value)
			{
				Main.LogInfraction((NetworkBehaviour)(object)__instance, "Stat Manipulation (Experience)", $"Capped massive XP gain: {num} -> {Main.MaxPlausibleXPGain.Value}");
				value = experience + Main.MaxPlausibleXPGain.Value;
				num = Main.MaxPlausibleXPGain.Value;
			}
			if (IsXpGainRateExceeded(netId, num))
			{
				Main.LogInfraction((NetworkBehaviour)(object)__instance, "Stat Manipulation (XP Rate)", "XP Gain Rate Exceeded. Capping tick.");
				value = experience;
				return true;
			}
			PlayerStatsData value4 = Main.ServerPlayerStats[netId];
			value4.Experience = value;
			Main.ServerPlayerStats[netId] = value4;
			return true;
		}

		private static bool IsXpGainRateExceeded(uint netId, int gain)
		{
			if (!Main.XpGainHistory.ContainsKey(netId))
			{
				Main.XpGainHistory[netId] = new List<(float, int)>();
				Main.ServerRunningXpTotal[netId] = 0;
			}
			List<(float, int)> list = Main.XpGainHistory[netId];
			float currentTime = Time.time;
			float window = Main.XPGainWindowSeconds.Value;
			list.RemoveAll(delegate((float Timestamp, int Amount) entry)
			{
				if (currentTime - entry.Timestamp > window)
				{
					Main.ServerRunningXpTotal[netId] -= entry.Amount;
					return true;
				}
				return false;
			});
			if (Main.ServerRunningXpTotal[netId] + gain > Main.MaxXPGainPerWindow.Value)
			{
				return true;
			}
			list.Add((currentTime, gain));
			Main.ServerRunningXpTotal[netId] += gain;
			return false;
		}

		[HarmonyPatch(typeof(PlayerStats), "set_Network_currentLevel")]
		[HarmonyPrefix]
		public static bool ValidateLevelChange(PlayerStats __instance, ref int value)
		{
			Player component = ((Component)__instance).GetComponent<Player>();
			if (!NetworkServer.active || !Main.EnableAntiCheat.Value || !Main.EnableExperienceChecks.Value || Main.IsPlayerExempt(component))
			{
				return true;
			}
			uint netId = ((NetworkBehaviour)__instance).netId;
			if (StatTracking.InJoinWindow(netId))
			{
				Main.ServerPlayerStats[netId] = new PlayerStatsData
				{
					Experience = __instance.Network_currentExp,
					Level = value
				};
				return true;
			}
			if (!Main.ServerPlayerStats.TryGetValue(netId, out var value2))
			{
				Main.ServerPlayerStats[netId] = new PlayerStatsData
				{
					Experience = __instance.Network_currentExp,
					Level = value
				};
				return true;
			}
			int level = value2.Level;
			int num = value - level;
			if (num > 1)
			{
				Main.LogInfraction((NetworkBehaviour)(object)__instance, "Stat Manipulation (Level)", $"Attempted to jump from level {level} to {value}. Blocked.");
				value = level;
			}
			PlayerStatsData value3 = Main.ServerPlayerStats[netId];
			value3.Level = value;
			Main.ServerPlayerStats[netId] = value3;
			return true;
		}
	}
	public class HoneypotComponent : NetworkBehaviour
	{
		[Command]
		public void Cmd_ExecuteDebugBypass()
		{
			if (!ulong.TryParse(((Component)this).GetComponent<Player>()._steamID, out var result))
			{
				return;
			}
			Main.Log.LogWarning((object)$"HONEYPOT TRIGGERED by SteamID {result}. Banning immediately.");
			if ((Object)(object)HostConsole._current != (Object)null)
			{
				HC_PeerListEntry val = null;
				foreach (HC_PeerListEntry peerListEntry in HostConsole._current._peerListEntries)
				{
					if ((Object)(object)peerListEntry._netId != (Object)null && peerListEntry._netId.netId == ((NetworkBehaviour)this).netId)
					{
						val = peerListEntry;
						break;
					}
				}
				if ((Object)(object)val != (Object)null)
				{
					CodeTalkerNetwork.SendNetworkPacket(((Component)this).GetComponent<Player>(), (BinaryPacketBase)(object)new HAC_KickMessage
					{
						Reason = "Honeypot triggered. Banned."
					}, (CompressionType)0, CompressionLevel.Fastest);
					HostConsole._current._selectedPeerEntry = val;
					HostConsole._current.Ban_Peer();
				}
			}
			else
			{
				((MonoBehaviour)Main.Instance).StartCoroutine(Main.Instance.DelayedKick(((Component)this).GetComponent<Player>(), "Honeypot triggered. Kicked."));
			}
		}
	}
	[HarmonyPatch(typeof(PlayerMove), "Update")]
	public static class MovementAndAirborneValidationPatch
	{
		private const float MAX_ALLOWED_AIR_TIME = 10f;

		private const float MAX_FLIGHT_HEIGHT = 4240f;

		public static void Postfix(PlayerMove __instance)
		{
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: 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_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0412: Unknown result type (might be due to invalid IL or missing references)
			//IL_0413: Unknown result type (might be due to invalid IL or missing references)
			//IL_0422: Unknown result type (might be due to invalid IL or missing references)
			//IL_0469: Unknown result type (might be due to invalid IL or missing references)
			//IL_046a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0484: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0557: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)AtlyssNetworkManager._current == (Object)null || !NetworkServer.active || !Main.EnableAntiCheat.Value || AtlyssNetworkManager._current._soloMode || (Object)(object)__instance == (Object)null)
			{
				return;
			}
			Player component = ((Component)__instance).GetComponent<Player>();
			if ((Object)(object)component == (Object)null || Main.IsPlayerExempt(component))
			{
				return;
			}
			uint netId = ((NetworkBehaviour)__instance).netId;
			if (Main.ServerPlayerGracePeriod.TryGetValue(netId, out var value))
			{
				if (Time.time < value)
				{
					return;
				}
				Main.ServerPlayerGracePeriod.Remove(netId);
			}
			if (Main.EnableSpeedChecks.Value && (!Main.ServerSpeedCheckCooldowns.ContainsKey(netId) || Time.time > Main.ServerSpeedCheckCooldowns[netId]) && Main.ServerPlayerInitialSpeeds.TryGetValue(netId, out var value2) && __instance._movSpeed > value2 * 1.5f)
			{
				Main.LogInfraction((NetworkBehaviour)(object)__instance, "Stat Manipulation (Move Speed)", $"Detected illegal move speed of {__instance._movSpeed}. Reverting.");
				__instance._movSpeed = value2;
				Main.ServerSpeedCheckCooldowns[netId] = Time.time + Main.SpeedHackDetectionCooldown.Value;
			}
			if (Main.EnableExperienceChecks.Value)
			{
				StatusEntity component2 = ((Component)__instance).GetComponent<StatusEntity>();
				PlayerStats component3 = ((Component)__instance).GetComponent<PlayerStats>();
				if ((Object)(object)component2 != (Object)null && (Object)(object)component3 != (Object)null && component2.Network_currentStamina >= component3._statStruct._maxStamina - 1 && __instance.RayGroundCheck() && Main.ServerPlayerInitialSpeeds.TryGetValue(netId, out var value3))
				{
					Rigidbody component4 = ((Component)__instance).GetComponent<Rigidbody>();
					if ((Object)(object)component4 != (Object)null)
					{
						Vector3 velocity = component4.velocity;
						Vector3 val = new Vector3(velocity.x, 0f, velocity.z);
						float magnitude = ((Vector3)(ref val)).magnitude;
						if (magnitude > value3 * 1.2f)
						{
							if (!Main.StaminaCheckTimer.ContainsKey(netId))
							{
								Main.StaminaCheckTimer[netId] = Time.time;
							}
							if (Time.time - Main.StaminaCheckTimer[netId] > 3f)
							{
								Main.LogInfraction((NetworkBehaviour)(object)__instance, "Stat Manipulation (Infinite Stamina)", "Sustained high speed with full stamina.");
								Main.StaminaCheckTimer[netId] = Time.time + 5f;
							}
							else
							{
								Main.StaminaCheckTimer[netId] = Time.time;
							}
						}
					}
				}
				else
				{
					Main.StaminaCheckTimer[netId] = Time.time;
				}
			}
			Vector3 position = ((Component)__instance).transform.position;
			if (Main.EnableMovementChecks.Value && Main.ServerPlayerPositions.TryGetValue(netId, out var value4))
			{
				float num = Time.time - value4.Timestamp;
				float num2 = Vector3.Distance(value4.Position, position);
				if (num > Main.MovementTimeThreshold.Value)
				{
					float value5;
					float num3 = (Main.ServerPlayerInitialSpeeds.TryGetValue(netId, out value5) ? value5 : 50f) * 1.5f * num + Main.MovementGraceBuffer.Value;
					if (num2 > num3)
					{
						string cheatType = ((num2 > Main.TeleportDistanceThreshold.Value) ? "Movement Hack (Teleport)" : "Movement Hack (Speed Mismatch)");
						Main.LogInfraction((NetworkBehaviour)(object)__instance, cheatType, $"Moved {num2:F1} units in {num:F2}s (Expected max: {num3:F1}). Reverting position.");
						((Component)component).transform.position = value4.Position;
					}
				}
				Main.ServerPlayerPositions[netId] = new PlayerPositionData
				{
					Position = position,
					Timestamp = Time.time
				};
			}
			if (!Main.EnableAirborneChecks.Value)
			{
				return;
			}
			PlayerAirborneData value6 = (Main.ServerPlayerAirborneStates.ContainsKey(netId) ? Main.ServerPlayerAirborneStates[netId] : new PlayerAirborneData
			{
				AirTime = 0f,
				LastGroundedPosition = position,
				ServerSideJumpCount = 0,
				LastVerticalPosition = position.y,
				VerticalStallTime = 0f
			});
			if (__instance.RayGroundCheck())
			{
				value6.AirTime = 0f;
				value6.LastGroundedPosition = position;
			}
			else
			{
				value6.AirTime += Time.deltaTime;
			}
			if (position.y > 4240f)
			{
				Main.LogInfraction((NetworkBehaviour)(object)__instance, "Movement Hack (Fly)", "Exceeded maximum height limit. Reverting.");
				((Component)__instance).transform.position = value6.LastGroundedPosition;
				return;
			}
			if (value6.AirTime > 10f && (!Main.ServerAirborneCheckCooldowns.ContainsKey(netId) || Time.time > Main.ServerAirborneCheckCooldowns[netId]))
			{
				Main.LogInfraction((NetworkBehaviour)(object)__instance, "Movement Hack (Fly)", $"Airborne for {value6.AirTime:F1} seconds. Reverting.");
				Main.ServerAirborneCheckCooldowns[netId] = Time.time + Main.AirborneHackDetectionCooldown.Value;
			}
			if (value6.AirTime > 10f)
			{
				((Component)__instance).transform.position = value6.LastGroundedPosition;
				value6.AirTime = 0f;
			}
			Main.ServerPlayerAirborneStates[netId] = value6;
		}
	}
	[HarmonyPatch(typeof(NetworkTransformBase), "CmdTeleport", new Type[]
	{
		typeof(Vector3),
		typeof(Quaternion)
	})]
	public static class CmdTeleportValidationPatch2
	{
		public static bool Prefix(NetworkBehaviour __instance)
		{
			if (!NetworkServer.active || !Main.EnableAntiCheat.Value || !Main.EnableMovementChecks.Value || Main.IsPlayerExempt(((Component)__instance).GetComponent<Player>()))
			{
				return true;
			}
			Main.LogInfraction(__instance, "Movement Hack (Illegal Teleport Command)", "Player directly called a Teleport command.");
			return false;
		}
	}
	[HarmonyPatch(typeof(NetworkTransformBase), "CmdTeleport", new Type[] { typeof(Vector3) })]
	public static class CmdTeleportValidationPatch
	{
		public static bool Prefix(NetworkBehaviour __instance)
		{
			if (!NetworkServer.active || !Main.EnableAntiCheat.Value || !Main.EnableMovementChecks.Value || Main.IsPlayerExempt(((Component)__instance).GetComponent<Player>()))
			{
				return true;
			}
			Main.LogInfraction(__instance, "Movement Hack (Illegal Teleport Command)", "Player directly called a Teleport command.");
			return false;
		}
	}
	[HarmonyPatch(typeof(PlayerMove), "Init_Jump")]
	public static class JumpValidationPatch
	{
		public static bool Prefix(PlayerMove __instance)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			Player component = ((Component)__instance).GetComponent<Player>();
			if (!NetworkServer.active || !Main.EnableAntiCheat.Value || !Main.EnableAirborneChecks.Value || Main.IsPlayerExempt(component))
			{
				return true;
			}
			uint netId = ((NetworkBehaviour)__instance).netId;
			if (!Main.ServerPlayerAirborneStates.ContainsKey(netId))
			{
				Main.ServerPlayerAirborneStates[netId] = new PlayerAirborneData
				{
					AirTime = 0f,
					LastGroundedPosition = ((Component)__instance).transform.position,
					ServerSideJumpCount = 0,
					LastVerticalPosition = ((Component)__instance).transform.position.y,
					VerticalStallTime = 0f
				};
			}
			PlayerAirborneData value = Main.ServerPlayerAirborneStates[netId];
			if (value.ServerSideJumpCount >= Main.JumpThreshold.Value)
			{
				return false;
			}
			value.ServerSideJumpCount++;
			Main.ServerPlayerAirborneStates[netId] = value;
			return true;
		}
	}
	[HarmonyPatch]
	public static class NetworkLifecyclePatches
	{
		private static bool serverListenersRegistered;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(AtlyssNetworkManager), "OnStartServer")]
		public static void OnStartServer_Postfix()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			Main.Log.LogInfo((object)"Server started. Behavioral checks active.");
			if ((Main.EnableModBlacklist.Value || Main.EnableModWhitelist.Value || Main.EnableRequiredMods.Value || Main.EnableThunderstoreWhitelist.Value) && !serverListenersRegistered)
			{
				serverListenersRegistered = true;
				CSteamID steamID = SteamUser.GetSteamID();
				if (((CSteamID)(ref steamID)).IsValid())
				{
					Main.VerifiedSteamIDs.Add(steamID.m_SteamID);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(AtlyssNetworkManager), "OnStopClient")]
		public static void OnStopClient_Postfix()
		{
			serverListenersRegistered = false;
			Main.VerifiedSteamIDs.Clear();
			Main.PendingVerification.Clear();
			Main.Log.LogInfo((object)"Client stopped. HAC data cleared.");
		}
	}
	[HarmonyPatch(typeof(AtlyssNetworkManager), "OnServerDisconnect")]
	public static class PlayerDisconnectPatch
	{
		public static void Postfix(NetworkConnectionToClient _conn)
		{
			if (_conn != null && (Object)(object)((NetworkConnection)_conn).identity != (Object)null && ulong.TryParse(((Component)((NetworkConnection)_conn).identity).GetComponent<Player>()?._steamID, out var result))
			{
				Main.ClearAllPlayerData(((NetworkConnection)_conn).identity.netId, result);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerMove), "Start")]
	public static class PlayerSpawnPatch
	{
		private const float GRACE_PERIOD_SECONDS = 3f;

		public static void Postfix(PlayerMove __instance)
		{
			//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)
			uint netId = ((NetworkBehaviour)__instance).netId;
			Player component = ((Component)__instance).GetComponent<Player>();
			ulong result2;
			if (NetworkServer.active)
			{
				if ((Object)(object)((Component)__instance).gameObject.GetComponent<HoneypotComponent>() == (Object)null)
				{
					((Component)__instance).gameObject.AddComponent<HoneypotComponent>();
				}
				Main.ServerPlayerGracePeriod[netId] = Time.time + 3f;
				if (!Main.ServerPlayerInitialSpeeds.ContainsKey(netId))
				{
					Main.ServerPlayerInitialSpeeds[netId] = __instance.Network_movSpeed;
				}
				Main.ServerPlayerPositions[netId] = new PlayerPositionData
				{
					Position = ((Component)__instance).transform.position,
					Timestamp = Time.time
				};
				Main.ServerPlayerMovementStats[netId] = new PlayerMovementStats
				{
					RecentSpeeds = new List<float>(),
					TimeAtMaxSpeed = 0f
				};
				Main.ServerPlayerMovementTimers[netId] = Time.time;