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);
}
}

View File

@@ -0,0 +1,25 @@
namespace FreeSR.Shared.Configuration
{
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;
public sealed class ConfigurationManager<T> : Singleton<ConfigurationManager<T>> where T : class
{
public IConfiguration Config { get; private set; }
public T Model { get; private set; }
public void Initialize(string path)
{
var builder = new ConfigurationBuilder()
.AddJsonFile(path, false, true)
.AddEnvironmentVariables();
Config = builder.Build();
Model = Config.Get<T>();
ChangeToken.OnChange(
Config.GetReloadToken,
() => Model = Config.Get<T>());
}
}
}

View File

@@ -0,0 +1,8 @@
namespace FreeSR.Shared.Configuration
{
public class NetworkConfiguration
{
public string Host { get; set; }
public ushort Port { get; set; }
}
}

View File

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

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="NLog" Version="5.2.2" />
</ItemGroup>
<ItemGroup>
<None Update="nlog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,10 @@
namespace FreeSR.Shared
{
public abstract class Singleton<T> where T : class
{
private static readonly Lazy<T> instance = new Lazy<T>(() =>
Activator.CreateInstance(typeof(T), true) as T);
public static T Instance => instance.Value;
}
}

29
FreeSR.Shared/nlog.config Normal file
View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="coloredConsole" xsi:type="ColoredConsole" useDefaultRowHighlightingRules="false"
layout="${date:format=HH\:mm\:ss} [${level:uppercase=true}] ${logger:shortName=true}: ${message}" >
<highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" />
<highlight-row condition="level == LogLevel.Info" foregroundColor="Blue" />
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" />
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" />
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" />
</target>
<target name="infoFile" xsi:type="File"
layout="${longdate} ${pad:padding=5:inner=${level:uppercase=true}} ${logger} ${message}"
fileName="${basedir}/logs/info.log" keepFileOpen="false" encoding="iso-8859-2" />
<target name="errorFile" xsi:type="File"
layout="${longdate} ${pad:padding=5:inner=${level:uppercase=true}} ${logger} ${message}"
fileName="${basedir}/logs/error.log" keepFileOpen="false" encoding="iso-8859-2" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="coloredConsole" />
<logger name="*" minlevel="Debug" maxlevel="Info" writeTo="infoFile" />
<logger name="*" minlevel="Warn" maxlevel="Fatal" writeTo="errorFile" />
</rules>
</nlog>