feat: a lot of reflection work
This commit is contained in:
parent
df3aae5d49
commit
2efbbe792c
13 changed files with 182 additions and 59 deletions
|
@ -192,9 +192,11 @@ public abstract class AesSession : TcpSession
|
||||||
Console.WriteLine("password: " + Encoding.ASCII.GetString(dataBuffer.ToArray(), 20, 32));
|
Console.WriteLine("password: " + Encoding.ASCII.GetString(dataBuffer.ToArray(), 20, 32));
|
||||||
Console.WriteLine("Full buffer: " + Encoding.ASCII.GetString(dataBuffer.ToArray()));
|
Console.WriteLine("Full buffer: " + Encoding.ASCII.GetString(dataBuffer.ToArray()));
|
||||||
|
|
||||||
Packet packet = new Packet((OperationCode)opCode, dataBuffer, clientAliveTime, buffer[0], buffer[3],
|
RawPacket rawPacket = new RawPacket((OperationCode)opCode, dataBuffer, clientAliveTime, buffer[0],
|
||||||
Id);
|
buffer[3],
|
||||||
Parallel.Invoke(() => _mediator.Send(packet));
|
Id, this);
|
||||||
|
Parallel.Invoke(() => _mediator.Send(rawPacket));
|
||||||
|
_logger.LogInformation("Connection from: {@RemoteEndpoint}", Socket.RemoteEndPoint);
|
||||||
base.OnReceived(decryptedBuffer.ToArray(), offset, decryptedBuffer.Length);
|
base.OnReceived(decryptedBuffer.ToArray(), offset, decryptedBuffer.Length);
|
||||||
}
|
}
|
||||||
catch (CryptographicException ex)
|
catch (CryptographicException ex)
|
||||||
|
|
13
Server/FieldOffsetAttribute.cs
Normal file
13
Server/FieldOffsetAttribute.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
namespace Server;
|
||||||
|
|
||||||
|
public class FieldOffsetAttribute : Attribute
|
||||||
|
{
|
||||||
|
public int Offset { get; set; }
|
||||||
|
public int Size { get; set; }
|
||||||
|
|
||||||
|
public FieldOffsetAttribute(int offset, int size)
|
||||||
|
{
|
||||||
|
Offset = offset;
|
||||||
|
Size = size;
|
||||||
|
}
|
||||||
|
}
|
5
Server/IPacket.cs
Normal file
5
Server/IPacket.cs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
namespace Server;
|
||||||
|
|
||||||
|
public interface IPacket
|
||||||
|
{
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Server;
|
namespace Server;
|
||||||
|
|
||||||
public interface IPacketHandler
|
public interface IPacketHandler<T> where T: IPacket
|
||||||
{
|
{
|
||||||
public void Process(Packet packet);
|
public void Handle(T packet);
|
||||||
}
|
}
|
|
@ -1,12 +0,0 @@
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Server;
|
|
||||||
|
|
||||||
[PacketHandler(OperationCode.LoginInfo)]
|
|
||||||
public class LoginHandler : IPacketHandler
|
|
||||||
{
|
|
||||||
public void Process(Packet packet)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Login being processed.");
|
|
||||||
}
|
|
||||||
}
|
|
18
Server/LoginInfoHandler.cs
Normal file
18
Server/LoginInfoHandler.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Server;
|
||||||
|
|
||||||
|
public class LoginHandler : IPacketHandler<LoginInfoPacket>
|
||||||
|
{
|
||||||
|
private readonly ILogger<LoginHandler> _logger;
|
||||||
|
|
||||||
|
public LoginHandler(ILogger<LoginHandler> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(LoginInfoPacket packet)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Login data: Username {Username} & Password {Password}", packet.Username, packet.Password);
|
||||||
|
}
|
||||||
|
}
|
11
Server/LoginInfoPacket.cs
Normal file
11
Server/LoginInfoPacket.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Server;
|
||||||
|
|
||||||
|
[PacketId(OperationCode.LoginInfo)]
|
||||||
|
public class LoginInfoPacket : IPacket
|
||||||
|
{
|
||||||
|
[FieldOffset(0, 20)] public string Username;
|
||||||
|
|
||||||
|
[FieldOffset(20, 31)] public string Password;
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Server;
|
namespace Server;
|
||||||
|
|
||||||
public class PacketConsumer : IConsumer<Packet>
|
public class PacketConsumer : IConsumer<RawPacket>
|
||||||
{
|
{
|
||||||
private readonly PacketDistributorService _distributorService;
|
private readonly PacketDistributorService _distributorService;
|
||||||
|
|
||||||
|
@ -11,8 +11,9 @@ public class PacketConsumer : IConsumer<Packet>
|
||||||
_distributorService = distributorService;
|
_distributorService = distributorService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Consume(ConsumeContext<Packet> context)
|
public Task Consume(ConsumeContext<RawPacket> context)
|
||||||
{
|
{
|
||||||
return _distributorService.AddPacket(context.Message);
|
_distributorService.AddPacket(context.Message);
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,35 +1,74 @@
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using MassTransit.Internals;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.VisualBasic.CompilerServices;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Server;
|
namespace Server;
|
||||||
|
|
||||||
public class PacketDistributorService : IHostedService
|
public class PacketDistributorService : IHostedService
|
||||||
{
|
{
|
||||||
private readonly ConcurrentQueue<Packet> _concurrentQueue;
|
private readonly ConcurrentQueue<RawPacket> _concurrentQueue;
|
||||||
private readonly ILogger<PacketDistributorService> _logger;
|
private readonly ILogger<PacketDistributorService> _logger;
|
||||||
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
private readonly Dictionary<OperationCode, Type> _packetsTypes;
|
||||||
private readonly Dictionary<OperationCode, IPacketHandler> _actions;
|
private readonly Dictionary<OperationCode, Type> _packetHandlers;
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
|
||||||
public PacketDistributorService(ILogger<PacketDistributorService> logger)
|
public PacketDistributorService(ILogger<PacketDistributorService> logger, IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
_concurrentQueue = new ConcurrentQueue<Packet>();
|
_concurrentQueue = new ConcurrentQueue<RawPacket>();
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_actions = new Dictionary<OperationCode, IPacketHandler>();
|
_serviceProvider = serviceProvider;
|
||||||
var packetHandlers = Assembly.GetExecutingAssembly().GetTypes().Where(t =>
|
_packetHandlers = new Dictionary<OperationCode, Type>();
|
||||||
t.GetCustomAttributes().Any(attribute => attribute.GetType() == typeof(PacketHandler)) &&
|
_packetsTypes = new Dictionary<OperationCode, Type>();
|
||||||
t.GetInterfaces().Contains(typeof(IPacketHandler)));
|
|
||||||
foreach (var type in packetHandlers)
|
var packetsWithId = Assembly.GetEntryAssembly()?.GetTypes().AsParallel()
|
||||||
|
.Where(type => type.GetCustomAttribute<PacketId>() != null && type.HasInterface(typeof(IPacket)) &&
|
||||||
|
!type.IsInterface)
|
||||||
|
//.Select(Activator.CreateInstance).Cast<IPacket>()
|
||||||
|
.ToDictionary(packet => packet.GetCustomAttribute<PacketId>()!.Code);
|
||||||
|
|
||||||
|
if (packetsWithId == null || packetsWithId.Count == 0)
|
||||||
{
|
{
|
||||||
PacketHandler packetHandler = type.GetCustomAttribute<PacketHandler>()!;
|
_logger.LogCritical("No Packets have been found");
|
||||||
_actions.Add(packetHandler.Code,
|
throw new IncompleteInitialization();
|
||||||
type.GetConstructor( Type.EmptyTypes)
|
|
||||||
.Invoke(null) as IPacketHandler);
|
|
||||||
_logger.LogInformation(
|
|
||||||
"Handler for ID \"{PacketHandlerCode}\" has been added with name \"{PacketHandlerName}\"",
|
|
||||||
packetHandler.Code, type.Name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packetsWithId.AsParallel().ForAll(packet =>
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Packet with ID: {PacketID} has been added as {PacketName}", packet.Key,
|
||||||
|
packet.Value.FullName);
|
||||||
|
});
|
||||||
|
_packetsTypes = packetsWithId;
|
||||||
|
|
||||||
|
|
||||||
|
var packetHandlersWithId = Assembly.GetEntryAssembly()?.GetTypes().Where(t =>
|
||||||
|
t is { IsClass: true, IsAbstract: false } && t
|
||||||
|
.GetInterfaces().Any(i =>
|
||||||
|
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IPacketHandler<>))).ToDictionary(type =>
|
||||||
|
type.GetInterfaces().First(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IPacketHandler<>))
|
||||||
|
.GetGenericArguments()[0].GetCustomAttribute<PacketId>().Code);
|
||||||
|
|
||||||
|
|
||||||
|
if (packetHandlersWithId == null || packetHandlersWithId.Count == 0)
|
||||||
|
{
|
||||||
|
_logger.LogCritical("No PacketHandlers have been found");
|
||||||
|
throw new IncompleteInitialization();
|
||||||
|
}
|
||||||
|
|
||||||
|
packetHandlersWithId.AsParallel().ForAll(packetHandler =>
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Packet with ID: {PacketID} has been added as {PacketName}", packetHandler.Key,
|
||||||
|
packetHandler.Value.FullName);
|
||||||
|
});
|
||||||
|
|
||||||
|
_packetHandlers = packetHandlersWithId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
@ -37,33 +76,60 @@ public class PacketDistributorService : IHostedService
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task AddPacket(Packet packet)
|
public void AddPacket(RawPacket rawPacket)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Packet with ID: {MessageOperationCode} has been received",
|
_logger.LogInformation("Packet with ID: {MessageOperationCode} has been received",
|
||||||
packet.OperationCode);
|
rawPacket.OperationCode);
|
||||||
_concurrentQueue.Enqueue(packet);
|
_concurrentQueue.Enqueue(rawPacket);
|
||||||
return Task.Run(DequeueAndProcessAsync);
|
Parallel.Invoke(DequeueAndProcessAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DequeueAndProcessAsync()
|
private async void DequeueAndProcessAsync()
|
||||||
{
|
{
|
||||||
while (!_cancellationTokenSource.IsCancellationRequested)
|
if (_concurrentQueue.TryDequeue(out var item))
|
||||||
{
|
{
|
||||||
if (_concurrentQueue.TryDequeue(out var item))
|
Parallel.Invoke(() =>
|
||||||
{
|
{
|
||||||
Parallel.Invoke(() =>
|
_logger.LogInformation("[{TempId}] Packet with ID: {MessageOperationCode} is being dequeued",
|
||||||
|
item.Session.Id, item.OperationCode);
|
||||||
|
var packetType = _packetsTypes[item.OperationCode];
|
||||||
|
var packet = Activator.CreateInstance(packetType);
|
||||||
|
packetType.GetFields().AsParallel().ForAll(field =>
|
||||||
{
|
{
|
||||||
var tempId = Guid.NewGuid();
|
var typeOfField = field.FieldType;
|
||||||
_logger.LogInformation("[{TempId}] Packet with ID: {MessageOperationCode} is being dequeued",tempId, item.OperationCode);
|
var fieldOffsetAttribute = field.GetCustomAttribute<FieldOffsetAttribute>();
|
||||||
var packetHandler = _actions[item.OperationCode];
|
object value;
|
||||||
packetHandler.Process(item);
|
_logger.LogInformation("Type of field of {fieldName}: {Name}", field.Name, typeOfField.Name);
|
||||||
_logger.LogInformation("[{TempId}] Packet with ID: {MessageOperationCode} has finished",tempId, item.OperationCode);
|
switch (typeOfField.Name)
|
||||||
|
{
|
||||||
|
case nameof(String):
|
||||||
|
{
|
||||||
|
value = Encoding.ASCII.GetString(item.MessageBody, fieldOffsetAttribute.Offset,
|
||||||
|
fieldOffsetAttribute.Size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
value = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
field.SetValue(packet, value);
|
||||||
});
|
});
|
||||||
}
|
var packetHandler =
|
||||||
else
|
ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider,
|
||||||
{
|
_packetHandlers[item.OperationCode]);
|
||||||
await Task.Delay(100); // Delay to prevent busy-waiting, can be adjusted based on needs
|
packetHandler.GetType().GetMethod("Handle")?.Invoke(packetHandler, new[] { packet });
|
||||||
}
|
_logger.LogInformation("Packet data {PacketData}", JsonConvert.SerializeObject(packet));
|
||||||
|
_logger.LogInformation("[{TempId}] Packet with ID: {MessageOperationCode} has finished",
|
||||||
|
item.Session.Id,
|
||||||
|
item.OperationCode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Task.Delay(100); // Delay to prevent busy-waiting, can be adjusted based on needs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
namespace Server;
|
namespace Server;
|
||||||
|
|
||||||
public class PacketHandler : Attribute
|
|
||||||
|
public class PacketId : Attribute
|
||||||
{
|
{
|
||||||
public readonly OperationCode Code;
|
public readonly OperationCode Code;
|
||||||
|
|
||||||
public PacketHandler(OperationCode code)
|
public PacketId(OperationCode code)
|
||||||
{
|
{
|
||||||
Code = code;
|
Code = code;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
namespace Server;
|
namespace Server;
|
||||||
|
|
||||||
[MessageUrn("packets")]
|
[MessageUrn("packets")]
|
||||||
public class Packet
|
public class RawPacket
|
||||||
{
|
{
|
||||||
public readonly OperationCode OperationCode;
|
public readonly OperationCode OperationCode;
|
||||||
public readonly byte[] MessageBody;
|
public readonly byte[] MessageBody;
|
||||||
|
@ -12,12 +12,16 @@ public class Packet
|
||||||
public readonly byte UnknownValue2;
|
public readonly byte UnknownValue2;
|
||||||
public readonly Guid SessionId;
|
public readonly Guid SessionId;
|
||||||
|
|
||||||
public Packet(OperationCode operationCode, byte[] messageBody, uint aliveTime, byte unknownValue2, byte unknownValue, Guid sessionId)
|
public readonly AesSession Session;
|
||||||
|
|
||||||
|
public RawPacket(OperationCode operationCode, byte[] messageBody, uint aliveTime, byte unknownValue2,
|
||||||
|
byte unknownValue, Guid sessionId, AesSession session)
|
||||||
{
|
{
|
||||||
MessageBody = messageBody;
|
MessageBody = messageBody;
|
||||||
UnknownValue2 = unknownValue2;
|
UnknownValue2 = unknownValue2;
|
||||||
UnknownValue = unknownValue;
|
UnknownValue = unknownValue;
|
||||||
SessionId = sessionId;
|
SessionId = sessionId;
|
||||||
|
Session = session;
|
||||||
OperationCode = operationCode;
|
OperationCode = operationCode;
|
||||||
ClientAliveTime = TimeSpan.FromSeconds(5 * aliveTime);
|
ClientAliveTime = TimeSpan.FromSeconds(5 * aliveTime);
|
||||||
}
|
}
|
|
@ -26,6 +26,7 @@
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
|
||||||
<PackageReference Include="NetCoreServer" Version="7.0.0" />
|
<PackageReference Include="NetCoreServer" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
13
Server/TextEncodingAttribute.cs
Normal file
13
Server/TextEncodingAttribute.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Server;
|
||||||
|
|
||||||
|
public class TextEncodingAttribute : Attribute
|
||||||
|
{
|
||||||
|
public Encoding Encoding { get; set; }
|
||||||
|
|
||||||
|
public TextEncodingAttribute(Encoding encoding)
|
||||||
|
{
|
||||||
|
Encoding = encoding;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue