using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; 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.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using AntiCheat; using AntiCheat.Locale; using AntiCheat.Patches; using AntiCheat.UI; using AntiCheat.Utils; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Netcode.Transports.Facepunch; using Newtonsoft.Json; using Overlay; using Steamworks; using Steamworks.Data; using TMPro; using Unity.Netcode; using UnityEngine; using UnityEngine.EventSystems; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("DunGen")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")] [assembly: IgnoresAccessChecksTo("EasyTextEffects")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")] [assembly: IgnoresAccessChecksTo("Unity.XR.Management")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("chuxiaaaa")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("AntiCheat")] [assembly: AssemblyFileVersion("0.8.9.0")] [assembly: AssemblyInformationalVersion("0.8.9+c4c32a494a4e01b6094e5b49735ec59042ea2ce9")] [assembly: AssemblyProduct("AntiCheat")] [assembly: AssemblyTitle("AntiCheat")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.8.9.0")] [module: UnverifiableCode] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("AntiCheat", "AntiCheat", "0.8.9")] public class AntiCheatPlugin : BaseUnityPlugin { internal static ManualLogSource Log = null; internal static LocalizationManager localizationManager = null; private static readonly MethodInfo AddChatMessageMethod = AccessTools.DeclaredMethod(typeof(HUDManager), "AddChatMessage", (Type[])null, (Type[])null); private static readonly MethodInfo AddTextMessageClientRpcMethod = AccessTools.DeclaredMethod(typeof(HUDManager), "AddTextMessageClientRpc", (Type[])null, (Type[])null); private const string AlertModule = "ANTI CHEAT"; private const double RepeatQuietSeconds = 5.0; private static readonly Dictionary _raisedAt = new Dictionary(); private void Awake() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; localizationManager = new LocalizationManager(); PluginConfig.Init((BaseUnityPlugin)(object)this); Network.PlayerNamesReceived += LateJoinSync.ApplyPlayerNames; Network.PlayerSlotRepairReceived += LateJoinSync.RepairPlayerSlot; AntiCheatMenu.Register(); Harmony val = new Harmony("AntiCheat"); IEnumerable enumerable = from t in Assembly.GetExecutingAssembly().GetTypes() where t.IsClass && t.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Any() select t; foreach (Type item in enumerable) { try { val.CreateClassProcessor(item).Patch(); } catch (Exception arg) { Log.LogWarning((object)$"Failed to patch {item.Name} - that feature is off for this session: {arg}"); } } LobbyImprovementsBans.TryIntegrate(val); BanList.EnsureBanCheckSubscribed(); MethodInfo methodInfo = AccessTools.Method(typeof(StartOfRound), "Update", (Type[])null, (Type[])null); bool flag = methodInfo != null && val.GetPatchedMethods().Contains(methodInfo); Log.LogInfo((object)$"StartOfRound.Update resolved:{methodInfo != null} movementHookBound:{flag} totalPatched:{val.GetPatchedMethods().Count()}"); } public static void LogInfo(string info) { MenuLog.Info(info); LogWithoutMenu(info); } private static void LogWithoutMenu(string info) { if ((!((Object)(object)StartOfRound.Instance != (Object)null) || ((NetworkBehaviour)StartOfRound.Instance).IsHost) && (PluginConfig.Log == null || PluginConfig.Log.Value)) { Log.LogInfo((object)info); LogFile.Write($"[{DateTime.Now:MM-dd HH:mm:ss:ff}] {info}"); } } public static void LogInfo(PlayerControllerB player, string rpc, params object[] param) { LogInfo(string.Format("{0}({1}) -> {2};{3}", player.playerUsername, player.playerClientId, rpc, string.Join("|", param))); } public static void Detected(string msg, string dedupeKey = null, string player = null, string cheat = null) { Raise(msg, dedupeKey, (AlertTone)2, player, cheat); } public static void Punished(string msg, string dedupeKey = null, string player = null, string cheat = null) { Raise(msg, dedupeKey, (AlertTone)1, player, cheat); } public static void Announce(string msg, string color = null) { if (!string.IsNullOrEmpty(msg)) { MenuLog.Info(msg); LogWithoutMenu(msg); if (!((Object)(object)HUDManager.Instance == (Object)null)) { HUDManager.Instance.AddTextToChatOnServer(Colorize(msg, color), -1); } } } public static void ForgetAlertsFor(string player) { if (!string.IsNullOrEmpty(player)) { Alerts.ClearForSubject(player); } } private static bool SendAlertToPlayer(string msg, AlertTone tone, string player, string cheat) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(player) || !Network.IsHost || (Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null) { return false; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && val.actualClientId != 0L && string.Equals(val.playerUsername, player, StringComparison.Ordinal)) { return Network.Send(val.actualClientId, msg, tone, player, cheat); } } return false; } private static void Raise(string msg, string dedupeKey, AlertTone tone, string player, string cheat) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(msg)) { return; } MenuLog.Alert(msg); LogWithoutMenu(msg); string key = dedupeKey ?? msg; DateTime utcNow = DateTime.UtcNow; if (!_raisedAt.TryGetValue(key, out var value) || !((utcNow - value).TotalSeconds < 5.0)) { if (_raisedAt.Count > 256) { _raisedAt.Clear(); } _raisedAt[key] = utcNow; if (!SendAlertToPlayer(msg, tone, player, cheat)) { Alerts.Raise("ANTI CHEAT", msg, tone, player ?? string.Empty, cheat ?? string.Empty); } } } public static void ShowGuiTip(string body, string header = null) { if (!((Object)(object)HUDManager.Instance == (Object)null)) { LogWithoutMenu("ShowGUI|" + body); HUDManager.Instance.DisplayTip(string.IsNullOrEmpty(header) ? localizationManager.Prefix() : header, body, false, false, "LC_Tip1"); } } public static string Colorize(string text, string color) { if (string.IsNullOrEmpty(color) || color.Trim().Length == 0) { return text; } return "" + text + ""; } public static void ShowMessageHostOnly(string msg) { if (!((Object)(object)HUDManager.Instance == (Object)null)) { LogWithoutMenu("ShowMessageHostOnly -> " + msg); AddChatMessageMethod.Invoke(HUDManager.Instance, new object[4] { msg, "", -1, false }); } } public static void AddTextMessageClientRpc(string showMessage) { if (!((Object)(object)HUDManager.Instance == (Object)null)) { AddTextMessageClientRpcMethod.Invoke(HUDManager.Instance, new object[1] { showMessage }); } } } internal static class LCMPluginInfo { public const string PLUGIN_GUID = "AntiCheat"; public const string PLUGIN_NAME = "AntiCheat"; public const string PLUGIN_VERSION = "0.8.9"; } namespace AntiCheat { [HarmonyPatch] [HarmonyWrapSafe] public static class AntiGrief { private const string HostOnlyWarning = "{0}, This Action is Restricted"; private const string GameStartWarning = "{0}, This Action is Restricted till the Ship Lands"; private static readonly Dictionary _violationCounts = new Dictionary(); private static int _landings; private static bool _countedThisLanding; private static readonly HashSet _landedParticipants = new HashSet(); private static int _lightSwitchUnlockableId = -2; private static int Landings() { if (!_countedThisLanding && (Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.shipHasLanded) { CountLanding("Ship is on the ground"); } return _landings; } private static void CountLanding(string reason) { if (_countedThisLanding) { return; } _countedThisLanding = true; _landings++; if ((Object)(object)NetworkManager.Singleton != (Object)null) { foreach (ulong connectedClientsId in NetworkManager.Singleton.ConnectedClientsIds) { _landedParticipants.Add(connectedClientsId); } } AntiCheatPlugin.LogInfo($"[Anti-Grief] {reason} - landing #{_landings}. Connected players are now unrestricted for GameStart actions."); } private static bool HasServedARound(ulong clientId, out string detail) { Landings(); bool flag = _landedParticipants.Contains(clientId); detail = (flag ? "participated in ShipHasLanded" : "has not participated in ShipHasLanded yet"); return flag; } [HarmonyPatch(typeof(StartOfRound), "OnShipLandedMiscEvents")] [HarmonyPostfix] public static void CountLandingOnShipLanded() { CountLanding("Ship has landed"); } [HarmonyPatch(typeof(StartOfRound), "Awake")] [HarmonyPostfix] public static void ResetLatchOnNewLobby() { if (_landings != 0 || _landedParticipants.Count != 0) { AntiCheatPlugin.LogInfo("[Anti-Grief] New lobby - landings back to zero, everybody starts as a newcomer again."); } _landings = 0; _countedThisLanding = false; _landedParticipants.Clear(); _violationCounts.Clear(); ForgetLightSwitchId(); } [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] public static void ResetViolationsOnRoundStart() { _countedThisLanding = false; _violationCounts.Clear(); BanList.EnsureBanCheckSubscribed(); } private static bool IsHostSender(__RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) return rpcParams.Server.Receive.SenderClientId == GameNetworkManager.Instance.localPlayerController.actualClientId; } private static bool RestrictionsDisabledDueToGameStart(PluginConfig.PermissionLevel level, ulong senderClientId, out string detail) { if (level != PluginConfig.PermissionLevel.GameStart) { detail = null; return false; } return HasServedARound(senderClientId, out detail); } private static bool IsAllowed(string ruleKey, __RpcParams rpcParams, string action, out PluginConfig.PermissionLevel level) { //IL_0057: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) GriefRule griefRule = AntiGriefRules.ByKey(ruleKey); if (griefRule == null) { AntiCheatPlugin.LogInfo("[Anti-Grief] No rule registered under \"" + ruleKey + "\" - " + action + " allowed."); level = PluginConfig.PermissionLevel.Unrestricted; return true; } level = griefRule.Level; ulong senderClientId = rpcParams.Server.Receive.SenderClientId; bool flag = IsHostSender(rpcParams); string detail = null; bool flag2 = !flag && RestrictionsDisabledDueToGameStart(level, senderClientId, out detail); bool flag3 = level == PluginConfig.PermissionLevel.Unrestricted || flag || flag2; bool flag4 = false; if (!flag3 && Profiles.AllowsGriefRule(senderClientId, griefRule.Key)) { flag3 = true; flag4 = true; } string text = (flag4 ? " (bypass profile)" : ((detail == null) ? string.Empty : (" (" + detail + ")"))); AntiCheatPlugin.LogInfo("[Anti-Grief] " + BanList.NameForClient(senderClientId) + " tried to " + action + " - " + (flag3 ? "allowed" : "forbidden") + text); return flag3; } private static bool IsNetworkRegisteredClient(ulong clientId) { if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer) { return false; } foreach (ulong connectedClientsId in NetworkManager.Singleton.ConnectedClientsIds) { if (connectedClientsId == clientId) { return true; } } return false; } private static void WarnHostAboutViolation(ulong senderClientId, PluginConfig.PermissionLevel level) { string format = ((level == PluginConfig.PermissionLevel.GameStart) ? "{0}, This Action is Restricted till the Ship Lands" : "{0}, This Action is Restricted"); AlertPlayer(senderClientId, string.Format(format, BanList.NameForClient(senderClientId))); } internal static void AlertPlayer(ulong senderClientId, string message) { try { string text = BanList.NameForClient(senderClientId); bool flag = Network.Send(senderClientId, message, (AlertTone)1, text, "Anti-Grief"); AntiCheatPlugin.LogInfo(string.Format("[Anti-Grief] Warning for {0} (client {1}) sent as {2}.", text, senderClientId, flag ? "Overlay alert" : "chat fallback")); if (!flag && (Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.AddTextToChatOnServer("" + message + "", -1); } } catch (Exception arg) { AntiCheatPlugin.LogInfo($"[Anti-Grief] AlertPlayer failed: {arg}"); } } internal static void HandleViolation(ulong senderClientId, PluginConfig.PermissionLevel level) { WarnHostAboutViolation(senderClientId, level); _violationCounts.TryGetValue(senderClientId, out var value); value++; _violationCounts[senderClientId] = value; int value2 = PluginConfig.AntiGrief_MaxViolations.Value; if (value2 > 0 && value >= value2) { PunishPlayer(senderClientId, PluginConfig.AntiGrief_Punishment.Value); _violationCounts[senderClientId] = 0; } } internal static void PunishPlayer(ulong clientId, PunishmentType punishment, string reasonOverride = null) { try { if (punishment != PunishmentType.Disable && punishment != PunishmentType.OnlyLog) { string text = reasonOverride; if (string.IsNullOrEmpty(text)) { text = ((punishment == PunishmentType.Ban) ? PluginConfig.AntiGrief_BanReason.Value : PluginConfig.AntiGrief_KickReason.Value); } if (string.IsNullOrEmpty(text)) { text = ((punishment == PunishmentType.Ban) ? "You were banned by the Anti-Grief System" : "You were kicked by the Anti-Grief System"); } AntiCheatPlugin.LogInfo(string.Format("[Anti-Grief] {0} {1} (client {2}) for exceeding the violation threshold.", (punishment == PunishmentType.Ban) ? "banned" : "kicked", BanList.NameForClient(clientId), clientId)); PlayerControllerB val = PlayerForClient(clientId); if ((Object)(object)val != (Object)null) { PatchHelper.Punish(val, punishment, text, BanSource.AntiGrief); } else { NetworkManager.Singleton.DisconnectClient(clientId, text); } } } catch (Exception arg) { AntiCheatPlugin.LogInfo($"[Anti-Grief] PunishPlayer failed: {arg}"); } } private static PlayerControllerB PlayerForClient(ulong clientId) { if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null) { return null; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val != (Object)null && val.actualClientId == clientId) { return val; } } return null; } internal static void ForgetLightSwitchId() { _lightSwitchUnlockableId = -2; } private static bool IsLightSwitch(NetworkObject networkObject, PlaceableShipObject placeable) { if ((Object)(object)networkObject == (Object)null) { return false; } if ((Object)(object)((Component)networkObject).gameObject.GetComponentInChildren(true) != (Object)null) { return true; } int num = LightSwitchUnlockableId(); return num >= 0 && (Object)(object)placeable != (Object)null && placeable.unlockableID == num; } private static int LightSwitchUnlockableId() { if (_lightSwitchUnlockableId != -2) { return _lightSwitchUnlockableId; } _lightSwitchUnlockableId = -1; try { UnlockablesList val = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.unlockablesList : null); if ((Object)(object)val == (Object)null || val.unlockables == null) { _lightSwitchUnlockableId = -2; return -1; } for (int i = 0; i < val.unlockables.Count; i++) { UnlockableItem val2 = val.unlockables[i]; if (val2 != null && !string.IsNullOrEmpty(val2.unlockableName) && val2.unlockableName.Replace(" ", string.Empty).Equals("lightswitch", StringComparison.OrdinalIgnoreCase)) { _lightSwitchUnlockableId = i; AntiCheatPlugin.LogInfo($"[Anti-Grief] Light switch is unlockable #{i} (\"{val2.unlockableName}\")."); break; } } } catch (Exception arg) { AntiCheatPlugin.LogInfo($"[Anti-Grief] LightSwitchUnlockableId failed: {arg}"); } return _lightSwitchUnlockableId; } [HarmonyPatch(typeof(Terminal), "__rpc_handler_4003509079")] [HarmonyPriority(600)] [HarmonyPrefix] public static bool IgnoreClientButNotHostPurchases(NetworkBehaviour target, __RpcParams rpcParams) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //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) if (IsAllowed("PurchaseStoreItems", rpcParams, "purchase store item", out var level)) { return true; } HandleViolation(rpcParams.Server.Receive.SenderClientId, level); Terminal val = (Terminal)(object)((target is Terminal) ? target : null); if ((Object)(object)val != (Object)null) { val.SyncGroupCreditsClientRpc(val.groupCredits, val.orderedItemsFromTerminal.Count); } return false; } [HarmonyPatch(typeof(StartOfRound), "__rpc_handler_3953483456")] [HarmonyPriority(600)] [HarmonyPrefix] public static bool IgnoreClientButNotHostPurchaseUnlockables(NetworkBehaviour target, __RpcParams rpcParams) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //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) if (IsAllowed("PurchaseStoreUnlocks", rpcParams, "purchase unlockable", out var level)) { return true; } HandleViolation(rpcParams.Server.Receive.SenderClientId, level); StartOfRound val = (StartOfRound)(object)((target is StartOfRound) ? target : null); if ((Object)(object)val != (Object)null) { val.BuyShipUnlockableClientRpc(Object.FindObjectOfType().groupCredits, -1); } return false; } [HarmonyPatch(typeof(StartOfRound), "__rpc_handler_1134466287")] [HarmonyPriority(600)] [HarmonyPrefix] public static bool IgnoreClientButNotHostRouteChanges(__RpcParams rpcParams) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //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) if (IsAllowed("RouteShipToMoon", rpcParams, "route ship to moon", out var level)) { return true; } HandleViolation(rpcParams.Server.Receive.SenderClientId, level); return false; } [HarmonyPatch(typeof(StartOfRound), "__rpc_handler_3380566632")] [HarmonyPriority(600)] [HarmonyPrefix] public static bool IgnoreClientButNotHostReturnFromStorage(__RpcParams rpcParams) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //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) if (IsAllowed("ReturnObjectFromStorage", rpcParams, "return object from storage", out var level)) { return true; } HandleViolation(rpcParams.Server.Receive.SenderClientId, level); return false; } [HarmonyPatch(typeof(ShipBuildModeManager), "__rpc_handler_3086821980")] [HarmonyPriority(600)] [HarmonyPrefix] public static bool IgnoreClientButNotHostPlaceInStorage(FastBufferReader reader, __RpcParams rpcParams) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (IsAllowed("PlaceObjectIntoStorage", rpcParams, "place object into storage", out var level)) { return true; } HandleViolation(rpcParams.Server.Receive.SenderClientId, level); NetworkObjectReference val = default(NetworkObjectReference); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val, default(ForNetworkSerializable)); NetworkObject val2 = default(NetworkObject); if (!((NetworkObjectReference)(ref val)).TryGet(ref val2, (NetworkManager)null)) { return false; } PlaceableShipObject componentInChildren = ((Component)val2).gameObject.GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { Object.FindObjectOfType().ReturnUnlockableFromStorageClientRpc(componentInChildren.unlockableID); } return false; } [HarmonyPatch(typeof(ShipBuildModeManager), "__rpc_handler_861494715")] [HarmonyPriority(600)] [HarmonyPrefix] public static bool IgnoreClientButNotHostMoveCabinet(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) Vector3 val = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val); NetworkObjectReference val2 = default(NetworkObjectReference); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val2, default(ForNetworkSerializable)); ((FastBufferReader)(ref reader)).Seek(0); NetworkObject val4 = default(NetworkObject); PlaceableShipObject val3 = ((((NetworkObjectReference)(ref val2)).TryGet(ref val4, (NetworkManager)null) && (Object)(object)val4 != (Object)null) ? ((Component)val4).gameObject.GetComponentInChildren() : null); bool flag = IsLightSwitch(val4, val3); string ruleKey = (flag ? "MoveLightSwitch" : "MoveObjects"); if (IsAllowed(ruleKey, rpcParams, flag ? "move the light switch" : "move object", out var level)) { return true; } HandleViolation(rpcParams.Server.Receive.SenderClientId, level); if ((Object)(object)val3 != (Object)null) { ShipBuildModeManager val5 = (ShipBuildModeManager)(object)((target is ShipBuildModeManager) ? target : null); if ((Object)(object)val5 != (Object)null) { Transform val6 = (((Object)(object)val3.mainMesh != (Object)null) ? ((Component)val3.mainMesh).transform : ((Component)val4).transform); Transform val7 = (((Object)(object)val3.placeObjectCollider != (Object)null) ? ((Component)val3.placeObjectCollider).transform : ((Component)val4).transform); val5.PlaceShipObjectClientRpc(val7.position, val6.eulerAngles, val2, (int)GameNetworkManager.Instance.localPlayerController.playerClientId); } } return false; } [HarmonyPatch(typeof(ShipLights), "__rpc_handler_1625678258")] [HarmonyPriority(600)] [HarmonyPrefix] public static bool IgnoreClientButNotHostSetLights(__RpcParams rpcParams) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) if (IsAllowed("ToggleShipLights", rpcParams, "toggle ship lights", out var level)) { return true; } ShipLights val = Object.FindObjectOfType(); if (GameNetworkManager.Instance.localPlayerController.isPlayerDead || Vector3.Distance(((Component)GameNetworkManager.Instance.localPlayerController).transform.position, ((Component)val).transform.position) >= 20f) { return true; } HandleViolation(rpcParams.Server.Receive.SenderClientId, level); val.SetShipLightsClientRpc(val.areLightsOn); return false; } } internal sealed class GriefRule { public string Key; public string Label; public string Help; public ConfigEntry LevelEntry; public ConfigEntry BinaryEntry; public bool Binary => BinaryEntry != null; public PluginConfig.PermissionLevel Level => (!Binary) ? LevelEntry.Value : ((BinaryEntry.Value != PluginConfig.FriendlyFirePolicy.Allowed) ? PluginConfig.PermissionLevel.HostOnly : PluginConfig.PermissionLevel.Unrestricted); public void SetLevel(PluginConfig.PermissionLevel level) { if (Binary) { BinaryEntry.Value = ((level != PluginConfig.PermissionLevel.Unrestricted) ? PluginConfig.FriendlyFirePolicy.Restricted : PluginConfig.FriendlyFirePolicy.Allowed); } else { LevelEntry.Value = level; } } } internal static class AntiGriefRules { public const string FriendlyFire = "FriendlyFire"; public const string PurchaseStoreItems = "PurchaseStoreItems"; public const string PurchaseStoreUnlocks = "PurchaseStoreUnlocks"; public const string RouteShipToMoon = "RouteShipToMoon"; public const string PlaceObjectIntoStorage = "PlaceObjectIntoStorage"; public const string ReturnObjectFromStorage = "ReturnObjectFromStorage"; public const string MoveObjects = "MoveObjects"; public const string MoveLightSwitch = "MoveLightSwitch"; public const string ToggleShipLights = "ToggleShipLights"; private static readonly List Rules = new List(); private static readonly Dictionary ByKeyLookup = new Dictionary(); public static IList All => Rules; public static GriefRule ByKey(string key) { if (string.IsNullOrEmpty(key)) { return null; } GriefRule value; return ByKeyLookup.TryGetValue(key, out value) ? value : null; } internal static void Register() { Rules.Clear(); ByKeyLookup.Clear(); AddBinary("FriendlyFire", "Friendly fire", PluginConfig.Restrict_FriendlyFire, "Player weapon damage. Restricted blocks it for everyone; Allowed restores vanilla."); AddLevel("PurchaseStoreItems", "Purchase store items", PluginConfig.Restrict_PurchaseStoreItems, "Terminal orders."); AddLevel("PurchaseStoreUnlocks", "Purchase unlockables", PluginConfig.Restrict_PurchaseStoreUnlocks, "Ship upgrades and furniture."); AddLevel("RouteShipToMoon", "Route the ship", PluginConfig.Restrict_RouteShipToMoon, "Which moon the ship flies to."); AddLevel("PlaceObjectIntoStorage", "Place into storage", PluginConfig.Restrict_PlaceObjectIntoStorage, "Furniture into the closet."); AddLevel("ReturnObjectFromStorage", "Take out of storage", PluginConfig.Restrict_ReturnObjectFromStorage, "Furniture back out."); AddLevel("MoveObjects", "Move ship objects", PluginConfig.Restrict_MoveObjects, "Furniture inside the ship."); AddLevel("MoveLightSwitch", "Move the light switch", PluginConfig.Restrict_MoveLightSwitch, "The cheapest grief there is."); AddLevel("ToggleShipLights", "Toggle the ship lights", PluginConfig.Restrict_ToggleShipLights, "Lights on and off."); } private static void AddLevel(string key, string label, ConfigEntry entry, string help) { GriefRule griefRule = new GriefRule { Key = key, Label = label, LevelEntry = entry, Help = help }; Rules.Add(griefRule); ByKeyLookup[griefRule.Key] = griefRule; } private static void AddBinary(string key, string label, ConfigEntry entry, string help) { GriefRule griefRule = new GriefRule { Key = key, Label = label, BinaryEntry = entry, Help = help }; Rules.Add(griefRule); ByKeyLookup[griefRule.Key] = griefRule; } } public enum BanSource { Unknown, AntiCheat, AntiGrief, Host } public sealed class BanRecord { public ulong SteamId; public BanSource Source; public string Name; public DateTime When; public bool HasWhen; public string SourceText => Source switch { BanSource.AntiCheat => "AntiCheat", BanSource.AntiGrief => "Anti-Grief", BanSource.Host => "Host ban", _ => "-", }; } [HarmonyPatch] [HarmonyWrapSafe] public static class BanList { private static bool _banCheckSubscribed; public static HashSet Parse() { string value = PluginConfig.BannedSteamIds.Value; if (string.IsNullOrEmpty(value) || value.Trim().Length == 0) { return new HashSet(); } ulong result; return new HashSet(from s in value.Split(',') select s.Trim() into s where s.Length > 0 select ulong.TryParse(s, out result) ? result : 0 into id where id != 0 select id); } public static bool Contains(ulong steamId) { return steamId != 0L && Parse().Contains(steamId); } public static void Add(ulong steamId, BanSource source = BanSource.Unknown, string name = null) { if (steamId == 0) { return; } HashSet hashSet = Parse(); bool flag = hashSet.Add(steamId); Dictionary dictionary = ParseRecords(); if (!dictionary.TryGetValue(steamId, out var value)) { value = (dictionary[steamId] = new BanRecord { SteamId = steamId }); } if (source != BanSource.Unknown) { value.Source = source; } if (!string.IsNullOrEmpty(name)) { value.Name = Sanitize(name); } if (flag || !value.HasWhen) { value.When = DateTime.Now; value.HasWhen = true; } if (flag) { PluginConfig.BannedSteamIds.Value = string.Join(",", hashSet.Select((ulong id) => id.ToString()).ToArray()); } WriteRecords(dictionary, hashSet); Save(); } public static bool Remove(ulong steamId) { HashSet hashSet = Parse(); if (!hashSet.Remove(steamId)) { return false; } PluginConfig.BannedSteamIds.Value = string.Join(",", hashSet.Select((ulong id) => id.ToString()).ToArray()); Dictionary dictionary = ParseRecords(); dictionary.Remove(steamId); WriteRecords(dictionary, hashSet); Save(); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && instance.KickedClientIds != null) { instance.KickedClientIds.Remove(steamId); } AntiCheatPlugin.LogInfo($"[Bans] Unbanned {steamId}."); return true; } public static List Records() { Dictionary dictionary = ParseRecords(); List list = new List(); foreach (ulong item in Parse()) { if (!dictionary.TryGetValue(item, out var value)) { value = new BanRecord { SteamId = item }; } list.Add(value); } list.Sort((BanRecord a, BanRecord b) => (a.HasWhen != b.HasWhen) ? ((!a.HasWhen) ? 1 : (-1)) : (a.HasWhen ? b.When.CompareTo(a.When) : a.SteamId.CompareTo(b.SteamId))); return list; } public static void CacheNames(Dictionary resolved) { if (resolved == null || resolved.Count == 0) { return; } HashSet hashSet = Parse(); Dictionary dictionary = ParseRecords(); bool flag = false; foreach (KeyValuePair item in resolved) { if (!string.IsNullOrEmpty(item.Value) && hashSet.Contains(item.Key)) { if (!dictionary.TryGetValue(item.Key, out var value)) { value = new BanRecord { SteamId = item.Key }; dictionary[item.Key] = value; } if (!(value.Name == item.Value)) { value.Name = Sanitize(item.Value); flag = true; } } } if (flag) { WriteRecords(dictionary, hashSet); Save(); } } private static Dictionary ParseRecords() { Dictionary dictionary = new Dictionary(); if (PluginConfig.BanRecords == null) { return dictionary; } string value = PluginConfig.BanRecords.Value; if (string.IsNullOrEmpty(value)) { return dictionary; } string[] array = value.Split(';'); foreach (string text in array) { string[] array2 = text.Split('|'); if (array2.Length >= 2 && ulong.TryParse(array2[0].Trim(), out var result) && result != 0) { BanRecord banRecord = new BanRecord { SteamId = result }; if (Enum.TryParse(array2[1].Trim(), out var result2)) { banRecord.Source = result2; } if (array2.Length >= 3) { banRecord.Name = array2[2].Trim(); } if (array2.Length >= 4 && long.TryParse(array2[3].Trim(), out var result3) && result3 > 0) { banRecord.When = new DateTime(result3); banRecord.HasWhen = true; } dictionary[result] = banRecord; } } return dictionary; } private static void WriteRecords(Dictionary records, HashSet banned) { List list = new List(); foreach (BanRecord value in records.Values) { if (banned.Contains(value.SteamId)) { list.Add(string.Join("|", value.SteamId.ToString(), value.Source.ToString(), value.Name ?? string.Empty, value.HasWhen ? value.When.Ticks.ToString() : "0")); } } PluginConfig.BanRecords.Value = string.Join(";", list.ToArray()); } private static string Sanitize(string name) { return name.Replace('|', ' ').Replace(';', ' ').Trim(); } private static void Save() { try { ((ConfigEntryBase)PluginConfig.BannedSteamIds).ConfigFile.Save(); } catch (Exception arg) { AntiCheatPlugin.LogInfo($"[Bans] Failed to write the ban list to disk: {arg}"); } } [HarmonyPatch(typeof(GameNetworkManager), "ConnectionApproval")] [HarmonyPriority(800)] [HarmonyPrefix] [HarmonyWrapSafe] public static bool RejectBannedSteamIdsAtApproval(ConnectionApprovalRequest request, ConnectionApprovalResponse response) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) try { string text = Encoding.UTF8.GetString(request.Payload); string[] array = text.Split(','); if (array.Length >= 2 && ulong.TryParse(array[1].Trim(), out var result) && Contains(result)) { response.Approved = false; response.CreatePlayerObject = false; response.Pending = false; response.Reason = PluginConfig.BanReason.Value; AntiCheatPlugin.LogInfo($"[Bans] Rejected banned steamId {result} at connection approval."); return false; } } catch (Exception arg) { AntiCheatPlugin.LogInfo($"[Bans] RejectBannedSteamIdsAtApproval failed: {arg}"); } return true; } public static void EnsureBanCheckSubscribed() { if (!_banCheckSubscribed && !((Object)(object)NetworkManager.Singleton == (Object)null)) { NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnectedBanCheck; _banCheckSubscribed = true; } } [HarmonyPatch(typeof(GameNetworkManager), "Start")] [HarmonyPostfix] [HarmonyWrapSafe] public static void HookClientConnectedForBanCheck() { EnsureBanCheckSubscribed(); } private static void OnClientConnectedBanCheck(ulong clientId) { if (!((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.IsServer && clientId != NetworkManager.Singleton.LocalClientId && !((Object)(object)StartOfRound.Instance == (Object)null)) { ((MonoBehaviour)StartOfRound.Instance).StartCoroutine(CheckAndKickIfBannedCoroutine(clientId)); } } private static IEnumerator CheckAndKickIfBannedCoroutine(ulong clientId) { for (int attempt = 0; attempt < 30; attempt++) { ulong steamId = SteamIdForClient(clientId); if (steamId != 0) { if (Contains(steamId)) { AntiCheatPlugin.LogInfo($"[Bans] Rejecting banned player {steamId} on connect."); NetworkManager.Singleton.DisconnectClient(clientId, PluginConfig.BanReason.Value); } yield break; } yield return null; } AntiCheatPlugin.LogInfo($"[Bans] Could not resolve a Steam ID for client {clientId} within the timeout - ban check skipped for this connection."); } public static ulong SteamIdForClient(ulong clientId) { if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null) { return 0uL; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val != (Object)null && val.actualClientId == clientId) { return val.playerSteamId; } } return 0uL; } public static string NameForClient(ulong clientId) { if ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.allPlayerScripts != null) { PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val != (Object)null && val.actualClientId == clientId) { return val.playerUsername; } } } return $"Client {clientId}"; } } internal sealed class CheckEntry { public string Label; public string Group; public FeatureConfig Feature; public string Key => Feature.Key; } internal static class CheckCatalog { private const string Combat = "COMBAT AND DAMAGE"; private const string Movement = "MOVEMENT"; private const string Items = "ITEMS"; private const string Ship = "SHIP AND TERMINAL"; private const string World = "WORLD AND ENEMIES"; private const string Chat = "IDENTITY AND CLIENT MODS"; private static readonly List Entries = new List(); private static readonly List CheatEntries = new List(); private static readonly Dictionary ByKeyLookup = new Dictionary(); private static readonly Dictionary Merged = new Dictionary { { "DamageMagnitudeSetting", "GodModeSetting" }, { "KillOrderSetting", "GodModeSetting" }, { "EnemyHitAttributionSetting", "GodModeSetting" }, { "EnemyContactSetting", "GodModeSetting" }, { "FallDamageSetting", "GodModeSetting" }, { "HealthSetting", "GodModeSetting" }, { "RPCReportSetting", "GodModeSetting" }, { "ChatRealSettings", "PlayerNameSetting" }, { "NamelessSettings", "PlayerNameSetting" }, { "ItemCooldownSetting", "UseRateSetting" }, { "ShipLightSettings", "UseRateSetting" }, { "TerminalNoiseSettings", "UseRateSetting" } }; public static IList All => Entries; public static IList Cheats => CheatEntries; public static string CanonicalKey(string key) { if (string.IsNullOrEmpty(key)) { return key; } string value; return Merged.TryGetValue(key, out value) ? value : key; } public static string LabelFor(FeatureConfig feature) { if (feature == null) { return null; } CheckEntry checkEntry = ByKey(feature.Key); return (checkEntry != null) ? checkEntry.Label : feature.Key; } public static CheckEntry ByKey(string key) { if (string.IsNullOrEmpty(key)) { return null; } CheckEntry value; return ByKeyLookup.TryGetValue(CanonicalKey(key), out value) ? value : null; } internal static void Register() { Entries.Clear(); CheatEntries.Clear(); ByKeyLookup.Clear(); Add("COMBAT AND DAMAGE", "God mode", PluginConfig.GodMode); Add("COMBAT AND DAMAGE", "Impossible melee damage", PluginConfig.Shovel); Add("COMBAT AND DAMAGE", "Infinite shotgun ammo", PluginConfig.InfiniteAmmo); Add("MOVEMENT", "Excessive movement speed", PluginConfig.SpeedHack); Add("MOVEMENT", "Impossible position jump", PluginConfig.Teleport); Add("MOVEMENT", "Forced jetpack explosion", PluginConfig.Jetpack); Add("ITEMS", "Out-of-range item grabs", PluginConfig.GrabObject); Add("ITEMS", "Item deletion", PluginConfig.DespawnItem); Add("ITEMS", "Use-rate limits", PluginConfig.UseRate); Add("ITEMS", "Gift box abuse", PluginConfig.Gift); Add("ITEMS", "Abnormal mask spawns", PluginConfig.Mask); Add("SHIP AND TERMINAL", "Forced ship lever", PluginConfig.Ship); Add("SHIP AND TERMINAL", "Furniture placement", PluginConfig.ShipBuild); Add("SHIP AND TERMINAL", "Terminal use at distance", PluginConfig.RemoteTerminal); Add("SHIP AND TERMINAL", "Credit-free purchases", PluginConfig.FreeBuy); Add("SHIP AND TERMINAL", "Forced company monster", PluginConfig.Boss); Add("WORLD AND ENEMIES", "Forced landmine detonation", PluginConfig.Landmine); Add("WORLD AND ENEMIES", "Spawned spider webs", PluginConfig.SpawnWebTrap); Add("IDENTITY AND CLIENT MODS", "Player names", PluginConfig.PlayerName); Add("IDENTITY AND CLIENT MODS", "Minimap mods", PluginConfig.Map); } private static void Add(string group, string label, FeatureConfig feature) { if (feature != null) { CheckEntry checkEntry = new CheckEntry { Group = group, Label = label, Feature = feature }; Entries.Add(checkEntry); CheatEntries.Add(checkEntry); ByKeyLookup[checkEntry.Key] = checkEntry; } } } public enum PunishmentType { Disable, OnlyLog, Kick, Ban } public class FeatureConfig { public ConfigEntry PunishmentEntry { get; } public string Key { get; } public PunishmentType Punishment => PunishmentEntry.Value; public bool Enable => Punishment != PunishmentType.Disable; public bool Value => Enable; public FeatureConfig(ConfigFile config, string section, string featureDesc, PunishmentType defaultPunishment = PunishmentType.OnlyLog) { Key = section; PunishmentEntry = config.Bind(section, "Punishment", defaultPunishment, featureDesc + "\n\n" + AntiCheatPlugin.localizationManager.Cfg_GetString("Punishment")); } } internal static class LateJoinSync { private const uint GenerateNewLevelRpcId = 3073943002u; private const uint FinishGeneratingNewLevelRpcId = 2729232387u; private const string Module = "[LateJoin]"; private static readonly HashSet Pending = new HashSet(); private static readonly HashSet FinishedGenerating = new HashSet(); internal static void Reset() { Pending.Clear(); FinishedGenerating.Clear(); } internal static void NoteFinishedGenerating(ulong clientId) { FinishedGenerating.Add(clientId); } internal static void OnClientConnected(ulong clientId) { if (!((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.IsServer && clientId != NetworkManager.Singleton.LocalClientId) { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null) && !instance.inShipPhase && !Pending.Contains(clientId)) { Pending.Add(clientId); FinishedGenerating.Remove(clientId); ((MonoBehaviour)instance).StartCoroutine(SyncJoiner(clientId)); } } } private static IEnumerator SyncJoiner(ulong clientId) { AntiCheatPlugin.LogInfo(string.Format("{0} Client {1} joined mid-round; syncing them into the level.", "[LateJoin]", clientId)); yield return (object)new WaitForSeconds(2f); if (!StillConnected(clientId)) { Pending.Remove(clientId); yield break; } ClearStaleSlotState(clientId); SendNamesAndRepair(clientId); RoundManager manager = RoundManager.Instance; StartOfRound round = StartOfRound.Instance; if ((Object)(object)manager == (Object)null || (Object)(object)round == (Object)null || (Object)(object)manager.currentLevel == (Object)null) { AntiCheatPlugin.LogInfo(string.Format("{0} No round to sync client {1} into.", "[LateJoin]", clientId)); Pending.Remove(clientId); yield break; } int seed = round.randomMapSeed; int levelId = manager.currentLevel.levelID; int moldIterations = manager.currentLevel.moldSpreadIterations; int moldStart = manager.currentLevel.moldStartPosition; uint generateId = TargetedClientRpc.ResolveRpcId(3073943002u, "GenerateNewLevelClientRpc"); if (!TargetedClientRpc.Send((NetworkBehaviour)(object)manager, generateId, clientId, delegate(FastBufferWriter writer) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) BytePacker.WriteValueBitPacked(writer, seed); BytePacker.WriteValueBitPacked(writer, levelId); BytePacker.WriteValueBitPacked(writer, moldIterations); BytePacker.WriteValueBitPacked(writer, moldStart); bool flag = false; ((FastBufferWriter)(ref writer)).WriteValueSafe(ref flag, default(ForPrimitives)); })) { AntiCheatPlugin.LogInfo(string.Format("{0} Could not send the level to client {1}. They are in the round with no interior - kick them and have them rejoin from orbit.", "[LateJoin]", clientId)); Pending.Remove(clientId); yield break; } AntiCheatPlugin.LogInfo(string.Format("{0} Sent level {1} (seed {2}) to client {3}; waiting for them to finish generating.", "[LateJoin]", levelId, seed, clientId)); float timeout = ((PluginConfig.Lobby_LateJoinSyncTimeout == null) ? 90f : Mathf.Clamp(PluginConfig.Lobby_LateJoinSyncTimeout.Value, 10f, 600f)); float deadline = Time.realtimeSinceStartup + timeout; while (!FinishedGenerating.Contains(clientId) && Time.realtimeSinceStartup < deadline) { if (!StillConnected(clientId)) { AntiCheatPlugin.LogInfo(string.Format("{0} Client {1} left while generating.", "[LateJoin]", clientId)); Pending.Remove(clientId); yield break; } yield return (object)new WaitForSeconds(0.5f); } if (!FinishedGenerating.Contains(clientId)) { AntiCheatPlugin.LogInfo(string.Format("{0} Client {1} did not report a finished dungeon within {2}s - lifting their loading screen regardless.", "[LateJoin]", clientId, timeout)); } uint finishId = TargetedClientRpc.ResolveRpcId(2729232387u, "FinishGeneratingNewLevelClientRpc"); TargetedClientRpc.Send((NetworkBehaviour)(object)manager, finishId, clientId, null); RecountLivingPlayers(); Pending.Remove(clientId); AntiCheatPlugin.LogInfo(string.Format("{0} Client {1} is now in the round.", "[LateJoin]", clientId)); MenuLog.Info(string.Format("{0} {1} joined mid-round.", "[LateJoin]", NameFor(clientId))); if (PluginConfig.Lobby_AnnounceLateJoin != null && PluginConfig.Lobby_AnnounceLateJoin.Value) { AntiCheatPlugin.AddTextMessageClientRpc(AntiCheatPlugin.Colorize($"{NameFor(clientId)} joined mid-round.", (PluginConfig.PlayerJoin_Color == null) ? string.Empty : PluginConfig.PlayerJoin_Color.Value)); } } private static bool StillConnected(ulong clientId) { if ((Object)(object)NetworkManager.Singleton == (Object)null) { return false; } foreach (ulong connectedClientsId in NetworkManager.Singleton.ConnectedClientsIds) { if (connectedClientsId == clientId) { return true; } } return false; } private static PlayerControllerB PlayerFor(ulong clientId) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null) { return null; } if (instance.ClientPlayerList != null && instance.ClientPlayerList.TryGetValue(clientId, out var value) && value >= 0 && value < instance.allPlayerScripts.Length) { return instance.allPlayerScripts[value]; } return null; } private static string NameFor(ulong clientId) { PlayerControllerB val = PlayerFor(clientId); return ((Object)(object)val == (Object)null || string.IsNullOrEmpty(val.playerUsername)) ? $"Client {clientId}" : val.playerUsername; } private static void SendNamesAndRepair(ulong clientId) { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null) && instance.allPlayerScripts != null && instance.ClientPlayerList != null) { string[] array = new string[instance.allPlayerScripts.Length]; for (int i = 0; i < array.Length; i++) { PlayerControllerB val = instance.allPlayerScripts[i]; array[i] = (((Object)(object)val == (Object)null) ? string.Empty : (val.playerUsername ?? string.Empty)); } if (instance.ClientPlayerList.TryGetValue(clientId, out var value)) { Network.SendPlayerNames(clientId, array); Network.SendPlayerSlotRepair(clientId, value); } } } internal static void ApplyPlayerNames(PlayerNames received) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null || ((PlayerNames)(ref received)).Names == null) { return; } int num = Math.Min(instance.allPlayerScripts.Length, ((PlayerNames)(ref received)).Names.Length); for (int i = 0; i < num; i++) { PlayerControllerB val = instance.allPlayerScripts[i]; if ((Object)(object)val != (Object)null && !string.IsNullOrEmpty(((PlayerNames)(ref received)).Names[i])) { val.playerUsername = ((PlayerNames)(ref received)).Names[i]; } } } internal static void RepairPlayerSlot(PlayerSlotRepair repair) { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null) && instance.allPlayerScripts != null && instance.allPlayerObjects != null && ((PlayerSlotRepair)(ref repair)).Slot >= 0 && ((PlayerSlotRepair)(ref repair)).Slot < instance.allPlayerScripts.Length && ((PlayerSlotRepair)(ref repair)).Slot < instance.allPlayerObjects.Length) { PlayerControllerB val = instance.allPlayerScripts[((PlayerSlotRepair)(ref repair)).Slot]; if ((Object)(object)val != (Object)null && (Object)(object)instance.allPlayerObjects[((PlayerSlotRepair)(ref repair)).Slot] != (Object)null) { val.DisablePlayerModel(instance.allPlayerObjects[((PlayerSlotRepair)(ref repair)).Slot], true, false); } } } private static void ClearStaleSlotState(ulong clientId) { PlayerControllerB val = PlayerFor(clientId); if ((Object)(object)val == (Object)null) { return; } try { val.disconnectedMidGame = false; val.isPlayerDead = false; val.isInsideFactory = false; val.criticallyInjured = false; val.bleedingHeavily = false; val.health = 100; } catch (Exception arg) { AntiCheatPlugin.LogInfo(string.Format("{0} Could not clear the slot for client {1}: {2}", "[LateJoin]", clientId, arg)); } } private static void RecountLivingPlayers() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null) { return; } int num = 0; for (int i = 0; i < instance.allPlayerScripts.Length; i++) { PlayerControllerB val = instance.allPlayerScripts[i]; if ((Object)(object)val != (Object)null && val.isPlayerControlled && !val.isPlayerDead) { num++; } } if (instance.livingPlayers != num) { AntiCheatPlugin.LogInfo(string.Format("{0} Living player count corrected from {1} to {2} after the join.", "[LateJoin]", instance.livingPlayers, num)); instance.livingPlayers = num; instance.allPlayersDead = num == 0; } } } public enum LobbyVisibility { Public, FriendsOnly, Private, Invisible } internal static class LobbyControl { private const string Module = "[Lobby]"; private static bool _visibilityChosen; private static bool _lateJoinChosen; public static LobbyVisibility Visibility { get; private set; } = LobbyVisibility.FriendsOnly; public static bool IsOpen { get; private set; } public static bool LateJoin { get; private set; } public static bool HasLobby => (Object)(object)GameNetworkManager.Instance != (Object)null && !GameNetworkManager.Instance.disableSteam && GameNetworkManager.Instance.currentLobby.HasValue; public static bool IsHost => (Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer; public static bool InOrbit => (Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.inShipPhase; public static bool Reopening { get; private set; } private static bool CanControl(out string reason) { if (!IsHost) { reason = "Only the host can change the lobby."; return false; } if ((Object)(object)GameNetworkManager.Instance == (Object)null || GameNetworkManager.Instance.disableSteam) { reason = "This session is not running on Steam."; return false; } if (!InOrbit) { reason = "Only in orbit. Come back to orbit to change the lobby."; return false; } reason = null; return true; } public static bool CanEditVisibility(out string reason) { return CanControl(out reason); } public static bool CanEditJoining(out string reason) { if (!CanControl(out reason)) { return false; } if (!HasLobby) { reason = "No Steam lobby - the game left it at take-off. Reopen one below."; return false; } reason = null; return true; } public static bool CanReopen(out string reason) { if (!CanControl(out reason)) { return false; } if (HasLobby) { reason = "The lobby is already up."; return false; } if (Reopening) { reason = "Already opening a lobby."; return false; } reason = null; return true; } public static string Describe() { if (!HasLobby) { return "no lobby"; } return string.Format("{0} / {1}", IsOpen ? "open" : "closed", VisibilityText(Visibility)); } public static string VisibilityText(LobbyVisibility visibility) { return visibility switch { LobbyVisibility.Public => "public", LobbyVisibility.FriendsOnly => "friends only", LobbyVisibility.Private => "invite only", _ => "invisible", }; } internal static void OnLobbyCreated(bool isPublic) { if (!_visibilityChosen) { Visibility = ((!isPublic) ? LobbyVisibility.FriendsOnly : LobbyVisibility.Public); } IsOpen = false; if (!_lateJoinChosen) { LateJoin = PluginConfig.Lobby_AllowLateJoin != null && PluginConfig.Lobby_AllowLateJoin.Value; } AntiCheatPlugin.LogInfo(string.Format("{0} Lobby created: {1}, late join {2}.", "[Lobby]", VisibilityText(Visibility), LateJoin ? "on" : "off")); } internal static void OnLobbyLeft() { IsOpen = false; LateJoinSync.Reset(); PlayerSlots.Reset(); } internal static void NoteJoinableChanged(bool joinable) { IsOpen = joinable; } public static bool SetVisibility(LobbyVisibility visibility, out string message) { if (!CanEditVisibility(out message)) { return false; } if (HasLobby && !ApplyVisibility(visibility, out message)) { return false; } Visibility = visibility; _visibilityChosen = true; message = (HasLobby ? $"Lobby is now {VisibilityText(visibility)}" : $"Next lobby will be {VisibilityText(visibility)}"); MenuLog.Info(string.Format("{0} Host set the lobby {1}.", "[Lobby]", VisibilityText(visibility))); return true; } public static bool SetOpen(bool open, out string message) { //IL_0025: 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) if (!CanEditJoining(out message)) { return false; } try { Lobby value = GameNetworkManager.Instance.currentLobby.Value; ((Lobby)(ref value)).SetJoinable(open); } catch (Exception arg) { message = "Steam refused the change - see the console"; AntiCheatPlugin.LogInfo(string.Format("{0} SetJoinable({1}) failed: {2}", "[Lobby]", open, arg)); return false; } IsOpen = open; message = (open ? "Lobby is open" : "Lobby is closed"); MenuLog.Info(string.Format("{0} Host {1} the lobby.", "[Lobby]", open ? "opened" : "closed")); return true; } public static bool SetLateJoin(bool on, out string message) { if (!CanControl(out message)) { return false; } LateJoin = on; _lateJoinChosen = true; message = (on ? "Late join on - the lobby stays up after take-off" : "Late join off - the lobby closes at take-off"); MenuLog.Info(string.Format("{0} Host turned late join {1}.", "[Lobby]", on ? "on" : "off")); return true; } public static bool Reopen(out string message) { if (!CanReopen(out message)) { return false; } Reopening = true; message = "Opening a new lobby..."; AntiCheatPlugin.LogInfo(string.Format("{0} Host asked for a new lobby.", "[Lobby]")); CreateReplacementLobby(); return true; } private static async void CreateReplacementLobby() { try { Lobby? created = await SteamMatchmaking.CreateLobbyAsync(4); if (!created.HasValue) { AntiCheatPlugin.LogInfo(string.Format("{0} Steam would not create a lobby.", "[Lobby]")); return; } if ((Object)(object)GameNetworkManager.Instance != (Object)null && !GameNetworkManager.Instance.currentLobby.HasValue) { AccessTools.Property(typeof(GameNetworkManager), "currentLobby").SetValue(GameNetworkManager.Instance, created); AntiCheatPlugin.LogInfo(string.Format("{0} Adopted the new lobby by hand.", "[Lobby]")); } ApplyVisibility(Visibility, out var _); Lobby lobby = GameNetworkManager.Instance.currentLobby.Value; ((Lobby)(ref lobby)).SetJoinable(true); IsOpen = true; AntiCheatPlugin.LogInfo(string.Format("{0} New lobby is up: open, {1}.", "[Lobby]", VisibilityText(Visibility))); MenuLog.Info(string.Format("{0} Host opened a new lobby ({1}).", "[Lobby]", VisibilityText(Visibility))); } catch (Exception ex) { Exception ex2 = ex; AntiCheatPlugin.LogInfo(string.Format("{0} Could not open a new lobby: {1}", "[Lobby]", ex2)); } finally { Reopening = false; } } internal static void ReassertAfterTakeOff() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (HasLobby) { ApplyVisibility(Visibility, out var _); try { Lobby value = GameNetworkManager.Instance.currentLobby.Value; ((Lobby)(ref value)).SetJoinable(IsOpen); } catch (Exception arg) { AntiCheatPlugin.LogInfo(string.Format("{0} Could not re-assert joinable at take-off: {1}", "[Lobby]", arg)); } AntiCheatPlugin.LogInfo(string.Format("{0} Kept the lobby alive through take-off: {1}, {2}.", "[Lobby]", IsOpen ? "open" : "closed", VisibilityText(Visibility))); } } private static bool ApplyVisibility(LobbyVisibility visibility, out string message) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) message = null; try { Lobby value = GameNetworkManager.Instance.currentLobby.Value; switch (visibility) { case LobbyVisibility.Public: ((Lobby)(ref value)).SetPublic(); break; case LobbyVisibility.FriendsOnly: ((Lobby)(ref value)).SetFriendsOnly(); break; case LobbyVisibility.Private: ((Lobby)(ref value)).SetPrivate(); break; default: ((Lobby)(ref value)).SetInvisible(); break; } ((Lobby)(ref value)).SetData("vers", GameNetworkManager.Instance.gameVersionNum.ToString()); if (!string.IsNullOrEmpty(GameNetworkManager.Instance.steamLobbyName)) { ((Lobby)(ref value)).SetData("name", GameNetworkManager.Instance.steamLobbyName); } return true; } catch (Exception arg) { message = "Steam refused the change - see the console"; AntiCheatPlugin.LogInfo(string.Format("{0} Could not set visibility {1}: {2}", "[Lobby]", visibility, arg)); return false; } } } internal static class LobbyImprovementsBans { private static FieldInfo _kickReasonField; private static string _banPrefix; private static bool _resolved; private const string DefaultBanPrefix = "Banned From Lobby:"; public static void TryIntegrate(Harmony harmony) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown if (!PluginConfig.EnableLobbyImprovementsBanIntegration.Value) { return; } try { harmony.Patch((MethodBase)AccessTools.DeclaredMethod(typeof(StartOfRound), "KickPlayer", (Type[])null, (Type[])null), new HarmonyMethod(typeof(LobbyImprovementsBans), "BanOnLobbyImprovementsBanPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); AntiCheatPlugin.LogInfo("[LobbyImprovements] Patched the kick method - the Ban button will now ban through the AntiCheat ban list."); } catch (Exception arg) { AntiCheatPlugin.LogInfo($"[LobbyImprovements] Setup failed: {arg}"); } } private static bool EnsureResolved() { if (_resolved) { return _kickReasonField != null; } _resolved = true; Type type = AccessTools.TypeByName("LobbyImprovements.General_Patches"); if (type == null) { AntiCheatPlugin.LogInfo("[LobbyImprovements] Type 'LobbyImprovements.General_Patches' not found - integration not active."); return false; } FieldInfo field = type.GetField("kickReason", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field == null || field.FieldType != typeof(string)) { AntiCheatPlugin.LogInfo("[LobbyImprovements] Field 'General_Patches.kickReason' not found - integration not active."); return false; } _banPrefix = ReadBanPrefixConstant(type); _kickReasonField = field; AntiCheatPlugin.LogInfo("[LobbyImprovements] Resolved the kick reason - the Ban button is now wired up."); return true; } private static string ReadBanPrefixConstant(Type generalPatchesType) { try { FieldInfo field = generalPatchesType.GetField("banPrefixStr", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.IsLiteral) { string text = field.GetRawConstantValue() as string; if (!string.IsNullOrEmpty(text)) { return text; } } } catch (Exception arg) { AntiCheatPlugin.LogInfo($"[LobbyImprovements] Could not read 'General_Patches.banPrefixStr', using the default wording: {arg}"); } return "Banned From Lobby:"; } private static bool BanOnLobbyImprovementsBanPrefix(StartOfRound __instance, int playerObjToKick) { try { if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer) { return true; } if (!EnsureResolved()) { return true; } string text = _kickReasonField.GetValue(null) as string; if (string.IsNullOrEmpty(text) || text.IndexOf(_banPrefix, StringComparison.OrdinalIgnoreCase) < 0) { return true; } if (playerObjToKick < 0 || playerObjToKick >= __instance.allPlayerScripts.Length) { return true; } PlayerControllerB val = __instance.allPlayerScripts[playerObjToKick]; if ((Object)(object)val == (Object)null || (!val.isPlayerControlled && !val.isPlayerDead)) { return true; } string playerUsername = val.playerUsername; ulong playerSteamId = val.playerSteamId; PatchHelper.BanPlayer(val, text, BanSource.Host); AntiCheatPlugin.LogInfo($"[LobbyImprovements] {playerUsername} (steamId {playerSteamId}) banned through the AntiCheat ban list instead of LobbyImprovements' session ban."); return false; } catch (Exception arg) { AntiCheatPlugin.LogInfo($"[LobbyImprovements] Ban redirect failed, falling back to LobbyImprovements' own ban: {arg}"); return true; } } } internal enum SlotState { Host, Occupied, Free, Unavailable } internal struct SlotInfo { public int Index; public SlotState State; public string Name; } internal static class PlayerSlots { internal const int Count = 4; private static readonly string[] LastNames = new string[4]; internal static void Reset() { for (int i = 0; i < LastNames.Length; i++) { LastNames[i] = null; } } internal static bool IsModelVisible(int index) { PlayerControllerB val = ScriptFor(index); return (Object)(object)val != (Object)null && (Object)(object)val.thisPlayerModel != (Object)null && ((Renderer)val.thisPlayerModel).enabled; } internal static List Describe() { List list = new List(4); StartOfRound instance = StartOfRound.Instance; for (int i = 0; i < 4; i++) { PlayerControllerB val = ScriptFor(i); SlotInfo item = new SlotInfo { Index = i }; if ((Object)(object)val == (Object)null || (Object)(object)instance == (Object)null) { item.State = ((i != 0) ? SlotState.Free : SlotState.Host); item.Name = LastNames[i]; list.Add(item); continue; } if (val.isPlayerControlled || val.isPlayerDead) { if (!string.IsNullOrEmpty(val.playerUsername)) { LastNames[i] = val.playerUsername; } item.State = ((i != 0) ? SlotState.Occupied : SlotState.Host); } else { item.State = (IsModelVisible(i) ? SlotState.Free : SlotState.Unavailable); } item.Name = LastNames[i]; list.Add(item); } return list; } internal static List UsableFree(StartOfRound round) { return Free(round, wantVisible: true); } internal static List UnavailableFree(StartOfRound round) { return Free(round, wantVisible: false); } private static List Free(StartOfRound round, bool wantVisible) { List list = new List(4); if ((Object)(object)round == (Object)null || round.ClientPlayerList == null) { return list; } for (int i = 1; i < 4; i++) { if (!round.ClientPlayerList.ContainsValue(i) && IsModelVisible(i) == wantVisible) { list.Add(i); } } return list; } internal static string NameFor(int index) { return (index >= 0 && index < LastNames.Length) ? LastNames[index] : null; } private static PlayerControllerB ScriptFor(int index) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null || index < 0 || index >= instance.allPlayerScripts.Length) { return null; } return instance.allPlayerScripts[index]; } } internal static class PluginConfig { public enum MessageType { GUI, HostChat, PublicChat, Menu } public enum PermissionLevel { Unrestricted, HostOnly, GameStart } public enum FriendlyFirePolicy { Allowed, Restricted } public const string DefaultAntiGriefKickReason = "You were kicked by the Anti-Grief System"; public const string DefaultAntiGriefBanReason = "You were banned by the Anti-Grief System"; public const string DefaultAntiCheatBanReason = "You have been banned by the Anti Cheat System for Cheating"; public static FeatureConfig Shovel; public static FeatureConfig Ship; public static FeatureConfig ShipBuild; public static FeatureConfig Landmine; public static FeatureConfig InfiniteAmmo; public static FeatureConfig Mask; public static FeatureConfig Gift; public static FeatureConfig Teleport; public static FeatureConfig SpawnWebTrap; public static FeatureConfig Map; public static FeatureConfig FreeBuy; public static FeatureConfig RemoteTerminal; public static FeatureConfig DespawnItem; public static FeatureConfig PlayerName; public static FeatureConfig UseRate; public static FeatureConfig Boss; public static FeatureConfig GrabObject; public static FeatureConfig Jetpack; public static FeatureConfig GodMode; public static FeatureConfig SpeedHack; public static ConfigEntry Restrict_FriendlyFire; public static ConfigEntry Restrict_PurchaseStoreItems; public static ConfigEntry Restrict_PurchaseStoreUnlocks; public static ConfigEntry Restrict_RouteShipToMoon; public static ConfigEntry Restrict_PlaceObjectIntoStorage; public static ConfigEntry Restrict_ReturnObjectFromStorage; public static ConfigEntry Restrict_MoveObjects; public static ConfigEntry Restrict_MoveLightSwitch; public static ConfigEntry Restrict_ToggleShipLights; public static ConfigEntry Restrict_PullShipLever; public static ConfigEntry AntiGrief_MaxViolations; public static ConfigEntry AntiGrief_Punishment; public static ConfigEntry AntiGrief_KickReason; public static ConfigEntry AntiGrief_BanReason; public static ConfigEntry AntiGrief_MessageColor; public static ConfigEntry Profiles_Data; public static ConfigEntry Profiles_Assignments; public static ConfigEntry BannedSteamIds; public static ConfigEntry BanRecords; public static ConfigEntry BanReason; public static ConfigEntry EnableLobbyImprovementsBanIntegration; public static ConfigEntry IgnoreClientConfig; public static ConfigEntry Prefix; public static ConfigEntry PlayerJoin; public static ConfigEntry PlayerJoin_Color; public static ConfigEntry PlayerJoin_MessageType; public static ConfigEntry Disclaimer_Enable; public static ConfigEntry Disclaimer_OnJoin; public static ConfigEntry Disclaimer_Text; public static ConfigEntry Disclaimer_Aliases; public static ConfigEntry Disclaimer_MessageType; public static ConfigEntry Disclaimer_JoinDelaySeconds; public static ConfigEntry Disclaimer_CooldownSeconds; public static ConfigEntry Log; public static ConfigEntry OperationLog; public static ConfigEntry ShipConfig2; public static ConfigEntry ShipConfig3; public static ConfigEntry ShipConfig4; public static ConfigEntry ShipSetting_OnlyOneVote; public static ConfigEntry ShipSetting_ChangToFreeMoon; public static ConfigEntry UseRate_ShipLightCooldown; public static ConfigEntry UseRate_TerminalNoiseCooldown; public static ConfigEntry UseRate_ChatCooldown; public static ConfigEntry GodMode_ReportDelay; public static ConfigEntry GodMode_WatchHitRpc; public static ConfigEntry GodMode_WatchKillPlayerRpc; public static ConfigEntry Shovel3; public static ConfigEntry GrabObject_SendLog; public static ConfigEntry GrabObject_MoreSlot; public static ConfigEntry GrabObject_TwoHand; public static ConfigEntry GrabObject_BeltBag; public static ConfigEntry OperationLog_MessageType; public static ConfigEntry Lobby_AllowLateJoin; public static ConfigEntry Lobby_LateJoinSyncTimeout; public static ConfigEntry Lobby_AnnounceLateJoin; public static ConfigEntry Menu_Enable; public static ConfigEntry Menu_MaxLogLines; public static ConfigEntry Menu_MaxRecentPlayers; public static ConfigEntry GodMode_InstakillGraceSeconds; public static ConfigEntry GodMode_EnemyHitLethalThreshold; public static ConfigEntry GodMode_EnemyHitRequireAck; public static ConfigEntry GodMode_EnemyHitAckSeconds; public static ConfigEntry SpeedHack_MaxMultiplier; public static ConfigEntry SpeedHack_MinMovingSpeed; public static ConfigEntry SpeedHack_MinAbsoluteSpeed; public static ConfigEntry SpeedHack_MinConsecutiveReadings; public static ConfigEntry PositionSampleWindowSeconds; public static ConfigEntry MaxPlausibleSpeed; public static ConfigEntry MovementGraceSeconds; public static ConfigEntry MovementDiagnostics; public static bool ShipConfig => Ship.Enable; internal static void Init(BaseUnityPlugin plugin) { ConfigFile config = plugin.Config; LocalizationManager localizationManager = AntiCheatPlugin.localizationManager; IgnoreClientConfig = config.Bind("VersionSetting", "NetworkSetting", false, localizationManager.Cfg_GetString("NetworkSetting")); Prefix = config.Bind("ServerNameSetting", "Prefix", "AC", localizationManager.Cfg_GetString("Prefix")); Log = config.Bind("LogSetting", "Log", true, localizationManager.Cfg_GetString("Log")); OperationLog = config.Bind("LogSetting", "OperationLog", true, localizationManager.Cfg_GetString("OperationLog")); Ship = new FeatureConfig(config, "ShipSetting", localizationManager.Cfg_GetString("ShipSetting")); ShipConfig2 = config.Bind("ShipSetting", "StartGamePlayerCount", 8, localizationManager.Cfg_GetString("ShipConfig2")); ShipConfig3 = config.Bind("ShipSetting", "EndGamePlayerTime", "14:00", localizationManager.Cfg_GetString("ShipConfig3")); ShipConfig4 = config.Bind("ShipSetting", "EndGamePlayerCount", 50, localizationManager.Cfg_GetString("ShipConfig4")); ShipSetting_OnlyOneVote = config.Bind("ShipSetting", "OnlyOneVote", true, localizationManager.Cfg_GetString("ShipConfig6")); ShipSetting_ChangToFreeMoon = config.Bind("ShipSetting", "ChangToFreeMoon", false, localizationManager.Cfg_GetString("ChangToFreeMoon")); ShipBuild = new FeatureConfig(config, "ShipBuildSetting", localizationManager.Cfg_GetString("ShipBuild")); UseRate = new FeatureConfig(config, "UseRateSetting", "Detects an action repeated faster than the game allows: items and weapons used inside their own animation, the ship's light switch, the terminal's noise, and chat. The action is refused; the cooldowns themselves are the entries below."); DespawnItem = new FeatureConfig(config, "DespawnItemSettings", localizationManager.Cfg_GetString("DespawnItem")); Mask = new FeatureConfig(config, "MaskSettings", localizationManager.Cfg_GetString("Mask")); Gift = new FeatureConfig(config, "GiftSettings", localizationManager.Cfg_GetString("Gift")); InfiniteAmmo = new FeatureConfig(config, "InfiniteAmmoSettings", localizationManager.Cfg_GetString("InfiniteAmmo")); Teleport = new FeatureConfig(config, "InvisibilitySettings", localizationManager.Cfg_GetString("Teleport")); Boss = new FeatureConfig(config, "BossSetting", localizationManager.Cfg_GetString("Boss")); Jetpack = new FeatureConfig(config, "JetpackSetting", localizationManager.Cfg_GetString("Jetpack")); Landmine = new FeatureConfig(config, "LandmineSetting", localizationManager.Cfg_GetString("Landmine")); SpawnWebTrap = new FeatureConfig(config, "SpawnWebTrapSetting", localizationManager.Cfg_GetString("SpawnWebTrap")); Map = new FeatureConfig(config, "MapSetting", localizationManager.Cfg_GetString("Map")); FreeBuy = new FeatureConfig(config, "FreeBuySettings", localizationManager.Cfg_GetString("FreeBuy")); RemoteTerminal = new FeatureConfig(config, "RemoteTerminalSettings", localizationManager.Cfg_GetString("RemoteTerminal")); PlayerName = new FeatureConfig(config, "PlayerNameSetting", "Detects a player sending chat under somebody else's identity - a message addressed from a crewmate's player id rather than their own. The message is refused as well as reported."); Shovel = new FeatureConfig(config, "ShovelSettings", localizationManager.Cfg_GetString("Shovel")); GrabObject = new FeatureConfig(config, "GrabObjectSetting", localizationManager.Cfg_GetString("GrabObject")); GodMode = new FeatureConfig(config, "GodModeSetting", "Flags a player who survives what should have killed them, from every angle the host can see it. Their own damage reports (more damage taken than health left, still alive). Damage reports that never arrive at all, for an announced Hit or KillPlayer RPC. Damage added up host-side from the enemies that announce their hits (Thumper 40, Bunker Spider 90, Hoarder Bug 30). The enemies that announce nothing and decide damage on the victim's machine (Coilhead, Hygrodere, Baboon Hawk, Cadaver Bloom, both bees), simulated host-side with vanilla's own collider overlap, hit interval and damage numbers. Falls, run through vanilla's own arithmetic - 38 per second, the 30/50/80/100 ladder - with jetpacks, ladders, vehicles, lifts, quicksand and water left alone as vanilla leaves them. The instakill enemies (Eyeless Dog, Jester, Bracken, Masked, Maneater), which send no damage report at all and are only visible by the victim not dying; a kill the enemy never got to finish is never counted. And negative damage reported as healing. Damage amounts and hit timing are never judged on their own, so modded enemies and weapons do not trip it."); SpeedHack = new FeatureConfig(config, "SpeedHackSetting", "Flags sustained movement speed well above a player's own recent baseline, inferred from position reports over time. Only movement a player produced themselves is judged: jetpack flights, ladders, the cruiser, lifts, the ship in orbit and both the landing and the take-off are all excluded outright, and so is every sample where the player was off the ground - which their own controller reports, rather than the host guessing at it with a raycast that misses on stairs and ramps."); BannedSteamIds = config.Bind("!Bans", "BannedSteamIds", "", "Comma-separated list of banned Steam64 IDs. Written automatically whenever a check or an Anti-Grief violation results in a Ban punishment; you can also edit it by hand. Every entry here is rejected at connection approval, so a ban survives a restart."); BanRecords = config.Bind("!Bans", "BanRecords", "", "Written automatically - what put each banned Steam ID on the list, their name and when, for the Ban List window in the menu. One entry per ban as steamId|source|name|timestamp, separated by semicolons. BannedSteamIds above is still the list that decides who is refused; an ID there with no entry here is simply an unattributed ban and shows as \"-\"."); BanReason = config.Bind("!Bans", "BanReason", "You have been banned by the Anti Cheat System for Cheating", "Disconnect reason shown to a player banned by one of the cheat checks."); EnableLobbyImprovementsBanIntegration = config.Bind("!Bans", "LobbyImprovementsBanIntegration", true, "Routes LobbyImprovements' in-game Ban button through the ban list above. LobbyImprovements' own ban only lasts the session, so without this the player is back the moment the host opens a new lobby. Does nothing if LobbyImprovements is not installed."); Restrict_FriendlyFire = config.Bind("AntiGriefSetting", "FriendlyFire", FriendlyFirePolicy.Restricted, "Restricted blocks player weapon damage for everyone. Allowed restores vanilla friendly fire. Creature, mine, turret, fall and ship-door damage are unchanged."); Restrict_PurchaseStoreItems = config.Bind("AntiGriefSetting", "PurchaseStoreItems", PermissionLevel.GameStart, "Who can order from the terminal. Unrestricted = anybody, HostOnly = the host, GameStart = the host until a player has been through a landing.\n\nGameStart is per player: it lifts once the ship has landed since they joined, so a newcomer three rounds in is restricted exactly as the founding crew were. Reconnecting keeps the rounds already served; a new lobby starts everybody over."); Restrict_PurchaseStoreUnlocks = config.Bind("AntiGriefSetting", "PurchaseStoreUnlocks", PermissionLevel.GameStart, "Restricts who can purchase store unlockables."); Restrict_RouteShipToMoon = config.Bind("AntiGriefSetting", "RouteShipToMoon", PermissionLevel.GameStart, "Restricts who can route the ship to a moon."); Restrict_PlaceObjectIntoStorage = config.Bind("AntiGriefSetting", "PlaceObjectIntoStorage", PermissionLevel.GameStart, "Restricts who can place objects into storage."); Restrict_ReturnObjectFromStorage = config.Bind("AntiGriefSetting", "ReturnObjectFromStorage", PermissionLevel.GameStart, "Restricts who can take objects back out of storage."); Restrict_MoveObjects = config.Bind("AntiGriefSetting", "MoveObjects", PermissionLevel.GameStart, "Who can move ship furniture. The light switch has its own setting below."); Restrict_MoveLightSwitch = config.Bind("AntiGriefSetting", "MoveLightSwitch", PermissionLevel.GameStart, "Who can move the light switch, separately from the rest of the furniture. Hiding it is the cheap grief, so it is worth locking down even where MoveObjects is Unrestricted."); Restrict_ToggleShipLights = config.Bind("AntiGriefSetting", "ToggleShipLights", PermissionLevel.GameStart, "Restricts who can toggle the ship lights."); Restrict_PullShipLever = config.Bind("AntiGriefSetting", "PullShipLever", 0, "Earliest in-game hour players may pull the ship lever. 0 means no hour restriction."); AntiGrief_MaxViolations = config.Bind("AntiGriefSetting", "MaxViolations", 5, "Blocked actions a player may rack up in a round before the punishment below applies. 0 turns punishment off - actions are still blocked and still warned, they just never lead to a kick or ban."); AntiGrief_Punishment = config.Bind("AntiGriefSetting", "Punishment", PunishmentType.Kick, "What happens to a player who reaches MaxViolations. Disable and OnlyLog both leave the player alone - actions are still blocked either way."); AntiGrief_KickReason = config.Bind("AntiGriefSetting", "KickReason", "You were kicked by the Anti-Grief System", "Disconnect reason shown to a player kicked by the Anti-Grief system."); AntiGrief_BanReason = config.Bind("AntiGriefSetting", "BanReason", "You were banned by the Anti-Grief System", "Disconnect reason shown to a player banned by the Anti-Grief system."); AntiGrief_MessageColor = config.Bind("AntiGriefSetting", "MessageColor", "red", "TextMeshPro color name or hex code for chat fallback when a blocked player does not have Overlay or is not registered with its network alerts."); Profiles_Data = config.Bind("!Profiles", "Profiles", "", "Written automatically by the Profiles window in the menu. One profile per entry as name|bypassAllChecks|antiGriefRuleKeys|checkKeys, separated by semicolons. A profile is a named set of exemptions and grants nothing until it is assigned to somebody in Assignments below."); Profiles_Assignments = config.Bind("!Profiles", "Assignments", "", "Written automatically - which Steam ID carries which profile, as steamId=profileName separated by semicolons. Host-side only: nothing about a profile is sent to the player it applies to, and a player with no assignment here is subject to every rule and every check as normal."); UseRate_ShipLightCooldown = config.Bind("UseRateSetting", "ShipLightCooldown", 2000, localizationManager.Cfg_GetString("Cooldown")); UseRate_TerminalNoiseCooldown = config.Bind("UseRateSetting", "TerminalNoiseCooldown", 1000, localizationManager.Cfg_GetString("Cooldown")); UseRate_ChatCooldown = config.Bind("UseRateSetting", "ChatCooldown", 100, localizationManager.Cfg_GetString("Cooldown")); GodMode_ReportDelay = config.Bind("GodModeSetting", "ReportDelay", 1000, localizationManager.Cfg_GetString("RPCReport_Delay")); GodMode_WatchHitRpc = config.Bind("GodModeSetting", "WatchHitRpc", true, localizationManager.Cfg_GetString("RPCReport_Hit")); GodMode_WatchKillPlayerRpc = config.Bind("GodModeSetting", "WatchKillPlayerRpc", true, localizationManager.Cfg_GetString("RPCReport_KillPlayer")); OperationLog_MessageType = config.Bind("LogSetting", "OperationLogType", MessageType.Menu, "Where the operation log goes - who bought what, who voted, who took the apparatus. Menu = the log window in the in-game menu only, HostChat = the host's chat as it used to be, PublicChat = everyone's chat, GUI = the host's tip panel. Everything is written to AntiCheat.log either way."); Lobby_AllowLateJoin = config.Bind("LobbySetting", "AllowLateJoin", false, "Keeps the Steam lobby alive when the ship leaves orbit, so players can still join once the round is underway. Vanilla does not close the lobby at take-off, it leaves it for good, so this is the switch that decides whether the lobby exists at all for the rest of the session. Host-side: a joining player needs no mod. This is the starting value only - the Lobby window in the menu turns it on and off per session, in orbit."); Lobby_LateJoinSyncTimeout = config.Bind("LobbySetting", "LateJoinSyncTimeoutSeconds", 90f, "How long to wait for a mid-round joiner to finish building the interior before lifting their loading screen anyway. They build it from the seed the host sends them, which on a large moon on a slow machine is not quick; the wait ends the moment they report it done, so this only matters when something has gone wrong."); Lobby_AnnounceLateJoin = config.Bind("LobbySetting", "AnnounceLateJoin", true, "Posts a chat line to everybody when a player joins mid-round. The normal join message is not shown for these, since the game itself only announces players who arrive in orbit."); Menu_Enable = config.Bind("MenuSetting", "Enable", true, "Whether this mod registers its windows with Overlay. Off means AntiCheat has no interface but goes on working exactly as configured - every check, every rule and every ban is host-side and none of them need a window open. The key that opens the dock, its colours and its diagnostics are Overlay's own settings, in Overlay.cfg, because they are shared by every mod that draws into it."); Menu_MaxLogLines = config.Bind("MenuSetting", "MaxLogLines", 1000, "How many log lines the menu keeps in memory. Older lines scroll off the top of the window; AntiCheat.log on disk still has all of them."); Menu_MaxRecentPlayers = config.Bind("MenuSetting", "MaxRecentPlayers", 50, "How many past players the Recent Players window remembers. Players currently in the lobby are never dropped."); PlayerJoin = config.Bind("MsgSettings", "PlayerJoinShip", localizationManager.Msg_GetString("wlc_player"), "Chat message posted when a player joins, with {player} replaced by their name. Leave it empty to post nothing at all."); PlayerJoin_Color = config.Bind("MsgSettings", "PlayerJoinShipColor", "", "TextMeshPro color name or hex code (for example: yellow, #FF8800) for the join message. Leave it empty to keep the default chat color. Applies to whichever display PlayerJoinShipType selects."); PlayerJoin_MessageType = config.Bind("MsgSettings", "PlayerJoinShipType", MessageType.PublicChat, "Where the join message is shown. PublicChat = in chat for everybody in the lobby. HostChat = in chat for the host only. GUI = the host's own tip panel (the box that shows in-game hints), host only - the game has no way to put a tip on an unmodded client's screen, so this cannot be shown to everyone."); Disclaimer_Enable = config.Bind("DisclaimerSetting", "Enable", true, "Posts the server's house rules to chat - what this lobby has changed, and what it has not. Host-side and sent over vanilla chat, so no client needs the mod to read it."); Disclaimer_OnJoin = config.Bind("DisclaimerSetting", "PostOnJoin", true, "Posts the disclaimer automatically a few seconds after a player joins. Turn this off to leave it as an on-demand command only."); Disclaimer_Text = config.Bind("DisclaimerSetting", "Text", "- Self Made Mods|- Dying increases quota|- Randomized moons|- Enemy AI changes|- Scrap rework|- etc...|NO usage of Control nor Brutal Company.", "The disclaimer itself. One chat line per \"|\" - a config value cannot hold a real newline. TextMeshPro colour tags work: text, text, or a hex code. Leave the whole thing empty to post nothing."); Disclaimer_Aliases = config.Bind("DisclaimerSetting", "Commands", "disclaimer,desclaimer,disclamer,rules,info", "Comma-separated chat commands that post the disclaimer, without the leading slash. Anybody in the lobby can type one; a leading \"/\" or \"!\" is accepted either way. Several spellings are listed by default because a command nobody can spell is a disclaimer nobody reads."); Disclaimer_MessageType = config.Bind("DisclaimerSetting", "MessageType", MessageType.PublicChat, "Where the disclaimer is shown. PublicChat = chat for everybody in the lobby, which is the only setting the players it is written for will ever see. HostChat = the host's chat only. GUI = the host's tip panel. Menu = the mod's own log window."); Disclaimer_JoinDelaySeconds = config.Bind("DisclaimerSetting", "JoinDelaySeconds", 4f, "How long after a player joins to post it. Not zero on purpose: the joining client is still loading at the moment it connects and its chat window is not up yet to catch the lines."); Disclaimer_CooldownSeconds = config.Bind("DisclaimerSetting", "CommandCooldownSeconds", 15f, "How long the chat command is ignored for after the disclaimer has been posted, so a lobby that all asks at once gets one copy rather than four. A join always posts it regardless of this."); Shovel3 = config.Bind("ShovelSettings", "EmptyHand", false, localizationManager.Cfg_GetString("Shovel2")); GrabObject_SendLog = config.Bind("GrabObjectSetting", "SendLog", true, localizationManager.Cfg_GetString("GrabObject_SendLog")); GrabObject_MoreSlot = config.Bind("GrabObjectSetting", "MoreSlot", true, localizationManager.Cfg_GetString("GrabObject_MoreSlot")); GrabObject_TwoHand = config.Bind("GrabObjectSetting", "TwoHand", true, localizationManager.Cfg_GetString("GrabObject_TwoHand")); GrabObject_BeltBag = config.Bind("GrabObjectSetting", "BeltBag", true, localizationManager.Cfg_GetString("GrabObject_BeltBag")); GodMode_EnemyHitRequireAck = config.Bind("GodModeSetting", "EnemyHitRequireAcknowledgement", true, "Also requires the victim's client to answer each announced enemy hit - with a damage report, a critical-injury report or a death - instead of only adding the damage up until the total should have been lethal. Catches a damage-immunity mod on the first hit rather than the fourth."); GodMode_EnemyHitAckSeconds = config.Bind("GodModeSetting", "EnemyHitAcknowledgementSeconds", 2f, "How long the victim's client has to answer an announced enemy hit before the silence is treated as suppressed damage. Covers the round trip, so raise it if a high-ping lobby produces false reports."); MovementDiagnostics = config.Bind("SpeedHackSetting", "Diagnostics", true, "Prints a periodic line showing whether the movement sampling hook is running and what it sees. Purely for working out why the movement check is or is not firing - turn it off once movement detection is confirmed working."); MaxPlausibleSpeed = config.Bind("SpeedHackSetting", "MaxPlausibleSpeed", 30f, "Any position sample implying a speed above this is treated as a discontinuity - a teleport, an entrance warp, the ship moving underneath a player, a spawn - and is thrown away rather than judged. Vanilla sprint tops out around 13u/s, so anything past this is not movement at all, cheated or otherwise."); MovementGraceSeconds = config.Bind("SpeedHackSetting", "GraceSeconds", 5f, "How long movement checks stay quiet after something that moves players without them walking: a round starting, the ship landing or leaving, a discontinuity in a player's position, or a player dying or respawning."); GodMode_EnemyHitLethalThreshold = config.Bind("GodModeSetting", "EnemyHitLethalThreshold", 140, "Total accumulated enemy damage, counted host-side, at which a still-living player is flagged. Deliberately above 100: vanilla gives a one-time save where a single sub-50 hit that would kill instead leaves you on 5 HP, and healing exists, so the total has to clear real health by a margin before it can only mean immunity."); GodMode_InstakillGraceSeconds = config.Bind("GodModeSetting", "InstakillGraceSeconds", 5f, "How long to wait after an instakill enemy commits to killing a player before checking whether they actually died. Needs to comfortably outlast the kill animation and body spawn (vanilla itself gives the body 2 seconds) so a slow but legitimate death is never flagged."); SpeedHack_MaxMultiplier = config.Bind("SpeedHackSetting", "MaxMultiplier", 2.6f, "Highest sustained speed, as a multiple of a player's own recent baseline, before it is flagged. Vanilla's own sprint multiplier tops out at 2.25x, so this has headroom built in for network jitter."); SpeedHack_MinAbsoluteSpeed = config.Bind("SpeedHackSetting", "MinAbsoluteSpeed", 16f, "Speed, in units per second, below which the multiplier above is ignored entirely. A player who has been standing still has a baseline near zero, so simply starting to sprint clears any multiple - this stops that being a flag by requiring a speed no unmodded player reaches (vanilla sprint tops out around 13u/s)."); SpeedHack_MinMovingSpeed = config.Bind("SpeedHackSetting", "MinMovingSpeed", 0.75f, "Speed, in units per second, below which a sample is treated as the player standing still and is left out of the check entirely - it is neither judged nor folded into their baseline. Position reports jitter by a little even from somebody who has not moved, and letting those into the baseline dragged it to nearly zero, which made an ordinary sprint look like an enormous multiple of it."); SpeedHack_MinConsecutiveReadings = config.Bind("SpeedHackSetting", "MinConsecutiveReadings", 3, "How many consecutive position samples (one every 0.2s) must all be over the limit before a player is flagged."); PositionSampleWindowSeconds = config.Bind("SpeedHackSetting", "PositionSampleWindowSeconds", 5f, "How many seconds of position history are kept per player to compute sustained speed."); AntiGriefRules.Register(); CheckCatalog.Register(); CooldownManager.Reset(); RegisterCooldown("TerminalNoise", UseRate, UseRate_TerminalNoiseCooldown); RegisterCooldown("ShipLight", UseRate, UseRate_ShipLightCooldown); RegisterCooldown("Chat", UseRate, UseRate_ChatCooldown); AntiCheatPlugin.LogInfo(localizationManager.Log_GetString("load")); } private static void RegisterCooldown(string name, FeatureConfig feature, ConfigEntry cooldownMs) { CooldownManager.RegisterCooldownGroup(name, () => feature.Enable, () => (float)cooldownMs.Value / 1000f); } } internal sealed class Profile { public string Name; public bool BypassAllChecks; public readonly HashSet GriefBypass = new HashSet(); public readonly HashSet CheckBypass = new HashSet(); public int ExemptionCount => GriefBypass.Count + CheckBypass.Count + (BypassAllChecks ? 1 : 0); } internal static class Profiles { private const char RecordSeparator = ';'; private const char FieldSeparator = '|'; private const char ListSeparator = ','; private const char AssignSeparator = '='; private static Dictionary _assignments; private static string _assignmentsSource; private static List _profiles; private static string _profilesSource; public static List All() { string text = ((PluginConfig.Profiles_Data == null) ? string.Empty : (PluginConfig.Profiles_Data.Value ?? string.Empty)); if (_profiles != null && _profilesSource == text) { return _profiles; } List list = (_profiles = new List()); _profilesSource = text; if (string.IsNullOrEmpty(text)) { return list; } string[] array = text.Split(';'); foreach (string text2 in array) { if (text2.Trim().Length == 0) { continue; } string[] array2 = text2.Split('|'); string text3 = array2[0].Trim(); if (text3.Length != 0) { Profile profile = new Profile { Name = text3 }; if (array2.Length >= 2) { profile.BypassAllChecks = bool.TryParse(array2[1].Trim(), out var result) && result; } if (array2.Length >= 3) { FillKeys(profile.GriefBypass, array2[2]); } if (array2.Length >= 4) { FillCheckKeys(profile.CheckBypass, array2[3]); } list.Add(profile); } } return list; } public static Profile Get(string name) { if (string.IsNullOrEmpty(name)) { return null; } foreach (Profile item in All()) { if (string.Equals(item.Name, name, StringComparison.OrdinalIgnoreCase)) { return item; } } return null; } public static bool Create(string name, out string message) { string text = Sanitize(name); if (text.Length == 0) { message = "A profile needs a name."; return false; } if (Get(text) != null) { message = "There is already a profile called " + text + "."; return false; } List list = All(); list.Add(new Profile { Name = text }); Write(list); message = "Created the profile " + text + "."; AntiCheatPlugin.LogInfo("[Profiles] Created the profile " + text + "."); return true; } public static bool Delete(string name) { List list = All(); if (list.RemoveAll((Profile p) => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase)) == 0) { return false; } Write(list); Dictionary dictionary = Assignments(); List list2 = new List(); foreach (KeyValuePair item in dictionary) { if (string.Equals(item.Value, name, StringComparison.OrdinalIgnoreCase)) { list2.Add(item.Key); } } if (list2.Count > 0) { foreach (ulong item2 in list2) { dictionary.Remove(item2); } WriteAssignments(dictionary); } AntiCheatPlugin.LogInfo($"[Profiles] Deleted the profile {name} and unassigned {list2.Count} player(s)."); return true; } public static void Save(Profile profile) { if (profile == null || string.IsNullOrEmpty(profile.Name)) { return; } List list = All(); for (int i = 0; i < list.Count; i++) { if (string.Equals(list[i].Name, profile.Name, StringComparison.OrdinalIgnoreCase)) { list[i] = profile; Write(list); break; } } } public static void SetGriefBypass(Profile profile, string ruleKey, bool bypass) { if (profile != null && !string.IsNullOrEmpty(ruleKey)) { if (bypass) { profile.GriefBypass.Add(ruleKey); } else { profile.GriefBypass.Remove(ruleKey); } Save(profile); } } public static void SetCheckBypass(Profile profile, string checkKey, bool bypass) { if (profile != null && !string.IsNullOrEmpty(checkKey)) { if (bypass) { profile.CheckBypass.Add(checkKey); } else { profile.CheckBypass.Remove(checkKey); } Save(profile); } } public static void SetBypassAllChecks(Profile profile, bool bypass) { if (profile != null) { profile.BypassAllChecks = bypass; Save(profile); } } public static string AssignedName(ulong steamId) { if (steamId == 0) { return null; } string value; return Assignments().TryGetValue(steamId, out value) ? value : null; } public static void Assign(ulong steamId, string profileName) { if (steamId == 0) { return; } Dictionary dictionary = Assignments(); if (string.IsNullOrEmpty(profileName)) { if (!dictionary.Remove(steamId)) { return; } AntiCheatPlugin.LogInfo($"[Profiles] Cleared the profile on {steamId}."); } else { string text = Sanitize(profileName); if (text.Length == 0) { return; } dictionary[steamId] = text; AntiCheatPlugin.LogInfo($"[Profiles] Assigned {text} to {steamId}."); } WriteAssignments(dictionary); } public static int AssignedCount(string profileName) { int num = 0; foreach (KeyValuePair item in Assignments()) { if (string.Equals(item.Value, profileName, StringComparison.OrdinalIgnoreCase)) { num++; } } return num; } public static Profile For(ulong steamId) { string text = AssignedName(steamId); return (text == null) ? null : Get(text); } public static bool AllowsGriefRule(ulong clientId, string ruleKey) { if (string.IsNullOrEmpty(ruleKey) || Assignments().Count == 0) { return false; } ulong num = BanList.SteamIdForClient(clientId); if (num == 0) { return false; } return For(num)?.GriefBypass.Contains(ruleKey) ?? false; } public static bool BypassesAllChecks(PlayerControllerB player) { if ((Object)(object)player == (Object)null || Assignments().Count == 0) { return false; } return For(player.playerSteamId)?.BypassAllChecks ?? false; } public static bool BypassesCheck(PlayerControllerB player, FeatureConfig feature) { if ((Object)(object)player == (Object)null || player.playerSteamId == 0L || feature == null || Assignments().Count == 0) { return false; } Profile profile = For(player.playerSteamId); return profile != null && (profile.BypassAllChecks || profile.CheckBypass.Contains(feature.Key)); } private static Dictionary Assignments() { string text = ((PluginConfig.Profiles_Assignments == null) ? string.Empty : (PluginConfig.Profiles_Assignments.Value ?? string.Empty)); if (_assignments != null && _assignmentsSource == text) { return _assignments; } Dictionary dictionary = new Dictionary(); string[] array = text.Split(';'); foreach (string text2 in array) { string[] array2 = text2.Split('='); if (array2.Length >= 2 && ulong.TryParse(array2[0].Trim(), out var result) && result != 0) { string text3 = array2[1].Trim(); if (text3.Length > 0) { dictionary[result] = text3; } } } _assignments = dictionary; _assignmentsSource = text; return dictionary; } private static void FillKeys(HashSet into, string field) { string[] array = field.Split(','); foreach (string text in array) { string text2 = text.Trim(); if (text2.Length > 0) { into.Add(text2); } } } private static void FillCheckKeys(HashSet into, string field) { string[] array = field.Split(','); foreach (string text in array) { string text2 = text.Trim(); if (text2.Length > 0) { into.Add(CheckCatalog.CanonicalKey(text2)); } } } private static void Write(List profiles) { StringBuilder stringBuilder = new StringBuilder(); foreach (Profile profile in profiles) { if (stringBuilder.Length > 0) { stringBuilder.Append(';'); } stringBuilder.Append(profile.Name); stringBuilder.Append('|'); stringBuilder.Append(profile.BypassAllChecks ? "true" : "false"); stringBuilder.Append('|'); stringBuilder.Append(string.Join(','.ToString(), ToArray(profile.GriefBypass))); stringBuilder.Append('|'); stringBuilder.Append(string.Join(','.ToString(), ToArray(profile.CheckBypass))); } PluginConfig.Profiles_Data.Value = stringBuilder.ToString(); _profiles = null; Save(); } private static void WriteAssignments(Dictionary assignments) { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair assignment in assignments) { if (stringBuilder.Length > 0) { stringBuilder.Append(';'); } stringBuilder.Append(assignment.Key); stringBuilder.Append('='); stringBuilder.Append(assignment.Value); } PluginConfig.Profiles_Assignments.Value = stringBuilder.ToString(); _assignments = null; Save(); } private static string Sanitize(string name) { if (string.IsNullOrEmpty(name)) { return string.Empty; } return name.Replace(';', ' ').Replace('|', ' ').Replace(',', ' ') .Replace('=', ' ') .Trim(); } private static string[] ToArray(HashSet keys) { string[] array = new string[keys.Count]; keys.CopyTo(array); return array; } private static void Save() { try { ((ConfigEntryBase)PluginConfig.Profiles_Data).ConfigFile.Save(); } catch (Exception arg) { AntiCheatPlugin.LogInfo($"[Profiles] Failed to write the profiles to disk: {arg}"); } } } internal static class ServerDisclaimer { public const string DefaultText = "- Self Made Mods|- Dying increases quota|- Randomized moons|- Enemy AI changes|- Scrap rework|- etc...|NO usage of Control nor Brutal Company."; public const string DefaultAliases = "disclaimer,desclaimer,disclamer,rules,info"; private static float _lastPostedAt = float.NegativeInfinity; private static bool IsHost => (Object)(object)StartOfRound.Instance != (Object)null && ((NetworkBehaviour)StartOfRound.Instance).IsServer; private static IEnumerable Lines() { return from line in PluginConfig.Disclaimer_Text.Value.Split('|') select line.Trim() into line where line.Length > 0 select line; } internal static void Post() { if (!PluginConfig.Disclaimer_Enable.Value || !IsHost) { return; } foreach (string item in Lines()) { Show(item); } _lastPostedAt = Time.realtimeSinceStartup; } internal static void PostThrottled() { float num = Mathf.Max(0f, PluginConfig.Disclaimer_CooldownSeconds.Value); if (!(Time.realtimeSinceStartup - _lastPostedAt < num)) { Post(); } } internal static void PostOnJoin() { if (PluginConfig.Disclaimer_Enable.Value && PluginConfig.Disclaimer_OnJoin.Value && !((Object)(object)StartOfRound.Instance == (Object)null)) { ((MonoBehaviour)StartOfRound.Instance).StartCoroutine(PostAfterDelay()); } } private static IEnumerator PostAfterDelay() { yield return (object)new WaitForSeconds(Mathf.Max(0f, PluginConfig.Disclaimer_JoinDelaySeconds.Value)); Post(); } internal static bool IsRequest(string chatMessage) { if (string.IsNullOrEmpty(chatMessage)) { return false; } string token = chatMessage.Trim().TrimStart('/', '!').Trim() .ToLowerInvariant(); if (token.Length == 0) { return false; } return (from alias in PluginConfig.Disclaimer_Aliases.Value.Split(',') select alias.Trim().TrimStart('/', '!').ToLowerInvariant()).Any((string alias) => alias.Length > 0 && alias == token); } internal static void HandleChat(string chatMessage, string nameOfUserWhoTyped) { if (PluginConfig.Disclaimer_Enable.Value && IsHost && !string.IsNullOrEmpty(chatMessage) && !string.IsNullOrEmpty(nameOfUserWhoTyped) && IsRequest(chatMessage)) { PostThrottled(); } } private static void Show(string line) { switch (PluginConfig.Disclaimer_MessageType.Value) { case PluginConfig.MessageType.PublicChat: AntiCheatPlugin.AddTextMessageClientRpc(line); break; case PluginConfig.MessageType.HostChat: AntiCheatPlugin.ShowMessageHostOnly(line); break; case PluginConfig.MessageType.Menu: MenuLog.Info(line); break; default: AntiCheatPlugin.ShowGuiTip(line); break; } } } internal static class TargetedClientRpc { private static readonly MethodInfo BeginSend = AccessTools.Method(typeof(NetworkBehaviour), "__beginSendClientRpc", (Type[])null, (Type[])null); private static readonly MethodInfo EndSend = AccessTools.Method(typeof(NetworkBehaviour), "__endSendClientRpc", (Type[])null, (Type[])null); private static bool _loggedBinding; public static bool Available { get { LogBindingOnce(); return BeginSend != null && EndSend != null; } } private static void LogBindingOnce() { if (!_loggedBinding) { _loggedBinding = true; AntiCheatPlugin.LogInfo($"[LateJoin] Netcode send path bound: begin:{BeginSend != null} end:{EndSend != null}"); } } public static bool Send(NetworkBehaviour behaviour, uint rpcId, ulong clientId, Action writeArguments) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)behaviour == (Object)null || !Available) { return false; } try { ClientRpcParams val = new ClientRpcParams { Send = new ClientRpcSendParams { TargetClientIds = new ulong[1] { clientId } } }; object[] parameters = new object[3] { rpcId, val, (object)(RpcDelivery)0 }; FastBufferWriter val2 = (FastBufferWriter)BeginSend.Invoke(behaviour, parameters); writeArguments?.Invoke(val2); object[] parameters2 = new object[4] { val2, rpcId, val, (object)(RpcDelivery)0 }; EndSend.Invoke(behaviour, parameters2); return true; } catch (Exception arg) { AntiCheatPlugin.LogInfo($"[LateJoin] Could not send rpc {rpcId} to client {clientId}: {arg}"); return false; } } public static uint ResolveRpcId(uint expected, string rpcName) { try { if (RegisteredIds().Contains(expected)) { return expected; } foreach (KeyValuePair item in RegisteredNames()) { if (string.Equals(item.Value, rpcName, StringComparison.Ordinal)) { AntiCheatPlugin.LogInfo($"[LateJoin] {rpcName} moved from id {expected} to {item.Key} on this build."); return item.Key; } } AntiCheatPlugin.LogInfo($"[LateJoin] {rpcName} (id {expected}) is not registered on this build - mid-round level sync will not work."); } catch (Exception arg) { AntiCheatPlugin.LogInfo($"[LateJoin] Could not check the rpc table: {arg}"); } return expected; } private static HashSet RegisteredIds() { HashSet hashSet = new HashSet(); foreach (object item2 in StaticDictionaries()) { if (!(item2 is IDictionary dictionary)) { continue; } foreach (object key in dictionary.Keys) { if (key is uint item) { hashSet.Add(item); } } } return hashSet; } private static IEnumerable> RegisteredNames() { List> list = new List>(); foreach (object item in StaticDictionaries()) { if (!(item is IDictionary dictionary)) { continue; } foreach (DictionaryEntry item2 in dictionary) { if (item2.Key is uint key && item2.Value is string value) { list.Add(new KeyValuePair(key, value)); } } } return list; } private static IEnumerable StaticDictionaries() { List list = new List(); FieldInfo[] fields = typeof(NetworkManager).GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); for (int i = 0; i < fields.Length; i++) { if (fields[i].Name.StartsWith("__rpc", StringComparison.Ordinal)) { object value = fields[i].GetValue(null); if (value != null) { list.Add(value); } } } return list; } } } namespace AntiCheat.Utils { public static class CooldownManager { private struct CooldownData { public Func IsEnabled; public Func GetCooldown; public List CooldownList; } private static readonly Dictionary cooldownGroups = new Dictionary(); public static void Reset() { cooldownGroups.Clear(); } public static void RegisterCooldownGroup(string groupName, Func isEnabled, Func getCooldown) { cooldownGroups.Add(groupName, new CooldownData { IsEnabled = isEnabled, GetCooldown = getCooldown, CooldownList = new List() }); } public static IEnumerator HandleCooldown(string groupName, ulong playerId) { CooldownData data = cooldownGroups[groupName]; data.CooldownList.Add(playerId); yield return (object)new WaitForSeconds(data.GetCooldown()); data.CooldownList.Remove(playerId); } public static bool CheckCooldown(string groupName, PlayerControllerB player) { CooldownData cooldownData = cooldownGroups[groupName]; if (!cooldownData.IsEnabled() || cooldownData.GetCooldown() <= 0f) { return true; } if (cooldownData.CooldownList.Contains(PatchHelper.IdOf(player))) { return false; } ((MonoBehaviour)player).StartCoroutine(HandleCooldown(groupName, PatchHelper.IdOf(player))); return true; } } internal static class LogFile { private const string FileName = "AntiCheat.log"; private const int MaxPending = 8192; private const int MaxConsecutiveFailures = 5; private static readonly ConcurrentQueue Pending = new ConcurrentQueue(); private static readonly AutoResetEvent HasWork = new AutoResetEvent(initialState: false); private static readonly object StartGate = new object(); private static Thread _writer; private static string _path; private static StreamWriter _stream; private static int _pendingCount; private static int _droppedCount; private static int _failures; private static volatile bool _disabled; private static volatile bool _stopping; private static volatile bool _truncate; internal static void Reset() { _truncate = true; if (_writer != null) { HasWork.Set(); } } internal static void Write(string line) { if (!_disabled && !string.IsNullOrEmpty(line)) { if (Volatile.Read(in _pendingCount) >= 8192) { Interlocked.Increment(ref _droppedCount); return; } EnsureWriterStarted(); Interlocked.Increment(ref _pendingCount); Pending.Enqueue(line); HasWork.Set(); } } private static void EnsureWriterStarted() { if (_writer != null) { return; } lock (StartGate) { if (_writer == null) { _path = ResolvePath(); _writer = new Thread(WriteLoop) { IsBackground = true, Name = "AntiCheat log writer" }; _writer.Start(); AppDomain.CurrentDomain.ProcessExit += delegate { Stop(); }; } } } private static string ResolvePath() { try { string directoryName = Path.GetDirectoryName(typeof(LogFile).Assembly.Location); if (!string.IsNullOrEmpty(directoryName)) { return Path.Combine(directoryName, "AntiCheat.log"); } } catch (Exception) { } return Path.GetFullPath("AntiCheat.log"); } private static void WriteLoop() { while (!_stopping) { HasWork.WaitOne(250); Drain(); } Drain(); CloseStream(); } private static void Drain() { if (_disabled || (!_truncate && Volatile.Read(in _pendingCount) == 0)) { return; } try { if (_truncate) { _truncate = false; Truncate(); } if (Volatile.Read(in _pendingCount) != 0 && EnsureStreamOpen()) { string result; while (Pending.TryDequeue(out result)) { Interlocked.Decrement(ref _pendingCount); _stream.WriteLine(result); } int num = Interlocked.Exchange(ref _droppedCount, 0); if (num > 0) { _stream.WriteLine($"[{DateTime.Now:MM-dd HH:mm:ss:ff}] [log] {num} lines dropped - the log was being written faster than the disk would take it."); } _stream.Flush(); _failures = 0; } } catch (Exception ex) { CloseStream(); RecordFailure(ex); } } private static bool EnsureStreamOpen() { if (_stream != null) { return true; } FileStream stream = new FileStream(_path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete); _stream = new StreamWriter(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); return true; } private static void Truncate() { CloseStream(); using (new FileStream(_path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete)) { } } private static void CloseStream() { if (_stream != null) { try { _stream.Dispose(); } catch (Exception) { } _stream = null; } } private static void RecordFailure(Exception ex) { _failures++; if (_failures >= 5) { _disabled = true; try { AntiCheatPlugin.Log.LogWarning((object)$"[log] Giving up on {_path} for this session after {_failures} failed writes. In-game logging is unaffected: {ex.Message}"); } catch (Exception) { } string result; while (Pending.TryDequeue(out result)) { Interlocked.Decrement(ref _pendingCount); } } } internal static void Stop() { if (_writer != null) { _stopping = true; HasWork.Set(); _writer.Join(TimeSpan.FromSeconds(2.0)); } } } } namespace AntiCheat.UI { internal sealed class AntiCheatMenu { private struct SlotLine { public string Label; public string State; public GUIStyle StateStyle; public string Detail; public GUIStyle DetailStyle; public float Height; } private struct PlayerRow { public string Header; public PlayerRecord Record; } private OverlayPanel _panel; private OverlayWindow _players; private OverlayWindow _logs; private OverlayWindow _bans; private OverlayWindow _lobby; private OverlayWindow _grief; private OverlayWindow _checks; private OverlayWindow _profiles; private string _search = string.Empty; private bool _alertsOnly; private bool _autoScroll = true; private MenuLogEntry[] _filtered = new MenuLogEntry[0]; private int _filterVersion = -1; private string _filterSearch; private bool _filterAlertsOnly; private int _filterGeneration = -1; private float[] _logRows = new float[0]; private float _logContentHeight; private float _logLayoutWidth = -1f; private int _logLayoutFilterVersion = -1; private int _logLayoutGeneration = -1; private GUIStyle _logStyle; private GUIStyle _alertLogStyle; private bool _scrollToEnd = true; private readonly OverlayColumns _playerColumns = new OverlayColumns(new string[4] { "PLAYER", "STEAM ID", "STATUS", "LAST SEEN" }); private readonly OverlayColumns _banColumns = new OverlayColumns(new string[4] { "NAME", "STEAM ID", "BANNED BY", "WHEN" }); private int _playerRows = -1; private int _banRows = -1; private HashSet _bannedIds = new HashSet(); private float _nextBanRefresh; private ulong _banArmedFor; private float _banArmedUntil; private float _nextNameResolve; private string _selectedProfile; private string _newProfileName = string.Empty; private float _deleteArmedUntil; private string _deleteArmedFor; private ulong _pickerFor; private Rect _pickerAnchor; private List _profileCache; private int _profileCacheFrame = -1; private const float SettingGap = 8f; private readonly Dictionary _numberFields = new Dictionary(); private static float BanColumnWidth => Mathf.Max(56f, OverlayStyles.ButtonWidth("SURE?", (GUIStyle)null)); private static float ProfileColumnWidth => Mathf.Max(84f, OverlayStyles.ButtonWidth("PROFILE", (GUIStyle)null)); private static float UnbanColumnWidth => Mathf.Max(66f, OverlayStyles.ButtonWidth("UNBAN", (GUIStyle)null)); private static float RowHeight => OverlayStyles.RowHeight; private static float LogRowHeight => Mathf.Max(14f, OverlayStyles.RowHeight - 1f); private static float ChoiceHeight => OverlayStyles.RowHeight; internal static AntiCheatMenu Instance { get; private set; } private static float SlotLabelWidth => OverlayStyles.Measure("SLOT 0", OverlayStyles.Label) + OverlayStyles.CellPadding * 2f; private static float SlotStateWidth => OverlayStyles.Measure("UNAVAILABLE", OverlayStyles.LabelAlert) + OverlayStyles.CellPadding * 2f; private static float PickerRowHeight => OverlayStyles.RowHeight; private static float PunishmentWidth => ChoicesWidth("OFF", "LOG ONLY", "KICK", "BAN"); internal static void Register() { if (Instance == null) { if (PluginConfig.Menu_Enable != null && !PluginConfig.Menu_Enable.Value) { AntiCheatPlugin.Log.LogInfo((object)"[Menu] MenuSetting/Enable is off - no panel registered."); return; } Instance = new AntiCheatMenu(); Instance.Build(); RegistrySweeper.Attach(); } } private void Build() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) _panel = Dock.Register("ANTI CHEAT"); _panel.Order = 10; _players = Add("RECENT PLAYERS", DrawPlayersWindow, "players.png", new Rect(60f, 90f, 640f, 320f)); _players.Subtitle = delegate { List list = PlayerRegistry.Recent(); int num = 0; for (int i = 0; i < list.Count; i++) { if (list[i].Online) { num++; } } return $"{num} in lobby / {list.Count} seen"; }; _logs = Add("LOGS", DrawLogsWindow, "logs.png", new Rect(60f, 430f, 860f, 380f)); _logs.Subtitle = () => $"{_filtered.Length} / {MenuLog.Count} lines"; _bans = Add("BAN LIST", DrawBansWindow, "bans.png", new Rect(700f, 90f, 620f, 320f)); _bans.Subtitle = () => $"{BanList.Records().Count} banned"; _lobby = Add("LOBBY", DrawLobbyWindow, "lobby.png", new Rect(360f, 150f, 560f, 440f)); _lobby.Subtitle = LobbyControl.Describe; _grief = Add("ANTI-GRIEF", DrawGriefWindow, "antigrief.png", new Rect(120f, 120f, 620f, 480f)); _grief.Subtitle = () => $"{AntiGriefRules.All.Count} rules"; _checks = Add("CHEAT CHECKS", DrawChecksWindow, "checks.png", new Rect(760f, 120f, 560f, 480f)); _checks.Subtitle = () => $"{CheckCatalog.Cheats.Count} checks"; _profiles = Add("PROFILES", DrawProfilesWindow, "profiles.png", new Rect(300f, 140f, 660f, 500f)); _profiles.Subtitle = () => $"{CachedProfiles().Count} profiles"; AntiCheatPlugin.Log.LogInfo((object)$"[Menu] Registered {_panel.Windows.Count} windows with Overlay (open it with {Dock.ToggleKeyName()})."); } private OverlayWindow Add(string title, Action draw, string iconFile, Rect defaultRect) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) return _panel.AddWindow(title, draw, iconFile, defaultRect); } private static void Toast(string message) { Dock.Toast(message); } private static bool ChoiceButton(Rect rect, string label, bool active) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return OverlayStyles.Choice(rect, label, active); } private static float Wider(string one, string other) { return Mathf.Max(OverlayStyles.ButtonWidth(one, (GUIStyle)null), OverlayStyles.ButtonWidth(other, (GUIStyle)null)); } private void DrawLobbyWindow(Rect content) { //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_02af: 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_034b: Unknown result type (might be due to invalid IL or missing references) //IL_0378: Unknown result type (might be due to invalid IL or missing references) //IL_0460: Unknown result type (might be due to invalid IL or missing references) //IL_03ce: Unknown result type (might be due to invalid IL or missing references) //IL_040d: Unknown result type (might be due to invalid IL or missing references) //IL_04bf: Unknown result type (might be due to invalid IL or missing references) //IL_04ec: Unknown result type (might be due to invalid IL or missing references) //IL_0523: Unknown result type (might be due to invalid IL or missing references) //IL_0562: Unknown result type (might be due to invalid IL or missing references) //IL_05b6: Unknown result type (might be due to invalid IL or missing references) //IL_05ee: Unknown result type (might be due to invalid IL or missing references) string reason; bool flag = LobbyControl.CanEditVisibility(out reason); string reason2; bool flag2 = LobbyControl.CanEditJoining(out reason2); string reason3; bool flag3 = LobbyControl.CanReopen(out reason3); bool enabled = GUI.enabled; float num = Mathf.Max(22f, OverlayStyles.RowHeight); _lobby.FitContentWidth(Mathf.Max(OverlayStyles.ButtonWidth("PUBLIC", (GUIStyle)null) + OverlayStyles.ButtonWidth("FRIENDS", (GUIStyle)null) + OverlayStyles.ButtonWidth("INVITE", (GUIStyle)null) + OverlayStyles.ButtonWidth("HIDDEN", (GUIStyle)null) + 18f, SlotLabelWidth + SlotStateWidth + 160f + 14f)); string text = "PUBLIC is the server browser. FRIENDS and INVITE are Steam only. HIDDEN is listed nowhere."; string text2 = (LobbyControl.HasLobby ? "Whether anybody may connect at all, whoever can see the lobby." : "The game left the lobby behind at take-off. This makes a new one, open, under the same name."); string text3 = "ALLOWED keeps the lobby up after take-off and drops joiners into the round already running."; bool flag4 = flag && (flag2 || flag3); string text4 = ((!flag) ? reason : ((flag2 || flag3) ? "Nobody else needs the mod - this is all host-side." : reason2)); GUIStyle val = (flag4 ? OverlayStyles.LabelMuted : OverlayStyles.LabelAlert); float num2 = Mathf.Max(40f, ((Rect)(ref content)).width - 14f); float height; List lines = SlotLines(num2, out height); float num3 = OverlayStyles.TextHeight(text, num2, OverlayStyles.LabelMuted); float num4 = OverlayStyles.TextHeight(text2, num2, OverlayStyles.LabelMuted); float num5 = OverlayStyles.TextHeight(text3, num2, OverlayStyles.LabelMuted); float num6 = OverlayStyles.TextHeight(text4, num2, val); float num7 = RowHeight + 2f + num + 3f; float num8 = num7 + num3 + 8f + num7 + num4 + 8f + num7 + num5 + 8f + height + num6; _lobby.FitContentHeight(num8); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 0f, num2, num8); _lobby.Scroll = GUI.BeginScrollView(content, _lobby.Scroll, val2); float num9 = 0f; GUI.Label(new Rect(0f, num9, num2, RowHeight), "VISIBILITY", OverlayStyles.ColumnHeader); num9 += RowHeight + 2f; GUI.enabled = flag; float num10 = (num2 - 18f) / 4f; if (ChoiceButton(new Rect(0f, num9, num10, num), "PUBLIC", LobbyControl.Visibility == LobbyVisibility.Public) && LobbyControl.SetVisibility(LobbyVisibility.Public, out var message)) { Toast(message); } if (ChoiceButton(new Rect(num10 + 6f, num9, num10, num), "FRIENDS", LobbyControl.Visibility == LobbyVisibility.FriendsOnly) && LobbyControl.SetVisibility(LobbyVisibility.FriendsOnly, out message)) { Toast(message); } if (ChoiceButton(new Rect((num10 + 6f) * 2f, num9, num10, num), "INVITE", LobbyControl.Visibility == LobbyVisibility.Private) && LobbyControl.SetVisibility(LobbyVisibility.Private, out message)) { Toast(message); } if (ChoiceButton(new Rect((num10 + 6f) * 3f, num9, num10, num), "HIDDEN", LobbyControl.Visibility == LobbyVisibility.Invisible) && LobbyControl.SetVisibility(LobbyVisibility.Invisible, out message)) { Toast(message); } GUI.enabled = enabled; num9 += num + 3f; OverlayStyles.Paragraph(new Rect(0f, num9, num2, num3), text, OverlayStyles.LabelMuted); num9 += num3 + 8f; GUI.Label(new Rect(0f, num9, num2, RowHeight), "JOINING", OverlayStyles.ColumnHeader); num9 += RowHeight + 2f; float num11 = (num2 - 6f) / 2f; if (LobbyControl.HasLobby) { GUI.enabled = flag2; if (ChoiceButton(new Rect(0f, num9, num11, num), "OPEN", LobbyControl.IsOpen) && LobbyControl.SetOpen(open: true, out message)) { Toast(message); } if (ChoiceButton(new Rect(num11 + 6f, num9, num11, num), "CLOSED", !LobbyControl.IsOpen) && LobbyControl.SetOpen(open: false, out message)) { Toast(message); } GUI.enabled = enabled; } else { GUI.enabled = flag3; if (OverlayStyles.Press(new Rect(0f, num9, num2, num), LobbyControl.Reopening ? "OPENING..." : "REOPEN LOBBY", OverlayStyles.ButtonActive) && LobbyControl.Reopen(out message)) { Toast(message); } GUI.enabled = enabled; } num9 += num + 3f; OverlayStyles.Paragraph(new Rect(0f, num9, num2, num4), text2, OverlayStyles.LabelMuted); num9 += num4 + 8f; GUI.Label(new Rect(0f, num9, num2, RowHeight), "JOIN MID-ROUND", OverlayStyles.ColumnHeader); num9 += RowHeight + 2f; GUI.enabled = flag; if (ChoiceButton(new Rect(0f, num9, num11, num), "ALLOWED", LobbyControl.LateJoin) && LobbyControl.SetLateJoin(on: true, out message)) { Toast(message); } if (ChoiceButton(new Rect(num11 + 6f, num9, num11, num), "ORBIT ONLY", !LobbyControl.LateJoin) && LobbyControl.SetLateJoin(on: false, out message)) { Toast(message); } GUI.enabled = enabled; num9 += num + 3f; OverlayStyles.Paragraph(new Rect(0f, num9, num2, num5), text3, OverlayStyles.LabelMuted); num9 += num5 + 8f; num9 = DrawSlots(num2, num9, lines); OverlayStyles.Paragraph(new Rect(0f, num9, num2, num6), text4, val); GUI.EndScrollView(); } private static List SlotLines(float width, out float height) { List list = PlayerSlots.Describe(); List list2 = new List(list.Count); float num = Mathf.Max(40f, width - SlotLabelWidth - SlotStateWidth - OverlayStyles.CellPadding * 2f); int num2 = 0; for (int i = 0; i < list.Count; i++) { SlotInfo slotInfo = list[i]; SlotLine item = new SlotLine { Label = $"SLOT {slotInfo.Index}" }; switch (slotInfo.State) { case SlotState.Host: item.State = "HOST"; item.StateStyle = OverlayStyles.LabelMuted; break; case SlotState.Occupied: item.State = "IN USE"; item.StateStyle = OverlayStyles.Label; break; case SlotState.Unavailable: item.State = "UNAVAILABLE"; item.StateStyle = OverlayStyles.LabelAlert; num2++; break; default: item.State = "FREE"; item.StateStyle = OverlayStyles.LabelMuted; break; } if (slotInfo.State == SlotState.Unavailable) { item.Detail = (string.IsNullOrEmpty(slotInfo.Name) ? "model still hidden - a joiner put here is invisible" : (slotInfo.Name + " died here and left - a joiner put here is invisible")); } else if (slotInfo.State == SlotState.Free) { item.Detail = (string.IsNullOrEmpty(slotInfo.Name) ? "-" : ("last: " + slotInfo.Name)); } else { item.Detail = (string.IsNullOrEmpty(slotInfo.Name) ? "-" : slotInfo.Name); } item.DetailStyle = ((slotInfo.State == SlotState.Unavailable) ? OverlayStyles.LabelAlert : OverlayStyles.LabelMuted); item.Height = Mathf.Max(RowHeight, OverlayStyles.TextHeight(item.Detail, num, item.DetailStyle) + 2f); list2.Add(item); } string text = ((num2 == 0) ? "All four slots are clean. Joiners are given the lowest free one." : "An UNAVAILABLE slot is skipped when a joiner is placed. It clears when whoever holds it next dies and is revived at the end of a round."); GUIStyle val = ((num2 == 0) ? OverlayStyles.LabelMuted : OverlayStyles.LabelAlert); height = RowHeight + 2f; for (int j = 0; j < list2.Count; j++) { height += list2[j].Height; } height += 3f + OverlayStyles.TextHeight(text, width, val) + 6f; list2.Add(new SlotLine { Label = null, Detail = text, DetailStyle = val, Height = OverlayStyles.TextHeight(text, width, val) }); return list2; } private float DrawSlots(float width, float y, List lines) { //IL_000d: 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_0177: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(0f, y, width, RowHeight), "PLAYER SLOTS", OverlayStyles.ColumnHeader); y += RowHeight + 2f; float slotLabelWidth = SlotLabelWidth; float slotStateWidth = SlotStateWidth; float cellPadding = OverlayStyles.CellPadding; Rect val = default(Rect); for (int i = 0; i < lines.Count - 1; i++) { SlotLine slotLine = lines[i]; ((Rect)(ref val))..ctor(0f, y, width, slotLine.Height); GUI.Label(val, GUIContent.none, (i % 2 == 0) ? OverlayStyles.Row : OverlayStyles.RowAlt); GUI.Label(new Rect(((Rect)(ref val)).x + cellPadding, ((Rect)(ref val)).y, slotLabelWidth, RowHeight), slotLine.Label, OverlayStyles.Label); GUI.Label(new Rect(((Rect)(ref val)).x + slotLabelWidth, ((Rect)(ref val)).y, slotStateWidth, RowHeight), slotLine.State, slotLine.StateStyle); OverlayStyles.Paragraph(new Rect(((Rect)(ref val)).x + slotLabelWidth + slotStateWidth, ((Rect)(ref val)).y + 1f, ((Rect)(ref val)).width - slotLabelWidth - slotStateWidth - cellPadding, slotLine.Height), slotLine.Detail, slotLine.DetailStyle); y += slotLine.Height; } y += 3f; SlotLine slotLine2 = lines[lines.Count - 1]; OverlayStyles.Paragraph(new Rect(0f, y, width, slotLine2.Height), slotLine2.Detail, slotLine2.DetailStyle); return y + slotLine2.Height + 6f; } private static List PlayerRows(List records, out int online) { List list = new List(records.Count + 2); online = 0; for (int i = 0; i < records.Count; i++) { if (records[i].Online) { online++; } } list.Add(new PlayerRow { Header = ((online == 1) ? "IN LOBBY (1)" : $"IN LOBBY ({online})") }); if (online == 0) { list.Add(new PlayerRow { Header = null, Record = null }); } else { for (int j = 0; j < records.Count; j++) { if (records[j].Online) { list.Add(new PlayerRow { Record = records[j] }); } } } int num = records.Count - online; list.Add(new PlayerRow { Header = ((num == 1) ? "RECENT (1)" : $"RECENT ({num})") }); if (num == 0) { list.Add(new PlayerRow { Header = null, Record = null }); } else { for (int k = 0; k < records.Count; k++) { if (!records[k].Online) { list.Add(new PlayerRow { Record = records[k] }); } } } return list; } private void DrawPlayersWindow(Rect content) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_052d: Unknown result type (might be due to invalid IL or missing references) //IL_0554: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0282: 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_0344: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: Unknown result type (might be due to invalid IL or missing references) //IL_03d4: Unknown result type (might be due to invalid IL or missing references) //IL_0478: Unknown result type (might be due to invalid IL or missing references) //IL_0490: Unknown result type (might be due to invalid IL or missing references) //IL_04b0: Unknown result type (might be due to invalid IL or missing references) //IL_04e9: Unknown result type (might be due to invalid IL or missing references) List list = PlayerRegistry.Recent(); RefreshBanCache(force: false); if (list.Count != _playerRows) { _playerRows = list.Count; _playerColumns.Reset(); } float num = BanColumnWidth + ProfileColumnWidth; _playerColumns.Fit(_players, num + 14f); float num2 = ((Rect)(ref content)).width - 14f - num; Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref content)).x, ((Rect)(ref content)).y, num2, RowHeight); _playerColumns.DrawHeaders(val); GUI.Label(new Rect(((Rect)(ref val)).xMax + 2f, ((Rect)(ref val)).y, ProfileColumnWidth, RowHeight), "PROFILE", OverlayStyles.ColumnHeader); OverlayStyles.Fill(new Rect(((Rect)(ref content)).x, ((Rect)(ref content)).y + RowHeight, ((Rect)(ref content)).width, 1f), new Color(1f, 1f, 1f, 0.12f)); string text = "Click a row to copy its Steam ID. PROFILE assigns a bypass profile. BAN twice to confirm - it survives a restart."; float num3 = OverlayStyles.TextHeight(text, ((Rect)(ref content)).width - 20f, OverlayStyles.LabelMuted); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref content)).x, ((Rect)(ref content)).y + RowHeight + 5f, ((Rect)(ref content)).width, Mathf.Max(RowHeight, ((Rect)(ref content)).height - RowHeight - 5f - num3)); int online; List list2 = PlayerRows(list, out online); Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(0f, 0f, ((Rect)(ref val2)).width - 14f, (float)list2.Count * RowHeight); _players.Scroll = GUI.BeginScrollView(val2, _players.Scroll, val3); bool enabled = GUI.enabled; GUI.enabled = enabled && _pickerFor == 0; Rect val4 = default(Rect); Rect rect = default(Rect); Rect rect2 = default(Rect); Rect val7 = default(Rect); for (int i = 0; i < list2.Count; i++) { PlayerRecord record = list2[i].Record; ((Rect)(ref val4))..ctor(0f, (float)i * RowHeight, ((Rect)(ref val3)).width, RowHeight); if (((Rect)(ref val4)).yMax < _players.Scroll.y || ((Rect)(ref val4)).y > _players.Scroll.y + ((Rect)(ref val2)).height) { continue; } if (record == null) { GUI.Label(OverlayStyles.Pad(val4), list2[i].Header ?? " nobody", (list2[i].Header == null) ? OverlayStyles.LabelMuted : OverlayStyles.ColumnHeader); continue; } bool flag = ((Rect)(ref val4)).Contains(Event.current.mousePosition); GUIStyle val5 = (flag ? OverlayStyles.RowHover : ((i % 2 == 0) ? OverlayStyles.Row : OverlayStyles.RowAlt)); GUI.Label(val4, GUIContent.none, val5); ((Rect)(ref rect))..ctor(((Rect)(ref val4)).xMax - BanColumnWidth + 2f, ((Rect)(ref val4)).y + 1f, BanColumnWidth - 6f, RowHeight - 2f); DrawBanButton(rect, record); ((Rect)(ref rect2))..ctor(((Rect)(ref val4)).xMax - BanColumnWidth - ProfileColumnWidth + 2f, ((Rect)(ref val4)).y + 1f, ProfileColumnWidth - 6f, RowHeight - 2f); DrawProfileButton(rect2, record, val2); if (flag && GUI.enabled && !((Rect)(ref rect)).Contains(Event.current.mousePosition) && !((Rect)(ref rect2)).Contains(Event.current.mousePosition) && (int)Event.current.type == 0 && Event.current.button == 0) { GUIUtility.systemCopyBuffer = record.SteamId.ToString(); Toast($"Copied {record.SteamId}"); Event.current.Use(); } GUIStyle val6 = (record.Online ? OverlayStyles.Label : OverlayStyles.LabelMuted); ((Rect)(ref val7))..ctor(((Rect)(ref val4)).x, ((Rect)(ref val4)).y, ((Rect)(ref val4)).width - BanColumnWidth - ProfileColumnWidth, ((Rect)(ref val4)).height); _playerColumns.DrawCell(val7, 0, record.DisplayName, val6); _playerColumns.DrawCell(val7, 1, record.SteamId.ToString(), OverlayStyles.Label); _playerColumns.DrawCell(val7, 2, record.Online ? "IN LOBBY" : "LEFT", record.Online ? OverlayStyles.LabelAlert : OverlayStyles.LabelMuted); _playerColumns.DrawCell(val7, 3, Ago(record.LastSeen), OverlayStyles.LabelMuted); } GUI.enabled = enabled; GUI.EndScrollView(); DrawProfilePicker(content); OverlayStyles.Paragraph(new Rect(((Rect)(ref content)).x, ((Rect)(ref content)).yMax - num3, ((Rect)(ref content)).width - 20f, num3), text, OverlayStyles.LabelMuted); } private void DrawBanButton(Rect rect, PlayerRecord record) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if (_bannedIds.Contains(record.SteamId)) { GUI.Label(rect, "BANNED", OverlayStyles.LabelMuted); return; } bool flag = _banArmedFor == record.SteamId && Time.unscaledTime < _banArmedUntil; if (OverlayStyles.Press(rect, flag ? "SURE?" : "BAN", flag ? OverlayStyles.ButtonActive : OverlayStyles.DangerButton)) { if (!flag) { _banArmedFor = record.SteamId; _banArmedUntil = Time.unscaledTime + 3f; Toast("Click again to ban " + record.DisplayName); } else { _banArmedFor = 0uL; Ban(record); } } } private void Ban(PlayerRecord record) { try { PlayerControllerB val = FindPlayer(record.SteamId); if ((Object)(object)val != (Object)null && val.playerClientId != 0) { PatchHelper.BanPlayer(val, PluginConfig.BanReason.Value, BanSource.Host); Toast("Banned " + record.DisplayName + " and disconnected them"); } else { BanList.Add(record.SteamId, BanSource.Host, record.DisplayName); Toast("Banned " + record.DisplayName); } MenuLog.Alert($"[Menu] {record.DisplayName}({record.SteamId}) banned by the host from the menu."); RefreshBanCache(force: true); } catch (Exception arg) { AntiCheatPlugin.Log.LogWarning((object)$"[Menu] Ban failed: {arg}"); Toast("Ban failed - see the console"); } } private static PlayerControllerB FindPlayer(ulong steamId) { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null) { return null; } PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val != (Object)null && val.isPlayerControlled && val.playerSteamId == steamId) { return val; } } return null; } private void RefreshBanCache(bool force) { if (force || !(Time.unscaledTime < _nextBanRefresh)) { _nextBanRefresh = Time.unscaledTime + 2f; _bannedIds = BanList.Parse(); } } private void DrawBansWindow(Rect content) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_043a: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_0390: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Unknown result type (might be due to invalid IL or missing references) //IL_03db: Unknown result type (might be due to invalid IL or missing references) List list = BanList.Records(); ResolveBanNames(list); if (list.Count != _banRows) { _banRows = list.Count; _banColumns.Reset(); } _banColumns.Fit(_bans, UnbanColumnWidth + 14f); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref content)).x, ((Rect)(ref content)).y, ((Rect)(ref content)).width - 14f - UnbanColumnWidth, RowHeight); _banColumns.DrawHeaders(val); OverlayStyles.Fill(new Rect(((Rect)(ref content)).x, ((Rect)(ref content)).y + RowHeight, ((Rect)(ref content)).width, 1f), new Color(1f, 1f, 1f, 0.12f)); string text = "UNBAN lifts the ban immediately and lets them rejoin this lobby."; float num = OverlayStyles.TextHeight(text, ((Rect)(ref content)).width - 20f, OverlayStyles.LabelMuted); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref content)).x, ((Rect)(ref content)).y + RowHeight + 5f, ((Rect)(ref content)).width, Mathf.Max(RowHeight, ((Rect)(ref content)).height - RowHeight - 5f - num)); Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(0f, 0f, ((Rect)(ref val2)).width - 14f, (float)list.Count * RowHeight); _bans.Scroll = GUI.BeginScrollView(val2, _bans.Scroll, val3); ulong num2 = 0uL; Rect val4 = default(Rect); Rect val5 = default(Rect); Rect val6 = default(Rect); for (int i = 0; i < list.Count; i++) { BanRecord banRecord = list[i]; ((Rect)(ref val4))..ctor(0f, (float)i * RowHeight, ((Rect)(ref val3)).width, RowHeight); if (!(((Rect)(ref val4)).yMax < _bans.Scroll.y) && !(((Rect)(ref val4)).y > _bans.Scroll.y + ((Rect)(ref val2)).height)) { bool flag = ((Rect)(ref val4)).Contains(Event.current.mousePosition); GUI.Label(val4, GUIContent.none, flag ? OverlayStyles.RowHover : ((i % 2 == 0) ? OverlayStyles.Row : OverlayStyles.RowAlt)); ((Rect)(ref val5))..ctor(((Rect)(ref val4)).xMax - UnbanColumnWidth + 2f, ((Rect)(ref val4)).y + 1f, UnbanColumnWidth - 6f, RowHeight - 2f); if (OverlayStyles.Press(val5, "UNBAN", OverlayStyles.Button)) { num2 = banRecord.SteamId; } if (flag && !((Rect)(ref val5)).Contains(Event.current.mousePosition) && (int)Event.current.type == 0 && Event.current.button == 0) { GUIUtility.systemCopyBuffer = banRecord.SteamId.ToString(); Toast($"Copied {banRecord.SteamId}"); Event.current.Use(); } ((Rect)(ref val6))..ctor(((Rect)(ref val4)).x, ((Rect)(ref val4)).y, ((Rect)(ref val4)).width - UnbanColumnWidth, ((Rect)(ref val4)).height); _banColumns.DrawCell(val6, 0, string.IsNullOrEmpty(banRecord.Name) ? "-" : banRecord.Name, string.IsNullOrEmpty(banRecord.Name) ? OverlayStyles.LabelMuted : OverlayStyles.Label); _banColumns.DrawCell(val6, 1, banRecord.SteamId.ToString(), OverlayStyles.Label); _banColumns.DrawCell(val6, 2, banRecord.SourceText, (banRecord.Source == BanSource.Unknown) ? OverlayStyles.LabelMuted : OverlayStyles.LabelAlert); _banColumns.DrawCell(val6, 3, banRecord.HasWhen ? banRecord.When.ToString("yyyy-MM-dd HH:mm") : "-", OverlayStyles.LabelMuted); } } GUI.EndScrollView(); if (list.Count == 0) { GUI.Label(val2, " Nobody is banned.", OverlayStyles.LabelMuted); } OverlayStyles.Paragraph(new Rect(((Rect)(ref content)).x, ((Rect)(ref content)).yMax - num, ((Rect)(ref content)).width - 20f, num), text, OverlayStyles.LabelMuted); if (num2 != 0) { Unban(num2); } } private void Unban(ulong steamId) { string text = PlayerRegistry.NameFor(steamId) ?? steamId.ToString(); if (BanList.Remove(steamId)) { MenuLog.Alert($"[Menu] {text}({steamId}) unbanned by the host from the menu."); Toast("Unbanned " + text); } RefreshBanCache(force: true); } private void ResolveBanNames(List records) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if (Time.unscaledTime < _nextNameResolve) { return; } _nextNameResolve = Time.unscaledTime + 3f; Dictionary dictionary = null; Friend val = default(Friend); for (int i = 0; i < records.Count; i++) { BanRecord banRecord = records[i]; if (!string.IsNullOrEmpty(banRecord.Name)) { continue; } string text = PlayerRegistry.NameFor(banRecord.SteamId); if (string.IsNullOrEmpty(text)) { try { ((Friend)(ref val))..ctor(SteamId.op_Implicit(banRecord.SteamId)); text = ((Friend)(ref val)).Name; if (string.IsNullOrEmpty(text) || text == "[unknown]") { text = null; SteamFriends.RequestUserInformation(SteamId.op_Implicit(banRecord.SteamId), true); } } catch (Exception) { text = null; } } if (!string.IsNullOrEmpty(text)) { if (dictionary == null) { dictionary = new Dictionary(); } dictionary[banRecord.SteamId] = text; banRecord.Name = text; } } if (dictionary != null) { BanList.CacheNames(dictionary); } } private static string Ago(DateTime when) { TimeSpan timeSpan = DateTime.Now - when; if (timeSpan.TotalSeconds < 10.0) { return "now"; } if (timeSpan.TotalMinutes < 1.0) { return $"{(int)timeSpan.TotalSeconds}s ago"; } if (timeSpan.TotalHours < 1.0) { return $"{(int)timeSpan.TotalMinutes}m ago"; } return when.ToString("HH:mm:ss"); } private void DrawLogsWindow(Rect content) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03b9: Unknown result type (might be due to invalid IL or missing references) //IL_03be: 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_03c5: 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_0563: Unknown result type (might be due to invalid IL or missing references) //IL_04ec: Unknown result type (might be due to invalid IL or missing references) //IL_04a7: Unknown result type (might be due to invalid IL or missing references) //IL_04ac: Unknown result type (might be due to invalid IL or missing references) RefreshFilter(); float num = OverlayStyles.ButtonWidth("CLEAR", (GUIStyle)null); float num2 = Wider("AUTOSCROLL", "SCROLL OFF"); float num3 = Wider("ALERTS ONLY", "ALL LINES"); _logs.FitContentWidth(120f + num + num2 + num3 + 18f); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref content)).x, ((Rect)(ref content)).y, ((Rect)(ref content)).width, RowHeight); float xMax = ((Rect)(ref val)).xMax; Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(xMax - num, ((Rect)(ref val)).y, num, RowHeight); if (OverlayStyles.Press(val2, "CLEAR", OverlayStyles.Button)) { MenuLog.Clear(); } Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(((Rect)(ref val2)).x - 6f - num2, ((Rect)(ref val)).y, num2, RowHeight); if (OverlayStyles.Press(val3, _autoScroll ? "AUTOSCROLL" : "SCROLL OFF", _autoScroll ? OverlayStyles.ButtonActive : OverlayStyles.Button)) { _autoScroll = !_autoScroll; _scrollToEnd = _autoScroll; } Rect val4 = default(Rect); ((Rect)(ref val4))..ctor(((Rect)(ref val3)).x - 6f - num3, ((Rect)(ref val)).y, num3, RowHeight); if (OverlayStyles.Press(val4, _alertsOnly ? "ALERTS ONLY" : "ALL LINES", _alertsOnly ? OverlayStyles.ButtonActive : OverlayStyles.Button)) { _alertsOnly = !_alertsOnly; _scrollToEnd = _autoScroll; } Rect val5 = default(Rect); ((Rect)(ref val5))..ctor(((Rect)(ref val)).x, ((Rect)(ref val)).y, Mathf.Max(80f, ((Rect)(ref val4)).x - ((Rect)(ref val)).x - 6f), RowHeight); GUI.SetNextControlName("AntiCheatMenuSearch"); string text = GUI.TextField(val5, _search, OverlayStyles.Field); if (text != _search) { _search = text; _scrollToEnd = _autoScroll; } if (_search.Length == 0 && GUI.GetNameOfFocusedControl() != "AntiCheatMenuSearch") { GUI.Label(new Rect(((Rect)(ref val5)).x + 6f, ((Rect)(ref val5)).y, ((Rect)(ref val5)).width, ((Rect)(ref val5)).height), "Search...", OverlayStyles.LabelMuted); } Rect val6 = default(Rect); ((Rect)(ref val6))..ctor(((Rect)(ref content)).x, ((Rect)(ref content)).y + RowHeight + 6f, ((Rect)(ref content)).width, ((Rect)(ref content)).height - RowHeight - 6f); OverlayStyles.DrawBox(val6, new Color(0f, 0f, 0f, 0.25f), new Color(1f, 1f, 1f, 0.1f)); Rect val7 = default(Rect); ((Rect)(ref val7))..ctor(((Rect)(ref val6)).x + 3f, ((Rect)(ref val6)).y + 3f, ((Rect)(ref val6)).width - 6f, ((Rect)(ref val6)).height - 6f); LayoutLogs(Mathf.Max(1f, ((Rect)(ref val7)).width - 20f)); Rect val8 = default(Rect); ((Rect)(ref val8))..ctor(0f, 0f, ((Rect)(ref val7)).width - 14f, _logContentHeight); if (_scrollToEnd) { _logs.Scroll.y = Mathf.Max(0f, ((Rect)(ref val8)).height - ((Rect)(ref val7)).height); _scrollToEnd = false; } _logs.Scroll = GUI.BeginScrollView(val7, _logs.Scroll, val8); int num4 = FirstVisible(_logs.Scroll.y); int num5 = Mathf.Min(_filtered.Length, FirstVisible(_logs.Scroll.y + ((Rect)(ref val7)).height) + 2); Rect val9 = default(Rect); for (int i = num4; i < num5; i++) { MenuLogEntry menuLogEntry = _filtered[i]; float num6 = _logRows[i + 1] - _logRows[i]; ((Rect)(ref val9))..ctor(0f, _logRows[i], ((Rect)(ref val8)).width, num6); if (i % 2 == 1) { GUI.Label(val9, GUIContent.none, OverlayStyles.RowAlt); } if (menuLogEntry.Level == MenuLogLevel.Alert) { OverlayStyles.Fill(new Rect(((Rect)(ref val9)).x, ((Rect)(ref val9)).y, 2f, ((Rect)(ref val9)).height), OverlayStyles.Primary); } GUI.Label(new Rect(((Rect)(ref val9)).x + 6f, ((Rect)(ref val9)).y + 2f, ((Rect)(ref val9)).width - 8f, ((Rect)(ref val9)).height - 4f), menuLogEntry.Display, (menuLogEntry.Level == MenuLogLevel.Alert) ? AlertLogStyle() : LogStyle()); } GUI.EndScrollView(); if (_filtered.Length == 0) { GUI.Label(new Rect(((Rect)(ref val7)).x + 6f, ((Rect)(ref val7)).y, ((Rect)(ref val7)).width, 20f), (MenuLog.Count == 0) ? "Nothing logged yet." : "No lines match that search.", OverlayStyles.LabelMuted); } } private void RefreshFilter() { int version = MenuLog.Version; int generation = OverlayStyles.Generation; if (version == _filterVersion && _filterSearch == _search && _filterAlertsOnly == _alertsOnly && generation == _filterGeneration) { return; } bool flag = _filterVersion != version && _autoScroll; _filterVersion = version; _filterSearch = _search; _filterAlertsOnly = _alertsOnly; _filterGeneration = generation; MenuLogEntry[] array = MenuLog.Snapshot(); List list = new List(array.Length); string text = (string.IsNullOrEmpty(_search) ? null : _search.Trim()); foreach (MenuLogEntry menuLogEntry in array) { if ((!_alertsOnly || menuLogEntry.Level == MenuLogLevel.Alert) && (text == null || text.Length <= 0 || menuLogEntry.Text.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0)) { list.Add(menuLogEntry); } } _filtered = list.ToArray(); _logLayoutWidth = -1f; if (flag) { _scrollToEnd = true; } } private void LayoutLogs(float width) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown int generation = OverlayStyles.Generation; if (!Mathf.Approximately(width, _logLayoutWidth) || _logLayoutFilterVersion != _filterVersion || _logLayoutGeneration != generation) { _logLayoutWidth = width; _logLayoutFilterVersion = _filterVersion; _logLayoutGeneration = generation; _logStyle = null; _alertLogStyle = null; _logRows = new float[_filtered.Length + 1]; GUIStyle val = LogStyle(); float num = 0f; for (int i = 0; i < _filtered.Length; i++) { _logRows[i] = num; num += Mathf.Max(LogRowHeight, val.CalcHeight(new GUIContent(_filtered[i].Display), width - 8f) + 4f); } _logRows[_filtered.Length] = num; _logContentHeight = num; } } private int FirstVisible(float y) { int num = 0; int num2 = _filtered.Length; while (num < num2) { int num3 = num + (num2 - num) / 2; if (_logRows[num3 + 1] <= y) { num = num3 + 1; } else { num2 = num3; } } return Mathf.Clamp(num, 0, _filtered.Length); } private GUIStyle LogStyle() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_002f: Expected O, but got Unknown if (_logStyle == null) { _logStyle = new GUIStyle(OverlayStyles.Label) { wordWrap = true, clipping = (TextClipping)1 }; } return _logStyle; } private GUIStyle AlertLogStyle() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (_alertLogStyle == null) { _alertLogStyle = new GUIStyle(LogStyle()); _alertLogStyle.normal.textColor = OverlayStyles.LabelAlert.normal.textColor; } return _alertLogStyle; } private List CachedProfiles() { if (_profileCache != null && _profileCacheFrame == Time.frameCount) { return _profileCache; } _profileCache = Profiles.All(); _profileCacheFrame = Time.frameCount; return _profileCache; } private void InvalidateProfileCache() { _profileCache = null; _profileCacheFrame = -1; } private void DrawProfileButton(Rect rect, PlayerRecord record, Rect listArea) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) string text = Profiles.AssignedName(record.SteamId); bool flag = _pickerFor == record.SteamId; if (OverlayStyles.Press(rect, string.IsNullOrEmpty(text) ? "-" : text, flag ? OverlayStyles.ButtonActive : OverlayStyles.Button)) { if (flag) { _pickerFor = 0uL; return; } if (CachedProfiles().Count == 0) { Toast("No profiles yet - make one in the Profiles window."); return; } _pickerFor = record.SteamId; _pickerAnchor = new Rect(((Rect)(ref listArea)).x + ((Rect)(ref rect)).x, ((Rect)(ref listArea)).y + ((Rect)(ref rect)).y - _players.Scroll.y, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height); } } private void DrawProfilePicker(Rect content) { //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) if (_pickerFor == 0) { return; } List list = CachedProfiles(); string text = Profiles.AssignedName(_pickerFor); float num = (float)(list.Count + 1) * PickerRowHeight + 6f; float num2 = Mathf.Max(120f, ((Rect)(ref _pickerAnchor)).width + 60f); for (int i = 0; i < list.Count; i++) { num2 = Mathf.Max(num2, OverlayStyles.ButtonWidth(list[i].Name, (GUIStyle)null) + 6f); } num2 = Mathf.Max(num2, OverlayStyles.ButtonWidth("NO PROFILE", (GUIStyle)null) + 6f); Rect val = default(Rect); ((Rect)(ref val))..ctor(Mathf.Min(((Rect)(ref _pickerAnchor)).x, ((Rect)(ref content)).xMax - num2), Mathf.Min(((Rect)(ref _pickerAnchor)).yMax + 2f, ((Rect)(ref content)).yMax - num), num2, num); OverlayStyles.DrawBox(val, OverlayStyles.Background, OverlayStyles.Primary); float num3 = ((Rect)(ref val)).y + 3f; if (OverlayStyles.Press(new Rect(((Rect)(ref val)).x + 3f, num3, ((Rect)(ref val)).width - 6f, PickerRowHeight - 2f), "NO PROFILE", string.IsNullOrEmpty(text) ? OverlayStyles.ButtonActive : OverlayStyles.Button)) { Assign(_pickerFor, null); } num3 += PickerRowHeight; for (int j = 0; j < list.Count; j++) { Profile profile = list[j]; bool flag = string.Equals(profile.Name, text, StringComparison.OrdinalIgnoreCase); if (OverlayStyles.Press(new Rect(((Rect)(ref val)).x + 3f, num3, ((Rect)(ref val)).width - 6f, PickerRowHeight - 2f), profile.Name, flag ? OverlayStyles.ButtonActive : OverlayStyles.Button)) { Assign(_pickerFor, profile.Name); } num3 += PickerRowHeight; } if ((int)Event.current.type == 0 && !((Rect)(ref val)).Contains(Event.current.mousePosition)) { _pickerFor = 0uL; } } private void Assign(ulong steamId, string profileName) { Profiles.Assign(steamId, profileName); InvalidateProfileCache(); _pickerFor = 0uL; string text = PlayerRegistry.NameFor(steamId) ?? steamId.ToString(); Toast(string.IsNullOrEmpty(profileName) ? ("Cleared the profile on " + text) : (text + " now carries " + profileName)); MenuLog.Info(string.IsNullOrEmpty(profileName) ? $"[Menu] Profile cleared on {text}({steamId})." : $"[Menu] Profile {profileName} assigned to {text}({steamId})."); } private void DrawProfilesWindow(Rect content) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) List list = CachedProfiles(); Profile profile = Selected(list); float y = ((Rect)(ref content)).y; y = DrawProfileBar(content, list, profile, y); if (profile == null) { OverlayStyles.Paragraph(new Rect(((Rect)(ref content)).x, y + 6f, ((Rect)(ref content)).width, 0f), (list.Count == 0) ? "No profiles yet. Name one above and press CREATE." : "Pick a profile above to edit what it lets through.", OverlayStyles.LabelMuted); return; } float num = OverlayStyles.TextHeight("Assign a profile from the PROFILE button in the Recent Players window.", ((Rect)(ref content)).width - 20f, OverlayStyles.LabelMuted); DrawProfileEditor(new Rect(((Rect)(ref content)).x, y + 6f, ((Rect)(ref content)).width, Mathf.Max(RowHeight, ((Rect)(ref content)).yMax - y - 6f - num - 4f)), profile); OverlayStyles.Paragraph(new Rect(((Rect)(ref content)).x, ((Rect)(ref content)).yMax - num, ((Rect)(ref content)).width - 20f, num), "Assign a profile from the PROFILE button in the Recent Players window.", OverlayStyles.LabelMuted); } private Profile Selected(List profiles) { if (profiles.Count == 0) { _selectedProfile = null; return null; } for (int i = 0; i < profiles.Count; i++) { if (profiles[i].Name == _selectedProfile) { return profiles[i]; } } _selectedProfile = profiles[0].Name; return profiles[0]; } private float DrawProfileBar(Rect content, List profiles, Profile selected, float y) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(22f, OverlayStyles.RowHeight); float num2 = OverlayStyles.ButtonWidth("CREATE", (GUIStyle)null); float num3 = Wider("DELETE", "SURE?"); float num4 = Mathf.Max(150f, OverlayStyles.Measure("New profile name...", OverlayStyles.LabelMuted) + 12f); _profiles.FitContentWidth(num4 + num2 + num3 + 18f); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref content)).x, y, num4, num); GUI.SetNextControlName("AntiCheatNewProfile"); _newProfileName = GUI.TextField(val, _newProfileName, 24, OverlayStyles.Field); if (_newProfileName.Length == 0 && GUI.GetNameOfFocusedControl() != "AntiCheatNewProfile") { GUI.Label(new Rect(((Rect)(ref val)).x + 6f, ((Rect)(ref val)).y, ((Rect)(ref val)).width, ((Rect)(ref val)).height), "New profile name...", OverlayStyles.LabelMuted); } Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).xMax + 6f, y, num2, num); if (OverlayStyles.Press(val2, "CREATE", OverlayStyles.Button)) { if (Profiles.Create(_newProfileName, out var message)) { _newProfileName = string.Empty; InvalidateProfileCache(); List list = CachedProfiles(); _selectedProfile = ((list.Count == 0) ? null : list[list.Count - 1].Name); } Toast(message); } if (selected != null) { bool flag = _deleteArmedFor == selected.Name && Time.unscaledTime < _deleteArmedUntil; Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(((Rect)(ref content)).xMax - num3, y, num3, num); if (OverlayStyles.Press(val3, flag ? "SURE?" : "DELETE", flag ? OverlayStyles.ButtonActive : OverlayStyles.DangerButton)) { if (flag) { string name = selected.Name; _deleteArmedFor = null; if (Profiles.Delete(name)) { _selectedProfile = null; InvalidateProfileCache(); Toast("Deleted " + name + " and unassigned everybody who had it"); } } else { _deleteArmedFor = selected.Name; _deleteArmedUntil = Time.unscaledTime + 3f; Toast("Click again to delete " + selected.Name); } } } y += num + 6f; if (profiles.Count == 0) { return y; } float num5 = ((Rect)(ref content)).x; for (int i = 0; i < profiles.Count; i++) { Profile profile = profiles[i]; int num6 = Profiles.AssignedCount(profile.Name); string text = $"{profile.Name} ({num6})"; float num7 = Mathf.Max(116f, OverlayStyles.ButtonWidth(text, (GUIStyle)null) + 4f); if (num5 > ((Rect)(ref content)).x && num5 + num7 > ((Rect)(ref content)).xMax) { num5 = ((Rect)(ref content)).x; y += num + 4f; } if (OverlayStyles.Press(new Rect(num5, y, num7 - 4f, num), text, (profile.Name == _selectedProfile) ? OverlayStyles.ButtonActive : OverlayStyles.Button)) { _selectedProfile = profile.Name; _deleteArmedFor = null; } num5 += num7; } return y + num + 4f; } private void DrawProfileEditor(Rect area, Profile profile) { //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: 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_03f5: Unknown result type (might be due to invalid IL or missing references) //IL_044c: Unknown result type (might be due to invalid IL or missing references) IList all = AntiGriefRules.All; IList all2 = CheckCatalog.All; float num = Mathf.Max(40f, ((Rect)(ref area)).width - 14f); float num2 = OverlayStyles.TextHeight("SKIP ALL runs no check at all against them - nothing is detected, blocked or announced.", num, OverlayStyles.LabelMuted); float num3 = OverlayStyles.TextHeight("ALLOW lets them do it whatever the rule says, with no warning and no violation counted.", num, OverlayStyles.LabelMuted); float num4 = OverlayStyles.TextHeight("The check still runs, still blocks and is still logged - it just cannot kick or ban them.", num, OverlayStyles.LabelMuted); float num5 = RowHeight + 4f + ChoiceHeight + 2f + num2 + 8f + RowHeight + 4f + num3 + (float)all.Count * (ChoiceHeight + 4f) + 8f + RowHeight + 4f + num4 + (float)all2.Count * (ChoiceHeight + 4f) + 8f; Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, num, num5); _profiles.Scroll = GUI.BeginScrollView(area, _profiles.Scroll, val); float num6 = 0f; GUI.Label(new Rect(0f, num6, ((Rect)(ref val)).width, RowHeight), "EVERY CHEAT CHECK", OverlayStyles.ColumnHeader); num6 += RowHeight + 4f; float num7 = (Mathf.Min(240f, ((Rect)(ref val)).width) - 4f) / 2f; if (ChoiceButton(new Rect(0f, num6, num7, ChoiceHeight), "NORMAL", !profile.BypassAllChecks) && profile.BypassAllChecks) { Profiles.SetBypassAllChecks(profile, bypass: false); InvalidateProfileCache(); Toast(profile.Name + " is subject to the checks again"); } if (ChoiceButton(new Rect(num7 + 4f, num6, num7, ChoiceHeight), "SKIP ALL", profile.BypassAllChecks) && !profile.BypassAllChecks) { Profiles.SetBypassAllChecks(profile, bypass: true); InvalidateProfileCache(); Toast(profile.Name + " now skips every cheat check"); } num6 += ChoiceHeight + 2f; OverlayStyles.Paragraph(new Rect(0f, num6, ((Rect)(ref val)).width, num2), "SKIP ALL runs no check at all against them - nothing is detected, blocked or announced.", OverlayStyles.LabelMuted); num6 += num2 + 8f; GUI.Label(new Rect(0f, num6, ((Rect)(ref val)).width, RowHeight), "ANTI-GRIEF RULES", OverlayStyles.ColumnHeader); num6 += RowHeight + 4f; OverlayStyles.Paragraph(new Rect(0f, num6, ((Rect)(ref val)).width, num3), "ALLOW lets them do it whatever the rule says, with no warning and no violation counted.", OverlayStyles.LabelMuted); num6 += num3; for (int i = 0; i < all.Count; i++) { GriefRule griefRule = all[i]; bool flag = profile.GriefBypass.Contains(griefRule.Key); if (DrawBypassRow(new Rect(0f, num6, ((Rect)(ref val)).width, ChoiceHeight), i, griefRule.Label, flag)) { Profiles.SetGriefBypass(profile, griefRule.Key, !flag); InvalidateProfileCache(); Toast(profile.Name + ": " + griefRule.Label + " " + (flag ? "back to normal" : "allowed")); } num6 += ChoiceHeight + 4f; } num6 += 8f; GUI.Label(new Rect(0f, num6, ((Rect)(ref val)).width, RowHeight), "CHECKS THAT MAY NEVER PUNISH THEM", OverlayStyles.ColumnHeader); num6 += RowHeight + 4f; OverlayStyles.Paragraph(new Rect(0f, num6, ((Rect)(ref val)).width, num4), "The check still runs, still blocks and is still logged - it just cannot kick or ban them.", OverlayStyles.LabelMuted); num6 += num4; for (int j = 0; j < all2.Count; j++) { CheckEntry checkEntry = all2[j]; bool flag2 = profile.CheckBypass.Contains(checkEntry.Key); if (DrawBypassRow(new Rect(0f, num6, ((Rect)(ref val)).width, ChoiceHeight), j, checkEntry.Label, flag2)) { Profiles.SetCheckBypass(profile, checkEntry.Key, !flag2); InvalidateProfileCache(); Toast(profile.Name + ": " + checkEntry.Label + " " + (flag2 ? "punishes as normal" : "never punishes")); } num6 += ChoiceHeight + 4f; } GUI.EndScrollView(); } private bool DrawBypassRow(Rect row, int index, string label, bool bypass) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) if (index % 2 == 1) { GUI.Label(row, GUIContent.none, OverlayStyles.RowAlt); } float num = Mathf.Max(96f, Wider("ALLOW", "NORMAL")); _profiles.FitContentWidth(OverlayStyles.Measure(label, OverlayStyles.LabelAlert) + num + 30f); GUI.Label(new Rect(((Rect)(ref row)).x + 4f, ((Rect)(ref row)).y, Mathf.Max(60f, ((Rect)(ref row)).width - num - 12f), ((Rect)(ref row)).height), label, bypass ? OverlayStyles.LabelAlert : OverlayStyles.Label); return ChoiceButton(new Rect(((Rect)(ref row)).xMax - num - 4f, ((Rect)(ref row)).y, num, ((Rect)(ref row)).height), bypass ? "ALLOW" : "NORMAL", bypass); } private static float ChoicesWidth(params string[] labels) { float num = 0f; for (int i = 0; i < labels.Length; i++) { num = Mathf.Max(num, OverlayStyles.ButtonWidth(labels[i], (GUIStyle)null)); } return Mathf.Max(258f, num * (float)labels.Length + 4f * (float)(labels.Length - 1)); } private static float RuleChoicesWidth(GriefRule rule) { return rule.Binary ? ChoicesWidth("RESTRICTED", "ALLOWED") : ChoicesWidth("RESTRICTED", "GAME START", "ALLOWED"); } private static float RuleRowHeight(GriefRule rule, float width) { float num = OverlayStyles.TextHeight(rule.Help, Mathf.Max(40f, width - 8f), OverlayStyles.LabelMuted); return ChoiceHeight + 2f + num + 4f; } private static float SettingHeight(string help, float width) { return ChoiceHeight + 2f + OverlayStyles.TextHeight(help, Mathf.Max(40f, width - 8f), OverlayStyles.LabelMuted) + 4f; } private void DrawGriefWindow(Rect content) { //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Unknown result type (might be due to invalid IL or missing references) //IL_03b9: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Unknown result type (might be due to invalid IL or missing references) //IL_0429: Unknown result type (might be due to invalid IL or missing references) //IL_04c9: Unknown result type (might be due to invalid IL or missing references) //IL_04fd: Unknown result type (might be due to invalid IL or missing references) //IL_054a: Unknown result type (might be due to invalid IL or missing references) //IL_0590: Unknown result type (might be due to invalid IL or missing references) IList all = AntiGriefRules.All; float num = OverlayStyles.TextHeight("Saved as you click. Host-side: nobody else needs the mod.", ((Rect)(ref content)).width - 20f, OverlayStyles.LabelMuted); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref content)).x, ((Rect)(ref content)).y, ((Rect)(ref content)).width, Mathf.Max(RowHeight, ((Rect)(ref content)).height - num - 4f)); float num2 = Mathf.Max(40f, ((Rect)(ref val)).width - 14f); float num3 = OverlayStyles.TextHeight("GAME START is per player: it lifts after they participate in ShipHasLanded.", num2, OverlayStyles.LabelMuted); float num4 = SettingHeight("Applied at the violation count below, then the count starts over.", num2); float num5 = SettingHeight("0 turns punishment off. Actions are still blocked.", num2); float num6 = SettingHeight("0 disables this limit. Players may pull the ship lever at this in-game hour or later.", num2); float[] array = new float[all.Count]; float num7 = 0f; for (int i = 0; i < all.Count; i++) { array[i] = RuleRowHeight(all[i], num2); num7 += array[i]; } float num8 = RowHeight + 2f + num3 + 2f + num7 + 8f + RowHeight + 2f + num6 + 8f + RowHeight + 2f + num4 + 8f + RowHeight + 2f + num5 + 8f; Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 0f, num2, num8); _grief.Scroll = GUI.BeginScrollView(val, _grief.Scroll, val2); float num9 = 0f; GUI.Label(new Rect(0f, num9, ((Rect)(ref val2)).width, RowHeight), "RULES", OverlayStyles.ColumnHeader); num9 += RowHeight + 2f; OverlayStyles.Paragraph(new Rect(0f, num9, ((Rect)(ref val2)).width, num3), "GAME START is per player: it lifts after they participate in ShipHasLanded.", OverlayStyles.LabelMuted); num9 += num3 + 2f; for (int j = 0; j < all.Count; j++) { DrawRuleRow(new Rect(0f, num9, ((Rect)(ref val2)).width, array[j]), all[j], j); num9 += array[j]; } num9 += 8f; GUI.Label(new Rect(0f, num9, ((Rect)(ref val2)).width, RowHeight), "SHIP LEVER", OverlayStyles.ColumnHeader); num9 += RowHeight + 2f; Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(0f, num9, ((Rect)(ref val2)).width, num6); float num10 = OverlayStyles.FieldWidth("24", (GUIStyle)null); _grief.FitContentWidth(OverlayStyles.Measure("Earliest hour players may pull lever", OverlayStyles.Label) + num10 + 24f); GUI.Label(new Rect(((Rect)(ref val3)).x + OverlayStyles.CellPadding, ((Rect)(ref val3)).y, Mathf.Max(0f, ((Rect)(ref val3)).width - num10 - 12f), ChoiceHeight), "Earliest hour players may pull lever", OverlayStyles.Label); DrawIntField(new Rect(((Rect)(ref val3)).xMax - num10 - 6f, ((Rect)(ref val3)).y, num10, ChoiceHeight), "AntiGriefPullShipLeverHour", PluginConfig.Restrict_PullShipLever, 0, 24); OverlayStyles.Paragraph(new Rect(((Rect)(ref val3)).x + 4f, ((Rect)(ref val3)).y + ChoiceHeight + 2f, ((Rect)(ref val3)).width - 8f, 0f), "0 disables this limit. Players may pull the ship lever at this in-game hour or later.", OverlayStyles.LabelMuted); num9 += num6 + 8f; GUI.Label(new Rect(0f, num9, ((Rect)(ref val2)).width, RowHeight), "PUNISHMENT", OverlayStyles.ColumnHeader); num9 += RowHeight + 2f; DrawPunishmentRow(new Rect(0f, num9, ((Rect)(ref val2)).width, num4), "What happens at the limit", "Applied at the violation count below, then the count starts over.", PluginConfig.AntiGrief_Punishment); num9 += num4 + 8f; GUI.Label(new Rect(0f, num9, ((Rect)(ref val2)).width, RowHeight), "VIOLATIONS REQUIRED FOR PUNISHMENT", OverlayStyles.ColumnHeader); num9 += RowHeight + 2f; Rect val4 = default(Rect); ((Rect)(ref val4))..ctor(0f, num9, ((Rect)(ref val2)).width, num5); float num11 = OverlayStyles.FieldWidth("9999", (GUIStyle)null); _grief.FitContentWidth(OverlayStyles.Measure("Blocked actions allowed per round", OverlayStyles.Label) + num11 + 24f); GUI.Label(new Rect(((Rect)(ref val4)).x + OverlayStyles.CellPadding, ((Rect)(ref val4)).y, Mathf.Max(0f, ((Rect)(ref val4)).width - num11 - 12f), ChoiceHeight), "Blocked actions allowed per round", OverlayStyles.Label); DrawIntField(new Rect(((Rect)(ref val4)).xMax - num11 - 6f, ((Rect)(ref val4)).y, num11, ChoiceHeight), "AntiGriefMaxViolations", PluginConfig.AntiGrief_MaxViolations, 0, 999); OverlayStyles.Paragraph(new Rect(((Rect)(ref val4)).x + 4f, ((Rect)(ref val4)).y + ChoiceHeight + 2f, ((Rect)(ref val4)).width - 8f, 0f), "0 turns punishment off. Actions are still blocked.", OverlayStyles.LabelMuted); num9 += num5 + 8f; GUI.EndScrollView(); OverlayStyles.Paragraph(new Rect(((Rect)(ref content)).x, ((Rect)(ref content)).yMax - num, ((Rect)(ref content)).width - 20f, num), "Saved as you click. Host-side: nobody else needs the mod.", OverlayStyles.LabelMuted); } private void DrawRuleRow(Rect row, GriefRule rule, int index) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) if (index % 2 == 1) { GUI.Label(row, GUIContent.none, OverlayStyles.RowAlt); } float num = RuleChoicesWidth(rule); _grief.FitContentWidth(OverlayStyles.Measure(rule.Label, OverlayStyles.Label) + num + 28f); float num2 = Mathf.Max(80f, ((Rect)(ref row)).width - num - 12f); GUI.Label(new Rect(((Rect)(ref row)).x + 4f, ((Rect)(ref row)).y + 2f, num2, ChoiceHeight), rule.Label, OverlayStyles.Label); OverlayStyles.Paragraph(new Rect(((Rect)(ref row)).x + 4f, ((Rect)(ref row)).y + 2f + ChoiceHeight, ((Rect)(ref row)).width - 8f, 0f), rule.Help, OverlayStyles.LabelMuted); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref row)).xMax - num - 4f, ((Rect)(ref row)).y + 2f, num, ChoiceHeight); PluginConfig.PermissionLevel level = rule.Level; if (rule.Binary) { float num3 = (((Rect)(ref val)).width - 4f) / 2f; if (!LevelButton(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, num3, ((Rect)(ref val)).height), "RESTRICTED", rule, level, PluginConfig.PermissionLevel.HostOnly)) { LevelButton(new Rect(((Rect)(ref val)).x + num3 + 4f, ((Rect)(ref val)).y, num3, ((Rect)(ref val)).height), "ALLOWED", rule, level, PluginConfig.PermissionLevel.Unrestricted); } } else { float num4 = (((Rect)(ref val)).width - 8f) / 3f; if (!LevelButton(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, num4, ((Rect)(ref val)).height), "RESTRICTED", rule, level, PluginConfig.PermissionLevel.HostOnly) && !LevelButton(new Rect(((Rect)(ref val)).x + num4 + 4f, ((Rect)(ref val)).y, num4, ((Rect)(ref val)).height), "GAME START", rule, level, PluginConfig.PermissionLevel.GameStart)) { LevelButton(new Rect(((Rect)(ref val)).x + (num4 + 4f) * 2f, ((Rect)(ref val)).y, num4, ((Rect)(ref val)).height), "ALLOWED", rule, level, PluginConfig.PermissionLevel.Unrestricted); } } } private bool LevelButton(Rect rect, string label, GriefRule rule, PluginConfig.PermissionLevel current, PluginConfig.PermissionLevel value) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!ChoiceButton(rect, label, current == value) || current == value) { return false; } rule.SetLevel(value); Toast(rule.Label + ": " + label); return true; } private void DrawCheckRow(Rect row, OverlayWindow window, CheckEntry check, int index) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (index % 2 == 1) { GUI.Label(row, GUIContent.none, OverlayStyles.RowAlt); } float punishmentWidth = PunishmentWidth; window.FitContentWidth(OverlayStyles.Measure(check.Label, OverlayStyles.Label) + punishmentWidth + 30f); GUI.Label(new Rect(((Rect)(ref row)).x + 4f, ((Rect)(ref row)).y, Mathf.Max(60f, ((Rect)(ref row)).width - punishmentWidth - 12f), ((Rect)(ref row)).height), check.Label, OverlayStyles.Label); DrawPunishmentChoices(new Rect(((Rect)(ref row)).xMax - punishmentWidth - 4f, ((Rect)(ref row)).y, punishmentWidth, ChoiceHeight), check.Feature.PunishmentEntry, check.Label); } private void DrawChecksWindow(Rect content) { //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) IList cheats = CheckCatalog.Cheats; float num = 0f; string text = null; for (int i = 0; i < cheats.Count; i++) { if (cheats[i].Group != text) { text = cheats[i].Group; num += RowHeight + 4f + ((i == 0) ? 0f : 8f); } num += ChoiceHeight + 4f; } float num2 = OverlayStyles.TextHeight("OFF stops the check. LOG ONLY still detects, blocks and alerts - it just leaves the player in the lobby.", ((Rect)(ref content)).width - 20f, OverlayStyles.LabelMuted); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref content)).x, ((Rect)(ref content)).y, ((Rect)(ref content)).width, Mathf.Max(RowHeight, ((Rect)(ref content)).height - num2 - 4f)); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 0f, Mathf.Max(40f, ((Rect)(ref val)).width - 14f), num); _checks.Scroll = GUI.BeginScrollView(val, _checks.Scroll, val2); float num3 = 0f; string text2 = null; for (int j = 0; j < cheats.Count; j++) { CheckEntry checkEntry = cheats[j]; if (checkEntry.Group != text2) { if (text2 != null) { num3 += 8f; } text2 = checkEntry.Group; GUI.Label(new Rect(0f, num3, ((Rect)(ref val2)).width, RowHeight), text2, OverlayStyles.ColumnHeader); num3 += RowHeight + 4f; } DrawCheckRow(new Rect(0f, num3, ((Rect)(ref val2)).width, ChoiceHeight), _checks, checkEntry, j); num3 += ChoiceHeight + 4f; } GUI.EndScrollView(); OverlayStyles.Paragraph(new Rect(((Rect)(ref content)).x, ((Rect)(ref content)).yMax - num2, ((Rect)(ref content)).width - 20f, num2), "OFF stops the check. LOG ONLY still detects, blocks and alerts - it just leaves the player in the lobby.", OverlayStyles.LabelMuted); } private void DrawPunishmentRow(Rect row, string label, string help, ConfigEntry entry) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) float punishmentWidth = PunishmentWidth; GUI.Label(new Rect(((Rect)(ref row)).x + 4f, ((Rect)(ref row)).y, Mathf.Max(80f, ((Rect)(ref row)).width - punishmentWidth - 12f), ChoiceHeight), label, OverlayStyles.Label); OverlayStyles.Paragraph(new Rect(((Rect)(ref row)).x + 4f, ((Rect)(ref row)).y + ChoiceHeight + 2f, ((Rect)(ref row)).width - 8f, 0f), help, OverlayStyles.LabelMuted); DrawPunishmentChoices(new Rect(((Rect)(ref row)).xMax - punishmentWidth - 4f, ((Rect)(ref row)).y, punishmentWidth, ChoiceHeight), entry, label); } private void DrawPunishmentChoices(Rect rect, ConfigEntry entry, string what) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) float num = (((Rect)(ref rect)).width - 12f) / 4f; PunishButton(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, num, ((Rect)(ref rect)).height), "OFF", entry, PunishmentType.Disable, what); PunishButton(new Rect(((Rect)(ref rect)).x + num + 4f, ((Rect)(ref rect)).y, num, ((Rect)(ref rect)).height), "LOG ONLY", entry, PunishmentType.OnlyLog, what); PunishButton(new Rect(((Rect)(ref rect)).x + (num + 4f) * 2f, ((Rect)(ref rect)).y, num, ((Rect)(ref rect)).height), "KICK", entry, PunishmentType.Kick, what); PunishButton(new Rect(((Rect)(ref rect)).x + (num + 4f) * 3f, ((Rect)(ref rect)).y, num, ((Rect)(ref rect)).height), "BAN", entry, PunishmentType.Ban, what); } private void PunishButton(Rect rect, string label, ConfigEntry entry, PunishmentType value, string what) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) bool flag = entry.Value == value; if (ChoiceButton(rect, label, flag) && !flag) { entry.Value = value; Toast(what + ": " + label); } } private void DrawIntField(Rect rect, string fieldName, ConfigEntry entry, int minimum, int maximum) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (!_numberFields.TryGetValue(fieldName, out var value)) { value = entry.Value.ToString(); _numberFields[fieldName] = value; } int result; bool flag = int.TryParse(value, out result) && result >= minimum && result <= maximum; GUI.SetNextControlName(fieldName); string text = GUI.TextField(OverlayStyles.Fit(rect, value, OverlayStyles.Field, false), value, 4, flag ? OverlayStyles.Field : OverlayStyles.FieldInvalid); if (text == value) { if (GUI.GetNameOfFocusedControl() != fieldName && (!flag || result != entry.Value)) { _numberFields[fieldName] = entry.Value.ToString(); } return; } _numberFields[fieldName] = text; if (int.TryParse(text, out var result2) && result2 >= minimum && result2 <= maximum) { entry.Value = result2; } } } internal enum MenuLogLevel { Info, Alert } internal sealed class MenuLogEntry { public DateTime Time; public MenuLogLevel Level; public string Text; public string Display; } internal static class MenuLog { private static readonly object Gate = new object(); private static readonly List Entries = new List(); private static readonly Regex RichTextTag = new Regex("<[^<>]*>", RegexOptions.Compiled); public static int Version { get; private set; } public static int Count { get { lock (Gate) { return Entries.Count; } } } public static void Info(string message) { Add(MenuLogLevel.Info, message); } public static void Alert(string message) { Add(MenuLogLevel.Alert, message); } public static void Add(MenuLogLevel level, string message) { if (string.IsNullOrEmpty(message)) { return; } string text = RichTextTag.Replace(message, string.Empty).TrimEnd(); if (text.Length == 0) { return; } DateTime now = DateTime.Now; MenuLogEntry item = new MenuLogEntry { Time = now, Level = level, Text = text, Display = $"{now:HH:mm:ss} {text}" }; int num = Capacity(); lock (Gate) { Entries.Add(item); if (Entries.Count > num) { Entries.RemoveRange(0, Entries.Count - num); } Version++; } } public static void Clear() { lock (Gate) { Entries.Clear(); Version++; } } public static MenuLogEntry[] Snapshot() { lock (Gate) { return Entries.ToArray(); } } private static int Capacity() { ConfigEntry menu_MaxLogLines = PluginConfig.Menu_MaxLogLines; if (menu_MaxLogLines == null) { return 1000; } return (menu_MaxLogLines.Value < 50) ? 50 : menu_MaxLogLines.Value; } } internal sealed class PlayerRecord { public ulong SteamId; public string SteamName; public string Username; public DateTime FirstSeen; public DateTime LastSeen; public bool Online; public int Joins; public string DisplayName { get { if (!string.IsNullOrEmpty(Username)) { return Username; } return string.IsNullOrEmpty(SteamName) ? $"Unknown ({SteamId})" : SteamName; } } } internal static class PlayerRegistry { private static readonly object Gate = new object(); private static readonly Dictionary Records = new Dictionary(); public static int Version { get; private set; } public static void RecordConnect(ulong steamId, string steamName) { if (steamId == 0) { return; } lock (Gate) { PlayerRecord orAdd = GetOrAdd(steamId); if (!string.IsNullOrEmpty(steamName)) { orAdd.SteamName = steamName; } if (!orAdd.Online) { orAdd.Joins++; } orAdd.Online = true; orAdd.LastSeen = DateTime.Now; Version++; } } public static void RecordDisconnect(ulong steamId) { if (steamId == 0) { return; } lock (Gate) { if (Records.TryGetValue(steamId, out var value) && value.Online) { value.Online = false; value.LastSeen = DateTime.Now; Version++; } } } public static void Sweep() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.allPlayerScripts == null) { return; } HashSet hashSet = new HashSet(); DateTime now = DateTime.Now; lock (Gate) { PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && val.playerSteamId != 0) { hashSet.Add(val.playerSteamId); PlayerRecord orAdd = GetOrAdd(val.playerSteamId); if (!orAdd.Online) { orAdd.Online = true; orAdd.Joins++; Version++; } if (!string.IsNullOrEmpty(val.playerUsername) && orAdd.Username != val.playerUsername) { orAdd.Username = val.playerUsername; Version++; } orAdd.LastSeen = now; } } foreach (PlayerRecord value in Records.Values) { if (value.Online && !hashSet.Contains(value.SteamId)) { value.Online = false; Version++; } } Trim(); } } public static List Recent() { lock (Gate) { List list = new List(Records.Values); list.Sort((PlayerRecord a, PlayerRecord b) => b.LastSeen.CompareTo(a.LastSeen)); return list; } } public static string NameFor(ulong steamId) { lock (Gate) { if (!Records.TryGetValue(steamId, out var value)) { return null; } return string.IsNullOrEmpty(value.Username) ? value.SteamName : value.Username; } } public static void Clear() { lock (Gate) { Records.Clear(); Version++; } } private static PlayerRecord GetOrAdd(ulong steamId) { if (Records.TryGetValue(steamId, out var value)) { return value; } value = new PlayerRecord { SteamId = steamId, FirstSeen = DateTime.Now, LastSeen = DateTime.Now }; Records[steamId] = value; Version++; return value; } private static void Trim() { int num = ((PluginConfig.Menu_MaxRecentPlayers == null) ? 50 : Math.Max(4, PluginConfig.Menu_MaxRecentPlayers.Value)); if (Records.Count <= num) { return; } List list = new List(Records.Values); list.Sort((PlayerRecord a, PlayerRecord b) => a.LastSeen.CompareTo(b.LastSeen)); for (int num2 = 0; num2 < list.Count; num2++) { if (Records.Count <= num) { break; } if (!list[num2].Online) { Records.Remove(list[num2].SteamId); Version++; } } } } internal sealed class RegistrySweeper : MonoBehaviour { private static RegistrySweeper _instance; private float _next; internal static void Attach() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown if (!((Object)(object)_instance != (Object)null)) { GameObject val = new GameObject("AntiCheatRegistrySweeper"); Object.DontDestroyOnLoad((Object)(object)val); _instance = val.AddComponent(); } } private void OnDestroy() { _instance = null; } private void Update() { if (Time.unscaledTime < _next) { return; } _next = Time.unscaledTime + 1f; try { PlayerRegistry.Sweep(); } catch (Exception ex) { AntiCheatPlugin.Log.LogWarning((object)("[Menu] Player sweep failed: " + ex.Message)); } } } } namespace AntiCheat.Patches { [HarmonyPatch(typeof(HUDManager))] [HarmonyWrapSafe] public static class HUDManagerChatRealPatch { [HarmonyPatch(typeof(HUDManager), "__rpc_handler_2930587515")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool AddPlayerChatMessageServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.PlayerName.Value || PluginConfig.UseRate.Value) { try { bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); string text = null; if (flag) { ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); } int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); PatchHelper.LogInfo(p, "HUDManager.AddPlayerChatMessageServerRpc", "chatMessage:" + text); if (num == -1) { return false; } if (num <= StartOfRound.Instance.allPlayerScripts.Length) { if (PluginConfig.PlayerName.Value && StartOfRound.Instance.allPlayerScripts[num].playerSteamId != p.playerSteamId) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("ChatReal", new Dictionary { { "{player}", p.playerUsername }, { "{player2}", StartOfRound.Instance.allPlayerScripts[num].playerUsername } })); PatchHelper.Punish(p, PluginConfig.PlayerName); return false; } bool flag2 = CooldownManager.CheckCooldown("Chat", p); if (flag2 && PatchHelper.locale.current_language == "zh_CN") { AccessTools.DeclaredMethod(typeof(HUDManager), "AddPlayerChatMessageClientRpc", (Type[])null, (Type[])null).Invoke(HUDManager.Instance, new object[2] { text, num }); return false; } return flag2; } return false; } catch (Exception ex) { PatchHelper.LogInfo(ex.ToString()); return false; } } } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(HUDManager))] [HarmonyWrapSafe] public static class HUDManagerMapPatch { [HarmonyPatch("__rpc_handler_2787681914")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool AddTextMessageServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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) if (PatchHelper.Check(rpcParams, out var p)) { bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); string text = null; if (flag) { ((FastBufferReader)(ref reader)).ReadValueSafe(ref text, false); } ((FastBufferReader)(ref reader)).Seek(0); PatchHelper.LogInfo(p, "HUDManager.AddTextMessageServerRpc", "chatMessage:" + text); if (text.Contains("Tyzeron.Minimap")) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("Map", new Dictionary { { "{player}", p.playerUsername } })); PatchHelper.Punish(p, PluginConfig.Map); } return false; } return text.StartsWith("[morecompanycosmetics]"); } if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(FacepunchTransport))] [HarmonyWrapSafe] public static class FacepunchTransportPatch { [HarmonyPatch("Steamworks.ISocketManager.OnConnecting")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool FacepunchTransportOnConnecting(ref Connection connection, ref ConnectionInfo info) { //IL_0009: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) NetIdentity identity = Traverse.Create((object)info).Field("identity").Value; if (StartOfRound.Instance.allPlayerScripts.Any((PlayerControllerB x) => x.isPlayerControlled && x.playerSteamId == ((NetIdentity)(ref identity)).SteamId.Value)) { PatchHelper.LogInfo("{steamId} repeatedly joins the game."); return false; } if (PatchHelper.ConnectionIdtoSteamIdMap.ContainsKey(((Connection)(ref connection)).Id)) { PatchHelper.ConnectionIdtoSteamIdMap[((Connection)(ref connection)).Id] = ((NetIdentity)(ref identity)).SteamId.Value; } else { PatchHelper.ConnectionIdtoSteamIdMap.Add(((Connection)(ref connection)).Id, ((NetIdentity)(ref identity)).SteamId.Value); } ulong value = ((NetIdentity)(ref identity)).SteamId.Value; Friend val = new Friend(SteamId.op_Implicit(((NetIdentity)(ref identity)).SteamId.Value)); PlayerRegistry.RecordConnect(value, ((Friend)(ref val)).Name); if (PluginConfig.OperationLog.Value) { LocalizationManager locale = PatchHelper.locale; Dictionary dictionary = new Dictionary(); val = new Friend(SteamId.op_Implicit(PatchHelper.ConnectionIdtoSteamIdMap[((Connection)(ref connection)).Id])); dictionary.Add("{player}", ((Friend)(ref val)).Name); PatchHelper.ShowMessageHostOnly(locale.OperationLog_GetString("JoinLobby", dictionary)); } return true; } [HarmonyPatch("Steamworks.ISocketManager.OnDisconnected")] [HarmonyPrefix] [HarmonyWrapSafe] public static void FacepunchTransportOnDisconnected(ref Connection connection, ref ConnectionInfo info) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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_003c: Unknown result type (might be due to invalid IL or missing references) NetworkManager singleton = NetworkManager.Singleton; if (singleton != null && singleton.IsListening) { NetIdentity value = Traverse.Create((object)info).Field("identity").Value; PlayerRegistry.RecordDisconnect(((NetIdentity)(ref value)).SteamId.Value); HUDManagerRoundSyncPatch.SyncAllPlayerLevelsServerRpcCalls.Remove(PatchHelper.ConnectionIdtoSteamIdMap[((Connection)(ref connection)).Id]); StartOfRoundPatch.SyncShipUnlockablesServerRpcCalls.Remove(PatchHelper.ConnectionIdtoSteamIdMap[((Connection)(ref connection)).Id]); StartOfRoundPatch.SyncAlreadyHeldObjectsServerRpcCalls.Remove(PatchHelper.ConnectionIdtoSteamIdMap[((Connection)(ref connection)).Id]); PatchHelper.ConnectionIdtoSteamIdMap.Remove(((Connection)(ref connection)).Id); } } } [HarmonyPatch(typeof(NetworkConfig))] [HarmonyWrapSafe] public static class NetworkConfigPatch { [HarmonyPatch("CompareConfig")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool NetworkConnectionManagerInitialize(ref bool __result) { if ((Object)(object)StartOfRound.Instance != (Object)null && ((NetworkBehaviour)StartOfRound.Instance).IsHost && PluginConfig.IgnoreClientConfig.Value) { __result = true; return false; } return true; } } [HarmonyPatch(typeof(NetworkManager))] [HarmonyWrapSafe] public static class NetworkManagerPatch { [HarmonyPatch("Awake")] [HarmonyPrefix] [HarmonyWrapSafe] public static void NetworkManagerAwake() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (!GameNetworkManager.Instance.disableSteam) { PatchHelper.ConnectionIdtoSteamIdMap[0u] = SteamId.op_Implicit(SteamClient.SteamId); } } } [HarmonyPatch(typeof(PlayerControllerB))] [HarmonyWrapSafe] public static class PlayerControllerConnectionPatch { [HarmonyPatch("__rpc_handler_2504133785")] [HarmonyPrefix] public static bool SendNewPlayerValuesServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) if (!((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost || rpcParams.Server.Receive.SenderClientId == 0) { return true; } ulong num = default(ulong); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); if (!PatchHelper.ConnectionIdtoSteamIdMap.TryGetValue(PatchHelper.ClientIdToTransportId(rpcParams.Server.Receive.SenderClientId), out var value)) { AntiCheatPlugin.LogInfo($"No recorded Steam ID for joining client {rpcParams.Server.Receive.SenderClientId} - identity check skipped."); return true; } Friend val = default(Friend); ((Friend)(ref val))..ctor(SteamId.op_Implicit(value)); if (num != value) { NetworkManager.Singleton.DisconnectClient(rpcParams.Server.Receive.SenderClientId); AntiCheatPlugin.LogInfo($"Player {((Friend)(ref val)).Name}({value}) spoofed SteamID({num}) while joining"); return false; } string value2 = PluginConfig.PlayerJoin.Value; if (!string.IsNullOrEmpty(value2) && value2.Trim().Length > 0) { ShowJoinMessage(value2.Replace("{player}", ((Friend)(ref val)).Name)); } ServerDisclaimer.PostOnJoin(); return true; } private static void ShowJoinMessage(string message) { string text = AntiCheatPlugin.Colorize(message, PluginConfig.PlayerJoin_Color.Value); switch (PluginConfig.PlayerJoin_MessageType.Value) { case PluginConfig.MessageType.PublicChat: PatchHelper.AddTextMessageClientRpc(text); break; case PluginConfig.MessageType.HostChat: AntiCheatPlugin.ShowMessageHostOnly(text); break; case PluginConfig.MessageType.Menu: MenuLog.Info(text); break; default: AntiCheatPlugin.ShowGuiTip(text); break; } } } [HarmonyPatch(typeof(StartOfRound))] [HarmonyWrapSafe] public static class StartOfRoundConnectionPatch { [HarmonyPatch("OnPlayerDC")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnPlayerDC(int playerObjectNumber, ulong clientId) { PlayerControllerB component = StartOfRound.Instance.allPlayerObjects[playerObjectNumber].GetComponent(); component.playerSteamId = 0uL; } } [HarmonyPatch(typeof(BeltBagItem))] [HarmonyWrapSafe] public static class BeltBagItemPatch { [HarmonyPatch("__rpc_handler_2988305002")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_2988305002(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: 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_002f: 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_012a: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.GrabObject.Value) { NetworkObjectReference val = default(NetworkObjectReference); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val, default(ForNetworkSerializable)); int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); NetworkObject val2 = default(NetworkObject); if (((NetworkObjectReference)(ref val)).TryGet(ref val2, (NetworkManager)null)) { GrabbableObject component = ((Component)val2).GetComponent(); if (!component.itemProperties.isScrap && !component.isHeld && !component.isHeldByEnemy && component.itemProperties.itemId != 123984 && component.itemProperties.itemId != 819501) { return true; } if (PluginConfig.GrabObject_SendLog.Value) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("GrabObject_BeltBag", new Dictionary { { "{player}", p.playerUsername }, { "{itemName}", component.itemProperties.itemName } })); } if (PluginConfig.GrabObject_BeltBag.Value) { ((BeltBagItem)target).CancelAddObjectToBagClientRpc(num); return false; } } } } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(PlayerControllerB))] [HarmonyWrapSafe] public static class PlayerControllerGrabObjectPatch { [HarmonyPatch("__rpc_handler_412259855")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool SwitchItemSlotsServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if ((Object)(object)p.currentlyHeldObjectServer != (Object)null && p.currentlyHeldObjectServer.itemProperties.twoHanded) { return false; } return true; } if ((Object)(object)p == (Object)null) { return false; } return true; } [HarmonyPatch("__rpc_handler_1554282707")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool GrabObjectServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014e: 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_0419: Unknown result type (might be due to invalid IL or missing references) //IL_04a1: Unknown result type (might be due to invalid IL or missing references) //IL_04a6: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.GrabObject.Value) { NetworkObjectReference val = default(NetworkObjectReference); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val, default(ForNetworkSerializable)); ((FastBufferReader)(ref reader)).Seek(0); NetworkObject val2 = default(NetworkObject); if (((NetworkObjectReference)(ref val)).TryGet(ref val2, (NetworkManager)null)) { bool flag = true; bool flag2 = false; bool flag3 = false; GrabbableObject[] itemSlots = p.ItemSlots; foreach (GrabbableObject val3 in itemSlots) { if ((Object)(object)val3 == (Object)null) { flag = false; } else if (val3.itemProperties.twoHanded) { flag3 = true; } else if (val3 is JetpackItem) { flag2 = true; } } GrabbableObject componentInChildren = ((Component)val2).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { PatchHelper.LogInfo(p, "PlayerControllerB.GrabObjectServerRpc", "itemName:" + componentInChildren.itemProperties.itemName, "heldByPlayerOnServer:" + ((!componentInChildren.heldByPlayerOnServer) ? "false" : componentInChildren.playerHeldBy?.playerUsername), $"Distance:{Vector3.Distance(((Component)p).transform.position, ((Component)componentInChildren).transform.position)}"); bool flag4 = false; GrabbableObject val4 = null; if (p.currentItemSlot >= 0 && p.currentItemSlot < p.ItemSlots.Length) { val4 = p.ItemSlots[p.currentItemSlot]; } bool flag5 = ((Object)(object)p.currentlyHeldObjectServer != (Object)null && p.currentlyHeldObjectServer.itemProperties.twoHanded) || ((Object)(object)val4 != (Object)null && val4.itemProperties.twoHanded); bool flag6 = p.currentlyHeldObjectServer is JetpackItem || val4 is JetpackItem; bool flag7 = flag5 && !componentInChildren.itemProperties.twoHanded; string value = (((Object)(object)p.currentlyHeldObjectServer != (Object)null) ? p.currentlyHeldObjectServer.itemProperties.itemName : (((Object)(object)val4 != (Object)null) ? val4.itemProperties.itemName : "unknown")); bool flag8 = flag6 && componentInChildren.itemProperties.twoHanded && !flag3; if (flag7 || (componentInChildren.itemProperties.twoHanded && flag3) || (componentInChildren.itemProperties.twoHanded && flag2) || (flag3 && flag2)) { if (PluginConfig.GrabObject_SendLog.Value && !flag8) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("GrabObject_TwoHand", new Dictionary { { "{player}", p.playerUsername }, { "{heldItemName}", value }, { "{itemName}", componentInChildren.itemProperties.itemName }, { "{hasTwoHand}", flag3.ToString() }, { "{jetpack}", flag2.ToString() } })); } if (PluginConfig.GrabObject_TwoHand.Value) { flag4 = true; } } if (flag && !flag4 && PluginConfig.GrabObject_SendLog.Value) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("GrabObject_MoreSlot", new Dictionary { { "{player}", p.playerUsername }, { "{itemName}", componentInChildren.itemProperties.itemName } }), p, PluginConfig.GrabObject); } if (PluginConfig.GrabObject_MoreSlot.Value && !flag4) { flag4 = flag; } if (flag4) { PatchHelper.Punish(p, PluginConfig.GrabObject); ForceCancelGrab(target); return false; } if (Vector3.Distance(((Component)componentInChildren).transform.position, p.serverPlayerPosition) > 100f && !StartOfRound.Instance.shipIsLeaving && StartOfRound.Instance.shipHasLanded) { if (p.teleportedLastFrame) { return true; } if (PluginConfig.GrabObject_SendLog.Value) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("GrabObject", new Dictionary { { "{player}", p.playerUsername }, { "{object_position}", ((object)((Component)componentInChildren).transform.position/*cast due to .constrained prefix*/).ToString() }, { "{player_position}", ((object)Unsafe.As(ref p.serverPlayerPosition)/*cast due to .constrained prefix*/).ToString() } }), p, PluginConfig.GrabObject); } PatchHelper.Punish(p, PluginConfig.GrabObject); ForceCancelGrab(target); return false; } } } } } else if ((Object)(object)p == (Object)null) { return false; } return true; } private static void ForceCancelGrab(NetworkBehaviour target) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown FieldInfo field = typeof(NetworkBehaviour).GetField("__rpc_exec_stage", BindingFlags.Instance | BindingFlags.NonPublic); field.SetValue(target, 1); typeof(PlayerControllerB).GetMethod("GrabObjectServerRpc", BindingFlags.Instance | BindingFlags.NonPublic).Invoke((object?)(PlayerControllerB)target, new object[1] { (object)default(NetworkObjectReference) }); field.SetValue(target, 0); } } [HarmonyPatch(typeof(GiftBoxItem))] [HarmonyWrapSafe] public static class GiftBoxItemPatch { [HarmonyPatch("__rpc_handler_2878544999")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_2878544999(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.Gift.Value) { GiftBoxItem val = (GiftBoxItem)target; if ((bool)AccessTools.DeclaredField(typeof(GiftBoxItem), "hasUsedGift").GetValue(val)) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("Gift", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.Gift); PatchHelper.Punish(p, PluginConfig.Gift); return false; } ((MonoBehaviour)StartOfRound.Instance.localPlayerController).StartCoroutine(PatchHelper.DestroySelf(((Component)val).gameObject)); } } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(GrabbableObject))] [HarmonyWrapSafe] public static class GrabbableObjectItemUsePatch { [HarmonyPostfix] [HarmonyPatch(typeof(GiftBoxItem), "ItemActivate")] public static void ItemActivate(GiftBoxItem __instance) { if (((NetworkBehaviour)StartOfRound.Instance).IsHost) { Object.Destroy((Object)(object)((Component)__instance).gameObject); } } [HarmonyPrefix] [HarmonyPatch("__rpc_handler_3484508350")] public static bool SyncBatteryServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown if (!PatchHelper.Check(rpcParams, out var p)) { return (Object)(object)p != (Object)null; } int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); GrabbableObject val = (GrabbableObject)target; if (val.itemProperties.requiresBattery) { AntiCheatPlugin.LogInfo(p, "(" + val.itemProperties.itemName + ")GrabbableObject.SyncBatteryServerRpc", $"num:{num}"); if (num > 100) { return false; } } return true; } } [HarmonyPatch(typeof(HauntedMaskItem))] [HarmonyWrapSafe] public static class HauntedMaskItemPatch { [HarmonyPatch("__rpc_handler_1065539967")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_1065539967(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.Mask.Value) { if (PatchHelper.mjs.Contains(((Object)target).GetInstanceID())) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("Mask", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.Mask); PatchHelper.Punish(p, PluginConfig.Mask); return false; } PatchHelper.mjs.Add(((Object)target).GetInstanceID()); } } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(JetpackItem))] [HarmonyWrapSafe] public static class JetpackItemPatch { [HarmonyPatch("__rpc_handler_3663112878")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_3663112878(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.Jetpack.Value) { JetpackItem val = (JetpackItem)target; if ((Object)(object)((GrabbableObject)val).playerHeldBy != (Object)null && ((GrabbableObject)val).playerHeldBy.playerClientId != p.playerClientId) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("Jetpack", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.Jetpack); PatchHelper.Punish(p, PluginConfig.Jetpack); return false; } if ((Object)(object)((GrabbableObject)val).playerHeldBy == (Object)null) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("Jetpack", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.Jetpack); PatchHelper.Punish(p, PluginConfig.Jetpack); return false; } return true; } } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(PlayerControllerB))] [HarmonyWrapSafe] public static class PlayerControllerItemUsePatch { [HarmonyPatch("__rpc_handler_1786952262")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool DespawnHeldObjectServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.DespawnItem.Value) { PatchHelper.LogInfo(p, "PlayerControllerB.DespawnHeldObjectServerRpc", "itemName:" + p.currentlyHeldObjectServer.itemProperties.itemName); if ((Object)(object)p.currentlyHeldObjectServer != (Object)null && !(p.currentlyHeldObjectServer is GiftBoxItem) && !(p.currentlyHeldObjectServer is KeyItem)) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("DespawnItem", new Dictionary { { "{player}", p.playerUsername }, { "{item}", p.currentlyHeldObjectServer.itemProperties.itemName } }), p, PluginConfig.DespawnItem); PatchHelper.Punish(p, PluginConfig.DespawnItem); return false; } } } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(EnemyAI))] [HarmonyWrapSafe] public static class EnemyAIKillEnemyPatch { [HarmonyPatch("KillEnemy")] [HarmonyPrefix] [HarmonyWrapSafe] public static void KillEnemy(EnemyAI __instance) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost && __instance is ButlerEnemyAI) { LandminePatch.SpawnExplosion(((Component)__instance).transform.position + Vector3.up * 0.15f); } } } [HarmonyPatch] [HarmonyWrapSafe] public static class KickAnnouncePatch { private static readonly Regex Announcement = new Regex("^\\[playerNum\\d+\\]\\s+was\\s+(kicked|banned)", RegexOptions.IgnoreCase); [HarmonyPatch(typeof(StartOfRound), "KickPlayer")] [HarmonyPrefix] [HarmonyPriority(0)] [HarmonyWrapSafe] public static void ReadNameBeforeTheyGo(StartOfRound __instance, int playerObjToKick, out string __state) { __state = null; if (!((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.IsServer && !((Object)(object)__instance == (Object)null) && __instance.allPlayerScripts != null && playerObjToKick >= 0 && playerObjToKick < __instance.allPlayerScripts.Length) { PlayerControllerB val = __instance.allPlayerScripts[playerObjToKick]; if (!((Object)(object)val == (Object)null) && (val.isPlayerControlled || val.isPlayerDead)) { __state = val.playerUsername; } } } [HarmonyPatch(typeof(StartOfRound), "KickPlayer")] [HarmonyPostfix] [HarmonyWrapSafe] public static void AlertOnVanillaKick(string __state) { if (!string.IsNullOrEmpty(__state)) { AntiCheatPlugin.Announce(__state + " kicked"); AntiCheatPlugin.ForgetAlertsFor(__state); } } [HarmonyPatch(typeof(HUDManager), "AddTextToChatOnServer")] [HarmonyPrefix] [HarmonyPriority(0)] [HarmonyWrapSafe] public static bool SwallowKickAnnouncement(string chatMessage, int playerId) { if (playerId != -1 || string.IsNullOrEmpty(chatMessage)) { return true; } return !Announcement.IsMatch(chatMessage); } } [HarmonyPatch] [HarmonyWrapSafe] public static class LobbyControlPatch { private const string GameStartedReason = "Game has already started!"; [HarmonyPatch(typeof(GameNetworkManager), "SteamMatchmaking_OnLobbyCreated")] [HarmonyPostfix] [HarmonyWrapSafe] public static void LobbyCreated() { if (!((Object)(object)GameNetworkManager.Instance == (Object)null)) { LobbyControl.OnLobbyCreated(GameNetworkManager.Instance.lobbyHostSettings.isLobbyPublic); } } [HarmonyPatch(typeof(GameNetworkManager), "SetLobbyJoinable")] [HarmonyPostfix] [HarmonyWrapSafe] public static void SetLobbyJoinable(bool joinable) { LobbyControl.NoteJoinableChanged(joinable); } [HarmonyPatch(typeof(GameNetworkManager), "LeaveCurrentSteamLobby")] [HarmonyPostfix] [HarmonyWrapSafe] public static void LeftLobby() { LobbyControl.OnLobbyLeft(); } [HarmonyPatch(typeof(GameNetworkManager), "LeaveLobbyAtGameStart")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool LeaveLobbyAtGameStart() { if (!LobbyControl.LateJoin || !LobbyControl.HasLobby) { return true; } LobbyControl.ReassertAfterTakeOff(); return false; } [HarmonyPatch(typeof(GameNetworkManager), "ConnectionApproval")] [HarmonyPostfix] [HarmonyPriority(0)] [HarmonyWrapSafe] public static void ConnectionApproval(ConnectionApprovalRequest request, ConnectionApprovalResponse response) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) try { if (response.Approved || response.Pending || !string.Equals(response.Reason, "Game has already started!", StringComparison.Ordinal) || !LobbyControl.IsOpen || (!LobbyControl.InOrbit && !LobbyControl.LateJoin)) { return; } string text = Encoding.ASCII.GetString(request.Payload); string[] array = text.Split(','); if (array.Length < 1 || GameNetworkManager.Instance.gameVersionNum.ToString() != array[0]) { response.Reason = string.Format("Game version mismatch! Their version: {0}. Your version: {1}", GameNetworkManager.Instance.gameVersionNum, (array.Length < 1) ? "unknown" : array[0]); return; } ulong result = 0uL; if (array.Length >= 2) { ulong.TryParse(array[1].Trim(), out result); } if (!GameNetworkManager.Instance.disableSteam && ((Object)(object)StartOfRound.Instance == (Object)null || array.Length < 2 || StartOfRound.Instance.KickedClientIds.Contains(result))) { response.Reason = "You cannot rejoin after being kicked."; return; } if (result != 0L && BanList.Contains(result)) { response.Reason = PluginConfig.BanReason.Value; return; } response.Reason = string.Empty; response.CreatePlayerObject = false; response.Approved = true; response.Pending = false; AntiCheatPlugin.LogInfo(string.Format("[Lobby] Allowed steamId {0} to join {1}.", result, LobbyControl.InOrbit ? "in orbit after the game had started" : "mid-round")); } catch (Exception arg) { AntiCheatPlugin.LogInfo($"[Lobby] Late join approval failed: {arg}"); } } [HarmonyPatch(typeof(StartOfRound), "OnClientConnect")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnClientConnect(ulong clientId) { LateJoinSync.OnClientConnected(clientId); } [HarmonyPatch(typeof(RoundManager), "FinishedGeneratingLevelServerRpc")] [HarmonyPostfix] [HarmonyWrapSafe] public static void FinishedGeneratingLevelServerRpc(ulong clientId) { if (!((Object)(object)NetworkManager.Singleton == (Object)null) && NetworkManager.Singleton.IsServer) { LateJoinSync.NoteFinishedGenerating(clientId); } } } [HarmonyPatch] [HarmonyWrapSafe] public static class PlayerSlotPatch { private const string Module = "[Slots]"; private static ulong Placeholder(int slot) { return ulong.MaxValue - (ulong)slot; } [HarmonyPatch(typeof(StartOfRound), "OnClientConnect")] [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyWrapSafe] public static void Prefix(StartOfRound __instance, ulong clientId, out List __state) { __state = null; if ((Object)(object)__instance == (Object)null || (Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer || __instance.ClientPlayerList == null) { return; } List list = PlayerSlots.UnavailableFree(__instance); if (list.Count == 0) { return; } List list2 = PlayerSlots.UsableFree(__instance); if (list2.Count == 0) { string text = Describe(list); AntiCheatPlugin.LogInfo(string.Format("{0} Client {1} has to take slot {2}, whose model is still hidden - they will be invisible to everybody else until they die once.", "[Slots]", clientId, text)); MenuLog.Info(string.Format("{0} No usable player slot left - the joiner takes slot {1} and will be invisible until they die once.", "[Slots]", text)); return; } __state = new List(list.Count); for (int i = 0; i < list.Count; i++) { ulong num = Placeholder(list[i]); if (!__instance.ClientPlayerList.ContainsKey(num)) { __instance.ClientPlayerList.Add(num, list[i]); __state.Add(num); } } if (__state.Count > 0) { AntiCheatPlugin.LogInfo(string.Format("{0} Slot {1} unusable (model still hidden from its last occupant); client {2} goes to slot {3} instead.", "[Slots]", Describe(list), clientId, list2[0])); } } [HarmonyPatch(typeof(StartOfRound), "OnClientConnect")] [HarmonyPostfix] [HarmonyPriority(0)] [HarmonyWrapSafe] public static void Postfix(StartOfRound __instance, List __state) { if (__state != null && !((Object)(object)__instance == (Object)null) && __instance.ClientPlayerList != null) { for (int i = 0; i < __state.Count; i++) { __instance.ClientPlayerList.Remove(__state[i]); } } } private static string Describe(List slots) { if (slots.Count == 1) { return slots[0].ToString(); } StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < slots.Count; i++) { if (i > 0) { stringBuilder.Append((i == slots.Count - 1) ? " and " : ", "); } stringBuilder.Append(slots[i]); } return stringBuilder.ToString(); } } [HarmonyPatch(typeof(GameNetworkManager))] [HarmonyWrapSafe] public static class GameNetworkManagerLobbyMetadataPatch { [HarmonyPatch("StartHost")] [HarmonyPrefix] [HarmonyWrapSafe] public static void StartHost() { if (!string.IsNullOrWhiteSpace(PluginConfig.Prefix.Value)) { HostSettings lobbyHostSettings = GameNetworkManager.Instance.lobbyHostSettings; string text = lobbyHostSettings.lobbyName.Replace('【', '[').Replace('】', ']').Replace('[', '[') .Replace(']', ']'); List list = new List(); Match match = Regex.Match(text, "^\\[(.*?)\\]"); if (match.Success) { string value = match.Groups[1].Value; list.AddRange(value.Split('/')); text = text.Remove(0, match.Groups[0].Value.Length).TrimStart(); } if (!list.Any((string x) => x == PluginConfig.Prefix.Value)) { list.Add(PluginConfig.Prefix.Value); } lobbyHostSettings.lobbyName = "[" + string.Join("/", list) + "] " + text; } } } [HarmonyPatch(typeof(DoorLock))] [HarmonyWrapSafe] public static class DoorLockPatch { [HarmonyPatch("__rpc_handler_184554516")] [HarmonyPrefix] public static bool UnlockDoorServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { AntiCheatPlugin.LogInfo(p, "DoorLock.UnlockDoorServerRpc"); } else if ((Object)(object)p == (Object)null) { return false; } return true; } [HarmonyPatch("__rpc_handler_2046162111")] [HarmonyPrefix] public static bool OpenDoorAsEnemyServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { AntiCheatPlugin.LogInfo(p, "DoorLock.OpenDoorAsEnemyServerRpc"); } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(GrabbableObject))] [HarmonyWrapSafe] public static class GrabbableObjectOperationLogPatch { [HarmonyPrefix] [HarmonyPatch(typeof(LungProp), "EquipItem")] public static bool EquipItem(LungProp __instance) { if (PluginConfig.OperationLog.Value && __instance.isLungDocked && StartOfRound.Instance.shipHasLanded) { PatchHelper.ShowMessageHostOnly(PatchHelper.locale.OperationLog_GetString("GrabLungProp", new Dictionary { ["{player}"] = StartOfRound.Instance.allPlayerScripts.First((PlayerControllerB x) => ((NetworkBehaviour)x).OwnerClientId == ((NetworkBehaviour)__instance).OwnerClientId).playerUsername })); } return true; } } [HarmonyPatch(typeof(PlayerControllerB))] [HarmonyWrapSafe] public static class PlayerControllerOperationLogPatch { [HarmonyPatch("__rpc_handler_1748753755")] [HarmonyPrefix] public static bool DropAllHeldItemsRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!PatchHelper.Check(rpcParams, out var p)) { return (Object)(object)p != (Object)null; } AntiCheatPlugin.LogInfo(p, "PlayerControllerB.DropAllHeldItemsRpc"); return true; } } [HarmonyPatch(typeof(TerminalAccessibleObject))] [HarmonyWrapSafe] public static class TerminalAccessibleObjectPatch { [HarmonyPatch("__rpc_handler_1181174413")] [HarmonyPrefix] public static bool Prefix(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { AntiCheatPlugin.LogInfo(p, "TerminalAccessibleObject.SetDoorOpenServerRpc"); } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(CentipedeAI))] [HarmonyWrapSafe] public static class CentipedeAIPatch { [HarmonyPatch("__rpc_handler_2791977891")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_2791977891(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: 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) if (!PatchHelper.Check(rpcParams, out var p)) { return (Object)(object)p != (Object)null; } int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); PatchHelper.LogInfo(p, "CentipedeAI.ClingToPlayerServerRpc", $"num:{num}"); if ((int)p.playerClientId == num) { if (PatchHelper.ClingTime.ContainsKey(PatchHelper.IdOf(p))) { PatchHelper.ClingTime[PatchHelper.IdOf(p)] = DateTime.Now; } else { PatchHelper.ClingTime.Add(PatchHelper.IdOf(p), DateTime.Now); } } return true; } } [HarmonyPatch(typeof(PlayerControllerB))] internal static class DamageMagnitudePatch { private const float DeathGraceSeconds = 3f; [HarmonyPatch("DamagePlayer")] [HarmonyPrefix] public static void DamagePlayerLocalPrefix(PlayerControllerB __instance, int damageNumber) { if (!((Object)(object)__instance == (Object)null) && ((NetworkBehaviour)__instance).IsOwner && !__instance.isPlayerDead) { LogReport(__instance, damageNumber, __instance.health, Mathf.Max(0, __instance.health - damageNumber)); } } [HarmonyPatch("DamagePlayerServerRpc")] [HarmonyPrefix] public static void DamagePlayerServerRpcPrefix(PlayerControllerB __instance, int damageNumber, int newHealthAmount) { if (IsGenuineIncomingCall(__instance)) { ulong actualClientId = __instance.actualClientId; int healthBefore = PlayerDamageState.Get(actualClientId); LogReport(__instance, damageNumber, healthBefore, newHealthAmount); PlayerDamageState.Set(actualClientId, newHealthAmount); PlayerDamageState.MarkAcknowledged(actualClientId); CheckSurvivedLethalDamage(__instance, damageNumber, healthBefore, newHealthAmount); } } [HarmonyPatch("DamagePlayerFromOtherClientServerRpc")] [HarmonyPrefix] public static void DamagePlayerFromOtherClientServerRpcPrefix(PlayerControllerB __instance, int damageAmount) { if (IsGenuineIncomingCall(__instance)) { ulong actualClientId = __instance.actualClientId; int num = PlayerDamageState.Get(actualClientId); int num2 = num - damageAmount; LogReport(__instance, damageAmount, num, num2); PlayerDamageState.Set(actualClientId, Mathf.Max(0, num2)); PlayerDamageState.MarkAcknowledged(actualClientId); CheckSurvivedLethalDamage(__instance, damageAmount, num, num2); } } private static void LogReport(PlayerControllerB player, int damage, int healthBefore, int healthAfter) { AntiCheatPlugin.LogInfo($"{player.playerUsername} took {damage} dmg\n" + $"HP Before : {healthBefore}\n" + $"HP After : {healthAfter}"); } private static void CheckSurvivedLethalDamage(PlayerControllerB player, int damage, int healthBefore, int healthAfter) { if (PluginConfig.GodMode.Value && (healthAfter <= 0 || damage >= healthBefore)) { ((MonoBehaviour)player).StartCoroutine(VerifyDeathFollowsLethalReport(player, damage, healthBefore, healthAfter)); } } private static IEnumerator VerifyDeathFollowsLethalReport(PlayerControllerB player, int damage, int healthBefore, int healthAfter) { yield return (object)new WaitForSeconds(3f); if (!((Object)(object)player == (Object)null) && !player.isPlayerDead && !PatchHelper.ChecksBypassed(player)) { AntiCheatPlugin.LogInfo($"{player.playerUsername} took {damage} damage on {healthBefore} HP (leaving {healthAfter}) and is still alive."); PatchHelper.ShowMessage($"{player.playerUsername}: survived {damage} dmg on {healthBefore} HP", $"lethal:{player.actualClientId}", player, PluginConfig.GodMode); PatchHelper.Punish(player, PluginConfig.GodMode); } } private static bool IsGenuineIncomingCall(PlayerControllerB instance) { return (Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.localPlayerController != (Object)null && ((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost && RpcExecStage.IsExecutingReceivedRpc((NetworkBehaviour)(object)instance); } } [HarmonyPatch(typeof(EnemyAI))] [HarmonyWrapSafe] public static class EnemyAICombatPatch { [HarmonyPatch("HitEnemyOnLocalClient")] [HarmonyPrefix] [HarmonyWrapSafe] public static void HitEnemyOnLocalClient(EnemyAI __instance, int force, Vector3 hitDirection, PlayerControllerB playerWhoHit, bool playHitSFX, int hitID) { if (((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost) { PatchHelper.bypassHit.Add(new PatchHelper.HitData { EnemyInstanceId = ((Object)__instance).GetInstanceID(), force = force, CalledClient = new List() }); } } [HarmonyPatch(typeof(EnemyAI), "__rpc_handler_3538577804")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool HitEnemyServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_0336: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_0355: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if ((Object)(object)p == (Object)null) { return false; } int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); int num2 = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num2); bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); ((FastBufferReader)(ref reader)).Seek(0); EnemyAI val = (EnemyAI)target; PatchHelper.LogInfo(p, "(" + val.enemyType.enemyName + ")EnemyAI.HitEnemyServerRpc", $"EnemyId:{((Object)val).GetInstanceID()}", $"HP:{val.enemyHP}", $"force:{num}", $"playerWhoHit:{PatchHelper.PlayerClientIdConvertName(num2)}({num2})"); if (p.isHostPlayerObject) { return true; } if (num2 != -1 && (Object)(object)StartOfRound.Instance.allPlayerScripts[num2] != (Object)(object)p) { PatchHelper.LogInfo("return false;"); return false; } VehicleController val2 = Object.FindFirstObjectByType(); if ((Object)(object)val2 != (Object)null) { PlayerControllerB currentDriver = val2.currentDriver; if (num == 2 && (Object)(object)currentDriver != (Object)null && currentDriver.playerClientId == p.playerClientId) { PatchHelper.lastDriver = currentDriver; return true; } if (num == 2 && (Object)(object)currentDriver == (Object)null && PatchHelper.lastDriver.playerClientId == p.playerClientId) { return true; } } foreach (PatchHelper.HitData item in PatchHelper.bypassHit) { if (item.EnemyInstanceId == ((Object)val).GetInstanceID() && item.force == num && !item.CalledClient.Contains(p.playerClientId)) { PatchHelper.LogInfo($"{((Object)val).GetInstanceID()} bypass|playerClientId:{p.playerClientId}"); item.CalledClient.Add(p.playerClientId); return true; } } if (PluginConfig.Shovel.Value) { GrabbableObject val3 = p.ItemSlots[p.currentItemSlot]; if (num == 6 && num2 == -1) { PatchHelper.LogInfo($"force = 6||enemyPostion:{((Component)val).transform.position}"); PatchHelper.explosions = PatchHelper.explosions.Where((PatchHelper.ExplosionData x) => x.CreateDateTime.AddSeconds(10.0) > DateTime.Now).ToList(); for (int num3 = PatchHelper.explosions.Count - 1; num3 > 0; num3--) { PatchHelper.ExplosionData explosionData = PatchHelper.explosions[num3]; if (!explosionData.CalledClient.Contains(PatchHelper.IdOf(p))) { float num4 = Vector3.Distance(explosionData.ExplosionPostion, ((Component)val).transform.position); PatchHelper.LogInfo($"ExplosionPostion:{explosionData.ExplosionPostion}||Distance:{num4}"); if (num4 < 5f) { explosionData.CalledClient.Add(PatchHelper.IdOf(p)); return true; } } } } else if (num != 1 && (Object)(object)val3 != (Object)null && (PatchHelper.isShovel(val3) || PatchHelper.isKnife(val3))) { if (!PatchHelper.jcs.Contains(PatchHelper.IdOf(p))) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("Shovel4", new Dictionary { { "{player}", p.playerUsername }, { "{enemyName}", val.enemyType.enemyName }, { "{damageAmount}", num.ToString() }, { "{item}", PatchHelper.isShovel(val3) ? PatchHelper.locale.Item_GetString("Shovel") : PatchHelper.locale.Item_GetString("Knife") } }), p, PluginConfig.Shovel); PatchHelper.jcs.Add(PatchHelper.IdOf(p)); PatchHelper.Punish(p, PluginConfig.Shovel); return false; } } else if (!p.isPlayerDead && (Object)(object)val3 == (Object)null) { if (PatchHelper.ClingTime.ContainsKey(PatchHelper.IdOf(p)) && PatchHelper.ClingTime[PatchHelper.IdOf(p)].AddSeconds(5.0) > DateTime.Now) { return true; } if (p.ItemSlots.Any((GrabbableObject x) => PatchHelper.isShovel(x)) && num == 1) { return true; } if (!PatchHelper.jcs.Contains(PatchHelper.IdOf(p))) { for (int num5 = 0; num5 < p.ItemSlots.Length; num5++) { PatchHelper.LogInfo($"p:{p.playerUsername}|i:{num5}|itemName:{p.ItemSlots[num5]?.itemProperties?.itemName}"); } PatchHelper.LogInfo($"currentItemSlot:{p.currentItemSlot}"); PatchHelper.LogInfo("currentlyHeldObjectServer:" + p.currentlyHeldObjectServer?.itemProperties?.itemName); PatchHelper.LogInfo($"obj:{val3}"); PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("Shovel6", new Dictionary { { "{player}", p.playerUsername }, { "{enemyName}", val.enemyType.enemyName }, { "{damageAmount}", num.ToString() } }), p, PluginConfig.Shovel); if (!PluginConfig.Shovel3.Value && (num == 1 || num == 2 || num == 3 || num == 5)) { PatchHelper.jcs.Add(PatchHelper.IdOf(p)); PatchHelper.Punish(p, PluginConfig.Shovel); } return false; } } else if (PatchHelper.jcs.Contains(PatchHelper.IdOf(p))) { return false; } } } return true; } } [HarmonyPatch(typeof(StartOfRound), "Update")] internal static class EnemyContactPatch { private sealed class ContactProfile { public string Name; public int Damage; public float Interval; public Func Ready; } private sealed class Tally { public int UnansweredDamage; public string LastEnemy; public float FirstHitTime; public float LastHitTime; public float LastReportTime = float.NegativeInfinity; } private const float SampleIntervalSeconds = 0.1f; private const float AcknowledgeSeconds = 1.5f; private const float TallyForgetSeconds = 10f; private static readonly Dictionary _profiles = BuildProfiles(); private static readonly Dictionary _tallies = new Dictionary(); private static readonly Dictionary _lastSimulatedHit = new Dictionary(); private static float _lastSampleTime; private static readonly Dictionary _triggers = new Dictionary(); private static FieldRef _blobTamedTimer; private static FieldRef _blobAngeredTimer; private static bool _blobFieldsResolved; private static readonly Dictionary _fieldRefs = new Dictionary(); private static Dictionary BuildProfiles() { Dictionary dictionary = new Dictionary(); Add(dictionary, typeof(BlobAI), "Hygrodere", 35, 0.25f, (EnemyAI enemy) => !IsTamedBlob((BlobAI)enemy)); Add(dictionary, typeof(SpringManAI), "Coilhead", 90, 0.45f, (EnemyAI enemy) => ((EnemyAI)(SpringManAI)enemy).currentBehaviourStateIndex == 1 && !GetBool("stoppingMovement", (SpringManAI)enemy, fallback: true) && !GetBool("setOnCooldown", (SpringManAI)enemy, fallback: true) && GetFloat("hitPlayerTimer", (SpringManAI)enemy, 1f) < 0f); Add(dictionary, typeof(BaboonBirdAI), "Baboon Hawk", 20, 0.5f, (EnemyAI enemy) => !((EnemyAI)(BaboonBirdAI)enemy).inSpecialAnimation && !GetBool("doingKillAnimation", (BaboonBirdAI)enemy, fallback: true)); Add(dictionary, typeof(CadaverBloomAI), "Cadaver Bloom", 30, 0.65f, null); Add(dictionary, typeof(RedLocustBees), "Circuit Bees", 10, 0.4f, null); Add(dictionary, typeof(ButlerBeesEnemyAI), "Butler Bees", 10, 0.5f, (EnemyAI enemy) => Time.realtimeSinceStartup - GetFloat("timeAtSpawning", (ButlerBeesEnemyAI)enemy, float.MaxValue) >= 1.7f); return dictionary; } private static void Add(Dictionary profiles, Type type, string name, int damage, float interval, Func ready) { if (type != null) { profiles[type] = new ContactProfile { Name = name, Damage = damage, Interval = interval, Ready = ready }; } } internal static void ResetAll() { _tallies.Clear(); _lastSimulatedHit.Clear(); } [HarmonyPostfix] public static void Postfix() { if (!PluginConfig.GodMode.Value || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.localPlayerController == (Object)null || !((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost || (Object)(object)RoundManager.Instance == (Object)null) { return; } float time = Time.time; if (time - _lastSampleTime < 0.1f) { return; } _lastSampleTime = time; if (!PlayerDamageState.DamageIsPossibleRightNow()) { _tallies.Clear(); _lastSimulatedHit.Clear(); return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; List spawnedEnemies = RoundManager.Instance.SpawnedEnemies; if (allPlayerScripts == null || spawnedEnemies == null) { return; } ulong actualClientId = StartOfRound.Instance.localPlayerController.actualClientId; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null)) { if (val.actualClientId == actualClientId || !val.isPlayerControlled || val.isPlayerDead) { _tallies.Remove(val.actualClientId); } else { EvaluatePlayer(val, spawnedEnemies, time); } } } } private static void EvaluatePlayer(PlayerControllerB player, List enemies, float now) { ulong actualClientId = player.actualClientId; for (int i = 0; i < enemies.Count; i++) { EnemyAI enemy = enemies[i]; if (CanHit(enemy, out var profile)) { long key = HitKey(actualClientId, enemy); if ((!_lastSimulatedHit.TryGetValue(key, out var value) || !(now - value < profile.Interval)) && IsTouching(enemy, player) && IsReachable(enemy, player)) { _lastSimulatedHit[key] = now; RecordSimulatedHit(player, profile, now); } } } Judge(player, now); } private static void RecordSimulatedHit(PlayerControllerB player, ContactProfile profile, float now) { ulong actualClientId = player.actualClientId; if (!_tallies.TryGetValue(actualClientId, out var value) || now - value.LastHitTime > 10f) { value = new Tally { FirstHitTime = now, LastReportTime = (value?.LastReportTime ?? float.NegativeInfinity) }; _tallies[actualClientId] = value; } value.LastEnemy = profile.Name; value.LastHitTime = now; value.UnansweredDamage += profile.Damage; } private static void Judge(PlayerControllerB player, float now) { ulong actualClientId = player.actualClientId; if (_tallies.TryGetValue(actualClientId, out var value) && value.UnansweredDamage > 0) { if (PlayerDamageState.LastAcknowledged(actualClientId) >= value.FirstHitTime) { value.UnansweredDamage = 0; } else if (!(now - value.LastHitTime < 1.5f) && value.UnansweredDamage >= PluginConfig.GodMode_EnemyHitLethalThreshold.Value && !(now - value.LastReportTime < 10f) && !PatchHelper.ChecksBypassed(player)) { value.LastReportTime = now; int unansweredDamage = value.UnansweredDamage; value.UnansweredDamage = 0; AntiCheatPlugin.LogInfo($"{player.playerUsername} was in contact with {value.LastEnemy} for {unansweredDamage} damage over " + $"{now - value.FirstHitTime:0.0}s and reported nothing."); PatchHelper.ShowMessage($"{player.playerUsername}: ignored {unansweredDamage} dmg from {value.LastEnemy}", $"contact:{actualClientId}", player, PluginConfig.GodMode); PatchHelper.Punish(player, PluginConfig.GodMode); } } } private static bool CanHit(EnemyAI enemy, out ContactProfile profile) { profile = null; if ((Object)(object)enemy == (Object)null || enemy.isEnemyDead || !enemy.ventAnimationFinished || enemy.stunNormalizedTimer >= 0f || enemy.inSpecialAnimation) { return false; } if (!_profiles.TryGetValue(((object)enemy).GetType(), out profile)) { return false; } try { return profile.Ready == null || profile.Ready(enemy); } catch (Exception ex) { AntiCheatPlugin.LogInfo("Contact profile for " + profile.Name + " could not be evaluated: " + ex.Message); return false; } } private static bool IsTouching(EnemyAI enemy, PlayerControllerB player) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008e: 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) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) Collider[] array = TriggersFor(enemy); if (array == null || array.Length == 0) { return false; } CharacterController thisController = player.thisController; if ((Object)(object)thisController == (Object)null) { return false; } Vector3 val = ((Component)player).transform.TransformPoint(thisController.center); float radius = thisController.radius; float num = Mathf.Max(0f, thisController.height * 0.5f - radius); Vector3 b = val + Vector3.up * num; Vector3 a = val - Vector3.up * num; foreach (Collider val2 in array) { if (!((Object)(object)val2 == (Object)null) && val2.enabled && ((Component)val2).gameObject.activeInHierarchy) { Vector3 point = val2.ClosestPoint(val); if (DistanceToSegment(point, a, b) <= radius) { return true; } } } return false; } private static float DistanceToSegment(Vector3 point, Vector3 a, Vector3 b) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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) Vector3 val = b - a; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude <= Mathf.Epsilon) { return Vector3.Distance(point, a); } float num = Mathf.Clamp01(Vector3.Dot(point - a, val) / sqrMagnitude); return Vector3.Distance(point, a + val * num); } private static bool IsReachable(EnemyAI enemy, PlayerControllerB player) { //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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (!enemy.PlayerIsTargetable(player, false, false, true)) { return false; } return !Physics.Linecast(((Component)enemy).transform.position + Vector3.up * 0.5f, ((Component)player).transform.position + Vector3.up * 0.5f, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1); } private static Collider[] TriggersFor(EnemyAI enemy) { int instanceID = ((Object)enemy).GetInstanceID(); if (_triggers.TryGetValue(instanceID, out var value) && value != null && value.Length != 0 && (Object)(object)value[0] != (Object)null) { return value; } EnemyAICollisionDetect[] componentsInChildren = ((Component)enemy).GetComponentsInChildren(true); List list = new List(componentsInChildren.Length); foreach (EnemyAICollisionDetect val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.mainScript != (Object)(object)enemy)) { Collider component = ((Component)val).GetComponent(); if ((Object)(object)component != (Object)null) { list.Add(component); } } } value = list.ToArray(); _triggers[instanceID] = value; return value; } private static long HitKey(ulong clientId, EnemyAI enemy) { return ((long)((Object)enemy).GetInstanceID() << 8) ^ (long)clientId; } private static bool IsTamedBlob(BlobAI blob) { if (!_blobFieldsResolved) { _blobFieldsResolved = true; _blobTamedTimer = BuildFieldRef("tamedTimer"); _blobAngeredTimer = BuildFieldRef("angeredTimer"); } if (_blobTamedTimer == null || _blobAngeredTimer == null) { return true; } return _blobTamedTimer.Invoke(blob) > 0f && _blobAngeredTimer.Invoke(blob) <= 0f; } private static bool GetBool(string field, T instance, bool fallback) where T : class { FieldRef val = FieldRef(field); return (val == null) ? fallback : val.Invoke(instance); } private static float GetFloat(string field, T instance, float fallback) where T : class { FieldRef val = FieldRef(field); return (val == null) ? fallback : val.Invoke(instance); } private static FieldRef FieldRef(string fieldName) where TInstance : class { string key = typeof(TInstance).Name + "." + fieldName; if (_fieldRefs.TryGetValue(key, out var value)) { return (FieldRef)value; } FieldRef val = BuildFieldRef(fieldName); _fieldRefs[key] = val; return val; } private static FieldRef BuildFieldRef(string fieldName) where TInstance : class { try { FieldInfo fieldInfo = AccessTools.Field(typeof(TInstance), fieldName); if (fieldInfo == null) { AntiCheatPlugin.LogInfo(typeof(TInstance).Name + "." + fieldName + " not found - that part of the enemy contact check is disabled for this session."); return null; } return AccessTools.FieldRefAccess(fieldInfo); } catch (Exception ex) { AntiCheatPlugin.LogInfo("Could not read " + typeof(TInstance).Name + "." + fieldName + " (" + ex.Message + ") - that part of the enemy contact check is disabled for this session."); return null; } } } [HarmonyPatch(typeof(StartOfRound), "StartGame")] internal static class EnemyContactResetPatch { [HarmonyPostfix] public static void ResetOnRoundStart() { EnemyContactPatch.ResetAll(); } } [HarmonyPatch] internal static class EnemyHitAttributionPatch { private class Tally { public int CumulativeDamage; public float LastHitTime; } private const int CrawlerDamage = 40; private const int SandSpiderDamage = 90; private const int HoarderBugDamage = 30; private const int PufferDamage = 20; private const int ButlerDamage = 10; private static readonly Dictionary _tallies = new Dictionary(); private static int? _lastPlayerIdArg; internal static IEnumerable TargetMethods() { MethodInfo[] candidates = new MethodInfo[5] { AccessTools.Method(typeof(CrawlerAI), "HitPlayerServerRpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(SandSpiderAI), "HitPlayerServerRpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(HoarderBugAI), "HitPlayerServerRpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(PufferAI), "BitePlayerServerRpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(ButlerEnemyAI), "StabPlayerServerRpc", (Type[])null, (Type[])null) }; MethodInfo[] array = candidates; foreach (MethodInfo method in array) { if (method != null) { yield return method; } } } internal static void ResetAll() { _tallies.Clear(); } [HarmonyPrefix] public static void Prefix(EnemyAI __instance, MethodBase __originalMethod) { if (!PluginConfig.GodMode.Value || (Object)(object)__instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.localPlayerController == (Object)null || !((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost || !RpcExecStage.IsExecutingReceivedRpc((NetworkBehaviour)(object)__instance)) { return; } int damage; PlayerControllerB val; if (__instance is CrawlerAI || __instance is SandSpiderAI || __instance is PufferAI || __instance is ButlerEnemyAI) { damage = ((__instance is CrawlerAI) ? 40 : ((__instance is SandSpiderAI) ? 90 : ((__instance is PufferAI) ? 20 : 10))); val = ResolveVictimFromArgs(__originalMethod, __instance); } else { if (!(__instance is HoarderBugAI)) { return; } damage = 30; val = FindNearestPlayer(__instance, 4f); } if (!((Object)(object)val == (Object)null) && !val.isPlayerDead && val.isPlayerControlled && !PatchHelper.IsHost(val)) { AttributeDamage(val, damage, DescribeEnemy(__instance)); } } private static PlayerControllerB ResolveVictimFromArgs(MethodBase originalMethod, EnemyAI enemy) { if (_lastPlayerIdArg.HasValue) { int value = _lastPlayerIdArg.Value; _lastPlayerIdArg = null; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (allPlayerScripts != null && value >= 0 && value < allPlayerScripts.Length) { return allPlayerScripts[value]; } } return FindNearestPlayer(enemy, 4f); } internal static void RecordPlayerIdArg(int playerId) { _lastPlayerIdArg = playerId; } private static PlayerControllerB FindNearestPlayer(EnemyAI enemy, float maxDistance) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (allPlayerScripts == null) { return null; } PlayerControllerB result = null; float num = maxDistance; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead) { float num2 = Vector3.Distance(((Component)val).transform.position, ((Component)enemy).transform.position); if (num2 < num) { num = num2; result = val; } } } return result; } private static string DescribeEnemy(EnemyAI enemy) { if ((Object)(object)enemy.enemyType != (Object)null && !string.IsNullOrEmpty(enemy.enemyType.enemyName)) { return enemy.enemyType.enemyName; } return ((object)enemy).GetType().Name; } private static void AttributeDamage(PlayerControllerB victim, int damage, string enemyName) { ulong actualClientId = victim.actualClientId; if (!_tallies.TryGetValue(actualClientId, out var value)) { value = new Tally(); _tallies[actualClientId] = value; } value.CumulativeDamage += damage; value.LastHitTime = Time.time; int num = Mathf.Max(0, 100 - value.CumulativeDamage); AntiCheatPlugin.LogInfo($"{enemyName} dealt {damage} damage to {victim.playerUsername}, health:{num}"); ExpectAcknowledgement(victim, enemyName, damage); if (value.CumulativeDamage >= PluginConfig.GodMode_EnemyHitLethalThreshold.Value) { if (victim.isPlayerDead) { _tallies.Remove(actualClientId); } else if (!PatchHelper.ChecksBypassed(victim)) { AntiCheatPlugin.LogInfo($"{victim.playerUsername} has taken {value.CumulativeDamage} damage from enemies without dying."); PatchHelper.ShowMessage($"{victim.playerUsername}: tanked {value.CumulativeDamage} enemy dmg", $"tanked:{actualClientId}", victim, PluginConfig.GodMode); PatchHelper.Punish(victim, PluginConfig.GodMode); } } } private static void ExpectAcknowledgement(PlayerControllerB victim, string enemyName, int damage) { if (PluginConfig.GodMode_EnemyHitRequireAck.Value && PlayerDamageState.DamageIsPossibleRightNow()) { ((MonoBehaviour)victim).StartCoroutine(VerifyHitAcknowledged(victim, enemyName, damage, Time.time)); } } private static IEnumerator VerifyHitAcknowledged(PlayerControllerB victim, string enemyName, int damage, float hitTime) { yield return (object)new WaitForSeconds(PluginConfig.GodMode_EnemyHitAckSeconds.Value); if (!((Object)(object)victim == (Object)null) && !victim.isPlayerDead && victim.isPlayerControlled && !(PlayerDamageState.LastAcknowledged(victim.actualClientId) >= hitTime - 1f) && !PatchHelper.ChecksBypassed(victim)) { AntiCheatPlugin.LogInfo($"{victim.playerUsername} was hit by {enemyName} for {damage} damage but reported nothing and did not die."); PatchHelper.ShowMessage($"{victim.playerUsername}: ignored {damage} dmg from {enemyName}", $"noack:{victim.actualClientId}", victim, PluginConfig.GodMode); PatchHelper.Punish(victim, PluginConfig.GodMode); } } internal static void ClearFor(ulong clientId) { _tallies.Remove(clientId); } } [HarmonyPatch] internal static class EnemyHitPlayerIdCapturePatch { internal static IEnumerable TargetMethods() { MethodInfo[] candidates = new MethodInfo[4] { AccessTools.Method(typeof(CrawlerAI), "HitPlayerServerRpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(SandSpiderAI), "HitPlayerServerRpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(PufferAI), "BitePlayerServerRpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(ButlerEnemyAI), "StabPlayerServerRpc", (Type[])null, (Type[])null) }; MethodInfo[] array = candidates; foreach (MethodInfo method in array) { if (method != null) { yield return method; } } } [HarmonyPrefix] [HarmonyPriority(800)] public static void Prefix(int __0) { EnemyHitAttributionPatch.RecordPlayerIdArg(__0); } } [HarmonyPatch] internal static class EnemyHitAttributionResetPatch { [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] public static void ResetOnRoundStart() { EnemyHitAttributionPatch.ResetAll(); } [HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")] [HarmonyPostfix] public static void ClearOnDeath(PlayerControllerB __instance) { if ((Object)(object)__instance != (Object)null) { EnemyHitAttributionPatch.ClearFor(__instance.actualClientId); } } } [HarmonyPatch(typeof(StartOfRound), "Update")] internal static class FallDamagePatch { private sealed class Fall { public bool Airborne; public float StartTime; public float StartHeight; public float LowestHeight; public Vector3 LastPosition; public float LastSampleTime; public bool HasLastPosition; public float SuppressUntil; public int UnansweredDamage; public float FirstLandingTime; public float LastLandingTime; public float LastReportTime = float.NegativeInfinity; } private const float SampleIntervalSeconds = 0.1f; private const float StartingFallValue = 7f; private const float FallAcceleration = 38f; private const float MaxPlausibleFallSeconds = 12f; private const float DiscontinuityDistance = 30f; private const float AcknowledgeSeconds = 2f; private const float TallyForgetSeconds = 20f; private const float ReportQuietSeconds = 5f; private static readonly Dictionary _falls = new Dictionary(); private static float _lastSampleTime; internal static void ResetAll() { _falls.Clear(); } [HarmonyPostfix] public static void Postfix() { if (!PluginConfig.GodMode.Value || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.localPlayerController == (Object)null || !((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost) { return; } float time = Time.time; if (time - _lastSampleTime < 0.1f) { return; } _lastSampleTime = time; PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (allPlayerScripts == null) { return; } bool judgeable = PlayerDamageState.DamageIsPossibleRightNow(); ulong actualClientId = StartOfRound.Instance.localPlayerController.actualClientId; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && val.actualClientId != actualClientId) { Sample(val, time, judgeable); } } } private static void Sample(PlayerControllerB player, float now, bool judgeable) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) ulong actualClientId = player.actualClientId; if (!_falls.TryGetValue(actualClientId, out var value)) { value = new Fall(); _falls[actualClientId] = value; } if (!player.isPlayerControlled || player.isPlayerDead) { Abandon(value, now); return; } Vector3 position = ((Component)player).transform.position; if (!value.HasLastPosition) { value.LastPosition = position; value.LastSampleTime = now; value.HasLastPosition = true; return; } float num = Vector3.Distance(value.LastPosition, position); value.LastPosition = position; value.LastSampleTime = now; if (num > 30f) { Abandon(value, now); } else if (!judgeable || NotAFreeFall(player)) { Abandon(value, now); } else { if (now < value.SuppressUntil) { return; } if (!IsTouchingGround(player)) { if (!value.Airborne) { value.Airborne = true; value.StartTime = now; value.StartHeight = position.y; value.LowestHeight = position.y; } value.LowestHeight = Mathf.Min(value.LowestHeight, position.y); } else { if (value.Airborne) { value.Airborne = false; Land(player, value, now, position.y); } Judge(player, value, now); } } } private static void Abandon(Fall fall, float now) { fall.Airborne = false; fall.HasLastPosition = false; fall.SuppressUntil = now + PluginConfig.MovementGraceSeconds.Value; } private static void Land(PlayerControllerB player, Fall fall, float now, float landingHeight) { float num = now - fall.StartTime; if (num > 12f) { return; } float num2 = fall.StartHeight - Mathf.Min(landingHeight, fall.LowestHeight); if (num2 <= 0f) { return; } float num3 = SecondsToFall(num2); if (num < num3 * 0.6f) { return; } int num4 = DamageForFallValue(FallValueAfter(num3)); if (num4 <= 0) { return; } if (PlayerDamageState.LastAcknowledged(player.actualClientId) >= fall.StartTime) { fall.UnansweredDamage = 0; return; } if (fall.UnansweredDamage <= 0 || now - fall.LastLandingTime > 20f) { fall.UnansweredDamage = 0; fall.FirstLandingTime = now; } fall.UnansweredDamage += num4; fall.LastLandingTime = now; AntiCheatPlugin.LogInfo($"{player.playerUsername} fell {num2:0.0}u in {num:0.00}s - vanilla charges {num4}."); } private static float FallValueAfter(float seconds) { return 0f - (7f + 38f * seconds); } private static float SecondsToFall(float drop) { float num = 19f; return (-7f + Mathf.Sqrt(49f + 4f * num * drop)) / (2f * num); } private static int DamageForFallValue(float fallValue) { if (fallValue < -48.5f) { return 100; } if (fallValue < -45f) { return 80; } if (fallValue < -40f) { return 50; } if (fallValue < -38f) { return 30; } return 0; } private static void Judge(PlayerControllerB player, Fall fall, float now) { if (fall.UnansweredDamage <= 0) { return; } ulong actualClientId = player.actualClientId; if (PlayerDamageState.LastAcknowledged(actualClientId) >= fall.FirstLandingTime) { fall.UnansweredDamage = 0; } else if (!(now - fall.LastLandingTime < 2f)) { int unansweredDamage = fall.UnansweredDamage; if (!(now - fall.LastReportTime < 5f) && !PatchHelper.ChecksBypassed(player)) { fall.LastReportTime = now; fall.UnansweredDamage = 0; AntiCheatPlugin.LogInfo($"{player.playerUsername} landed owing {unansweredDamage} fall damage and reported nothing."); PatchHelper.ShowMessage($"{player.playerUsername}: ignored {unansweredDamage} fall dmg", $"fall:{actualClientId}", player, PluginConfig.GodMode); PatchHelper.Punish(player, PluginConfig.GodMode); } } } private static bool NotAFreeFall(PlayerControllerB player) { if (player.inVehicleAnimation || player.isClimbingLadder || player.inSpecialInteractAnimation || (Object)(object)player.inAnimationWithEnemy != (Object)null || (Object)(object)player.physicsParent != (Object)null || (Object)(object)player.overridePhysicsParent != (Object)null || player.isInElevator || player.isInHangarShipRoom) { return true; } if (player.isSinking || player.sinkingValue > 0f || player.isUnderwater) { return true; } return IsHoldingJetpack(player); } private static bool IsHoldingJetpack(PlayerControllerB player) { GrabbableObject[] itemSlots = player.ItemSlots; if (itemSlots == null) { return false; } for (int i = 0; i < itemSlots.Length; i++) { if (itemSlots[i] is JetpackItem) { return true; } } return false; } private static bool IsTouchingGround(PlayerControllerB player) { return PlayerPositionState.IsOnGround(player); } } [HarmonyPatch(typeof(StartOfRound), "StartGame")] internal static class FallDamageResetPatch { [HarmonyPostfix] public static void ResetOnRoundStart() { FallDamagePatch.ResetAll(); } } [HarmonyPatch(typeof(PlayerControllerB))] [HarmonyWrapSafe] public static class FriendlyFirePatch { private static readonly Dictionary _lastLogged = new Dictionary(); private const double LogIntervalSeconds = 3.0; [HarmonyPatch("DamagePlayerFromOtherClientServerRpc")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool BlockFriendlyFire(PlayerControllerB __instance, int damageAmount, Vector3 hitDirection, int playerWhoHit) { if (PluginConfig.Restrict_FriendlyFire.Value == PluginConfig.FriendlyFirePolicy.Allowed) { return true; } if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer) { return true; } PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null); if ((Object)(object)val == (Object)null || playerWhoHit != (int)val.playerClientId) { return true; } LogThrottled(__instance, damageAmount, playerWhoHit); return false; } internal static bool AllowRemote(PlayerControllerB victim, ulong senderClientId, int damageAmount, int playerWhoHit) { PluginConfig.PermissionLevel level = PluginConfig.PermissionLevel.HostOnly; if (PluginConfig.Restrict_FriendlyFire.Value == PluginConfig.FriendlyFirePolicy.Allowed || Profiles.AllowsGriefRule(senderClientId, "FriendlyFire")) { return true; } LogThrottled(victim, damageAmount, playerWhoHit); AntiGrief.HandleViolation(senderClientId, level); return false; } private static void LogThrottled(PlayerControllerB victim, int damageAmount, int playerWhoHit) { ulong key = (ulong)Math.Max(playerWhoHit, 0); if (!_lastLogged.TryGetValue(key, out var value) || !((DateTime.UtcNow - value).TotalSeconds < 3.0)) { _lastLogged[key] = DateTime.UtcNow; string text = PatchHelper.PlayerClientIdConvertName(playerWhoHit); string text2 = (((Object)(object)victim != (Object)null) ? victim.playerUsername : "unknown"); AntiCheatPlugin.LogInfo($"[FriendlyFire] Blocked {damageAmount} damage from {text}({playerWhoHit}) to {text2}."); } } } [HarmonyPatch(typeof(JesterAI))] [HarmonyWrapSafe] public static class JesterAIPatch { [HarmonyPatch("__rpc_handler_3446243450")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_3446243450(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) return PatchHelper.KillPlayerServerRpc(target, reader, rpcParams, "JesterAI.KillPlayerServerRpc"); } } [HarmonyPatch] internal static class KillOrderPatch { private static readonly HashSet _pending = new HashSet(); internal static IEnumerable TargetMethods() { MethodInfo[] candidates = new MethodInfo[6] { AccessTools.Method(typeof(MouthDogAI), "KillPlayerClientRpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(JesterAI), "KillPlayerClientRpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(FlowermanAI), "KillPlayerAnimationClientRpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(MaskedPlayerEnemy), "KillPlayerAnimationClientRpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(CaveDwellerAI), "KillPlayerAnimationClientRpc", (Type[])null, (Type[])null), AccessTools.Method(typeof(RedLocustBees), "BeeKillPlayerClientRpc", (Type[])null, (Type[])null) }; MethodInfo[] array = candidates; foreach (MethodInfo method in array) { if (method != null) { yield return method; } } } [HarmonyPrefix] public static void Prefix(EnemyAI __instance, int __0) { if (!PluginConfig.GodMode.Value || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.localPlayerController == (Object)null || !((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost || !RpcExecStage.IsExecutingReceivedRpc((NetworkBehaviour)(object)__instance)) { return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (allPlayerScripts == null || __0 < 0 || __0 >= allPlayerScripts.Length) { return; } PlayerControllerB val = allPlayerScripts[__0]; if (!((Object)(object)val == (Object)null) && !val.isPlayerDead && val.isPlayerControlled && !PatchHelper.IsHost(val)) { string text = (((Object)(object)__instance != (Object)null && (Object)(object)__instance.enemyType != (Object)null) ? __instance.enemyType.enemyName : (((Object)(object)__instance != (Object)null) ? ((object)__instance).GetType().Name : "Unknown")); AntiCheatPlugin.LogInfo(text + " ordered a kill on " + val.playerUsername); if (!_pending.Contains(val.actualClientId)) { _pending.Add(val.actualClientId); ((MonoBehaviour)val).StartCoroutine(VerifyKillHonored(val, __instance, text, Time.time)); } } } private static IEnumerator VerifyKillHonored(PlayerControllerB victim, EnemyAI enemy, string enemyName, float orderedAt) { yield return (object)new WaitForSeconds(PluginConfig.GodMode_InstakillGraceSeconds.Value); if ((Object)(object)victim == (Object)null) { yield break; } _pending.Remove(victim.actualClientId); if (!victim.isPlayerDead && victim.isPlayerControlled && !((Object)(object)StartOfRound.Instance == (Object)null) && !StartOfRound.Instance.inShipPhase) { if (EnemyInterruptState.InterruptedSince(enemy, orderedAt)) { AntiCheatPlugin.LogInfo(enemyName + " was interrupted before it killed " + victim.playerUsername + " - not a suppressed death."); } else if (!PatchHelper.ChecksBypassed(victim)) { AntiCheatPlugin.LogInfo(victim.playerUsername + " did not die after " + enemyName + " killed them."); PatchHelper.ShowMessage(victim.playerUsername + ": survived " + enemyName, $"killorder:{victim.actualClientId}", victim, PluginConfig.GodMode); PatchHelper.Punish(victim, PluginConfig.GodMode); } } } internal static void ResetAll() { _pending.Clear(); } } [HarmonyPatch(typeof(StartOfRound), "StartGame")] internal static class KillOrderResetPatch { [HarmonyPostfix] public static void ResetOnRoundStart() { KillOrderPatch.ResetAll(); } } [HarmonyPatch(typeof(NutcrackerEnemyAI))] [HarmonyWrapSafe] public static class NutcrackerEnemyAIPatch { [HarmonyPatch("__rpc_handler_3881699224")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_3881699224(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) return PatchHelper.KillPlayerServerRpc(target, reader, rpcParams, "NutcrackerEnemyAI.LegKickPlayerServerRpc"); } } [HarmonyPatch(typeof(PlayerControllerB))] [HarmonyWrapSafe] public static class PlayerControllerCombatPatch { [HarmonyPatch("__rpc_handler_1583426973")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool KillPlayerServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PatchHelper.rpcs.ContainsKey("KillPlayer")) { PatchHelper.rpcs["KillPlayer"].Remove(p.playerClientId); } int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); Vector3 val = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val); int num2 = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num2); ((FastBufferReader)(ref reader)).Seek(0); PatchHelper.LogInfo(p, "PlayerControllerB.KillPlayerServerRpc", $"playerId:{PatchHelper.PlayerClientIdConvertName(num)}({num})", $"spawnBody:{flag}", $"bodyVelocity:{val}", $"num:{(object)(CauseOfDeath)num2}({num2})"); if (num < 0) { PatchHelper.LogInfo($"KillPlayerServerRpc:Invalid PlayerId({num})"); return false; } if ((Object)(object)StartOfRound.Instance.allPlayerScripts[num] != (Object)(object)p) { PatchHelper.LogInfo("KillPlayerServerRpc:Can't kill other player!"); return false; } if (p.isPlayerDead) { PatchHelper.LogInfo("KillPlayerServerRpc:Player death can't kill!"); return false; } if (num2 == 10) { string text = PatchHelper.locale.Msg_GetString("behind_player", new Dictionary { { "{player}", p.playerUsername } }); PatchHelper.LogInfo(text); PatchHelper.AddTextMessageClientRpc(text); } } else if ((Object)(object)p == (Object)null) { return false; } return true; } [HarmonyPatch("__rpc_handler_638895557")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool DamagePlayerFromOtherClientServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { int damageAmount = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref damageAmount); Vector3 val = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val); int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); PatchHelper.LogInfo(p, "PlayerControllerB.DamagePlayerFromOtherClientServerRpc", $"damageAmount:{damageAmount}", $"hitDirection:{val}", $"playerWhoHit:{PatchHelper.PlayerClientIdConvertName(num)}({num})"); PlayerControllerB val2 = (PlayerControllerB)target; if (!FriendlyFirePatch.AllowRemote(val2, rpcParams.Server.Receive.SenderClientId, damageAmount, num)) { return false; } return PatchHelper.CheckDamage(val2, p, ref damageAmount); } if ((Object)(object)p == (Object)null) { return false; } return true; } [HarmonyPatch("__rpc_handler_2585603452")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool HealServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { PatchHelper.LogInfo(p, "PlayerControllerB.HealServerRpc", $"health:{p.health}", "newHealth:20"); p.health = 20; PlayerDamageState.Set(p.actualClientId, 20); } else if ((Object)(object)p == (Object)null) { return false; } return true; } [HarmonyPatch("DamagePlayer")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool DamagePlayer(PlayerControllerB __instance, int damageNumber, bool hasDamageSFX = true, bool callRPC = true, CauseOfDeath causeOfDeath = (CauseOfDeath)0, int deathAnimation = 0, bool fallDamage = false, Vector3 force = default(Vector3)) { return true; } [HarmonyPatch("__rpc_handler_1084949295")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool DamagePlayerServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown if (PatchHelper.Check(rpcParams, out var p)) { if (PatchHelper.rpcs.ContainsKey("Hit")) { PatchHelper.rpcs["Hit"].Remove(p.playerClientId); } int damageAmount = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref damageAmount); int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); PatchHelper.LogInfo(p, "PlayerControllerB.DamagePlayerServerRpc", $"damageNumber:{damageAmount}", $"newHealthAmount:{num}"); PlayerControllerB val = (PlayerControllerB)target; if ((Object)(object)val == (Object)(object)p) { if (PluginConfig.GodMode.Value && damageAmount < 0) { string msg = PatchHelper.locale.Msg_GetString("Health_Recover", new Dictionary { { "{player}", p.playerUsername }, { "{hp}", (damageAmount * -1).ToString() } }); PatchHelper.ShowMessage(msg, p, PluginConfig.GodMode); PatchHelper.Punish(p, PluginConfig.GodMode); return false; } return true; } return PatchHelper.CheckDamage(val, p, ref damageAmount); } if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(RedLocustBees))] [HarmonyWrapSafe] public static class RedLocustBeesPatch { [HarmonyPatch("__rpc_handler_3246315153")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_3246315153(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) return PatchHelper.KillPlayerServerRpc(target, reader, rpcParams, "RedLocustBees.BeeKillPlayerServerRpc"); } } [HarmonyPatch(typeof(PlayerControllerB))] [HarmonyWrapSafe] public static class PlayerControllerMovementPatch { private const float TeleportDistance = 12f; [HarmonyPatch(typeof(PlayerControllerB), "__rpc_handler_890924887")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool UpdatePlayerPositionRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) Vector3 val = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val); ((FastBufferReader)(ref reader)).Seek(0); if (PatchHelper.Check(rpcParams, out var p)) { if ((Object)(object)StartOfRound.Instance.currentLevel != (Object)null && StartOfRound.Instance.currentLevel.spawnEnemiesAndScrap) { ulong key = PatchHelper.IdOf(p); if (!PatchHelper.recentPlayerPositions.ContainsKey(key)) { PatchHelper.recentPlayerPositions[key] = new List<(Vector3, float)>(); } float sampleTime = Time.time; PatchHelper.recentPlayerPositions[key] = PatchHelper.recentPlayerPositions[key].Where(((Vector3 pos, float time) entry) => sampleTime - entry.time <= 5f).ToList(); PatchHelper.recentPlayerPositions[key].Add((val, sampleTime)); } if (PluginConfig.Teleport.Value && IsUnexplainedJump(p, val)) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("Teleport", new Dictionary { { "{player}", p.playerUsername }, { "{distance}", Vector3.Distance(p.serverPlayerPosition, val).ToString("0") } }), $"teleport:{PatchHelper.IdOf(p)}", p, PluginConfig.Teleport); PatchHelper.Punish(p, PluginConfig.Teleport); return false; } } else if ((Object)(object)p == (Object)null) { return false; } return true; } private static bool IsUnexplainedJump(PlayerControllerB player, Vector3 newPos) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) Vector3 serverPlayerPosition = player.serverPlayerPosition; if (serverPlayerPosition == Vector3.zero || newPos == Vector3.zero) { return false; } float num = Vector3.Distance(serverPlayerPosition, newPos); if (num < 12f) { return false; } if (TeleportWatch.IsExpected(player.actualClientId)) { return false; } PlayerPositionState.Report report = PlayerPositionState.For(player.actualClientId); if (report == null || !report.Any || report.InShip != player.isInHangarShipRoom || report.InElevator != player.isInElevator) { return false; } if (player.isPlayerDead || !player.isPlayerControlled || player.inSpecialInteractAnimation || (Object)(object)player.inAnimationWithEnemy != (Object)null || (Object)(object)player.physicsParent != (Object)null || (Object)(object)player.overridePhysicsParent != (Object)null || player.isInElevator || player.isInHangarShipRoom || StartOfRound.Instance.inShipPhase || StartOfRound.Instance.shipIsLeaving || !StartOfRound.Instance.shipHasLanded) { return false; } AntiCheatPlugin.LogInfo($"{player.playerUsername} moved {num:0.0}u in one report with no teleport to explain it."); return true; } } [HarmonyPatch(typeof(StartOfRound), "Update")] internal static class SpeedHackPatch { private class Track { public Vector3 LastPosition; public float LastSampleTime; public bool HasLastPosition; public readonly Queue RecentSpeeds = new Queue(); public int ConsecutiveBursts; public float LastLogTime; public float SuppressUntil; public bool WasDead; public int ConsecutiveImplausible; public int ConsecutiveAbsolute; } private static readonly Dictionary _tracks = new Dictionary(); private const float SampleIntervalSeconds = 0.2f; private const float VehicleRescanSeconds = 5f; private static VehicleController[] _vehicles = (VehicleController[])(object)new VehicleController[0]; private static float _vehiclesScannedAt = float.NegativeInfinity; private static bool _loggedFirstSample; private static float _lastDiagnosticTime; private static float _judgingSuppressedUntil; private static int ImplausibleRunToReport() { return Mathf.Max(2, PluginConfig.SpeedHack_MinConsecutiveReadings.Value); } internal static void ResetAll() { _tracks.Clear(); _vehicles = (VehicleController[])(object)new VehicleController[0]; _vehiclesScannedAt = float.NegativeInfinity; } internal static void SuppressJudging() { _judgingSuppressedUntil = Time.time + PluginConfig.MovementGraceSeconds.Value; } [HarmonyPostfix] public static void Postfix() { bool flag = PluginConfig.MovementDiagnostics.Value && Time.time - _lastDiagnosticTime >= 15f; if (flag) { _lastDiagnosticTime = Time.time; string text = $"[movement diag] hook running. checksEnabled:{PluginConfig.SpeedHack.Value} "; StartOfRound instance = StartOfRound.Instance; bool? obj; if (instance == null) { obj = null; } else { PlayerControllerB localPlayerController = instance.localPlayerController; obj = ((localPlayerController != null) ? new bool?(((NetworkBehaviour)localPlayerController).IsHost) : ((bool?)null)); } AntiCheatPlugin.LogInfo(text + $"isHost:{obj} " + $"players:{StartOfRound.Instance?.allPlayerScripts?.Length ?? (-1)} " + $"tracked:{_tracks.Count}"); } if (!PluginConfig.SpeedHack.Value || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.localPlayerController == (Object)null || !((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost) { return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; if (allPlayerScripts != null) { if (!RoundIsJudgeable()) { _judgingSuppressedUntil = Time.time + PluginConfig.MovementGraceSeconds.Value; } for (int i = 0; i < allPlayerScripts.Length; i++) { SamplePlayer(allPlayerScripts[i], flag); } } } private static bool RoundIsJudgeable() { StartOfRound instance = StartOfRound.Instance; return (Object)(object)instance != (Object)null && instance.shipHasLanded && !instance.shipIsLeaving && !instance.inShipPhase; } private static void SuppressPlayer(Track track, float now) { track.SuppressUntil = now + PluginConfig.MovementGraceSeconds.Value; SoftReset(track); } private static void SamplePlayer(PlayerControllerB __instance, bool diagnose) { //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null) { return; } if (diagnose && (!__instance.isPlayerControlled || __instance.isPlayerDead) && __instance.actualClientId != StartOfRound.Instance.localPlayerController.actualClientId) { AntiCheatPlugin.LogInfo("[movement diag] skipping " + __instance.playerUsername + " " + $"controlled:{__instance.isPlayerControlled} dead:{__instance.isPlayerDead}"); } if (__instance.actualClientId == StartOfRound.Instance.localPlayerController.actualClientId) { return; } float time = Time.time; ulong actualClientId = __instance.actualClientId; if (!_tracks.TryGetValue(actualClientId, out var value)) { value = new Track(); _tracks[actualClientId] = value; } bool flag = !__instance.isPlayerControlled || __instance.isPlayerDead; if (flag != value.WasDead) { value.WasDead = flag; value.HasLastPosition = false; SuppressPlayer(value, time); } if (flag || (value.HasLastPosition && time - value.LastSampleTime < 0.2f)) { return; } Vector3 position = ((Component)__instance).transform.position; if (!value.HasLastPosition) { value.LastPosition = position; value.LastSampleTime = time; value.HasLastPosition = true; return; } float num = time - value.LastSampleTime; float num2 = Vector3.Distance(value.LastPosition, position); value.LastPosition = position; value.LastSampleTime = time; if (num <= 0f) { return; } string text = MovementNotUnderOwnPower(__instance); if (text != null) { if (diagnose) { AntiCheatPlugin.LogInfo("[movement diag] not judging " + __instance.playerUsername + ": " + text); } SuppressPlayer(value, time); return; } if (num2 > 30f) { SuppressPlayer(value, time); return; } float num3 = num2 / num; if (num3 > PluginConfig.MaxPlausibleSpeed.Value) { value.ConsecutiveImplausible++; if (IsFlyingAJetpack(__instance)) { value.ConsecutiveImplausible = 0; SuppressPlayer(value, time); return; } if (time >= _judgingSuppressedUntil && value.ConsecutiveImplausible >= ImplausibleRunToReport()) { value.ConsecutiveImplausible = 0; Report(__instance, $"{__instance.playerUsername}: {num3:0.0}u/s sustained"); } SoftReset(value); } else { value.ConsecutiveImplausible = 0; if (!_loggedFirstSample) { _loggedFirstSample = true; AntiCheatPlugin.LogInfo($"movement monitoring active - first sample from {__instance.playerUsername} at {num3:0.0}u/s"); } if (!(time < value.SuppressUntil) && !(time < _judgingSuppressedUntil) && !(num3 < PluginConfig.SpeedHack_MinMovingSpeed.Value) && IsTouchingGround(__instance)) { EvaluateSample(__instance, value, num3, time); } } } private static string MovementNotUnderOwnPower(PlayerControllerB player) { if (player.inVehicleAnimation) { return "riding the cruiser"; } if (IsInVehicle(player)) { return "in the cruiser"; } if (player.isClimbingLadder) { return "on a ladder"; } if (player.inSpecialInteractAnimation) { return "in a special animation"; } if ((Object)(object)player.inAnimationWithEnemy != (Object)null) { return "being held by an enemy"; } if ((Object)(object)player.physicsParent != (Object)null || (Object)(object)player.overridePhysicsParent != (Object)null) { return "standing on something that is moving"; } if (player.isInElevator || player.isInHangarShipRoom) { return "inside the ship or elevator"; } PlayerPositionState.Report report = PlayerPositionState.For(player.actualClientId); if (report != null && report.Any && (report.InElevator || report.InShip)) { return "inside the ship or elevator"; } return null; } private static bool IsFlyingAJetpack(PlayerControllerB player) { return !IsTouchingGround(player) && IsHoldingJetpack(player); } private static bool IsHoldingJetpack(PlayerControllerB player) { GrabbableObject[] itemSlots = player.ItemSlots; if (itemSlots == null) { return false; } for (int i = 0; i < itemSlots.Length; i++) { if (itemSlots[i] is JetpackItem) { return true; } } return false; } private static bool IsInVehicle(PlayerControllerB player) { if (Time.time - _vehiclesScannedAt >= 5f) { _vehiclesScannedAt = Time.time; _vehicles = Object.FindObjectsByType((FindObjectsSortMode)0); } for (int i = 0; i < _vehicles.Length; i++) { VehicleController val = _vehicles[i]; if (!((Object)(object)val == (Object)null) && ((Object)(object)val.currentDriver == (Object)(object)player || (Object)(object)val.currentPassenger == (Object)(object)player)) { return true; } } return false; } private static bool IsTouchingGround(PlayerControllerB player) { return PlayerPositionState.IsOnGround(player); } private static void SoftReset(Track track) { track.RecentSpeeds.Clear(); track.ConsecutiveBursts = 0; track.ConsecutiveAbsolute = 0; } private static void EvaluateSample(PlayerControllerB player, Track track, float speed, float now) { EvaluateAbsoluteSpeed(player, track, speed); track.RecentSpeeds.Enqueue(speed); int num = Mathf.Max(3, Mathf.RoundToInt(PluginConfig.PositionSampleWindowSeconds.Value / 0.2f)); while (track.RecentSpeeds.Count > num) { track.RecentSpeeds.Dequeue(); } EvaluateRawSpeedBurst(player, track, speed); if (PluginConfig.MovementDiagnostics.Value && now - track.LastLogTime >= 1f) { track.LastLogTime = now; AntiCheatPlugin.LogInfo($"{player.playerUsername} speed:{speed:0.0}u/s baseline:{AverageBaseline(track):0.0}u/s"); } } private static float AverageBaseline(Track track) { if (track.RecentSpeeds.Count == 0) { return 0f; } float num = 0f; foreach (float recentSpeed in track.RecentSpeeds) { num += recentSpeed; } return num / (float)track.RecentSpeeds.Count; } private static void EvaluateAbsoluteSpeed(PlayerControllerB player, Track track, float speed) { if (speed <= PluginConfig.SpeedHack_MinAbsoluteSpeed.Value) { track.ConsecutiveAbsolute = 0; return; } track.ConsecutiveAbsolute++; if (track.ConsecutiveAbsolute >= Mathf.Max(2, PluginConfig.SpeedHack_MinConsecutiveReadings.Value)) { track.ConsecutiveAbsolute = 0; track.ConsecutiveBursts = 0; Report(player, $"{player.playerUsername}: {speed:0.0}u/s on foot"); } } private static void EvaluateRawSpeedBurst(PlayerControllerB player, Track track, float speed) { if (track.RecentSpeeds.Count < 3) { return; } float num = Mathf.Max(AverageBaseline(track), 1.5f); if (speed > num * PluginConfig.SpeedHack_MaxMultiplier.Value && speed > PluginConfig.SpeedHack_MinAbsoluteSpeed.Value) { track.ConsecutiveBursts++; if (track.ConsecutiveBursts >= Mathf.Max(2, PluginConfig.SpeedHack_MinConsecutiveReadings.Value)) { track.ConsecutiveBursts = 0; Report(player, $"{player.playerUsername}: {speed:0.0}u/s, {speed / num:0.0}x baseline"); } } else { track.ConsecutiveBursts = 0; } } private static void Report(PlayerControllerB player, string message) { if (!PatchHelper.ChecksBypassed(player)) { AntiCheatPlugin.LogInfo(message); PatchHelper.ShowMessage(message, $"speed:{player.actualClientId}", player, PluginConfig.SpeedHack); PatchHelper.Punish(player, PluginConfig.SpeedHack); } } } [HarmonyPatch(typeof(StartOfRound), "StartGame")] internal static class SpeedHackResetPatch { [HarmonyPostfix] public static void ResetOnRoundStart() { SpeedHackPatch.ResetAll(); SpeedHackPatch.SuppressJudging(); } } [HarmonyPatch(typeof(HUDManager))] [HarmonyWrapSafe] public static class HUDManagerRoundSyncPatch { public static List SyncAllPlayerLevelsServerRpcCalls { get; set; } = new List(); [HarmonyPrefix] [HarmonyPatch("__rpc_handler_4217433937")] public static bool SyncAllPlayerLevelsServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (!PatchHelper.Check(rpcParams, out var p)) { return (Object)(object)p != (Object)null; } if (SyncAllPlayerLevelsServerRpcCalls.Contains(PatchHelper.IdOf(p))) { return false; } int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); int num2 = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num2); ((FastBufferReader)(ref reader)).Seek(0); if (num2 != (int)p.playerClientId) { return false; } SyncAllPlayerLevelsServerRpcCalls.Add(PatchHelper.IdOf(p)); return true; } } [HarmonyPatch(typeof(RoundManager))] [HarmonyWrapSafe] public static class RoundManagerPatch { public static List CallFinishedGeneratingLevelServerRpc { get; set; } = new List(); [HarmonyPrefix] [HarmonyPatch("__rpc_handler_192551691")] public static bool FinishedGeneratingLevelServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { int num = RoundManager.Instance.playersFinishedGeneratingFloor.Count + 1; AntiCheatPlugin.LogInfo(p, "RoundManager.FinishedGeneratingLevelServerRpc", $"playersFinishedGeneratingFloor:{num}", $"connectedPlayers:{GameNetworkManager.Instance.connectedPlayers}"); CallFinishedGeneratingLevelServerRpc.Add(PatchHelper.IdOf(p)); if (num < GameNetworkManager.Instance.connectedPlayers && num + 5 > GameNetworkManager.Instance.connectedPlayers) { StringBuilder stringBuilder = new StringBuilder(); PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (val.isPlayerControlled && !val.isHostPlayerObject && !CallFinishedGeneratingLevelServerRpc.Contains(PatchHelper.IdOf(val))) { stringBuilder.Append(val.playerUsername + "||"); } } if (!string.IsNullOrWhiteSpace(stringBuilder.ToString())) { AntiCheatPlugin.LogInfo("FinishedGeneratingLevelServerRpc Wait For:" + stringBuilder.ToString()); } } if (num == GameNetworkManager.Instance.connectedPlayers) { AntiCheatPlugin.LogInfo("FinishedGeneratingLevelServerRpc All Players Loaded"); CallFinishedGeneratingLevelServerRpc = new List(); } } return true; } } [HarmonyPatch(typeof(StartOfRound))] [HarmonyWrapSafe] public static class StartOfRoundPatch { public static List SyncShipUnlockablesServerRpcCalls = new List(); public static List SyncAlreadyHeldObjectsServerRpcCalls = new List(); public static List CallPlayerHasRevivedServerRpc { get; set; } = new List(); public static List CallPlayerLoadedServerRpc { get; set; } = new List(); [HarmonyPrefix] [HarmonyPatch("__rpc_handler_682230258")] public static bool SyncAlreadyHeldObjectsServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!PatchHelper.ConnectionIdtoSteamIdMap.TryGetValue(PatchHelper.ClientIdToTransportId(rpcParams.Server.Receive.SenderClientId), out var value)) { AntiCheatPlugin.LogInfo($"StartOfRound.SyncAlreadyHeldObjectsServerRpc: no Steam ID for client {rpcParams.Server.Receive.SenderClientId} - letting it through."); return true; } AntiCheatPlugin.LogInfo($"StartOfRound.SyncShipUnlockablesServerRpc:{value}|32"); if (SyncAlreadyHeldObjectsServerRpcCalls.Contains(value)) { AntiCheatPlugin.LogInfo($"StartOfRound.SyncShipUnlockablesServerRpc:{value}|36"); return false; } AntiCheatPlugin.LogInfo($"StartOfRound.SyncShipUnlockablesServerRpc:{value}|39"); SyncAlreadyHeldObjectsServerRpcCalls.Add(value); return true; } [HarmonyPrefix] [HarmonyPatch("__rpc_handler_3083945322")] public static bool PlayerHasRevivedServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { int num = (int)AccessTools.DeclaredField(typeof(StartOfRound), "playersRevived").GetValue(StartOfRound.Instance); AntiCheatPlugin.LogInfo(p, "StartOfRound.PlayerHasRevivedServerRpc", $"playersRevived:{num + 1}", $"connectedPlayers:{GameNetworkManager.Instance.connectedPlayers}"); CallPlayerHasRevivedServerRpc.Add(PatchHelper.IdOf(p)); if (num < GameNetworkManager.Instance.connectedPlayers && num + 5 > GameNetworkManager.Instance.connectedPlayers) { StringBuilder stringBuilder = new StringBuilder(); PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (val.isPlayerControlled && !val.isHostPlayerObject && !CallPlayerHasRevivedServerRpc.Contains(PatchHelper.IdOf(val))) { stringBuilder.Append(val.playerUsername + "||"); } } if (!string.IsNullOrWhiteSpace(stringBuilder.ToString())) { AntiCheatPlugin.LogInfo("PlayerHasRevivedServerRpc Wait For:" + stringBuilder.ToString()); } } if (num + 1 == GameNetworkManager.Instance.connectedPlayers) { AntiCheatPlugin.LogInfo("PlayerHasRevivedServerRpc All Players Loaded"); CallPlayerHasRevivedServerRpc = new List(); } } return true; } [HarmonyPostfix] [HarmonyPatch("StartGame")] public static void StartGame() { LogFile.Reset(); } [HarmonyPrefix] [HarmonyPatch("__rpc_handler_4249638645")] public static bool PlayerLoadedServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { int count = StartOfRound.Instance.fullyLoadedPlayers.Count; AntiCheatPlugin.LogInfo(p, "StartOfRound.PlayerLoadedServerRpc", $"fullyLoadedPlayers:{count + 1}", $"connectedPlayers:{GameNetworkManager.Instance.connectedPlayers}"); CallPlayerLoadedServerRpc.Add(PatchHelper.IdOf(p)); if (count < GameNetworkManager.Instance.connectedPlayers && count + 5 > GameNetworkManager.Instance.connectedPlayers) { StringBuilder stringBuilder = new StringBuilder(); PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (val.isPlayerControlled && !val.isHostPlayerObject && !CallPlayerLoadedServerRpc.Contains(PatchHelper.IdOf(val))) { stringBuilder.Append(val.playerUsername + "||"); } } if (!string.IsNullOrWhiteSpace(stringBuilder.ToString())) { AntiCheatPlugin.LogInfo("PlayerLoadedServerRpc Wait For:" + stringBuilder.ToString()); } } if (count + 1 == GameNetworkManager.Instance.connectedPlayers) { CallPlayerLoadedServerRpc = new List(); AntiCheatPlugin.LogInfo("PlayerLoadedServerRpc All Players Loaded"); } } return true; } [HarmonyPrefix] [HarmonyPatch("__rpc_handler_744998938")] public static bool __rpc_handler_744998938(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { AntiCheatPlugin.LogInfo(p, "StartOfRound.SyncShipUnlockablesServerRpc", "140"); if (SyncShipUnlockablesServerRpcCalls.Contains(PatchHelper.IdOf(p))) { AntiCheatPlugin.LogInfo(p, "StartOfRound.SyncShipUnlockablesServerRpc", "144"); return false; } AntiCheatPlugin.LogInfo(p, "StartOfRound.SyncShipUnlockablesServerRpc", "146"); SyncShipUnlockablesServerRpcCalls.Add(PatchHelper.IdOf(p)); return true; } if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(PlayerControllerB))] [HarmonyWrapSafe] public static class PlayerControllerSessionPatch { [HarmonyPostfix] [HarmonyPatch("ConnectClientToPlayerObject")] public static void ConnectClientToPlayerObject(PlayerControllerB __instance) { if (__instance.isHostPlayerObject && ((NetworkBehaviour)StartOfRound.Instance).IsHost) { LogFile.Reset(); PatchHelper.explosions = new List(); PatchHelper.ConnectionIdtoSteamIdMap = new Dictionary(); HUDManagerRoundSyncPatch.SyncAllPlayerLevelsServerRpcCalls = new List(); StartOfRoundPatch.SyncShipUnlockablesServerRpcCalls = new List(); StartOfRoundPatch.SyncAlreadyHeldObjectsServerRpcCalls = new List(); } } } [HarmonyPatch(typeof(RoundManager))] [HarmonyWrapSafe] public static class RoundManagerSessionPatch { [HarmonyPatch("LoadNewLevel")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool LoadNewLevel(SelectableLevel newLevel) { if (!((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost) { return true; } PatchHelper.landMines = new List(); PatchHelper.bypassHit = new List(); PatchHelper.bypassKill = new List(); HUDManager.Instance.AddTextToChatOnServer(PatchHelper.locale.Msg_GetString("game_start", new Dictionary { { "{ver}", "0.8.9" } }), -1); return true; } } [HarmonyPatch(typeof(StartOfRound))] [HarmonyWrapSafe] public static class StartOfRoundSessionPatch { [HarmonyPatch("EndOfGame")] [HarmonyPostfix] [HarmonyWrapSafe] public static void EndOfGame() { PatchHelper.jcs = new List(); if (PatchHelper.rpcs.ContainsKey("Hit")) { PatchHelper.rpcs["Hit"] = new List(); } if (PatchHelper.rpcs.ContainsKey("KillPlayer")) { PatchHelper.rpcs["KillPlayer"] = new List(); } } } [HarmonyPatch(typeof(HUDManager))] [HarmonyWrapSafe] public static class HUDManagerShipFlowPatch { [HarmonyPatch("Update")] [HarmonyPostfix] [HarmonyWrapSafe] public static void Update() { if (((NetworkBehaviour)StartOfRound.Instance).IsHost && !((Object)(object)GameNetworkManager.Instance == (Object)null) && !((Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) && !StartOfRound.Instance.shipIsLeaving && StartOfRound.Instance.currentLevel.planetHasTime && PluginConfig.ShipSetting_OnlyOneVote.Value && !TimeOfDay.Instance.shipLeavingAlertCalled && GameNetworkManager.Instance.localPlayerController.isPlayerDead && !string.IsNullOrEmpty(((TMP_Text)HUDManager.Instance.holdButtonToEndGameEarlyVotesText).text)) { TextMeshProUGUI holdButtonToEndGameEarlyVotesText = HUDManager.Instance.holdButtonToEndGameEarlyVotesText; ((TMP_Text)holdButtonToEndGameEarlyVotesText).text = ((TMP_Text)holdButtonToEndGameEarlyVotesText).text + Environment.NewLine + PatchHelper.locale.Msg_GetString("vote"); } } } [HarmonyPatch(typeof(StartOfRound))] [HarmonyWrapSafe] public static class StartOfRoundShipFlowPatch { [HarmonyPatch("__rpc_handler_1089447320")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool StartGameServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) StartMatchLever val = Object.FindObjectOfType(); if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.ShipConfig && !GameNetworkManager.Instance.gameHasStarted) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("ShipConfig5", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.Ship); val.triggerScript.interactable = true; PatchHelper.Punish(p, PluginConfig.Ship); return false; } return true; } if ((Object)(object)p == (Object)null) { val.triggerScript.interactable = true; return false; } return true; } [HarmonyPatch("__rpc_handler_2028434619")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool EndGameServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); PatchHelper.LogInfo(p, "StartOfRound.EndGameServerRpc", $"num:{num}", $"shipHasLanded:{StartOfRound.Instance.shipHasLanded}"); if (num == 0 && p.playerClientId != 0) { Object.FindObjectOfType().triggerScript.interactable = true; return false; } if (!StartOfRound.Instance.shipHasLanded) { Object.FindObjectOfType().triggerScript.interactable = true; return false; } if (TimeOfDay.Instance.shipLeavingAlertCalled) { return true; } int value = PluginConfig.Restrict_PullShipLever.Value; TimeOfDay instance = TimeOfDay.Instance; if (value <= 0 || (Object)(object)instance == (Object)null || instance.hour >= value) { return true; } string message = $"Please wait till {value}:00 time to start the ship"; AntiGrief.AlertPlayer(rpcParams.Server.Receive.SenderClientId, message); AntiCheatPlugin.LogInfo($"[Anti-Grief] Ship departure blocked until hour {value}; it is hour {instance.hour}."); StartMatchLever val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null) { val.CancelStartGameClientRpc(); } return false; } if ((Object)(object)p == (Object)null) { Object.FindObjectOfType().triggerScript.interactable = true; return false; } return true; } } [HarmonyPatch(typeof(TimeOfDay))] [HarmonyWrapSafe] public static class TimeOfDayShipFlowPatch { [HarmonyPatch("__rpc_handler_543987598")] [HarmonyPostfix] [HarmonyWrapSafe] public static void SetShipLeaveEarlyServerRpcPostfix(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost && PluginConfig.ShipSetting_OnlyOneVote.Value && rpcParams.Server.Receive.SenderClientId == 0) { TimeOfDay.Instance.SetShipLeaveEarlyClientRpc(TimeOfDay.Instance.normalizedTimeOfDay + 0.1f, StartOfRound.Instance.allPlayerScripts.Length); } } [HarmonyPatch("__rpc_handler_543987598")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool SetShipLeaveEarlyServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { int num = StartOfRound.Instance.connectedPlayersAmount + 1 - StartOfRound.Instance.livingPlayers; string msg = PatchHelper.locale.Msg_GetString("vote_player", new Dictionary { { "{player}", p.playerUsername }, { "{now}", (TimeOfDay.Instance.votesForShipToLeaveEarly + 1).ToString() }, { "{max}", num.ToString() } }); PatchHelper.ShowMessageHostOnly(msg); if (TimeOfDay.Instance.votesForShipToLeaveEarly + 1 >= num) { PatchHelper.LogInfo("Vote EndGame"); return StartOfRoundShipFlowPatch.EndGameServerRpc(target, reader, rpcParams); } } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(GrabbableObject))] [HarmonyWrapSafe] public static class GrabbableObjectShipSystemsPatch { [HarmonyPrefix] [HarmonyPatch("__rpc_handler_319375719")] public static bool ActivateItemRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { RemoteProp val = (RemoteProp)(object)((target is RemoteProp) ? target : null); if (val != null) { AntiCheatPlugin.LogInfo(p, "(" + ((GrabbableObject)val).itemProperties.itemName + ")GrabbableObject.ActivateItemRpc"); bool flag = CooldownManager.CheckCooldown("ShipLight", p); if (!flag) { Object.FindFirstObjectByType().SetShipLightsClientRpc(true); } return flag; } } else if ((Object)(object)p == (Object)null) { return false; } return (Object)(object)target != (Object)null; } } [HarmonyPatch(typeof(ShipBuildModeManager))] [HarmonyWrapSafe] public static class ShipBuildModeManagerPatch { [HarmonyPatch("__rpc_handler_861494715")] [HarmonyPrefix] [HarmonyWrapSafe] public unsafe static bool __rpc_handler_861494715(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Expected O, but got Unknown //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.ShipBuild.Value) { Vector3 val = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val); Vector3 val2 = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val2); NetworkObjectReference val3 = default(NetworkObjectReference); ((FastBufferReader)(ref reader)).ReadValueSafe(ref val3, default(ForNetworkSerializable)); ((FastBufferReader)(ref reader)).Seek(0); NetworkObject val4 = default(NetworkObject); if (((NetworkObjectReference)(ref val3)).TryGet(ref val4, (NetworkManager)null)) { PlaceableShipObject componentInChildren = ((Component)val4).gameObject.GetComponentInChildren(); PatchHelper.LogInfo(p, "ShipBuildModeManager.PlaceShipObjectServerRpc", "object:" + ((Object)componentInChildren.parentObject).name, "newPosition:" + ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString(), $"newRotation:{val2}"); if (Math.Floor(val2.x) != Math.Floor(((Component)componentInChildren.mainMesh).transform.eulerAngles.x) || Math.Floor(val2.z) != Math.Floor(((Component)componentInChildren.mainMesh).transform.eulerAngles.z)) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("ShipBuild", new Dictionary { { "{player}", p.playerUsername }, { "{position}", ((object)(*(Vector3*)(&val2))/*cast due to .constrained prefix*/).ToString() }, { "{object}", ((Object)componentInChildren.parentObject).name } }), PatchHelper.locale.Msg_GetString("ShipBuild"), p, PluginConfig.ShipBuild); PatchHelper.Punish(p, PluginConfig.ShipBuild); return false; } ShipBuildModeManager val5 = (ShipBuildModeManager)target; Bounds bounds = StartOfRound.Instance.shipInnerRoomBounds.bounds; if (!((Bounds)(ref bounds)).Contains(val)) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("ShipBuild", new Dictionary { { "{player}", p.playerUsername }, { "{position}", ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString() } }), PatchHelper.locale.Msg_GetString("ShipBuild"), p, PluginConfig.ShipBuild); PatchHelper.Punish(p, PluginConfig.ShipBuild); return false; } } } return true; } if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(ShipLights))] [HarmonyWrapSafe] public static class ShipLightsPatch { [HarmonyPatch("__rpc_handler_1625678258")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_1625678258(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { bool flag = CooldownManager.CheckCooldown("ShipLight", p); if (!flag) { ((ShipLights)target).SetShipLightsClientRpc(true); } return flag; } if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(ShipTeleporter))] [HarmonyWrapSafe] public static class ShipTeleporterPatch { [HarmonyPrefix] [HarmonyPatch("__rpc_handler_389447712")] public static bool PressTeleportButtonServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown if (PatchHelper.Check(rpcParams, out var p)) { ShipTeleporter val = (ShipTeleporter)target; AntiCheatPlugin.LogInfo(p, "ShipTeleporter.PressTeleportButtonServerRpc", $"isInverseTeleporter:{val.isInverseTeleporter}"); if (val.isInverseTeleporter && !(bool)AccessTools.DeclaredMethod(typeof(ShipTeleporter), "CanUseInverseTeleporter", (Type[])null, (Type[])null).Invoke(val, null)) { return false; } float num = (float)AccessTools.DeclaredField(typeof(ShipTeleporter), "cooldownTime").GetValue(val); if (num > 0f) { AntiCheatPlugin.LogInfo(p, "ShipTeleporter.PressTeleportButtonServerRpc", $"cooldownAmount:{num}"); return false; } return true; } if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(HUDManager))] [HarmonyWrapSafe] public static class HUDManagerTerminalAccessPatch { [HarmonyPatch("__rpc_handler_2436660286")] [HarmonyPrefix] public static bool UseSignalTranslatorServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!PatchHelper.Check(rpcParams, out var p)) { return (Object)(object)p != (Object)null; } if (PluginConfig.RemoteTerminal.Enable && !PatchHelper.CheckRemoteTerminal(p, "HUDManager.UseSignalTranslatorServerRpc")) { return false; } return true; } } [HarmonyPatch(typeof(InteractTrigger))] [HarmonyWrapSafe] public static class InteractTriggerPatch { [HarmonyPatch("__rpc_handler_1430497838")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_1430497838(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); if (num == (int)p.playerClientId) { InteractTrigger val = (InteractTrigger)AccessTools.DeclaredField(typeof(Terminal), "terminalTrigger").GetValue(PatchHelper.terminal); if (((Object)val).GetInstanceID() == ((Object)(InteractTrigger)target).GetInstanceID()) { PatchHelper.whoUseTerminal = p; PatchHelper.LogInfo("player " + p.playerUsername + " use Terminal"); } } } else if ((Object)(object)p == (Object)null) { return false; } return true; } [HarmonyPatch("__rpc_handler_880620475")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_880620475(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_0092: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); if (num == (int)p.playerClientId) { if (p.isPlayerDead) { PatchHelper.LogInfo("player " + p.playerUsername + " death can't use terminal"); return false; } InteractTrigger val = (InteractTrigger)AccessTools.DeclaredField(typeof(Terminal), "terminalTrigger").GetValue(PatchHelper.terminal); if (((Object)val).GetInstanceID() == ((Object)(InteractTrigger)target).GetInstanceID()) { PatchHelper.lastWhoUseTerminal = new KeyValuePair(DateTime.Now, PatchHelper.IdOf(p)); PatchHelper.whoUseTerminal = null; PatchHelper.LogInfo("player " + p.playerUsername + " stop use Terminal"); } } } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(Terminal))] [HarmonyWrapSafe] public static class TerminalAccessPatch { [HarmonyPatch("__rpc_handler_1713627637")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool PlayTerminalAudioServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.RemoteTerminal.Value && !PatchHelper.CheckRemoteTerminal(p, "Terminal.PlayTerminalAudioServerRpc")) { return false; } return CooldownManager.CheckCooldown("TerminalNoise", p); } if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(Turret))] [HarmonyWrapSafe] public static class TurretAccessPatch { [HarmonyPatch("__rpc_handler_2339273208")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool ToggleTurretServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.RemoteTerminal.Enable && !PatchHelper.CheckRemoteTerminal(p, "Turret.ToggleTurretServerRpc")) { return false; } } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(DepositItemsDesk))] [HarmonyWrapSafe] public static class DepositItemsDeskTransactionPatch { [HarmonyPatch("SellAndDisplayItemProfits")] [HarmonyPostfix] [HarmonyWrapSafe] public static void SellAndDisplayItemProfits(int profit, int newGroupCredits) { if (((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost) { PatchHelper.Money = newGroupCredits; PatchHelper.LogInfo($"SetMoney:{PatchHelper.Money}"); } } } [HarmonyPatch(typeof(GameNetworkManager))] [HarmonyWrapSafe] public static class GameNetworkManagerTransactionPatch { [HarmonyPatch("SteamMatchmaking_OnLobbyMemberJoined")] [HarmonyPostfix] [HarmonyWrapSafe] public static void SteamMatchmaking_OnLobbyMemberJoined() { if (!((Object)(object)StartOfRound.Instance == (Object)null) && !((Object)(object)StartOfRound.Instance.localPlayerController == (Object)null) && ((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost) { PatchHelper.LogInfo($"SetMoney:{PatchHelper.Money}"); PatchHelper.Money = PatchHelper.terminal.groupCredits; } } } internal static class PurchaseLog { internal static void Write(PlayerControllerB buyer, string items, int creditsBefore, int creditsAfter) { if (PluginConfig.OperationLog.Value && !((Object)(object)buyer == (Object)null)) { int num = creditsBefore - creditsAfter; string text = ((creditsBefore < 0) ? "cost unknown" : $"{num}cr ({creditsBefore} to {creditsAfter})"); PatchHelper.ShowMessageHostOnly("[ActionLog] " + buyer.playerUsername + " bought " + items + " - " + text); } } internal static string Describe(Terminal terminal, int[] boughtItems) { if ((Object)(object)terminal == (Object)null || boughtItems == null || boughtItems.Length == 0) { return "nothing"; } Dictionary dictionary = new Dictionary(); List list = new List(); foreach (int num in boughtItems) { string text = ((terminal.buyableItemsList != null && num >= 0 && num < terminal.buyableItemsList.Length) ? terminal.buyableItemsList[num].itemName : $"item #{num}"); if (dictionary.ContainsKey(text)) { dictionary[text]++; continue; } dictionary[text] = 1; list.Add(text); } StringBuilder stringBuilder = new StringBuilder(); for (int j = 0; j < list.Count; j++) { if (j > 0) { stringBuilder.Append(", "); } stringBuilder.Append(dictionary[list[j]]).Append("x ").Append(list[j]); } return stringBuilder.ToString(); } } [HarmonyPatch(typeof(StartOfRound))] [HarmonyWrapSafe] public static class StartOfRoundTransactionPatch { [HarmonyPatch("__rpc_handler_1134466287")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool ChangeLevelServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.RemoteTerminal.Value && !PatchHelper.CheckRemoteTerminal(p, "StartOfRound.ChangeLevelServerRpc")) { return false; } int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); int num2 = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num2); ((FastBufferReader)(ref reader)).Seek(0); if (PluginConfig.FreeBuy.Value) { if (num2 > PatchHelper.Money || PatchHelper.Money < 0) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("FreeBuy_SetMoney", new Dictionary { { "{player}", p.playerUsername }, { "{Money}", (num2 - PatchHelper.Money).ToString() } }), p, PluginConfig.FreeBuy); PatchHelper.Punish(p, PluginConfig.FreeBuy); return false; } if (num > StartOfRound.Instance.levels.Length) { PatchHelper.LogInfo(p, "StartOfRound.ChangeLevelServerRpc", "levelID > StartOfRound.Instance.levels.Length"); return false; } TerminalKeyword val = ((IEnumerable)PatchHelper.terminal.terminalNodes.allKeywords).FirstOrDefault((Func)((TerminalKeyword x) => x.word.ToLower() == "route")); string level = StartOfRound.Instance.levels[num].PlanetName.Split(' ')[0]; IEnumerable source = val.compatibleNouns.Where((CompatibleNoun x) => ((Object)x.result).name == level + "route"); if (source.Any()) { int itemCost = source.First().result.itemCost; level = StartOfRound.Instance.currentLevel.PlanetName.Split(' ')[0]; IEnumerable source2 = val.compatibleNouns.Where((CompatibleNoun x) => ((Object)x.result).name == level + "route"); if (itemCost == 0 && source2.Any() && source2.First().result.itemCost != 0) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("ChangeToFreeLevel", new Dictionary { { "{player}", p.playerUsername } })); return false; } PatchHelper.LogInfo(p, "StartOfRound.ChangeLevelServerRpc", $"levelID:{num}", $"itemCost:{itemCost}"); if (itemCost != 0) { int num3 = PatchHelper.Money - itemCost; if (num3 != num2 || PatchHelper.Money == 0) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("FreeBuy_Level", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.FreeBuy); PatchHelper.Punish(p, PluginConfig.FreeBuy); return false; } } } } if (PluginConfig.OperationLog.Value && num < StartOfRound.Instance.levels.Length) { PatchHelper.ShowMessageHostOnly(PatchHelper.locale.OperationLog_GetString("ChangeLevel", new Dictionary { { "{player}", p.playerUsername }, { "{planet}", StartOfRound.Instance.levels[num].PlanetName } })); } } else if ((Object)(object)p == (Object)null) { return false; } return true; } [HarmonyPatch("__rpc_handler_3953483456")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool BuyShipUnlockableServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.RemoteTerminal.Value && !PatchHelper.CheckRemoteTerminal(p, "StartOfRound.BuyShipUnlockableServerRpc")) { return false; } int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); int num2 = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num2); ((FastBufferReader)(ref reader)).Seek(0); if (PluginConfig.FreeBuy.Value) { if (PatchHelper.Money == num2 || PatchHelper.Money == 0) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("FreeBuy_unlockable", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.FreeBuy); PatchHelper.Punish(p, PluginConfig.FreeBuy); return false; } if (num2 > PatchHelper.Money || PatchHelper.Money < 0) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("FreeBuy_SetMoney", new Dictionary { { "{player}", p.playerUsername }, { "{Money}", (num2 - PatchHelper.Money).ToString() } }), p, PluginConfig.FreeBuy); PatchHelper.Punish(p, PluginConfig.FreeBuy); return false; } } if (num >= 0 && num < StartOfRound.Instance.unlockablesList.unlockables.Count) { PurchaseLog.Write(p, "1x " + StartOfRound.Instance.unlockablesList.unlockables[num].unlockableName, PatchHelper.Money, num2); } } else if ((Object)(object)p == (Object)null) { return false; } return true; } [HarmonyPatch("BuyShipUnlockableClientRpc")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool BuyShipUnlockableClientRpc(int newGroupCreditsAmount, int unlockableID = -1) { if (!((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost) { return true; } PatchHelper.Money = newGroupCreditsAmount; return true; } [HarmonyPatch("ChangeLevelClientRpc")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool ChangeLevelClientRpc(int levelID, int newGroupCreditsAmount) { if (!((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost) { return true; } PatchHelper.Money = newGroupCreditsAmount; return true; } } [HarmonyPatch(typeof(Terminal))] [HarmonyWrapSafe] public static class TerminalCreditTrackingPatch { [HarmonyPatch("SyncGroupCreditsClientRpc")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool SyncGroupCreditsClientRpc(int newGroupCredits, int numItemsInShip) { if (!((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost) { return true; } PatchHelper.Money = newGroupCredits; return true; } [HarmonyPatch("BeginUsingTerminal")] [HarmonyPrefix] public static bool BeginUsingTerminal(Terminal __instance) { if (!((NetworkBehaviour)StartOfRound.Instance).IsHost) { return true; } PatchHelper.Money = __instance.groupCredits; PatchHelper.LogInfo($"SetMoney:{PatchHelper.Money}"); return true; } } [HarmonyPatch(typeof(Terminal))] [HarmonyWrapSafe] public static class TerminalTransactionPatch { [HarmonyPatch("__rpc_handler_4003509079")] [HarmonyPrefix] public static bool BuyItemsServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.RemoteTerminal.Value && !PatchHelper.CheckRemoteTerminal(p, "Terminal.BuyItemsServerRpc")) { return false; } bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); int[] array = null; if (flag) { ((FastBufferReader)(ref reader)).ReadValueSafe(ref array, default(ForPrimitives)); } int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); if (PluginConfig.FreeBuy.Value) { if (PatchHelper.Money == num || PatchHelper.Money == 0) { Terminal terminal = (Terminal)target; PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("FreeBuy_Item", new Dictionary { { "{player}", p.playerUsername }, { "{items}", string.Join(",", from x in array group x by terminal.buyableItemsList[x].itemName into g select (g.Count() == 1) ? g.Key : $"{g.Key}*{g.Count()}") } })); PatchHelper.Punish(p, PluginConfig.FreeBuy); return false; } if (num > PatchHelper.Money || PatchHelper.Money < 0) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("FreeBuy_SetMoney", new Dictionary { { "{player}", p.playerUsername }, { "{Money}", (num - PatchHelper.Money).ToString() } })); PatchHelper.Punish(p, PluginConfig.FreeBuy); return false; } } PurchaseLog.Write(p, PurchaseLog.Describe((Terminal)target, array), PatchHelper.Money, num); } else if ((Object)(object)p == (Object)null) { return false; } return true; } [HarmonyPrefix] [HarmonyPatch("__rpc_handler_2452398197")] public static bool BuyVehicleServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.RemoteTerminal.Value && !PatchHelper.CheckRemoteTerminal(p, "Terminal.BuyVehicleServerRpc")) { return false; } int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); int num2 = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num2); bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); ((FastBufferReader)(ref reader)).Seek(0); if (PluginConfig.FreeBuy.Value && (!flag || !(bool)AccessTools.DeclaredField(typeof(Terminal), "hasWarrantyTicket").GetValue(target))) { if (PatchHelper.Money == num2 || PatchHelper.Money == 0) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("FreeBuy_Item", new Dictionary { { "{player}", p.playerUsername }, { "{items}", PatchHelper.locale.Item_GetString("Cruiser") } })); PatchHelper.Punish(p, PluginConfig.FreeBuy); return false; } if (num2 > PatchHelper.Money || PatchHelper.Money < 0) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("FreeBuy_SetMoney", new Dictionary { { "{player}", p.playerUsername }, { "{Money}", (num2 - PatchHelper.Money).ToString() } })); PatchHelper.Punish(p, PluginConfig.FreeBuy); return false; } } PurchaseLog.Write(p, "1x " + PatchHelper.locale.Item_GetString("Cruiser"), PatchHelper.Money, num2); } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(TimeOfDay))] [HarmonyWrapSafe] public static class TimeOfDayTransactionPatch { [HarmonyPatch("SyncNewProfitQuotaClientRpc")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool SyncNewProfitQuotaClientRpc(int newProfitQuota, int overtimeBonus, int fulfilledQuota) { if (!((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost) { return true; } PatchHelper.Money = Mathf.Clamp(PatchHelper.terminal.groupCredits + overtimeBonus, PatchHelper.terminal.groupCredits, 100000000); return true; } } [HarmonyPatch(typeof(HUDManager))] [HarmonyWrapSafe] public static class HUDManagerTerminalValidationPatch { [HarmonyPrefix] [HarmonyPatch("__rpc_handler_3153465849")] public static bool GetNewStoryLogServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (!PatchHelper.Check(rpcParams, out var p)) { return (Object)(object)p != (Object)null; } int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); Terminal val = Object.FindObjectOfType(); return num < val.logEntryFiles.Count && num > 0; } [HarmonyPrefix] [HarmonyPatch("__rpc_handler_1043384750")] public static bool SendErrorMessageServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB p; return !PatchHelper.Check(rpcParams, out p); } [HarmonyPatch("__rpc_handler_1944155956")] [HarmonyPrefix] public static bool ScanNewCreatureServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!PatchHelper.Check(rpcParams, out var p)) { return (Object)(object)p != (Object)null; } int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); Terminal val = Object.FindObjectOfType(); if (num >= val.enemyFiles.Count || num <= 0) { return false; } if (val.scannedEnemyIDs.Contains(num) && val.newlyScannedEnemyIDs.Contains(num)) { return false; } string info = AntiCheatPlugin.localizationManager.Msg_GetString("snc_player", new Dictionary { ["{player}"] = p.playerUsername, ["{enemy}"] = val.enemyFiles[num].creatureName }); PatchHelper.LogInfo(info); return true; } } [HarmonyPatch(typeof(KnifeItem))] [HarmonyWrapSafe] public static class KnifeItemPatch { [HarmonyPatch("__rpc_handler_2696735117")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_2696735117(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return PatchHelper.CheckCoolDownMethod(rpcParams, target, 0.2f); } } [HarmonyPatch(typeof(ShotgunItem))] [HarmonyWrapSafe] public static class ShotgunItemPatch { private static readonly Dictionary> _reloadStarted = new Dictionary>(); private const float MinimumReloadSeconds = 1.9f; [HarmonyPatch("__rpc_handler_3349119596")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_3349119596(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.InfiniteAmmo.Value) { bool flag = default(bool); ((FastBufferReader)(ref reader)).ReadValueSafe(ref flag, default(ForPrimitives)); ((FastBufferReader)(ref reader)).Seek(0); ShotgunItem shot = (ShotgunItem)target; PatchHelper.LogInfo(p, "ShotgunItem.ReloadGunEffectsServerRpc", $"start:{flag}", $"shellsLoaded:{shot.shellsLoaded}"); if (!PatchHelper.ReloadGun.ContainsKey(PatchHelper.IdOf(p))) { PatchHelper.ReloadGun.Add(PatchHelper.IdOf(p), flag); } else { PatchHelper.ReloadGun[PatchHelper.IdOf(p)] = flag; } if (flag) { _reloadStarted[((Object)shot).GetInstanceID()] = new KeyValuePair(PatchHelper.IdOf(p), Time.time); GrabbableObject val = ((IEnumerable)((GrabbableObject)shot).playerHeldBy.ItemSlots).FirstOrDefault((Func)delegate(GrabbableObject x) { GunAmmo val2 = (GunAmmo)(object)((x is GunAmmo) ? x : null); return val2 != null && val2.ammoType == shot.gunCompatibleAmmoID; }); if ((Object)(object)val != (Object)null) { ((MonoBehaviour)shot).StartCoroutine(PatchHelper.CheckAmmo(p, shot, val)); return true; } PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("InfiniteAmmo", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.InfiniteAmmo); PatchHelper.Punish(p, PluginConfig.InfiniteAmmo); return false; } if (!EarnedThisShell(shot, p)) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("InfiniteAmmo", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.InfiniteAmmo); PatchHelper.Punish(p, PluginConfig.InfiniteAmmo); return false; } _reloadStarted.Remove(((Object)shot).GetInstanceID()); } } else if ((Object)(object)p == (Object)null) { return false; } return true; } private static bool EarnedThisShell(ShotgunItem shot, PlayerControllerB p) { if (!_reloadStarted.TryGetValue(((Object)shot).GetInstanceID(), out var value)) { PatchHelper.LogInfo($"{p.playerUsername} finished a reload that never started|shellsLoaded:{shot.shellsLoaded}"); return false; } if (value.Key != PatchHelper.IdOf(p)) { PatchHelper.LogInfo($"{p.playerUsername} finished somebody else's reload|shellsLoaded:{shot.shellsLoaded}"); return false; } float num = Time.time - value.Value; if (num < 1.9f) { PatchHelper.LogInfo($"{p.playerUsername} finished a reload in {num:0.00}s|shellsLoaded:{shot.shellsLoaded}"); return false; } return true; } [HarmonyPatch("__rpc_handler_1329927282")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_1329927282(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.InfiniteAmmo.Value) { ShotgunItem val = (ShotgunItem)target; if ((bool)typeof(ShotgunItem).GetField("localClientSendingShootGunRPC", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(val)) { return true; } PatchHelper.LogInfo(p, "ShotgunItem.ShootGunServerRpc", $"shellsLoaded:{val.shellsLoaded}"); if (val.shellsLoaded == 0) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("InfiniteAmmo", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.InfiniteAmmo); PatchHelper.Punish(p, PluginConfig.InfiniteAmmo); return false; } } if (PluginConfig.UseRate.Value) { ulong key = PatchHelper.IdOf(p); string m = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); if (!PatchHelper.sdqcd.ContainsKey(key)) { PatchHelper.sdqcd.Add(key, new List()); } if (PatchHelper.sdqcd[key].Count > 200) { PatchHelper.sdqcd[key].RemoveRange(0, PatchHelper.sdqcd[key].Count - 1); } if (PatchHelper.sdqcd[key].Count((string x) => x == m) >= 2) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("ItemCooldown", new Dictionary { { "{player}", p.playerUsername }, { "{item}", PatchHelper.locale.Item_GetString("Shotgun") } }), p, PluginConfig.UseRate); PatchHelper.Punish(p, PluginConfig.UseRate); return false; } PatchHelper.sdqcd[key].Add(m); } } else if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(Shovel))] [HarmonyWrapSafe] public static class ShovelPatch { [HarmonyPatch("__rpc_handler_2096026133")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool __rpc_handler_2096026133(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return PatchHelper.CheckCoolDownMethod(rpcParams, target, 0.78f); } } [HarmonyPatch(typeof(DepositItemsDesk))] [HarmonyWrapSafe] public static class DepositItemsDeskHazardPatch { [HarmonyPatch("__rpc_handler_3230280218")] [HarmonyPrefix] [HarmonyWrapSafe] public static bool AttackPlayersServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PatchHelper.Check(rpcParams, out var p)) { if (PluginConfig.Boss.Value) { PatchHelper.ShowMessage(PatchHelper.locale.Msg_GetString("Boss", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.Boss); PatchHelper.Punish(p, PluginConfig.Boss); } return false; } if ((Object)(object)p == (Object)null) { return false; } return true; } } [HarmonyPatch(typeof(Landmine))] [HarmonyWrapSafe] public static class LandminePatch { private static readonly HashSet LandMines = new HashSet(); private static readonly FieldRef MineActivatedField = AccessTools.FieldRefAccess("mineActivated"); private const float MineTriggerDistance = 5f; [HarmonyPrefix] [HarmonyPatch("OnTriggerExit")] public static bool TriggerMineOnLocalClientByExiting(Landmine __instance, Collider other) { if (!((NetworkBehaviour)StartOfRound.Instance).IsHost || __instance.hasExploded || !((Component)__instance).gameObject.activeSelf || !MineActivatedField.Invoke(__instance)) { return true; } lock (LandMines) { LandMines.Add(((Object)__instance).GetInstanceID()); } return true; } [HarmonyPrefix] [HarmonyPatch("SpawnExplosion")] public static void SpawnExplosion(Vector3 explosionPosition) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) AntiCheatPlugin.LogInfo($"Landmine.SpawnExplosion -> {explosionPosition}"); PatchHelper.explosions = PatchHelper.explosions.Where((PatchHelper.ExplosionData x) => x.CreateDateTime.AddSeconds(10.0) > DateTime.Now).ToList(); PatchHelper.explosions.Add(new PatchHelper.ExplosionData { ExplosionPostion = explosionPosition, CalledClient = new List(), CreateDateTime = DateTime.Now }); } [HarmonyPatch("__rpc_handler_3032666565")] [HarmonyPrefix] public static bool ExplodeMineServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown if (!PluginConfig.Landmine.Enable) { return true; } if (!PatchHelper.Check(rpcParams, out var p)) { return (Object)(object)p != (Object)null; } Landmine landmine = (Landmine)target; int instanceID = ((Object)landmine).GetInstanceID(); lock (LandMines) { if (LandMines.Contains(instanceID) || landmine.hasExploded) { return true; } } if (PatchHelper.recentPlayerPositions.TryGetValue(PatchHelper.IdOf(p), out List<(Vector3, float)> value) && !value.Any<(Vector3, float)>(delegate((Vector3 pos, float time) x) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //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_001b: Unknown result type (might be due to invalid IL or missing references) Vector3 val = x.pos - ((Component)landmine).transform.position; return ((Vector3)(ref val)).sqrMagnitude < 25f; })) { AntiCheatPlugin.Detected(PatchHelper.locale.Msg_GetString("Landmine", new Dictionary { ["{player}"] = p.playerUsername })); PatchHelper.Punish(p, PluginConfig.Landmine); } return true; } } [HarmonyPatch] internal static class EnemyInterruptState { private static readonly Dictionary _interrupted = new Dictionary(); internal static void Reset() { _interrupted.Clear(); } internal static void Note(EnemyAI enemy) { if ((Object)(object)enemy != (Object)null) { _interrupted[((Object)enemy).GetInstanceID()] = Time.time; } } internal static bool InterruptedSince(EnemyAI enemy, float since) { if ((Object)(object)enemy == (Object)null || enemy.isEnemyDead) { return true; } if (enemy.stunNormalizedTimer >= 0f) { return true; } float value; return _interrupted.TryGetValue(((Object)enemy).GetInstanceID(), out value) && value >= since; } [HarmonyPatch(typeof(EnemyAI), "HitEnemyOnLocalClient")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnHitLocally(EnemyAI __instance) { Note(__instance); } [HarmonyPatch(typeof(EnemyAI), "HitEnemyClientRpc")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnHitAnnounced(EnemyAI __instance) { Note(__instance); } [HarmonyPatch(typeof(EnemyAI), "SetEnemyStunned")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnStunned(EnemyAI __instance, bool setToStunned) { if (setToStunned) { Note(__instance); } } [HarmonyPatch(typeof(EnemyAI), "KillEnemy")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnKilled(EnemyAI __instance) { Note(__instance); } [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] public static void ResetOnRoundStart() { Reset(); } } public static class PatchHelper { public class ExplosionData { public List CalledClient { get; set; } public Vector3 ExplosionPostion { get; set; } public DateTime CreateDateTime { get; set; } } public class HitData { public int EnemyInstanceId { get; set; } public int force { get; set; } public List CalledClient { get; set; } } public static List jcs = new List(); public static Dictionary> czcd = new Dictionary>(); public static Dictionary> sdqcd = new Dictionary>(); internal static Dictionary> recentPlayerPositions = new Dictionary>(); public static int Money = -1; public static List bypassHit = new List(); public static List bypassKill = new List(); private static bool _notedLocalLobby; private const ulong LocalIdPrefix = 11636175537218519040uL; private static readonly Dictionary _bypassNoted = new Dictionary(); public static Dictionary ReloadGun = new Dictionary(); private static readonly Dictionary _lastUse = new Dictionary(); public static LocalizationManager locale => AntiCheatPlugin.localizationManager; public static Dictionary ConnectionIdtoSteamIdMap { get; set; } = new Dictionary(); public static List mjs { get; set; } = new List(); public static List landMines { get; set; } public static Dictionary> rpcs { get; set; } = new Dictionary>(); public static ulong lastClientId { get; set; } public static Terminal terminal { get { if ((Object)(object)_terminal == (Object)null) { _terminal = Object.FindObjectOfType(); } return _terminal; } } internal static Terminal _terminal { get; set; } public static Dictionary ClingTime { get; set; } = new Dictionary(); public static List explosions { get; set; } = new List(); public static PlayerControllerB lastDriver { get; set; } public static bool bypass { get; set; } public static KeyValuePair lastWhoUseTerminal { get; set; } public static PlayerControllerB whoUseTerminal { get; set; } public static void LogInfo(string info) { AntiCheatPlugin.LogInfo(info); } public static void LogInfo(PlayerControllerB p, string rpc, params object[] param) { AntiCheatPlugin.LogInfo(p, rpc, param); } public static void LogError(string info) { if (PluginConfig.Log.Value) { AntiCheatPlugin.Log.LogError((object)(info ?? "")); } } internal static bool CheckDamage(PlayerControllerB p2, PlayerControllerB p, ref int damageAmount) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (damageAmount == 0) { return true; } LogInfo($"{p.playerUsername} hit {p2.playerUsername} damageAmount:{damageAmount}|p2:{p2.playerUsername}"); try { if (PluginConfig.Shovel.Value) { float num = Vector3.Distance(((Component)p).transform.position, ((Component)p2).transform.position); GrabbableObject val = p.ItemSlots[p.currentItemSlot]; string playerUsername = p.playerUsername; if (jcs.Contains(IdOf(p))) { ShowMessage(p.playerUsername + ": melee damage blocked again", $"melee-blocked:{IdOf(p)}"); damageAmount = 0; } else if (damageAmount != 20 && (Object)(object)val != (Object)null && (isShovel(val) || isKnife(val))) { if (!jcs.Contains(IdOf(p))) { ShowMessage(locale.Msg_GetString("Shovel", new Dictionary { { "{player}", p.playerUsername }, { "{player2}", p2.playerUsername }, { "{damageAmount}", damageAmount.ToString() }, { "{item}", isShovel(val) ? locale.Item_GetString("Shovel") : locale.Item_GetString("Knife") } }), p, PluginConfig.Shovel); jcs.Add(IdOf(p)); Punish(p, PluginConfig.Shovel); } damageAmount = 0; } else if (num > 11f && (Object)(object)val != (Object)null && (isShovel(val) || isKnife(val))) { if (p2.isPlayerDead) { return true; } if (!jcs.Contains(IdOf(p))) { ShowMessage(locale.Msg_GetString("Shovel2", new Dictionary { { "{player}", p.playerUsername }, { "{player2}", p2.playerUsername }, { "{distance}", num.ToString() }, { "{damageAmount}", damageAmount.ToString() }, { "{item}", isShovel(val) ? locale.Item_GetString("Shovel") : locale.Item_GetString("Knife") } }), p, PluginConfig.Shovel); jcs.Add(IdOf(p)); Punish(p, PluginConfig.Shovel); } damageAmount = 0; } else { if (ClingTime.ContainsKey(IdOf(p)) && ClingTime[IdOf(p)].AddSeconds(5.0) > DateTime.Now) { return true; } if (p.ItemSlots.Any((GrabbableObject x) => isShovel(x)) && damageAmount == 10) { return true; } } if (damageAmount == 0) { return false; } return true; } } catch (Exception ex) { LogInfo(ex.ToString() ?? ""); } return true; } public static bool isGun(GrabbableObject item) { return item is ShotgunItem; } public static bool isShovel(GrabbableObject item) { return item is Shovel; } public static bool isKnife(GrabbableObject item) { return item is KnifeItem; } public static bool isJetpack(GrabbableObject item) { return item is JetpackItem; } [HarmonyWrapSafe] public static IEnumerator CheckRpc(PlayerControllerB __instance, string RPC) { if ((RPC == "Hit" && !PluginConfig.GodMode_WatchHitRpc.Value) || (RPC == "KillPlayer" && !PluginConfig.GodMode_WatchKillPlayerRpc.Value)) { yield break; } yield return (object)new WaitForSeconds((float)PluginConfig.GodMode_ReportDelay.Value / 1000f); if (!__instance.isPlayerDead && rpcs[RPC].Contains(__instance.playerClientId)) { rpcs[RPC].Remove(__instance.playerClientId); if (!ChecksBypassed(__instance)) { LogInfo($"{__instance.playerUsername}:{__instance.isPlayerDead}"); ShowMessage(locale.Msg_GetString("RPCReport", new Dictionary { { "{player}", __instance.playerUsername }, { "{RPC}", RPC } }), __instance, PluginConfig.GodMode); Punish(__instance, PluginConfig.GodMode); } } } internal static bool KillPlayerServerRpc(NetworkBehaviour target, FastBufferReader reader, __RpcParams rpcParams, string call) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (Check(rpcParams, out var p)) { EnemyAI val = (EnemyAI)target; int num = default(int); ByteUnpacker.ReadValueBitPacked(reader, ref num); ((FastBufferReader)(ref reader)).Seek(0); LogInfo(p, $"{((object)val).GetType()}.KillPlayerServerRpc", $"playerId:{num}"); if (num <= StartOfRound.Instance.allPlayerScripts.Length && (Object)(object)StartOfRound.Instance.allPlayerScripts[num] != (Object)(object)p) { LogInfo(p, $"{((object)val).GetType()}.KillPlayerServerRpc", "playerUsername:" + StartOfRound.Instance.allPlayerScripts[num].playerUsername); return false; } if (!rpcs.ContainsKey("KillPlayer")) { rpcs.Add("KillPlayer", new List()); } if (p.AllowPlayerDeath()) { LogInfo(p, "StartCoroutine:CheckRpc(KillPlayer)", "call:" + call); rpcs["KillPlayer"].Add(p.playerClientId); ((MonoBehaviour)p).StartCoroutine(CheckRpc(p, "KillPlayer")); } } else if ((Object)(object)p == (Object)null) { return false; } return true; } public static string PlayerClientIdConvertName(int index) { if (index < 0 || index >= StartOfRound.Instance.allPlayerScripts.Length) { return "Unknown"; } return StartOfRound.Instance.allPlayerScripts[index].playerUsername; } public static void ShowMessage(string msg, string lastmsg = null) { AntiCheatPlugin.Detected(msg, lastmsg ?? msg); } public static void ShowMessage(string msg, PlayerControllerB player, FeatureConfig feature) { ShowMessage(msg, null, player, feature); } public static void ShowMessage(string msg, string lastmsg, PlayerControllerB player, FeatureConfig feature) { string text = CheckCatalog.LabelFor(feature); AntiCheatPlugin.Detected(string.IsNullOrEmpty(text) ? msg : ("[" + text + "] " + msg), lastmsg ?? msg, ((Object)(object)player != (Object)null) ? player.playerUsername : null, text); } public static void AddTextMessageClientRpc(string showmsg) { AntiCheatPlugin.AddTextMessageClientRpc(showmsg); } public static IEnumerator DestroySelf(GameObject self) { yield return (object)new WaitForSeconds(5f); Object.Destroy((Object)(object)self); } public static bool Check(__RpcParams rpcParams, out PlayerControllerB p) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)StartOfRound.Instance.localPlayerController == (Object)null) { p = null; return false; } if (!((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost) { p = StartOfRound.Instance.localPlayerController; return false; } PlayerControllerB player = GetPlayer(rpcParams); p = player; if ((Object)(object)p == (Object)null) { NetworkManager.Singleton.DisconnectClient(rpcParams.Server.Receive.SenderClientId); return false; } if (rpcParams.Server.Receive.SenderClientId == GameNetworkManager.Instance.localPlayerController.actualClientId) { p = StartOfRound.Instance.localPlayerController; return false; } if (StartOfRound.Instance.KickedClientIds.Contains(p.playerSteamId)) { Task.Delay(-100); NetworkManager.Singleton.DisconnectClient(rpcParams.Server.Receive.SenderClientId); return false; } if (p.playerSteamId == 0L && !LobbyIsOnSteam()) { NoteLocalLobby(); return true; } if (p.playerSteamId == 0) { uint num = ClientIdToTransportId(rpcParams.Server.Receive.SenderClientId); if (num == 0) { return false; } if (!ConnectionIdtoSteamIdMap.TryGetValue(num, out var value)) { return false; } Friend val = default(Friend); ((Friend)(ref val))..ctor(SteamId.op_Implicit(value)); NetworkManager.Singleton.DisconnectClient(rpcParams.Server.Receive.SenderClientId); StartOfRound.Instance.KickedClientIds.Add(value); LogInfo($"{((Friend)(ref val)).Name}({value}) hid their Steam ID - kicked."); return false; } if (Profiles.BypassesAllChecks(p)) { NoteBypass(p); return false; } return true; } private static void NoteLocalLobby() { if (!_notedLocalLobby) { _notedLocalLobby = true; LogInfo("No Steam IDs in this lobby (LAN or direct connect) - checks run against network client ids instead. The persistent ban list and bypass profiles are keyed on Steam IDs and cannot work here; kicks last the session."); } } internal static bool LobbyIsOnSteam() { try { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.localPlayerController == (Object)null || instance.localPlayerController.playerSteamId == 0) { return false; } GameNetworkManager instance2 = GameNetworkManager.Instance; return (Object)(object)instance2 != (Object)null && instance2.currentLobby.HasValue; } catch (Exception) { return false; } } internal static ulong IdOf(PlayerControllerB player) { if ((Object)(object)player == (Object)null) { return 0uL; } return (player.playerSteamId != 0L) ? player.playerSteamId : (0xA17C000000000000uL | player.actualClientId); } private static void NoteBypass(PlayerControllerB player) { if (!_bypassNoted.TryGetValue(IdOf(player), out var value) || !(value.AddSeconds(60.0) > DateTime.Now)) { _bypassNoted[IdOf(player)] = DateTime.Now; LogInfo($"[Profiles] {player.playerUsername}({player.playerSteamId}) carries a profile that bypasses every cheat check - checks skipped."); } } public static uint ClientIdToTransportId(ulong SenderClientId) { if (SenderClientId == 0) { return 0u; } NetworkConnectionManager value = Traverse.Create((object)NetworkManager.Singleton).Field("ConnectionManager").GetValue(); ulong value2 = Traverse.Create((object)value).Method("ClientIdToTransportId", new object[1] { SenderClientId }).GetValue(); return (uint)value2; } public static bool CheckRemoteTerminal(PlayerControllerB p, string call) { LogInfo(p, "CheckRemoteTerminal", "Call:" + call); if ((Object)(object)whoUseTerminal == (Object)null && lastWhoUseTerminal.Value == IdOf(p)) { LogInfo($"whoUseTerminal == null && lastWhoUseTerminal == {IdOf(p)}||Time:{Math.Round((DateTime.Now - lastWhoUseTerminal.Key).TotalSeconds, 2)}s"); if (lastWhoUseTerminal.Key.AddSeconds(10.0) > DateTime.Now) { return true; } } if ((Object)(object)whoUseTerminal != (Object)(object)p) { if ((Object)(object)whoUseTerminal == (Object)null) { LogInfo("no player use terminal|request player:" + p.playerUsername + "|call:" + call); } else { LogInfo("whoUseTerminal:" + whoUseTerminal.playerUsername + "|p:" + p.playerUsername + "|call:" + call); } ShowMessage(locale.Msg_GetString("RemoteTerminal", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.RemoteTerminal); Punish(p, PluginConfig.RemoteTerminal); return false; } return true; } internal static PlayerControllerB GetPlayer(__RpcParams rpcParams) { //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) PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (val.actualClientId == rpcParams.Server.Receive.SenderClientId) { return val; } } return null; } public static IEnumerator CheckAmmo(PlayerControllerB p, ShotgunItem shot, GrabbableObject ammo) { yield return (object)new WaitForSeconds(1.25f); yield return (object)new WaitForSeconds(3f); if (ReloadGun.ContainsKey(IdOf(p)) && !ReloadGun[IdOf(p)] && (Object)(object)ammo != (Object)null && (Object)(object)((NetworkBehaviour)ammo).NetworkObject != (Object)null && ((NetworkBehaviour)ammo).NetworkObject.IsSpawned) { ShowMessage(locale.Msg_GetString("InfiniteAmmo", new Dictionary { { "{player}", p.playerUsername } }), p, PluginConfig.InfiniteAmmo); Punish(p, PluginConfig.InfiniteAmmo); } } internal static bool CheckCoolDownMethod(__RpcParams rpcParams, NetworkBehaviour target, float minimumSeconds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (Check(rpcParams, out var p)) { if (PluginConfig.UseRate.Value) { GrabbableObject val = (GrabbableObject)(object)((target is GrabbableObject) ? target : null); float num = minimumSeconds * 0.75f; string text = $"{IdOf(p)}:{(((Object)(object)val != (Object)null) ? ((Object)val).GetInstanceID() : 0)}"; float time = Time.time; if (_lastUse.TryGetValue(text, out var value) && time - value < num) { string text2 = (((Object)(object)val != (Object)null && (Object)(object)val.itemProperties != (Object)null) ? val.itemProperties.itemName : locale.Item_GetString("Shovel")); LogInfo($"{p.playerUsername} used {text2} after {time - value:0.00}s (vanilla floor {minimumSeconds:0.00}s)"); ShowMessage(locale.Msg_GetString("ItemCooldown", new Dictionary { { "{player}", p.playerUsername }, { "{item}", text2 } }), "cooldown:" + text, p, PluginConfig.UseRate); Punish(p, PluginConfig.UseRate); _lastUse[text] = time; return false; } _lastUse[text] = time; } } else if ((Object)(object)p == (Object)null) { return false; } return true; } internal static void ResetCooldownTracking() { _lastUse.Clear(); } public static void KickPlayer(PlayerControllerB kick, bool canJoin = false, string Reason = null, BanSource source = BanSource.AntiCheat) { if (kick.playerClientId == 0) { return; } string text = (string.IsNullOrEmpty(Reason) ? ("[" + locale.Prefix() + "] " + locale.Msg_GetString("KickPlayer") + "!") : Reason); ulong playerSteamId = kick.playerSteamId; string playerUsername = kick.playerUsername; bool flag = (Object)(object)whoUseTerminal == (Object)(object)kick; NetworkManager.Singleton.DisconnectClient(kick.actualClientId, text); if (playerSteamId != 0) { StartOfRound instance = StartOfRound.Instance; if (!instance.KickedClientIds.Contains(playerSteamId) && !canJoin) { instance.KickedClientIds.Add(playerSteamId); } Terminal val = Object.FindAnyObjectByType(); LogInfo("terminalInUse" + val.placeableObject.inUse); LogInfo("whoUseTerminal:" + whoUseTerminal?.playerUsername); LogInfo("playerUsername:" + playerUsername); if (flag && val.placeableObject.inUse) { LogInfo("SetTerminalInUseServerRpc"); val.SetTerminalInUseClientRpc(false); val.terminalInUse = false; } if (source == BanSource.AntiCheat) { AntiCheatPlugin.Punished(playerUsername + " kicked", $"kick:{playerSteamId}:{DateTime.Now.Ticks}", playerUsername, "kicked"); } else { AntiCheatPlugin.Announce(playerUsername + " kicked"); } AntiCheatPlugin.ForgetAlertsFor(playerUsername); } } public static void Punish(PlayerControllerB player, FeatureConfig feature, string reason = null) { if (!((Object)(object)player == (Object)null) && feature != null) { if (IsHost(player)) { LogInfo("[Checks] " + feature.Key + " named the host - ignored."); } else if (Profiles.BypassesCheck(player, feature)) { LogInfo($"[Profiles] {player.playerUsername}({player.playerSteamId}) is exempt from {feature.Key} - detection stands, no punishment."); } else { Punish(player, feature.Punishment, reason); } } } public static bool IsHost(PlayerControllerB player) { if ((Object)(object)player == (Object)null) { return false; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.localPlayerController == (Object)null) { return false; } return player.actualClientId == instance.localPlayerController.actualClientId; } public static bool ChecksBypassed(PlayerControllerB player) { if ((Object)(object)player == (Object)null) { return false; } if (IsHost(player)) { return true; } if (!Profiles.BypassesAllChecks(player)) { return false; } NoteBypass(player); return true; } public static void Punish(PlayerControllerB player, PunishmentType punishment, string reason = null, BanSource source = BanSource.AntiCheat) { if (!((Object)(object)player == (Object)null)) { switch (punishment) { case PunishmentType.Kick: KickPlayer(player, canJoin: false, reason, source); break; case PunishmentType.Ban: BanPlayer(player, reason, source); break; } } } public static void BanPlayer(PlayerControllerB player, string reason = null, BanSource source = BanSource.AntiCheat) { if (!((Object)(object)player == (Object)null) && player.playerClientId != 0) { string text = (string.IsNullOrEmpty(reason) ? PluginConfig.BanReason.Value : reason); if (string.IsNullOrEmpty(text)) { text = "You have been banned by the Anti Cheat System for Cheating"; } ulong playerSteamId = player.playerSteamId; ulong actualClientId = player.actualClientId; string playerUsername = player.playerUsername; BanList.Add(playerSteamId, source, playerUsername); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && playerSteamId != 0L && !instance.KickedClientIds.Contains(playerSteamId)) { instance.KickedClientIds.Add(playerSteamId); } LogInfo($"[Bans] Banning {playerUsername}({playerSteamId}) on client {actualClientId}: {text}"); NetworkManager.Singleton.DisconnectClient(actualClientId, text); if (source == BanSource.AntiCheat) { AntiCheatPlugin.Punished(playerUsername + " banned", $"ban:{playerSteamId}:{DateTime.Now.Ticks}", playerUsername, "banned"); } else { AntiCheatPlugin.Announce(playerUsername + " banned"); } AntiCheatPlugin.ForgetAlertsFor(playerUsername); } } public static void BanClient(ulong clientId, string reason, BanSource source = BanSource.AntiCheat) { if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null) { return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val != (Object)null && val.actualClientId == clientId) { BanPlayer(val, reason, source); return; } } LogInfo($"[Bans] No player found for client {clientId} - ban skipped."); } public static void ShowMessageHostOnly(string msg) { switch (PluginConfig.OperationLog_MessageType.Value) { case PluginConfig.MessageType.Menu: MenuLog.Info(msg); break; case PluginConfig.MessageType.PublicChat: MenuLog.Info(msg); AntiCheatPlugin.AddTextMessageClientRpc(msg); break; case PluginConfig.MessageType.GUI: MenuLog.Info(msg); AntiCheatPlugin.ShowGuiTip(msg); break; default: MenuLog.Info(msg); AntiCheatPlugin.ShowMessageHostOnly(msg); break; } } } [HarmonyPatch] internal static class PlayerDamageState { internal const int FullHealth = 100; private static readonly Dictionary _health = new Dictionary(); private static readonly Dictionary _lastAcknowledged = new Dictionary(); [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] public static void ResetOnRoundStart() { _health.Clear(); _lastAcknowledged.Clear(); PatchHelper.ResetCooldownTracking(); } [HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")] [HarmonyPostfix] public static void ResetOnDeath(PlayerControllerB __instance) { if ((Object)(object)__instance != (Object)null) { _health.Remove(__instance.actualClientId); MarkAcknowledged(__instance.actualClientId); } } [HarmonyPatch(typeof(PlayerControllerB), "MakeCriticallyInjuredServerRpc")] [HarmonyPrefix] public static void OnCriticallyInjuredReported(PlayerControllerB __instance) { if ((Object)(object)__instance != (Object)null && RpcExecStage.IsExecutingReceivedRpc((NetworkBehaviour)(object)__instance)) { _health[__instance.actualClientId] = 5; MarkAcknowledged(__instance.actualClientId); } } internal static int Get(ulong clientId) { int value; return _health.TryGetValue(clientId, out value) ? value : 100; } internal static void Set(ulong clientId, int health) { _health[clientId] = health; } internal static void MarkAcknowledged(ulong clientId) { _lastAcknowledged[clientId] = Time.time; } internal static float LastAcknowledged(ulong clientId) { float value; return _lastAcknowledged.TryGetValue(clientId, out value) ? value : float.NegativeInfinity; } internal static bool DamageIsPossibleRightNow() { StartOfRound instance = StartOfRound.Instance; return (Object)(object)instance != (Object)null && instance.allowLocalPlayerDeath && instance.shipDoorsEnabled && instance.timeSinceRoundStarted >= 2f && !instance.inShipPhase && !instance.shipIsLeaving; } } [HarmonyPatch] internal static class PlayerPositionState { internal sealed class Report { public Vector3 LocalPosition; public float Time; public bool Grounded; public bool InElevator; public bool InShip; public bool Any; } private static readonly Dictionary _reports = new Dictionary(); private const float ProbeRiseAboveFeet = 0.2f; private const float ProbeReach = 0.7f; internal static Report For(ulong clientId) { Report value; return _reports.TryGetValue(clientId, out value) ? value : null; } internal static bool IsOnGround(PlayerControllerB player) { if ((Object)(object)player == (Object)null) { return true; } Report report = For(player.actualClientId); if (report != null && report.Any && report.Grounded) { return true; } return FloorBeneath(player); } private static bool FloorBeneath(PlayerControllerB player) { //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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return true; } return Physics.Raycast(new Ray(((Component)player).transform.position + Vector3.up * 0.2f, Vector3.down), 0.7f, instance.allPlayersCollideWithMask, (QueryTriggerInteraction)1); } internal static void Reset() { _reports.Clear(); } [HarmonyPatch(typeof(PlayerControllerB), "UpdatePlayerPositionRpc")] [HarmonyPrefix] [HarmonyWrapSafe] public static void OnPositionReported(PlayerControllerB __instance, Vector3 newPos, bool inElevator, bool isInShip, bool isPlayerGrounded) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance == (Object)null) && !((Object)(object)StartOfRound.Instance == (Object)null) && !((Object)(object)StartOfRound.Instance.localPlayerController == (Object)null) && ((NetworkBehaviour)StartOfRound.Instance.localPlayerController).IsHost && RpcExecStage.IsExecutingReceivedRpc((NetworkBehaviour)(object)__instance)) { ulong actualClientId = __instance.actualClientId; if (!_reports.TryGetValue(actualClientId, out var value)) { value = new Report(); _reports[actualClientId] = value; } value.LocalPosition = newPos; value.Time = Time.time; value.Grounded = isPlayerGrounded; value.InElevator = inElevator; value.InShip = isInShip; value.Any = true; } } [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] public static void ResetOnRoundStart() { Reset(); } } internal static class RpcExecStage { private static readonly FieldInfo ExecStageField = typeof(NetworkBehaviour).GetField("__rpc_exec_stage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static bool _warnedMissingField; private const int ExecuteStageValue = 1; internal static bool IsExecutingReceivedRpc(NetworkBehaviour instance) { if ((Object)(object)instance == (Object)null) { return false; } if (ExecStageField == null) { if (!_warnedMissingField) { _warnedMissingField = true; AntiCheatPlugin.LogInfo("NetworkBehaviour.__rpc_exec_stage not found - RPC receive-side detection is disabled for this session."); } return false; } object value = ExecStageField.GetValue(instance); if (value == null) { return false; } return Convert.ToInt32(value) == 1; } } [HarmonyPatch] internal static class TeleportWatch { private const float ExpectedSeconds = 3f; private static readonly Dictionary _expected = new Dictionary(); internal static void Reset() { _expected.Clear(); } internal static void Expect(PlayerControllerB player, string why) { if (!((Object)(object)player == (Object)null)) { _expected[player.actualClientId] = Time.time + 3f; if (PluginConfig.MovementDiagnostics != null && PluginConfig.MovementDiagnostics.Value) { AntiCheatPlugin.LogInfo("[teleport] " + player.playerUsername + " expected to jump: " + why); } } } internal static bool IsExpected(ulong clientId) { float value; return _expected.TryGetValue(clientId, out value) && Time.time < value; } private static void ExpectByIndex(int playerObj, string why) { StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null) && instance.allPlayerScripts != null && playerObj >= 0 && playerObj < instance.allPlayerScripts.Length) { Expect(instance.allPlayerScripts[playerObj], why); } } [HarmonyPatch(typeof(PlayerControllerB), "TeleportPlayer")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnTeleportPlayer(PlayerControllerB __instance) { Expect(__instance, "TeleportPlayer"); } [HarmonyPatch(typeof(EntranceTeleport), "TeleportPlayerClientRpc")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnEntranceTeleport(int playerObj) { ExpectByIndex(playerObj, "entrance"); } [HarmonyPatch(typeof(ShipTeleporter), "TeleportPlayerOutClientRpc")] [HarmonyPostfix] [HarmonyWrapSafe] public static void OnInverseTeleport(int playerObj) { ExpectByIndex(playerObj, "inverse teleporter"); } [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] public static void ResetOnRoundStart() { Reset(); } } [HarmonyPatch(typeof(HUDManager))] [HarmonyWrapSafe] public static class HUDManagerDisclaimerPatch { [HarmonyPatch("AddChatMessage")] [HarmonyPostfix] [HarmonyWrapSafe] public static void WatchForDisclaimerCommand(string chatMessage, string nameOfUserWhoTyped) { ServerDisclaimer.HandleChat(chatMessage, nameOfUserWhoTyped); } [HarmonyPatch("SubmitChat_performed")] [HarmonyPrefix] [HarmonyPriority(800)] [HarmonyWrapSafe] public static bool InterceptTypedDisclaimerCommand(HUDManager __instance) { PlayerControllerB val = (((Object)(object)GameNetworkManager.Instance != (Object)null) ? GameNetworkManager.Instance.localPlayerController : null); if ((Object)(object)val == (Object)null || !val.isTypingChat || (Object)(object)StartOfRound.Instance == (Object)null || !((NetworkBehaviour)StartOfRound.Instance).IsServer || (Object)(object)__instance.chatTextField == (Object)null || !ServerDisclaimer.IsRequest(__instance.chatTextField.text)) { return true; } ServerDisclaimer.PostThrottled(); val.isTypingChat = false; __instance.chatTextField.text = ""; if ((Object)(object)EventSystem.current != (Object)null) { EventSystem.current.SetSelectedGameObject((GameObject)null); } __instance.PingHUDElement(__instance.Chat, 2f, 1f, 0.2f); ((Behaviour)__instance.typingIndicator).enabled = false; return false; } } } namespace AntiCheat.Patches.Features.ShipFlow { [HarmonyPatch(typeof(StartMatchLever))] [HarmonyWrapSafe] internal static class StartMatchLeverPatch { [HarmonyPatch("Start")] [HarmonyPostfix] private static void AllowSoloLaunch(StartMatchLever __instance) { __instance.singlePlayerEnabled = true; } [HarmonyPatch("CancelStartGameClientRpc")] [HarmonyPostfix] private static void RestoreBlockedDepartureLever(StartMatchLever __instance) { if (!((Object)(object)__instance.playersManager == (Object)null) && __instance.playersManager.shipHasLanded) { __instance.playersManager.shipIsLeaving = false; __instance.triggerScript.interactable = true; } } } } namespace AntiCheat.Locale { public class Locale { public Dictionary Config { get; set; } public Dictionary Item { get; set; } public Dictionary Log { get; set; } public Dictionary Message { get; set; } public Dictionary OperationLog { get; set; } public string Prefix { get; set; } public string MessageFormat { get; set; } } public class LocaleCfg { public string current_language { get; set; } public Dictionary support_language { get; set; } } public class LocalizationManager { public string current_language { get; set; } public Locale locale { get; set; } public LocalizationManager() { string directoryName = Path.GetDirectoryName(GetType().Assembly.Location); string path = Path.Combine(directoryName, "locales", "localization_cfg.json"); string text = File.ReadAllText(path); LocaleCfg localeCfg = JsonConvert.DeserializeObject(text); string path2 = Path.Combine(directoryName, "locales"); if (string.IsNullOrWhiteSpace(localeCfg.current_language)) { localeCfg.current_language = "en_US"; } string path3 = Path.Combine(path2, localeCfg.current_language + ".json"); if (!File.Exists(path3)) { AntiCheatPlugin.LogInfo("locale '" + localeCfg.current_language + "' is not available in this build, using en_US"); localeCfg.current_language = "en_US"; path3 = Path.Combine(path2, "en_US.json"); } current_language = localeCfg.current_language; text = File.ReadAllText(path3); locale = JsonConvert.DeserializeObject(text); } public string Prefix() { return locale.Prefix; } public string MessageFormat(Dictionary pairs = null) { return ReplacePlaceholders(locale.MessageFormat, pairs); } public string OperationLog_GetString(string key, Dictionary pairs = null) { return GetStringWithFallback(locale.OperationLog, key, pairs, "OperationLog_GetString"); } public string Cfg_GetString(string key) { return GetStringWithFallback(locale.Config, key, null, "Cfg_GetString"); } public string Item_GetString(string key) { return GetStringWithFallback(locale.Item, key, null, "Item_GetString"); } public string Log_GetString(string key, Dictionary pairs = null) { return GetStringWithFallback(locale.Log, key, pairs, "Log_GetString"); } public string Msg_GetString(string key, Dictionary pairs = null) { return GetStringWithFallback(locale.Message, key, pairs, "Msg_GetString"); } private string GetStringWithFallback(Dictionary dictionary, string key, Dictionary pairs, string methodName) { if (dictionary.TryGetValue(key, out var value)) { return ReplacePlaceholders(value, pairs); } return methodName + " fail -> " + key; } private string ReplacePlaceholders(string input, Dictionary pairs) { if (pairs == null || string.IsNullOrEmpty(input)) { return input; } StringBuilder stringBuilder = new StringBuilder(input); foreach (KeyValuePair pair in pairs) { stringBuilder.Replace(pair.Key, pair.Value); } return stringBuilder.ToString(); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }