# About This project is meant as a simple-enough API to create different network packet definitions, which are required to be serialized and deserialized in a high throughput environment. The primary user audience would be for server emulation for old software, specifically games, that have been abandoned or simply canceled together. Rai.PacketMediator was formerly a part of a larger side-project to create a server emulator in a more modern .NET manner than older projects for an old 2D side-scrolling mmorpg called *Wonderking*. # Caveats It does not and probably will not support ahead-of-time compilation for a long time, as to why it can be inferred by reading the "how it works" section. Furthermore, with how the current API is designed, there will be lots of reoccurring text components specifically looking at how packet handlers are implemented. # How it works This library is using DotNext to generate on startup the required methods for packet handling and passing around of packets. That is the primary reason why it will not support ahead-of-time compilation as custom IL is emitted. It will primarily create a contract that needs to be followed for packet definitions and their respective handlers. # Samples Minimal setup using NetCoreServer for TCPSession and MassTransit for Consumption While this, so to say, minimal sample is already rather long as there are lots of parts that need to be considered when bootstrapping this library. It should give a general overview on how interact and a possible solution as to how to transfer packets between a session and a handler. ```csharp // OperationCode.cs public enum OperationCode : ushort { LoginRequest = 1 } // LoginRequestPacket.cs [GamePacketIdAttribute(OperationCode.LoginRequest)] public class LoginRequestPacket : IIncomingPacket { public required string Username { get; set; } public required string Password { get; set; } public void Deserialize(byte[] data) { Username = Encoding.ASCII.GetString(data, 0, 32) Password = Encoding.ASCII.GetString(data, 32, 64) } } // GamePacketIdAttribute.cs [AttributeUsage(AttributeTargets.Class, Inherited = false)] public class GamePacketIdAttribute : PacketIdAttribute { public GamePacketIdAttribute(OperationCode code) : base(code) { } } // LoginHandler.cs public class LoginHandler : IPacketHandler { private readonly IConfiguration _configuration; private readonly ILogger _logger; private readonly DbContext _dbContext; public LoginHandler(ILogger logger, DbContext dbContext, IConfiguration configuration) { _logger = logger; _dbContext = dbContext; _configuration = configuration; } public async Task HandleAsync(LoginInfoPacket packet, AuthSession session, CancellationToken cancellationToken) { // Insert logic here _ = session.SendAsync(new byte[]{}); } } // Packetconsumer.cs public class PacketConsumer : IConsumer { private readonly PacketDistributorService _distributorService; public PacketConsumer(PacketDistributorService distributorService) { _distributorService = distributorService; } public Task Consume(ConsumeContext context) { return _distributorService.AddPacketAsync(context.Message.MessageBody, context.Message.OperationCode, context.Message.Session); } } // RawPacket.cs [MessageUrn("packets")] public class RawPacket { public RawPacket(OperationCode operationCode, Span messageBody, Guid sessionId, AuthSession session) { MessageBody = messageBody.ToArray(); SessionId = sessionId; Session = session; OperationCode = operationCode; } public OperationCode OperationCode { get; } public byte[] MessageBody { get; } public Guid SessionId { get; } public AuthSession Session { get; } } // AuthSession.cs public class AuthSession : TcpSession { private readonly PacketDistributorService _distributorService; private readonly ILogger _logger; private readonly IMediator _mediator; public AuthSession(TcpServer server, IMediator mediator, ILogger logger, PacketDistributorService distributorService) : base(server) { _mediator = mediator; _logger = logger; _distributorService = distributorService; } public Guid AccountId { get; set; } public Task SendAsync(IOutgoingPacket packet) { var opcode = _distributorService.GetOperationCodeByPacketType(packet); Span packetData = packet.Serialize(); var length = (ushort)(packetData.Length + 2); Span buffer = stackalloc byte[length]; buffer.Clear(); packetData.CopyTo(buffer[2..length]); var bytesOfOpcode = BitConverter.GetBytes((ushort)opcode); for (var i = 0; i < bytesOfOpcode.Length || i < 2; i++) { buffer[2 + i] = bytesOfOpcode[i]; } SendAsync(buffer); return Task.CompletedTask; } protected override void OnReceived(byte[] buffer, long offset, long size) { Span decryptedBuffer = stackalloc byte[(int)size]; var dataBuffer = Decrypt(buffer.AsSpan(2, (int)size - 2)); var opCode = BitConverter.ToUInt16(buffer.ToArray(), 0); var rawPacket = new RawPacket((OperationCode)opCode, dataBuffer, Id, this); _ = _mediator.Send(rawPacket); base.OnReceived(decryptedBuffer.ToArray(), offset, decryptedBuffer.Length); } } // Program.cs // create builder builder.Services.AddSingleton(provider => new PacketDistributorService( provider.GetRequiredService(), new List { Assembly.GetAssembly(typeof(OperationCode)) }.AsReadOnly(), new List { Assembly.GetAssembly(typeof(LoginHandler)) }.AsReadOnly() )); builder.Services.AddHostedService(provider => provider.GetService>() ?? throw new InvalidOperationException()); // build services ```