mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-03-04 09:25:22 +03:00
Simplify server architecture.
This commit is contained in:
parent
677b9eb9c7
commit
bb7d633f21
@ -6,7 +6,7 @@ namespace Syroot.Worms.OnlineWorms.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a connection with an Online Worms client which replies to received packets appropriately.
|
/// Represents a connection with an Online Worms client which replies to received packets appropriately.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class Client : PacketHandler
|
internal class Client : GameConnection
|
||||||
{
|
{
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
namespace Syroot.Worms.OnlineWorms.Server.Core
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents common mathematical operations.
|
|
||||||
/// </summary>
|
|
||||||
internal static class MathFuncs
|
|
||||||
{
|
|
||||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the nearest power of two bigger than the given <paramref name="value"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to get the nearest, bigger power of two for.</param>
|
|
||||||
/// <returns>The nearest, bigger power of two.</returns>
|
|
||||||
internal static int NextPowerOfTwo(int value)
|
|
||||||
{
|
|
||||||
value--;
|
|
||||||
value |= value >> 1;
|
|
||||||
value |= value >> 2;
|
|
||||||
value |= value >> 4;
|
|
||||||
value |= value >> 8;
|
|
||||||
value |= value >> 16;
|
|
||||||
return ++value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +1,161 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Reflection;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
|
|
||||||
namespace Syroot.Worms.OnlineWorms.Server.Net
|
namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a stream of <see cref="Packet"/> instances send between a game client and server.
|
/// Represents a class capable of dispatching received <see cref="Packet"/> instances to a corresponding method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class GameConnection
|
internal class GameConnection : IDisposable
|
||||||
{
|
{
|
||||||
// A complete packet consists of the following:
|
|
||||||
// - ushort id
|
|
||||||
// - ushort dataSize
|
|
||||||
// - byte[dataSize] data
|
|
||||||
|
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private const int _maxDataSize = 2048;
|
private const int _maxDataSize = 2048;
|
||||||
|
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private readonly Stream _tcpStream;
|
private static readonly Dictionary<Type, Dictionary<Type, MethodInfo>> _connectionClassesCache
|
||||||
private readonly byte[] _receiveBuffer = new byte[_maxDataSize];
|
= new Dictionary<Type, Dictionary<Type, MethodInfo>>();
|
||||||
private readonly byte[] _sendDataBuffer = new byte[_maxDataSize];
|
|
||||||
private readonly BinaryStream _writeStream;
|
private readonly Dictionary<Type, MethodInfo> _handlers;
|
||||||
|
private readonly TcpClient _tcpClient;
|
||||||
|
private readonly byte[] _receiveBuffer;
|
||||||
|
private readonly byte[] _sendDataBuffer;
|
||||||
|
private readonly PacketStream _receiveStream;
|
||||||
|
private readonly PacketStream _sendStream;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="GameConnection"/> class, receiving and sending packets from and
|
/// Initializes a new instance of the <see cref="GameConnection"/> class, handling the given
|
||||||
/// to the given <paramref name="tcpClient"/>.
|
/// <paramref name="tcpClient"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tcpClient">The <see cref="TcpClient"/> to communicate with.</param>
|
/// <param name="tcpClient">The <see cref="TcpClient"/> to communicate with.</param>
|
||||||
internal GameConnection(TcpClient tcpClient)
|
internal GameConnection(TcpClient tcpClient)
|
||||||
{
|
{
|
||||||
_tcpStream = tcpClient.GetStream();
|
_tcpClient = tcpClient;
|
||||||
_writeStream = new BinaryStream(new MemoryStream(_sendDataBuffer, 0, _maxDataSize, true),
|
Console.WriteLine($"{_tcpClient.Client.RemoteEndPoint} connected");
|
||||||
encoding: Encoding.GetEncoding(949), stringCoding: StringCoding.Int16CharCount);
|
|
||||||
|
_handlers = GetHandlers();
|
||||||
|
_receiveBuffer = new byte[_maxDataSize];
|
||||||
|
_receiveBuffer = new byte[_maxDataSize];
|
||||||
|
_receiveStream = new PacketStream(_tcpClient.GetStream());
|
||||||
|
_sendStream = new PacketStream(new MemoryStream(_sendDataBuffer, 0, _maxDataSize, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in Dispose(bool disposing).
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a <see cref="Packet"/> read from the stream. If the packet could not be read, <see langword="null">
|
/// Starts handling packets incoming from this connection and dispatches them to corresponding methods.
|
||||||
/// is returned.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The read <see cref="Packet"/> instance or <see langword="null"> if the packet could not be read.
|
internal void Listen()
|
||||||
/// </returns>
|
{
|
||||||
internal Packet ReceivePacket()
|
Packet packet;
|
||||||
|
while ((packet = ReceivePacket()) != null)
|
||||||
|
HandlePacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_tcpClient.Dispose();
|
||||||
|
_sendStream.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private Dictionary<Type, MethodInfo> GetHandlers()
|
||||||
|
{
|
||||||
|
Type classType = GetType();
|
||||||
|
if (!_connectionClassesCache.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);
|
||||||
|
}
|
||||||
|
_connectionClassesCache.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 (_handlers[packetType].Invoke(this, new[] { inPacket }) is Packet outPacket)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{_tcpClient.Client.RemoteEndPoint} << {outPacket.GetType().Name}");
|
||||||
|
SendPacket(outPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Packet ReceivePacket()
|
||||||
{
|
{
|
||||||
// Receive the raw packet data.
|
// Receive the raw packet data.
|
||||||
ushort id;
|
ushort id;
|
||||||
ushort dataSize;
|
ushort dataSize;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
id = _tcpStream.ReadUInt16();
|
id = _receiveStream.ReadUInt16();
|
||||||
dataSize = _tcpStream.ReadUInt16();
|
dataSize = _receiveStream.ReadUInt16();
|
||||||
ReadComplete(_receiveBuffer, 0, dataSize);
|
_receiveStream.ReadAll(_receiveBuffer, 0, dataSize);
|
||||||
}
|
}
|
||||||
catch (IOException) { return null; } // The underlying socket closed.
|
catch (IOException) { return null; } // The underlying socket closed.
|
||||||
catch (ObjectDisposedException) { return null; } // The underlying stream closed.
|
catch (ObjectDisposedException) { return null; } // The underlying stream closed.
|
||||||
|
|
||||||
// Deserialize and return the packet.
|
// Deserialize and return the packet.
|
||||||
Packet packet = PacketFactory.Create(id);
|
Packet packet = PacketFactory.Create(id);
|
||||||
using (BinaryStream readStream = new BinaryStream(new MemoryStream(_receiveBuffer, 0, dataSize, false),
|
using (PacketStream readStream = new PacketStream(new MemoryStream(_receiveBuffer, 0, dataSize, false)))
|
||||||
encoding: Encoding.GetEncoding(949), stringCoding: StringCoding.Int16CharCount))
|
|
||||||
{
|
|
||||||
packet.Deserialize(readStream);
|
packet.Deserialize(readStream);
|
||||||
}
|
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private bool SendPacket(Packet packet)
|
||||||
/// Sends the given <paramref name="packet"/> and returns <see langword="true"/> if the packet was sent
|
|
||||||
/// successfully.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="packet">The <see cref="Packet"/> to send.</param>
|
|
||||||
/// <returns><see langword="true"/> when the packet was sent.</returns>
|
|
||||||
internal bool SendPacket(Packet packet)
|
|
||||||
{
|
{
|
||||||
// Serialize the raw packet data.
|
// Serialize the raw packet data.
|
||||||
_writeStream.Position = 0;
|
_sendStream.Position = 0;
|
||||||
packet.Serialize(_writeStream);
|
packet.Serialize(_sendStream);
|
||||||
ushort dataSize = (ushort)_writeStream.Position;
|
ushort dataSize = (ushort)_sendStream.Position;
|
||||||
|
|
||||||
// Send the data and return success.
|
// Send the data and return success.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_tcpStream.WriteUInt16(PacketFactory.GetID(packet));
|
_receiveStream.WriteUInt16(PacketFactory.GetID(packet));
|
||||||
_tcpStream.WriteUInt16(dataSize);
|
_receiveStream.WriteUInt16(dataSize);
|
||||||
_tcpStream.Write(_sendDataBuffer, 0, dataSize);
|
_receiveStream.Write(_sendDataBuffer, 0, dataSize);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (IOException) { return false; } // A network error appeared, and communication should end.
|
catch (IOException) { return false; } // A network error appeared, and communication should end.
|
||||||
catch (ObjectDisposedException) { return false; } // The underlying stream was most apparently closed.
|
catch (ObjectDisposedException) { return false; } // The underlying stream was most apparently closed.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads <paramref name="count"/> number of bytes to the given <paramref name="buffer"/>, starting at the
|
|
||||||
/// specified <paramref name="offset"/>, and returns the number of bytes read. Since this method ensures that
|
|
||||||
/// the requested number of bytes is always read, it always returns <paramref name="count"/>, and otherwise
|
|
||||||
/// throws an <see cref="IOException"/> if the required bytes could not be read.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">The byte array to write the read data to.</param>
|
|
||||||
/// <param name="offset">The offset into <paramref name="buffer"/> at which to start writing data.</param>
|
|
||||||
/// <param name="count">The number of bytes to read into <paramref name="buffer"/>.</param>
|
|
||||||
/// <exception cref="ArgumentException">The <paramref name="buffer"/> cannot store the requested number of
|
|
||||||
/// bytes.</exception>
|
|
||||||
/// <exception cref="IOException">The requested number of bytes could not be read.</exception>
|
|
||||||
private void ReadComplete(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
int totalRead = 0;
|
|
||||||
while (totalRead < count)
|
|
||||||
{
|
|
||||||
// Read returns 0 only when the underlying socket is closed, otherwise it blocks.
|
|
||||||
int read = _tcpStream.Read(buffer, offset + totalRead, count - totalRead);
|
|
||||||
if (read == 0)
|
|
||||||
throw new IOException("The underlying stream has closed, 0 bytes were retrieved.");
|
|
||||||
totalRead += read;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
using Syroot.BinaryData;
|
namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||||
|
|
||||||
namespace Syroot.Worms.OnlineWorms.Server.Net
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a packet with an ID specifying its contents. To allow the server to instantiate child packet classes,
|
/// Represents a packet with an ID specifying its contents. To allow the server to instantiate child packet classes,
|
||||||
@ -10,8 +8,8 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
|||||||
{
|
{
|
||||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
internal abstract void Deserialize(BinaryStream stream);
|
internal abstract void Deserialize(PacketStream stream);
|
||||||
|
|
||||||
internal abstract void Serialize(BinaryStream stream);
|
internal abstract void Serialize(PacketStream stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,100 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
106
src/Syroot.OnlineWorms.Server/Net/PacketStream.cs
Normal file
106
src/Syroot.OnlineWorms.Server/Net/PacketStream.cs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using Syroot.BinaryData;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a stream formatting data for being sent or received from <see cref="Packet"/> instances.
|
||||||
|
/// </summary>
|
||||||
|
internal class PacketStream : Stream
|
||||||
|
{
|
||||||
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static readonly Encoding _win949Encoding;
|
||||||
|
|
||||||
|
private readonly Stream _baseStream;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static PacketStream()
|
||||||
|
{
|
||||||
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
|
_win949Encoding = Encoding.GetEncoding(949);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal PacketStream(Stream baseStream)
|
||||||
|
{
|
||||||
|
_baseStream = baseStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public override bool CanRead => _baseStream.CanRead;
|
||||||
|
public override bool CanSeek => _baseStream.CanSeek;
|
||||||
|
public override bool CanWrite => _baseStream.CanWrite;
|
||||||
|
public override long Length => _baseStream.Length;
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => _baseStream.Position;
|
||||||
|
set => _baseStream.Position = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public override void Flush() => _baseStream.Flush();
|
||||||
|
public override int Read(byte[] buffer, int offset, int count) => _baseStream.Read(buffer, offset, count);
|
||||||
|
public override long Seek(long offset, SeekOrigin origin) => _baseStream.Seek(offset, origin);
|
||||||
|
public override void SetLength(long value) => _baseStream.SetLength(value);
|
||||||
|
public override void Write(byte[] buffer, int offset, int count) => _baseStream.Write(buffer, offset, count);
|
||||||
|
|
||||||
|
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
_baseStream.Dispose();
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads <paramref name="count"/> number of bytes to the given <paramref name="buffer"/>, starting at the
|
||||||
|
/// specified <paramref name="offset"/>, and returns the number of bytes read. Since this method ensures that
|
||||||
|
/// the requested number of bytes is always read, it always returns <paramref name="count"/>, and otherwise
|
||||||
|
/// throws an <see cref="IOException"/> if the required bytes could not be read.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="buffer">The byte array to write the read data to.</param>
|
||||||
|
/// <param name="offset">The offset into <paramref name="buffer"/> at which to start writing data.</param>
|
||||||
|
/// <param name="count">The number of bytes to read into <paramref name="buffer"/>.</param>
|
||||||
|
/// <exception cref="IOException">The requested number of bytes could not be read.</exception>
|
||||||
|
internal void ReadAll(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
int totalRead = 0;
|
||||||
|
while (totalRead < count)
|
||||||
|
{
|
||||||
|
// Read returns 0 only when the underlying socket is closed, otherwise it blocks.
|
||||||
|
int read = _baseStream.Read(buffer, offset + totalRead, count - totalRead);
|
||||||
|
if (read == 0)
|
||||||
|
throw new IOException("The underlying stream has closed, 0 bytes were retrieved.");
|
||||||
|
totalRead += read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal string ReadString()
|
||||||
|
{
|
||||||
|
// Strings are word prefixed and 0 termianted.
|
||||||
|
string value = _baseStream.ReadString(StringCoding.Int16CharCount);
|
||||||
|
_baseStream.Seek(1);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void WriteString(string value)
|
||||||
|
{
|
||||||
|
// Strings are word prefixed and 0 termianted.
|
||||||
|
_baseStream.WriteString(value, StringCoding.Int16CharCount, _win949Encoding);
|
||||||
|
_baseStream.WriteByte(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Syroot.BinaryData;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.OnlineWorms.Server.Net
|
namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||||
{
|
{
|
||||||
@ -11,8 +10,8 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
|||||||
{
|
{
|
||||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
internal override void Deserialize(BinaryStream stream) { }
|
internal override void Deserialize(PacketStream stream) { }
|
||||||
|
|
||||||
internal override void Serialize(BinaryStream stream) => throw new NotImplementedException();
|
internal override void Serialize(PacketStream stream) => throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Syroot.BinaryData;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.OnlineWorms.Server.Net
|
namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||||
{
|
{
|
||||||
@ -17,9 +16,9 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
|||||||
|
|
||||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
internal override void Deserialize(BinaryStream stream) => throw new NotImplementedException();
|
internal override void Deserialize(PacketStream stream) => throw new NotImplementedException();
|
||||||
|
|
||||||
internal override void Serialize(BinaryStream stream)
|
internal override void Serialize(PacketStream stream)
|
||||||
{
|
{
|
||||||
stream.WriteString(Unknown);
|
stream.WriteString(Unknown);
|
||||||
stream.WriteString(Version);
|
stream.WriteString(Version);
|
||||||
|
@ -15,9 +15,9 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
|||||||
|
|
||||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
internal override void Deserialize(BinaryStream stream) => throw new NotImplementedException();
|
internal override void Deserialize(PacketStream stream) => throw new NotImplementedException();
|
||||||
|
|
||||||
internal override void Serialize(BinaryStream stream)
|
internal override void Serialize(PacketStream stream)
|
||||||
{
|
{
|
||||||
stream.WriteBoolean(LoginResult == LoginResult.Success);
|
stream.WriteBoolean(LoginResult == LoginResult.Success);
|
||||||
stream.WriteEnum(LoginResult);
|
stream.WriteEnum(LoginResult);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
using Syroot.BinaryData;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.OnlineWorms.Server.Net
|
namespace Syroot.Worms.OnlineWorms.Server.Net
|
||||||
{
|
{
|
||||||
@ -23,13 +22,13 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
|
|||||||
|
|
||||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
internal override void Deserialize(BinaryStream stream)
|
internal override void Deserialize(PacketStream stream)
|
||||||
{
|
{
|
||||||
Data = new byte[stream.Length];
|
Data = new byte[stream.Length];
|
||||||
stream.Read(Data, 0, Data.Length);
|
stream.Read(Data, 0, Data.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void Serialize(BinaryStream stream)
|
internal override void Serialize(PacketStream stream)
|
||||||
{
|
{
|
||||||
stream.Write(Data, 0, Data.Length);
|
stream.Write(Data, 0, Data.Length);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.OnlineWorms.Server
|
namespace Syroot.Worms.OnlineWorms.Server
|
||||||
{
|
{
|
||||||
@ -8,13 +7,6 @@ namespace Syroot.Worms.OnlineWorms.Server
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
static Program()
|
|
||||||
{
|
|
||||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private static void Main(string[] args)
|
private static void Main(string[] args)
|
||||||
|
@ -29,12 +29,13 @@ namespace Syroot.Worms.OnlineWorms.Server
|
|||||||
tcpListener.Start();
|
tcpListener.Start();
|
||||||
Console.WriteLine($"Listening on port {port}...");
|
Console.WriteLine($"Listening on port {port}...");
|
||||||
|
|
||||||
|
// Continually accept clients and dispatch them to their listening thread.
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Client client = new Client(tcpListener.AcceptTcpClient(), this);
|
Client client = new Client(tcpListener.AcceptTcpClient(), this);
|
||||||
_clients.Add(client);
|
_clients.Add(client);
|
||||||
|
|
||||||
Task.Run(client.Accept).ContinueWith(_ =>
|
Task.Run(client.Listen).ContinueWith(_ =>
|
||||||
{
|
{
|
||||||
_clients.Remove(client);
|
_clients.Remove(client);
|
||||||
client.Dispose();
|
client.Dispose();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user