Decompiled source of AntiHacker v2.0.3

AntiHacker.dll

Decompiled a day ago
using 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}.");
		}
	}
}