2023-11-21 20:37:50 +00:00
|
|
|
// Copyright (c) 2023 Timothy Schenk. Subject to the GNU AGPL Version 3 License.
|
2023-11-20 18:58:30 +00:00
|
|
|
|
2023-08-12 21:02:59 +00:00
|
|
|
using System.Collections.Concurrent;
|
2023-08-14 19:07:15 +00:00
|
|
|
using System.Collections.Immutable;
|
2023-11-22 13:55:46 +00:00
|
|
|
using System.Diagnostics;
|
2023-08-09 14:23:41 +00:00
|
|
|
using System.Reflection;
|
2024-01-29 07:39:18 +00:00
|
|
|
using Continuity.AuthServer.LoggerMessages;
|
|
|
|
using Continuity.AuthServer.PacketHandlers;
|
|
|
|
using Continuity.AuthServer.Packets;
|
2023-08-14 19:07:15 +00:00
|
|
|
using DotNext.Collections.Generic;
|
2023-11-19 16:07:28 +00:00
|
|
|
using DotNext.Linq.Expressions;
|
|
|
|
using DotNext.Metaprogramming;
|
2023-08-09 18:14:14 +00:00
|
|
|
using MassTransit.Internals;
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
2023-08-09 14:23:41 +00:00
|
|
|
using Microsoft.Extensions.Hosting;
|
|
|
|
using Microsoft.Extensions.Logging;
|
2023-08-09 18:14:14 +00:00
|
|
|
using Microsoft.VisualBasic.CompilerServices;
|
2023-08-14 20:30:35 +00:00
|
|
|
using Newtonsoft.Json;
|
2023-11-19 16:07:28 +00:00
|
|
|
using Wonderking.Packets;
|
|
|
|
|
2024-01-29 07:39:18 +00:00
|
|
|
namespace Continuity.AuthServer.Services;
|
2023-11-19 16:07:28 +00:00
|
|
|
|
|
|
|
using static CodeGenerator;
|
|
|
|
using static ExpressionBuilder;
|
2023-08-09 14:23:41 +00:00
|
|
|
|
2023-11-25 13:31:30 +00:00
|
|
|
public class PacketDistributorService : IHostedService, IDisposable
|
2023-08-09 14:23:41 +00:00
|
|
|
{
|
2023-10-27 17:47:17 +00:00
|
|
|
private readonly ConcurrentQueue<RawPacket> _concurrentQueue;
|
2023-08-14 19:07:15 +00:00
|
|
|
|
2023-11-19 16:07:28 +00:00
|
|
|
private readonly ILogger<PacketDistributorService> _logger;
|
|
|
|
private readonly IServiceProvider _serviceProvider;
|
|
|
|
|
2023-11-16 20:01:55 +00:00
|
|
|
private ImmutableDictionary<OperationCode,
|
|
|
|
Func<byte[], IPacket>> _deserializationMap;
|
2023-08-14 19:30:32 +00:00
|
|
|
|
2023-11-22 13:55:46 +00:00
|
|
|
private ConcurrentDictionary<OperationCode, IPacketHandler> _packetHandlersInstantiation;
|
|
|
|
private readonly ActivitySource _activitySource;
|
2023-08-09 14:23:41 +00:00
|
|
|
|
2023-08-09 18:14:14 +00:00
|
|
|
public PacketDistributorService(ILogger<PacketDistributorService> logger, IServiceProvider serviceProvider)
|
2023-08-09 14:23:41 +00:00
|
|
|
{
|
2023-11-19 16:07:28 +00:00
|
|
|
_concurrentQueue = new ConcurrentQueue<RawPacket>();
|
|
|
|
_logger = logger;
|
2023-11-16 20:01:55 +00:00
|
|
|
_serviceProvider = serviceProvider;
|
2023-11-22 13:55:46 +00:00
|
|
|
_activitySource = new ActivitySource(nameof(Server));
|
2023-11-16 20:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public Task StartAsync(CancellationToken cancellationToken)
|
|
|
|
{
|
2023-08-14 19:07:15 +00:00
|
|
|
var tempDeserializationMap =
|
|
|
|
new Dictionary<OperationCode, Func<byte[], IPacket>>();
|
2023-08-09 18:14:14 +00:00
|
|
|
|
2023-11-08 18:48:58 +00:00
|
|
|
var wonderkingAssembly = Assembly.GetAssembly(typeof(IPacket));
|
2023-11-19 16:07:28 +00:00
|
|
|
var packetsTypes = GetPacketsWithId(wonderkingAssembly);
|
|
|
|
var packetHandlers = GetAllPacketHandlersWithId(Assembly.GetExecutingAssembly());
|
2023-11-22 13:55:46 +00:00
|
|
|
_packetHandlersInstantiation = new ConcurrentDictionary<OperationCode, IPacketHandler>();
|
2023-08-14 19:07:15 +00:00
|
|
|
packetHandlers.ForEach(x =>
|
|
|
|
{
|
|
|
|
var packetHandler =
|
2023-11-16 20:01:55 +00:00
|
|
|
ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider,
|
2023-08-14 19:07:15 +00:00
|
|
|
x.Value);
|
2023-11-22 13:55:46 +00:00
|
|
|
_packetHandlersInstantiation.TryAdd(x.Key, packetHandler as IPacketHandler);
|
2023-08-14 19:07:15 +00:00
|
|
|
});
|
2023-08-13 06:17:20 +00:00
|
|
|
foreach (var packetsType in packetsTypes)
|
|
|
|
{
|
|
|
|
var lambda = Lambda<Func<byte[], IPacket>>(fun =>
|
|
|
|
{
|
2023-08-14 19:07:15 +00:00
|
|
|
var argPacketData = fun[0];
|
2023-08-13 06:17:20 +00:00
|
|
|
var newPacket = packetsType.Value.New();
|
|
|
|
|
|
|
|
var packetVariable = DeclareVariable(packetsType.Value, "packet");
|
|
|
|
Assign(packetVariable, newPacket);
|
2023-08-14 19:07:15 +00:00
|
|
|
Call(packetVariable, nameof(IPacket.Deserialize), argPacketData);
|
2023-08-13 06:17:20 +00:00
|
|
|
|
|
|
|
Return(packetVariable);
|
|
|
|
}).Compile();
|
2023-11-16 20:01:55 +00:00
|
|
|
_logger.PacketCreationFunctionCreated(packetsType.Key);
|
2023-08-14 19:07:15 +00:00
|
|
|
tempDeserializationMap.Add(packetsType.Key, lambda);
|
2023-08-13 06:17:20 +00:00
|
|
|
}
|
2023-08-14 19:07:15 +00:00
|
|
|
|
2023-11-19 16:07:28 +00:00
|
|
|
_deserializationMap = tempDeserializationMap.ToImmutableDictionary();
|
2023-11-16 20:01:55 +00:00
|
|
|
return Task.CompletedTask;
|
2023-08-11 09:19:43 +00:00
|
|
|
}
|
|
|
|
|
2023-11-19 16:07:28 +00:00
|
|
|
public Task StopAsync(CancellationToken cancellationToken)
|
|
|
|
{
|
|
|
|
return Task.CompletedTask;
|
|
|
|
}
|
2023-08-12 21:02:59 +00:00
|
|
|
|
2023-08-11 09:19:43 +00:00
|
|
|
private Dictionary<OperationCode, Type> GetPacketsWithId(Assembly executingAssembly)
|
|
|
|
{
|
2023-10-27 15:40:58 +00:00
|
|
|
// ! : We are filtering if types that don't have an instance of the required Attribute
|
2023-08-11 09:19:43 +00:00
|
|
|
var packetsWithId = executingAssembly.GetTypes().AsParallel()
|
2023-10-27 15:40:58 +00:00
|
|
|
.Where(type => type.HasInterface(typeof(IPacket)) && type is { IsInterface: false, IsAbstract: false })
|
2023-11-13 18:45:52 +00:00
|
|
|
.Where(type => type.Namespace?.Contains("Incoming") ?? false)
|
2023-10-27 15:40:58 +00:00
|
|
|
.Select(type => new { Type = type, Attribute = type.GetCustomAttribute<PacketIdAttribute>() })
|
|
|
|
.Where(item => item.Attribute is not null)
|
|
|
|
.ToDictionary(item => item.Attribute!.Code, item => item.Type);
|
2023-08-11 09:19:43 +00:00
|
|
|
if (packetsWithId is not { Count: 0 })
|
2023-08-09 14:23:41 +00:00
|
|
|
{
|
2023-10-27 15:40:58 +00:00
|
|
|
packetsWithId.AsParallel()
|
2023-11-19 16:07:28 +00:00
|
|
|
.ForAll(packet => _logger.PacketWithIdAdded(packet.Key, packet.Value.FullName));
|
2023-08-11 09:19:43 +00:00
|
|
|
return packetsWithId;
|
2023-08-09 14:23:41 +00:00
|
|
|
}
|
2023-08-09 18:14:14 +00:00
|
|
|
|
2023-11-19 16:07:28 +00:00
|
|
|
_logger.NoPacketsFound();
|
2023-08-11 09:19:43 +00:00
|
|
|
throw new IncompleteInitialization();
|
|
|
|
}
|
2023-08-09 18:14:14 +00:00
|
|
|
|
2023-08-11 09:19:43 +00:00
|
|
|
private Dictionary<OperationCode, Type> GetAllPacketHandlersWithId(Assembly assembly)
|
|
|
|
{
|
2023-10-27 17:47:17 +00:00
|
|
|
// ! : We are filtering if types that don't have an instance of the required Attribute
|
|
|
|
var packetHandlersWithId = assembly.GetTypes().AsParallel()
|
|
|
|
.Where(t =>
|
|
|
|
t is { IsClass: true, IsAbstract: false } && Array.Exists(t
|
|
|
|
.GetInterfaces(), i =>
|
|
|
|
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPacketHandler<>)))
|
|
|
|
.Select(type => new
|
|
|
|
{
|
|
|
|
Type = type,
|
|
|
|
PacketId = type
|
|
|
|
.GetInterfaces().First(t1 =>
|
|
|
|
t1 is { IsGenericType: true } && t1.GetGenericTypeDefinition() == typeof(IPacketHandler<>))
|
|
|
|
.GetGenericArguments()[0].GetCustomAttribute<PacketIdAttribute>()?.Code
|
|
|
|
})
|
|
|
|
.Where(x => x.PacketId is not null)
|
|
|
|
.ToDictionary(
|
|
|
|
x => x.PacketId!.Value, x => x.Type
|
|
|
|
);
|
2023-08-09 18:14:14 +00:00
|
|
|
|
2023-08-11 09:19:43 +00:00
|
|
|
if (packetHandlersWithId is not { Count: 0 })
|
2023-08-09 18:14:14 +00:00
|
|
|
{
|
2023-10-27 15:40:58 +00:00
|
|
|
packetHandlersWithId.AsParallel().ForAll(packetHandler =>
|
2023-11-19 16:07:28 +00:00
|
|
|
_logger.PacketHandlerWithIdAdded(packetHandler.Key, packetHandler.Value.FullName));
|
2023-08-11 09:19:43 +00:00
|
|
|
return packetHandlersWithId;
|
2023-08-09 18:14:14 +00:00
|
|
|
}
|
|
|
|
|
2023-11-19 16:07:28 +00:00
|
|
|
_logger.NoPacketHandlersFound();
|
2023-08-11 09:19:43 +00:00
|
|
|
throw new IncompleteInitialization();
|
2023-08-09 14:23:41 +00:00
|
|
|
}
|
|
|
|
|
2023-08-09 18:14:14 +00:00
|
|
|
public void AddPacket(RawPacket rawPacket)
|
2023-08-09 14:23:41 +00:00
|
|
|
{
|
2023-11-19 16:07:28 +00:00
|
|
|
_concurrentQueue.Enqueue(rawPacket);
|
|
|
|
DequeueRawPacket();
|
|
|
|
_logger.PacketReceived(rawPacket.OperationCode);
|
2023-08-09 14:23:41 +00:00
|
|
|
}
|
|
|
|
|
2023-08-13 20:27:24 +00:00
|
|
|
private void DequeueRawPacket()
|
2023-08-09 14:23:41 +00:00
|
|
|
{
|
2023-11-19 16:07:28 +00:00
|
|
|
if (_concurrentQueue.TryDequeue(out var item))
|
2023-08-09 14:23:41 +00:00
|
|
|
{
|
2023-11-25 13:37:54 +00:00
|
|
|
ThreadPool.QueueUserWorkItem(InvokePacketHandler, item, preferLocal: false);
|
2023-08-09 18:14:14 +00:00
|
|
|
}
|
2023-08-09 14:23:41 +00:00
|
|
|
}
|
|
|
|
|
2023-08-13 16:29:39 +00:00
|
|
|
private void InvokePacketHandler(RawPacket item)
|
2023-08-11 09:19:43 +00:00
|
|
|
{
|
2023-11-22 13:55:46 +00:00
|
|
|
IPacket packet;
|
2023-11-19 16:07:28 +00:00
|
|
|
_logger.PacketDequeued(item.Session.Id, item.OperationCode);
|
|
|
|
if (!_deserializationMap.TryGetValue(item.OperationCode, out var value))
|
2023-08-13 16:29:39 +00:00
|
|
|
{
|
2023-11-19 16:07:28 +00:00
|
|
|
_logger.PacketTypeNotFound(item.OperationCode);
|
2023-08-13 16:29:39 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-11-22 13:55:46 +00:00
|
|
|
using (var packetParsingActivity = _activitySource.StartActivity("PacketParsing"))
|
|
|
|
{
|
|
|
|
packetParsingActivity?.SetTag("PacketId", item.OperationCode);
|
|
|
|
packet = value(item.MessageBody);
|
|
|
|
}
|
2023-08-14 19:07:15 +00:00
|
|
|
|
2023-11-22 13:55:46 +00:00
|
|
|
using (var packetHandlerActivity = _activitySource.StartActivity("PacketHandler"))
|
|
|
|
{
|
|
|
|
packetHandlerActivity?.SetTag("PacketId", item.OperationCode);
|
|
|
|
_ = _packetHandlersInstantiation[item.OperationCode].TryHandleAsync(packet, item.Session);
|
|
|
|
}
|
|
|
|
|
|
|
|
_logger.PacketData(JsonConvert.SerializeObject(packet));
|
2023-11-19 16:07:28 +00:00
|
|
|
_logger.PacketFinished(item.Session.Id, item.OperationCode);
|
2023-08-11 09:19:43 +00:00
|
|
|
}
|
2023-11-25 13:31:30 +00:00
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
GC.SuppressFinalize(this);
|
|
|
|
_activitySource.Dispose();
|
|
|
|
}
|
2023-08-11 09:31:30 +00:00
|
|
|
}
|