Snowflake/RPG.Services.Gameserver/Modules/ModuleManager.cs
2024-01-19 17:45:18 +03:00

75 lines
3 KiB
C#

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);
private static readonly ImmutableDictionary<CmdType, ReqHandler> s_handlers;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
static ModuleManager()
{
s_handlers = MapHandlers();
}
public ModuleManager(IServiceProvider serviceProvider, ILogger<ModuleManager> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
public async Task HandleAsync(PlayerSession session, CmdType cmdType, ReadOnlyMemory<byte> body)
{
if (s_handlers.TryGetValue(cmdType, out var handler))
{
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);
}
}
private static ImmutableDictionary<CmdType, ReqHandler> MapHandlers()
{
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();
}
}