Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ChatCommandAPI v1.1.2
baer1.ChatCommandAPI.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Globalization; 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.Configuration; using BepInEx.Logging; using ChatCommandAPI.Commands; using ChatCommandAPI.ServerCommands; using ChatCommandAPI.Utils; using GameNetcodeStuff; using HarmonyLib; using LethalModUtils; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.InputSystem; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("DunGen")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")] [assembly: IgnoresAccessChecksTo("EasyTextEffects")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")] [assembly: IgnoresAccessChecksTo("Unity.XR.Management")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("baer1")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright (c) baer1 2026")] [assembly: AssemblyDescription("A Lethal Company modding API for easy creation of Minecraft-style Chat Commands")] [assembly: AssemblyFileVersion("1.1.2.0")] [assembly: AssemblyInformationalVersion("1.1.2+5d04878647c5d1ace2335b8890355c8eb6c21999")] [assembly: AssemblyProduct("ChatCommandAPI")] [assembly: AssemblyTitle("baer1.ChatCommandAPI")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/baerchen201/LethalChatCommands.git")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] [CompilerGenerated] internal sealed class <>z__ReadOnlySingleElementList<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T> { private sealed class Enumerator : IDisposable, IEnumerator, IEnumerator<T> { object IEnumerator.Current => _item; T IEnumerator<T>.Current => _item; public Enumerator(T item) { _item = item; } bool IEnumerator.MoveNext() { if (!_moveNextCalled) { return _moveNextCalled = true; } return false; } void IEnumerator.Reset() { _moveNextCalled = false; } void IDisposable.Dispose() { } } int ICollection.Count => 1; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; object IList.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } set { throw new NotSupportedException(); } } bool IList.IsFixedSize => true; bool IList.IsReadOnly => true; int IReadOnlyCollection<T>.Count => 1; T IReadOnlyList<T>.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } } int ICollection<T>.Count => 1; bool ICollection<T>.IsReadOnly => true; T IList<T>.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } set { throw new NotSupportedException(); } } public <>z__ReadOnlySingleElementList(T item) { _item = item; } IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(_item); } void ICollection.CopyTo(Array array, int index) { array.SetValue(_item, index); } int IList.Add(object value) { throw new NotSupportedException(); } void IList.Clear() { throw new NotSupportedException(); } bool IList.Contains(object value) { return EqualityComparer<T>.Default.Equals(_item, (T)value); } int IList.IndexOf(object value) { if (!EqualityComparer<T>.Default.Equals(_item, (T)value)) { return -1; } return 0; } void IList.Insert(int index, object value) { throw new NotSupportedException(); } void IList.Remove(object value) { throw new NotSupportedException(); } void IList.RemoveAt(int index) { throw new NotSupportedException(); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return new Enumerator(_item); } void ICollection<T>.Add(T item) { throw new NotSupportedException(); } void ICollection<T>.Clear() { throw new NotSupportedException(); } bool ICollection<T>.Contains(T item) { return EqualityComparer<T>.Default.Equals(_item, item); } void ICollection<T>.CopyTo(T[] array, int arrayIndex) { array[arrayIndex] = _item; } bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); } int IList<T>.IndexOf(T item) { if (!EqualityComparer<T>.Default.Equals(_item, item)) { return -1; } return 0; } void IList<T>.Insert(int index, T item) { throw new NotSupportedException(); } void IList<T>.RemoveAt(int index) { throw new NotSupportedException(); } } 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 ChatCommandAPI { public abstract class BaseCommand { public virtual string Name => GetType().Name; public virtual string Description => string.Empty; public virtual string Command => Name.ToLowerInvariant(); public virtual string[] Aliases => new string[1] { GetType().Name.ToLowerInvariant() }; public virtual string[] Syntax => Array.Empty<string>(); public virtual bool Hidden => false; public string FullName => GetType().FullName; public sealed override string ToString() { return GetType().AssemblyQualifiedName ?? ""; } } [BepInPlugin("baer1.ChatCommandAPI", "ChatCommandAPI", "1.1.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class ChatCommandAPI : BaseUnityPlugin { internal const string COMMAND_NAME_REGEX = "\\S+"; internal readonly Dictionary<string, Command> commands = new Dictionary<string, Command>(); internal readonly Dictionary<string, ServerCommand> serverCommands = new Dictionary<string, ServerCommand>(); private ConfigEntry<string> commandPrefix; private ConfigEntry<bool> compatibilityModeEnabled; private ConfigEntry<Color> defaultMessageColor; private ConfigEntry<string> serverCommandPrefix; private ConfigEntry<bool> serverCommandsEnabled; private ConfigEntry<string> serverWelcomeMessage; public static ChatCommandAPI Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } internal static Harmony? Harmony { get; set; } public System.Drawing.Color DefaultMessageColor => defaultMessageColor.Value.ToSystem(); public char CommandPrefix => commandPrefix.Value[0]; public bool CompatibilityModeEnabled => compatibilityModeEnabled.Value; public bool ServerCommandsEnabled => serverCommandsEnabled.Value; public char ServerCommandPrefix => serverCommandPrefix.Value[0]; public string ServerWelcomeMessage => string.Format(serverWelcomeMessage.Value.Trim(), ServerCommandPrefix); public IReadOnlyDictionary<string, Command> Commands => commands; public IReadOnlyDictionary<string, ServerCommand> ServerCommands => serverCommands; private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) Logger = ((BaseUnityPlugin)this).Logger; Instance = this; defaultMessageColor = ((BaseUnityPlugin)this).Config.Bind<Color>("General", "DefaultMessageColor", System.Drawing.Color.Aqua.ToUnity(), "The default color to use for chat responses"); commandPrefix = ((BaseUnityPlugin)this).Config.Bind<string>("Client", "CommandPrefix", '/'.ToString(), "The prefix to use for client-side commands"); compatibilityModeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Client", "CompatibilityModeEnabled", false, "Allows non-API chat commands to run (may cause the command you run to be sent in chat)"); serverCommandsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Server", "ServerCommandsEnabled", false, "Enables or disables server commands"); serverCommandPrefix = ((BaseUnityPlugin)this).Config.Bind<string>("Server", "ServerCommandPrefix", '!'.ToString(), "The prefix to use for server-side commands"); serverWelcomeMessage = ((BaseUnityPlugin)this).Config.Bind<string>("Server", "ServerWelcomeMessage", "This server supports chat commands.\nType {0}help for more information", "A welcome message that is displayed to any player that joins (clear to disable). {0} is replaced with ServerCommandPrefix"); ((BaseUnityPlugin)this).Config.SettingChanged += verifyConfigValues; verifyConfigValues(null, null); new global::ChatCommandAPI.Commands.Help(); new global::ChatCommandAPI.ServerCommands.Help(); Patch(); Logger.LogInfo((object)"baer1.ChatCommandAPI v1.1.2 has loaded!"); static void Patch() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown if (Harmony == null) { Harmony = new Harmony("baer1.ChatCommandAPI"); } Logger.LogDebug((object)"Patching..."); Harmony.PatchAll(); Logger.LogDebug((object)"Finished patching!"); } void verifyConfigValues(object args, object sender) { int length = commandPrefix.Value.Length; if (length > 0) { if (length != 1) { Logger.LogWarning((object)("CommandPrefix '" + commandPrefix.Value + "' contains more than one character, only the first one will be used")); } if (!char.IsSymbol(CommandPrefix) && !char.IsPunctuation(CommandPrefix)) { Logger.LogWarning((object)string.Format("Invalid {0} value '{1}', symbol expected...", "CommandPrefix", CommandPrefix)); commandPrefix.Value = (string)((ConfigEntryBase)commandPrefix).DefaultValue; } } else { Logger.LogWarning((object)string.Format("Invalid {0} value '{1}', value expected...", "CommandPrefix", CommandPrefix)); commandPrefix.Value = (string)((ConfigEntryBase)commandPrefix).DefaultValue; } int length2 = serverCommandPrefix.Value.Length; if (length2 > 0) { if (length2 != 1) { Logger.LogWarning((object)("ServerCommandPrefix '" + serverCommandPrefix.Value + "' contains more than one character, only the first one will be used")); } if (!char.IsSymbol(ServerCommandPrefix) && !char.IsPunctuation(ServerCommandPrefix)) { Logger.LogWarning((object)string.Format("Invalid {0} value '{1}', symbol expected...", "ServerCommandPrefix", ServerCommandPrefix)); serverCommandPrefix.Value = (string)((ConfigEntryBase)serverCommandPrefix).DefaultValue; } } else { Logger.LogWarning((object)string.Format("Invalid {0} value '{1}', symbol expected...", "ServerCommandPrefix", ServerCommandPrefix)); serverCommandPrefix.Value = (string)((ConfigEntryBase)serverCommandPrefix).DefaultValue; } if (ServerCommandPrefix == CommandPrefix) { Logger.LogWarning((object)"ServerCommandPrefix is the same as CommandPrefix, this configuration will prioritize client commands and is not supported"); } } } public static bool TryParseCommand(char prefix, string cmdline, out string name, out string args) { name = null; args = null; Match match = Regex.Match(cmdline, "^" + Regex.Escape(prefix.ToString()) + "(\\S+)(?: (.*))?$"); if (!match.Success) { return false; } Group obj = match.Groups[2]; name = match.Groups[1].Value; args = (obj.Success ? obj.Value : string.Empty); return true; } public bool TryParseCommand(string cmdline, out string name, out string args) { return TryParseCommand(CommandPrefix, cmdline, out name, out args); } public bool TryParseServerCommand(string cmdline, out string name, out string args) { return TryParseCommand(ServerCommandPrefix, cmdline, out name, out args); } public bool TryGetCommand(string name, out Command command, out string primaryName) { primaryName = (name = name.ToLowerInvariant()); IReadOnlyDictionary<string, Command> readOnlyDictionary = Commands; if (readOnlyDictionary.TryGetValue(name, out command)) { return true; } foreach (KeyValuePair<string, Command> item in readOnlyDictionary) { if (string.Equals(item.Value.FullName, name, StringComparison.InvariantCultureIgnoreCase)) { primaryName = item.Key; command = item.Value; return true; } } foreach (KeyValuePair<string, Command> item2 in readOnlyDictionary) { if (string.Equals(item2.Value.Command, name, StringComparison.InvariantCultureIgnoreCase)) { primaryName = item2.Key; command = item2.Value; return true; } } foreach (KeyValuePair<string, Command> item3 in readOnlyDictionary) { if (item3.Value.Aliases.Contains<string>(name, StringComparer.InvariantCultureIgnoreCase)) { primaryName = item3.Key; command = item3.Value; return true; } } return false; } public bool TryGetServerCommand(string name, out ServerCommand command, out string primaryName) { primaryName = (name = name.ToLowerInvariant()); IReadOnlyDictionary<string, ServerCommand> readOnlyDictionary = ServerCommands; if (readOnlyDictionary.TryGetValue(name, out command)) { return true; } foreach (KeyValuePair<string, ServerCommand> item in readOnlyDictionary) { if (string.Equals(item.Value.FullName, name, StringComparison.InvariantCultureIgnoreCase)) { primaryName = item.Key; command = item.Value; return true; } } foreach (KeyValuePair<string, ServerCommand> item2 in readOnlyDictionary) { if (string.Equals(item2.Value.Command, name, StringComparison.InvariantCultureIgnoreCase)) { primaryName = item2.Key; command = item2.Value; return true; } } foreach (KeyValuePair<string, ServerCommand> item3 in readOnlyDictionary) { if (item3.Value.Aliases.Contains<string>(name, StringComparer.InvariantCultureIgnoreCase)) { primaryName = item3.Key; command = item3.Value; return true; } } return false; } } public abstract class Command : BaseCommand { protected Command() { ChatCommandAPI.Logger.LogInfo((object)("Registering command " + base.FullName + "...")); string name = Name; if (string.IsNullOrWhiteSpace(name)) { throw new InvalidCommandException("Name", name, this); } string text = Command.ToLowerInvariant(); if (string.IsNullOrWhiteSpace(text) || !Regex.Match(text, "\\S+").Success) { throw new InvalidCommandException("Command", text, this); } IReadOnlyDictionary<string, Command> commands = ChatCommandAPI.Instance.Commands; if (commands.TryGetValue(text, out var value)) { ChatCommandAPI.Logger.LogDebug((object)string.Format("{0} '{1}' >> {2}", "Command", text, value)); string[] aliases = Aliases; int num = 0; while (true) { if (num < aliases.Length) { string text2 = aliases[num]; text = text2.ToLowerInvariant(); if (string.IsNullOrWhiteSpace(text) || !Regex.Match(text, "\\S+").Success) { throw new InvalidCommandException("Aliases", text, this); } if (!commands.TryGetValue(text, out value)) { break; } ChatCommandAPI.Logger.LogDebug((object)string.Format("{0} '{1}' >> {2}", "Aliases", text, value)); num++; continue; } text = base.FullName.ToLowerInvariant(); if (!commands.TryGetValue(text, out value)) { break; } ChatCommandAPI.Logger.LogDebug((object)string.Format("{0} '{1}' >> {2}", "FullName", text, value)); throw new DuplicateCommandException(this); } } ChatCommandAPI.Instance.commands[text] = this; ChatCommandAPI.Logger.LogInfo((object)("Registered as '" + text + "'")); } public abstract void Invoke(string args); } public class CommandException : Exception { public CommandException(string message) : base(message) { } } internal sealed class DuplicateCommandException : Exception { public DuplicateCommandException(BaseCommand command) : base("Duplicate command registry: " + command.FullName + " in " + command.GetType().Assembly.FullName) { } } public sealed class InvalidArgumentsException : CommandException { public InvalidArgumentsException() : base("Invalid arguments") { } } internal class InvalidCommandException : Exception { public InvalidCommandException(string propertyName, string propertyValue, BaseCommand command) : base("Error registering " + command.FullName + ": Invalid " + propertyName + " '" + propertyValue + "'") { } } public abstract class MultiOptionCommand<T> : Command where T : Enum { public sealed override string[] Syntax { get { Array values = Enum.GetValues(typeof(T)); if (values.Length <= 5) { return new string[1] { "{ " + string.Join(" | ", values) + " }" }; } return new string[1] { "[value]" }; } } public virtual T CurrentValue { get; set; } protected MultiOptionCommand() { Array values = Enum.GetValues(typeof(T)); if (values.Length == 0) { throw new ArgumentException("Invalid enum type " + typeof(T).AssemblyQualifiedName + " (contains no values)", "T"); } } public sealed override void Invoke(string args) { if (string.IsNullOrWhiteSpace(args)) { Array values = Enum.GetValues(typeof(T)); Chat.Print(string.Format("{0} values:\n<indent=15%>{1}</indent>\n{2} value: <color=#{3}>{4}</color>", typeof(T).Name, string.Join('\n', values), Name, Enum.IsDefined(typeof(T), CurrentValue) ? "00ffff" : "ff0000", CurrentValue)); } else { if (!Enum.TryParse(typeof(T), args, out object result)) { throw new InvalidArgumentsException(); } T currentValue = CurrentValue; CurrentValue = (T)result; Changed(currentValue); } } protected virtual void Changed(T oldValue) { Chat.Print($"{Name} changed to {CurrentValue}"); } } public abstract class ServerCommand : BaseCommand { public ServerCommand() { ChatCommandAPI.Logger.LogInfo((object)("Registering server command " + base.FullName + "...")); string name = Name; if (string.IsNullOrWhiteSpace(name)) { throw new InvalidCommandException("Name", name, this); } string text = Command.ToLowerInvariant(); if (string.IsNullOrWhiteSpace(text) || !Regex.Match(text, "\\S+").Success) { throw new InvalidCommandException("Command", text, this); } IReadOnlyDictionary<string, ServerCommand> serverCommands = ChatCommandAPI.Instance.ServerCommands; if (serverCommands.TryGetValue(text, out var value)) { ChatCommandAPI.Logger.LogDebug((object)string.Format("{0} '{1}' >> {2}", "Command", text, value)); string[] aliases = Aliases; int num = 0; while (true) { if (num < aliases.Length) { string text2 = aliases[num]; text = text2.ToLowerInvariant(); if (string.IsNullOrWhiteSpace(text) || !Regex.Match(text, "\\S+").Success) { throw new InvalidCommandException("Aliases", text, this); } if (!serverCommands.TryGetValue(text, out value)) { break; } ChatCommandAPI.Logger.LogDebug((object)string.Format("{0} '{1}' >> {2}", "Aliases", text, value)); num++; continue; } text = base.FullName.ToLowerInvariant(); if (!serverCommands.TryGetValue(text, out value)) { break; } ChatCommandAPI.Logger.LogDebug((object)string.Format("{0} '{1}' >> {2}", "FullName", text, value)); throw new DuplicateCommandException(this); } } ChatCommandAPI.Instance.serverCommands[text] = this; ChatCommandAPI.Logger.LogInfo((object)("Registered as '" + text + "'")); } public abstract void Invoke(PlayerControllerB caller, string args); } public abstract class ToggleCommand : Command { public sealed override string[] Syntax => new string[1] { "{ true | false }" }; public virtual bool CurrentValue { get; set; } public sealed override void Invoke(string args) { bool result; if (string.IsNullOrWhiteSpace(args)) { result = !CurrentValue; } else if (!bool.TryParse(args, out result)) { throw new InvalidArgumentsException(); } bool currentValue = CurrentValue; CurrentValue = result; Changed(currentValue); } protected virtual void Changed(bool oldValue) { Chat.Print($"{Name} is now {CurrentValue}"); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "baer1.ChatCommandAPI"; public const string PLUGIN_NAME = "ChatCommandAPI"; public const string PLUGIN_VERSION = "1.1.2"; } } namespace ChatCommandAPI.Utils { public static class Args { private enum QuotationMarks : byte { None, Double, PostDouble, Single, PostSingle } public static IEnumerable<string> Parse(string args, uint? max = null, bool escape = true) { args = args.TrimEnd(); QuotationMarks q = QuotationMarks.None; StringBuilder sb = new StringBuilder(); bool escaped = false; int i = 0; string text = args; int num = 0; while (true) { if (num < text.Length) { char c = text[num]; int num2 = i + 1; i = num2; switch (c) { case '"': if (escaped) { sb.Append(c); escaped = false; } else { switch (q) { case QuotationMarks.None: if (sb.Length <= 0) { q = QuotationMarks.Double; } else { sb.Append(c); } goto IL_051e; case QuotationMarks.Double: break; case QuotationMarks.Single: sb.Append(c); goto IL_051e; default: throw new InvalidArgumentsException(); } yield return sb.ToString(); if (max.HasValue) { uint? num3 = max - 1; max = num3; if (num3 == 0) { break; } } sb.Clear(); q = QuotationMarks.PostDouble; } goto IL_051e; case '\'': if (escaped) { sb.Append(c); escaped = false; } else { switch (q) { case QuotationMarks.None: if (sb.Length <= 0) { q = QuotationMarks.Single; } else { sb.Append(c); } goto IL_051e; case QuotationMarks.Single: break; case QuotationMarks.Double: sb.Append(c); goto IL_051e; default: throw new InvalidArgumentsException(); } yield return sb.ToString(); if (max.HasValue) { uint? num3 = max - 1; max = num3; if (num3 == 0) { break; } } sb.Clear(); q = QuotationMarks.PostSingle; } goto IL_051e; case '\\': if (escaped || !escape) { sb.Append(c); escaped = false; } else { escaped = escape; } goto IL_051e; default: if (char.IsWhiteSpace(c)) { switch (q) { case QuotationMarks.PostDouble: case QuotationMarks.PostSingle: q = QuotationMarks.None; goto IL_051e; case QuotationMarks.None: break; default: sb.Append(c); goto IL_051e; } if (escaped) { sb.Append(c); escaped = false; } else if (sb.Length > 0) { yield return sb.ToString(); if (max.HasValue) { uint? num3 = max - 1; max = num3; if (num3 == 0) { break; } } sb.Clear(); } } else { QuotationMarks quotationMarks = q; if (quotationMarks == QuotationMarks.PostDouble || quotationMarks == QuotationMarks.PostSingle) { throw new InvalidArgumentsException(); } if (escaped) { sb.Append(c switch { '0' => '\0', 'a' => '\a', 'b' => '\b', 'e' => '\u001b', 'f' => '\f', 'n' => '\n', 'r' => '\r', 't' => '\t', 'v' => '\v', _ => throw new InvalidArgumentsException(), }); escaped = false; } else { sb.Append(c); } } goto IL_051e; } break; } switch (q) { case QuotationMarks.None: case QuotationMarks.PostDouble: case QuotationMarks.PostSingle: if (sb.Length > 0) { yield return sb.ToString(); } break; default: throw new InvalidArgumentsException(); } yield break; IL_051e: num++; } string text2 = args.Substring(i); if (text2.Length > 0) { yield return text2.Trim(); } } } public static class Chat { public static readonly System.Drawing.Color DEFAULT_CHAT_COLOR = System.Drawing.Color.FromArgb(112, 105, 255); internal static ulong? targetClientId; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static string Clean(string message) { return message.Replace("\0", "\\0"); } private static void Print(string message, System.Drawing.Color color) { HUDManager.Instance.AddChatMessage($"<color=#{color.R:X2}{color.G:X2}{color.B:X2}{color.A:X2}>{Clean(message)}</color>", "", -1, false); } internal static void Print(ulong targetClientId, string message, System.Drawing.Color color) { Chat.targetClientId = targetClientId; HUDManager.Instance.AddTextMessageClientRpc($"<color=#{color.R:X2}{color.G:X2}{color.B:X2}{color.A:X2}>{Clean(message)}</color>"); Chat.targetClientId = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void Print(PlayerControllerB target, string message, System.Drawing.Color color) { Print(target.actualClientId, message, color); } private static void PrintGlobal(string message, System.Drawing.Color color) { HUDManager.Instance.AddTextMessageServerRpc($"<color=#{color.R:X2}{color.G:X2}{color.B:X2}{color.A:X2}>{Clean(message)}</color>"); } public static void Print(string message, System.Drawing.Color? color = null) { ChatCommandAPI.Logger.LogInfo((object)("Print: " + message)); Print(message, color ?? ChatCommandAPI.Instance.DefaultMessageColor); } public static void PrintWarning(string message) { ChatCommandAPI.Logger.LogWarning((object)("PrintWarning: " + message)); Print(message, System.Drawing.Color.Yellow); } public static void PrintError(string message) { ChatCommandAPI.Logger.LogError((object)("PrintError: " + message)); Print(message, System.Drawing.Color.Red); } public static void Print(PlayerControllerB target, string message, System.Drawing.Color? color = null) { ChatCommandAPI.Logger.LogInfo((object)("Print(" + target.PlayerString() + "): " + message)); Print(target, message, color ?? ChatCommandAPI.Instance.DefaultMessageColor); } public static void PrintWarning(PlayerControllerB target, string message) { ChatCommandAPI.Logger.LogWarning((object)("PrintWarning(" + target.PlayerString() + "): " + message)); Print(target, message, System.Drawing.Color.Yellow); } public static void PrintError(PlayerControllerB target, string message) { ChatCommandAPI.Logger.LogError((object)("PrintError(" + target.PlayerString() + "): " + message)); Print(target, message, System.Drawing.Color.Red); } public static void PrintGlobal(string message, System.Drawing.Color? color = null) { ChatCommandAPI.Logger.LogInfo((object)("PrintGlobal: " + message)); PrintGlobal(message, color ?? ChatCommandAPI.Instance.DefaultMessageColor); } public static void PrintWarningGlobal(string message) { ChatCommandAPI.Logger.LogWarning((object)("PrintWarningGlobal: " + message)); PrintGlobal(message, System.Drawing.Color.Yellow); } public static void PrintErrorGlobal(string message) { ChatCommandAPI.Logger.LogError((object)("PrintErrorGlobal: " + message)); PrintGlobal(message, System.Drawing.Color.Red); } public static string Escape(string message) { return message.Replace('<', '<').Replace('>', '>'); } } public static class Color { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static System.Drawing.Color ToSystem(this Color color) { //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) return Color32.op_Implicit(color).ToSystem(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static System.Drawing.Color ToSystem(this Color32 color) { //IL_0000: 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) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) return System.Drawing.Color.FromArgb(color.a, color.r, color.g, color.b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color ToUnity(this System.Drawing.Color color) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) return new Color((float)(int)color.R / 255f, (float)(int)color.G / 255f, (float)(int)color.B / 255f, (float)(int)color.A / 255f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Color32 ToUnity32(this System.Drawing.Color color) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) return new Color32(color.R, color.G, color.B, color.A); } } public static class Player { public sealed class UnknownPlayerException : CommandException { public UnknownPlayerException(string identifier) : base("Unknown player '" + identifier + "'") { } } private static readonly Regex PLAYER_IDENTIFIER_REGEX = new Regex("(?:#?(\\d+))(?: (.+))?", RegexOptions.Compiled); public static bool TryGetPlayer(int playerId, out PlayerControllerB result) { result = null; if (playerId < 0) { return false; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return false; } PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; if (allPlayerScripts.Length <= playerId) { return false; } result = allPlayerScripts[playerId]; return result.IsPlayerControlled(); } public static bool TryGetPlayer(string identifier, out PlayerControllerB result) { result = null; if (string.IsNullOrWhiteSpace(identifier)) { return false; } identifier = identifier.Trim(); StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return false; } if (identifier == "@s") { result = instance.localPlayerController; return true; } PlayerControllerB[] array = instance.allPlayerScripts.Where((PlayerControllerB i) => i.IsPlayerControlled()).ToArray(); if (array.Length == 0) { return false; } if (identifier == "@r") { result = array[new Random().Next(array.Length)]; return true; } if (Object.op_Implicit((Object)(object)(result = ((IEnumerable<PlayerControllerB>)array).FirstOrDefault((Func<PlayerControllerB, bool>)((PlayerControllerB i) => string.Equals(i.playerUsername.Trim(), identifier, StringComparison.InvariantCulture)))))) { return true; } if (Object.op_Implicit((Object)(object)(result = ((IEnumerable<PlayerControllerB>)array).FirstOrDefault((Func<PlayerControllerB, bool>)((PlayerControllerB i) => string.Equals(i.playerUsername.Trim(), identifier, StringComparison.InvariantCultureIgnoreCase)))))) { return true; } Match match = PLAYER_IDENTIFIER_REGEX.Match(identifier); if (match.Success) { Group obj = match.Groups[1]; if (obj.Success && int.TryParse(obj.Value, out var result2) && TryGetPlayer(result2, out result)) { return true; } Group obj2 = match.Groups[2]; if (obj2.Success && TryGetPlayer(obj2.Value, out result)) { return true; } } PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if (val.playerUsername.StartsWith(identifier, StringComparison.InvariantCultureIgnoreCase)) { result = val; return true; } } return false; } public static PlayerControllerB[] GetPlayers(string identifier) { if (string.IsNullOrWhiteSpace(identifier)) { return Array.Empty<PlayerControllerB>(); } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return Array.Empty<PlayerControllerB>(); } identifier = identifier.Trim(); if (identifier == "@s") { return (PlayerControllerB[])(object)new PlayerControllerB[1] { instance.localPlayerController }; } Match match = PLAYER_IDENTIFIER_REGEX.Match(identifier); if (match.Success) { Group obj = match.Groups[1]; if (obj.Success && int.TryParse(obj.Value, out var result) && TryGetPlayer(result, out PlayerControllerB result2)) { return (PlayerControllerB[])(object)new PlayerControllerB[1] { result2 }; } } PlayerControllerB[] array = instance.allPlayerScripts.Where((PlayerControllerB i) => i.IsPlayerControlled()).ToArray(); if (array.Length == 0) { return Array.Empty<PlayerControllerB>(); } string text = identifier; if (!(text == "@a")) { if (text == "@r") { return (PlayerControllerB[])(object)new PlayerControllerB[1] { array[new Random().Next(array.Length)] }; } return array.Where((PlayerControllerB i) => i.playerUsername.Trim().StartsWith(identifier, StringComparison.InvariantCultureIgnoreCase)).ToArray(); } return array; } public static string PlayerString(this PlayerControllerB player) { return $"#{player.playerClientId} {player.playerUsername}"; } public static bool IsPlayerControlled(this PlayerControllerB player) { if (Object.op_Implicit((Object)(object)player) && player != null && !player.disconnectedMidGame && !player.isTestingPlayer) { if (!player.isPlayerControlled) { return player.isPlayerDead; } return true; } return false; } } public static class Pos { private static readonly Regex POSITION_REGEX = new Regex("(~)?(-?(?:(?:\\d+)(?:.\\d+)?|(?:\\d+)?(?:.\\d+)))", RegexOptions.Compiled); public static bool TryParse(string pos, out Vector3 result, Vector3? playerPosition = null, Quaternion? cameraAngles = null) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) result = default(Vector3); string[] array = Args.Parse(pos).ToArray(); if (array.Length != 3) { return false; } return TryParse(array[0], array[1], array[2], out result, playerPosition, cameraAngles); } public static bool TryParse(string x, string y, string z, out Vector3 result, Vector3? playerPosition = null, Quaternion? cameraAngles = null) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: 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_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) result = default(Vector3); float result2; float result3; float result4; if (x.StartsWith('^')) { if (!y.StartsWith('^') || !z.StartsWith('^') || !playerPosition.HasValue || !cameraAngles.HasValue) { return false; } if (string.IsNullOrWhiteSpace(x = x.Substring(1))) { result2 = 0f; } else if (!float.TryParse(x, NumberStyles.Float, CultureInfo.InvariantCulture, out result2)) { return false; } if (string.IsNullOrWhiteSpace(y = y.Substring(1))) { result3 = 0f; } else if (!float.TryParse(y, NumberStyles.Float, CultureInfo.InvariantCulture, out result3)) { return false; } if (string.IsNullOrWhiteSpace(z = z.Substring(1))) { result4 = 0f; } else if (!float.TryParse(z, NumberStyles.Float, CultureInfo.InvariantCulture, out result4)) { return false; } result = playerPosition.Value + cameraAngles.Value * Vector3.forward * result4 + cameraAngles.Value * Vector3.up * result3 + cameraAngles.Value * Vector3.right * (0f - result2); return true; } if (y.StartsWith('^') || z.StartsWith('^')) { return false; } bool flag = false; bool flag2 = false; bool flag3 = false; if (x.StartsWith('~')) { if (!playerPosition.HasValue) { return false; } flag = true; x = x.Substring(1); if (string.IsNullOrWhiteSpace(x)) { result2 = 0f; goto IL_0191; } } if (!float.TryParse(x, NumberStyles.Float, CultureInfo.InvariantCulture, out result2)) { return false; } goto IL_0191; IL_01d8: if (z.StartsWith('~')) { if (!playerPosition.HasValue) { return false; } flag3 = true; z = z.Substring(1); if (string.IsNullOrWhiteSpace(z)) { result4 = 0f; goto IL_021f; } } if (!float.TryParse(z, NumberStyles.Float, CultureInfo.InvariantCulture, out result4)) { return false; } goto IL_021f; IL_0191: if (y.StartsWith('~')) { if (!playerPosition.HasValue) { return false; } flag2 = true; y = y.Substring(1); if (string.IsNullOrWhiteSpace(y)) { result3 = 0f; goto IL_01d8; } } if (!float.TryParse(y, NumberStyles.Float, CultureInfo.InvariantCulture, out result3)) { return false; } goto IL_01d8; IL_021f: result = new Vector3(flag ? (result2 + playerPosition.Value.x) : result2, flag2 ? (result3 + playerPosition.Value.y) : result3, flag3 ? (result4 + playerPosition.Value.z) : result4); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryParse(string pos, out Vector3 result, PlayerControllerB player) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) return TryParse(pos, out result, ((Component)player).transform.position, ((Component)player.gameplayCamera).transform.rotation); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryParse(string x, string y, string z, out Vector3 result, PlayerControllerB player) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) return TryParse(x, y, z, out result, ((Component)player).transform.position, ((Component)player.gameplayCamera).transform.rotation); } } } namespace ChatCommandAPI.ServerCommands { public class Help : ServerCommand { public override string Description => "Displays all available commands or details about a single one"; public override string[] Aliases => new string[1] { "?" }; public override string[] Syntax => new string[1] { "[command]" }; public override bool Hidden => true; public override void Invoke(PlayerControllerB caller, string args) { char prefix = ChatCommandAPI.Instance.ServerCommandPrefix; if (!string.IsNullOrWhiteSpace(args)) { Match match = Regex.Match(args.Trim(), "\\S+"); if (!match.Success) { throw new CommandException("Invalid command name"); } if (!ChatCommandAPI.Instance.TryGetServerCommand(match.Value, out ServerCommand command, out string primaryName)) { throw new CommandException("Unknown command '" + match.Value + "'"); } Chat.Print(caller, global::ChatCommandAPI.Commands.Help.DetailedHelp(prefix, command, primaryName)); } else { KeyValuePair<string, ServerCommand>[] array = ChatCommandAPI.Instance.ServerCommands.Where<KeyValuePair<string, ServerCommand>>((KeyValuePair<string, ServerCommand> kvp) => !kvp.Value.Hidden).ToArray(); Chat.Print(caller, global::ChatCommandAPI.Commands.Help.DetailedHelp(prefix, this, null, includeSource: false) + "\n<color=#00FFFF>===============</color>\n" + ((array.Length == 0) ? "<color=#ff0000>No commands available</color>" : ("Available commands:\n" + string.Join('\n', array.Select((KeyValuePair<string, ServerCommand> kvp) => $"{prefix}{kvp.Key}"))))); } } } } namespace ChatCommandAPI.Patches { [HarmonyPatch(typeof(HUDManager), "AddPlayerChatMessageServerRpc")] internal static class HUDManager_AddPlayerChatMessageServerRpc { private static bool Prefix(ref HUDManager __instance, ref string chatMessage, ref int playerId) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 if ((int)((NetworkBehaviour)__instance).__rpc_exec_stage != 1 || !((NetworkBehaviour)__instance).IsServer || string.IsNullOrWhiteSpace(chatMessage)) { return true; } if (!Player.TryGetPlayer(playerId, out PlayerControllerB result)) { ChatCommandAPI.Logger.LogWarning((object)$"Chat message sent by invalid player #{playerId}"); return true; } if (!ChatCommandAPI.Instance.TryParseServerCommand(chatMessage, out string name, out string args)) { return true; } if (ChatCommandAPI.Instance.TryGetServerCommand(name, out ServerCommand command, out string _)) { ChatCommandAPI.Logger.LogInfo((object)("Executing server command as '" + result.PlayerString() + "': " + name + " (" + command.FullName + ")")); try { command.Invoke(result, args); ChatCommandAPI.Logger.LogInfo((object)"Command executed successfully"); } catch (CommandException ex) { Chat.PrintError(result, string.IsNullOrWhiteSpace(ex.Message) ? ("An unspecified error occurred while executing command '" + name + "'") : ("An error occurred while executing command '" + name + "': " + ex.Message.Trim())); ChatCommandAPI.Logger.LogError((object)ex); } catch (Exception ex2) { Chat.PrintError(result, "An unexpected " + ex2.GetType().Name + " error occurred while executing command '" + name + "'"); Chat.PrintWarning("An unexpected " + ex2.GetType().Name + " error occurred while executing command '" + name + "' as " + result.PlayerString() + "\nCheck the logs for more details"); ChatCommandAPI.Logger.LogError((object)ex2); } return false; } Chat.PrintError(result, "Unknown command: '" + name + "'"); return false; } } [HarmonyPatch(typeof(HUDManager), "AddTextMessageClientRpc")] internal static class HUDManager_AddTextMessageClientRpc { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_003b: Expected O, but got Unknown return CodeMatcherExtensions.MatchForward(new CodeMatcher(instructions, (ILGenerator)null), (CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(NetworkBehaviour), "__endSendClientRpc", (Type[])null, (Type[])null), (string)null) }).Advance(-1).Insert((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstruction.Call(typeof(HUDManager_AddTextMessageClientRpc), "RedirectMessageToClient", (Type[])null, (Type[])null) }) .InstructionEnumeration(); } private static ClientRpcParams RedirectMessageToClient(ClientRpcParams clientRpcParams) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (Chat.targetClientId.HasValue) { return new ClientRpcParams { Send = { TargetClientIds = new <>z__ReadOnlySingleElementList<ulong>(Chat.targetClientId.Value) } }; } return clientRpcParams; } } [HarmonyPatch(typeof(HUDManager), "SubmitChat_performed")] [HarmonyPriority(int.MaxValue)] internal static class HUDManager_SubmitChat_performed { private static bool Prefix(ref HUDManager __instance, ref CallbackContext context) { string text = __instance.chatTextField.text; if (!((CallbackContext)(ref context)).performed || string.IsNullOrWhiteSpace(text)) { return true; } if (!ChatCommandAPI.Instance.TryParseCommand(text, out string name, out string args)) { return true; } if (ChatCommandAPI.Instance.TryGetCommand(name, out Command command, out string _)) { resetChat(ref __instance); ChatCommandAPI.Logger.LogInfo((object)("Executing command: " + name + " (" + command.FullName + ")")); try { command.Invoke(args); ChatCommandAPI.Logger.LogInfo((object)"Command executed successfully"); } catch (CommandException ex) { Chat.PrintError(string.IsNullOrWhiteSpace(ex.Message) ? ("An unspecified error occurred while executing command '" + name + "'") : ("An error occurred while executing command '" + name + "': " + ex.Message.Trim())); ChatCommandAPI.Logger.LogError((object)ex); } catch (Exception ex2) { Chat.PrintError("An unexpected " + ex2.GetType().Name + " error occurred while executing command '" + name + "'\nCheck the logs for more details"); ChatCommandAPI.Logger.LogError((object)ex2); } return false; } Chat.PrintError("Unknown command: '" + name + "'"); if (ChatCommandAPI.Instance.CompatibilityModeEnabled) { return true; } resetChat(ref __instance); return false; [MethodImpl(MethodImplOptions.AggressiveInlining)] static void resetChat(ref HUDManager reference) { reference.localPlayer.isTypingChat = false; reference.chatTextField.text = ""; EventSystem.current.SetSelectedGameObject((GameObject)null); reference.PingHUDElement(reference.Chat, 2f, 1f, 0.2f); ((Behaviour)reference.typingIndicator).enabled = false; } } } [HarmonyPatch(typeof(StartOfRound), "SyncAlreadyHeldObjectsServerRpc")] internal static class StartOfRound_SyncAlreadyHeldObjectsServerRpc { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_004d: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown return CodeMatcherExtensions.MatchForward(new CodeMatcher(instructions, (ILGenerator)null), (CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(Debug), "Log", new Type[1] { typeof(string) }, (Type[])null), (string)null) }).Insert((CodeInstruction[])(object)new CodeInstruction[2] { new CodeInstruction(OpCodes.Ldarg_1, (object)null), CodeInstruction.Call(typeof(StartOfRound_SyncAlreadyHeldObjectsServerRpc), "SendWelcomeMessage", (Type[])null, (Type[])null) }).InstructionEnumeration(); } private static void SendWelcomeMessage(ulong clientId) { string serverWelcomeMessage = ChatCommandAPI.Instance.ServerWelcomeMessage; if (!string.IsNullOrWhiteSpace(serverWelcomeMessage) && !ChatCommandAPI.Instance.ServerCommands.All<KeyValuePair<string, ServerCommand>>((KeyValuePair<string, ServerCommand> kvp) => kvp.Value.Hidden)) { Chat.Print(clientId, serverWelcomeMessage, Chat.DEFAULT_CHAT_COLOR); } } } } namespace ChatCommandAPI.Old { [Obsolete("This is provided for easier porting of old mods.\nTHIS IS A TEMPORARY SOLUTION AND IT MAY BE COMPLETELY REMOVED IN FUTURE VERSIONS")] public abstract class LegacyCommand : Command { internal const string OBSOLETE_MESSAGE = "This is provided for easier porting of old mods.\nTHIS IS A TEMPORARY SOLUTION AND IT MAY BE COMPLETELY REMOVED IN FUTURE VERSIONS"; public virtual string[] Commands => new string[1] { Name.ToLowerInvariant() }; public sealed override string Command => Commands[0]; public sealed override string[] Aliases { get { string[] commands = Commands; if (commands.Length == 0) { return Array.Empty<string>(); } return commands[1..]; } } protected LegacyCommand() { ChatCommandAPI.Logger.LogWarning((object)$"The command {base.FullName} ({this}) is using the Legacy API. Please upgrade it to the new API"); if (Commands.Length == 0) { throw new Exception("You need to provide at least one command"); } } public sealed override void Invoke(string args) { if (!Invoke(Args.Parse(args).ToArray(), out string error)) { throw new CommandException(error); } } public abstract bool Invoke(string[] args, out string? error); } [Obsolete("This is provided for easier porting of old mods.\nTHIS IS A TEMPORARY SOLUTION AND IT MAY BE COMPLETELY REMOVED IN FUTURE VERSIONS")] public abstract class LegacyServerCommand : ServerCommand { public virtual string[] Commands => new string[1] { Name.ToLowerInvariant() }; public sealed override string Command => Commands[0]; public sealed override string[] Aliases { get { string[] commands = Commands; if (commands.Length == 0) { return Array.Empty<string>(); } return commands[1..]; } } protected LegacyServerCommand() { ChatCommandAPI.Logger.LogWarning((object)$"The server command {base.FullName} ({this}) is using the Legacy API. Please upgrade it to the new API"); if (Commands.Length == 0) { throw new Exception("You need to provide at least one command"); } } public sealed override void Invoke(PlayerControllerB caller, string args) { if (!Invoke(caller, Args.Parse(args).ToArray(), out string error)) { throw new CommandException(error); } } public abstract bool Invoke(PlayerControllerB caller, string[] args, out string? error); } [Obsolete("This is provided for easier porting of old mods.\nTHIS IS A TEMPORARY SOLUTION AND IT MAY BE COMPLETELY REMOVED IN FUTURE VERSIONS")] public abstract class LegacyToggleCommand : ToggleCommand { public virtual string[] Commands => new string[1] { Name.ToLowerInvariant() }; public sealed override string Command => Commands[0]; public sealed override string[] Aliases { get { string[] commands = Commands; if (commands.Length == 0) { return Array.Empty<string>(); } return commands[1..]; } } public sealed override bool CurrentValue { get { return Value; } set { Value = value; } } public abstract bool Value { get; set; } public virtual string EnabledString => "enabled"; public virtual string DisabledString => "disabled"; public virtual string ValueString { get { if (!Value) { return DisabledString; } return EnabledString; } } public virtual string? ToggleDescription => null; public sealed override string Description { get { string toggleDescription = ToggleDescription; if (!string.IsNullOrWhiteSpace(toggleDescription)) { return toggleDescription + " - " + ValueString; } return ValueString; } } protected LegacyToggleCommand() { ChatCommandAPI.Logger.LogWarning((object)$"The toggle command {base.FullName} ({this}) is using the Legacy API. Please upgrade it to the new API"); if (Commands.Length == 0) { throw new Exception("You need to provide at least one command"); } } public virtual void PrintValue() { Chat.Print(Name + " " + ValueString); } protected sealed override void Changed(bool oldValue) { PrintValue(); } } } namespace ChatCommandAPI.Commands { public class Help : Command { internal const string SEPARATOR = "<color=#00FFFF>===============</color>\n"; public override string Description => "Displays all available commands or details about a single one"; public override string[] Aliases => new string[1] { "?" }; public override string[] Syntax => new string[1] { "[command]" }; public override bool Hidden => true; public override void Invoke(string args) { char prefix = ChatCommandAPI.Instance.CommandPrefix; if (!string.IsNullOrWhiteSpace(args)) { Match match = Regex.Match(args.Trim(), "\\S+"); if (!match.Success) { throw new CommandException("Invalid command name"); } if (!ChatCommandAPI.Instance.TryGetCommand(match.Value, out Command command, out string primaryName)) { throw new CommandException("Unknown command '" + match.Value + "'"); } Chat.Print(DetailedHelp(prefix, command, primaryName)); } else { KeyValuePair<string, Command>[] array = ChatCommandAPI.Instance.Commands.Where<KeyValuePair<string, Command>>((KeyValuePair<string, Command> kvp) => !kvp.Value.Hidden).ToArray(); Chat.Print(DetailedHelp(prefix, this, null, includeSource: false) + "\n<color=#00FFFF>===============</color>\n" + ((array.Length == 0) ? "<color=#ff0000>No commands available</color>" : ("Available commands:\n" + string.Join('\n', array.Select((KeyValuePair<string, Command> kvp) => $"{prefix}{kvp.Key}"))))); } } public static string DetailedHelp(char prefix, BaseCommand command, string primaryName = null, bool includeSource = true) { if (string.IsNullOrWhiteSpace(primaryName)) { primaryName = command.Command.ToLowerInvariant(); } string description = command.Description; string[] syntax = command.Syntax; return command.Name + (string.IsNullOrWhiteSpace(description) ? null : (" - " + description)) + "\n" + string.Join('\n', ((syntax.Length == 0) ? new string[1] : syntax).Select((string i) => string.Format("<color=#ffff00>{0}{1}</color>{2}", prefix, primaryName, string.IsNullOrWhiteSpace(i) ? null : (" <color=#dddd00><noparse>" + i + "</noparse></color>")))) + (includeSource ? ("\n<color=#888888><size=60%><b>" + command.FullName + "</b> in " + command.GetType().Assembly.GetName().Name + "</size></color>") : null); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }