2018-12-25 17:08:43 +01:00

101 lines
4.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
namespace Syroot.Worms.OnlineWorms.Server.Net
{
/// <summary>
/// Represents a class capable of dispatching received <see cref="Packet"/> instances to a corresponding method.
/// </summary>
internal class PacketHandler : IDisposable
{
// ---- FIELDS -------------------------------------------------------------------------------------------------
private static readonly Dictionary<Type, Dictionary<Type, MethodInfo>> _packetHandlers
= new Dictionary<Type, Dictionary<Type, MethodInfo>>();
private readonly TcpClient _tcpClient;
private readonly Dictionary<Type, MethodInfo> _handlerMethods;
private readonly GameConnection _connection;
private bool _disposed;
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
internal PacketHandler(TcpClient tcpClient)
{
Console.WriteLine($"{tcpClient.Client.RemoteEndPoint} connected");
_tcpClient = tcpClient;
_handlerMethods = GetHandlerMethods();
_connection = new GameConnection(_tcpClient);
}
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing).
Dispose(true);
}
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
/// <summary>
/// Starts handling packets incoming from this client and calls corresponding methods.
/// </summary>
internal void Accept()
{
Packet packet;
while ((packet = _connection.ReceivePacket()) != null)
HandlePacket(packet);
}
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
_tcpClient.Dispose();
_disposed = true;
}
}
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
private Dictionary<Type, MethodInfo> GetHandlerMethods()
{
Type classType = GetType();
if (!_packetHandlers.TryGetValue(classType, out Dictionary<Type, MethodInfo> handlerMethods))
{
handlerMethods = new Dictionary<Type, MethodInfo>();
// Find all packet handling methods which are methods accepting and returning a specific packet.
foreach (MethodInfo method in classType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance))
{
Type inPacket = method.GetParameters().FirstOrDefault()?.ParameterType;
if (typeof(Packet).IsAssignableFrom(inPacket) && typeof(Packet).IsAssignableFrom(method.ReturnType))
handlerMethods.Add(inPacket, method);
}
_packetHandlers.Add(classType, handlerMethods);
}
return handlerMethods;
}
private void HandlePacket(Packet inPacket)
{
Type packetType = inPacket.GetType();
Console.WriteLine($"{_tcpClient.Client.RemoteEndPoint} >> {packetType.Name}");
// Invoke the handler and send back any packet resulting from it.
if (_handlerMethods[packetType].Invoke(this, new[] { inPacket }) is Packet outPacket)
{
Console.WriteLine($"{_tcpClient.Client.RemoteEndPoint} << {outPacket.GetType().Name}");
_connection.SendPacket(outPacket);
}
}
}
}