Decompiled source of VampireCommandFramework v0.11.0
VampireCommandFramework.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using Il2CppSystem.Collections.Generic; using Microsoft.CodeAnalysis; using Microsoft.Extensions.DependencyInjection; using ProjectM; using ProjectM.Network; using SemanticVersioning; using Unity.Collections; using Unity.Entities; using UnityEngine; using VCF.Core.Basics; using VampireCommandFramework; using VampireCommandFramework.Basics; using VampireCommandFramework.Breadstone; using VampireCommandFramework.Common; using VampireCommandFramework.Framework; using VampireCommandFramework.Registry; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("deca")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Framework for commands in V Rising")] [assembly: AssemblyFileVersion("0.11.0.0")] [assembly: AssemblyInformationalVersion("0.11.0+6.Branch.main.Sha.49faa2ed38b7c35e0a05ce0a7f973261c7d58620")] [assembly: AssemblyProduct("VampireCommandFramework")] [assembly: AssemblyTitle("VampireCommandFramework")] [assembly: InternalsVisibleTo("VCF.Tests")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.11.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace VampireCommandFramework { public class ChatCommandContext : ICommandContext { private static int maxMessageLength = 509; public VChatEvent Event { get; } public User User => Event.User; public IServiceProvider Services { get; } public string Name { get { //IL_0001: 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) User user = User; return ((object)(FixedString64Bytes)(ref user.CharacterName)).ToString(); } } public bool IsAdmin => User.IsAdmin; public ChatCommandContext(VChatEvent e) { Event = e; } public void Reply(string v) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (v.Length > maxMessageLength) { v = v.Substring(0, maxMessageLength); } ChatMessageQueue.Send(User, v); } public CommandException Error(string LogMessage) { return new CommandException(LogMessage); } } public static class Color { public static string Red = "red"; public static string Primary = "#d25"; public static string White = "#eee"; public static string LightGrey = "#bbf"; public static string Yellow = "#dd0"; public static string DarkGreen = "#0c0"; public static string Green = "#4c4"; public static string Command = "#4ED"; public static string Beige = "#fed"; public static string Gold = "#eb0"; public static string Lavender = "#c5d"; public static string Pink = "#faf"; public static string Periwinkle = "#52d"; public static string Teal = "#3ac"; public static string LightRed = "#f45"; public static string LightPurple = "#84c"; public static string Lilac = "#b9f"; public static string LightPink = "#EED"; public static string SoftBGrey = "#eef"; public static string AdminUsername = "#afa"; public static string ClanName = "#59e"; public static string LightBlood = "#f44"; public static string Blood = "#c24"; public static string LightChaos = "#faf"; public static string Chaos = "#d5e"; public static string LightUnholy = "#bd3"; public static string Unholy = "#4c0"; public static string LightIllusion = "#bfd"; public static string Illusion = "#3db"; public static string LightFrost = "#aef"; public static string Frost = "#09f"; public static string LightStorm = "#fe7"; public static string Storm = "#ff5"; public static string Discord = "#78f"; public static string Global = "#8cf"; public static string ClassicUsername = "#876"; } public abstract class CommandArgumentConverter<T> : CommandArgumentConverter<T, ICommandContext> { } public abstract class CommandArgumentConverter<T, C> where C : ICommandContext { public abstract T Parse(C ctx, string input); } [AttributeUsage(AttributeTargets.Method)] public sealed class CommandAttribute : Attribute { public string Name { get; } public string ShortHand { get; } public string Usage { get; } public string Description { get; } public string Id { get; } public bool AdminOnly { get; } public CommandAttribute(string name, string shortHand = null, string usage = null, string description = null, string id = null, bool adminOnly = false) { Name = name; ShortHand = shortHand; Usage = usage; Description = description; Id = id ?? Name.Replace(" ", "-"); AdminOnly = adminOnly; } } [Serializable] public class CommandException : Exception { public CommandException() { } public CommandException(string message) : base(message) { } public CommandException(string message, Exception innerException) : base(message, innerException) { } protected CommandException(SerializationInfo info, StreamingContext context) : base(info, context) { } } [AttributeUsage(AttributeTargets.Class)] public sealed class CommandGroupAttribute : Attribute { public string Name { get; } public string ShortHand { get; } public CommandGroupAttribute(string name, string shortHand = null) { Name = name; ShortHand = shortHand; } } public abstract class CommandMiddleware { public virtual bool CanExecute(ICommandContext ctx, CommandAttribute attribute, MethodInfo method) { return true; } public virtual void BeforeExecute(ICommandContext ctx, CommandAttribute attribute, MethodInfo method) { } public virtual void AfterExecute(ICommandContext ctx, CommandAttribute attribute, MethodInfo method) { } } public enum CommandResult { Unmatched, UsageError, CommandError, InternalError, Denied, Success } public static class Format { public enum FormatMode { GameChat, None } public static FormatMode Mode { get; set; } public static string B(string input) { return input.Bold(); } public static string Bold(this string input) { return (Mode == FormatMode.GameChat) ? ("<b>" + input + "</b>") : input; } public static string I(string input) { return input.Italic(); } public static string Italic(this string input) { return (Mode == FormatMode.GameChat) ? ("<i>" + input + "</i>") : input; } public static string Underline(this string input) { return (Mode == FormatMode.GameChat) ? ("<u>" + input + "</u>") : input; } public static string Color(this string input, string color) { return (Mode == FormatMode.GameChat) ? $"<color={color}>{input}</color>" : input; } public static string Size(this string input, int size) { return (Mode == FormatMode.GameChat) ? $"<size={size}>{input}</size>" : input; } public static string Small(this string input) { return input.Size(10); } public static string Normal(this string input) { return input.Size(16); } public static string Medium(this string input) { return input.Size(20); } public static string Large(this string input) { return input.Size(24); } } public interface ICommandContext { IServiceProvider Services { get; } string Name { get; } bool IsAdmin { get; } CommandException Error(string LogMessage); void Reply(string v); } public interface IConverterUsage { string Usage { get; } } [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] public sealed class RemainderAttribute : Attribute { } [BepInPlugin("gg.deca.VampireCommandFramework", "VampireCommandFramework", "0.11.0")] internal class Plugin : BasePlugin { private Harmony _harmony; public override void Load() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown Log.Instance = ((BasePlugin)this).Log; if (!VWorld.IsServer) { ((BasePlugin)this).Log.LogMessage((object)"Note: Vampire Command Framework is loading on the client but only adds functionality on the server at this time, seeing this message is not a problem or bug."); return; } _harmony = new Harmony("gg.deca.VampireCommandFramework"); _harmony.PatchAll(); ChatDrainPatch.Install(); CommandRegistry.RegisterCommandType(typeof(HelpCommands)); CommandRegistry.RegisterCommandType(typeof(BepInExConfigCommands)); CommandRegistry.RegisterCommandType(typeof(RepeatCommands)); CommandRegistry.RegisterCommandType(typeof(VersionCommands)); ((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.TryGetValue("gg.deca.VampireCommandFramework", out var value); ManualLogSource log = ((BasePlugin)this).Log; bool flag = default(bool); BepInExMessageLogInterpolatedStringHandler val = new BepInExMessageLogInterpolatedStringHandler(12, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("VCF Loaded: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Version>((value != null) ? value.Metadata.Version : null); } log.LogMessage(val); } public override bool Unload() { _harmony.UnpatchSelf(); return true; } } public static class CommandRegistry { internal const string DEFAULT_PREFIX = "."; internal static CommandCache _cache = new CommandCache(); internal static Dictionary<Type, (object instance, MethodInfo tryParse, Type contextType)> _converters = new Dictionary<Type, (object, MethodInfo, Type)>(); private static List<CommandMiddleware> DEFAULT_MIDDLEWARES = new List<CommandMiddleware> { new BasicAdminCheck() }; private static Dictionary<string, (string input, List<(CommandMetadata Command, object[] Args, string Error)> commands)> _pendingCommands = new Dictionary<string, (string, List<(CommandMetadata, object[], string)>)>(); public static List<CommandMiddleware> Middlewares { get; } = new List<CommandMiddleware> { new BasicAdminCheck() }; internal static Dictionary<string, Dictionary<CommandMetadata, List<string>>> AssemblyCommandMap { get; } = new Dictionary<string, Dictionary<CommandMetadata, List<string>>>(); internal static void Reset() { Middlewares.Clear(); Middlewares.AddRange(DEFAULT_MIDDLEWARES); AssemblyCommandMap.Clear(); _converters.Clear(); _cache = new CommandCache(); CommandHistory.Reset(); _pendingCommands.Clear(); } internal static ParsedCommandInput ParseInput(string input) { string text = input.Substring(".".Length); int num = text.IndexOf(' '); if (num > 0) { string potentialAssemblyName = text.Substring(0, num); if (AssemblyCommandMap.Keys.Any((string an) => an.Equals(potentialAssemblyName, StringComparison.OrdinalIgnoreCase))) { string text2 = text.Substring(num + 1); string text3 = "." + text2; CacheResult commandFromAssembly = _cache.GetCommandFromAssembly(text3, potentialAssemblyName); if (commandFromAssembly != null && commandFromAssembly.IsMatched) { return new ParsedCommandInput(potentialAssemblyName, text3, text2); } } } return new ParsedCommandInput(null, input, text); } internal static CacheResult GetCommandFromCache(string input, string assemblyName = null) { if (assemblyName != null) { return _cache.GetCommandFromAssembly(input, assemblyName); } return _cache.GetCommand(input); } internal static bool CanCommandExecute(ICommandContext ctx, CommandMetadata command) { foreach (CommandMiddleware middleware in Middlewares) { try { if (!middleware.CanExecute(ctx, command.Attribute, command.Method)) { return false; } } catch (Exception value) { Log.Error($"Error executing {middleware.GetType().Name.Color(Color.Gold)} {value}"); return false; } } return true; } internal static bool IsRemainderParameter(ParameterInfo p) { return p.ParameterType == typeof(string) && p.IsDefined(typeof(RemainderAttribute), inherit: false); } internal static bool HasRemainderParameter(CommandMetadata command) { if (command.Parameters.Length == 0) { return false; } return IsRemainderParameter(command.Parameters[command.Parameters.Length - 1]); } internal static IEnumerable<string> FindCloseMatches(ICommandContext ctx, string input) { if (string.IsNullOrWhiteSpace(input)) { return Enumerable.Empty<string>(); } string normalizedInput = ParseInput(input).AfterPrefixAndAssembly.ToLowerInvariant(); int maxDistance = Math.Max(3, (int)Math.Ceiling((double)normalizedInput.Length * 0.5)); List<CommandMetadata> source = AssemblyCommandMap.SelectMany((KeyValuePair<string, Dictionary<CommandMetadata, List<string>>> a) => a.Value.Keys).ToList(); return from x in (from x in source.Where((CommandMetadata c) => CanCommandExecute(ctx, c)).SelectMany(delegate(CommandMetadata cmd) { string[] source2 = ((cmd.GroupAttribute != null) ? ((cmd.GroupAttribute.ShortHand != null) ? new string[2] { cmd.GroupAttribute.Name + " ", cmd.GroupAttribute.ShortHand + " " } : new string[1] { cmd.GroupAttribute.Name + " " }) : new string[1] { "" }); string[] commandNames = ((cmd.Attribute.ShortHand != null) ? new string[2] { cmd.Attribute.Name, cmd.Attribute.ShortHand } : new string[1] { cmd.Attribute.Name }); return source2.SelectMany((string @group) => commandNames.Select((string name) => new { FullName = (@group + name).ToLowerInvariant(), Command = cmd })); }).Select(cmdInfo => { float distance = DamerauLevenshteinDistance(normalizedInput, cmdInfo.FullName); int maxDistance2 = Math.Max(maxDistance, (int)Math.Ceiling((double)normalizedInput.Length * 0.5)); return new { Command = cmdInfo.FullName, Distance = distance, MaxDistance = maxDistance2 }; }) where x.Distance <= (float)x.MaxDistance orderby x.Distance select x).DistinctBy(x => x.Command).Take(3) select "." + x.Command; } private static float DamerauLevenshteinDistance(string s, string t) { if (string.IsNullOrEmpty(s)) { return (!string.IsNullOrEmpty(t)) ? t.Length : 0; } if (string.IsNullOrEmpty(t)) { return s.Length; } float[,] array = new float[s.Length + 1, t.Length + 1]; for (int i = 0; i <= s.Length; i++) { array[i, 0] = i; } for (int j = 0; j <= t.Length; j++) { array[0, j] = j; } for (int k = 1; k <= s.Length; k++) { for (int l = 1; l <= t.Length; l++) { int num = ((s[k - 1] != t[l - 1]) ? 1 : 0); array[k, l] = Math.Min(Math.Min(array[k - 1, l] + 1f, array[k, l - 1] + 1f), array[k - 1, l - 1] + 1.5f * (float)num); if (k > 1 && l > 1 && s[k - 1] == t[l - 2] && s[k - 2] == t[l - 1]) { array[k, l] = Math.Min(array[k, l], array[k - 2, l - 2] + (float)num); } } } return array[s.Length, t.Length]; } private static void HandleBeforeExecute(ICommandContext ctx, CommandMetadata command) { Middlewares.ForEach(delegate(CommandMiddleware m) { m.BeforeExecute(ctx, command.Attribute, command.Method); }); } private static void HandleAfterExecute(ICommandContext ctx, CommandMetadata command) { Middlewares.ForEach(delegate(CommandMiddleware m) { m.AfterExecute(ctx, command.Attribute, command.Method); }); } public static CommandResult Handle(ICommandContext ctx, string input) { CommandHistory.EnsureHistoryLoaded(ctx); if (input.StartsWith(".") && input.Length > 1) { string s = input.Substring(1); if (int.TryParse(s, out var result) && result > 0) { return HandleCommandSelection(ctx, result); } } if (!input.StartsWith(".")) { return CommandResult.Unmatched; } if (input.Trim().StartsWith(".!")) { return CommandHistory.HandleHistoryCommand(ctx, input.Trim(), Handle, ExecuteCommandWithArgs); } ParsedCommandInput parsedCommandInput = ParseInput(input); CacheResult cacheResult = null; if (parsedCommandInput.HasAssembly) { cacheResult = _cache.GetCommandFromAssembly(parsedCommandInput.CommandInput, parsedCommandInput.AssemblyName); } if (cacheResult == null || !cacheResult.IsMatched) { cacheResult = _cache.GetCommand(input); } IEnumerable<(CommandMetadata, string[])> commands = cacheResult.Commands; if (!cacheResult.IsMatched) { if (!cacheResult.HasPartial) { return CommandResult.Unmatched; } foreach (CommandMetadata partialMatch in cacheResult.PartialMatches) { ctx.SysReply(HelpCommands.GetShortHelp(partialMatch)); } return CommandResult.UsageError; } if (commands.Count() != 1) { List<(CommandMetadata, object[], string)> list = new List<(CommandMetadata, object[], string)>(); List<(CommandMetadata, string)> list2 = new List<(CommandMetadata, string)>(); List<CommandMetadata> list3 = new List<CommandMetadata>(); foreach (var (commandMetadata, args) in commands) { if (!CanCommandExecute(ctx, commandMetadata)) { list3.Add(commandMetadata); continue; } var (flag, item, item2) = TryConvertParameters(ctx, commandMetadata, args, parsedCommandInput.CommandInput); if (flag) { list.Add((commandMetadata, item, null)); } else { list2.Add((commandMetadata, item2)); } } StringBuilder stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler; if (list.Count == 0) { if (list2.Count == 0 && list3.Count > 0) { ctx.SysReply("[denied]".Color(Color.Red) + " " + list3[0].Attribute.Name.ToString().Color(Color.Gold)); return CommandResult.Denied; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(62, 1, stringBuilder2); handler.AppendFormatted("[error]".Color(Color.Red)); handler.AppendLiteral(" Failed to execute command due to parameter conversion errors:"); stringBuilder3.AppendLine(ref handler); foreach (var item6 in list2) { CommandMetadata item3 = item6.Item1; string item4 = item6.Item2; string assemblyName = item3.AssemblyName; stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(9, 3, stringBuilder2); handler.AppendLiteral(" - "); handler.AppendFormatted(item3.Attribute.Name); handler.AppendLiteral(" ("); handler.AppendFormatted(assemblyName); handler.AppendLiteral("): "); handler.AppendFormatted(item4); stringBuilder4.AppendLine(ref handler); } ctx.SysPaginatedReply(stringBuilder); return CommandResult.UsageError; } if (list.Count == 1) { var (command, array, _) = list[0]; CommandHistory.AddToHistory(ctx, input, command, array); return ExecuteCommandWithArgs(ctx, command, array); } string name = ctx.Name; _pendingCommands[name] = (input, list); StringBuilder stringBuilder5 = new StringBuilder(); stringBuilder2 = stringBuilder5; StringBuilder stringBuilder6 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(58, 1, stringBuilder2); handler.AppendLiteral("Multiple commands match this input. Select one by typing "); handler.AppendFormatted(Format.B(".<#>").Color(Color.Command)); handler.AppendLiteral(":"); stringBuilder6.AppendLine(ref handler); for (int i = 0; i < list.Count; i++) { CommandMetadata item5 = list[i].Item1; string assemblyName2 = item5.AssemblyName; string description = item5.Attribute.Description; stringBuilder2 = stringBuilder5; StringBuilder stringBuilder7 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(8, 4, stringBuilder2); handler.AppendLiteral(" "); handler.AppendFormatted(("." + (i + 1)).Color(Color.Command)); handler.AppendLiteral(" - "); handler.AppendFormatted(assemblyName2.Bold().Color(Color.Primary)); handler.AppendLiteral(" - "); handler.AppendFormatted(Format.B(item5.Attribute.Name)); handler.AppendLiteral(" "); handler.AppendFormatted(item5.Attribute.Description); stringBuilder7.AppendLine(ref handler); stringBuilder5.AppendLine(" " + HelpCommands.GetShortHelp(item5)); } ctx.SysPaginatedReply(stringBuilder5); return CommandResult.Success; } var (command2, args2) = commands.First(); return ExecuteCommand(ctx, command2, args2, parsedCommandInput.CommandInput); } private static CommandResult HandleCommandSelection(ICommandContext ctx, int selectedIndex) { string name = ctx.Name; if (!_pendingCommands.TryGetValue(name, out (string, List<(CommandMetadata, object[], string)>) value) || value.Item2.Count == 0) { ctx.SysReply("[error]".Color(Color.Red) + " No command selection is pending."); return CommandResult.CommandError; } if (selectedIndex < 1 || selectedIndex > value.Item2.Count) { ctx.SysReply($"{"[error]".Color(Color.Red)} Invalid selection. Please select a number between {"1".Color(Color.Gold)} and {value.Item2.Count.ToString().Color(Color.Gold)}."); return CommandResult.UsageError; } var (command, array, _) = value.Item2[selectedIndex - 1]; CommandHistory.AddToHistory(ctx, value.Item1, command, array); CommandResult result = ExecuteCommandWithArgs(ctx, command, array); _pendingCommands.Remove(name); return result; } internal static (bool Success, object[] Args, string Error) TryConvertParameters(ICommandContext ctx, CommandMetadata command, string[] args, string originalInput = null) { int num = ((args != null) ? args.Length : 0); int num2 = command.Parameters.Length; object[] array = new object[num2 + 1]; array[0] = ctx; bool flag = HasRemainderParameter(command); if (num2 == 0 && num == 0) { return (true, array, null); } if (flag) { return TryConvertParametersWithRemainder(ctx, command, args, originalInput, array); } if (num > num2) { return (false, null, "Too many parameters: expected " + num2.ToString().Color(Color.Gold) + ", got " + num.ToString().Color(Color.Gold)); } if (num < num2) { IEnumerable<ParameterInfo> source = command.Parameters.Skip(num); if (!source.All((ParameterInfo p) => p.HasDefaultValue)) { return (false, null, "Missing required parameters: expected " + num2.ToString().Color(Color.Gold) + ", got " + num.ToString().Color(Color.Gold)); } for (int i = num; i < num2; i++) { array[i + 1] = command.Parameters[i].DefaultValue; } } for (int j = 0; j < Math.Min(num, num2); j++) { ParameterInfo param = command.Parameters[j]; string arg = args[j]; var (flag2, obj, item) = TryConvertSingleParameter(ctx, param, arg, j); if (!flag2) { return (false, null, item); } array[j + 1] = obj; } return (true, array, null); } private static (bool Success, object[] Args, string Error) TryConvertParametersWithRemainder(ICommandContext ctx, CommandMetadata command, string[] args, string originalInput, object[] commandArgs) { int num = ((args != null) ? args.Length : 0); int num2 = command.Parameters.Length; int num3 = num2 - 1; int num4 = 0; for (int i = 0; i < num3; i++) { if (!command.Parameters[i].HasDefaultValue) { num4++; } } if (num < num4) { return (false, null, "Missing required parameters: expected at least " + num4.ToString().Color(Color.Gold) + ", got " + num.ToString().Color(Color.Gold)); } int num5 = Math.Min(num, num3); for (int num6 = num5; num6 >= num4; num6--) { var (flag, text) = TryConvertWithSplitPoint(ctx, command, args, originalInput, commandArgs, num6); if (flag) { return (true, commandArgs, null); } if ((text == null || !text.Contains("Parameter") || num6 <= num4) && num6 == num4) { return (false, null, text); } } return (false, null, "Failed to parse parameters"); } private static (bool Success, string Error) TryConvertWithSplitPoint(ICommandContext ctx, CommandMetadata command, string[] args, string originalInput, object[] commandArgs, int splitPoint) { int num = command.Parameters.Length; int num2 = num - 1; for (int i = 0; i < splitPoint; i++) { ParameterInfo param = command.Parameters[i]; string arg = args[i]; var (flag, obj, item) = TryConvertSingleParameter(ctx, param, arg, i); if (!flag) { return (false, item); } commandArgs[i + 1] = obj; } for (int j = splitPoint; j < num2; j++) { ParameterInfo parameterInfo = command.Parameters[j]; if (parameterInfo.HasDefaultValue) { commandArgs[j + 1] = parameterInfo.DefaultValue; continue; } return (false, $"Parameter {j + 1} ({parameterInfo.Name.ToString().Color(Color.Gold)}) is required but no value provided"); } if (!string.IsNullOrEmpty(originalInput)) { commandArgs[num2 + 1] = ExtractRemainderFromOriginalInput(originalInput, command, num2, splitPoint); } else { string[] value = args.Skip(splitPoint).ToArray(); commandArgs[num2 + 1] = string.Join(" ", value); } return (true, null); } private static (bool Success, object ConvertedValue, string Error) TryConvertSingleParameter(ICommandContext ctx, ParameterInfo param, string arg, int paramIndex) { try { if (!_converters.TryGetValue(param.ParameterType, out (object, MethodInfo, Type) value)) { TypeConverter converter = TypeDescriptor.GetConverter(param.ParameterType); try { object item = converter.ConvertFromInvariantString(arg); if (param.ParameterType.IsEnum) { bool flag = false; if (int.TryParse(arg, out var result) && !Enum.IsDefined(param.ParameterType, result)) { return (false, null, $"Parameter {paramIndex + 1} ({param.Name.ToString().Color(Color.Gold)}): Invalid enum value '{arg.ToString().Color(Color.Gold)}' for {param.ParameterType.Name.ToString().Color(Color.Gold)}"); } } return (true, item, null); } catch (Exception ex) { return (false, null, $"Parameter {paramIndex + 1} ({param.Name.ToString().Color(Color.Gold)}): {ex.Message}"); } } var (obj, methodInfo, type) = value; if (!type.IsAssignableFrom(ctx.GetType())) { return (false, null, "INTERNAL_ERROR:Converter type " + type.Name.ToString().Color(Color.Gold) + " is not assignable from " + ctx.GetType().Name.ToString().Color(Color.Gold)); } object[] parameters = new object[2] { ctx, arg }; try { object item2 = methodInfo.Invoke(obj, parameters); return (true, item2, null); } catch (TargetInvocationException ex2) { if (ex2.InnerException is CommandException ex3) { return (false, null, $"Parameter {paramIndex + 1} ({param.Name.ToString().Color(Color.Gold)}): {ex3.Message}"); } return (false, null, $"Parameter {paramIndex + 1} ({param.Name.ToString().Color(Color.Gold)}): Unexpected error converting parameter"); } catch (Exception) { return (false, null, $"Parameter {paramIndex + 1} ({param.Name.ToString().Color(Color.Gold)}): Unexpected error converting parameter"); } } catch (Exception ex5) { return (false, null, $"Parameter {paramIndex + 1} ({param.Name.ToString().Color(Color.Gold)}): Unexpected error: {ex5.Message}"); } } private static string ExtractRemainderFromOriginalInput(string originalInput, CommandMetadata command, int remainderParameterIndex, int splitPoint = -1) { string text = originalInput.Substring(".".Length); int num = 0; if (command.GroupAttribute != null) { string name = command.GroupAttribute.Name; string shortHand = command.GroupAttribute.ShortHand; num = ((shortHand == null || !text.StartsWith(shortHand + " ", StringComparison.OrdinalIgnoreCase)) ? (num + name.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length) : (num + shortHand.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length)); } string name2 = command.Attribute.Name; string shortHand2 = command.Attribute.ShortHand; if (shortHand2 != null) { int i = 0; for (int j = 0; j < num; j++) { for (; i < text.Length && text[i] != ' '; i++) { } for (; i < text.Length && text[i] == ' '; i++) { } } string text2 = text.Substring(i); num = ((!text2.StartsWith(shortHand2 + " ", StringComparison.OrdinalIgnoreCase) && !text2.Equals(shortHand2, StringComparison.OrdinalIgnoreCase)) ? (num + name2.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length) : (num + shortHand2.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length)); } else { num += name2.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length; } int k = 0; for (int l = 0; l < num; l++) { for (; k < text.Length && text[k] != ' '; k++) { } for (; k < text.Length && text[k] == ' '; k++) { } } if (k >= text.Length) { return ""; } string text3 = text.Substring(k); if (remainderParameterIndex == 0) { return text3; } int num2 = ((splitPoint >= 0) ? splitPoint : remainderParameterIndex); int num3 = 0; int m = 0; bool flag = false; while (m < text3.Length && num3 < num2) { char c = text3[m]; if (c == '\\' && m + 1 < text3.Length && text3[m + 1] == '"') { m += 2; continue; } switch (c) { case '"': flag = !flag; break; case ' ': if (!flag) { for (; m < text3.Length && text3[m] == ' '; m++) { } num3++; continue; } break; } m++; } if (m < text3.Length) { return text3.Substring(m); } return ""; } private static CommandResult ExecuteCommand(ICommandContext ctx, CommandMetadata command, string[] args, string input) { if (!command.ContextType.IsAssignableFrom(ctx?.GetType())) { Log.Warning($"Matched [{command.Attribute.Name.ToString().Color(Color.Gold)}] but can not assign {command.ContextType.Name.ToString().Color(Color.Gold)} from {ctx?.GetType().Name.ToString().Color(Color.Gold)}"); return CommandResult.InternalError; } var (flag, array, text) = TryConvertParameters(ctx, command, args, input); if (!flag) { if (text != null && text.StartsWith("INTERNAL_ERROR:")) { string s = text.Substring("INTERNAL_ERROR:".Length); Log.Warning(s); ctx.InternalError(); return CommandResult.InternalError; } ctx.SysReply("[error]".Color(Color.Red) + " " + text); return CommandResult.UsageError; } CommandHistory.AddToHistory(ctx, input, command, array); return ExecuteCommandWithArgs(ctx, command, array); } private static CommandResult ExecuteCommandWithArgs(ICommandContext ctx, CommandMetadata command, object[] commandArgs) { if (!command.ContextType.IsAssignableFrom(ctx?.GetType())) { Log.Warning($"Matched [{command.Attribute.Name.ToString().Color(Color.Gold)}] but can not assign {command.ContextType.Name.ToString().Color(Color.Gold)} from {ctx?.GetType().Name.ToString().Color(Color.Gold)}"); return CommandResult.InternalError; } if (command.Constructor != null && !command.ConstructorType.IsAssignableFrom(ctx?.GetType())) { Log.Warning($"Matched [{command.Attribute.Name.ToString().Color(Color.Gold)}] but can not assign {command.ConstructorType.Name.ToString().Color(Color.Gold)} from {ctx?.GetType().Name.ToString().Color(Color.Gold)}"); ctx.InternalError(); return CommandResult.InternalError; } object obj = null; if (!command.Method.IsStatic && (!command.Method.DeclaringType.IsAbstract || !command.Method.DeclaringType.IsSealed)) { try { object? obj2; if (!(command.Constructor == null)) { ConstructorInfo constructor = command.Constructor; object[] parameters = new ICommandContext[1] { ctx }; obj2 = constructor.Invoke(parameters); } else { obj2 = Activator.CreateInstance(command.Method.DeclaringType); } obj = obj2; } catch (TargetInvocationException ex) { if (ex.InnerException is CommandException ex2) { ctx.SysReply(ex2.Message); } else { ctx.InternalError(); } return CommandResult.InternalError; } } if (!CanCommandExecute(ctx, command)) { ctx.SysReply("[denied]".Color(Color.Red) + " " + command.Attribute.Name.ToString().Color(Color.Gold)); return CommandResult.Denied; } HandleBeforeExecute(ctx, command); CommandException ex4 = default(CommandException); try { command.Method.Invoke(obj, commandArgs); } catch (TargetInvocationException ex3) when (((Func<bool>)delegate { // Could not convert BlockContainer to single expression ex4 = ex3.InnerException as CommandException; return ex4 != null; }).Invoke()) { ctx.SysReply("[error]".Color(Color.Red) + " " + ex4.Message); return CommandResult.CommandError; } catch (Exception value) { Log.Warning($"Hit unexpected exception executing command {command.Attribute.Id.ToString().Color(Color.Gold)}\n: {value}"); ctx.InternalError(); return CommandResult.InternalError; } HandleAfterExecute(ctx, command); return CommandResult.Success; } public static void UnregisterConverter(Type converter) { if (IsGenericConverterContext(converter) || IsSpecificConverterContext(converter)) { Type[] genericTypeArguments = converter.BaseType.GenericTypeArguments; Type type = genericTypeArguments.FirstOrDefault(); if (type == null) { Log.Warning("Could not resolve converter type " + converter.Name.ToString().Color(Color.Gold)); } else if (_converters.ContainsKey(type)) { _converters.Remove(type); Log.Info("Unregistered converter " + converter.Name); } else { Log.Warning("Call to UnregisterConverter for a converter that was not registered. Type: " + converter.Name.ToString().Color(Color.Gold)); } } } internal static bool IsGenericConverterContext(Type rootType) { return rootType?.BaseType?.Name == typeof(CommandArgumentConverter<>).Name; } internal static bool IsSpecificConverterContext(Type rootType) { return rootType?.BaseType?.Name == typeof(CommandArgumentConverter<, >).Name; } public static void RegisterConverter(Type converter) { bool flag = IsGenericConverterContext(converter); bool flag2 = IsSpecificConverterContext(converter); if (!flag && !flag2) { return; } Log.Debug($"Trying to process {converter} as specifc={flag2} generic={flag}"); object item = Activator.CreateInstance(converter); MethodInfo method = converter.GetMethod("Parse", BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod); if (method == null) { Log.Error("Can't find TryParse that matches"); return; } Type[] genericTypeArguments = converter.BaseType.GenericTypeArguments; Type type = genericTypeArguments.FirstOrDefault(); if (type == null) { Log.Error("Can't determine generic base type to convert from. "); return; } Type item2 = typeof(ICommandContext); if (flag2) { if (genericTypeArguments.Length != 2 || !typeof(ICommandContext).IsAssignableFrom(genericTypeArguments[1])) { Log.Error("Can't determine generic base type to convert from."); return; } item2 = genericTypeArguments[1]; } _converters.Add(type, (item, method, item2)); } public static void RegisterAll() { RegisterAll(Assembly.GetCallingAssembly()); } public static void RegisterAll(Assembly assembly) { Type[] types = assembly.GetTypes(); Type[] array = types; foreach (Type converter in array) { RegisterConverter(converter); } Type[] array2 = types; foreach (Type type in array2) { RegisterCommandType(type); } } public static void RegisterCommandType(Type type) { CommandGroupAttribute customAttribute = type.GetCustomAttribute<CommandGroupAttribute>(); Assembly assembly = type.Assembly; if (customAttribute != null) { } MethodInfo[] methods = type.GetMethods(); ConstructorInfo customConstructor = (from c in type.GetConstructors() where c.GetParameters().Length == 1 && typeof(ICommandContext).IsAssignableFrom(c.GetParameters().SingleOrDefault()?.ParameterType) select c).FirstOrDefault(); MethodInfo[] array = methods; foreach (MethodInfo method in array) { RegisterMethod(assembly, customAttribute, customConstructor, method); } } private static void RegisterMethod(Assembly assembly, CommandGroupAttribute groupAttr, ConstructorInfo customConstructor, MethodInfo method) { CommandAttribute customAttribute = method.GetCustomAttribute<CommandAttribute>(); if (customAttribute == null) { return; } ParameterInfo[] parameters = method.GetParameters(); ParameterInfo parameterInfo = parameters.FirstOrDefault(); if (parameterInfo == null || parameterInfo.ParameterType is ICommandContext) { Log.Error("Method " + method.Name.ToString().Color(Color.Gold) + " has no CommandContext as first argument"); return; } ParameterInfo[] array = parameters.Skip(1).ToArray(); for (int i = 0; i < array.Length - 1; i++) { if (array[i].IsDefined(typeof(RemainderAttribute), inherit: false)) { Log.Error($"Method {method.Name.ToString().Color(Color.Gold)} has [Remainder] on parameter {array[i].Name.ToString().Color(Color.Gold)} which is not the last parameter. [Remainder] must be on the last parameter. Command will be ignored."); return; } } if (!array.All(delegate(ParameterInfo param) { if (IsRemainderParameter(param)) { Log.Debug($"Method {method.Name.ToString().Color(Color.Gold)} has a remainder parameter ({param.Name})"); return true; } if (_converters.ContainsKey(param.ParameterType)) { Log.Debug($"Method {method.Name.ToString().Color(Color.Gold)} has a parameter of type {param.ParameterType.Name.ToString().Color(Color.Gold)} which is registered as a converter"); return true; } TypeConverter converter = TypeDescriptor.GetConverter(param.ParameterType); if (converter == null || !converter.CanConvertFrom(typeof(string))) { Log.Warning($"Parameter {param.Name.ToString().Color(Color.Gold)} could not be converted, so {method.Name.ToString().Color(Color.Gold)} will be ignored."); return false; } return true; })) { return; } Type constructorType = customConstructor?.GetParameters().Single().ParameterType; CommandMetadata commandMetadata = new CommandMetadata(customAttribute, assembly.GetName().Name, method, customConstructor, array, parameterInfo.ParameterType, constructorType, groupAttr); string[] array2 = ((groupAttr != null) ? ((groupAttr.ShortHand != null) ? new string[2] { groupAttr.Name + " ", groupAttr.ShortHand + " " } : new string[1] { groupAttr.Name + " " }) : new string[1] { "" }); string[] array3 = ((customAttribute.ShortHand != null) ? new string[2] { customAttribute.Name, customAttribute.ShortHand } : new string[1] { customAttribute.Name }); string text = "."; List<string> list = new List<string>(); string[] array4 = array2; foreach (string text2 in array4) { string[] array5 = array3; foreach (string text3 in array5) { string text4 = text + text2 + text3; _cache.AddCommand(text4, array, commandMetadata); list.Add(text4); } } string name = assembly.GetName().Name; AssemblyCommandMap.TryGetValue(name, out var value); if (value == null) { value = new Dictionary<CommandMetadata, List<string>>(); } value[commandMetadata] = list; AssemblyCommandMap[name] = value; } public static void UnregisterAssembly() { UnregisterAssembly(Assembly.GetCallingAssembly()); } public static void UnregisterAssembly(Assembly assembly) { string name = assembly.GetName().Name; foreach (TypeInfo definedType in assembly.DefinedTypes) { _cache.RemoveCommandsFromType(definedType); UnregisterConverter(definedType); } AssemblyCommandMap.Remove(name); } } public static class PluginInfo { public const string PLUGIN_GUID = "gg.deca.VampireCommandFramework"; public const string PLUGIN_NAME = "VampireCommandFramework"; public const string PLUGIN_VERSION = "0.11.0"; } } namespace VampireCommandFramework.Registry { internal record CacheResult { internal IEnumerable<(CommandMetadata Command, string[] Args)> Commands { get; } internal IEnumerable<CommandMetadata> PartialMatches { get; } internal bool IsMatched => Commands != null && Commands.Any(); internal bool HasPartial => PartialMatches?.Any() ?? false; public CacheResult(IEnumerable<(CommandMetadata Command, string[] Args)> commands, IEnumerable<CommandMetadata> partialMatches) { Commands = commands; PartialMatches = partialMatches; } public CacheResult((CommandMetadata Command, string[] Args)? command, IEnumerable<CommandMetadata> partialMatches) { Commands = ((!command.HasValue) ? null : new(CommandMetadata, string[])[1] { command.Value }); PartialMatches = partialMatches; } [CompilerGenerated] protected virtual bool PrintMembers(StringBuilder builder) { return false; } } internal class CommandCache { private static Dictionary<Type, HashSet<(string, int)>> _commandAssemblyMap = new Dictionary<Type, HashSet<(string, int)>>(); internal Dictionary<string, Dictionary<int, List<CommandMetadata>>> _newCache = new Dictionary<string, Dictionary<int, List<CommandMetadata>>>(); internal Dictionary<string, List<CommandMetadata>> _remainderCache = new Dictionary<string, List<CommandMetadata>>(); internal void AddCommand(string key, ParameterInfo[] parameters, CommandMetadata command) { key = key.ToLowerInvariant(); int num = parameters.Length; int num2 = parameters.Where((ParameterInfo p) => p.HasDefaultValue).Count(); bool flag = parameters.Length != 0 && CommandRegistry.IsRemainderParameter(parameters[^1]); if (!_newCache.ContainsKey(key)) { _newCache.Add(key, new Dictionary<int, List<CommandMetadata>>()); } if (flag) { if (!_remainderCache.ContainsKey(key)) { _remainderCache[key] = new List<CommandMetadata>(); } _remainderCache[key].Add(command); Type declaringType = command.Method.DeclaringType; HashSet<(string, int)> value; HashSet<(string, int)> hashSet = (_commandAssemblyMap.TryGetValue(declaringType, out value) ? value : new HashSet<(string, int)>()); hashSet.Add((key, -1)); _commandAssemblyMap[declaringType] = hashSet; return; } for (int i = num - num2; i <= num; i++) { _newCache[key] = _newCache.GetValueOrDefault(key, new Dictionary<int, List<CommandMetadata>>()) ?? new Dictionary<int, List<CommandMetadata>>(); if (!_newCache[key].ContainsKey(i)) { _newCache[key][i] = new List<CommandMetadata>(); } _newCache[key][i].Add(command); Type declaringType2 = command.Method.DeclaringType; HashSet<(string, int)> value2; HashSet<(string, int)> hashSet2 = (_commandAssemblyMap.TryGetValue(declaringType2, out value2) ? value2 : new HashSet<(string, int)>()); hashSet2.Add((key, i)); _commandAssemblyMap[declaringType2] = hashSet2; } } internal CacheResult GetCommand(string rawInput) { string text = rawInput.ToLowerInvariant(); List<CommandMetadata> list = new List<CommandMetadata>(); List<(CommandMetadata, string[])> list2 = new List<(CommandMetadata, string[])>(); foreach (var (text3, dictionary2) in _newCache) { if (!text.StartsWith(text3)) { continue; } bool flag = text.Length == text3.Length; bool flag2 = text.Length > text3.Length && text[text3.Length] == ' '; if (!(flag || flag2)) { continue; } string text4 = (flag ? "" : rawInput.Substring(text3.Length).Trim()); string[] array = ((text4.Length > 0) ? Utility.GetParts(text4).ToArray() : Array.Empty<string>()); if (dictionary2.TryGetValue(array.Length, out var value)) { foreach (CommandMetadata item in value) { list2.Add((item, array)); } } if (_remainderCache.TryGetValue(text3, out var value2)) { foreach (CommandMetadata item2 in value2) { ParameterInfo[] parameters = item2.Method.GetParameters(); int num = parameters.Count((ParameterInfo p) => !p.HasDefaultValue) - 2; if (array.Length >= num) { list2.Add((item2, array)); } } } if (list2.Count == 0) { list.AddRange(dictionary2.Values.SelectMany((List<CommandMetadata> x) => x)); if (_remainderCache.TryGetValue(text3, out var value3)) { list.AddRange(value3); } } } if (list2.Count > 0) { return new CacheResult(list2, null); } return new CacheResult(((CommandMetadata Command, string[] Args)?)null, list.Distinct()); } internal CacheResult GetCommandFromAssembly(string rawInput, string assemblyName) { string text = rawInput.ToLowerInvariant(); List<CommandMetadata> list = new List<CommandMetadata>(); List<(CommandMetadata, string[])> list2 = new List<(CommandMetadata, string[])>(); foreach (var (text3, dictionary2) in _newCache) { if (!text.StartsWith(text3)) { continue; } bool flag = text.Length == text3.Length; bool flag2 = text.Length > text3.Length && text[text3.Length] == ' '; if (!(flag || flag2)) { continue; } string text4 = (flag ? "" : rawInput.Substring(text3.Length).Trim()); string[] array = ((text4.Length > 0) ? Utility.GetParts(text4).ToArray() : Array.Empty<string>()); if (dictionary2.TryGetValue(array.Length, out var value)) { foreach (CommandMetadata item in value.Where((CommandMetadata cmd) => cmd.AssemblyName.Equals(assemblyName, StringComparison.OrdinalIgnoreCase))) { list2.Add((item, array)); } } if (_remainderCache.TryGetValue(text3, out var value2)) { foreach (CommandMetadata item2 in value2.Where((CommandMetadata cmd) => cmd.AssemblyName.Equals(assemblyName, StringComparison.OrdinalIgnoreCase))) { ParameterInfo[] parameters = item2.Method.GetParameters(); int num = parameters.Count((ParameterInfo p) => !p.HasDefaultValue) - 2; if (array.Length >= num) { list2.Add((item2, array)); } } } if (list2.Count != 0) { continue; } list.AddRange(from cmd in dictionary2.Values.SelectMany((List<CommandMetadata> x) => x) where cmd.AssemblyName.Equals(assemblyName, StringComparison.OrdinalIgnoreCase) select cmd); if (_remainderCache.TryGetValue(text3, out var value3)) { list.AddRange(value3.Where((CommandMetadata cmd) => cmd.AssemblyName.Equals(assemblyName, StringComparison.OrdinalIgnoreCase))); } } if (list2.Count > 0) { return new CacheResult(list2, null); } return new CacheResult(((CommandMetadata Command, string[] Args)?)null, list.Distinct()); } internal void RemoveCommandsFromType(Type t) { if (!_commandAssemblyMap.TryGetValue(t, out var value)) { return; } foreach (var (key, num) in value) { Dictionary<int, List<CommandMetadata>> value3; List<CommandMetadata> value4; if (num == -1) { if (_remainderCache.TryGetValue(key, out var value2)) { value2.RemoveAll((CommandMetadata cmd) => cmd.Method.DeclaringType == t); if (value2.Count == 0) { _remainderCache.Remove(key); } } } else if (_newCache.TryGetValue(key, out value3) && value3.TryGetValue(num, out value4)) { value4.RemoveAll((CommandMetadata cmd) => cmd.Method.DeclaringType == t); if (value4.Count == 0) { value3.Remove(num); } } } _commandAssemblyMap.Remove(t); } internal void Clear() { _newCache.Clear(); _remainderCache.Clear(); } internal void Reset() { throw new NotImplementedException(); } } public static class CommandHistory { private static Dictionary<string, List<(string input, CommandMetadata Command, object[] Args)>> _commandHistory = new Dictionary<string, List<(string, CommandMetadata, object[])>>(); private const int MAX_COMMAND_HISTORY = 10; private static HashSet<string> _loadedHistories = new HashSet<string>(); private static readonly Queue<(string filePath, string[] inputs)> _saveQueue = new Queue<(string, string[])>(); private static volatile bool _saveThreadRunning = false; private static string HistoryDirectory => Path.Combine(Path.Combine(Paths.ConfigPath, "VampireCommandFramework"), "CommandHistory"); internal static void Reset() { WaitOnSaves(); _commandHistory.Clear(); _loadedHistories.Clear(); } internal static void WaitOnSaves() { while (_saveThreadRunning) { Thread.Sleep(1); } } internal static bool IsHistoryLoaded(string contextName) { return _loadedHistories.Contains(contextName); } internal static void EnsureHistoryLoaded(ICommandContext ctx) { string name = ctx.Name; if (!_loadedHistories.Contains(name)) { LoadHistoryFromFile(ctx, name); } } internal static void AddToHistory(ICommandContext ctx, string input, CommandMetadata command, object[] args) { string name = ctx.Name; if (!_commandHistory.TryGetValue(name, out List<(string, CommandMetadata, object[])> value)) { value = new List<(string, CommandMetadata, object[])>(); _commandHistory[name] = value; } for (int i = 0; i < value.Count; i++) { (string, CommandMetadata, object[]) tuple = value[i]; if (!(tuple.Item2 == null) && tuple.Item2.Method == command.Method && tuple.Item2.Attribute.Name == command.Attribute.Name && ArgsEqual(tuple.Item3, args)) { value.RemoveAt(i); break; } } value.Insert(0, (input, command, args)); if (value.Count > 10) { value.RemoveAt(value.Count - 1); } SaveHistoryToFile(name, value); } internal static CommandResult HandleHistoryCommand(ICommandContext ctx, string input, Func<ICommandContext, string, CommandResult> handleCommand, Func<ICommandContext, CommandMetadata, object[], CommandResult> executeCommandWithArgs) { string name = ctx.Name; string text = input.Substring(2).Trim(); if (!_commandHistory.TryGetValue(name, out List<(string, CommandMetadata, object[])> value) || value.Count == 0) { ctx.SysReply("[error]".Color(Color.Red) + " No command history available."); return CommandResult.CommandError; } if (text == "list" || text == "l") { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Command history:"); for (int i = 0; i < value.Count; i++) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(2, 2, stringBuilder2); handler.AppendFormatted((i + 1).ToString().Color(Color.Gold)); handler.AppendLiteral(". "); handler.AppendFormatted(value[i].Item1.Color(Color.Command)); stringBuilder2.AppendLine(ref handler); } ctx.SysPaginatedReply(stringBuilder); return CommandResult.Success; } if (int.TryParse(text, out var result) && result > 0 && result <= value.Count) { (string, CommandMetadata, object[]) tuple = value[result - 1]; ctx.SysReply("Executing command " + result.ToString().Color(Color.Gold) + ": " + tuple.Item1.Color(Color.Command)); if (tuple.Item2 != null && tuple.Item3 != null) { object[] array = tuple.Item3.ToArray(); array[0] = ctx; return executeCommandWithArgs(ctx, tuple.Item2, array); } return handleCommand(ctx, tuple.Item1); } if (string.IsNullOrWhiteSpace(text)) { (string, CommandMetadata, object[]) tuple2 = value[0]; ctx.SysReply("Repeating most recent command: " + tuple2.Item1.Color(Color.Command)); if (tuple2.Item2 != null && tuple2.Item3 != null) { object[] array2 = tuple2.Item3.ToArray(); array2[0] = ctx; return executeCommandWithArgs(ctx, tuple2.Item2, array2); } return handleCommand(ctx, tuple2.Item1); } ctx.SysReply($"{"[error]".Color(Color.Red)} Invalid command history selection. Use {".! list".Color(Color.Command)} to see available commands or {".! #".Color(Color.Command)} to execute a specific command."); return CommandResult.UsageError; } private static bool ArgsEqual(object[] args1, object[] args2) { if (args1 == null && args2 == null) { return true; } if (args1 == null || args2 == null) { return false; } if (args1.Length != args2.Length) { return false; } for (int i = 1; i < args1.Length; i++) { if (!object.Equals(args1[i], args2[i])) { return false; } } return true; } private static void SaveHistoryToFile(string contextName, List<(string input, CommandMetadata Command, object[] Args)> history) { try { string text = string.Join("_", contextName.Split(Path.GetInvalidFileNameChars())); string item = Path.Combine(HistoryDirectory, text + ".txt"); string[] item2 = history.Select(((string input, CommandMetadata Command, object[] Args) h) => h.input).ToArray(); lock (_saveQueue) { _saveQueue.Enqueue((item, item2)); if (!_saveThreadRunning) { _saveThreadRunning = true; Thread thread = new Thread(ProcessSaveQueue); thread.IsBackground = true; thread.Name = "VCF-HistorySave"; thread.Start(); } } } catch (Exception ex) { Log.Error("Failed to save command history for context " + contextName + ": " + ex.Message); } } private static bool TryDequeueFromSaveQueue(out (string filePath, string[] inputs) item) { lock (_saveQueue) { if (_saveQueue.TryDequeue(out item)) { return true; } _saveThreadRunning = false; return false; } } private static void ProcessSaveQueue() { (string, string[]) item; while (TryDequeueFromSaveQueue(out item)) { try { if (!Directory.Exists(HistoryDirectory)) { Directory.CreateDirectory(HistoryDirectory); } File.WriteAllLines(item.Item1, item.Item2); } catch (Exception ex) { Log.Error("Failed to save command history to " + item.Item1 + ": " + ex.Message); } } } private static void LoadHistoryFromFile(ICommandContext ctx, string contextName) { try { string text = string.Join("_", contextName.Split(Path.GetInvalidFileNameChars())); string path = Path.Combine(HistoryDirectory, text + ".txt"); if (!File.Exists(path)) { return; } string[] array = File.ReadAllLines(path); if (array.Length == 0) { return; } List<(string, CommandMetadata, object[])> list = new List<(string, CommandMetadata, object[])>(); string[] array2 = array; foreach (string text2 in array2) { try { var (commandMetadata, array3) = ParseCommandForHistory(ctx, text2); if (commandMetadata != null && array3 != null) { list.Add((text2, commandMetadata, array3)); } else { list.Add((text2, null, null)); } } catch (Exception) { list.Add((text2, null, null)); } } _commandHistory[contextName] = list; } catch (Exception ex2) { Log.Error("Failed to load command history for context " + contextName + ": " + ex2.Message); } finally { _loadedHistories.Add(contextName); } } private static (CommandMetadata command, object[] args) ParseCommandForHistory(ICommandContext ctx, string input) { try { if (!input.StartsWith(".")) { return (null, null); } ParsedCommandInput parsedCommandInput = CommandRegistry.ParseInput(input); CacheResult commandFromCache = CommandRegistry.GetCommandFromCache(parsedCommandInput.CommandInput, parsedCommandInput.AssemblyName); if (commandFromCache == null || !commandFromCache.IsMatched) { commandFromCache = CommandRegistry.GetCommandFromCache(input); } IEnumerable<(CommandMetadata, string[])> commands = commandFromCache.Commands; if (!commandFromCache.IsMatched || !commands.Any()) { return (null, null); } foreach (var (commandMetadata, args) in commands) { if (CommandRegistry.CanCommandExecute(ctx, commandMetadata)) { var (flag, item, text) = CommandRegistry.TryConvertParameters(ctx, commandMetadata, args, parsedCommandInput.CommandInput); if (flag) { return (commandMetadata, item); } } } return (null, null); } catch (Exception) { return (null, null); } } } internal record CommandMetadata(CommandAttribute Attribute, string AssemblyName, MethodInfo Method, ConstructorInfo Constructor, ParameterInfo[] Parameters, Type ContextType, Type ConstructorType, CommandGroupAttribute GroupAttribute); internal readonly struct ParsedCommandInput { internal string AssemblyName { get; } internal string CommandInput { get; } internal string AfterPrefixAndAssembly { get; } internal bool HasAssembly => AssemblyName != null; internal ParsedCommandInput(string assemblyName, string commandInput, string afterPrefixAndAssembly) { AssemblyName = assemblyName; CommandInput = commandInput; AfterPrefixAndAssembly = afterPrefixAndAssembly; } } } namespace VampireCommandFramework.Framework { internal static class ChatDrainPatch { [HarmonyPatch(typeof(ServerBootstrapSystem), "OnUpdate")] public static class DrainTick_Patch { [HarmonyPostfix] public static void Postfix() { try { ChatMessageQueue.DrainOneTick(); } catch (Exception value) { Log.Error($"ChatMessageQueue.DrainOneTick failed: {value}"); } } } public static void Install() { ChatMessageQueue.SendSink = delegate(object userObj, string message) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) User val = (User)userObj; FixedString512Bytes val2 = FixedString512Bytes.op_Implicit(message); ServerChatUtils.SendSystemMessageToClient(VWorld.Server.EntityManager, val, ref val2); }; } } internal static class ChatMessageQueue { internal static readonly Dictionary<ulong, (object User, Queue<string> Queue)> _queues = new Dictionary<ulong, (object, Queue<string>)>(); internal static readonly HashSet<ulong> _sentThisTick = new HashSet<ulong>(); internal static Action<object, string> SendSink = delegate { }; internal static void Send(User user, string message) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) Send(user.PlatformId, user, message); } internal static void Send(ulong platformId, string message) { Send(platformId, null, message); } private static void Send(ulong platformId, object user, string message) { if (!_sentThisTick.Contains(platformId) && (!_queues.TryGetValue(platformId, out (object, Queue<string>) value) || value.Item2.Count == 0)) { SendSink(user, message); _sentThisTick.Add(platformId); } else { (object, Queue<string>) value2 = (_queues.TryGetValue(platformId, out value2) ? (user, value2.Item2) : (user, new Queue<string>())); value2.Item2.Enqueue(message); _queues[platformId] = value2; } } internal static void DrainOneTick() { _sentThisTick.Clear(); if (_queues.Count == 0) { return; } List<ulong> list = new List<ulong>(_queues.Keys); foreach (ulong item in list) { if (!_queues.TryGetValue(item, out (object, Queue<string>) value) || value.Item2.Count == 0) { _queues.Remove(item); continue; } string arg = value.Item2.Dequeue(); SendSink(value.Item1, arg); _sentThisTick.Add(item); if (value.Item2.Count == 0) { _queues.Remove(item); } } } internal static void Clear(ulong platformId) { _queues.Remove(platformId); _sentThisTick.Remove(platformId); } internal static void ResetForTests() { _queues.Clear(); _sentThisTick.Clear(); SendSink = delegate { }; } } } namespace VampireCommandFramework.Common { internal static class Log { internal static ManualLogSource Instance { get; set; } public static void Warning(string s) { LogOrConsole(s, delegate(string s) { Instance.LogWarning((object)s); }); } public static void Error(string s) { LogOrConsole(s, delegate(string s) { Instance.LogError((object)s); }); } public static void Debug(string s) { LogOrConsole(s, delegate(string s) { Instance.LogDebug((object)s); }); } public static void Info(string s) { LogOrConsole(s, delegate(string s) { Instance.LogInfo((object)s); }); } private static void LogOrConsole(string message, Action<string> instanceLog) { if (Instance == null) { Console.WriteLine(message); } else { instanceLog(message); } } } internal static class Utility { private const int MAX_MESSAGE_SIZE = 460; internal static List<string> GetParts(string input) { List<string> list = new List<string>(); if (string.IsNullOrWhiteSpace(input)) { return list; } bool flag = false; StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < input.Length; i++) { char c = input[i]; if (c == '\\' && i + 1 < input.Length) { char c2 = input[i + 1]; if (c2 == '"') { stringBuilder.Append(c2); i++; continue; } } switch (c) { case '"': flag = !flag; continue; case ' ': if (!flag) { if (stringBuilder.Length > 0) { list.Add(stringBuilder.ToString()); stringBuilder.Clear(); } continue; } break; } stringBuilder.Append(c); } if (stringBuilder.Length > 0) { list.Add(stringBuilder.ToString()); } return list; } internal static void InternalError(this ICommandContext ctx) { ctx.SysReply("An internal error has occurred."); } internal static void SysReply(this ICommandContext ctx, string input) { ctx.Reply("[vcf] ".Color(Color.Primary) + input.Color(Color.Beige)); } internal static void SysPaginatedReply(this ICommandContext ctx, StringBuilder input) { ctx.SysPaginatedReply(input.ToString()); } internal static void SysPaginatedReply(this ICommandContext ctx, string input) { if (input.Length <= 460) { ctx.SysReply(input); return; } string[] array = SplitIntoPages(input); string[] array2 = array; foreach (string text in array2) { string text2 = text.TrimEnd('\n', '\r', ' '); text2 = Environment.NewLine + text2; ctx.SysReply(text2); } } internal static string[] SplitIntoPages(string rawText, int pageSize = 460) { List<string> list = new List<string>(); StringBuilder stringBuilder = new StringBuilder(); string[] array = rawText.Split("\n"); List<string> list2 = new List<string>(); string[] array2 = array; foreach (string text in array2) { if (text.Length > pageSize) { string text2 = text; while (!string.IsNullOrWhiteSpace(text2) && text2.Length > pageSize) { int num = text2.LastIndexOf(' ', pageSize - (int)((double)pageSize * 0.05)); if (num <= 0) { num = Math.Min(pageSize - 1, text2.Length); } list2.Add(text2.Substring(0, num)); text2 = text2.Substring(num); } list2.Add(text2); } else { list2.Add(text); } } foreach (string item in list2) { if (stringBuilder.Length + item.Length > pageSize) { list.Add(stringBuilder.ToString()); stringBuilder.Clear(); } stringBuilder.AppendLine(item); } if (stringBuilder.Length > 0) { list.Add(stringBuilder.ToString()); } return list.ToArray(); } } internal static class VersionChecker { private class InstalledPluginInfo { public string GUID { get; set; } public string Name { get; set; } public string Version { get; set; } } public static void ListAllPluginVersions(Entity userEntity = default(Entity)) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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) try { List<InstalledPluginInfo> installedPlugins = GetInstalledPlugins(); if (installedPlugins.Count == 0) { LogInfoAndSendMessageToClient(userEntity, "No plugins found."); return; } LogInfoAndSendMessageToClient(userEntity, $"Installed Plugins ({installedPlugins.Count}):"); foreach (InstalledPluginInfo item in installedPlugins.OrderBy((InstalledPluginInfo p) => p.Name)) { string text = item.Name.Color(Color.Command) + ": " + item.Version.Color(Color.Green); string message = "[vcf] ".Color(Color.Primary) + text; SendMessageToClient(userEntity, message); } } catch (Exception ex) { Log.Error("Error listing plugin versions: " + ex.Message); } } private static void SendMessageToClient(Entity userEntity, string message) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) if (userEntity == default(Entity)) { return; } try { World server = VWorld.Server; if (server == null) { return; } _ = server.EntityManager; if (false) { return; } EntityManager entityManager = VWorld.Server.EntityManager; if (!((EntityManager)(ref entityManager)).Exists(userEntity)) { return; } entityManager = VWorld.Server.EntityManager; if (((EntityManager)(ref entityManager)).HasComponent<User>(userEntity)) { entityManager = VWorld.Server.EntityManager; User componentData = ((EntityManager)(ref entityManager)).GetComponentData<User>(userEntity); if (componentData.IsConnected) { FixedString512Bytes val = default(FixedString512Bytes); ((FixedString512Bytes)(ref val))..ctor(message); ServerChatUtils.SendSystemMessageToClient(VWorld.Server.EntityManager, componentData, ref val); } } } catch (Exception ex) { Log.Debug("Could not send message to client (user may have disconnected): " + ex.Message); } } private static void LogInfoAndSendMessageToClient(Entity userEntity, string message) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) Log.Info(message); SendMessageToClient(userEntity, message); } private static List<InstalledPluginInfo> GetInstalledPlugins() { List<InstalledPluginInfo> list = new List<InstalledPluginInfo>(); foreach (KeyValuePair<string, PluginInfo> plugin in ((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins) { PluginInfo value = plugin.Value; if (((value != null) ? value.Metadata : null) != null) { list.Add(new InstalledPluginInfo { GUID = value.Metadata.GUID, Name = value.Metadata.Name, Version = ((object)value.Metadata.Version).ToString() }); } } return list; } } } namespace VampireCommandFramework.Breadstone { [HarmonyPriority(200)] [HarmonyBefore(new string[] { "gg.deca.Bloodstone" })] [HarmonyPatch(typeof(ChatMessageSystem), "OnUpdate")] public static class ChatMessageSystem_Patch { public static void Prefix(ChatMessageSystem __instance) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) EntityQuery _query_661171423_ = __instance.__query_661171423_0; Enumerator<Entity> enumerator = ((EntityQuery)(ref _query_661171423_)).ToEntityArray(AllocatorHandle.op_Implicit((Allocator)2)).GetEnumerator(); while (enumerator.MoveNext()) { Entity current = enumerator.Current; EntityManager entityManager = ((ComponentSystemBase)__instance).EntityManager; FromCharacter componentData = ((EntityManager)(ref entityManager)).GetComponentData<FromCharacter>(current); entityManager = ((ComponentSystemBase)__instance).EntityManager; User componentData2 = ((EntityManager)(ref entityManager)).GetComponentData<User>(componentData.User); entityManager = ((ComponentSystemBase)__instance).EntityManager; ChatMessageEvent componentData3 = ((EntityManager)(ref entityManager)).GetComponentData<ChatMessageEvent>(current); string text = ((object)(FixedString512Bytes)(ref componentData3.MessageText)).ToString(); if (!text.StartsWith(".") || text.StartsWith("..")) { continue; } VChatEvent e = new VChatEvent(componentData.User, componentData.Character, text, componentData3.MessageType, componentData2); ChatCommandContext ctx = new ChatCommandContext(e); CommandResult commandResult; try { commandResult = CommandRegistry.Handle(ctx, text); } catch (Exception value) { Log.Error($"Error while handling chat message {value}"); continue; } if (commandResult == CommandResult.Success && text.StartsWith(".help-legacy", StringComparison.InvariantCulture)) { componentData3.MessageText = FixedString512Bytes.op_Implicit(text.Replace("-legacy", string.Empty)); entityManager = ((ComponentSystemBase)__instance).EntityManager; ((EntityManager)(ref entityManager)).SetComponentData<ChatMessageEvent>(current, componentData3); continue; } if (commandResult == CommandResult.Unmatched) { StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(19, 1, stringBuilder2); handler.AppendLiteral("Command not found: "); handler.AppendFormatted(text.Color(Color.Command)); stringBuilder3.AppendLine(ref handler); string[] array = CommandRegistry.FindCloseMatches(ctx, text).ToArray(); if (array.Length != 0) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(14, 1, stringBuilder2); handler.AppendLiteral("Did you mean: "); handler.AppendFormatted(string.Join(", ", array.Select((string c) => c.Color(Color.Command)))); stringBuilder4.AppendLine(ref handler); } ctx.SysReply(stringBuilder.ToString()); } entityManager = VWorld.Server.EntityManager; ((EntityManager)(ref entityManager)).DestroyEntity(current); } } } public class VChatEvent { public Entity SenderUserEntity { get; } public Entity SenderCharacterEntity { get; } public string Message { get; } public ChatMessageType Type { get; } public bool Cancelled { get; private set; } = false; public User User { get; } internal VChatEvent(Entity userEntity, Entity characterEntity, string message, ChatMessageType type, User user) { //IL_0010: 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: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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) SenderUserEntity = userEntity; SenderCharacterEntity = characterEntity; Message = message; Type = type; User = user; } public void Cancel() { Cancelled = true; } } internal static class VWorld { private static World _serverWorld; public static World Server { get { if (_serverWorld != null) { return _serverWorld; } _serverWorld = GetWorld("Server") ?? throw new Exception("There is no Server world (yet). Did you install a server mod on the client?"); return _serverWorld; } } public static bool IsServer => Application.productName == "VRisingServer"; public static void SendSystemMessage(this User user, string message) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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) FixedString512Bytes val = FixedString512Bytes.op_Implicit(message); ServerChatUtils.SendSystemMessageToClient(Server.EntityManager, user, ref val); } private static World GetWorld(string name) { Enumerator<World> enumerator = World.s_AllWorlds.GetEnumerator(); while (enumerator.MoveNext()) { World current = enumerator.Current; if (current.Name == name) { _serverWorld = current; return current; } } return null; } } } namespace VampireCommandFramework.Basics { [CommandGroup("config", null)] public class BepInExConfigCommands { [Command("dump", null, null, null, null, true)] public void DumpConfig(ICommandContext ctx, string pluginGuid) { var (guid, val2) = (KeyValuePair<string, PluginInfo>)(ref ((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.FirstOrDefault((KeyValuePair<string, PluginInfo> x) => x.Value.Metadata.GUID.Contains(pluginGuid, StringComparison.InvariantCultureIgnoreCase))); if (val2 != null) { object instance = val2.Instance; BasePlugin val3 = (BasePlugin)((instance is BasePlugin) ? instance : null); if (val3 != null) { DumpConfig(ctx, guid, val3); return; } } foreach (KeyValuePair<string, PluginInfo> plugin in ((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins) { ctx.SysReply("Found: " + plugin.Value.Metadata.GUID); } throw ctx.Error("Can not find that plugin"); } [Command("set", null, null, null, null, true)] public void DumpConfig(ICommandContext ctx, string pluginGuid, string section, string key, string value) { var (text2, val2) = (KeyValuePair<string, PluginInfo>)(ref ((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins.FirstOrDefault((KeyValuePair<string, PluginInfo> x) => x.Value.Metadata.GUID.Contains(pluginGuid, StringComparison.InvariantCultureIgnoreCase))); if (val2 != null) { object instance = val2.Instance; BasePlugin val3 = (BasePlugin)((instance is BasePlugin) ? instance : null); if (val3 != null) { var (val6, val7) = (KeyValuePair<ConfigDefinition, ConfigEntryBase>)(ref ((IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)val3.Config).FirstOrDefault((KeyValuePair<ConfigDefinition, ConfigEntryBase> k) => k.Key.Section.Equals(section, StringComparison.InvariantCultureIgnoreCase) && k.Key.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase))); if (val6 == (ConfigDefinition)null) { throw ctx.Error("Could not find property"); } try { object value2 = TomlTypeConverter.ConvertToValue(value, val7.SettingType); val7.SetSerializedValue(value); if (!val3.Config.SaveOnConfigSet) { val3.Config.Save(); } ctx.SysReply($"Set {val6.Key} = {value2}"); return; } catch (Exception) { throw ctx.Error($"Can not convert {value} to {val7.SettingType}"); } } } foreach (KeyValuePair<string, PluginInfo> plugin in ((BaseChainloader<BasePlugin>)(object)IL2CPPChainloader.Instance).Plugins) { ctx.SysReply("Found: " + plugin.Value.Metadata.GUID); } throw ctx.Error("Can not find that plugin"); } private static void DumpConfig(ICommandContext ctx, string guid, BasePlugin plugin) { ConfigFile config = plugin.Config; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("Path: " + config.ConfigFilePath); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(33, 2, stringBuilder2); handler.AppendLiteral("Dumping config for "); handler.AppendFormatted(guid.Color("#f0f")); handler.AppendLiteral(" with "); handler.AppendFormatted(((IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)config).Count()); handler.AppendLiteral(" entries"); stringBuilder3.AppendLine(ref handler); foreach (IGrouping<string, KeyValuePair<ConfigDefinition, ConfigEntryBase>> item in from k in (IEnumerable<KeyValuePair<ConfigDefinition, ConfigEntryBase>>)config group k by k.Key.Section into k orderby k.Key select k) { stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(2, 1, stringBuilder2); handler.AppendLiteral("["); handler.AppendFormatted(item.Key); handler.AppendLiteral("]"); stringBuilder4.AppendLine(ref handler); foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item2 in item) { item2.Deconstruct(out var key, out var value); ConfigDefinition val = key; ConfigEntryBase val2 = value; stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(3, 2, stringBuilder2); handler.AppendFormatted(val.Key.Color(Color.Beige)); handler.AppendLiteral(" = "); handler.AppendFormatted(val2.BoxedValue.ToString().Color(Color.LightGrey)); stringBuilder5.AppendLine(ref handler); } } ctx.SysPaginatedReply(stringBuilder); } } internal static class HelpCommands { private static readonly Regex _trailingLongDashRegex = new Regex("-\\d+$"); [Command("help-legacy", null, null, "Passes through a .help command that is compatible with other mods that don't use VCF.", null, false)] public static void HelpLegacy(ICommandContext ctx, string search = null) { ctx.SysReply("Attempting compatible .help " + search + " for non-VCF mods."); } [Command("help", null, null, null, null, false)] public static void HelpCommand(ICommandContext ctx, string search = null, string filter = null) { if (!string.IsNullOrEmpty(search)) { KeyValuePair<string, Dictionary<CommandMetadata, List<string>>> assembly = CommandRegistry.AssemblyCommandMap.FirstOrDefault((KeyValuePair<string, Dictionary<CommandMetadata, List<string>>> x) => x.Key.StartsWith(search, StringComparison.OrdinalIgnoreCase)); if (assembly.Value != null) { StringBuilder stringBuilder = new StringBuilder(); PrintAssemblyHelp(ctx, assembly, stringBuilder, filter); ctx.SysPaginatedReply(stringBuilder); return; } IEnumerable<KeyValuePair<CommandMetadata, List<string>>> source = CommandRegistry.AssemblyCommandMap.SelectMany((KeyValuePair<string, Dictionary<CommandMetadata, List<string>>> x) => x.Value); IEnumerable<KeyValuePair<CommandMetadata, List<string>>> source2 = source.Where((KeyValuePair<CommandMetadata, List<string>> x) => string.Equals(x.Key.Attribute.Id, search, StringComparison.InvariantCultureIgnoreCase) || string.Equals(x.Key.Attribute.Name, search, StringComparison.InvariantCultureIgnoreCase) || (x.Key.GroupAttribute != null && string.Equals(x.Key.GroupAttribute.Name, search, StringComparison.InvariantCultureIgnoreCase)) || x.Value.Contains<string>(search, StringComparer.InvariantCultureIgnoreCase)); source2 = from kvp in source2 where CommandRegistry.CanCommandExecute(ctx, kvp.Key) where filter == null || kvp.Key.Attribute.Name.Contains(filter, StringComparison.InvariantCultureIgnoreCase) select kvp; if (!source2.Any()) { throw ctx.Error("Could not find any commands for \"" + search.Color(Color.Gold) + "\""); } StringBuilder stringBuilder2 = new StringBuilder(); foreach (KeyValuePair<CommandMetadata, List<string>> item2 in source2) { GenerateFullHelp(item2.Key, item2.Value, stringBuilder2); } ctx.SysPaginatedReply(stringBuilder2); return; } StringBuilder stringBuilder3 = new StringBuilder(); StringBuilder stringBuilder4 = stringBuilder3; StringBuilder stringBuilder5 = stringBuilder4; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(16, 1, stringBuilder4); handler.AppendLiteral("Listing "); handler.AppendFormatted(Format.B("all")); handler.AppendLiteral(" plugins"); stringBuilder5.AppendLine(ref handler); stringBuilder4 = stringBuilder3; StringBuilder stringBuilder6 = stringBuilder4; handler = new StringBuilder.AppendInterpolatedStringHandler(32, 1, stringBuilder4); handler.AppendLiteral("Use "); handler.AppendFormatted(Format.B(".help <plugin>").Color(Color.Gold)); handler.AppendLiteral(" for commands in that plugin"); stringBuilder6.AppendLine(ref handler); foreach (string item3 in from x in CommandRegistry.AssemblyCommandMap where x.Value.Keys.Any((CommandMetadata c) => CommandRegistry.CanCommandExecute(ctx, c)) select x.Key into x orderby x select x) { stringBuilder4 = stringBuilder3; StringBuilder stringBuilder7 = stringBuilder4; handler = new StringBuilder.AppendInterpolatedStringHandler(0, 1, stringBuilder4); handler.AppendFormatted(item3.Color(Color.Lilac)); stringBuilder7.AppendLine(ref handler); } ctx.SysPaginatedReply(stringBuilder3); static void GenerateFullHelp(CommandMetadata command, List<string> aliases, StringBuilder sb) { StringBuilder stringBuilder8 = sb; StringBuilder stringBuilder9 = stringBuilder8; StringBuilder.AppendInterpolatedStringHandler handler2 = new StringBuilder.AppendInterpolatedStringHandler(1, 2, stringBuilder8); handler2.AppendFormatted(Format.B(command.Attribute.Name).Color(Color.LightRed)); handler2.AppendLiteral(" "); handler2.AppendFormatted(command.Attribute.Description.Color(Color.SoftBGrey)); stringBuilder9.AppendLine(ref handler2); sb.AppendLine(GetShortHelp(command)); stringBuilder8 = sb; StringBuilder stringBuilder10 = stringBuilder8; handler2 = new StringBuilder.AppendInterpolatedStringHandler(2, 2, stringBuilder8); handler2.AppendFormatted(Format.B("Aliases").Underline().Color(Color.Pink)); handler2.AppendLiteral(": "); handler2.AppendFormatted(string.Join(", ", aliases).Italic()); stringBuilder10.AppendLine(ref handler2); IEnumerable<Type> enumerable = from t in command.Parameters.Select((ParameterInfo p) => p.ParameterType).Distinct() where t.IsEnum select t; foreach (Type item4 in enumerable) { stringBuilder8 = sb; StringBuilder stringBuilder11 = stringBuilder8; handler2 = new StringBuilder.AppendInterpolatedStringHandler(2, 2, stringBuilder8); handler2.AppendFormatted((item4.Name + " Values").Bold().Underline().Color(Color.Pink)); handler2.AppendLiteral(": "); handler2.AppendFormatted(string.Join(", ", Enum.GetNames(item4)).Color(Color.Command)); stringBuilder11.AppendLine(ref handler2); } IEnumerable<Type> enumerable2 = from p in command.Parameters.Select((ParameterInfo p) => p.ParameterType).Distinct() where CommandRegistry._converters.ContainsKey(p) select p; foreach (Type item5 in enumerable2) { object item = CommandRegistry._converters[item5].instance; if (item is IConverterUsage) { IConverterUsage converterUsage = item as IConverterUsage; stringBuilder8 = sb; StringBuilder stringBuilder12 = stringBuilder8; handler2 = new StringBuilder.AppendInterpolatedStringHandler(2, 2, stringBuilder8); handler2.AppendFormatted((item5.Name ?? "").Bold()); handler2.AppendLiteral(": "); handler2.AppendFormatted(converterUsage.Usage); stringBuilder12.AppendLine(ref handler2); } } } } [Command("help-all", null, null, "Returns all plugin commands", null, false)] public static void HelpAllCommand(ICommandContext ctx, string filter = null) { StringBuilder stringBuilder = new StringBuilder(); if (filter == null) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(17, 1, stringBuilder2); handler.AppendLiteral("Listing "); handler.AppendFormatted(Format.B("all")); handler.AppendLiteral(" commands"); stringBuilder3.AppendLine(ref handler); } else { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(36, 2, stringBuilder2); handler.AppendLiteral("Listing "); handler.AppendFormatted(Format.B("all")); handler.AppendLiteral(" commands matching filter '"); handler.AppendFormatted(filter); handler.AppendLiteral("'"); stringBuilder4.AppendLine(ref handler); } bool flag = false; foreach (KeyValuePair<string, Dictionary<CommandMetadata, List<string>>> item in CommandRegistry.AssemblyCommandMap.Where((KeyValuePair<string, Dictionary<CommandMetadata, List<string>>> x) => x.Value.Keys.Any((CommandMetadata c) => CommandRegistry.CanCommandExecute(ctx, c) && (filter == null || GetShortHelp(c).Contains(filter, StringComparison.InvariantCultureIgnoreCase))))) { PrintAssemblyHelp(ctx, item, stringBuilder, filter); flag = true; } if (!flag) { throw ctx.Error("Could not find any commands for \"" + filter + "\""); } ctx.SysPaginatedReply(stringBuilder); } private static void PrintAssemblyHelp(ICommandContext ctx, KeyValuePair<string, Dictionary<CommandMetadata, List<string>>> assembly, StringBuilder sb, string filter = null) { string key = assembly.Key; key = _trailingLongDashRegex.Replace(key, ""); sb.AppendLine(("Commands from " + key.Medium().Color(Color.Primary) + ":").Underline()); IEnumerable<CommandMetadata> source = assembly.Value.Keys.Where((CommandMetadata c) => CommandRegistry.CanCommandExecute(ctx, c)); foreach (CommandMetadata item in source.OrderBy((CommandMetadata c) => ((c.GroupAttribute != null) ? (c.GroupAttribute.Name + " ") : "") + c.Attribute.Name)) { string shortHelp = GetShortHelp(item); if (filter == null || shortHelp.Contains(filter, StringComparison.InvariantCultureIgnoreCase)) { sb.AppendLine(shortHelp); } } } internal static string GetShortHelp(CommandMetadata command) { CommandAttribute attribute = command.Attribute; string text = (string.IsNullOrEmpty(command.GroupAttribute?.Name) ? string.Empty : (command.GroupAttribute.Name + " ")); string input = text + attribute.Name; string orGenerateUsage = GetOrGenerateUsage(command); string text2 = ".".Color(Color.Yellow); string text3 = input.Color(Color.Beige); return text2 + text3 + orGenerateUsage; } internal static string GetOrGenerateUsage(CommandMetadata command) { string text = command.Attribute.Usage; if (string.IsNullOrWhiteSpace(text)) { IEnumerable<string> values = command.Parameters.Select((ParameterInfo p) => CommandRegistry.IsRemainderParameter(p) ? ("<" + p.Name + "...>").Color(Color.LightGrey) : ((!p.HasDefaultValue) ? ("(" + p.Name + ")").Color(Color.LightGrey) : $"[{p.Name}={p.DefaultValue}]".Color(Color.Green))); text = string.Join(" ", values); } return (!string.IsNullOrWhiteSpace(text)) ? (" " + text) : string.Empty; } } public static class RepeatCommands { [Command("!", null, null, "Repeats the most recently executed command", null, false)] public static void RepeatLastCommand(ICommandContext ctx) { ctx.Error("This command is only a placeholder for the help system."); } [Command("! list", "! l", null, "Lists up to the last 10 commands you used.", null, false)] public static void ListCommandHistory(ICommandContext ctx) { ctx.Error("This command is only a placeholder for the help system."); } [Command("!", null, null, "Executes a specific command from your history by its number", null, false)] public static void ExecuteHistoryCommand(ICommandContext ctx, int previousXCommand) { ctx.Error("This command is only a placeholder for the help system."); } } internal static class VersionCommands { [Command("version", null, null, "Lists all installed plugins and their versions", null, true)] public static void VersionCommand(ICommandContext ctx) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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) //IL_0022: Unknown result type (might be due to invalid IL or missing references) Entity userEntity = (Entity)((ctx is ChatCommandContext chatCommandContext) ? chatCommandContext.Event.SenderUserEntity : default(Entity)); VersionChecker.ListAllPluginVersions(userEntity); } } } namespace VCF.Core.Basics { public class BasicAdminCheck : CommandMiddleware { public override bool CanExecute(ICommandContext ctx, CommandAttribute cmd, MethodInfo m) { return !cmd.AdminOnly || ctx.IsAdmin; } } public class RolePermissionMiddleware : CommandMiddleware { public override bool CanExecute(ICommandContext ctx, CommandAttribute command, MethodInfo method) { if (ctx.IsAdmin) { return true; } RoleRepository service = ctx.Services.GetService<RoleRepository>(); return service.CanUserExecuteCommand(ctx.Name, command.Id); } } public interface IRoleStorage { HashSet<string> Roles { get; } void SetCommandPermission(string command, HashSet<string> roleIds); void SetUserRoles(string userId, HashSet<string> roleIds); HashSet<string> GetCommandPermission(string command); HashSet<string> GetUserRoles(string userId); } public class RoleRepository { private IRoleStorage _storage; public HashSet<string> Roles => _storage.Roles; public RoleRepository(IRoleStorage storage) { _storage = storage; } public void AddUserToRole(string user, string role) { HashSet<string> hashSet = _storage.GetUserRoles(user) ?? new HashSet<string>(); hashSet.Add(role); _storage.SetUserRoles(user, hashSet); } public void RemoveUserFromRole(string user, string role) { HashSet<string> hashSet = _storage.GetUserRoles(user) ?? new HashSet<string>(); hashSet.Remove(role); _storage.SetUserRoles(user, hashSet); } public void AddRoleToCommand(string command, string role) { HashSet<string> hashSet = _storage.GetCommandPermission(command) ?? new HashSet<string>(); hashSet.Add(role); _storage.SetCommandPermission(command, hashSet); } public void RemoveRoleFromCommand(string command, string role) { HashSet<string> hashSet = _storage.GetCommandPermission(command) ?? new HashSet<string>(); hashSet.Remove(role); _storage.SetCommandPermission(command, hashSet); } public HashSet<string> ListUserRoles(string user) { return _storage.GetUserRoles(user); } public HashSet<string> ListCommandRoles(string command) { return _storage.GetCommandPermission(command); } public bool CanUserExecuteCommand(string user, string command) { HashSet<string> commandPermission = _storage.GetCommandPermission(command); if (commandPermission == null) { return false; } HashSet<string> userRoles = _storage.GetUserRoles(user); if (userRoles == null) { return false; } return commandPermission.Any(userRoles.Contains); } } public class MemoryRoleStorage : IRoleStorage { private Dictionary<string, HashSet<string>> _userRoles = new Dictionary<string, HashSet<string>>(); private Dictionary<string, HashSet<string>> _commandPermissions = new Dictionary<string, HashSet<string>>(); public HashSet<string> Roles => new HashSet<string>(); public void SetCommandPermission(string command, HashSet<string> roleIds) { foreach (string roleId in roleIds) { Roles.Add(roleId); } _commandPermissions[command] = roleIds; } public void SetUserRoles(string userId, HashSet<string> roleIds) { _userRoles[userId] = roleIds; } public HashSet<string> GetCommandPermission(string command) { return _commandPermissions.GetValueOrDefault(command); } public HashSet<string> GetUserRoles(string userId) { HashSet<string> value; return _userRoles.TryGetValue(userId, out value) ? value : new HashSet<string>(); } } public class RoleCommands { public record struct Role(string Name); public record struct User(string Id); public record struct Command(string Id); public class RoleConverter : CommandArgumentConverter<Role> { public override Role Parse(ICommandContext ctx, string input) { RoleRepository requiredService = ctx.Services.GetRequiredService<RoleRepository>(); if (requiredService.Roles.Contains(input)) { return new Role(input); } throw ctx.Error("Invalid role"); } } public class UserConverter : CommandArgumentConverter<User> { public override User Parse(ICommandContext ctx, string input) { return new User(input); } } private RoleRepository _roleRepository = new RoleRepository(new MemoryRoleStorage()); [Command("create", null, null, null, null, false)] public void CreateRole(ICommandContext ctx, s