Snowflake/RPG.Services.Core/Network/Command/ServiceCommandHandler.cs

73 lines
2.7 KiB
C#

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<ServiceCommandType, HandlerDelegate> _handlers;
private readonly ILogger _logger;
private readonly ServiceBox _services;
public ServiceCommandHandler(ILogger<ServiceCommandHandler> logger, ServiceBox services)
{
_logger = logger;
_services = services;
_handlers = MapHandlers();
}
public async Task HandleAsync(ServiceCommand command)
{
if (_handlers.TryGetValue(command.CommandType, out HandlerDelegate? handler))
{
try
{
await handler(command);
}
catch (Exception handlingException)
{
_logger.LogError("Exception occurred while handling ServiceCommand of type {type}, trace:\n{exception}", command.CommandType, handlingException);
}
}
else
{
_logger.LogWarning("Handler for service command of type {type} not found!", command.CommandType);
}
}
protected void Send<TBody>(ServiceCommandType commandType, TBody body, RPGServiceType target) where TBody : IMessage<TBody>
{
ServiceCommand command = new(_services.CurrentType, commandType, body.ToByteArray());
byte[] buffer = GC.AllocateUninitializedArray<byte>(command.Body.Length + 7);
ServiceCommandEncoder.EncodeCommand(command, buffer);
_services.SendToService(target, buffer);
}
private ImmutableDictionary<ServiceCommandType, HandlerDelegate> MapHandlers()
{
var builder = ImmutableDictionary.CreateBuilder<ServiceCommandType, HandlerDelegate>();
IEnumerable<MethodInfo> methods = GetType().GetMethods().Where(m => m.GetCustomAttribute<ServiceCommandAttribute>() != null);
foreach (MethodInfo method in methods)
{
ServiceCommandAttribute attribute = method.GetCustomAttribute<ServiceCommandAttribute>()!;
Expression self = Expression.Convert(Expression.Constant(this), GetType());
ParameterExpression commandParameter = Expression.Parameter(typeof(ServiceCommand));
MethodCallExpression call = Expression.Call(self, method, commandParameter);
Expression<HandlerDelegate> lambda = Expression.Lambda<HandlerDelegate>(call, commandParameter);
builder.Add(attribute.CommandType, lambda.Compile());
}
return builder.ToImmutable();
}
}