using 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 : IEnumerable, ICollection, IList, IEnumerable, IReadOnlyCollection, IReadOnlyList, ICollection, IList { private sealed class Enumerator : IDisposable, IEnumerator, IEnumerator { object IEnumerator.Current => _item; T IEnumerator.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.Count => 1; T IReadOnlyList.this[int index] { get { if (index != 0) { throw new IndexOutOfRangeException(); } return _item; } } int ICollection.Count => 1; bool ICollection.IsReadOnly => true; T IList.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.Default.Equals(_item, (T)value); } int IList.IndexOf(object value) { if (!EqualityComparer.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 IEnumerable.GetEnumerator() { return new Enumerator(_item); } void ICollection.Add(T item) { throw new NotSupportedException(); } void ICollection.Clear() { throw new NotSupportedException(); } bool ICollection.Contains(T item) { return EqualityComparer.Default.Equals(_item, item); } void ICollection.CopyTo(T[] array, int arrayIndex) { array[arrayIndex] = _item; } bool ICollection.Remove(T item) { throw new NotSupportedException(); } int IList.IndexOf(T item) { if (!EqualityComparer.Default.Equals(_item, item)) { return -1; } return 0; } void IList.Insert(int index, T item) { throw new NotSupportedException(); } void IList.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(); 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 commands = new Dictionary(); internal readonly Dictionary serverCommands = new Dictionary(); private ConfigEntry commandPrefix; private ConfigEntry compatibilityModeEnabled; private ConfigEntry defaultMessageColor; private ConfigEntry serverCommandPrefix; private ConfigEntry serverCommandsEnabled; private ConfigEntry 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 Commands => commands; public IReadOnlyDictionary 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("General", "DefaultMessageColor", System.Drawing.Color.Aqua.ToUnity(), "The default color to use for chat responses"); commandPrefix = ((BaseUnityPlugin)this).Config.Bind("Client", "CommandPrefix", '/'.ToString(), "The prefix to use for client-side commands"); compatibilityModeEnabled = ((BaseUnityPlugin)this).Config.Bind("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("Server", "ServerCommandsEnabled", false, "Enables or disables server commands"); serverCommandPrefix = ((BaseUnityPlugin)this).Config.Bind("Server", "ServerCommandPrefix", '!'.ToString(), "The prefix to use for server-side commands"); serverWelcomeMessage = ((BaseUnityPlugin)this).Config.Bind("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 readOnlyDictionary = Commands; if (readOnlyDictionary.TryGetValue(name, out command)) { return true; } foreach (KeyValuePair item in readOnlyDictionary) { if (string.Equals(item.Value.FullName, name, StringComparison.InvariantCultureIgnoreCase)) { primaryName = item.Key; command = item.Value; return true; } } foreach (KeyValuePair item2 in readOnlyDictionary) { if (string.Equals(item2.Value.Command, name, StringComparison.InvariantCultureIgnoreCase)) { primaryName = item2.Key; command = item2.Value; return true; } } foreach (KeyValuePair item3 in readOnlyDictionary) { if (item3.Value.Aliases.Contains(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 readOnlyDictionary = ServerCommands; if (readOnlyDictionary.TryGetValue(name, out command)) { return true; } foreach (KeyValuePair item in readOnlyDictionary) { if (string.Equals(item.Value.FullName, name, StringComparison.InvariantCultureIgnoreCase)) { primaryName = item.Key; command = item.Value; return true; } } foreach (KeyValuePair item2 in readOnlyDictionary) { if (string.Equals(item2.Value.Command, name, StringComparison.InvariantCultureIgnoreCase)) { primaryName = item2.Key; command = item2.Value; return true; } } foreach (KeyValuePair item3 in readOnlyDictionary) { if (item3.Value.Aliases.Contains(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 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 : 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{1}\n{2} value: {4}", 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 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 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($"{Clean(message)}", "", -1, false); } internal static void Print(ulong targetClientId, string message, System.Drawing.Color color) { Chat.targetClientId = targetClientId; HUDManager.Instance.AddTextMessageClientRpc($"{Clean(message)}"); 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($"{Clean(message)}"); } 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)array).FirstOrDefault((Func)((PlayerControllerB i) => string.Equals(i.playerUsername.Trim(), identifier, StringComparison.InvariantCulture)))))) { return true; } if (Object.op_Implicit((Object)(object)(result = ((IEnumerable)array).FirstOrDefault((Func)((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(); } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return Array.Empty(); } 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(); } 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[] array = ChatCommandAPI.Instance.ServerCommands.Where>((KeyValuePair kvp) => !kvp.Value.Hidden).ToArray(); Chat.Print(caller, global::ChatCommandAPI.Commands.Help.DetailedHelp(prefix, this, null, includeSource: false) + "\n===============\n" + ((array.Length == 0) ? "No commands available" : ("Available commands:\n" + string.Join('\n', array.Select((KeyValuePair 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 Transpiler(IEnumerable 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(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 Transpiler(IEnumerable 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 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(); } 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(); } 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(); } 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 = "===============\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[] array = ChatCommandAPI.Instance.Commands.Where>((KeyValuePair kvp) => !kvp.Value.Hidden).ToArray(); Chat.Print(DetailedHelp(prefix, this, null, includeSource: false) + "\n===============\n" + ((array.Length == 0) ? "No commands available" : ("Available commands:\n" + string.Join('\n', array.Select((KeyValuePair 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("{0}{1}{2}", prefix, primaryName, string.IsNullOrWhiteSpace(i) ? null : (" " + i + "")))) + (includeSource ? ("\n" + command.FullName + " in " + command.GetType().Assembly.GetName().Name + "") : null); } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }