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 body); private static readonly ImmutableDictionary s_handlers; private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; static ModuleManager() { s_handlers = MapHandlers(); } public ModuleManager(IServiceProvider serviceProvider, ILogger logger) { _serviceProvider = serviceProvider; _logger = logger; } public async Task HandleAsync(PlayerSession session, CmdType cmdType, ReadOnlyMemory 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 MapHandlers() { var builder = ImmutableDictionary.CreateBuilder(); IEnumerable 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 methods = type.GetMethods() .Where(method => method.GetCustomAttribute() != null); foreach (MethodInfo method in methods) { OnCommandAttribute attribute = method.GetCustomAttribute()!; ParameterExpression sessionParam = Expression.Parameter(typeof(PlayerSession)); ParameterExpression serviceProviderParam = Expression.Parameter(typeof(IServiceProvider)); ParameterExpression bodyParam = Expression.Parameter(typeof(ReadOnlyMemory)); MethodCallExpression getServiceCall = Expression.Call(getServiceMethod.MakeGenericMethod(type), serviceProviderParam); MethodCallExpression handlerCall = Expression.Call(getServiceCall, method, sessionParam, bodyParam); Expression lambda = Expression.Lambda(handlerCall, sessionParam, serviceProviderParam, bodyParam); builder.Add(attribute.CmdType, lambda.Compile()); } } return builder.ToImmutable(); } }