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.TerminalCommands.Attributes;
using LethalAPI.TerminalCommands.Commands;
using LethalAPI.TerminalCommands.Configs;
using LethalAPI.TerminalCommands.Models;
using LethalAPI.TerminalCommands.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(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[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.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+5290c6d02b20cc3e4c96dd09d0f7234c78366019")]
[assembly: AssemblyProduct("LethalAPI.TerminalCommands")]
[assembly: AssemblyTitle("LethalAPI.TerminalCommands")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/LethalCompany/LethalAPI.TerminalCommands")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.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.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace LethalAPI.TerminalCommands
{
[HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")]
public 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.TryExecute(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;
}
}
[BepInPlugin("LethalAPI.TerminalCommands", "LethalAPI.TerminalCommands", "1.1.0")]
public class TerminalCommandsPlugin : BaseUnityPlugin
{
private Harmony HarmonyInstance = new Harmony("LethalAPI.TerminalCommands");
private TerminalModRegistry Terminal;
private TerminalConfig TerminalConfig;
private void Awake()
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"LethalAPI.TerminalCommands is loading...");
((BaseUnityPlugin)this).Logger.LogInfo((object)"Installing patches");
HarmonyInstance.PatchAll(typeof(TerminalCommandsPlugin).Assembly);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Registering built-in Commands");
Terminal = TerminalRegistry.CreateTerminalRegistry();
Terminal.RegisterFrom<CommandInfoCommands>();
TerminalConfig = Terminal.RegisterFrom<TerminalConfig>();
Object.DontDestroyOnLoad((Object)(object)this);
((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin LethalAPI.TerminalCommands 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 class PluginInfo
{
public const string PLUGIN_GUID = "LethalAPI.TerminalCommands";
public const string PLUGIN_NAME = "LethalAPI.TerminalCommands";
public const string PLUGIN_VERSION = "1.1.0";
}
}
namespace LethalAPI.TerminalCommands.Patches
{
[HarmonyPatch(typeof(Terminal), "selectTextFieldDelayed")]
public 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")]
public static class TerminalSubmitPatch
{
private static ManualLogSource m_LogSource = new ManualLogSource("LethalAPI.TerminalCommands");
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")]
public static class TextPostProcessPatch
{
[HarmonyPrefix]
public static void Prefix(ref string modifiedDisplayText)
{
modifiedDisplayText = modifiedDisplayText.TrimStart('\n', ' ');
if (!modifiedDisplayText.EndsWith('\n'))
{
modifiedDisplayText += "\n";
}
}
}
}
namespace LethalAPI.TerminalCommands.Models
{
public enum AllowedCaller
{
None = -1,
Player,
Host
}
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 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 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 Regex m_SplitRegex = new Regex("[\\\"](.+?)[\\\"]|([^ ]+)", RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled);
private static readonly CommandComparer m_Comparer = new CommandComparer();
public static TerminalNode TryExecute(string command, Terminal terminal)
{
IEnumerable<string> source = from Match x in m_SplitRegex.Matches(command.Trim())
select x.Value.Trim('"', ' ');
string commandName = source.First();
string[] arguments = source.Skip(1).ToArray();
List<(TerminalCommand, Func<TerminalNode>)> list = new List<(TerminalCommand, Func<TerminalNode>)>();
TerminalCommand[] array = TerminalRegistry.GetCommands(commandName).ToArray();
foreach (TerminalCommand terminalCommand in array)
{
if (terminalCommand.CheckAllowed() && terminalCommand.TryCreateInvoker(arguments, terminal, out var invoker))
{
list.Add((terminalCommand, invoker));
}
}
foreach (var item in list.OrderByDescending<(TerminalCommand, Func<TerminalNode>), TerminalCommand>(((TerminalCommand command, Func<TerminalNode> invoker) x) => x.command, m_Comparer))
{
TerminalNode val = item.Item2();
if ((Object)(object)val != (Object)null)
{
return val;
}
}
return null;
}
}
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();
}
public static PlayerControllerB ParsePlayerControllerB(string value)
{
if ((Object)(object)StartOfRound.Instance == (Object)null)
{
throw new ArgumentException("Game has not started");
}
PlayerControllerB val = null;
if (ulong.TryParse(value, 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(value, StringComparison.InvariantCultureIgnoreCase) != -1));
}
if ((Object)(object)val == (Object)null)
{
throw new ArgumentException("Failed to find player");
}
return val;
}
}
public enum PersistType
{
LocalPlayer,
Host,
Save
}
public delegate object StringConversionHandler(string value);
public static class StringConverter
{
private static bool m_Initialized = false;
public static ConcurrentDictionary<Type, StringConversionHandler> StringConverters { get; } = new ConcurrentDictionary<Type, StringConversionHandler>();
public static bool TryConvert(string value, Type type, out object result)
{
if (!m_Initialized)
{
m_Initialized = true;
RegisterFromType(typeof(DefaultStringConverters), null, replaceExisting: false);
}
if (!StringConverters.TryGetValue(type, out var value2))
{
result = null;
return false;
}
try
{
result = value2(value);
return true;
}
catch (ArgumentException)
{
}
result = null;
return false;
}
public static void RegisterFrom<T>(T instance, bool replaceExisting = true) where T : class
{
RegisterFromType(typeof(T), instance, replaceExisting);
}
public static void RegisterFromType(Type type, object instance = null, bool replaceExisting = true)
{
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 value2 = (string value) => method.Invoke(instance, new object[1] { value });
if (replaceExisting || !StringConverters.ContainsKey(returnType))
{
StringConverters[returnType] = value2;
}
}
}
}
}
public class TerminalCommand
{
private ManualLogSource m_LogSource = new ManualLogSource("LethalAPI.TerminalCommands");
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;
}
return new TerminalCommand(text, info, instance, clearConsole, syntax, description, priority);
}
public bool TryCreateInvoker(string[] arguments, Terminal terminal, out Func<TerminalNode> invoker)
{
ParameterInfo[] parameters = Method.GetParameters();
object[] values = new object[parameters.Length];
ArgumentStream argumentStream = new ArgumentStream(arguments);
invoker = null;
for (int i = 0; i < parameters.Length; i++)
{
ParameterInfo parameterInfo = parameters[i];
Type parameterType = parameterInfo.ParameterType;
if (parameterType == typeof(Terminal))
{
values[i] = terminal;
}
else if (parameterType == typeof(ArgumentStream))
{
values[i] = argumentStream;
}
else if (parameterType == typeof(string[]))
{
values[i] = arguments;
}
else if (parameterType == typeof(string) && parameterInfo.GetCustomAttribute<RemainingTextAttribute>() != null)
{
if (!argumentStream.TryReadRemaining(out var result))
{
return false;
}
values[i] = result;
}
else
{
if (!argumentStream.TryReadNext(parameterType, out var value))
{
return false;
}
values[i] = value;
}
}
argumentStream.Reset();
invoker = () => ExecuteCommand(values);
return true;
}
private TerminalNode ExecuteCommand(object[] arguments)
{
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: Expected O, but got Unknown
object obj;
try
{
obj = Method.Invoke(Instance, arguments);
}
catch (Exception ex)
{
m_LogSource.LogError((object)("Error caught while invoking command hander: " + ex.Message));
m_LogSource.LogError((object)ex.StackTrace);
return null;
}
if (obj == null)
{
return null;
}
Type type = obj.GetType();
if (!typeof(TerminalNode).IsAssignableFrom(type))
{
TerminalNode obj2 = ScriptableObject.CreateInstance<TerminalNode>();
obj2.displayText = obj.ToString() + "\n";
obj2.clearPreviousText = ClearConsole;
return obj2;
}
return (TerminalNode)obj;
}
}
public class TerminalModRegistry
{
public List<TerminalCommand> Commands { get; } = new List<TerminalCommand>();
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);
}
}
StringConverter.RegisterFrom(instance);
return instance;
}
public void Deregister()
{
if (Commands != null)
{
for (int i = 0; i < Commands.Count; i++)
{
TerminalRegistry.Deregister(Commands[i]);
}
}
}
}
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();
foreach (MethodInfo commandMethod in GetCommandMethods<T>())
{
TerminalCommand terminalCommand = TerminalCommand.FromMethod(commandMethod, instance);
RegisterCommand(terminalCommand);
terminalModRegistry.Commands.Add(terminalCommand);
}
StringConverter.RegisterFrom(instance);
return terminalModRegistry;
}
public static TerminalModRegistry CreateTerminalRegistry()
{
return new TerminalModRegistry();
}
public static void RegisterCommand(TerminalCommand command)
{
if (!m_RegisteredCommands.TryGetValue(command.Name, out var 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 var value))
{
return;
}
lock (value)
{
value.Remove(command);
}
}
public static IReadOnlyList<TerminalCommand> GetCommands(string commandName)
{
if (m_RegisteredCommands.TryGetValue(commandName, out var value))
{
return value;
}
return new List<TerminalCommand>();
}
public static IEnumerable<TerminalCommand> EnumerateCommands(string name)
{
if (!m_RegisteredCommands.TryGetValue(name, out var 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;
}
}
}
}
}
namespace LethalAPI.TerminalCommands.Configs
{
[ConfigGroup("Terminal", "Configure the behavior of the terminal")]
public class TerminalConfig
{
[TerminalConfig("Enables/Disables terminal verb commands", null)]
[ConfigPersist(PersistType.LocalPlayer, null)]
public bool VerbsEnabled { get; set; }
[TerminalConfig("Specifies if the Confirm/Deny pop-up should be shown", null)]
[ConfigPersist(PersistType.LocalPlayer, null)]
public bool AutoConfirm { get; set; }
}
}
namespace LethalAPI.TerminalCommands.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();
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.TerminalCommands.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.Class)]
public sealed class ConfigGroupAttribute : Attribute
{
public string Name { get; }
public string Description { get; }
public ConfigGroupAttribute(string name, string description)
{
Name = name;
Description = description;
}
}
public class ConfigPersistAttribute : Attribute
{
public PersistType PersistType { get; }
public string ConfigPath { get; }
public ConfigPersistAttribute(PersistType persistType, string configPath = null)
{
PersistType = persistType;
ConfigPath = configPath;
}
}
[AttributeUsage(AttributeTargets.Parameter)]
public sealed class RemainingTextAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
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;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class TerminalConfigAttribute : Attribute
{
public string Name { get; }
public string Description { get; }
public TerminalConfigAttribute(string description, string name = null)
{
Name = name;
Description = description;
}
}
}