Decompiled source of HardAntiCheat v2.6.0
HardAntiCheat.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
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;