using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalAPI.LibTerminal.Attributes;
using LethalAPI.LibTerminal.Commands;
using LethalAPI.LibTerminal.Interfaces;
using LethalAPI.LibTerminal.Models;
using LethalAPI.LibTerminal.Models.Enums;
using LethalAPI.LibTerminal.Patches;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("ShimmyMySherbet, LethalAPI Modding Team")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("A library that allows the creation of custom terminal commands for Lethal Company mods")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+ca75a0c8c866f23427b380e2a531bbae90b0e939")]
[assembly: AssemblyProduct("LethalAPI.Terminal")]
[assembly: AssemblyTitle("LethalAPI.Terminal")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/LethalCompany/LethalAPI.Terminal")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.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]
[Microsoft.CodeAnalysis.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]
[Microsoft.CodeAnalysis.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 LethalAPI.LibTerminal
{
[BepInPlugin("LethalAPI.Terminal", "LethalAPI.Terminal", "1.0.0")]
internal class LibTerminalPlugin : BaseUnityPlugin
{
private readonly Harmony m_Harmony = new Harmony("LethalAPI.Terminal");
private TerminalModRegistry? m_Registry;
private void Awake()
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"LethalAPI.Terminal is loading...");
((BaseUnityPlugin)this).Logger.LogInfo((object)"Installing patches");
m_Harmony.PatchAll(typeof(LibTerminalPlugin).Assembly);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Registering built-in Commands");
m_Registry = TerminalRegistry.CreateTerminalRegistry();
m_Registry.RegisterFrom<CommandInfoCommands>();
Object.DontDestroyOnLoad((Object)(object)this);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin LethalAPI.Terminal is loaded!");
}
}
public static class TerminalExtensions
{
public static void PlayVideoFile(this Terminal terminal, string filePath)
{
string url = "file:///" + filePath.Replace('\\', '/');
terminal.PlayVideoLink(url);
}
public static void PlayVideoLink(this Terminal terminal, Uri url)
{
((MonoBehaviour)terminal).StartCoroutine(PlayVideoLink(url.AbsoluteUri, terminal));
}
public static void PlayVideoLink(this Terminal terminal, string url)
{
((MonoBehaviour)terminal).StartCoroutine(PlayVideoLink(url, terminal));
}
private static IEnumerator PlayVideoLink(string url, Terminal terminal)
{
yield return (object)new WaitForFixedUpdate();
((Behaviour)terminal.terminalImage).enabled = true;
terminal.terminalImage.texture = (Texture)(object)terminal.videoTexture;
terminal.displayingPersistentImage = null;
terminal.videoPlayer.clip = null;
terminal.videoPlayer.source = (VideoSource)1;
terminal.videoPlayer.url = url;
((Behaviour)terminal.videoPlayer).enabled = true;
}
public static TerminalNode? GetLastLoadedNode()
{
return LoadNewNodePatch.LastLoadedNode;
}
public static TerminalNode? GetLastLoadedNode(this Terminal terminal)
{
return LoadNewNodePatch.LastLoadedNode ?? terminal.currentNode;
}
}
public static class TerminalNodeExtensions
{
public static TerminalNode WithDisplayText(this TerminalNode node, object? displayText)
{
node.displayText = displayText?.ToString() ?? string.Empty;
return node;
}
public static TerminalNode WithDisplayTexture(this TerminalNode node, Texture texture)
{
node.displayTexture = texture;
return node;
}
public static TerminalNode WithVideoClip(this TerminalNode node, VideoClip video)
{
node.displayVideo = video;
return node;
}
public static TerminalNode WithAcceptsAnything(this TerminalNode node)
{
node.acceptAnything = true;
return node;
}
public static TerminalNode WithBuyItemIndex(this TerminalNode node, int index)
{
node.buyItemIndex = index;
return node;
}
public static TerminalNode WithMoonIndex(this TerminalNode node, int index)
{
node.buyRerouteToMoon = index;
return node;
}
public static TerminalNode ClearText(this TerminalNode node)
{
node.clearPreviousText = true;
return node;
}
public static TerminalNode WithItemCost(this TerminalNode node, int cost)
{
node.itemCost = cost;
return node;
}
public static TerminalNode LoadImageSlowly(this TerminalNode node)
{
node.loadImageSlowly = true;
return node;
}
public static TerminalNode PersistImage(this TerminalNode node)
{
node.persistentImage = true;
return node;
}
public static TerminalNode WithAudio(this TerminalNode node, AudioClip audio)
{
node.playClip = audio;
return node;
}
public static TerminalNode WithTerminalEvent(this TerminalNode node, string eventName)
{
node.terminalEvent = eventName;
return node;
}
public static TerminalNode ReturnItemFromStorage(this TerminalNode node, int unlockID)
{
node.shipUnlockableID = unlockID;
node.returnFromStorage = true;
return node;
}
public static TerminalNode PlayEffect(this TerminalNode node, SyncedTerminalClip clip)
{
node.playSyncedClip = (int)clip;
return node;
}
public static TerminalNode PlayError(this TerminalNode node)
{
node.playSyncedClip = 1;
return node;
}
}
public class TerminalRegistry
{
private static readonly ConcurrentDictionary<string, List<TerminalCommand>> m_RegisteredCommands = new ConcurrentDictionary<string, List<TerminalCommand>>(StringComparer.InvariantCultureIgnoreCase);
public static TerminalModRegistry RegisterFrom<T>(T instance) where T : class
{
TerminalModRegistry terminalModRegistry = new TerminalModRegistry();
terminalModRegistry.RegisterFrom(instance);
return terminalModRegistry;
}
public static TerminalModRegistry CreateTerminalRegistry()
{
return new TerminalModRegistry();
}
public static void RegisterCommand(TerminalCommand command)
{
if (!m_RegisteredCommands.TryGetValue(command.Name, out List<TerminalCommand> value))
{
value = new List<TerminalCommand>();
m_RegisteredCommands[command.Name] = value;
}
lock (value)
{
value.Add(command);
}
}
public static void Deregister(TerminalCommand command)
{
if (!m_RegisteredCommands.TryGetValue(command.Name, out List<TerminalCommand> value))
{
return;
}
lock (value)
{
value.Remove(command);
}
}
public static IReadOnlyList<TerminalCommand> GetCommands(string commandName)
{
if (m_RegisteredCommands.TryGetValue(commandName, out List<TerminalCommand> value))
{
return value;
}
return new List<TerminalCommand>();
}
public static IEnumerable<TerminalCommand> EnumerateCommands(string name)
{
if (!m_RegisteredCommands.TryGetValue(name, out List<TerminalCommand> value))
{
return Enumerable.Empty<TerminalCommand>();
}
return value;
}
public static IEnumerable<TerminalCommand> EnumerateCommands()
{
string[] keys = m_RegisteredCommands.Keys.ToArray();
for (int i = 0; i < keys.Length; i++)
{
List<TerminalCommand> overloads = m_RegisteredCommands[keys[i]];
for (int c = 0; c < overloads.Count; c++)
{
yield return overloads[c];
}
}
}
public static IEnumerable<MethodInfo> GetCommandMethods<T>()
{
MethodInfo[] methods = typeof(T).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (MethodInfo methodInfo in methods)
{
if (methodInfo.GetCustomAttribute<TerminalCommandAttribute>() != null)
{
yield return methodInfo;
}
}
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "LethalAPI.Terminal";
public const string PLUGIN_NAME = "LethalAPI.Terminal";
public const string PLUGIN_VERSION = "1.0.0";
}
}
namespace LethalAPI.LibTerminal.Patches
{
[HarmonyPatch(typeof(Terminal), "BeginUsingTerminal")]
internal static class BeginUsingTerminalPatch
{
[HarmonyPostfix]
public static void Postfix(Terminal __instance)
{
ITerminalInterface currentInterface = CommandHandler.CurrentInterface;
if (currentInterface != null)
{
TerminalNode splashScreen = currentInterface.GetSplashScreen(__instance);
if ((Object)(object)splashScreen != (Object)null)
{
__instance.LoadNewNode(splashScreen);
}
}
}
}
[HarmonyPatch(typeof(Terminal), "LoadNewNode")]
internal static class LoadNewNodePatch
{
public static TerminalNode? LastLoadedNode { get; private set; }
[HarmonyPrefix]
public static void Prefix(TerminalNode node)
{
LastLoadedNode = node;
}
}
[HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")]
internal static class ParseSentencePatch
{
[HarmonyPrefix]
public static bool ParsePrefix(Terminal __instance, ref TerminalNode? __state)
{
__state = null;
string command = __instance.screenText.text.Substring(__instance.screenText.text.Length - __instance.textAdded);
__state = CommandHandler.HandleCommandInput(command, __instance);
return (Object)(object)__state == (Object)null;
}
[HarmonyPostfix]
public static TerminalNode? ParsePostfix(TerminalNode? __result, TerminalNode? __state, Terminal __instance)
{
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Invalid comparison between Unknown and I4
if ((Object)(object)__state != (Object)null)
{
TerminalSubmitPatch.LastNode = __state;
return __state;
}
if ((int)__instance.videoPlayer.source == 1)
{
__instance.videoPlayer.source = (VideoSource)0;
}
TerminalSubmitPatch.LastNode = __result;
return __result;
}
}
[HarmonyPatch(typeof(Terminal), "selectTextFieldDelayed")]
internal static class SelectTextFieldPatch
{
[HarmonyPrefix]
public static bool Prefix()
{
return false;
}
[HarmonyPostfix]
public static void Postfix(Terminal __instance, ref IEnumerator __result)
{
__result = Patch(__instance);
}
private static IEnumerator Patch(Terminal terminal)
{
yield return (object)new WaitForSeconds(0.2f);
terminal.screenText.ActivateInputField();
((Selectable)terminal.screenText).Select();
}
}
[HarmonyPatch(typeof(Terminal), "OnSubmit")]
internal static class TerminalSubmitPatch
{
private static ManualLogSource m_LogSource = new ManualLogSource("LethalAPI.Terminal");
public static TerminalNode? LastNode { get; set; }
[HarmonyPrefix]
public static void Prefix()
{
LastNode = null;
}
[HarmonyTranspiler]
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Expected O, but got Unknown
CodeInstruction[] array = instructions.ToArray();
for (int num = array.Length - 1; num >= 0; num--)
{
if (!(array[num].opcode != OpCodes.Callvirt))
{
if (array[num + 1].opcode != OpCodes.Ldarg_0)
{
ReportTranspileError("Ldarg_0 expected after final callVirt, not found");
return array;
}
array[num + 1] = new CodeInstruction(OpCodes.Ret, (object)null);
return array;
}
}
ReportTranspileError("Failed to find Callvirt in backward scan");
return array;
}
private static void ReportTranspileError(string message)
{
m_LogSource.LogError((object)("Failed to transpile OnSubmit to remove Scroll To Bottom. Did the method get modified in an update? (" + message + ")"));
m_LogSource.LogWarning((object)"This won't break the mod, but it will cause some odd terminal scrolling behavior");
}
[HarmonyPostfix]
public static void Postfix(Terminal __instance, ref Coroutine ___forceScrollbarCoroutine)
{
if ((Object)(object)LastNode == (Object)null || LastNode.clearPreviousText)
{
ExecuteScrollCoroutine(__instance, ref ___forceScrollbarCoroutine);
}
else
{
((MonoBehaviour)__instance).StartCoroutine("forceScrollbarDown");
}
}
private static void ExecuteScrollCoroutine(Terminal terminal, ref Coroutine forceScrollbarCoroutine)
{
if (forceScrollbarCoroutine != null)
{
((MonoBehaviour)terminal).StopCoroutine(forceScrollbarCoroutine);
}
forceScrollbarCoroutine = ((MonoBehaviour)terminal).StartCoroutine("forceScrollbarUp");
}
}
[HarmonyPatch(typeof(Terminal), "TextPostProcess")]
internal static class TextPostProcessPatch
{
[HarmonyPrefix]
public static bool Prefix(Terminal __instance, ref string modifiedDisplayText, ref string? __state)
{
__state = null;
if (CommandHandler.CurrentInterface == null || CommandHandler.CurrentInterface.APITextPostProcessing)
{
Console.WriteLine("Run text post process");
modifiedDisplayText = TextUtil.PostProcessResponse(__instance, modifiedDisplayText);
return true;
}
bool num = ProcessInterface(CommandHandler.CurrentInterface, ref modifiedDisplayText, __instance);
if (!num)
{
__state = modifiedDisplayText;
}
return num;
}
[HarmonyPostfix]
public static string? Postfix(string __result, Terminal __instance, string? __state)
{
string text = __state ?? __result;
if (CommandHandler.CurrentInterface != null)
{
text = CommandHandler.CurrentInterface.PostProcessText(__instance, text);
}
return text;
}
private static bool ProcessInterface(ITerminalInterface terminalInterface, ref string modifiedDisplayText, Terminal terminal)
{
modifiedDisplayText = terminalInterface.PreProcessText(terminal, modifiedDisplayText);
return terminalInterface.VanillaTextPostProcessing;
}
}
}
namespace LethalAPI.LibTerminal.Models
{
public class ArgumentStream
{
public string[] Arguments { get; }
public int Index { get; set; }
public bool EndOfStream => Index >= Arguments.Length;
public ArgumentStream(string[] arguments)
{
Arguments = arguments;
}
public ArgumentStream(IEnumerable<string> arguments)
{
Arguments = arguments.ToArray();
}
public void Reset()
{
Index = 0;
}
public bool TryReadNext(Type type, out object? value)
{
if (EndOfStream)
{
value = null;
return false;
}
return StringConverter.TryConvert(Arguments[Index++], type, out value);
}
public bool TryReadRemaining(out string? result)
{
if (EndOfStream)
{
result = null;
return false;
}
result = string.Join(" ", Arguments.Skip(Index));
return true;
}
public bool TryReadNext(out string value)
{
value = string.Empty;
if (EndOfStream)
{
return false;
}
value = Arguments[Index++];
return true;
}
public bool TryReadNext(out sbyte value)
{
value = 0;
if (EndOfStream)
{
return false;
}
return sbyte.TryParse(Arguments[Index++], out value);
}
public bool TryReadNext(out byte value)
{
value = 0;
if (EndOfStream)
{
return false;
}
return byte.TryParse(Arguments[Index++], out value);
}
public bool TryReadNext(out short value)
{
value = 0;
if (EndOfStream)
{
return false;
}
return short.TryParse(Arguments[Index++], out value);
}
public bool TryReadNext(out ushort value)
{
value = 0;
if (EndOfStream)
{
return false;
}
return ushort.TryParse(Arguments[Index++], out value);
}
public bool TryReadNext(out int value)
{
value = 0;
if (EndOfStream)
{
return false;
}
return int.TryParse(Arguments[Index++], out value);
}
public bool TryReadNext(out uint value)
{
value = 0u;
if (EndOfStream)
{
return false;
}
return uint.TryParse(Arguments[Index++], out value);
}
public bool TryReadNext(out long value)
{
value = 0L;
if (EndOfStream)
{
return false;
}
return long.TryParse(Arguments[Index++], out value);
}
public bool TryReadNext(out ulong value)
{
value = 0uL;
if (EndOfStream)
{
return false;
}
return ulong.TryParse(Arguments[Index++], out value);
}
public bool TryReadNext(out float value)
{
value = 0f;
if (EndOfStream)
{
return false;
}
return float.TryParse(Arguments[Index++], out value);
}
public bool TryReadNext(out double value)
{
value = 0.0;
if (EndOfStream)
{
return false;
}
return double.TryParse(Arguments[Index++], out value);
}
}
public static class CommandActivator
{
public static bool TryCreateInvoker(ArgumentStream arguments, ServiceCollection services, MethodInfo method, out Func<object, object?>? invoker)
{
MethodInfo method2 = method;
TerminalCommandAttribute commandAttribute = method2.GetCustomAttribute<TerminalCommandAttribute>();
ParameterInfo[] parameters = method2.GetParameters();
object?[] values = new object[parameters.Length];
invoker = null;
for (int i = 0; i < parameters.Length; i++)
{
ParameterInfo parameterInfo = parameters[i];
Type parameterType = parameterInfo.ParameterType;
if (services.TryGetService(parameterType, out object service))
{
values[i] = service;
}
else if (parameterType == typeof(string) && parameterInfo.GetCustomAttribute<RemainingTextAttribute>() != null)
{
if (!arguments.TryReadRemaining(out string result) || result == null)
{
return false;
}
values[i] = result;
}
else
{
if (!arguments.TryReadNext(parameterType, out object value) || value == null)
{
return false;
}
values[i] = value;
}
}
arguments.Reset();
invoker = (object instance) => ExecuteCommand(method2, instance, values, commandAttribute?.ClearText ?? false);
return true;
}
private static object? ExecuteCommand(MethodInfo method, object instance, object?[] arguments, bool clearConsole)
{
object obj;
try
{
obj = method.Invoke(instance, arguments);
}
catch (Exception ex)
{
Console.WriteLine("Error caught while invoking command hander: " + ex.Message);
Console.WriteLine(ex.StackTrace);
return null;
}
if (obj == null)
{
return null;
}
Type type = obj.GetType();
if (typeof(TerminalNode).IsAssignableFrom(type))
{
return obj;
}
if (typeof(ITerminalInteraction).IsAssignableFrom(type))
{
return obj;
}
if (typeof(ITerminalInterface).IsAssignableFrom(type))
{
return obj;
}
TerminalNode obj2 = ScriptableObject.CreateInstance<TerminalNode>();
obj2.displayText = obj.ToString() + "\n";
obj2.clearPreviousText = clearConsole;
return obj2;
}
}
public class CommandComparer : IComparer<TerminalCommand>
{
public int Compare(TerminalCommand x, TerminalCommand y)
{
if (x.Priority > y.Priority)
{
return 1;
}
if (x.Priority < y.Priority)
{
return -1;
}
return x.ArgumentCount.CompareTo(y.ArgumentCount);
}
}
public static class CommandHandler
{
private static readonly Stack<ITerminalInteraction> m_Interactions = new Stack<ITerminalInteraction>();
private static readonly Regex m_SplitRegex = new Regex("[\\\"](.+?)[\\\"]|([^ ]+)", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled);
private static readonly CommandComparer m_Comparer = new CommandComparer();
public static int InteractionDepth => m_Interactions.Count;
public static ITerminalInterface? CurrentInterface { get; private set; }
public static TerminalNode? HandleCommandInput(string command, Terminal terminal)
{
string[] array = (from Match x in m_SplitRegex.Matches(command.Trim())
select x.Value.Trim('"', ' ')).ToArray();
ArgumentStream arguments = new ArgumentStream(array);
if (CurrentInterface != null)
{
return CurrentInterface.HandleInput(terminal, arguments);
}
TerminalNode val = ExecuteInteractions(arguments, terminal);
if ((Object)(object)val != (Object)null)
{
return val;
}
string commandName = array.First();
ArgumentStream arguments2 = new ArgumentStream(array.Skip(1));
return ExecuteCommand(commandName, arguments2, terminal);
}
public static TerminalNode? ExecuteInteractions(ArgumentStream arguments, Terminal terminal)
{
while (m_Interactions.Count > 0)
{
ITerminalInteraction terminalInteraction = m_Interactions.Pop();
arguments.Reset();
try
{
terminalInteraction.Services.WithServices(arguments, terminal, arguments.Arguments);
object obj = terminalInteraction.HandleTerminalResponse(arguments);
if (obj != null)
{
return HandleCommandResult(obj, terminal);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().Name);
Console.WriteLine("Error executing interaction: " + ex.Message + ", " + ex.StackTrace);
}
}
return null;
}
public static TerminalNode? ExecuteCommand(ArgumentStream arguments, Terminal terminal)
{
arguments.Reset();
if (!arguments.TryReadNext(out string value))
{
return null;
}
ArgumentStream arguments2 = new ArgumentStream(arguments.Arguments.Skip(1));
return ExecuteCommand(value, arguments2, terminal);
}
public static TerminalNode? ExecuteCommand(string commandName, ArgumentStream arguments, Terminal terminal)
{
Terminal terminal2 = terminal;
List<(TerminalCommand, Func<TerminalNode>)> list = new List<(TerminalCommand, Func<TerminalNode>)>();
TerminalCommand[] array = TerminalRegistry.GetCommands(commandName).ToArray();
ServiceCollection services = new ServiceCollection(arguments, arguments.Arguments, terminal2);
foreach (TerminalCommand terminalCommand in array)
{
if (!terminalCommand.CheckAllowed())
{
continue;
}
arguments.Reset();
if (terminalCommand.TryCreateInvoker(arguments, services, out Func<object?> invoker) && invoker != null)
{
Func<TerminalNode> item = () => HandleCommandResult(invoker(), terminal2);
list.Add((terminalCommand, item));
}
}
foreach (var item2 in list.OrderByDescending<(TerminalCommand, Func<TerminalNode>), TerminalCommand>(((TerminalCommand command, Func<TerminalNode> invoker) x) => x.command, m_Comparer))
{
TerminalNode val = item2.Item2();
if ((Object)(object)val != (Object)null)
{
return val;
}
}
return null;
}
private static TerminalNode? HandleCommandResult(object? result, Terminal terminal)
{
TerminalNode val = (TerminalNode)((result is TerminalNode) ? result : null);
if (val != null)
{
return val;
}
if (result is ITerminalInteraction terminalInteraction)
{
SetInteraction(terminalInteraction);
return terminalInteraction.Prompt;
}
if (result is ITerminalInterface terminalInterface)
{
SetInterface(terminalInterface);
return terminalInterface.GetSplashScreen(terminal);
}
return ScriptableObject.CreateInstance<TerminalNode>().WithDisplayText(result);
}
public static void SetInteraction(ITerminalInteraction interaction)
{
m_Interactions.Push(interaction);
}
public static void SetInterface(ITerminalInterface terminalInterface)
{
CurrentInterface = terminalInterface;
}
public static void ResetInterface()
{
CurrentInterface = null;
}
public static void ResetInterface<T>() where T : ITerminalInterface
{
if (CurrentInterface != null && CurrentInterface is T)
{
ResetInterface();
}
}
}
public static class DefaultStringConverters
{
[StringConverter]
public static string ParseString(string input)
{
return input;
}
[StringConverter]
public static sbyte ParseSByte(string input)
{
if (sbyte.TryParse(input, out var result))
{
return result;
}
throw new ArgumentException();
}
[StringConverter]
public static byte ParseByte(string input)
{
if (byte.TryParse(input, out var result))
{
return result;
}
throw new ArgumentException();
}
[StringConverter]
public static short ParseShort(string input)
{
if (short.TryParse(input, out var result))
{
return result;
}
throw new ArgumentException();
}
[StringConverter]
public static ushort ParseUShort(string input)
{
if (ushort.TryParse(input, out var result))
{
return result;
}
throw new ArgumentException();
}
[StringConverter]
public static int ParseInt(string input)
{
if (int.TryParse(input, out var result))
{
return result;
}
throw new ArgumentException();
}
[StringConverter]
public static uint ParseUInt(string input)
{
if (uint.TryParse(input, out var result))
{
return result;
}
throw new ArgumentException();
}
[StringConverter]
public static long ParseLong(string input)
{
if (long.TryParse(input, out var result))
{
return result;
}
throw new ArgumentException();
}
[StringConverter]
public static ulong ParseULong(string input)
{
if (ulong.TryParse(input, out var result))
{
return result;
}
throw new ArgumentException();
}
[StringConverter]
public static float ParseFloat(string input)
{
if (float.TryParse(input, out var result))
{
return result;
}
throw new ArgumentException();
}
[StringConverter]
public static double ParseDouble(string input)
{
if (double.TryParse(input, out var result))
{
return result;
}
throw new ArgumentException();
}
[StringConverter]
public static decimal ParseDecimal(string input)
{
if (decimal.TryParse(input, out var result))
{
return result;
}
throw new ArgumentException();
}
[StringConverter]
public static PlayerControllerB ParsePlayerControllerB(string value)
{
string value2 = value;
if ((Object)(object)StartOfRound.Instance == (Object)null)
{
throw new ArgumentException("Game has not started");
}
PlayerControllerB val = null;
if (ulong.TryParse(value2, out var steamID))
{
val = ((IEnumerable<PlayerControllerB>)StartOfRound.Instance.allPlayerScripts).FirstOrDefault((Func<PlayerControllerB, bool>)((PlayerControllerB x) => x.playerSteamId == steamID));
}
if ((Object)(object)val == (Object)null)
{
val = ((IEnumerable<PlayerControllerB>)StartOfRound.Instance.allPlayerScripts).FirstOrDefault((Func<PlayerControllerB, bool>)((PlayerControllerB x) => x.playerUsername.IndexOf(value2, StringComparison.InvariantCultureIgnoreCase) != -1));
}
if ((Object)(object)val == (Object)null)
{
throw new ArgumentException("Failed to find player");
}
return val;
}
}
public readonly struct RegisteredStringConverter
{
public Type Type { get; }
public StringConversionHandler Handler { get; }
public RegisteredStringConverter(Type type, StringConversionHandler handler)
{
Type = type;
Handler = handler;
}
}
public struct ServiceCollection
{
private Dictionary<Type, object?> m_Services;
public ServiceCollection(params object[] services)
{
m_Services = new Dictionary<Type, object>();
WithServices(services);
}
public ServiceCollection()
{
m_Services = new Dictionary<Type, object>();
}
public bool TryGetService(Type t, out object? service)
{
if (m_Services == null)
{
service = null;
return false;
}
return m_Services.TryGetValue(t, out service);
}
public void WithService<T>(T instance)
{
if (m_Services != null)
{
m_Services[typeof(T)] = instance;
}
}
public void WithServices(params object[] services)
{
if (m_Services == null)
{
return;
}
foreach (object obj in services)
{
if (obj != null)
{
m_Services.Add(obj.GetType(), obj);
}
}
}
}
public delegate object StringConversionHandler(string value);
public static class StringConverter
{
private static bool m_Initialized = false;
private static ConcurrentDictionary<Type, List<StringConversionHandler>> m_StringConverters { get; } = new ConcurrentDictionary<Type, List<StringConversionHandler>>();
public static bool TryConvert(string value, Type type, out object? result)
{
if (!m_Initialized)
{
m_Initialized = true;
RegisterFromType(typeof(DefaultStringConverters));
}
if (!m_StringConverters.TryGetValue(type, out List<StringConversionHandler> value2))
{
result = null;
return false;
}
for (int i = 0; i < value2.Count; i++)
{
try
{
result = value2[i](value);
return true;
}
catch (Exception)
{
}
}
result = null;
return false;
}
public static List<RegisteredStringConverter> RegisterFrom<T>(T instance) where T : class
{
return RegisterFromType(typeof(T), instance);
}
public static bool Deregister(RegisteredStringConverter converter)
{
if (!m_StringConverters.TryGetValue(converter.Type, out List<StringConversionHandler> value))
{
return false;
}
return value.Remove(converter.Handler);
}
public static bool CanConvert(Type type)
{
return m_StringConverters.ContainsKey(type);
}
public static List<RegisteredStringConverter> RegisterFromType(Type type, object? instance = null)
{
object instance2 = instance;
List<RegisteredStringConverter> list = new List<RegisteredStringConverter>();
MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
foreach (MethodInfo method in methods)
{
if (method.GetCustomAttribute<StringConverterAttribute>() == null)
{
continue;
}
ParameterInfo[] parameters = method.GetParameters();
if (parameters.Length == 1 && !(parameters[0].ParameterType != typeof(string)))
{
Type returnType = method.ReturnType;
StringConversionHandler stringConversionHandler = (string value) => method.Invoke(instance2, new object[1] { value });
if (!m_StringConverters.TryGetValue(returnType, out List<StringConversionHandler> value2))
{
value2 = new List<StringConversionHandler>();
m_StringConverters[returnType] = value2;
}
value2.Add(stringConversionHandler);
list.Add(new RegisteredStringConverter(returnType, stringConversionHandler));
}
}
return list;
}
}
public class TerminalCommand
{
private ManualLogSource m_LogSource = new ManualLogSource("LethalAPI.Terminal");
public string Name { get; }
public MethodInfo Method { get; }
public object Instance { get; }
public bool ClearConsole { get; }
public int ArgumentCount { get; }
public string? Syntax { get; }
public string? Description { get; }
public int Priority { get; }
public TerminalCommand(string name, MethodInfo method, object instance, bool clearConsole, string? syntax = null, string? description = null, int priority = 0)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Expected O, but got Unknown
Name = name;
Method = method;
Instance = instance;
ClearConsole = clearConsole;
ArgumentCount = method.GetParameters().Length;
Syntax = syntax;
Description = description;
Priority = priority;
}
public bool CheckAllowed()
{
foreach (AccessControlAttribute customAttribute in Method.GetCustomAttributes<AccessControlAttribute>())
{
if (!customAttribute.CheckAllowed())
{
return false;
}
}
return true;
}
public static TerminalCommand FromMethod(MethodInfo info, object instance, string? overrideName = null)
{
bool clearConsole = false;
string syntax = null;
string description = null;
string text = overrideName;
int priority = 0;
TerminalCommandAttribute customAttribute = info.GetCustomAttribute<TerminalCommandAttribute>();
if (customAttribute != null)
{
text = text ?? customAttribute.CommandName;
clearConsole = customAttribute.ClearText;
}
CommandInfoAttribute customAttribute2 = info.GetCustomAttribute<CommandInfoAttribute>();
if (customAttribute2 != null)
{
syntax = customAttribute2.Syntax;
description = customAttribute2.Description;
}
CommandPriority customAttribute3 = info.GetCustomAttribute<CommandPriority>();
if (customAttribute3 != null)
{
priority = customAttribute3.Priority;
}
if (text == null)
{
throw new ArgumentException("No override name provided, and command name could not be resolved from method");
}
return new TerminalCommand(text, info, instance, clearConsole, syntax, description, priority);
}
public bool TryCreateInvoker(ArgumentStream arguments, ServiceCollection services, out Func<object?>? invoker)
{
if (CommandActivator.TryCreateInvoker(arguments, services, Method, out Func<object, object?> activatedInvoker) && activatedInvoker != null)
{
invoker = () => activatedInvoker(Instance);
return true;
}
invoker = null;
return false;
}
}
public class TerminalModRegistry
{
public List<TerminalCommand> Commands { get; } = new List<TerminalCommand>();
public List<RegisteredStringConverter> StringConverters { get; } = new List<RegisteredStringConverter>();
public T RegisterFrom<T>() where T : class, new()
{
return RegisterFrom(new T());
}
public T RegisterFrom<T>(T instance) where T : class
{
foreach (MethodInfo commandMethod in TerminalRegistry.GetCommandMethods<T>())
{
TerminalCommand terminalCommand = TerminalCommand.FromMethod(commandMethod, instance);
TerminalRegistry.RegisterCommand(terminalCommand);
lock (Commands)
{
Commands.Add(terminalCommand);
}
}
StringConverters.AddRange(StringConverter.RegisterFrom(instance));
return instance;
}
public void Deregister()
{
for (int i = 0; i < Commands.Count; i++)
{
TerminalRegistry.Deregister(Commands[i]);
}
Commands.Clear();
for (int j = 0; j < StringConverters.Count; j++)
{
StringConverter.Deregister(StringConverters[j]);
}
StringConverters.Clear();
}
}
public static class TextUtil
{
public static string PostProcessResponse(Terminal terminal, string message)
{
TerminalNode? lastLoadedNode = terminal.GetLastLoadedNode();
message = ((lastLoadedNode != null && !lastLoadedNode.clearPreviousText) ? message.TrimStart('\n', ' ') : message.SetStartPadding('\n', 2));
message = message.SetEndPadding('\n', 2);
return message;
}
public static string SetEndPadding(this string text, char character, int minPadding)
{
string text2 = text;
int num = 0;
int num2 = text.Length - 1;
while (num2 >= 0 && text[num2] == character)
{
num++;
num2--;
}
int num3 = minPadding - num;
if (num3 > 0)
{
text2 += new string(character, num3);
}
return text2;
}
public static string SetStartPadding(this string text, char character, int minPadding)
{
string text2 = text;
int num = 0;
for (int i = 0; i < text.Length && text[i] == character; i++)
{
num++;
}
int num2 = minPadding - num;
if (num2 > 0)
{
text2 = new string(character, num2) + text2;
}
return text2;
}
}
}
namespace LethalAPI.LibTerminal.Models.Enums
{
public enum AllowedCaller
{
None = -1,
Player,
Host
}
public enum SyncedTerminalClip
{
ItemPurchased,
Error,
BroadcastEffect
}
}
namespace LethalAPI.LibTerminal.Interfaces
{
public interface ITerminalInteraction
{
TerminalNode? Prompt { get; }
ServiceCollection Services { get; }
object? HandleTerminalResponse(ArgumentStream arguments);
}
public interface ITerminalInterface
{
bool APITextPostProcessing { get; }
bool VanillaTextPostProcessing { get; }
TerminalNode HandleInput(Terminal instance, ArgumentStream arguments);
string PreProcessText(Terminal instance, string text);
string PostProcessText(Terminal terminal, string text);
TerminalNode GetSplashScreen(Terminal terminal);
}
}
namespace LethalAPI.LibTerminal.Interactions
{
public class ConfirmInteraction : ITerminalInteraction
{
public TerminalNode? Prompt { get; private set; }
public ServiceCollection Services { get; } = new ServiceCollection();
public Delegate? ConfirmHandler { get; set; }
public Delegate? DenyHandler { get; set; }
public ConfirmInteraction()
{
}
public ConfirmInteraction(TerminalNode prompt)
{
Prompt = prompt;
PostprocessPrompt();
}
public ConfirmInteraction(Action<TerminalNode> promptBuilder)
{
TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>();
promptBuilder(val);
WithPrompt(val);
}
public ConfirmInteraction(TerminalNode prompt, Delegate confirm, Delegate deny)
{
WithPrompt(prompt);
ConfirmHandler = confirm;
DenyHandler = deny;
}
public ConfirmInteraction(Action<TerminalNode> promptBuilder, Delegate confirm, Delegate deny)
{
WithPrompt(promptBuilder);
ConfirmHandler = confirm;
DenyHandler = deny;
}
public ConfirmInteraction(string prompt)
{
WithPrompt(prompt);
}
public ConfirmInteraction(string prompt, Delegate confirm, Delegate deny)
{
WithPrompt(prompt);
ConfirmHandler = confirm;
DenyHandler = deny;
}
public ConfirmInteraction WithPrompt(string prompt)
{
Prompt = ScriptableObject.CreateInstance<TerminalNode>();
Prompt.WithDisplayText(prompt);
PostprocessPrompt();
return this;
}
public ConfirmInteraction WithPrompt(TerminalNode prompt)
{
Prompt = prompt;
PostprocessPrompt();
return this;
}
public ConfirmInteraction WithPrompt(Action<TerminalNode> promptBuilder)
{
TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>();
promptBuilder(val);
WithPrompt(val);
return this;
}
public ConfirmInteraction Confirm(Delegate confirmHandler)
{
ConfirmHandler = confirmHandler;
return this;
}
public ConfirmInteraction Deny(Delegate denyHandler)
{
DenyHandler = denyHandler;
return this;
}
public ConfirmInteraction WithContext(params object[] services)
{
Services.WithServices(services);
return this;
}
public object? HandleTerminalResponse(ArgumentStream arguments)
{
if (!arguments.TryReadNext(out string value))
{
return Deny(arguments);
}
if (value.Trim().Equals("confirm", StringComparison.InvariantCultureIgnoreCase))
{
return Confirm(arguments);
}
return Deny(arguments);
}
private object? Confirm(ArgumentStream arguments)
{
if ((object)DenyHandler == null)
{
return string.Empty;
}
if ((object)ConfirmHandler != null && CommandActivator.TryCreateInvoker(arguments, Services, ConfirmHandler.GetMethodInfo(), out Func<object, object> invoker) && invoker != null)
{
return invoker(ConfirmHandler.Target);
}
return null;
}
private object? Deny(ArgumentStream arguments)
{
if ((object)DenyHandler == null)
{
return string.Empty;
}
if (CommandActivator.TryCreateInvoker(arguments, Services, DenyHandler.GetMethodInfo(), out Func<object, object> invoker) && invoker != null)
{
return invoker(DenyHandler.Target);
}
return null;
}
private void PostprocessPrompt()
{
if ((Object)(object)Prompt != (Object)null)
{
string text = Prompt.displayText ?? string.Empty;
text = text.SetEndPadding('\n', 2) + "Please CONFIRM or DENY.\n";
Prompt.displayText = text;
}
}
}
public class TerminalInteraction : ITerminalInteraction
{
public TerminalNode? Prompt { get; private set; }
public ServiceCollection Services { get; } = new ServiceCollection();
public List<Delegate> Handlers { get; } = new List<Delegate>();
public TerminalInteraction()
{
}
public TerminalInteraction(TerminalNode prompt, Delegate handler)
{
Prompt = prompt;
Handlers.Add(handler);
}
public TerminalInteraction(Action<TerminalNode> promptBuilder, Delegate handler)
{
TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>();
promptBuilder(val);
WithPrompt(val);
Handlers.Add(handler);
}
public TerminalInteraction WithContext(params object[] services)
{
Services.WithServices(services);
return this;
}
public TerminalInteraction WithPrompt(TerminalNode prompt)
{
Prompt = prompt;
return this;
}
public TerminalInteraction WithPrompt(Action<TerminalNode> promptBuilder)
{
TerminalNode val = ScriptableObject.CreateInstance<TerminalNode>();
promptBuilder(val);
WithPrompt(val);
return this;
}
public TerminalInteraction WithPrompt(string prompt)
{
Prompt = ScriptableObject.CreateInstance<TerminalNode>();
Prompt.WithDisplayText(prompt);
return this;
}
public TerminalInteraction WithHandler(Delegate handler)
{
Handlers.Add(handler);
return this;
}
public object? HandleTerminalResponse(ArgumentStream arguments)
{
(from x in Handlers
select (x.GetMethodInfo(), x.Target) into x
orderby x.info.GetParameters().Length descending
select x).ToList();
foreach (Delegate handler in Handlers)
{
MethodInfo methodInfo = handler.GetMethodInfo();
arguments.Reset();
if (CommandActivator.TryCreateInvoker(arguments, Services, methodInfo, out Func<object, object> invoker) && invoker != null)
{
object obj = invoker(handler.Target);
if (obj != null)
{
return obj;
}
}
}
return null;
}
}
}
namespace LethalAPI.LibTerminal.Commands
{
public class CommandInfoCommands
{
[TerminalCommand("Other", true)]
public string CommandList()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Other commands:");
stringBuilder.AppendLine();
stringBuilder.AppendLine(">VIEW MONITOR");
stringBuilder.AppendLine("To toggle on/off the main monitor's map cam");
stringBuilder.AppendLine();
stringBuilder.AppendLine(">SWITCH {RADAR}");
stringBuilder.AppendLine("To switch the player view on the main monitor");
stringBuilder.AppendLine();
stringBuilder.AppendLine(">PING [Radar booster name]");
stringBuilder.AppendLine("To switch the player view on the main monitor");
stringBuilder.AppendLine();
stringBuilder.AppendLine(">SCAN");
stringBuilder.AppendLine("To scan for the number of items left on the current planet");
stringBuilder.AppendLine();
stringBuilder.AppendLine(">TRANSMIT [message]");
stringBuilder.AppendLine("Transmit a message with the signal translator");
stringBuilder.AppendLine();
foreach (TerminalCommand item in TerminalRegistry.EnumerateCommands())
{
if (item.Description != null && item.CheckAllowed())
{
stringBuilder.AppendLine(">" + item.Name.ToUpper() + " " + item.Syntax?.ToUpper());
stringBuilder.AppendLine(item.Description);
stringBuilder.AppendLine();
}
}
return stringBuilder.ToString();
}
[TerminalCommand("Help", false)]
[CommandInfo("Shows further information about a command", "[Command]")]
public string HelpCommand(string name)
{
StringBuilder stringBuilder = new StringBuilder();
TerminalCommand[] array = TerminalRegistry.EnumerateCommands(name).ToArray();
if (array.Length == 0)
{
return "Unknown command: '" + name + "'";
}
TerminalCommand[] array2 = array;
foreach (TerminalCommand terminalCommand in array2)
{
stringBuilder.AppendLine(">" + terminalCommand.Name.ToUpper() + " " + terminalCommand.Syntax?.ToUpper());
stringBuilder.AppendLine(terminalCommand.Description);
if (!terminalCommand.CheckAllowed())
{
stringBuilder.AppendLine("[Host Only]");
}
stringBuilder.AppendLine();
}
return stringBuilder.ToString();
}
}
}
namespace LethalAPI.LibTerminal.Attributes
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public abstract class AccessControlAttribute : Attribute
{
public abstract bool CheckAllowed();
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class AllowedCallerAttribute : AccessControlAttribute
{
public AllowedCaller Caller { get; }
public AllowedCallerAttribute(AllowedCaller caller)
{
Caller = caller;
}
public override bool CheckAllowed()
{
switch (Caller)
{
case AllowedCaller.None:
return false;
case AllowedCaller.Player:
return true;
case AllowedCaller.Host:
if ((Object)(object)StartOfRound.Instance == (Object)null)
{
return false;
}
return ((NetworkBehaviour)StartOfRound.Instance).IsHost;
default:
return true;
}
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CommandInfoAttribute : Attribute
{
public string Syntax { get; }
public string Description { get; }
public CommandInfoAttribute(string description, string syntax = "")
{
Syntax = syntax;
Description = description;
}
}
public sealed class CommandPriority : Attribute
{
public int Priority { get; }
public CommandPriority(int priority)
{
Priority = priority;
}
}
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class RemainingTextAttribute : Attribute
{
}
public class RequireInterfaceAttribute : AccessControlAttribute
{
public Type InterfaceType { get; }
public RequireInterfaceAttribute(Type interfaceType)
{
InterfaceType = interfaceType;
}
public override bool CheckAllowed()
{
if (CommandHandler.CurrentInterface != null)
{
return CommandHandler.CurrentInterface.GetType() == InterfaceType;
}
return false;
}
}
public sealed class RequireInterfaceAttribute<T> : RequireInterfaceAttribute where T : ITerminalInterface
{
public RequireInterfaceAttribute()
: base(typeof(T))
{
}
}
public sealed class StringConverterAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class TerminalCommandAttribute : Attribute
{
public string CommandName { get; }
public bool ClearText { get; }
public TerminalCommandAttribute(string name, bool clearText = false)
{
CommandName = name;
ClearText = clearText;
}
}
}