Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of AntiHacker v2.0.3
AntiHacker.dll
Decompiled a day agousing System; using System.Collections; 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 BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using Steamworks.Data; using Unity.Netcode; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("AntiHacker")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Prevent hackers from rejoining after being kicked in Lethal Company")] [assembly: AssemblyFileVersion("2.0.3.0")] [assembly: AssemblyInformationalVersion("2.0.3+0260d38d4a4e1466171b4c2cb63be129dcad9380")] [assembly: AssemblyProduct("AntiHacker")] [assembly: AssemblyTitle("AntiHacker")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.0.3.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace AntiHacker { [BepInPlugin("com.invertigo.antihacker", "AntiHacker", "2.0.3")] public class Plugin : BaseUnityPlugin { public const string PLUGIN_GUID = "com.invertigo.antihacker"; public const string PLUGIN_NAME = "AntiHacker"; public const string PLUGIN_VERSION = "2.0.3"; public static Plugin Instance; public static ManualLogSource Log; private Harmony harmony; public ConfigEntry<bool> PersistKickList; public ConfigEntry<bool> ForceVanillaLobby; public ConfigEntry<string> ExtraBlacklist; public ConfigEntry<bool> AllowClientStartEnd; public ConfigEntry<Key> RecoveryKey; public ConfigEntry<bool> RotationTipEveryTime; public ConfigEntry<bool> EnableMoneyCommand; public ConfigEntry<bool> LockShipScrapUntilCompany; public ConfigEntry<int> MaxShipGrabsPerSecond; public ConfigEntry<bool> EnableTeleport; public ConfigEntry<Key> TeleportKey; public ConfigEntry<Key> LightSwitchKey; public ConfigEntry<Key> MoveTerminalKey; public ConfigEntry<Key> UnstoreKey; private static Vector3 defaultTerminalPos; private static Vector3 defaultTerminalRot; private static bool defaultTerminalSet; private static bool wasInBuildMode; private static PlaceableShipObject cachedTerminalPlaceable; private void Awake() { //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; PersistKickList = ((BaseUnityPlugin)this).Config.Bind<bool>("AntiHacker", "PersistKickList", true, "If true, the kick list will persist across game sessions. If false, it will reset every time you close the game."); ForceVanillaLobby = ((BaseUnityPlugin)this).Config.Bind<bool>("AntiHacker", "ForceVanillaLobby", false, "If true, explicitly forces your server to appear in the vanilla server list, bypassing the automatic check for other mods."); ExtraBlacklist = ((BaseUnityPlugin)this).Config.Bind<string>("AntiHacker", "ExtraBlacklist", "", "Extra Steam IDs (comma separated) to ban. A built-in blacklist of known hackers is always active."); AllowClientStartEnd = ((BaseUnityPlugin)this).Config.Bind<bool>("AntiHacker", "AllowClientStartEnd", true, "If false, only the host may pull the start/end lever (client attempts are ignored, not banned)."); RecoveryKey = ((BaseUnityPlugin)this).Config.Bind<Key>("AntiHacker", "RecoveryKey", (Key)102, "Host only: press to clear a stuck ship travel lock (left behind by a failed ChangeLevel exploit)."); EnableMoneyCommand = ((BaseUnityPlugin)this).Config.Bind<bool>("AntiHacker", "EnableMoneyCommand", false, "If true, the host can use the 'money' terminal command to set or adjust group credits. Disabled by default; only intended for repairing credits tampered with by hackers."); LockShipScrapUntilCompany = ((BaseUnityPlugin)this).Config.Bind<bool>("AntiHacker", "LockShipScrapUntilCompany", true, "If true, remote clients cannot pick up items with a scrap value (scrapValue > 0) once they are inside the ship, unless the ship is at the Company Building (71 Gordion) to sell."); MaxShipGrabsPerSecond = ((BaseUnityPlugin)this).Config.Bind<int>("AntiHacker", "MaxShipGrabsPerSecond", 3, "Maximum number of ship objects (isInShipRoom) a remote client can grab per second before grabs are blocked (and extreme spam is auto-banned)."); RotationTipEveryTime = ((BaseUnityPlugin)this).Config.Bind<bool>("Hotkeys", "RotationTipEveryTime", false, "If true, the rotation hint popup shows every time you start moving an object. If false, it shows only once."); EnableTeleport = ((BaseUnityPlugin)this).Config.Bind<bool>("Hotkeys", "EnableTeleport", false, "Whether the F5 contextual teleport function is enabled. Disabled by default; only intended for resolving issues caused by hackers."); TeleportKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Hotkeys", "TeleportKey", (Key)98, "Hotkey to teleport. Contextual based on if you are in the ship."); LightSwitchKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Hotkeys", "LightSwitchKey", (Key)100, "Hotkey to toggle the ship lights."); MoveTerminalKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Hotkeys", "MoveTerminalKey", (Key)101, "Hotkey to store/unstore the terminal."); UnstoreKey = ((BaseUnityPlugin)this).Config.Bind<Key>("Hotkeys", "UnstoreKey", (Key)99, "Hotkey to unstore the first item in storage and enter build mode."); Security.Init(); harmony = new Harmony("com.invertigo.antihacker"); harmony.PatchAll(); PatchRpcSenderTracking(); SteamMatchmaking.OnLobbyMemberJoined += OnLobbyMemberJoined; SteamMatchmaking.OnLobbyMemberLeave += OnLobbyMemberLeave; Log.LogInfo((object)"Plugin AntiHacker v2.0.3 is loaded!"); } private void OnDestroy() { SteamMatchmaking.OnLobbyMemberJoined -= OnLobbyMemberJoined; SteamMatchmaking.OnLobbyMemberLeave -= OnLobbyMemberLeave; } private static void OnLobbyMemberJoined(Lobby lobby, Friend friend) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) ulong value = friend.Id.Value; Security.PendingJoins[value] = Time.realtimeSinceStartup; Log.LogInfo((object)($"AntiHacker: lobby join {value}" + (Security.IsBanned(value) ? " (BANNED)" : ""))); } private static void OnLobbyMemberLeave(Lobby lobby, Friend friend) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) Security.PendingJoins.Remove(friend.Id.Value); } private void PatchRpcSenderTracking() { //IL_002e: 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_0050: Expected O, but got Unknown //IL_0050: Expected O, but got Unknown try { MethodInfo methodInfo = AccessTools.Method(AccessTools.TypeByName("Unity.Netcode.RpcMessageHelpers"), "Handle", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Plugin), "HandlePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "HandleFinalizer", (Type[])null), (HarmonyMethod)null); } catch (Exception arg) { Log.LogError((object)$"Failed to patch RpcMessageHelpers.Handle: {arg}"); } } private static void HandlePrefix(ref __RpcParams rpcParams) { try { Security.CurrentSender = rpcParams.Server.Receive.SenderClientId; } catch { Security.CurrentSender = null; } } private static Exception HandleFinalizer(Exception __exception) { Security.CurrentSender = null; return __exception; } private void Update() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) Keyboard current = Keyboard.current; if (current != null && !((Object)(object)StartOfRound.Instance == (Object)null) && ((ButtonControl)current[RecoveryKey.Value]).wasPressedThisFrame) { RepairShip(); } } public static string RepairShip() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || !((NetworkBehaviour)instance).IsHost) { return "Only the host can repair the ship."; } if (!instance.travellingToNewLevel) { Log.LogInfo((object)"AntiHacker: ship is not stuck travelling, nothing to repair."); return "Ship is not stuck; nothing to repair."; } Log.LogWarning((object)"AntiHacker: clearing stuck travellingToNewLevel flag"); instance.travellingToNewLevel = false; StartMatchLever val = Object.FindObjectOfType<StartMatchLever>(); if ((Object)(object)val != (Object)null && (Object)(object)val.triggerScript != (Object)null) { val.triggerScript.interactable = true; } Terminal val2 = Object.FindObjectOfType<Terminal>(); if ((Object)(object)val2 != (Object)null) { val2.useCreditsCooldown = false; } return "Ship repaired: travel lock cleared, lever re-enabled."; } private static void ShowRotationTip() { if (!((Object)(object)HUDManager.Instance == (Object)null)) { bool flag = !Instance.RotationTipEveryTime.Value; HUDManager.Instance.DisplayTip("MOVING OBJECTS", "Arrow keys: rotate 90 degrees\nEnd: reset rotation (fixes flipped objects)", false, flag, "LC_AntiHackerRotateTip"); } } public static void DoUpdate() { //IL_043f: Unknown result type (might be due to invalid IL or missing references) //IL_0486: Unknown result type (might be due to invalid IL or missing references) //IL_07ad: Unknown result type (might be due to invalid IL or missing references) //IL_0531: Unknown result type (might be due to invalid IL or missing references) //IL_08d6: Unknown result type (might be due to invalid IL or missing references) //IL_08ea: Unknown result type (might be due to invalid IL or missing references) //IL_08ef: Unknown result type (might be due to invalid IL or missing references) //IL_06c5: Unknown result type (might be due to invalid IL or missing references) //IL_0584: Unknown result type (might be due to invalid IL or missing references) //IL_0589: Unknown result type (might be due to invalid IL or missing references) //IL_059d: 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_0926: Unknown result type (might be due to invalid IL or missing references) //IL_093a: Unknown result type (might be due to invalid IL or missing references) //IL_093f: Unknown result type (might be due to invalid IL or missing references) //IL_0976: Unknown result type (might be due to invalid IL or missing references) //IL_098a: Unknown result type (might be due to invalid IL or missing references) //IL_098f: Unknown result type (might be due to invalid IL or missing references) //IL_09c6: Unknown result type (might be due to invalid IL or missing references) //IL_09da: Unknown result type (might be due to invalid IL or missing references) //IL_09df: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_0387: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_0a2e: Unknown result type (might be due to invalid IL or missing references) //IL_0a35: Expected O, but got Unknown //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0a5f: Unknown result type (might be due to invalid IL or missing references) //IL_0a64: Unknown result type (might be due to invalid IL or missing references) //IL_0684: 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_0129: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_06af: Unknown result type (might be due to invalid IL or missing references) //IL_0ada: Unknown result type (might be due to invalid IL or missing references) //IL_0ac9: Unknown result type (might be due to invalid IL or missing references) //IL_0ace: Unknown result type (might be due to invalid IL or missing references) if (Keyboard.current == null || (Object)(object)GameNetworkManager.Instance == (Object)null || (Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)null) { return; } PlayerControllerB localPlayerController = GameNetworkManager.Instance.localPlayerController; if (localPlayerController.isTypingChat || localPlayerController.inTerminalMenu || localPlayerController.isPlayerDead) { return; } if ((Object)(object)cachedTerminalPlaceable == (Object)null && (Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.unlockablesList != (Object)null) { int num = -1; for (int i = 0; i < StartOfRound.Instance.unlockablesList.unlockables.Count; i++) { if (StartOfRound.Instance.unlockablesList.unlockables[i].unlockableName == "Terminal") { num = i; break; } } if (num != -1) { PlaceableShipObject[] array = Resources.FindObjectsOfTypeAll<PlaceableShipObject>(); foreach (PlaceableShipObject val in array) { if (val.unlockableID == num) { cachedTerminalPlaceable = val; break; } } } if ((Object)(object)cachedTerminalPlaceable != (Object)null && !defaultTerminalSet) { defaultTerminalPos = ((Component)cachedTerminalPlaceable.parentObject).transform.position; defaultTerminalRot = ((Component)cachedTerminalPlaceable.parentObject).transform.eulerAngles; defaultTerminalSet = true; Log.LogInfo((object)$"Terminal default position recorded: {defaultTerminalPos} | Rotation: {defaultTerminalRot}"); } } try { if (Instance.EnableTeleport.Value && ((ButtonControl)Keyboard.current[Instance.TeleportKey.Value]).wasPressedThisFrame) { ShipTeleporter[] array2 = Object.FindObjectsOfType<ShipTeleporter>(); Log.LogInfo((object)$"F5 triggered! Found {array2.Length} teleporters."); if (localPlayerController.isInHangarShipRoom) { if (RoundManager.Instance.insideAINodes != null && RoundManager.Instance.insideAINodes.Length != 0) { Vector3 position = RoundManager.Instance.insideAINodes[Random.Range(0, RoundManager.Instance.insideAINodes.Length)].transform.position; localPlayerController.isInElevator = false; localPlayerController.isInHangarShipRoom = false; localPlayerController.isInsideFactory = true; localPlayerController.averageVelocity = 0f; localPlayerController.velocityLastFrame = Vector3.zero; localPlayerController.TeleportPlayer(position, false, 0f, false, true); AudioReverbPresets val2 = Object.FindObjectOfType<AudioReverbPresets>(); if ((Object)(object)val2 != (Object)null && val2.audioPresets.Length > 2) { val2.audioPresets[2].ChangeAudioReverbForPlayer(localPlayerController); } for (int k = 0; k < localPlayerController.ItemSlots.Length; k++) { if ((Object)(object)localPlayerController.ItemSlots[k] != (Object)null) { localPlayerController.ItemSlots[k].isInFactory = true; localPlayerController.ItemSlots[k].isInShipRoom = false; } } if ((Object)(object)localPlayerController.ItemOnlySlot != (Object)null) { localPlayerController.ItemOnlySlot.isInFactory = true; localPlayerController.ItemOnlySlot.isInShipRoom = false; } Log.LogInfo((object)"Standalone Inverse Teleport triggered and synced!"); } else { Log.LogWarning((object)"No AI nodes available yet to teleport to!"); } } else { AudioReverbPresets val3 = Object.FindObjectOfType<AudioReverbPresets>(); if ((Object)(object)val3 != (Object)null && val3.audioPresets.Length > 3) { val3.audioPresets[3].ChangeAudioReverbForPlayer(localPlayerController); } localPlayerController.isInElevator = true; localPlayerController.isInHangarShipRoom = true; localPlayerController.isInsideFactory = false; localPlayerController.averageVelocity = 0f; localPlayerController.velocityLastFrame = Vector3.zero; Vector3 position2 = StartOfRound.Instance.playerSpawnPositions[0].position; localPlayerController.TeleportPlayer(position2, true, 160f, false, true); for (int l = 0; l < localPlayerController.ItemSlots.Length; l++) { if ((Object)(object)localPlayerController.ItemSlots[l] != (Object)null) { localPlayerController.ItemSlots[l].isInFactory = false; localPlayerController.ItemSlots[l].isInShipRoom = true; } } if ((Object)(object)localPlayerController.ItemOnlySlot != (Object)null) { localPlayerController.ItemOnlySlot.isInFactory = false; localPlayerController.ItemOnlySlot.isInShipRoom = true; } Log.LogInfo((object)"Standalone Regular Teleport triggered and synced!"); } } } catch (Exception arg) { Log.LogError((object)$"Error during F5 check: {arg}"); } if (((ButtonControl)Keyboard.current[Instance.LightSwitchKey.Value]).wasPressedThisFrame) { ShipLights val4 = Object.FindObjectOfType<ShipLights>(); if ((Object)(object)val4 != (Object)null) { val4.ToggleShipLights(); Log.LogInfo((object)"Ship lights toggled!"); } } if (((ButtonControl)Keyboard.current[Instance.MoveTerminalKey.Value]).wasPressedThisFrame) { Log.LogInfo((object)"F8 (MoveTerminalKey) was pressed!"); if ((Object)(object)cachedTerminalPlaceable == (Object)null) { Log.LogError((object)"Could not find PlaceableShipObject for Terminal!"); } else { Log.LogInfo((object)$"Found terminalPlaceable with unlockableID: {cachedTerminalPlaceable.unlockableID}"); int unlockableID = cachedTerminalPlaceable.unlockableID; UnlockableItem val5 = StartOfRound.Instance.unlockablesList.unlockables[unlockableID]; val5.canBeStored = true; if (!val5.inStorage && !(((Component)cachedTerminalPlaceable.parentObject).transform.position.y < -100f)) { Log.LogInfo((object)"Terminal is NOT in storage. Storing it via teleportation..."); if (ShipBuildModeManager.Instance.InBuildMode) { ShipBuildModeManager.Instance.CancelBuildMode(true); } ShipBuildModeManager.Instance.PlaceShipObjectServerRpc(new Vector3(0f, -500f, 0f), Vector3.zero, NetworkObjectReference.op_Implicit(((Component)cachedTerminalPlaceable.parentObject).GetComponent<NetworkObject>()), (int)localPlayerController.playerClientId); val5.inStorage = true; cachedTerminalPlaceable.parentObject.disableObject = true; } else { Log.LogInfo((object)"Terminal is in storage. Unstoring it and entering placement mode..."); StartOfRound.Instance.ReturnUnlockableFromStorageServerRpc(unlockableID); val5.inStorage = false; cachedTerminalPlaceable.parentObject.disableObject = false; ShipBuildModeManager.Instance.CancelBuildMode(true); if (!float.IsNaN(defaultTerminalRot.x) && !float.IsNaN(defaultTerminalRot.y) && !float.IsNaN(defaultTerminalRot.z) && !float.IsInfinity(defaultTerminalRot.x) && !float.IsInfinity(defaultTerminalRot.y) && !float.IsInfinity(defaultTerminalRot.z)) { ((Component)cachedTerminalPlaceable.parentObject).transform.eulerAngles = defaultTerminalRot; if ((Object)(object)cachedTerminalPlaceable.mainMesh != (Object)null) { ((Component)cachedTerminalPlaceable.mainMesh).transform.eulerAngles = defaultTerminalRot; } } else { Log.LogWarning((object)$"Skipped rotation reset because defaultTerminalRot was invalid: {defaultTerminalRot}"); } typeof(ShipBuildModeManager).GetField("placingObject", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(ShipBuildModeManager.Instance, cachedTerminalPlaceable); Collider[] componentsInChildren = ((Component)cachedTerminalPlaceable.parentObject).GetComponentsInChildren<Collider>(); typeof(ShipBuildModeManager).GetField("collidersInPlacingObject", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(ShipBuildModeManager.Instance, componentsInChildren); for (int m = 0; m < componentsInChildren.Length; m++) { if (!((Component)componentsInChildren[m]).CompareTag("DoNotSet") && !((Component)componentsInChildren[m]).CompareTag("InteractTrigger")) { componentsInChildren[m].enabled = false; } } ShipBuildModeManager.Instance.InBuildMode = true; typeof(ShipBuildModeManager).GetMethod("CreateGhostObjectAndHighlight", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(ShipBuildModeManager.Instance, null); } } } if (((ButtonControl)Keyboard.current[Instance.UnstoreKey.Value]).wasPressedThisFrame) { Log.LogInfo((object)"UnstoreKey was pressed!"); int num2 = -1; for (int n = 0; n < StartOfRound.Instance.unlockablesList.unlockables.Count; n++) { if (StartOfRound.Instance.unlockablesList.unlockables[n].inStorage) { num2 = n; break; } } if (num2 != -1) { Log.LogInfo((object)$"Found stored item with ID {num2}. Unstoring and entering build mode..."); StartOfRound.Instance.ReturnUnlockableFromStorageServerRpc(num2); StartOfRound.Instance.unlockablesList.unlockables[num2].inStorage = false; ((MonoBehaviour)GameNetworkManager.Instance).StartCoroutine(WaitAndEnterBuildMode(num2)); } else { Log.LogInfo((object)"No items found in storage to unstore."); } } if (!((Object)(object)ShipBuildModeManager.Instance != (Object)null) || !ShipBuildModeManager.Instance.InBuildMode || !((Object)(object)ShipBuildModeManager.Instance.ghostObject != (Object)null)) { return; } if (((ButtonControl)Keyboard.current[(Key)63]).wasPressedThisFrame) { Transform ghostObject = ShipBuildModeManager.Instance.ghostObject; ghostObject.eulerAngles += new Vector3(90f, 0f, 0f); Log.LogInfo((object)"Rotated ghostObject +90 on X axis"); } if (((ButtonControl)Keyboard.current[(Key)64]).wasPressedThisFrame) { Transform ghostObject2 = ShipBuildModeManager.Instance.ghostObject; ghostObject2.eulerAngles += new Vector3(-90f, 0f, 0f); Log.LogInfo((object)"Rotated ghostObject -90 on X axis"); } if (((ButtonControl)Keyboard.current[(Key)62]).wasPressedThisFrame) { Transform ghostObject3 = ShipBuildModeManager.Instance.ghostObject; ghostObject3.eulerAngles += new Vector3(0f, 0f, 90f); Log.LogInfo((object)"Rotated ghostObject +90 on Z axis"); } if (((ButtonControl)Keyboard.current[(Key)61]).wasPressedThisFrame) { Transform ghostObject4 = ShipBuildModeManager.Instance.ghostObject; ghostObject4.eulerAngles += new Vector3(0f, 0f, -90f); Log.LogInfo((object)"Rotated ghostObject -90 on Z axis"); } if (!((ButtonControl)Keyboard.current[(Key)69]).wasPressedThisFrame) { return; } PlaceableShipObject val6 = (PlaceableShipObject)typeof(ShipBuildModeManager).GetField("placingObject", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(ShipBuildModeManager.Instance); if (!((Object)(object)val6 != (Object)null)) { return; } UnlockableItem val7 = StartOfRound.Instance.unlockablesList.unlockables[val6.unlockableID]; Vector3 eulerAngles = Vector3.zero; if (!val7.spawnPrefab) { ((Vector3)(ref eulerAngles))..ctor(270f, 0f, 0f); } else if ((Object)(object)val7.prefabObject != (Object)null) { PlaceableShipObject componentInChildren = val7.prefabObject.GetComponentInChildren<PlaceableShipObject>(); if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.mainMesh != (Object)null) { eulerAngles = ((Component)componentInChildren.mainMesh).transform.localEulerAngles; } } ShipBuildModeManager.Instance.ghostObject.eulerAngles = eulerAngles; Log.LogInfo((object)"Reset ghostObject rotation to default via End key"); } private static IEnumerator WaitAndEnterBuildMode(int unlockableID) { PlaceableShipObject foundPlaceable = null; float timeout = 2f; while (timeout > 0f) { PlaceableShipObject[] array = Object.FindObjectsOfType<PlaceableShipObject>(); foreach (PlaceableShipObject val in array) { if ((Object)(object)val != (Object)null && (Object)(object)val.parentObject != (Object)null && val.unlockableID == unlockableID) { NetworkObject component = ((Component)val.parentObject).GetComponent<NetworkObject>(); if ((Object)(object)component != (Object)null && component.IsSpawned) { foundPlaceable = val; break; } } } if ((Object)(object)foundPlaceable != (Object)null) { break; } timeout -= Time.deltaTime; yield return null; } if ((Object)(object)foundPlaceable != (Object)null) { foundPlaceable.parentObject.disableObject = false; if (ShipBuildModeManager.Instance.InBuildMode) { ShipBuildModeManager.Instance.CancelBuildMode(true); } typeof(ShipBuildModeManager).GetField("placingObject", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(ShipBuildModeManager.Instance, foundPlaceable); Collider[] componentsInChildren = ((Component)foundPlaceable.parentObject).GetComponentsInChildren<Collider>(); typeof(ShipBuildModeManager).GetField("collidersInPlacingObject", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(ShipBuildModeManager.Instance, componentsInChildren); for (int j = 0; j < componentsInChildren.Length; j++) { if (!((Component)componentsInChildren[j]).CompareTag("DoNotSet") && !((Component)componentsInChildren[j]).CompareTag("InteractTrigger")) { componentsInChildren[j].enabled = false; } } ShipBuildModeManager.Instance.InBuildMode = true; typeof(ShipBuildModeManager).GetMethod("CreateGhostObjectAndHighlight", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(ShipBuildModeManager.Instance, null); } else { Log.LogError((object)$"Could not find PlaceableShipObject for un-stored item ID {unlockableID} after waiting!"); } } } [HarmonyPatch(typeof(PlayerControllerB))] internal static class PlayerControllerBPatch { [HarmonyPostfix] [HarmonyPatch("Update")] private static void UpdatePostfix(PlayerControllerB __instance) { if ((Object)(object)__instance == (Object)(object)GameNetworkManager.Instance.localPlayerController) { Plugin.DoUpdate(); } } } internal static class Security { public static readonly ulong[] DefaultBlacklist = new ulong[2] { 76561199295933088uL, 76561198638639114uL }; private static readonly HashSet<ulong> banned = new HashSet<ulong>(); public static readonly Dictionary<ulong, float> PendingJoins = new Dictionary<ulong, float>(); public static readonly Dictionary<ulong, ulong> ClientSteamIds = new Dictionary<ulong, ulong>(); public static ulong? CurrentSender; private const float PendingLifetime = 120f; public static string KickListPath => Path.Combine(Application.persistentDataPath, "AntiHacker_KickList.txt"); public static void Init() { banned.Clear(); ulong[] defaultBlacklist = DefaultBlacklist; foreach (ulong item in defaultBlacklist) { banned.Add(item); } string[] array = Plugin.Instance.ExtraBlacklist.Value.Split(new char[3] { ',', ';', ' ' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { if (ulong.TryParse(array[i].Trim(), out var result)) { banned.Add(result); } } try { if (File.Exists(KickListPath)) { array = File.ReadAllLines(KickListPath); for (int i = 0; i < array.Length; i++) { if (ulong.TryParse(array[i].Trim(), out var result2)) { banned.Add(result2); } } } } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed reading kick list: {arg}"); } Plugin.Log.LogInfo((object)$"AntiHacker: {banned.Count} Steam IDs on the ban list."); } public static bool IsBanned(ulong steamId) { if (steamId != 0L) { return banned.Contains(steamId); } return false; } public static void Ban(ulong steamId, string reason) { if (steamId == 0L) { return; } bool num = banned.Add(steamId); Plugin.Log.LogWarning((object)$"AUTOBAN {steamId}: {reason}"); if (!num || !Plugin.Instance.PersistKickList.Value) { return; } try { File.AppendAllText(KickListPath, steamId + Environment.NewLine); } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed writing kick list: {arg}"); } } public static void PurgePending() { List<ulong> list = null; foreach (KeyValuePair<ulong, float> pendingJoin in PendingJoins) { if (Time.realtimeSinceStartup - pendingJoin.Value > 120f) { if (list == null) { list = new List<ulong>(); } list.Add(pendingJoin.Key); } } if (list == null) { return; } foreach (ulong item in list) { PendingJoins.Remove(item); } } public static void Punish(ulong clientId, string reason) { Plugin.Log.LogWarning((object)$"HACK ATTEMPT from clientId {clientId}: {reason}"); if ((!ClientSteamIds.TryGetValue(clientId, out var value) || value == 0L) && (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 && val.playerSteamId != 0L) { value = val.playerSteamId; break; } } } if (value != 0L) { Ban(value, reason); } else { Plugin.Log.LogWarning((object)$"No real Steam ID known for clientId {clientId}; disconnecting only."); } try { if ((Object)(object)NetworkManager.Singleton != (Object)null && NetworkManager.Singleton.IsServer) { NetworkManager.Singleton.DisconnectClient(clientId); } } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed to disconnect {clientId}: {arg}"); } } } } namespace AntiHacker.Patches { [HarmonyPatch(typeof(ModdedCheck))] internal class ModdedCheckPatch { [HarmonyPatch("GetModdedState")] [HarmonyPostfix] private static void PostfixGetModdedState(ref ModdedState __result) { if (Plugin.Instance.ForceVanillaLobby.Value) { __result = (ModdedState)1; } else if (!Chainloader.PluginInfos.Keys.Any((string guid) => guid != "com.invertigo.antihacker")) { __result = (ModdedState)1; } else { Plugin.Log.LogInfo((object)"AntiHacker: Other mods detected. Leaving server as Modded."); } } } [HarmonyPatch(typeof(GameNetworkManager))] internal class NetworkPatch { private static void Reject(ConnectionApprovalResponse response, string reason) { response.Reason = reason; response.CreatePlayerObject = false; response.Approved = false; response.Pending = false; } [HarmonyPatch("ConnectionApproval")] [HarmonyPrefix] private static bool ConnectionApprovalPrefix(GameNetworkManager __instance, ConnectionApprovalRequest request, ConnectionApprovalResponse response) { //IL_0014: 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) try { if (!NetworkManager.Singleton.IsHost) { return true; } ulong clientNetworkId = request.ClientNetworkId; if (clientNetworkId == NetworkManager.Singleton.LocalClientId) { return true; } if (__instance.disableSteam) { return true; } Security.PurgePending(); ulong result = 0uL; string[] array = Encoding.ASCII.GetString(request.Payload).Split(','); if (array.Length >= 2) { ulong.TryParse(array[1], out result); } ulong num = 0uL; bool flag = result != 0L && Security.PendingJoins.ContainsKey(result); if (flag) { num = result; } else if (Security.PendingJoins.Count == 1) { foreach (KeyValuePair<ulong, float> pendingJoin in Security.PendingJoins) { num = pendingJoin.Key; } } Plugin.Log.LogInfo((object)$"AntiHacker: approval clientId={clientNetworkId} payloadId={result} realId={num} pending={Security.PendingJoins.Count}"); if (Security.IsBanned(result) || Security.IsBanned(num)) { Plugin.Log.LogWarning((object)$"Blocked banned player. payload={result} real={num}"); Security.PendingJoins.Remove(num); Reject(response, "You have been permanently kicked from this lobby."); return false; } if (!flag && Security.PendingJoins.Count > 0) { Plugin.Log.LogWarning((object)$"Hacker detected! Claimed Steam ID {result} did not join the lobby (real: {num})."); if (num != 0L) { Security.Ban(num, $"Spoofed Steam ID {result}"); } Security.PendingJoins.Remove(num); Reject(response, "Spoofed Steam ID detected. Connection rejected."); return false; } if (result != 0L && Security.ClientSteamIds.ContainsValue(result)) { foreach (KeyValuePair<ulong, ulong> clientSteamId in Security.ClientSteamIds) { if (clientSteamId.Value == result && NetworkManager.Singleton.ConnectedClients.ContainsKey(clientSteamId.Key)) { Plugin.Log.LogWarning((object)$"Duplicate Steam ID {result} already connected as client {clientSteamId.Key}."); if (num != 0L && num != result) { Security.Ban(num, "Impersonating connected player " + result); } Reject(response, "Duplicate connection rejected."); return false; } } } ulong num2 = ((num != 0L) ? num : result); if (num2 != 0L) { Security.ClientSteamIds[clientNetworkId] = num2; Security.PendingJoins.Remove(num2); } } catch (Exception arg) { Plugin.Log.LogError((object)$"Error in ConnectionApprovalPrefix: {arg}"); } return true; } } [HarmonyPatch(typeof(StartOfRound))] internal class StartOfRoundPatch { [HarmonyPatch("KickPlayer")] [HarmonyPostfix] private static void KickPlayerPostfix(StartOfRound __instance, int playerObjToKick) { if (!((NetworkBehaviour)__instance).IsServer) { return; } try { if (!GameNetworkManager.Instance.disableSteam) { PlayerControllerB val = __instance.allPlayerScripts[playerObjToKick]; ulong num = val.playerSteamId; if (Security.ClientSteamIds.TryGetValue(val.actualClientId, out var value)) { num = value; } if (num != 0) { Security.Ban(num, "Kicked by host"); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"Error capturing kicked player: {arg}"); } } } internal static class RpcGuard { private static readonly Dictionary<ulong, List<float>> placeTimes = new Dictionary<ulong, List<float>>(); private static readonly Dictionary<ulong, List<float>> shipGrabTimes = new Dictionary<ulong, List<float>>(); private const int MaxCreditIncreaseBeforeBan = 1000; public static bool IsRemote(out ulong sender) { sender = 0uL; if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsServer) { return false; } if (!Security.CurrentSender.HasValue) { return false; } sender = Security.CurrentSender.Value; return sender != 0; } public static bool IsFinite(Vector3 v) { //IL_0000: 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) //IL_001a: 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_0034: 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) if (!float.IsNaN(v.x) && !float.IsInfinity(v.x) && !float.IsNaN(v.y) && !float.IsInfinity(v.y) && !float.IsNaN(v.z)) { return !float.IsInfinity(v.z); } return false; } public static bool RateLimited(ulong sender, int maxPerSecond) { if (!placeTimes.TryGetValue(sender, out var value)) { value = new List<float>(); placeTimes[sender] = value; } float now = Time.realtimeSinceStartup; value.RemoveAll((float t) => now - t > 1f); value.Add(now); return value.Count > maxPerSecond; } public static bool ShipGrabRateLimited(ulong sender, int maxPerSecond, out int countInWindow) { if (!shipGrabTimes.TryGetValue(sender, out var value)) { value = new List<float>(); shipGrabTimes[sender] = value; } float now = Time.realtimeSinceStartup; value.RemoveAll((float t) => now - t > 1f); value.Add(now); countInWindow = value.Count; return countInWindow > maxPerSecond; } public static bool InsideShip(Vector3 pos) { //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) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.shipBounds == (Object)null) { return true; } Bounds bounds = instance.shipBounds.bounds; ((Bounds)(ref bounds)).Expand(40f); if (((Bounds)(ref bounds)).Contains(pos)) { return true; } if ((Object)(object)instance.elevatorTransform != (Object)null && ((Bounds)(ref bounds)).Contains(instance.elevatorTransform.TransformPoint(pos))) { return true; } return false; } public static bool InsideShipInnerRoom(Vector3 worldPos) { //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) //IL_0039: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.shipInnerRoomBounds == (Object)null) { return false; } Bounds bounds = instance.shipInnerRoomBounds.bounds; ((Bounds)(ref bounds)).Expand(1f); return ((Bounds)(ref bounds)).Contains(worldPos); } public static bool IsCompanyMoon() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.currentLevel == (Object)null) { return false; } SelectableLevel currentLevel = instance.currentLevel; if (currentLevel.levelID != 3 && (string.IsNullOrEmpty(currentLevel.sceneName) || currentLevel.sceneName.IndexOf("Company", StringComparison.OrdinalIgnoreCase) < 0)) { if (!string.IsNullOrEmpty(currentLevel.PlanetName)) { return currentLevel.PlanetName.IndexOf("Gordion", StringComparison.OrdinalIgnoreCase) >= 0; } return false; } return true; } public static bool RejectGrab(ref NetworkObjectReference grabbedObject) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) grabbedObject = default(NetworkObjectReference); return true; } public static bool CreditsValid(ulong sender, int newCredits, string rpcName) { Terminal val = Object.FindObjectOfType<Terminal>(); if ((Object)(object)val == (Object)null) { return true; } int groupCredits = val.groupCredits; if (newCredits < 0 || newCredits > groupCredits + 1000) { Security.Punish(sender, $"{rpcName} with invalid credits {newCredits} (host has {groupCredits})"); return false; } if (newCredits > groupCredits) { Plugin.Log.LogWarning((object)$"AntiHacker: dropped {rpcName} from client {sender}: credits {newCredits} > host {groupCredits}"); return false; } return true; } } [HarmonyPatch(typeof(PlayerControllerB))] internal class PlayerItemRpcGuard { private const float MaxGrabDistance = 15f; private static bool IsExemptFromShipScrapLock(GrabbableObject grabbable) { if ((Object)(object)grabbable == (Object)null) { return true; } if (grabbable is RagdollGrabbableObject || grabbable is ClipboardItem) { return true; } string name = ((Object)grabbable).name; if (!string.IsNullOrEmpty(name) && (name.IndexOf("Clipboard", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("StickyNote", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Ragdoll", StringComparison.OrdinalIgnoreCase) >= 0)) { return true; } return false; } [HarmonyPatch("GrabObjectServerRpc")] [HarmonyPrefix] private static bool GrabObject(PlayerControllerB __instance, ref NetworkObjectReference grabbedObject) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (!RpcGuard.IsRemote(out var sender)) { return true; } NetworkObject val = default(NetworkObject); if (!((NetworkObjectReference)(ref grabbedObject)).TryGet(ref val, (NetworkManager)null) || (Object)(object)val == (Object)null) { return true; } GrabbableObject componentInChildren = ((Component)val).GetComponentInChildren<GrabbableObject>(); if ((Object)(object)componentInChildren == (Object)null) { return true; } float num = Vector3.Distance(((Component)__instance).transform.position, ((Component)componentInChildren).transform.position); if (num > 15f) { Plugin.Log.LogWarning((object)$"AntiHacker: blocked client {sender} from grabbing '{((Object)componentInChildren).name}' from {num:F1}m away"); return RpcGuard.RejectGrab(ref grabbedObject); } if (componentInChildren.isInShipRoom && !RpcGuard.IsCompanyMoon()) { if (Plugin.Instance.LockShipScrapUntilCompany.Value && componentInChildren.scrapValue > 0 && !IsExemptFromShipScrapLock(componentInChildren)) { return RpcGuard.RejectGrab(ref grabbedObject); } int num2 = Math.Max(1, Plugin.Instance.MaxShipGrabsPerSecond.Value); if (RpcGuard.ShipGrabRateLimited(sender, num2, out var countInWindow)) { Plugin.Log.LogWarning((object)$"AntiHacker: rate-limited client {sender} grabbing ship object '{((Object)componentInChildren).name}' ({countInWindow}/{num2} per sec)"); return RpcGuard.RejectGrab(ref grabbedObject); } } return true; } [HarmonyPatch("ThrowObjectServerRpc")] [HarmonyPrefix] private static bool ThrowObject(PlayerControllerB __instance, NetworkObjectReference grabbedObject, ref bool droppedInElevator, ref bool droppedInShipRoom, ref Vector3 targetFloorPosition) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_0106: 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_00fb: 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_0122: 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_017c: 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) if (!RpcGuard.IsRemote(out var sender)) { return true; } NetworkObject val = default(NetworkObject); if (!((NetworkObjectReference)(ref grabbedObject)).TryGet(ref val, (NetworkManager)null) || (Object)(object)val == (Object)null) { return false; } GrabbableObject componentInChildren = ((Component)val).GetComponentInChildren<GrabbableObject>(); if ((Object)(object)componentInChildren == (Object)null) { return false; } if ((Object)(object)__instance.currentlyHeldObjectServer != (Object)(object)componentInChildren && (Object)(object)componentInChildren.playerHeldBy != (Object)(object)__instance) { if (componentInChildren.isInShipRoom && !droppedInShipRoom) { Security.Punish(sender, $"ThrowObjectServerRpc on unheld ship item '{((Object)componentInChildren).name}' with droppedInShipRoom={droppedInShipRoom}"); return false; } Plugin.Log.LogWarning((object)$"AntiHacker: dropped ThrowObjectServerRpc from client {sender} for unheld item '{((Object)componentInChildren).name}'"); return false; } if (!RpcGuard.IsFinite(targetFloorPosition)) { Security.Punish(sender, $"ThrowObjectServerRpc with non-finite position {targetFloorPosition}"); return false; } StartOfRound instance = StartOfRound.Instance; Transform val2 = ((!droppedInElevator) ? instance?.propsContainer : instance?.elevatorTransform); Vector3 val3 = (((Object)(object)val2 != (Object)null) ? val2.TransformPoint(targetFloorPosition) : targetFloorPosition); if ((__instance.isInHangarShipRoom || componentInChildren.isInShipRoom) && RpcGuard.InsideShipInnerRoom(val3) && (!droppedInShipRoom || !droppedInElevator)) { Plugin.Log.LogWarning((object)$"AntiHacker: client {sender} dropped '{((Object)componentInChildren).name}' inside ship room with droppedInShipRoom={droppedInShipRoom}; forcing ship flags true"); if (!droppedInElevator && (Object)(object)instance?.elevatorTransform != (Object)null) { targetFloorPosition = instance.elevatorTransform.InverseTransformPoint(val3); } droppedInElevator = true; droppedInShipRoom = true; } return true; } [HarmonyPatch("PlaceObjectServerRpc")] [HarmonyPrefix] private static bool PlaceObject(PlayerControllerB __instance, NetworkObjectReference grabbedObject, NetworkObjectReference parentObject, Vector3 placePositionOffset) { //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) if (!RpcGuard.IsRemote(out var sender)) { return true; } NetworkObject val = default(NetworkObject); if (!((NetworkObjectReference)(ref grabbedObject)).TryGet(ref val, (NetworkManager)null) || (Object)(object)val == (Object)null) { return false; } GrabbableObject componentInChildren = ((Component)val).GetComponentInChildren<GrabbableObject>(); if ((Object)(object)componentInChildren == (Object)null) { return false; } if ((Object)(object)__instance.currentlyHeldObjectServer != (Object)(object)componentInChildren && (Object)(object)componentInChildren.playerHeldBy != (Object)(object)__instance) { if (componentInChildren.isInShipRoom) { Security.Punish(sender, "PlaceObjectServerRpc on unheld ship item '" + ((Object)componentInChildren).name + "'"); return false; } Plugin.Log.LogWarning((object)$"AntiHacker: dropped PlaceObjectServerRpc from client {sender} for unheld item '{((Object)componentInChildren).name}'"); return false; } NetworkObject val2 = default(NetworkObject); if (!RpcGuard.IsFinite(placePositionOffset) || !((NetworkObjectReference)(ref parentObject)).TryGet(ref val2, (NetworkManager)null) || (Object)(object)val2 == (Object)null) { Security.Punish(sender, $"PlaceObjectServerRpc with invalid parentObject or offset {placePositionOffset}"); return false; } if ((Object)(object)((Component)val2).GetComponent<EnemyAINestSpawnObject>() != (Object)null || (Object)(object)((Component)val2).GetComponentInChildren<EnemyAINestSpawnObject>() != (Object)null) { Security.Punish(sender, "PlaceObjectServerRpc parenting '" + ((Object)componentInChildren).name + "' onto EnemyAINestSpawnObject"); return false; } return true; } } [HarmonyPatch(typeof(StartOfRound))] internal class StartOfRoundRpcGuard { [HarmonyPatch("ChangeLevelServerRpc")] [HarmonyPrefix] private static bool ChangeLevel(StartOfRound __instance, int levelID, int newGroupCreditsAmount) { if (!RpcGuard.IsRemote(out var sender)) { return true; } if (levelID < 0 || levelID >= __instance.levels.Length) { Security.Punish(sender, $"ChangeLevelServerRpc with invalid level {levelID}"); return false; } return RpcGuard.CreditsValid(sender, newGroupCreditsAmount, "ChangeLevelServerRpc"); } [HarmonyPatch("BuyShipUnlockableServerRpc")] [HarmonyPrefix] private static bool BuyUnlockable(StartOfRound __instance, int unlockableID, int newGroupCreditsAmount) { if (!RpcGuard.IsRemote(out var sender)) { return true; } if (unlockableID < 0 || unlockableID >= __instance.unlockablesList.unlockables.Count) { Security.Punish(sender, $"BuyShipUnlockableServerRpc with invalid id {unlockableID}"); return false; } return RpcGuard.CreditsValid(sender, newGroupCreditsAmount, "BuyShipUnlockableServerRpc"); } [HarmonyPatch("ReturnUnlockableFromStorageServerRpc")] [HarmonyPrefix] private static bool ReturnUnlockable(StartOfRound __instance, int unlockableID) { if (!RpcGuard.IsRemote(out var sender)) { return true; } if (unlockableID < 0 || unlockableID >= __instance.unlockablesList.unlockables.Count) { Security.Punish(sender, $"ReturnUnlockableFromStorageServerRpc with invalid id {unlockableID}"); return false; } return true; } [HarmonyPatch("StartGameServerRpc")] [HarmonyPrefix] private static bool StartGame() { return AllowLever(); } [HarmonyPatch("EndGameServerRpc")] [HarmonyPrefix] private static bool EndGame() { return AllowLever(); } [HarmonyPatch("ManuallyEjectPlayersServerRpc")] [HarmonyPrefix] private static bool Eject() { return AllowLever(); } private static bool AllowLever() { if (!RpcGuard.IsRemote(out var _)) { return true; } return Plugin.Instance.AllowClientStartEnd.Value; } [HarmonyPatch("Debug_EnableTestRoomServerRpc")] [HarmonyPrefix] private static bool DebugTestRoom() { return DebugBlock("Debug_EnableTestRoomServerRpc"); } [HarmonyPatch("Debug_ToggleAllowDeathServerRpc")] [HarmonyPrefix] private static bool DebugDeath() { return DebugBlock("Debug_ToggleAllowDeathServerRpc"); } [HarmonyPatch("Debug_ReviveAllPlayersServerRpc")] [HarmonyPrefix] private static bool DebugRevive() { return DebugBlock("Debug_ReviveAllPlayersServerRpc"); } private static bool DebugBlock(string name) { if (!RpcGuard.IsRemote(out var sender)) { return true; } Security.Punish(sender, name + " called by a client"); return false; } } [HarmonyPatch(typeof(ShipBuildModeManager))] internal class ShipBuildRpcGuard { private static bool IsLightSwitch(NetworkObject netObj, UnlockableItem unlockable) { if ((Object)(object)netObj != (Object)null) { if ((Object)(object)((Component)netObj).GetComponentInChildren<ShipLights>() != (Object)null) { return true; } if (!string.IsNullOrEmpty(((Object)netObj).name) && ((Object)netObj).name.IndexOf("LightSwitch", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } if (unlockable != null && !string.IsNullOrEmpty(unlockable.unlockableName) && unlockable.unlockableName.IndexOf("Light switch", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } return false; } [HarmonyPatch("PlaceShipObjectServerRpc")] [HarmonyPrefix] private static bool PlaceShipObject(Vector3 newPosition, Vector3 newRotation, NetworkObjectReference objectRef) { //IL_000b: 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_0013: 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) if (!RpcGuard.IsRemote(out var sender)) { return true; } if (!RpcGuard.IsFinite(newPosition) || !RpcGuard.IsFinite(newRotation) || newPosition.y <= -100f || !RpcGuard.InsideShip(newPosition)) { Security.Punish(sender, $"PlaceShipObjectServerRpc to invalid position {newPosition}"); return false; } NetworkObject val = default(NetworkObject); if (((NetworkObjectReference)(ref objectRef)).TryGet(ref val, (NetworkManager)null) && (Object)(object)val != (Object)null) { UnlockableItem val2 = null; PlaceableShipObject componentInChildren = ((Component)val).GetComponentInChildren<PlaceableShipObject>(); if ((Object)(object)componentInChildren != (Object)null && (Object)(object)StartOfRound.Instance != (Object)null && componentInChildren.unlockableID >= 0 && componentInChildren.unlockableID < StartOfRound.Instance.unlockablesList.unlockables.Count) { val2 = StartOfRound.Instance.unlockablesList.unlockables[componentInChildren.unlockableID]; } if (IsLightSwitch(val, val2)) { Plugin.Log.LogWarning((object)$"AntiHacker: blocked client {sender} from moving the Light Switch"); return false; } if (val2 != null && val2.unlockableName == "Terminal") { Plugin.Log.LogWarning((object)$"AntiHacker: blocked client {sender} from moving the Terminal"); return false; } } if (RpcGuard.RateLimited(sender, 10)) { Plugin.Log.LogWarning((object)$"AntiHacker: dropping placement spam from client {sender}"); return false; } return true; } [HarmonyPatch("StoreObjectServerRpc")] [HarmonyPrefix] private static bool StoreObject(NetworkObjectReference objectRef) { if (!RpcGuard.IsRemote(out var sender)) { return true; } NetworkObject val = default(NetworkObject); if (((NetworkObjectReference)(ref objectRef)).TryGet(ref val, (NetworkManager)null) && (Object)(object)val != (Object)null) { UnlockableItem val2 = null; PlaceableShipObject componentInChildren = ((Component)val).GetComponentInChildren<PlaceableShipObject>(); if ((Object)(object)componentInChildren != (Object)null && (Object)(object)StartOfRound.Instance != (Object)null && componentInChildren.unlockableID >= 0 && componentInChildren.unlockableID < StartOfRound.Instance.unlockablesList.unlockables.Count) { val2 = StartOfRound.Instance.unlockablesList.unlockables[componentInChildren.unlockableID]; } if (IsLightSwitch(val, val2)) { Plugin.Log.LogWarning((object)$"AntiHacker: blocked client {sender} from storing the Light Switch"); return false; } if (val2 != null && (!val2.canBeStored || val2.unlockableName == "Terminal" || val2.unlockableName == "Teleporter" || val2.unlockableName == "Inverse Teleporter")) { Plugin.Log.LogWarning((object)$"AntiHacker: blocked client {sender} from storing protected ship object '{val2.unlockableName}'"); return false; } } if (RpcGuard.RateLimited(sender, 5)) { Plugin.Log.LogWarning((object)$"AntiHacker: dropping store spam from client {sender}"); return false; } return true; } } [HarmonyPatch(typeof(Terminal))] internal class TerminalRpcGuard { [HarmonyPatch("SyncGroupCreditsServerRpc")] [HarmonyPrefix] private static bool SyncGroupCredits(int newGroupCredits) { if (!RpcGuard.IsRemote(out var sender)) { return true; } return RpcGuard.CreditsValid(sender, newGroupCredits, "SyncGroupCreditsServerRpc"); } [HarmonyPatch("BuyItemsServerRpc")] [HarmonyPrefix] private static bool BuyItems(int newGroupCredits) { if (!RpcGuard.IsRemote(out var sender)) { return true; } return RpcGuard.CreditsValid(sender, newGroupCredits, "BuyItemsServerRpc"); } [HarmonyPatch("BuyVehicleServerRpc")] [HarmonyPrefix] private static bool BuyVehicle(int newGroupCredits) { if (!RpcGuard.IsRemote(out var sender)) { return true; } return RpcGuard.CreditsValid(sender, newGroupCredits, "BuyVehicleServerRpc"); } } [HarmonyPatch(typeof(Terminal))] internal class TerminalCommandPatch { private static TerminalNode Reply(string text) { TerminalNode obj = ScriptableObject.CreateInstance<TerminalNode>(); obj.clearPreviousText = true; obj.displayText = text + "\n\n"; return obj; } [HarmonyPatch("ParsePlayerSentence")] [HarmonyPrefix] private static bool ParsePlayerSentencePrefix(Terminal __instance, ref TerminalNode __result) { string text = __instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded).Replace("'", "").Replace(":", "") .Replace(";", "") .Replace(",", "") .Replace(".", "") .Trim() .ToLowerInvariant(); if (text == "fixship") { __result = Reply(Plugin.RepairShip()); return false; } if (Plugin.Instance.EnableMoneyCommand.Value && text.StartsWith("money ")) { __result = HandleMoney(__instance, text.Substring(6).Trim()); return false; } if (text == "clean all") { __result = HandleCleanAll(); return false; } if (text.StartsWith("clean ")) { __result = HandleClean(__instance, text.Substring(6).Trim()); return false; } return true; } private static TerminalNode HandleCleanAll() { //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsHost) { return Reply("Only the host can clean ship objects."); } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { return Reply("Player not found."); } int num = 0; PlaceableShipObject[] array = Object.FindObjectsOfType<PlaceableShipObject>(); foreach (PlaceableShipObject val2 in array) { if ((Object)(object)val2 == (Object)null || (Object)(object)val2.parentObject == (Object)null) { continue; } int unlockableID = val2.unlockableID; if (unlockableID < 0 || unlockableID >= StartOfRound.Instance.unlockablesList.unlockables.Count) { continue; } UnlockableItem val3 = StartOfRound.Instance.unlockablesList.unlockables[unlockableID]; if (val3 != null && !(val3.unlockableName == "Terminal") && !val3.inStorage && val3.canBeStored && !(((Component)val2.parentObject).transform.position.y < -100f) && !((Object)(object)((Component)val2.parentObject).GetComponentInChildren<GrabbableObject>() != (Object)null)) { if (ShipBuildModeManager.Instance.InBuildMode) { ShipBuildModeManager.Instance.CancelBuildMode(true); } NetworkObject component = ((Component)val2.parentObject).GetComponent<NetworkObject>(); if (!((Object)(object)component == (Object)null)) { NetworkObjectReference val4 = NetworkObjectReference.op_Implicit(component); ShipBuildModeManager.Instance.StoreObjectServerRpc(val4, (int)val.playerClientId); num++; } } } return Reply($"Stored {num} objects."); } private static TerminalNode HandleClean(Terminal terminal, string searchName) { //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018d: 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) if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsHost) { return Reply("Only the host can clean ship objects."); } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { return Reply("Player not found."); } int num = 0; string arg = ""; PlaceableShipObject[] array = Object.FindObjectsOfType<PlaceableShipObject>(); foreach (PlaceableShipObject val2 in array) { if ((Object)(object)val2 == (Object)null || (Object)(object)val2.parentObject == (Object)null) { continue; } int unlockableID = val2.unlockableID; if (unlockableID < 0 || unlockableID >= StartOfRound.Instance.unlockablesList.unlockables.Count) { continue; } UnlockableItem val3 = StartOfRound.Instance.unlockablesList.unlockables[unlockableID]; if (val3 != null && !string.IsNullOrEmpty(val3.unlockableName) && val3.unlockableName.ToLower().Contains(searchName) && !(val3.unlockableName == "Terminal") && !val3.inStorage && val3.canBeStored && !(((Component)val2.parentObject).transform.position.y < -100f) && !((Object)(object)((Component)val2.parentObject).GetComponentInChildren<GrabbableObject>() != (Object)null)) { if (ShipBuildModeManager.Instance.InBuildMode) { ShipBuildModeManager.Instance.CancelBuildMode(true); } NetworkObject component = ((Component)val2.parentObject).GetComponent<NetworkObject>(); if (!((Object)(object)component == (Object)null)) { NetworkObjectReference val4 = NetworkObjectReference.op_Implicit(component); ShipBuildModeManager.Instance.StoreObjectServerRpc(val4, (int)val.playerClientId); num++; arg = val3.unlockableName; } } } if (num > 0) { return Reply($"Stored {num} '{arg}'(s)."); } return Reply("Could not find a storable object matching '" + searchName + "' on the ship."); } private static TerminalNode HandleMoney(Terminal terminal, string arg) { if (!NetworkManager.Singleton.IsHost) { return Reply("Only the host can change credits."); } bool flag = arg.StartsWith("+") || arg.StartsWith("-"); if (!int.TryParse(arg, out var result)) { return Reply("Usage: money <amount> (or money +<amount> / money -<amount>)"); } int num = (flag ? (terminal.groupCredits + result) : result); num = Mathf.Max(0, num); terminal.SyncGroupCreditsServerRpc(num, terminal.numberOfItemsInDropship); Plugin.Log.LogInfo((object)$"AntiHacker: money command set credits to {num}"); return Reply($"Group credits set to {num}."); } } }