fork from 1.3

This commit is contained in:
moux23333
2024-01-27 21:06:07 +08:00
commit 22fc0b0848
1507 changed files with 24139 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
namespace FreeSR.Shared.Command
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class CommandAttribute : Attribute
{
public string Name { get; }
public CommandAttribute(string name)
{
Name = name;
}
}
}

View File

@@ -0,0 +1,46 @@
namespace FreeSR.Shared.Command
{
using FreeSR.Shared.Command.Context;
using System.Reflection;
public class CommandCategory : ICommandCategory
{
private readonly Dictionary<string, CommandHandler> handlers = new Dictionary<string, CommandHandler>();
private readonly Dictionary<string, ICommandCategory> categories = new Dictionary<string, ICommandCategory>();
public void Build()
{
Type type = GetType();
foreach (MethodInfo info in type.GetMethods())
{
CommandAttribute attribute = info.GetCustomAttribute<CommandAttribute>();
if (attribute == null)
continue;
var handler = new CommandHandler(type, info);
handlers.Add(attribute.Name, handler);
}
foreach (Type info in type.GetNestedTypes())
{
CommandAttribute attribute = info.GetCustomAttribute<CommandAttribute>();
if (attribute == null)
continue;
CommandCategory category = (CommandCategory)Activator.CreateInstance(info);
category.Build();
categories.Add(attribute.Name, category);
}
}
public CommandResult Invoke(ICommandContext context, string[] parameters, uint depth)
{
if (handlers.TryGetValue(parameters[depth], out CommandHandler handler))
return handler.Invoke(this, context, parameters, depth + 1);
if (categories.TryGetValue(parameters[depth], out ICommandCategory category))
return category.Invoke(context, parameters, depth + 1);
return CommandResult.Invalid;
}
}
}

View File

@@ -0,0 +1,10 @@
namespace FreeSR.Shared.Command
{
public class CommandException : Exception
{
public CommandException(string message) : base(message)
{
// CommandException.
}
}
}

View File

@@ -0,0 +1,68 @@
namespace FreeSR.Shared.Command
{
using FreeSR.Shared.Command.Context;
using FreeSR.Shared.Command.Convert;
using System.Linq.Expressions;
using System.Reflection;
public class CommandHandler
{
private Delegate _handlerDelegate;
private readonly List<ICommandParameterConverter> _additionalParameterConverters = new List<ICommandParameterConverter>();
public CommandHandler(Type type, MethodInfo method)
{
InitializeDelegate(type, method);
InitializeParameters(method);
}
private void InitializeDelegate(Type type, MethodInfo method)
{
ParameterInfo[] info = method.GetParameters();
if (info.Length < 1 || !typeof(ICommandContext).IsAssignableFrom(info[0].ParameterType))
throw new CommandException("");
ParameterExpression instance = Expression.Parameter(type);
var parameters = new List<ParameterExpression> { instance };
parameters.AddRange(info.Select(p => Expression.Parameter(p.ParameterType)));
MethodCallExpression call = Expression.Call(instance, method, parameters.Skip(1));
LambdaExpression lambda = Expression.Lambda(call, parameters);
_handlerDelegate = lambda.Compile();
}
private void InitializeParameters(MethodInfo method)
{
foreach (Type parameterType in method.GetParameters()
.Skip(1)
.Select(p => p.ParameterType))
{
ICommandParameterConverter converter = CommandManager.Instance.GetConverter(parameterType);
if (converter == null)
throw new CommandException("");
_additionalParameterConverters.Add(converter);
}
}
public CommandResult Invoke(ICommandCategory category, ICommandContext context, string[] parameters, uint depth)
{
var additionalParameterCount = parameters.Length - depth;
if (additionalParameterCount < _additionalParameterConverters.Count)
return CommandResult.Parameter;
var parameterObjects = new List<object> { category, context };
for (int i = 0; i < _additionalParameterConverters.Count; i++)
{
if (!_additionalParameterConverters[i].TryConvert(parameters[depth + i], out object result))
return CommandResult.Parameter;
parameterObjects.Add(result);
}
_handlerDelegate.DynamicInvoke(parameterObjects.ToArray());
return CommandResult.Ok;
}
}
}

View File

@@ -0,0 +1,93 @@
namespace FreeSR.Shared.Command
{
using System.Reflection;
using FreeSR.Shared.Command.Context;
using FreeSR.Shared.Command.Convert;
using NLog;
public sealed class CommandManager : Singleton<CommandManager>
{
private static readonly Logger s_log = LogManager.GetCurrentClassLogger();
private readonly Dictionary<Type, ICommandParameterConverter> converters = new Dictionary<Type, ICommandParameterConverter>();
private readonly Dictionary<string, CommandCategory> categories = new Dictionary<string, CommandCategory>();
private CommandManager()
{
// CommandManager.
}
public void Initialize(params Type[] categories)
{
InitializeConverters();
InitializeCategories(categories);
}
private void InitializeConverters()
{
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()
.Where(t => typeof(ICommandParameterConverter).IsAssignableFrom(t)))
{
CommandParameterConverterAttribute attribute = type.GetCustomAttribute<CommandParameterConverterAttribute>();
if (attribute == null)
continue;
ICommandParameterConverter converter = (ICommandParameterConverter)Activator.CreateInstance(type);
converters.Add(attribute.Type, converter);
}
}
private void InitializeCategories(params Type[] types)
{
foreach (Type type in types)
{
CommandAttribute attribute = type.GetCustomAttribute<CommandAttribute>();
if (attribute == null)
continue;
CommandCategory category = (CommandCategory)Activator.CreateInstance(type);
category.Build();
categories.Add(attribute.Name, category);
}
}
public ICommandParameterConverter GetConverter(Type type)
{
return converters.TryGetValue(type, out ICommandParameterConverter converter) ? converter : null;
}
public void Invoke(string command)
{
var context = new ConsoleCommandContext();
Invoke(context, command);
}
public void Invoke(ICommandContext context, string command)
{
CommandResult result = InvokeResult(context, command);
if (result != CommandResult.Ok)
context.SendError(GetError());
string GetError()
{
return result switch
{
CommandResult.Invalid => "No valid command was found.",
CommandResult.Parameter => "Invalid parameters were provided for the command.",
CommandResult.Permission => "You don't have permission to invoke the command.",
_ => throw new ArgumentOutOfRangeException()
};
}
}
private CommandResult InvokeResult(ICommandContext context, string command)
{
string[] parameters = command.Split(' ');
if (categories.TryGetValue(parameters[0], out CommandCategory category))
return category.Invoke(context, parameters, 1);
return CommandResult.Invalid;
}
}
}

View File

@@ -0,0 +1,10 @@
namespace FreeSR.Shared.Command
{
public enum CommandResult
{
Ok,
Invalid,
Permission,
Parameter
}
}

View File

@@ -0,0 +1,19 @@
namespace FreeSR.Shared.Command.Context
{
using NLog;
public class ConsoleCommandContext : ICommandContext
{
private static readonly Logger s_log = LogManager.GetCurrentClassLogger();
public void SendMessage(string message)
{
s_log.Info(message);
}
public void SendError(string message)
{
s_log.Error(message);
}
}
}

View File

@@ -0,0 +1,8 @@
namespace FreeSR.Shared.Command.Context
{
public interface ICommandContext
{
void SendMessage(string message);
void SendError(string message);
}
}

View File

@@ -0,0 +1,13 @@
namespace FreeSR.Shared.Command.Convert
{
[AttributeUsage(AttributeTargets.Class)]
public class CommandParameterConverterAttribute : Attribute
{
public Type Type { get; }
public CommandParameterConverterAttribute(Type type)
{
Type = type;
}
}
}

View File

@@ -0,0 +1,7 @@
namespace FreeSR.Shared.Command.Convert
{
public interface ICommandParameterConverter
{
bool TryConvert(string parameter, out object result);
}
}

View File

@@ -0,0 +1,12 @@
namespace FreeSR.Shared.Command.Convert
{
[CommandParameterConverter(typeof(string))]
public class StringCommandParameterConverter : ICommandParameterConverter
{
public bool TryConvert(string parameter, out object result)
{
result = parameter;
return true;
}
}
}

View File

@@ -0,0 +1,16 @@
namespace FreeSR.Shared.Command.Convert
{
[CommandParameterConverter(typeof(uint))]
public class UIntCommandParameterConverter : ICommandParameterConverter
{
public bool TryConvert(string parameter, out object result)
{
result = null;
if (!uint.TryParse(parameter, out uint parseResult))
return false;
result = parseResult;
return true;
}
}
}

View File

@@ -0,0 +1,9 @@
namespace FreeSR.Shared.Command
{
using FreeSR.Shared.Command.Context;
public interface ICommandCategory
{
CommandResult Invoke(ICommandContext context, string[] parameters, uint depth);
}
}