using System.Collections.Immutable; using System.Linq.Expressions; using System.Reflection; using Google.Protobuf; using Microsoft.Extensions.Logging; using RPG.Network.Proto; using RPG.Services.Core.Network.Attributes; namespace RPG.Services.Core.Network.Command; public abstract class ServiceCommandHandler { private delegate Task HandlerDelegate(ServiceCommand command); private readonly ImmutableDictionary _handlers; private readonly ILogger _logger; private readonly ServiceBox _services; public ServiceCommandHandler(ILogger logger, ServiceBox services) { _logger = logger; _services = services; _handlers = MapHandlers(); } public async Task HandleAsync(ServiceCommand command) { if (_handlers.TryGetValue(command.CommandType, out HandlerDelegate? handler)) { await handler(command); } else { _logger.LogWarning("Handler for service command of type {type} not found!", command.CommandType); } } protected void Send(ServiceCommandType commandType, TBody body, RPGServiceType target) where TBody : IMessage { ServiceCommand command = new(_services.CurrentType, commandType, body.ToByteArray()); byte[] buffer = GC.AllocateUninitializedArray(command.Body.Length + 7); ServiceCommandEncoder.EncodeCommand(command, buffer); _services.SendToService(target, buffer); } private ImmutableDictionary MapHandlers() { var builder = ImmutableDictionary.CreateBuilder(); IEnumerable methods = GetType().GetMethods().Where(m => m.GetCustomAttribute() != null); foreach (MethodInfo method in methods) { ServiceCommandAttribute attribute = method.GetCustomAttribute()!; Expression self = Expression.Convert(Expression.Constant(this), GetType()); ParameterExpression commandParameter = Expression.Parameter(typeof(ServiceCommand)); MethodCallExpression call = Expression.Call(self, method, commandParameter); Expression lambda = Expression.Lambda(call, commandParameter); builder.Add(attribute.CommandType, lambda.Compile()); } return builder.ToImmutable(); } }