using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
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 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.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.9.0.0")]
[assembly: AssemblyInformationalVersion("0.9.0+24.Branch.main.Sha.d432c15d766e8a2dc2260b16d84a5c4dc4882c7e")]
[assembly: AssemblyProduct("VampireCommandFramework")]
[assembly: AssemblyTitle("VampireCommandFramework")]
[assembly: InternalsVisibleTo("VCF.Tests")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.9.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_0025: 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)
if (v.Length > maxMessageLength)
{
v = v.Substring(0, maxMessageLength);
}
ServerChatUtils.SendSystemMessageToClient(VWorld.Server.EntityManager, User, v);
}
public CommandException Error(string LogMessage)
{
return new CommandException(LogMessage);
}
}
public static class Color
{
public static string Red = "red";
public static string Primary = "#b0b";
public static string White = "#eee";
public static string LightGrey = "#ccc";
public static string Yellow = "#dd0";
public static string DarkGreen = "#0c0";
}
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; }
}
[BepInPlugin("gg.deca.VampireCommandFramework", "VampireCommandFramework", "0.9.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_008f: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: 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();
CommandRegistry.RegisterCommandType(typeof(HelpCommands));
CommandRegistry.RegisterCommandType(typeof(BepInExConfigCommands));
((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 = ".";
private 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()
};
public static List<CommandMiddleware> Middlewares { get; } = new List<CommandMiddleware>
{
new BasicAdminCheck()
};
internal static Dictionary<Assembly, Dictionary<CommandMetadata, List<string>>> AssemblyCommandMap { get; } = new Dictionary<Assembly, Dictionary<CommandMetadata, List<string>>>();
internal static void Reset()
{
Middlewares.Clear();
Middlewares.AddRange(DEFAULT_MIDDLEWARES);
AssemblyCommandMap.Clear();
_converters.Clear();
_cache = new CommandCache();
}
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} {value}");
return false;
}
}
return true;
}
public static CommandResult Handle(ICommandContext ctx, string input)
{
CacheResult command2 = _cache.GetCommand(input);
CommandMetadata command3 = command2.Command;
string[] args = command2.Args;
CommandMetadata commandMetadata = command3;
string[] array = args;
if (!command2.IsMatched)
{
if (!command2.HasPartial)
{
return CommandResult.Unmatched;
}
foreach (CommandMetadata partialMatch in command2.PartialMatches)
{
ctx.SysReply(HelpCommands.PrintShortHelp(partialMatch));
}
return CommandResult.UsageError;
}
if (!commandMetadata.ContextType.IsAssignableFrom(ctx?.GetType()))
{
Log.Warning($"Matched [{commandMetadata.Attribute.Id}] but can not assign {commandMetadata.ContextType.Name} from {ctx?.GetType().Name}");
return CommandResult.InternalError;
}
if (commandMetadata.Constructor != null && !commandMetadata.ConstructorType.IsAssignableFrom(ctx?.GetType()))
{
Log.Warning($"Matched [{commandMetadata.Attribute.Id}] but can not assign {commandMetadata.ConstructorType.Name} from {ctx?.GetType().Name}");
ctx.InternalError();
return CommandResult.InternalError;
}
int num = array.Length;
int num2 = commandMetadata.Parameters.Length;
object[] array2 = new object[num2 + 1];
array2[0] = ctx;
if (num != num2)
{
if (!commandMetadata.Parameters.Skip(num).All((ParameterInfo p) => p.HasDefaultValue))
{
return CommandResult.UsageError;
}
for (int i = num; i < num2; i++)
{
array2[i + 1] = commandMetadata.Parameters[i].DefaultValue;
}
}
for (int j = 0; j < num; j++)
{
ParameterInfo parameterInfo = commandMetadata.Parameters[j];
string text = array[j];
if (_converters.TryGetValue(parameterInfo.ParameterType, out (object, MethodInfo, Type) value))
{
var (obj, methodInfo, type) = value;
if (!type.IsAssignableFrom(ctx.GetType()))
{
Log.Error("Converter type " + type.Name + " is not assignable from " + ctx.GetType().Name);
ctx.InternalError();
return CommandResult.InternalError;
}
object[] parameters = new object[2] { ctx, text };
try
{
object obj2 = methodInfo.Invoke(obj, parameters);
array2[j + 1] = obj2;
}
catch (TargetInvocationException ex)
{
if (ex.InnerException is CommandException ex2)
{
ctx.Reply("<color=red>[error]</color> Failed converted parameter: " + ex2.Message);
return CommandResult.UsageError;
}
Log.Warning($"Hit unexpected exception {ex}");
ctx.InternalError();
return CommandResult.InternalError;
}
catch (Exception value2)
{
Log.Warning($"Hit unexpected exception {value2}");
ctx.InternalError();
return CommandResult.InternalError;
}
continue;
}
TypeConverter converter = TypeDescriptor.GetConverter(parameterInfo.ParameterType);
try
{
object obj3 = converter.ConvertFromInvariantString(text);
if (converter is EnumConverter && !Enum.IsDefined(parameterInfo.ParameterType, obj3))
{
ctx.Reply($"<color=red>[error]</color> Invalid value {obj3} for {parameterInfo.ParameterType.Name}");
return CommandResult.UsageError;
}
array2[j + 1] = obj3;
}
catch (Exception ex3)
{
ctx.Reply("<color=red>[error]</color> Failed converted parameter: " + ex3.Message);
return CommandResult.UsageError;
}
}
object obj4 = null;
if (!commandMetadata.Method.IsStatic && (!commandMetadata.Method.DeclaringType.IsAbstract || !commandMetadata.Method.DeclaringType.IsSealed))
{
try
{
object? obj5;
if (!(commandMetadata.Constructor == null))
{
ConstructorInfo constructor = commandMetadata.Constructor;
object[] parameters2 = new ICommandContext[1] { ctx };
obj5 = constructor.Invoke(parameters2);
}
else
{
obj5 = Activator.CreateInstance(commandMetadata.Method.DeclaringType);
}
obj4 = obj5;
}
catch (TargetInvocationException ex4)
{
if (ex4.InnerException is CommandException ex5)
{
ctx.SysReply(ex5.Message);
}
else
{
ctx.InternalError();
}
return CommandResult.InternalError;
}
}
if (!CanCommandExecute(ctx, commandMetadata))
{
ctx.Reply("<color=red>[denied]</color> " + commandMetadata.Attribute.Id);
return CommandResult.Denied;
}
HandleBeforeExecute(ctx, commandMetadata);
CommandException ex7 = default(CommandException);
try
{
commandMetadata.Method.Invoke(obj4, array2);
}
catch (TargetInvocationException ex6) when (((Func<bool>)delegate
{
// Could not convert BlockContainer to single expression
ex7 = ex6.InnerException as CommandException;
return ex7 != null;
}).Invoke())
{
ctx.Reply("<color=red>[error]</color> " + ex7.Message);
return CommandResult.CommandError;
}
catch (Exception value3)
{
Log.Warning($"Hit unexpected exception executing command {commandMetadata.Attribute.Id}\n: {value3}");
ctx.InternalError();
return CommandResult.InternalError;
}
HandleAfterExecute(ctx, commandMetadata);
return CommandResult.Success;
static void HandleAfterExecute(ICommandContext ctx, CommandMetadata command)
{
Middlewares.ForEach(delegate(CommandMiddleware m)
{
m.AfterExecute(ctx, command.Attribute, command.Method);
});
}
static void HandleBeforeExecute(ICommandContext ctx, CommandMetadata command)
{
Middlewares.ForEach(delegate(CommandMiddleware m)
{
m.BeforeExecute(ctx, command.Attribute, command.Method);
});
}
}
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);
}
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);
}
}
}
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 + " has no CommandContext as first argument");
return;
}
ParameterInfo[] array = parameters.Skip(1).ToArray();
if (!array.All(delegate(ParameterInfo param)
{
if (_converters.ContainsKey(param.ParameterType))
{
Log.Debug($"Method {method.Name} has a parameter of type {param.ParameterType.Name} 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} could not be converted, so {method.Name} will be ignored.");
return false;
}
return true;
}))
{
return;
}
Type constructorType = customConstructor?.GetParameters().Single().ParameterType;
CommandMetadata commandMetadata = new CommandMetadata(customAttribute, 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);
}
}
AssemblyCommandMap.TryGetValue(assembly, out var value);
if (value == null)
{
value = new Dictionary<CommandMetadata, List<string>>();
}
value[commandMetadata] = list;
AssemblyCommandMap[assembly] = value;
}
public static void UnregisterAssembly()
{
UnregisterAssembly(Assembly.GetCallingAssembly());
}
public static void UnregisterAssembly(Assembly assembly)
{
foreach (TypeInfo definedType in assembly.DefinedTypes)
{
_cache.RemoveCommandsFromType(definedType);
UnregisterConverter(definedType);
}
AssemblyCommandMap.Remove(assembly);
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "gg.deca.VampireCommandFramework";
public const string PLUGIN_NAME = "VampireCommandFramework";
public const string PLUGIN_VERSION = "0.9.0";
}
}
namespace VampireCommandFramework.Registry
{
internal record CacheResult(CommandMetadata Command, string[] Args, IEnumerable<CommandMetadata> PartialMatches)
{
internal bool IsMatched => Command != null;
internal bool HasPartial => PartialMatches?.Any() ?? false;
[CompilerGenerated]
protected virtual bool PrintMembers(StringBuilder builder)
{
RuntimeHelpers.EnsureSufficientExecutionStack();
builder.Append("Command = ");
builder.Append(Command);
builder.Append(", Args = ");
builder.Append(Args);
builder.Append(", PartialMatches = ");
builder.Append(PartialMatches);
return true;
}
}
internal class CommandCache
{
private static Dictionary<Type, HashSet<(string, int)>> _commandAssemblyMap = new Dictionary<Type, HashSet<(string, int)>>();
private Dictionary<string, Dictionary<int, CommandMetadata>> _newCache = new Dictionary<string, Dictionary<int, CommandMetadata>>();
internal void AddCommand(string key, ParameterInfo[] parameters, CommandMetadata command)
{
int num = parameters.Length;
int num2 = parameters.Where((ParameterInfo p) => p.HasDefaultValue).Count();
if (!_newCache.ContainsKey(key))
{
_newCache.Add(key, new Dictionary<int, CommandMetadata>());
}
for (int i = num - num2; i <= num; i++)
{
_newCache[key] = _newCache.GetValueOrDefault(key, new Dictionary<int, CommandMetadata>()) ?? new Dictionary<int, CommandMetadata>();
if (_newCache[key].ContainsKey(i))
{
Log.Warning($"Command {key} has multiple commands with {i} parameters");
}
else
{
_newCache[key][i] = 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, i));
_commandAssemblyMap[declaringType] = hashSet;
}
}
}
internal CacheResult GetCommand(string rawInput)
{
List<CommandMetadata> list = new List<CommandMetadata>();
foreach (var (text2, dictionary2) in _newCache)
{
if (rawInput.StartsWith(text2) && (rawInput.Length <= text2.Length || rawInput[text2.Length] == ' '))
{
string input = rawInput.Substring(text2.Length).Trim();
string[] array = Utility.GetParts(input).ToArray();
if (dictionary2.TryGetValue(array.Length, out var value))
{
return new CacheResult(value, array, null);
}
list.AddRange(dictionary2.Values);
}
}
return new CacheResult(null, null, list.Distinct());
}
internal void RemoveCommandsFromType(Type t)
{
if (!_commandAssemblyMap.TryGetValue(t, out var value))
{
return;
}
foreach (var (key, key2) in value)
{
if (_newCache.TryGetValue(key, out var value2))
{
value2.Remove(key2);
}
}
_commandAssemblyMap.Remove(t);
}
internal void Clear()
{
_newCache.Clear();
}
internal void Reset()
{
throw new NotImplementedException();
}
}
internal record CommandMetadata(CommandAttribute Attribute, MethodInfo Method, ConstructorInfo Constructor, ParameterInfo[] Parameters, Type ContextType, Type ConstructorType, CommandGroupAttribute GroupAttribute);
}
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.White));
}
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(Environment.NewLine);
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();
}
}
}
namespace VampireCommandFramework.Breadstone
{
[HarmonyPriority(200)]
[HarmonyBefore(new string[] { "gg.deca.Bloodstone" })]
[HarmonyPatch(typeof(ChatMessageSystem), "OnUpdate")]
public static class ChatMessageSystem_Patch
{
public static bool Prefix(ChatMessageSystem __instance)
{
//IL_0002: 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)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: 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_0029: 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_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: 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_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_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
//IL_00a2: 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_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_0135: Unknown result type (might be due to invalid IL or missing references)
//IL_013a: Unknown result type (might be due to invalid IL or missing references)
//IL_013e: Unknown result type (might be due to invalid IL or missing references)
//IL_0140: Unknown result type (might be due to invalid IL or missing references)
//IL_015e: Unknown result type (might be due to invalid IL or missing references)
//IL_0163: Unknown result type (might be due to invalid IL or missing references)
//IL_0167: Unknown result type (might be due to invalid IL or missing references)
_ = __instance.__query_661171423_0;
if (true)
{
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();
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);
return true;
}
if (commandResult != 0)
{
entityManager = VWorld.Server.EntityManager;
((EntityManager)(ref entityManager)).DestroyEntity(current);
return true;
}
}
}
return true;
}
}
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_0006: 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)
ServerChatUtils.SendSystemMessageToClient(Server.EntityManager, user, message);
}
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.White));
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)
{
if (!string.IsNullOrEmpty(search))
{
KeyValuePair<Assembly, Dictionary<CommandMetadata, List<string>>> assembly2 = CommandRegistry.AssemblyCommandMap.FirstOrDefault((KeyValuePair<Assembly, Dictionary<CommandMetadata, List<string>>> x) => x.Key.GetName().Name.StartsWith(search, StringComparison.OrdinalIgnoreCase));
if (assembly2.Value != null)
{
StringBuilder stringBuilder = new StringBuilder();
PrintAssemblyHelp(ctx, assembly2, stringBuilder);
ctx.SysPaginatedReply(stringBuilder);
return;
}
IEnumerable<KeyValuePair<CommandMetadata, List<string>>> source = CommandRegistry.AssemblyCommandMap.SelectMany((KeyValuePair<Assembly, 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.Value.Contains<string>(search, StringComparer.InvariantCultureIgnoreCase));
source2 = source2.Where((KeyValuePair<CommandMetadata, List<string>> kvp) => CommandRegistry.CanCommandExecute(ctx, kvp.Key));
if (!source2.Any())
{
throw ctx.Error("Could not find any commands for \"" + search + "\"");
}
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.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(17, 1, stringBuilder4);
handler.AppendLiteral("Listing ");
handler.AppendFormatted(Format.B("all"));
handler.AppendLiteral(" commands");
stringBuilder4.AppendLine(ref handler);
foreach (KeyValuePair<Assembly, Dictionary<CommandMetadata, List<string>>> item3 in CommandRegistry.AssemblyCommandMap)
{
PrintAssemblyHelp(ctx, item3, stringBuilder3);
}
ctx.SysPaginatedReply(stringBuilder3);
static void GenerateFullHelp(CommandMetadata command, List<string> aliases, StringBuilder sb)
{
StringBuilder stringBuilder5 = sb;
StringBuilder stringBuilder6 = stringBuilder5;
StringBuilder.AppendInterpolatedStringHandler handler2 = new StringBuilder.AppendInterpolatedStringHandler(4, 3, stringBuilder5);
handler2.AppendFormatted(Format.B(command.Attribute.Name));
handler2.AppendLiteral(" (");
handler2.AppendFormatted(command.Attribute.Id);
handler2.AppendLiteral(") ");
handler2.AppendFormatted(command.Attribute.Description);
stringBuilder6.AppendLine(ref handler2);
sb.AppendLine(PrintShortHelp(command));
stringBuilder5 = sb;
StringBuilder stringBuilder7 = stringBuilder5;
handler2 = new StringBuilder.AppendInterpolatedStringHandler(2, 2, stringBuilder5);
handler2.AppendFormatted(Format.B("Aliases").Underline());
handler2.AppendLiteral(": ");
handler2.AppendFormatted(string.Join(", ", aliases).Italic());
stringBuilder7.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)
{
stringBuilder5 = sb;
StringBuilder stringBuilder8 = stringBuilder5;
handler2 = new StringBuilder.AppendInterpolatedStringHandler(2, 2, stringBuilder5);
handler2.AppendFormatted((item4.Name + " Values").Bold().Underline());
handler2.AppendLiteral(": ");
handler2.AppendFormatted(string.Join(", ", Enum.GetNames(item4)));
stringBuilder8.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;
stringBuilder5 = sb;
StringBuilder stringBuilder9 = stringBuilder5;
handler2 = new StringBuilder.AppendInterpolatedStringHandler(2, 2, stringBuilder5);
handler2.AppendFormatted((item5.Name ?? "").Bold());
handler2.AppendLiteral(": ");
handler2.AppendFormatted(converterUsage.Usage);
stringBuilder9.AppendLine(ref handler2);
}
}
}
static void PrintAssemblyHelp(ICommandContext ctx, KeyValuePair<Assembly, Dictionary<CommandMetadata, List<string>>> assembly, StringBuilder sb)
{
string name = assembly.Key.GetName().Name;
name = _trailingLongDashRegex.Replace(name, "");
sb.AppendLine(("Commands from " + name.Medium().Color(Color.Primary) + ":").Underline());
IEnumerable<CommandMetadata> enumerable3 = assembly.Value.Keys.Where((CommandMetadata c) => CommandRegistry.CanCommandExecute(ctx, c));
foreach (CommandMetadata item6 in enumerable3)
{
sb.AppendLine(PrintShortHelp(item6));
}
}
}
internal static string PrintShortHelp(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.White);
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) => (!p.HasDefaultValue) ? ("(" + p.Name + ")").Color(Color.LightGrey) : $"[{p.Name}={p.DefaultValue}]".Color(Color.DarkGreen));
text = string.Join(" ", values);
}
return (!string.IsNullOrWhiteSpace(text)) ? (" " + text) : string.Empty;
}
}
}
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, string name)
{
_roleRepository.Roles.Add(name);
}
[Command("allow", null, null, null, null, false)]
public void AllowCommand(ICommandContext ctx, Role role, Command command)
{
_roleRepository.AddRoleToCommand(command.Id, role.Name);
}
[Command("deny", null, null, null, null, false)]
public void DenyCommand(ICommandContext ctx, Role role, Command command)
{
_roleRepository.RemoveRoleFromCommand(command.Id, role.Name);
}
[Command("assign", null, null, null, null, false)]
public void AssignUserToRole(ICommandContext ctx, User user, Role role)
{
_roleRepository.AddUserToRole(user.Id, role.Name);
}
[Command("unassign", null, null, null, null, false)]
public void UnassignUserFromRole(ICommandContext ctx, User user, Role role)
{
_roleRepository.RemoveUserFromRole(user.Id, role.Name);
}
[Command("list", null, null, null, null, false)]
public void ListRoles(ICommandContext ctx)
{
ctx.Reply("Roles: " + string.Join(", ", _roleRepository.Roles));
}
[Command("list user", null, null, null, null, false)]
public void ListRoles(ICommandContext ctx, User user)
{
ctx.Reply("Roles: " + string.Join(", ", _roleRepository.ListUserRoles(user.Id)));
}
[Command("list command", null, null, null, null, false)]
public void ListCommands(ICommandContext ctx, Command command)
{
ctx.Reply("Roles: " + string.Join(", ", _roleRepository.ListCommandRoles(command.Id)));
}
}
}