Snowflake/RPG.Services.Gameserver/Modules/ModuleManager.cs

152 lines
6.4 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_reqHandlers;
private delegate Task GmCommandHandler(IServiceProvider provider, PlayerSession session, string[] args);
private static readonly ImmutableDictionary<string, ImmutableDictionary<string, GmCommandHandler>> s_gmCommandMap;
private static readonly IEnumerable<Type> s_moduleTypes;
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
public uint PlayerUID { get; private set; }
static ModuleManager()
{
s_moduleTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsAssignableTo(typeof(BaseModule)) && !t.IsAbstract);
s_reqHandlers = MapReqHandlers();
s_gmCommandMap = MapGmCommandHandlers();
}
public ModuleManager(IServiceProvider serviceProvider, ILogger<ModuleManager> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
public TModule Get<TModule>() where TModule : BaseModule
{
return _serviceProvider.GetRequiredService<TModule>();
}
public async Task OnLogin(uint gameUID)
{
PlayerUID = gameUID;
foreach (Type moduleType in s_moduleTypes)
{
if (_serviceProvider.GetRequiredService(moduleType) is BaseModule module)
await module.OnLogin();
}
}
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..]);
}
}
}
public async Task HandleAsync(PlayerSession session, CmdType cmdType, ReadOnlyMemory<byte> body)
{
if (s_reqHandlers.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> MapReqHandlers()
{
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();
}
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();
}
}