continuity/Server/Services/PacketDistributorService.cs
Timothy Schenk 85aaec2371
All checks were successful
Build, Package and Push Images / preprocess (push) Successful in 2s
Build, Package and Push Images / build (push) Successful in 32s
Build, Package and Push Images / sbom-scan (push) Successful in 1m2s
Build, Package and Push Images / container-sbom-scan (push) Successful in 40s
Build, Package and Push Images / container-build (push) Successful in 2m2s
Build, Package and Push Images / sonarqube (push) Successful in 2m5s
chore: .net8 support and extended support for pipeline jobs
fix: incorrect command

fix: incorrect command

fix: incorrect command

fix: export tools path

chore: test dir

fix: test build script

fix: missing semicolon

fix: missing semicolon

fix: missing buildscript

fix: missing buildscript

chore: update build to .net7

ci: set root dir

ci: set root dir

revert: .net 8 support for benchmarks

ci: disable docker for ci

ci: disable docker for ci

ci: disable docker for ci

ci: trigger

ci: i stg

ci: let's hope its the correct one

ci:  i stg

ci: further sonarscanner setup

ci: add tools

ci: rearrange

ci: verbose output

ci: hardcoded project key

ci: test env

ci: test more env

ci: env test again

ci: test env

ci: shit

ci: final setup

ci: final setup

ci: final setup

ci: adjust buildscript

ci: nuke

ci: install java

ci: install java

refactor: loggermessages to own namespace

chore: switch to dotnet foundation editorconfig preset

ci: switch to basic gitea ci

ci: steps

ci: add missing runs-on

ci: remove unnecessary actions

ci: test attempt?

ci: add missing name for step

ci: fix missing project name reference

ci: lets try again

ci: again

ci:  again..

ci: idk at this point

ci: append path prematurely

ci: add path to bash

cli: I really don't know

ci: again....

ci: idk

ci: again....

ci: another one

ci: fix incorrect path add-path

ci: add dependency track support

ci: fix upload

ci: forgot to adjust data for action

ci: incorrect path?

ci: add version tag

ci: idk

ci: fix incorrect path for bom

ci: fix version tag

ci: disable github license resolution for now

ci: does this work

ci: another one bites the dust

chore: .net 8 and extended pipeline support

ci: again

chore: dockerignore added

chore(deps): update dependency benchmarkdotnet to v0.13.10

Signed-off-by: noreply@rainote.dev

ci: dependency track can run on any branch

ci: first attempt docker image

ci: again

ci: some fixes

ci: idk

ci: does this help

ci: idk

ci: another one

ci:  forgot

ci: downgrade qemu setup

ci: downgrade..

ci: v1 includes docker

ci: rearrange

ci: idk what to do with this

ci: let's try cat

ci: alt

ci: depressing

ci: yikes

ci: ah come on

ci: let's try new version again

ci: again

ci: another one

ci: another one

ci: confusion

ci: try single

ci: aaaa

ci: one more time

ci: again

main (#69)

Reviewed-on: #69
Co-authored-by: Timothy Schenk <admin@rainote.dev>
Co-committed-by: Timothy Schenk <admin@rainote.dev>

chore: ci jobs expanded & .net 8 support

ci: branch name sanitization for docker

ci: another attempt

ci: forgot actor

chore: remove nuke remnants

chore(deps): update dependency dotnet-sdk to v7.0.403

Signed-off-by: noreply@rainote.dev

refactor: shared data into library

refactor: rewrite for packethandler/id map

chore: logging

chore: Sonar Warnings

chore: upgrade to .net8 image

chore: fetch all required information for characters

chore: upgrade to .net 8

ci: install .net 7.0 alongside 8.0 for sbom

fix: incorrect job

ci: let's try arm64 again

ci: adjust project for .net 8 support

chore: adjustments to composefile

chore: analyzer setup

chore: analyzer and project settings updated

chore: ci jobs expanded & .net 8 support
2023-11-08 10:13:04 +01:00

157 lines
6.2 KiB
C#

using Server.LoggerMessages;
using Wonderking.Packets;
namespace Server.Services;
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Reflection;
using DotNext.Collections.Generic;
using MassTransit.Internals;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.VisualBasic.CompilerServices;
using Newtonsoft.Json;
using PacketHandlers;
using Packets;
using static DotNext.Linq.Expressions.ExpressionBuilder;
using static DotNext.Metaprogramming.CodeGenerator;
public class PacketDistributorService : IHostedService
{
private readonly ConcurrentQueue<RawPacket> _concurrentQueue;
private readonly
ImmutableDictionary<OperationCode,
Func<byte[], IPacket>> _deserializationMap;
private readonly ILogger<PacketDistributorService> _logger;
private readonly ConcurrentDictionary<OperationCode, object> _packetHandlersInstantiation;
public PacketDistributorService(ILogger<PacketDistributorService> logger, IServiceProvider serviceProvider)
{
this._concurrentQueue = new ConcurrentQueue<RawPacket>();
this._logger = logger;
var tempDeserializationMap =
new Dictionary<OperationCode, Func<byte[], IPacket>>();
var executingAssembly = Assembly.GetExecutingAssembly();
var packetsTypes = this.GetPacketsWithId(executingAssembly);
var packetHandlers = this.GetAllPacketHandlersWithId(executingAssembly);
this._packetHandlersInstantiation = new ConcurrentDictionary<OperationCode, object>();
packetHandlers.ForEach(x =>
{
var packetHandler =
ActivatorUtilities.GetServiceOrCreateInstance(serviceProvider,
x.Value);
this._packetHandlersInstantiation.TryAdd(x.Key, packetHandler);
});
foreach (var packetsType in packetsTypes)
{
var lambda = Lambda<Func<byte[], IPacket>>(fun =>
{
var argPacketData = fun[0];
var newPacket = packetsType.Value.New();
var packetVariable = DeclareVariable(packetsType.Value, "packet");
Assign(packetVariable, newPacket);
Call(packetVariable, nameof(IPacket.Deserialize), argPacketData);
Return(packetVariable);
}).Compile();
logger.PacketCreationFunctionCreated(packetsType.Key);
tempDeserializationMap.Add(packetsType.Key, lambda);
}
this._deserializationMap = tempDeserializationMap.ToImmutableDictionary();
}
public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
private Dictionary<OperationCode, Type> GetPacketsWithId(Assembly executingAssembly)
{
// ! : We are filtering if types that don't have an instance of the required Attribute
var packetsWithId = executingAssembly.GetTypes().AsParallel()
.Where(type => type.HasInterface(typeof(IPacket)) && type is { IsInterface: false, IsAbstract: false })
.Select(type => new { Type = type, Attribute = type.GetCustomAttribute<PacketIdAttribute>() })
.Where(item => item.Attribute is not null)
.ToDictionary(item => item.Attribute!.Code, item => item.Type);
if (packetsWithId is not { Count: 0 })
{
packetsWithId.AsParallel()
.ForAll(packet => this._logger.PacketWithIdAdded(packet.Key, packet.Value.FullName));
return packetsWithId;
}
this._logger.NoPacketsFound();
throw new IncompleteInitialization();
}
private Dictionary<OperationCode, Type> GetAllPacketHandlersWithId(Assembly assembly)
{
// ! : 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
);
if (packetHandlersWithId is not { Count: 0 })
{
packetHandlersWithId.AsParallel().ForAll(packetHandler =>
this._logger.PacketHandlerWithIdAdded(packetHandler.Key, packetHandler.Value.FullName));
return packetHandlersWithId;
}
this._logger.NoPacketHandlersFound();
throw new IncompleteInitialization();
}
public void AddPacket(RawPacket rawPacket)
{
this._concurrentQueue.Enqueue(rawPacket);
this.DequeueRawPacket();
this._logger.PacketReceived(rawPacket.OperationCode);
}
private void DequeueRawPacket()
{
if (this._concurrentQueue.TryDequeue(out var item))
{
ThreadPool.QueueUserWorkItem(this.InvokePacketHandler, item, true);
}
}
private void InvokePacketHandler(RawPacket item)
{
this._logger.PacketDequeued(item.Session.Id, item.OperationCode);
if (!this._deserializationMap.TryGetValue(item.OperationCode, out var value))
{
this._logger.PacketTypeNotFound(item.OperationCode);
return;
}
var packet = value(item.MessageBody);
this._logger.PacketData(JsonConvert.SerializeObject(packet));
this._packetHandlersInstantiation[item.OperationCode].GetType()
.GetMethod(nameof(IPacketHandler<IPacket>.HandleAsync))
?.Invoke(this._packetHandlersInstantiation[item.OperationCode], new object[] { packet, item.Session });
this._logger.PacketFinished(item.Session.Id, item.OperationCode);
}
}