2024-01-19 14:45:18 +00:00
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
|
using System.Linq.Expressions;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using RPG.Network.Proto;
|
|
|
|
|
using RPG.Services.Gameserver.Modules.Attributes;
|
|
|
|
|
using RPG.Services.Gameserver.Session;
|
|
|
|
|
|
|
|
|
|
namespace RPG.Services.Gameserver.Modules;
|
|
|
|
|
internal class ModuleManager
|
|
|
|
|
{
|
|
|
|
|
private delegate Task ReqHandler(PlayerSession session, IServiceProvider serviceProvider, ReadOnlyMemory<byte> body);
|
2024-01-21 15:34:19 +00:00
|
|
|
|
private static readonly ImmutableDictionary<CmdType, ReqHandler> s_reqHandlers;
|
|
|
|
|
|
|
|
|
|
private delegate Task GmCommandHandler(IServiceProvider provider, PlayerSession session, string[] args);
|
|
|
|
|
private static readonly ImmutableDictionary<string, ImmutableDictionary<string, GmCommandHandler>> s_gmCommandMap;
|
2024-01-19 14:45:18 +00:00
|
|
|
|
|
2024-01-23 17:54:53 +00:00
|
|
|
|
private static readonly IEnumerable<Type> s_moduleTypes;
|
|
|
|
|
|
2024-01-19 14:45:18 +00:00
|
|
|
|
private readonly IServiceProvider _serviceProvider;
|
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
|
|
2024-01-25 12:14:47 +00:00
|
|
|
|
public uint PlayerUID { get; private set; }
|
|
|
|
|
|
2024-01-19 14:45:18 +00:00
|
|
|
|
static ModuleManager()
|
|
|
|
|
{
|
2024-01-23 17:54:53 +00:00
|
|
|
|
s_moduleTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsAssignableTo(typeof(BaseModule)) && !t.IsAbstract);
|
2024-01-21 15:34:19 +00:00
|
|
|
|
s_reqHandlers = MapReqHandlers();
|
|
|
|
|
s_gmCommandMap = MapGmCommandHandlers();
|
2024-01-19 14:45:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ModuleManager(IServiceProvider serviceProvider, ILogger<ModuleManager> logger)
|
|
|
|
|
{
|
|
|
|
|
_serviceProvider = serviceProvider;
|
|
|
|
|
_logger = logger;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-21 15:34:19 +00:00
|
|
|
|
public TModule Get<TModule>() where TModule : BaseModule
|
|
|
|
|
{
|
|
|
|
|
return _serviceProvider.GetRequiredService<TModule>();
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-25 12:14:47 +00:00
|
|
|
|
public async Task OnLogin(uint gameUID)
|
2024-01-23 17:54:53 +00:00
|
|
|
|
{
|
2024-01-25 12:14:47 +00:00
|
|
|
|
PlayerUID = gameUID;
|
|
|
|
|
|
2024-01-23 17:54:53 +00:00
|
|
|
|
foreach (Type moduleType in s_moduleTypes)
|
|
|
|
|
{
|
|
|
|
|
if (_serviceProvider.GetRequiredService(moduleType) is BaseModule module)
|
|
|
|
|
await module.OnLogin();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-21 15:34:19 +00:00
|
|
|
|
public async Task HandleGmCommandAsync(PlayerSession session, string[] args)
|
|
|
|
|
{
|
|
|
|
|
if (args.Length < 2) return;
|
|
|
|
|
|
|
|
|
|
if (s_gmCommandMap.TryGetValue(args[0], out var map))
|
|
|
|
|
{
|
|
|
|
|
if (map.TryGetValue(args[1], out GmCommandHandler? handler))
|
|
|
|
|
{
|
|
|
|
|
await handler(_serviceProvider, session, args[2..]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-19 14:45:18 +00:00
|
|
|
|
public async Task HandleAsync(PlayerSession session, CmdType cmdType, ReadOnlyMemory<byte> body)
|
|
|
|
|
{
|
2024-01-21 15:34:19 +00:00
|
|
|
|
if (s_reqHandlers.TryGetValue(cmdType, out var handler))
|
2024-01-19 14:45:18 +00:00
|
|
|
|
{
|
|
|
|
|
await handler(session, _serviceProvider, body);
|
|
|
|
|
_logger.LogInformation("Successfully handled command of type {cmdType}", cmdType);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_logger.LogWarning("Handler for command of type {cmdType} not defined!", cmdType);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-21 15:34:19 +00:00
|
|
|
|
private static ImmutableDictionary<CmdType, ReqHandler> MapReqHandlers()
|
2024-01-19 14:45:18 +00:00
|
|
|
|
{
|
|
|
|
|
var builder = ImmutableDictionary.CreateBuilder<CmdType, ReqHandler>();
|
|
|
|
|
|
|
|
|
|
IEnumerable<Type> types = Assembly.GetExecutingAssembly().GetTypes()
|
|
|
|
|
.Where(type => type.IsAssignableTo(typeof(BaseModule)) && !type.IsAbstract);
|
|
|
|
|
|
|
|
|
|
MethodInfo getServiceMethod = typeof(ServiceProviderServiceExtensions).GetMethod("GetRequiredService", [typeof(IServiceProvider)])!;
|
|
|
|
|
|
|
|
|
|
foreach (Type type in types)
|
|
|
|
|
{
|
|
|
|
|
IEnumerable<MethodInfo> methods = type.GetMethods()
|
|
|
|
|
.Where(method => method.GetCustomAttribute<OnCommandAttribute>() != null);
|
|
|
|
|
|
|
|
|
|
foreach (MethodInfo method in methods)
|
|
|
|
|
{
|
|
|
|
|
OnCommandAttribute attribute = method.GetCustomAttribute<OnCommandAttribute>()!;
|
|
|
|
|
|
|
|
|
|
ParameterExpression sessionParam = Expression.Parameter(typeof(PlayerSession));
|
|
|
|
|
ParameterExpression serviceProviderParam = Expression.Parameter(typeof(IServiceProvider));
|
|
|
|
|
ParameterExpression bodyParam = Expression.Parameter(typeof(ReadOnlyMemory<byte>));
|
|
|
|
|
|
|
|
|
|
MethodCallExpression getServiceCall = Expression.Call(getServiceMethod.MakeGenericMethod(type), serviceProviderParam);
|
|
|
|
|
MethodCallExpression handlerCall = Expression.Call(getServiceCall, method, sessionParam, bodyParam);
|
|
|
|
|
|
|
|
|
|
Expression<ReqHandler> lambda = Expression.Lambda<ReqHandler>(handlerCall, sessionParam, serviceProviderParam, bodyParam);
|
|
|
|
|
builder.Add(attribute.CmdType, lambda.Compile());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return builder.ToImmutable();
|
|
|
|
|
}
|
2024-01-21 15:34:19 +00:00
|
|
|
|
|
|
|
|
|
private static ImmutableDictionary<string, ImmutableDictionary<string, GmCommandHandler>> MapGmCommandHandlers()
|
|
|
|
|
{
|
|
|
|
|
var builder = ImmutableDictionary.CreateBuilder<string, ImmutableDictionary<string, GmCommandHandler>>();
|
|
|
|
|
|
|
|
|
|
IEnumerable<Type> types = Assembly.GetExecutingAssembly().GetTypes()
|
|
|
|
|
.Where(type => type.IsAssignableTo(typeof(BaseModule)) && !type.IsAbstract);
|
|
|
|
|
|
|
|
|
|
MethodInfo getServiceMethod = typeof(ServiceProviderServiceExtensions).GetMethod("GetRequiredService", [typeof(IServiceProvider)])!;
|
|
|
|
|
|
|
|
|
|
foreach (Type type in types)
|
|
|
|
|
{
|
|
|
|
|
GMAliasAttribute? aliasAttribute = type.GetCustomAttribute<GMAliasAttribute>();
|
|
|
|
|
if (aliasAttribute == null) continue;
|
|
|
|
|
|
|
|
|
|
var commands = ImmutableDictionary.CreateBuilder<string, GmCommandHandler>();
|
|
|
|
|
IEnumerable<MethodInfo> methods = type.GetMethods()
|
|
|
|
|
.Where(method => method.GetCustomAttribute<GMCommandAttribute>() != null);
|
|
|
|
|
|
|
|
|
|
foreach (MethodInfo method in methods)
|
|
|
|
|
{
|
|
|
|
|
GMCommandAttribute attribute = method.GetCustomAttribute<GMCommandAttribute>()!;
|
|
|
|
|
|
|
|
|
|
ParameterExpression serviceProviderParam = Expression.Parameter(typeof(IServiceProvider));
|
|
|
|
|
ParameterExpression sessionParam = Expression.Parameter(typeof(PlayerSession));
|
|
|
|
|
ParameterExpression argsParam = Expression.Parameter(typeof(string[]));
|
|
|
|
|
|
|
|
|
|
MethodCallExpression getServiceCall = Expression.Call(getServiceMethod.MakeGenericMethod(type), serviceProviderParam);
|
|
|
|
|
MethodCallExpression handlerCall = Expression.Call(getServiceCall, method, sessionParam, argsParam);
|
|
|
|
|
|
|
|
|
|
Expression<GmCommandHandler> lambda = Expression.Lambda<GmCommandHandler>(handlerCall, serviceProviderParam, sessionParam, argsParam);
|
|
|
|
|
commands.Add(attribute.Command, lambda.Compile());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builder.Add(aliasAttribute.Alias, commands.ToImmutable());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return builder.ToImmutable();
|
|
|
|
|
}
|
2024-01-19 14:45:18 +00:00
|
|
|
|
}
|