Decompiled source of PowerTerminalOS v1.0.0

BepInEx\plugins\PowerTerminalOS\PowerTerminalOS.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Netcode;
using UnityEngine;

[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("PowerTerminalOS")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PowerTerminalOS")]
[assembly: AssemblyTitle("PowerTerminalOS")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PowerTerminalOS
{
	[BepInPlugin("anton.powerterminalos", "PowerTerminalOS", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "anton.powerterminalos";

		public const string Name = "PowerTerminalOS";

		public const string Version = "1.0.0";

		internal static Plugin Instance;

		private Harmony harmony;

		private ConfigEntry<string> accent;

		private ConfigEntry<bool> hostOnly;

		internal ManualLogSource Log => ((BaseUnityPlugin)this).Logger;

		private void Awake()
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			Instance = this;
			accent = ((BaseUnityPlugin)this).Config.Bind<string>("Appearance", "AccentColor", "00D7FF", "Terminal accent color as an RGB hex value.");
			hostOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Security", "HostOnlyControlCommands", true, "Only the host may run commands that alter the world.");
			harmony = new Harmony("anton.powerterminalos");
			harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"PowerTerminalOS 1.0.0 loaded. Type pwsh or ps-help in the terminal.");
		}

		internal void Style(Terminal terminal)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Color val = default(Color);
				if (!ColorUtility.TryParseHtmlString("#" + accent.Value.TrimStart('#'), ref val))
				{
					((Color)(ref val))..ctor(0f, 0.84f, 1f);
				}
				terminal.screenText.textComponent.color = val;
				terminal.screenText.textComponent.fontStyle = (FontStyles)0;
				terminal.screenText.caretColor = Color.white;
				terminal.screenText.selectionColor = new Color(val.r, val.g, val.b, 0.35f);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)ex);
			}
		}

		internal bool TryExecute(Terminal terminal, string raw, out TerminalNode node)
		{
			node = null;
			string text = Normalize(raw);
			if (text.Length == 0)
			{
				return false;
			}
			string[] array = text.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			string text2 = array[0].ToLowerInvariant();
			string[] args = array.Skip(1).ToArray();
			if (new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
			{
				{ "pwsh", "ps-help" },
				{ "help-ps", "ps-help" },
				{ "status", "get-shipstatus" },
				{ "moon", "get-moon" },
				{ "crew", "get-crew" },
				{ "scan", "scan-map" },
				{ "lights", "set-lights" },
				{ "turrets", "set-turrets" },
				{ "doors", "set-doors" },
				{ "teleport", "invoke-teleporter" },
				{ "launch", "start-ship" },
				{ "clear", "clear-host" }
			}.TryGetValue(text2, out var value))
			{
				text2 = value;
			}
			bool clearPreviousText = false;
			string text3;
			switch (text2)
			{
			case "ps-help":
				text3 = Help();
				break;
			case "get-shipstatus":
				text3 = ShipStatus();
				break;
			case "get-moon":
				text3 = MoonStatus();
				break;
			case "get-crew":
				text3 = CrewStatus();
				break;
			case "scan-map":
				text3 = MapScan();
				break;
			case "get-weather":
				text3 = MoonStatus();
				break;
			case "get-scrap":
				text3 = ScrapScan();
				break;
			case "set-lights":
				text3 = Control(args, "lights");
				break;
			case "set-turrets":
				text3 = Control(args, "turrets");
				break;
			case "set-doors":
				text3 = Control(args, "doors");
				break;
			case "invoke-teleporter":
				text3 = Control(args, "teleporter");
				break;
			case "start-ship":
				text3 = Control(args, "ship");
				break;
			case "clear-host":
				text3 = Banner("SESSION CLEARED");
				clearPreviousText = true;
				break;
			default:
				return false;
			}
			node = ScriptableObject.CreateInstance<TerminalNode>();
			node.clearPreviousText = clearPreviousText;
			node.acceptAnything = false;
			node.displayText = text3 + "\n\nPS LC:\\Ship> ";
			node.maxCharactersToType = 16000;
			return true;
		}

		private string Control(string[] args, string system)
		{
			if (hostOnly.Value && (Object)(object)NetworkManager.Singleton != (Object)null && !NetworkManager.Singleton.IsHost)
			{
				return Error("ACCESS DENIED", "Control commands are host-only.");
			}
			string text = ((args.Length == 0) ? "toggle" : args[0].ToLowerInvariant());
			try
			{
				switch (system)
				{
				case "lights":
				{
					object obj = AccessTools.Field(typeof(StartOfRound), "shipRoomLights")?.GetValue(StartOfRound.Instance);
					if (obj == null)
					{
						return Error("NOT AVAILABLE", "Ship light controller was not found.");
					}
					bool flag3 = text == "on" || (text == "toggle" && !ReadBool(obj, "areLightsOn", "lightsOn"));
					bool flag4 = InvokeBest(obj, new string[2] { "SetShipLightsBoolean", "SetShipLights" }, flag3);
					if (!flag4)
					{
						flag4 = InvokeBest(obj, (!flag3) ? new string[2] { "SetShipLightsOff", "TurnOffLights" } : new string[2] { "SetShipLightsOn", "TurnOnLights" });
					}
					if (!flag4)
					{
						InvokeBest(obj, new string[1] { "ToggleShipLights" });
					}
					return Success("SHIP LIGHTS", flag3 ? "ONLINE" : "OFFLINE");
				}
				case "turrets":
				{
					bool flag = text == "on" || text == "enable";
					int num = 0;
					foreach (Object item in FindByTypeName("Turret"))
					{
						SetMember(item, flag, "turretActive", "turretEnabled");
						InvokeBest(item, new string[2] { "ToggleTurretEnabled", "SetTurretMode" }, flag);
						num++;
					}
					return Success("TURRET NETWORK", string.Format("{0} turret(s) {1}", num, flag ? "ENABLED" : "DISABLED"));
				}
				case "doors":
				{
					bool flag2 = text == "open" || text == "on";
					int num2 = 0;
					foreach (Object item2 in FindByTypeName("TerminalAccessibleObject"))
					{
						if (item2.name.ToLowerInvariant().Contains("door"))
						{
							if (!InvokeBest(item2, new string[2] { "SetDoorOpen", "SetDoorOpenServerRpc" }, flag2) && ReadBool(item2, "isDoorOpen") != flag2)
							{
								InvokeBest(item2, new string[1] { "CallFunctionFromTerminal" });
							}
							num2++;
						}
					}
					return Success("FACILITY DOORS", string.Format("{0} door(s) commanded {1}", num2, flag2 ? "OPEN" : "CLOSED"));
				}
				case "teleporter":
				{
					Object val = FindByTypeName("ShipTeleporter").FirstOrDefault();
					if (val == (Object)null)
					{
						return Error("NOT INSTALLED", "No ship teleporter was found.");
					}
					InvokeBest(val, new string[2] { "PressTeleportButtonOnLocalClient", "beamUpPlayer" });
					return Success("TELEPORTER", "Beam sequence requested");
				}
				case "ship":
					if ((Object)(object)StartOfRound.Instance == (Object)null)
					{
						return Error("NOT READY", "Round controller was not found.");
					}
					InvokeBest(StartOfRound.Instance, new string[2] { "EndGameServerRpc", "StartGameServerRpc" });
					return Success("AUTOPILOT", "Ship sequence requested");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)ex);
				return Error("COMMAND FAILED", ex.GetType().Name);
			}
			return Error("INVALID ACTION", "Use on/off, enable/disable, or open/close.");
		}

		private string ShipStatus()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return Error("OFFLINE", "No active ship controller.");
			}
			bool flag = ReadBool(instance, "shipHasLanded");
			bool flag2 = ReadBool(instance, "inShipPhase");
			return Panel("SHIP STATUS", new string[5]
			{
				Row("State", flag ? "LANDED" : (flag2 ? "IN ORBIT" : "IN TRANSIT")),
				Row("Crew", ActivePlayers().Count.ToString()),
				Row("Host", IsHost() ? "LOCAL" : "REMOTE"),
				Row("Moon", CurrentMoon()),
				Row("Time", TimeText())
			});
		}

		private string MoonStatus()
		{
			string value = GetMember(((Object)(object)StartOfRound.Instance == (Object)null) ? null : GetMember(StartOfRound.Instance, "currentLevel"), "currentWeather")?.ToString() ?? "Unknown";
			return Panel("MOON TELEMETRY", new string[5]
			{
				Row("Moon", CurrentMoon()),
				Row("Weather", value),
				Row("Time", TimeText()),
				Row("Enemies", FindByBaseType("EnemyAI").Count.ToString()),
				Row("Scrap", ScrapObjects().Count.ToString())
			});
		}

		private string CrewStatus()
		{
			List<string> list = new List<string>();
			foreach (object item in ActivePlayers())
			{
				string key = GetMember(item, "playerUsername")?.ToString() ?? "Unknown";
				bool flag = ReadBool(item, "isPlayerDead");
				bool flag2 = ReadBool(item, "isInsideFactory");
				list.Add(Row(key, flag ? "DECEASED" : (flag2 ? "FACILITY" : "SURFACE/SHIP")));
			}
			return Panel("CREW MANIFEST", list);
		}

		private string MapScan()
		{
			return Panel("LOCAL SENSOR ARRAY", new string[5]
			{
				Row("Hostiles", FindByBaseType("EnemyAI").Count.ToString()),
				Row("Turrets", FindByTypeName("Turret").Count.ToString()),
				Row("Landmines", FindByTypeName("Landmine").Count.ToString()),
				Row("Powered doors", FindByTypeName("TerminalAccessibleObject").Count.ToString()),
				Row("Scrap objects", ScrapObjects().Count.ToString())
			});
		}

		private string ScrapScan()
		{
			return Panel("SCRAP MANIFEST", new string[2]
			{
				Row("Detected", ScrapObjects().Count.ToString()),
				Row("Scanner", "LOCAL RANGE")
			});
		}

		private static string Help()
		{
			return Banner("POWERSHELL SHIP OS 1.0") + "\n  INFORMATION\n  Get-ShipStatus       Ship, crew, moon and time\n  Get-Moon             Weather and moon telemetry\n  Get-Crew             Player status and location\n  Scan-Map             Enemies, hazards and scrap\n  Get-Scrap            Local scrap scan\n\n  CONTROL (HOST)\n  Set-Lights on|off|toggle\n  Set-Turrets enable|disable\n  Set-Doors open|close\n  Invoke-Teleporter\n  Start-Ship\n\n  Aliases: status, moon, crew, scan, lights,\n           turrets, doors, teleport, launch, clear";
		}

		private static string Normalize(string value)
		{
			value = value.Replace("\r", "").Trim();
			int num = value.LastIndexOf('>');
			if (num >= 0)
			{
				value = value.Substring(num + 1).Trim();
			}
			return value;
		}

		private static string CurrentMoon()
		{
			object target = (((Object)(object)StartOfRound.Instance == (Object)null) ? null : GetMember(StartOfRound.Instance, "currentLevel"));
			return GetMember(target, "PlanetName")?.ToString() ?? GetMember(target, "name")?.ToString() ?? "Unknown";
		}

		private static string TimeText()
		{
			if (!(GetMember(Type.GetType("TimeOfDay, Assembly-CSharp")?.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null), "normalizedTimeOfDay") is float num))
			{
				return "Unknown";
			}
			return $"{(int)(6f + num * 12f):00}:{(int)(num * 12f % 1f * 60f):00}";
		}

		private static List<object> ActivePlayers()
		{
			if (!(GetMember(StartOfRound.Instance, "allPlayerScripts") is IEnumerable source))
			{
				return new List<object>();
			}
			return (from object p in source
				where p != null && ReadBool(p, "isPlayerControlled")
				select p).ToList();
		}

		private static List<Object> ScrapObjects()
		{
			return (from o in FindByBaseType("GrabbableObject")
				where ReadBool(o, "itemProperties.isScrap", "isScrap")
				select o).ToList();
		}

		private static List<Object> FindByTypeName(string name)
		{
			Type type = Type.GetType(name + ", Assembly-CSharp");
			if (!(type == null))
			{
				return Object.FindObjectsOfType(type).Cast<Object>().ToList();
			}
			return new List<Object>();
		}

		private static List<Object> FindByBaseType(string name)
		{
			return FindByTypeName(name);
		}

		private static bool IsHost()
		{
			if (!((Object)(object)NetworkManager.Singleton == (Object)null))
			{
				return NetworkManager.Singleton.IsHost;
			}
			return true;
		}

		private static object? GetMember(object? target, string name)
		{
			if (target == null)
			{
				return null;
			}
			Type type = target.GetType();
			object obj = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(target);
			if (obj == null)
			{
				PropertyInfo? property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if ((object)property == null)
				{
					return null;
				}
				obj = property.GetValue(target);
			}
			return obj;
		}

		private static bool ReadBool(object? target, params string[] names)
		{
			foreach (string text in names)
			{
				object obj = (text.Contains(".") ? text.Split('.').Aggregate(target, GetMember) : GetMember(target, text));
				if (obj is bool)
				{
					return (bool)obj;
				}
			}
			return false;
		}

		private static void SetMember(object target, object value, params string[] names)
		{
			foreach (string name in names)
			{
				FieldInfo field = target.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType.IsInstanceOfType(value))
				{
					field.SetValue(target, value);
					break;
				}
			}
		}

		private static bool InvokeBest(object target, string[] names, params object[] args)
		{
			foreach (string name in names)
			{
				MethodInfo methodInfo = target.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == name && m.GetParameters().Length == args.Length);
				if (!(methodInfo == null))
				{
					try
					{
						methodInfo.Invoke(target, args);
						return true;
					}
					catch
					{
					}
				}
			}
			return false;
		}

		private static string Banner(string title)
		{
			return "\n+------------------------------------------------+\n|  " + title.PadRight(44).Substring(0, 44) + "  |\n+------------------------------------------------+";
		}

		private static string Panel(string title, IEnumerable<string> rows)
		{
			return Banner(title) + "\n" + string.Join("\n", rows);
		}

		private static string Row(string key, string value)
		{
			return "  " + key.PadRight(18) + " " + value;
		}

		private static string Success(string title, string text)
		{
			return Panel(title, new string[2]
			{
				Row("Result", text),
				Row("Status", "OK")
			});
		}

		private static string Error(string title, string text)
		{
			return Panel(title, new string[2]
			{
				Row("Error", text),
				Row("Status", "FAILED")
			});
		}
	}
	[HarmonyPatch(typeof(Terminal), "BeginUsingTerminal")]
	internal static class BeginUsingTerminalPatch
	{
		private static void Postfix(Terminal __instance)
		{
			Plugin.Instance.Style(__instance);
		}
	}
	[HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")]
	internal static class ParsePlayerSentencePatch
	{
		private static void Postfix(Terminal __instance, ref TerminalNode __result)
		{
			try
			{
				int textAdded = __instance.textAdded;
				string text = __instance.screenText.text;
				string raw = ((textAdded > 0 && textAdded <= text.Length) ? text.Substring(text.Length - textAdded) : text);
				if (Plugin.Instance.TryExecute(__instance, raw, out TerminalNode node))
				{
					__result = node;
				}
			}
			catch (Exception ex)
			{
				Plugin.Instance.Log.LogError((object)ex);
			}
		}
	}
}