mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-01-13 07:18:00 +03:00
Implement WWPA packet format (WIP).
This commit is contained in:
parent
c0cab3ecf5
commit
efc1a9fcff
@ -37,8 +37,8 @@ developed by Team17:
|
||||
|
||||
## Available tools
|
||||
|
||||
* Mgame Launcher: Creates a fake launch configuration to start OW or WWPA clients with.
|
||||
* Mgame Server: Simulates a server communicating with OW or WWPA clients (WIP).
|
||||
* Syroot.Worms.Mgame.Launcher: Creates a fake launch configuration to start OW or WWPA clients with.
|
||||
* Syroot.Worms.Mgame.GameServer: Simulates a server communicating with OW or WWPA clients (WIP).
|
||||
|
||||
## Support
|
||||
The libraries are available as a [NuGet package](https://www.nuget.org/profiles/Syroot).
|
||||
|
@ -1,16 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using Syroot.Worms.Mgame.Server.Net;
|
||||
using Syroot.Worms.Mgame.GameServer.Core;
|
||||
using Syroot.Worms.Mgame.GameServer.Packets;
|
||||
using Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server
|
||||
namespace Syroot.Worms.Mgame.GameServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a connection with an Online Worms client which replies to received packets appropriately.
|
||||
/// </summary>
|
||||
internal class Client : GameConnection
|
||||
internal class Client : AppConnection
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -184,12 +187,14 @@ namespace Syroot.Worms.Mgame.Server
|
||||
|
||||
protected override void OnPrePacketHandle(Packet packet)
|
||||
{
|
||||
_server.Log.Write(LogCategory.Client, $"{TcpClient.Client.RemoteEndPoint} >> {packet}");
|
||||
_server.Log.Write(LogCategory.Client,
|
||||
$"{TcpClient.Client.RemoteEndPoint} >> {packet}{Environment.NewLine}{ObjectDumper.Dump(packet)}");
|
||||
}
|
||||
|
||||
protected override void OnPrePacketSend(Packet packet)
|
||||
{
|
||||
_server.Log.Write(LogCategory.Server, $"{TcpClient.Client.RemoteEndPoint} << {packet}");
|
||||
_server.Log.Write(LogCategory.Server,
|
||||
$"{TcpClient.Client.RemoteEndPoint} << {packet}{Environment.NewLine}{ObjectDumper.Dump(packet)}");
|
||||
if (_server.Config.SendDelay > 0)
|
||||
Thread.Sleep(_server.Config.SendDelay);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server
|
||||
namespace Syroot.Worms.Mgame.GameServer
|
||||
{
|
||||
internal class Config
|
||||
{
|
@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Syroot.Worms.Mgame.GameServer.Core.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a stream which ensures to read the specified number of bytes and does not return prematurely.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the wrapped <see cref="Stream"/>.</typeparam>
|
||||
internal class SafeWrapperStream<T> : WrapperStream<T>
|
||||
where T : Stream
|
||||
{
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
internal SafeWrapperStream(T baseStream) : base(baseStream) { }
|
||||
|
||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||
|
||||
/// <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>
|
||||
/// <returns>The number of bytes requested.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int size)
|
||||
{
|
||||
int totalRead = 0;
|
||||
while (totalRead < size)
|
||||
{
|
||||
// For network streams, read returns 0 only when the underlying socket is closed, otherwise it blocks.
|
||||
int read = BaseStream.Read(buffer, offset + totalRead, size - totalRead);
|
||||
if (read == 0)
|
||||
throw new IOException("The underlying stream closed, 0 bytes were retrieved.");
|
||||
totalRead += read;
|
||||
}
|
||||
return totalRead;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the required number of bytes to fill the given <paramref name="buffer"/>, and returns the number of
|
||||
/// bytes read. Since this method ensures that the requested number of bytes is always read, it always returns
|
||||
/// the length of the buffer, and otherwise throws an <see cref="IOException"/> if the required bytes could not
|
||||
/// be read.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The <see cref="Span{byte}"/> to write the read data to.</param>
|
||||
/// <exception cref="IOException">The requested number of bytes could not be read.</exception>
|
||||
/// <returns>The number of bytes requested.</returns>
|
||||
public override int Read(Span<byte> buffer)
|
||||
{
|
||||
int totalRead = 0;
|
||||
while (totalRead < buffer.Length)
|
||||
{
|
||||
// For network streams, read returns 0 only when the underlying socket is closed, otherwise it blocks.
|
||||
int read = BaseStream.Read(buffer.Slice(totalRead));
|
||||
if (read == 0)
|
||||
throw new IOException("The underlying stream closed, 0 bytes were retrieved.");
|
||||
totalRead += read;
|
||||
}
|
||||
return totalRead;
|
||||
}
|
||||
}
|
||||
}
|
44
src/Syroot.Worms.Mgame.GameServer/Core/IO/WrapperStream.cs
Normal file
44
src/Syroot.Worms.Mgame.GameServer/Core/IO/WrapperStream.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System.IO;
|
||||
|
||||
namespace Syroot.Worms.Mgame.GameServer.Core.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="Stream"/> wrapping around another, implementing default methods.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the wrapped <see cref="Stream"/>.</typeparam>
|
||||
internal class WrapperStream<T> : Stream
|
||||
where T : Stream
|
||||
{
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
internal WrapperStream(T 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying stream of type <see cref="T"/>.
|
||||
/// </summary>
|
||||
internal T BaseStream { get; }
|
||||
|
||||
// ---- 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);
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
using System.Net.Sockets;
|
||||
using Syroot.Worms.Mgame.GameServer.Core.IO;
|
||||
|
||||
namespace Syroot.Worms.Mgame.GameServer.Core.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a <see cref="NetworkStream"/> which ensures to read the specified number of bytes and does not return
|
||||
/// prematurely.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the wrapped <see cref="Stream"/>.</typeparam>
|
||||
internal class SafeNetworkStream : SafeWrapperStream<NetworkStream>
|
||||
{
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
internal SafeNetworkStream(NetworkStream baseStream) : base(baseStream) { }
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Syroot.Worms.Mgame.GameServer.Core.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents extension methods for <see cref="TcpClient"/> instances.
|
||||
/// </summary>
|
||||
internal static class TcpClientExtensions
|
||||
{
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="SafeNetworkStream"/> used to send and receive data.
|
||||
/// </summary>
|
||||
/// <param name="self">The extended <see cref="TcpClient"/> instance.</param>
|
||||
/// <returns>The <see cref="SafeNetworkStream"/> instance.</returns>
|
||||
internal static SafeNetworkStream GetSafeStream(this TcpClient self)
|
||||
{
|
||||
return new SafeNetworkStream(self.GetStream());
|
||||
}
|
||||
}
|
||||
}
|
@ -6,35 +6,23 @@ using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a packet with an ID specifying its contents. To allow the server to instantiate child packet classes,
|
||||
/// decorate it with the <see cref="PacketAttribute"/>.
|
||||
/// Represents tools for <see cref="Object"/> instances.
|
||||
/// </summary>
|
||||
internal abstract class Packet
|
||||
internal static class ObjectDumper
|
||||
{
|
||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
#if DEBUG
|
||||
return String.Concat(GetType().Name, DumpClass(this, 1));
|
||||
#else
|
||||
return GetType().Name;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal abstract void Deserialize(PacketStream stream);
|
||||
|
||||
internal abstract void Serialize(PacketStream stream);
|
||||
internal static string Dump(object obj)
|
||||
{
|
||||
return Dump(obj, 0);
|
||||
}
|
||||
|
||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||
|
||||
#if DEBUG
|
||||
private string DumpClass(object obj, int indentLevel)
|
||||
private static string Dump(object obj, int indentLevel)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
string indent = new string(' ', indentLevel * 4);
|
||||
@ -54,7 +42,7 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
sb.Append(indent);
|
||||
sb.Append("[ ");
|
||||
foreach (object element in iEnumerableValue)
|
||||
sb.Append(DumpClass(element, indentLevel)).Append(" ");
|
||||
sb.Append(Dump(element, indentLevel)).Append(" ");
|
||||
sb.Append("]");
|
||||
break;
|
||||
case Byte byteValue:
|
||||
@ -98,7 +86,7 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
sb.AppendLine();
|
||||
sb.Append((indent + property.Name).PadRight(20));
|
||||
sb.Append(" ");
|
||||
sb.Append(DumpClass(property.GetValue(obj), indentLevel + 1));
|
||||
sb.Append(Dump(property.GetValue(obj), indentLevel + 1));
|
||||
}
|
||||
sb.AppendLine();
|
||||
}
|
||||
@ -106,6 +94,5 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
}
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Syroot.Worms.Mgame.GameServer.Core.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an instance dispatching parameters of type <typeparamref name="T"/> to methods accepting them.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the parameter to dispatch to methods.</typeparam>
|
||||
internal class MethodHandler<T>
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private static readonly Dictionary<Type, Dictionary<Type, MethodInfo>> _classCache;
|
||||
|
||||
private readonly object _instance;
|
||||
private readonly Dictionary<Type, MethodInfo> _handlers;
|
||||
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
static MethodHandler()
|
||||
{
|
||||
_classCache = new Dictionary<Type, Dictionary<Type, MethodInfo>>();
|
||||
}
|
||||
|
||||
internal MethodHandler(object instance)
|
||||
{
|
||||
_instance = instance;
|
||||
_handlers = GetHandlers(_instance);
|
||||
}
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal void Handle(T parameter)
|
||||
{
|
||||
Type parameterType = parameter.GetType();
|
||||
if (!_handlers.TryGetValue(parameterType, out MethodInfo method))
|
||||
throw new ArgumentException($"No method handles type {parameterType}.");
|
||||
method.Invoke(_instance, new object[] { parameter });
|
||||
}
|
||||
|
||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||
|
||||
private Dictionary<Type, MethodInfo> GetHandlers(object instance)
|
||||
{
|
||||
Type classType = instance.GetType();
|
||||
if (!_classCache.TryGetValue(classType, out Dictionary<Type, MethodInfo> handlerMethods))
|
||||
{
|
||||
// Find all methods which accept a specific parameter and return nothing.
|
||||
handlerMethods = new Dictionary<Type, MethodInfo>();
|
||||
foreach (MethodInfo method in classType.GetMethods(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
Type parameterType = method.GetParameters().FirstOrDefault()?.ParameterType;
|
||||
if (parameterType == null)
|
||||
continue;
|
||||
if (handlerMethods.ContainsKey(parameterType))
|
||||
throw new InvalidOperationException($"Parameter {parameterType} handled by multiple methods.");
|
||||
if (method.ReturnType == typeof(void) && typeof(T).IsAssignableFrom(parameterType))
|
||||
handlerMethods.Add(parameterType, method);
|
||||
}
|
||||
_classCache.Add(classType, handlerMethods);
|
||||
}
|
||||
return handlerMethods;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server
|
||||
namespace Syroot.Worms.Mgame.GameServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents simplistic textual logging.
|
270
src/Syroot.Worms.Mgame.GameServer/Packets/AppConnection.cs
Normal file
270
src/Syroot.Worms.Mgame.GameServer/Packets/AppConnection.cs
Normal file
@ -0,0 +1,270 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using Syroot.BinaryData;
|
||||
using Syroot.Worms.Mgame.GameServer.Core.Net;
|
||||
using Syroot.Worms.Mgame.GameServer.Core.Reflection;
|
||||
using Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua;
|
||||
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a class capable of dispatching received <see cref="Packet"/> instances to a corresponding method.
|
||||
/// </summary>
|
||||
internal class AppConnection : IDisposable
|
||||
{
|
||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||
|
||||
private const int _maxDataSize = 2048;
|
||||
private const ushort _owTagStartChannel = 0xFFFE;
|
||||
private const ushort _owTagEndChannel = 0xFEFF;
|
||||
private const ushort _wwpaTagStart = 0x2E9E;
|
||||
private const ushort _wwpaTagEnd = 0x7F3F;
|
||||
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private readonly MethodHandler<Packet> _methodHandler;
|
||||
private readonly SafeNetworkStream _tcpStream;
|
||||
private readonly PacketDataStream _packetDataStream;
|
||||
private bool _disposed;
|
||||
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AppConnection"/> class, handling the given
|
||||
/// <paramref name="tcpClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="tcpClient">The <see cref="System.Net.Sockets.TcpClient"/> to communicate with.</param>
|
||||
internal AppConnection(TcpClient tcpClient)
|
||||
{
|
||||
_methodHandler = new MethodHandler<Packet>(this);
|
||||
TcpClient = tcpClient;
|
||||
_tcpStream = TcpClient.GetSafeStream();
|
||||
_packetDataStream = new PacketDataStream();
|
||||
}
|
||||
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="TcpClient"/> with which the connection communicates.
|
||||
/// </summary>
|
||||
internal TcpClient TcpClient { get; }
|
||||
|
||||
// ---- 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 connection and dispatches them to corresponding methods.
|
||||
/// </summary>
|
||||
internal void Listen()
|
||||
{
|
||||
Packet packet;
|
||||
while ((packet = ReceivePacket()) != null)
|
||||
{
|
||||
OnPrePacketHandle(packet);
|
||||
_methodHandler.Handle(packet);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
TcpClient.Close();
|
||||
_packetDataStream.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnPrePacketHandle(Packet packet) { }
|
||||
|
||||
protected virtual void OnPrePacketSend(Packet packet) { }
|
||||
|
||||
protected bool SendPacket(Packet packet)
|
||||
{
|
||||
OnPrePacketSend(packet);
|
||||
|
||||
// Serialize the packet data to retrieve its size.
|
||||
_packetDataStream.Position = 0;
|
||||
packet.SaveData(_packetDataStream);
|
||||
|
||||
// Send the total packet data including head and tail.
|
||||
try
|
||||
{
|
||||
PacketAttribute attribute = PacketFactory.GetAttribute(packet);
|
||||
switch (attribute.Format)
|
||||
{
|
||||
case PacketFormat.OWChannel:
|
||||
SendOWChannelPacket(attribute.ID);
|
||||
break;
|
||||
case PacketFormat.OWServer:
|
||||
SendOWServerPacket(attribute.ID);
|
||||
break;
|
||||
case PacketFormat.Wwpa:
|
||||
SaveWwpaPacket(packet);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Cannot send unknown packet format.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (IOException) { return false; } // A network error appeared, and communication should end.
|
||||
catch (ObjectDisposedException) { return false; } // The underlying stream was most apparently closed.
|
||||
}
|
||||
|
||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||
|
||||
private Packet ReceivePacket()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Handle specific packet formats.
|
||||
ushort tag = _tcpStream.ReadUInt16();
|
||||
switch (tag)
|
||||
{
|
||||
case _owTagStartChannel:
|
||||
return ReceiveOWChannelPacket();
|
||||
case _wwpaTagStart:
|
||||
return ReceiveWwpaPacket();
|
||||
default:
|
||||
return ReceiveOWServerPacket(tag);
|
||||
}
|
||||
}
|
||||
catch (IOException) { return null; } // The underlying socket closed.
|
||||
catch (ObjectDisposedException) { return null; } // The underlying stream closed.
|
||||
}
|
||||
|
||||
private Packet ReceiveOWChannelPacket()
|
||||
{
|
||||
// Read head.
|
||||
if (_tcpStream.Read1Byte() != 1)
|
||||
throw new IOException("Invalid OW channel packet start tag.");
|
||||
int dataSize = _tcpStream.ReadUInt16();
|
||||
int id = _tcpStream.Read1Byte();
|
||||
// Read data.
|
||||
byte[] data = _tcpStream.ReadBytes(dataSize);
|
||||
// Read tail.
|
||||
ushort endTag = _tcpStream.ReadUInt16();
|
||||
if (endTag != _owTagEndChannel)
|
||||
throw new IOException("Invalid OW channel packet end tag.");
|
||||
|
||||
// Instantiate, deserialize, and return packet.
|
||||
Packet packet = PacketFactory.Create(PacketFormat.OWChannel, id);
|
||||
using (PacketDataStream stream = new PacketDataStream(data))
|
||||
packet.LoadData(stream);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private Packet ReceiveOWServerPacket(int tag)
|
||||
{
|
||||
// Read head.
|
||||
int dataSize = _tcpStream.ReadUInt16();
|
||||
// Read data.
|
||||
byte[] data = _tcpStream.ReadBytes(dataSize);
|
||||
|
||||
// Instantiate, deserialize, and return packet.
|
||||
Packet packet = PacketFactory.Create(PacketFormat.OWServer, tag);
|
||||
using (PacketDataStream stream = new PacketDataStream(data))
|
||||
packet.LoadData(stream);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private Packet ReceiveWwpaPacket()
|
||||
{
|
||||
// Read head.
|
||||
if (!_tcpStream.ReadBoolean())
|
||||
throw new PacketException("Unexpected WWPA packet head2.");
|
||||
if (_tcpStream.ReadBoolean())
|
||||
throw new PacketException("Unexpected WWPA packet head3.");
|
||||
if (!_tcpStream.ReadBoolean())
|
||||
throw new PacketException("Unexpected WWPA packet bIsCompressed.");
|
||||
int decompressedSize = _tcpStream.ReadUInt16();
|
||||
int compressedSize = _tcpStream.ReadInt32();
|
||||
int idxPacket = _tcpStream.ReadInt32();
|
||||
// Read data.
|
||||
byte[] compressedData = _tcpStream.ReadBytes(compressedSize);
|
||||
// Read tail.
|
||||
if (_tcpStream.ReadInt32() != idxPacket)
|
||||
throw new PacketException("Invalid WWPA packet index.");
|
||||
if (_tcpStream.ReadUInt16() != _wwpaTagEnd)
|
||||
throw new PacketException("Invalid WWPA packet end tag.");
|
||||
|
||||
// Instantiate, deserialize, and return packet.
|
||||
int id = 0;
|
||||
Packet packet = PacketFactory.Create(PacketFormat.Wwpa, id);
|
||||
byte[] decompressedData = PacketCompression.Decompress(compressedData);
|
||||
using (PacketDataStream stream = new PacketDataStream(decompressedData))
|
||||
packet.LoadData(stream);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private void SendOWChannelPacket(int id)
|
||||
{
|
||||
// Retrieve data size. Must have at least 1 byte.
|
||||
if (_packetDataStream.Position == 0)
|
||||
_packetDataStream.WriteByte(0);
|
||||
ushort dataSize = (ushort)_packetDataStream.Position;
|
||||
|
||||
// Send head.
|
||||
_tcpStream.WriteUInt16(_owTagStartChannel);
|
||||
_tcpStream.WriteByte(1);
|
||||
_tcpStream.WriteUInt16(dataSize);
|
||||
_tcpStream.WriteByte((byte)id);
|
||||
|
||||
// Send data.
|
||||
_tcpStream.Write(_packetDataStream.GetSpan());
|
||||
|
||||
// Send tail.
|
||||
_tcpStream.WriteUInt16(_owTagEndChannel);
|
||||
}
|
||||
|
||||
private void SendOWServerPacket(int id)
|
||||
{
|
||||
// Retrieve data size.
|
||||
ushort dataSize = (ushort)_packetDataStream.Position;
|
||||
|
||||
// Send head.
|
||||
_tcpStream.WriteUInt16((ushort)id);
|
||||
_tcpStream.WriteUInt16(dataSize);
|
||||
|
||||
// Write data.
|
||||
_tcpStream.Write(_packetDataStream.GetSpan());
|
||||
}
|
||||
|
||||
private void SaveWwpaPacket(Packet packet)
|
||||
{
|
||||
// Retrieve (decompressed) data size.
|
||||
ReadOnlySpan<byte> decompressedData = _packetDataStream.GetSpan();
|
||||
ReadOnlySpan<byte> compressedData = PacketCompression.Compress(decompressedData);
|
||||
|
||||
// Send head.
|
||||
_tcpStream.WriteUInt16(_wwpaTagStart);
|
||||
_tcpStream.WriteBoolean(true);
|
||||
_tcpStream.WriteBoolean(false);
|
||||
_tcpStream.WriteBoolean(true); // isCompressed
|
||||
_tcpStream.WriteUInt16((ushort)decompressedData.Length);
|
||||
_tcpStream.WriteUInt32((uint)compressedData.Length);
|
||||
_tcpStream.WriteInt32(1); // Apparently only needs to be same as in tail.
|
||||
|
||||
// Send data.
|
||||
_tcpStream.Write(compressedData);
|
||||
|
||||
// Send tail.
|
||||
_tcpStream.WriteInt32(1); // Apparently only needs to be same as in head.
|
||||
_tcpStream.WriteUInt16(_wwpaTagEnd);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,12 +3,12 @@ using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the client request for a <see cref="ChannelConnectReply"/>.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Channel, 0x10)]
|
||||
[Packet(PacketFormat.OWChannel, 0x10)]
|
||||
internal class ChannelConnectQuery : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -25,7 +25,7 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream)
|
||||
internal override void LoadData(PacketDataStream stream)
|
||||
{
|
||||
Players = new List<ChannelConnectPlayerCredentials>
|
||||
{
|
||||
@ -51,7 +51,7 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
UnknownB = stream.Read1Byte();
|
||||
}
|
||||
|
||||
internal override void Serialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void SaveData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class ChannelConnectPlayerCredentials
|
@ -2,12 +2,12 @@
|
||||
using Syroot.BinaryData;
|
||||
using Syroot.Worms.Core.IO;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the server response to a <see cref="ChannelConnectQuery"/>.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Channel, 0x11)]
|
||||
[Packet(PacketFormat.OWChannel, 0x11)]
|
||||
internal class ChannelConnectReply : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -18,9 +18,9 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
|
||||
internal override void Serialize(PacketStream stream)
|
||||
internal override void SaveData(PacketDataStream stream)
|
||||
{
|
||||
if (Result == ChannelConnectResult.Success)
|
||||
{
|
@ -1,18 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an additional server response to a <see cref="ChannelTop20Query"/>, causing the client to switch
|
||||
/// to the channel screen (game lobby).
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Channel, 0x44)]
|
||||
[Packet(PacketFormat.OWChannel, 0x44)]
|
||||
internal class ChannelEnterFinishReply : Packet
|
||||
{
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
|
||||
internal override void Serialize(PacketStream stream) { }
|
||||
internal override void SaveData(PacketDataStream stream) { }
|
||||
}
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the client request for a <see cref="ChannelEnterReply"/>.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Server, 0x8034)]
|
||||
[Packet(PacketFormat.OWServer, 0x8034)]
|
||||
internal class ChannelEnterQuery : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -18,12 +17,12 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream)
|
||||
internal override void LoadData(PacketDataStream stream)
|
||||
{
|
||||
PlayerID = stream.ReadString();
|
||||
ChannelEndPoint = new IPEndPoint(IPAddress.Parse(stream.ReadString()), stream.ReadUInt16());
|
||||
}
|
||||
|
||||
internal override void Serialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void SaveData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the server response to a <see cref="ChannelEnterQuery"/>.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Server, 0x8035)]
|
||||
[Packet(PacketFormat.OWServer, 0x8035)]
|
||||
internal class ChannelEnterReply : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -19,9 +18,9 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
|
||||
internal override void Serialize(PacketStream stream)
|
||||
internal override void SaveData(PacketDataStream stream)
|
||||
{
|
||||
stream.WriteString(EndPoint.Address.ToString());
|
||||
stream.WriteUInt16((ushort)EndPoint.Port);
|
@ -4,12 +4,12 @@ using System.Drawing;
|
||||
using System.Net;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an additional server response to a <see cref="LoginQuery"/>, providing available server channels.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Server, 0x80C9)]
|
||||
[Packet(PacketFormat.OWServer, 0x80C9)]
|
||||
internal class ChannelInfosReply : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -18,9 +18,9 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
|
||||
internal override void Serialize(PacketStream stream)
|
||||
internal override void SaveData(PacketDataStream stream)
|
||||
{
|
||||
stream.WriteUInt16((ushort)Channels.Count);
|
||||
foreach (ChannelInfo channel in Channels)
|
@ -1,12 +1,11 @@
|
||||
using System;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the client request for a <see cref="ChannelConnectReply"/>.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Channel, 0x37)]
|
||||
[Packet(PacketFormat.OWChannel, 0x37)]
|
||||
internal class ChannelTop20Query : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -18,11 +17,11 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream)
|
||||
internal override void LoadData(PacketDataStream stream)
|
||||
{
|
||||
Count = stream.ReadUInt16();
|
||||
}
|
||||
|
||||
internal override void Serialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void SaveData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
@ -3,12 +3,12 @@ using System.Collections.Generic;
|
||||
using Syroot.BinaryData;
|
||||
using Syroot.Worms.Core.IO;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the server response to a <see cref="ChannelTop20Query"/>.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Channel, 0x36)]
|
||||
[Packet(PacketFormat.OWChannel, 0x36)]
|
||||
internal class ChannelTop20Reply : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -19,9 +19,9 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
|
||||
internal override void Serialize(PacketStream stream)
|
||||
internal override void SaveData(PacketDataStream stream)
|
||||
{
|
||||
stream.WriteString(UnknownA, 30);
|
||||
foreach (ChannelTop20Player top20Player in Top20)
|
@ -1,17 +1,17 @@
|
||||
using System;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the client request for a <see cref="ConnectReply"/>.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Server, 0x800E)]
|
||||
[Packet(PacketFormat.OWServer, 0x800E)]
|
||||
internal class ConnectQuery : Packet
|
||||
{
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream) { }
|
||||
internal override void LoadData(PacketDataStream stream) { }
|
||||
|
||||
internal override void Serialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void SaveData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
@ -1,12 +1,11 @@
|
||||
using System;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the server response to a <see cref="ConnectQuery"/>.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Server, 0x800F)]
|
||||
[Packet(PacketFormat.OWServer, 0x800F)]
|
||||
internal class ConnectReply : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -19,9 +18,9 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
|
||||
internal override void Serialize(PacketStream stream)
|
||||
internal override void SaveData(PacketDataStream stream)
|
||||
{
|
||||
stream.WriteString(Unknown);
|
||||
stream.WriteByte(0);
|
@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the client request for a <see cref="LoginReply"/>.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Server, 0x8000)]
|
||||
[Packet(PacketFormat.OWServer, 0x8000)]
|
||||
internal class LoginQuery : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -20,7 +19,7 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream)
|
||||
internal override void LoadData(PacketDataStream stream)
|
||||
{
|
||||
Unknown1 = stream.ReadUInt16();
|
||||
Logins = new LoginCredentials[stream.ReadUInt16()];
|
||||
@ -35,7 +34,7 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
ClientIP = IPAddress.Parse(stream.ReadString());
|
||||
}
|
||||
|
||||
internal override void Serialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void SaveData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class LoginCredentials
|
@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the server response to a <see cref="LoginQuery"/>.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Server, 0x8001)]
|
||||
[Packet(PacketFormat.OWServer, 0x8001)]
|
||||
internal class LoginReply : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -23,9 +23,9 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
|
||||
internal override void Serialize(PacketStream stream)
|
||||
internal override void SaveData(PacketDataStream stream)
|
||||
{
|
||||
bool loginSuccessful = Result == LoginResult.Success;
|
||||
stream.WriteBoolean(loginSuccessful);
|
@ -1,12 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an additional server response to a <see cref="LoginQuery"/>, providing informational server
|
||||
/// screen text.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Server, 0x8033)]
|
||||
[Packet(PacketFormat.OWServer, 0x8033)]
|
||||
internal class ServerInfoReply : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -18,9 +18,9 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
|
||||
internal override void Serialize(PacketStream stream)
|
||||
internal override void SaveData(PacketDataStream stream)
|
||||
{
|
||||
stream.WriteString(Text.Replace(Environment.NewLine, "\n"));
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the client request for a <see cref="StartSingleGameReply"/>.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Channel, 0x38)]
|
||||
[Packet(PacketFormat.OWChannel, 0x38)]
|
||||
internal class StartSingleGameQuery : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -17,14 +17,14 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream)
|
||||
internal override void LoadData(PacketDataStream stream)
|
||||
{
|
||||
RoundType = stream.ReadEnum<GameStartRoundType>(true);
|
||||
UnknownB = stream.Read1Byte();
|
||||
UnknownC = stream.Read1Byte();
|
||||
}
|
||||
|
||||
internal override void Serialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void SaveData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal enum GameStartRoundType : byte
|
@ -1,12 +1,12 @@
|
||||
using System;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the server response to a <see cref="StartSingleGameQuery"/>.
|
||||
/// </summary>
|
||||
[Packet(PacketFormat.Channel, 0x39)]
|
||||
[Packet(PacketFormat.OWChannel, 0x39)]
|
||||
internal class StartSingleGameReply : Packet
|
||||
{
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
@ -15,9 +15,9 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream) => throw new NotImplementedException();
|
||||
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException();
|
||||
|
||||
internal override void Serialize(PacketStream stream)
|
||||
internal override void SaveData(PacketDataStream stream)
|
||||
{
|
||||
stream.WriteBoolean(Success, BooleanCoding.Word);
|
||||
}
|
15
src/Syroot.Worms.Mgame.GameServer/Packets/Packet.cs
Normal file
15
src/Syroot.Worms.Mgame.GameServer/Packets/Packet.cs
Normal file
@ -0,0 +1,15 @@
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a packet with an ID specifying its contents. To allow the server to instantiate child packet classes,
|
||||
/// decorate it with the <see cref="PacketAttribute"/>.
|
||||
/// </summary>
|
||||
internal abstract class Packet
|
||||
{
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal abstract void LoadData(PacketDataStream stream);
|
||||
|
||||
internal abstract void SaveData(PacketDataStream stream);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets
|
||||
{
|
||||
/// <summary>
|
||||
/// Decorates a <see cref="Packet"/> child class with the ID of the packet it represents.
|
||||
@ -34,9 +34,16 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
public PacketFormat Format { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the known packet formats.
|
||||
/// </summary>
|
||||
internal enum PacketFormat
|
||||
{
|
||||
Server,
|
||||
Channel
|
||||
/// <summary>An Online Worms server packet.</summary>
|
||||
OWServer,
|
||||
/// <summary>An Online Worms channel packet.</summary>
|
||||
OWChannel,
|
||||
/// <summary>A Worms World Party Aqua packet.</summary>
|
||||
Wwpa
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.GameServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an in-memory stream formatting data for being sent or received from <see cref="Packet"/> instances.
|
||||
/// </summary>
|
||||
internal class PacketDataStream : BinaryStream
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private readonly MemoryStream _baseStream;
|
||||
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
static PacketDataStream()
|
||||
{
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
}
|
||||
|
||||
internal PacketDataStream() : this(new MemoryStream()) { }
|
||||
|
||||
internal PacketDataStream(byte[] buffer) : this(new MemoryStream(buffer)) { }
|
||||
|
||||
private PacketDataStream(MemoryStream baseStream)
|
||||
: base(baseStream, encoding: Encoding.GetEncoding(949), stringCoding: StringCoding.Int16CharCount)
|
||||
{
|
||||
_baseStream = baseStream;
|
||||
}
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="Span{Byte}"/> containing the actually used memory buffer bytes.
|
||||
/// </summary>
|
||||
/// <returns>The span containing the used buffer bytes.</returns>
|
||||
internal Span<byte> GetSpan() => _baseStream.GetBuffer().AsSpan(0, (int)Position);
|
||||
|
||||
/// <summary>
|
||||
/// Reads the remaining bytes in the buffer.
|
||||
/// </summary>
|
||||
/// <returns>The remaining bytes.</returns>
|
||||
internal byte[] ReadToEnd()
|
||||
{
|
||||
return ReadBytes((int)(Length - Position));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the given <paramref name="color"/> as an RGB0 integer value.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> to write.</param>
|
||||
internal void WriteColor(Color color)
|
||||
{
|
||||
WriteByte(color.R);
|
||||
WriteByte(color.G);
|
||||
WriteByte(color.B);
|
||||
WriteByte(0);
|
||||
}
|
||||
}
|
||||
}
|
18
src/Syroot.Worms.Mgame.GameServer/Packets/PacketException.cs
Normal file
18
src/Syroot.Worms.Mgame.GameServer/Packets/PacketException.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets
|
||||
{
|
||||
public class PacketException : Exception
|
||||
{
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
public PacketException() { }
|
||||
|
||||
public PacketException(string message) : base(message) { }
|
||||
|
||||
public PacketException(string message, Exception innerException) : base(message, innerException) { }
|
||||
|
||||
protected PacketException(SerializationInfo info, StreamingContext context) : base(info, context) { }
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a factory creating <see cref="Packet"/> instances by mapping their ID to types to instantiate.
|
||||
@ -12,23 +12,24 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private static readonly Dictionary<PacketAttribute, Type> _packetMetas = new Dictionary<PacketAttribute, Type>();
|
||||
private static readonly Dictionary<PacketAttribute, Type> _packetCache;
|
||||
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
static PacketFactory()
|
||||
{
|
||||
// Cache the ID-to-packet-class map.
|
||||
_packetCache = new Dictionary<PacketAttribute, Type>();
|
||||
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
|
||||
{
|
||||
foreach (PacketAttribute attrib in type.GetCustomAttributes<PacketAttribute>())
|
||||
{
|
||||
if (_packetMetas.ContainsKey(attrib))
|
||||
if (_packetCache.ContainsKey(attrib))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"{attrib.Format} packet with ID {attrib.ID} mapped to multiple classes.");
|
||||
}
|
||||
_packetMetas.Add(attrib, type);
|
||||
_packetCache.Add(attrib, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -43,7 +44,7 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
/// <exception cref="KeyNotFoundException">No class was mapped to the given packet ID.</exception>
|
||||
internal static Packet Create(PacketFormat type, int id)
|
||||
{
|
||||
foreach (KeyValuePair<PacketAttribute, Type> packetMeta in _packetMetas)
|
||||
foreach (KeyValuePair<PacketAttribute, Type> packetMeta in _packetCache)
|
||||
{
|
||||
if (packetMeta.Key.Format == type && packetMeta.Key.ID == id)
|
||||
return (Packet)Activator.CreateInstance(packetMeta.Value, true);
|
||||
@ -67,7 +68,7 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
return new PacketAttribute(rawPacket.Type, rawPacket.ID);
|
||||
else
|
||||
#endif
|
||||
return _packetMetas.Where(x => x.Value == packet.GetType()).First().Key;
|
||||
return _packetCache.Where(x => x.Value == packet.GetType()).First().Key;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
#if DEBUG
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a special fallback packet which simply stores any ID and the raw packet data.
|
||||
@ -27,12 +27,12 @@ namespace Syroot.Worms.Mgame.Server.Net
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal override void Deserialize(PacketStream stream)
|
||||
internal override void LoadData(PacketDataStream stream)
|
||||
{
|
||||
Data = stream.ReadBytes((int)stream.Length);
|
||||
}
|
||||
|
||||
internal override void Serialize(PacketStream stream)
|
||||
internal override void SaveData(PacketDataStream stream)
|
||||
{
|
||||
stream.WriteBytes(Data);
|
||||
}
|
@ -0,0 +1,166 @@
|
||||
using System;
|
||||
|
||||
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
||||
{
|
||||
internal static class PacketCompression
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private static int _field_0 = 0;
|
||||
|
||||
private static int _field_4 = 4;
|
||||
private static int _field_8 = 0;
|
||||
|
||||
private static int _field_C = 0;
|
||||
private static int _field_10 = 0;
|
||||
|
||||
private static int[] _bufferDwords = new int[512];
|
||||
|
||||
private static byte[] _buffer = new byte[256];
|
||||
private static int _bufferCursor = 0;
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal static ReadOnlySpan<byte> Compress(ReadOnlySpan<byte> decompressed)
|
||||
{
|
||||
Compressor compressor = new Compressor();
|
||||
|
||||
int idx = 0;
|
||||
while (idx < decompressed.Length)
|
||||
{
|
||||
int bytesToRepeat = Math.Max(0, idx - 0xFF);
|
||||
int idxDword = 0;
|
||||
int shiftValue1 = -1;
|
||||
if (bytesToRepeat >= idx)
|
||||
{
|
||||
Write(compressor, decompressed[idx++]);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
int i;
|
||||
for (i = idx; i < decompressed.Length; i++)
|
||||
{
|
||||
if (i - idx >= 0x111)
|
||||
break;
|
||||
if (decompressed[bytesToRepeat] != decompressed[i])
|
||||
break;
|
||||
bytesToRepeat++;
|
||||
}
|
||||
int offset = idx - i;
|
||||
int v11 = i - idx;
|
||||
int v12 = offset + bytesToRepeat;
|
||||
if (v11 >= 3 && v11 > idxDword)
|
||||
{
|
||||
idxDword = v11;
|
||||
shiftValue1 = idx - 12;
|
||||
if (v11 == 0x111)
|
||||
break;
|
||||
}
|
||||
bytesToRepeat = v12 + 1;
|
||||
} while (bytesToRepeat < idx);
|
||||
if (idxDword != 0)
|
||||
{
|
||||
TransferBuffer(compressor);
|
||||
compressor.Compress(true);
|
||||
if (idxDword >= 18)
|
||||
{
|
||||
compressor.Shift(0, 4);
|
||||
compressor.Shift(shiftValue1, 8);
|
||||
compressor.Shift(idxDword - 18, 8);
|
||||
_field_C++;
|
||||
_field_10 = idxDword - 3 + _field_10;
|
||||
}
|
||||
else
|
||||
{
|
||||
compressor.Shift(idxDword - 2, 4);
|
||||
compressor.Shift(shiftValue1 - 1, 8);
|
||||
_field_4++;
|
||||
_field_8 = idxDword - 2 + _field_8;
|
||||
}
|
||||
idx += idxDword;
|
||||
_bufferDwords[idxDword]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Write(compressor, decompressed[idx++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
TransferBuffer(compressor);
|
||||
compressor.Compress(true);
|
||||
compressor.Shift(0, 4);
|
||||
compressor.Shift(0, 8);
|
||||
|
||||
return compressor.Buffer.AsSpan(0, compressor.Cursor);
|
||||
}
|
||||
|
||||
internal static byte[] Decompress(ReadOnlySpan<byte> compressed)
|
||||
{
|
||||
return new byte[1];
|
||||
}
|
||||
|
||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||
|
||||
private static void TransferBuffer(Compressor compressor)
|
||||
{
|
||||
if (_bufferCursor != 0)
|
||||
{
|
||||
_field_0 += _bufferCursor;
|
||||
compressor.Compress(false);
|
||||
compressor.Shift(_bufferCursor - 1, 8);
|
||||
for (int i = 0; i < _bufferCursor; i++)
|
||||
compressor.Buffer[compressor.Cursor++] = _buffer[i];
|
||||
_bufferCursor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Write(Compressor compressor, byte c)
|
||||
{
|
||||
_buffer[_bufferCursor++] = c;
|
||||
if (_bufferCursor >= 256)
|
||||
TransferBuffer(compressor);
|
||||
}
|
||||
|
||||
// ---- CLASSES, STRUCTS & ENUMS -------------------------------------------------------------------------------
|
||||
|
||||
private class Compressor
|
||||
{
|
||||
internal byte[] Buffer = new byte[1024];
|
||||
internal int Cursor = 1;
|
||||
|
||||
private int _cursorCompress = 0;
|
||||
private int _shift = 0;
|
||||
|
||||
internal void Compress(bool bShift)
|
||||
{
|
||||
if (bShift)
|
||||
{
|
||||
Buffer[_cursorCompress] |= (byte)(0x80 >> _shift);
|
||||
}
|
||||
if (++_shift == 8)
|
||||
{
|
||||
_cursorCompress = Cursor++;
|
||||
Buffer[_cursorCompress] = 0;
|
||||
_shift = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal void Shift(int value, int shiftPlus1)
|
||||
{
|
||||
int prevShift = 0;
|
||||
int shift = shiftPlus1 - 1;
|
||||
if (shiftPlus1 != 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
Compress(((value >> shift) & 1) != 0);
|
||||
prevShift = shift--;
|
||||
} while (prevShift != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Syroot.BinaryData;
|
||||
using Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server
|
||||
namespace Syroot.Worms.Mgame.GameServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the main class of the application containing the entry point.
|
||||
@ -11,6 +14,12 @@ namespace Syroot.Worms.Mgame.Server
|
||||
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
byte[] decompressed = new byte[sizeof(int)];
|
||||
ByteConverter.System.GetBytes(0x8001, decompressed);
|
||||
ReadOnlySpan<byte> compressed = PacketCompression.Compress(decompressed);
|
||||
Debug.Assert(compressed.SequenceEqual(new byte[] { 0x01, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00 }),
|
||||
"Compression failed");
|
||||
|
||||
try
|
||||
{
|
||||
// Start a new server.
|
@ -4,7 +4,7 @@ using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server
|
||||
namespace Syroot.Worms.Mgame.GameServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a server listening for incoming client connections and dispatching them into <see cref="Client"/>
|
||||
@ -22,7 +22,7 @@ namespace Syroot.Worms.Mgame.Server
|
||||
{
|
||||
// Create and read the configuration.
|
||||
Config = new ConfigurationBuilder()
|
||||
.AddJsonFile("OWServerConfig.json", true)
|
||||
.AddJsonFile("ServerConfig.json", true)
|
||||
.Build()
|
||||
.Get<Config>();
|
||||
}
|
@ -2,10 +2,10 @@
|
||||
// Server settings
|
||||
"IP": "127.0.0.1", // external IP sent to clients to connect to
|
||||
"Port": 17022,
|
||||
"Name": "Online Worms Private Server",
|
||||
"Name": "Mgame Worms Private Server",
|
||||
"Region": "Global",
|
||||
"Version": 114,
|
||||
|
||||
// Debugging settings (optional)
|
||||
"SendDelay": 100 // milliseconds to sleep before sending packets (simulate network load)
|
||||
"SendDelay": 0 // milliseconds to sleep before sending packets (simulate network load)
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<AssemblyName>Server</AssemblyName>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>netcoreapp2.1</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
|
||||
<ProjectReference Include="..\Syroot.Worms\Syroot.Worms.csproj" />
|
||||
<None Update="ServerConfig.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -3,7 +3,7 @@
|
||||
"ServerIP": "127.0.0.1",
|
||||
"ServerPort": 17022,
|
||||
|
||||
// Autologin credentials (optional)
|
||||
// Autologin credentials
|
||||
"UserName": "UserName",
|
||||
"Password": "Password",
|
||||
|
||||
|
@ -1,211 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Reflection;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a class capable of dispatching received <see cref="Packet"/> instances to a corresponding method.
|
||||
/// </summary>
|
||||
internal class GameConnection : IDisposable
|
||||
{
|
||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||
|
||||
private const int _maxDataSize = 2048;
|
||||
private const ushort _channelPacketStartTag = 0xFFFE;
|
||||
private const byte _channelPacketStartTag2 = 1;
|
||||
private const ushort _channelPacketEndTag = 0xFEFF;
|
||||
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private static readonly Dictionary<Type, Dictionary<Type, MethodInfo>> _connectionClassesCache
|
||||
= new Dictionary<Type, Dictionary<Type, MethodInfo>>();
|
||||
|
||||
private readonly Dictionary<Type, MethodInfo> _handlers;
|
||||
private readonly byte[] _receiveBuffer;
|
||||
private readonly byte[] _sendDataBuffer;
|
||||
private readonly PacketStream _receiveStream;
|
||||
private readonly PacketStream _sendStream;
|
||||
private bool _disposed;
|
||||
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GameConnection"/> class, handling the given
|
||||
/// <paramref name="tcpClient"/>.
|
||||
/// </summary>
|
||||
/// <param name="tcpClient">The <see cref="System.Net.Sockets.TcpClient"/> to communicate with.</param>
|
||||
internal GameConnection(TcpClient tcpClient)
|
||||
{
|
||||
TcpClient = tcpClient;
|
||||
|
||||
_handlers = GetHandlers();
|
||||
_receiveBuffer = new byte[_maxDataSize];
|
||||
_sendDataBuffer = new byte[_maxDataSize];
|
||||
_receiveStream = new PacketStream(TcpClient.GetStream());
|
||||
_sendStream = new PacketStream(new MemoryStream(_sendDataBuffer, 0, _maxDataSize, true));
|
||||
}
|
||||
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="TcpClient"/> with which the connection communicates.
|
||||
/// </summary>
|
||||
internal TcpClient TcpClient { get; }
|
||||
|
||||
// ---- 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 connection and dispatches them to corresponding methods.
|
||||
/// </summary>
|
||||
internal void Listen()
|
||||
{
|
||||
Packet packet;
|
||||
while ((packet = ReceivePacket()) != null)
|
||||
HandlePacket(packet);
|
||||
}
|
||||
|
||||
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
TcpClient.Close();
|
||||
_sendStream.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnPrePacketHandle(Packet packet) { }
|
||||
|
||||
protected virtual void OnPrePacketSend(Packet packet) { }
|
||||
|
||||
// ---- 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 a specific packet and returning nothing.
|
||||
foreach (MethodInfo method in classType.GetMethods(BindingFlags.Public | BindingFlags.Instance))
|
||||
{
|
||||
Type inPacket = method.GetParameters().FirstOrDefault()?.ParameterType;
|
||||
if (method.ReturnType == typeof(void) && typeof(Packet).IsAssignableFrom(inPacket))
|
||||
handlerMethods.Add(inPacket, method);
|
||||
}
|
||||
_connectionClassesCache.Add(classType, handlerMethods);
|
||||
}
|
||||
return handlerMethods;
|
||||
}
|
||||
|
||||
private void HandlePacket(Packet inPacket)
|
||||
{
|
||||
OnPrePacketHandle(inPacket);
|
||||
_handlers[inPacket.GetType()].Invoke(this, new[] { inPacket });
|
||||
}
|
||||
|
||||
private Packet ReceivePacket()
|
||||
{
|
||||
// Receive the raw packet data.
|
||||
PacketFormat type;
|
||||
int id;
|
||||
ushort dataSize;
|
||||
try
|
||||
{
|
||||
// Handle both server and channel specific packet formats.
|
||||
ushort tag = _receiveStream.ReadUInt16();
|
||||
if (tag == _channelPacketStartTag)
|
||||
{
|
||||
type = PacketFormat.Channel;
|
||||
if (_receiveStream.Read1Byte() != 1)
|
||||
throw new IOException("Invalid channel packet start tag.");
|
||||
dataSize = _receiveStream.ReadUInt16();
|
||||
id = _receiveStream.Read1Byte();
|
||||
_receiveStream.ReadAll(_receiveBuffer, 0, dataSize);
|
||||
ushort endTag = _receiveStream.ReadUInt16();
|
||||
if (endTag != _channelPacketEndTag)
|
||||
throw new IOException("Invalid channel packet end tag.");
|
||||
}
|
||||
else
|
||||
{
|
||||
type = PacketFormat.Server;
|
||||
id = tag;
|
||||
dataSize = _receiveStream.ReadUInt16();
|
||||
_receiveStream.ReadAll(_receiveBuffer, 0, dataSize);
|
||||
}
|
||||
}
|
||||
catch (IOException) { return null; } // The underlying socket closed.
|
||||
catch (ObjectDisposedException) { return null; } // The underlying stream closed.
|
||||
|
||||
// Deserialize and return the packet.
|
||||
Packet packet = PacketFactory.Create(type, id);
|
||||
using (PacketStream readStream = new PacketStream(new MemoryStream(_receiveBuffer, 0, dataSize, false)))
|
||||
packet.Deserialize(readStream);
|
||||
return packet;
|
||||
}
|
||||
|
||||
protected bool SendPacket(Packet packet)
|
||||
{
|
||||
OnPrePacketSend(packet);
|
||||
|
||||
// Serialize the packet data.
|
||||
_sendStream.Position = 0;
|
||||
packet.Serialize(_sendStream);
|
||||
ushort dataSize;
|
||||
|
||||
// Send the packet in one of the two possible formats and return success.
|
||||
try
|
||||
{
|
||||
PacketAttribute attribute = PacketFactory.GetAttribute(packet);
|
||||
switch (attribute.Format)
|
||||
{
|
||||
case PacketFormat.Server:
|
||||
// Retrieve data size.
|
||||
dataSize = (ushort)_sendStream.Position;
|
||||
// Send head and data.
|
||||
_receiveStream.WriteUInt16((ushort)attribute.ID);
|
||||
_receiveStream.WriteUInt16(dataSize);
|
||||
_receiveStream.Write(_sendDataBuffer, 0, dataSize);
|
||||
break;
|
||||
case PacketFormat.Channel:
|
||||
// Retrieve data size. Data must have at least 1 byte.
|
||||
if (_sendStream.Position == 0)
|
||||
_sendStream.WriteByte(0);
|
||||
dataSize = (ushort)_sendStream.Position;
|
||||
// Send head, data, and tail.
|
||||
_receiveStream.WriteUInt16(_channelPacketStartTag);
|
||||
_receiveStream.WriteByte(1);
|
||||
_receiveStream.WriteUInt16(dataSize);
|
||||
_receiveStream.WriteByte((byte)attribute.ID);
|
||||
_receiveStream.Write(_sendDataBuffer, 0, dataSize);
|
||||
_receiveStream.Write(_channelPacketEndTag);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Cannot send unknown packet format.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (IOException) { return false; } // A network error appeared, and communication should end.
|
||||
catch (ObjectDisposedException) { return false; } // The underlying stream was most apparently closed.
|
||||
}
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Syroot.BinaryData;
|
||||
|
||||
namespace Syroot.Worms.Mgame.Server.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a stream formatting data for being sent or received from <see cref="Packet"/> instances.
|
||||
/// </summary>
|
||||
internal class PacketStream : BinaryStream
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private static readonly Encoding _win949Encoding;
|
||||
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
static PacketStream()
|
||||
{
|
||||
#if NETCOREAPP2_1
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
#endif
|
||||
_win949Encoding = Encoding.GetEncoding(949);
|
||||
}
|
||||
|
||||
internal PacketStream(Stream baseStream)
|
||||
: base(baseStream, encoding: _win949Encoding, stringCoding: StringCoding.Int16CharCount) { }
|
||||
|
||||
// ---- 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the remaining bytes in the buffer.
|
||||
/// </summary>
|
||||
/// <returns>The remaining bytes.</returns>
|
||||
internal byte[] ReadToEnd()
|
||||
{
|
||||
return BaseStream.ReadBytes((int)(Length - Position));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the given <paramref name="color"/> as an RGB0 integer value.
|
||||
/// </summary>
|
||||
/// <param name="color">The <see cref="Color"/> to write.</param>
|
||||
internal void WriteColor(Color color)
|
||||
{
|
||||
BaseStream.WriteByte(color.R);
|
||||
BaseStream.WriteByte(color.G);
|
||||
BaseStream.WriteByte(color.B);
|
||||
BaseStream.WriteByte(0);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>netcoreapp2.1</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Syroot.BinaryData" Version="5.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Syroot.Worms.Armageddon.ProjectX\Syroot.Worms.Armageddon.ProjectX.csproj" />
|
||||
<ProjectReference Include="..\Syroot.Worms.Armageddon\Syroot.Worms.Armageddon.csproj" />
|
||||
<ProjectReference Include="..\Syroot.Worms.Mgame.Launcher\Syroot.Worms.Mgame.Launcher.csproj" />
|
||||
<ProjectReference Include="..\Syroot.Worms.Mgame\Syroot.Worms.Mgame.csproj" />
|
||||
<ProjectReference Include="..\Syroot.Worms.WorldParty\Syroot.Worms.WorldParty.csproj" />
|
||||
<ProjectReference Include="..\Syroot.Worms.Worms2\Syroot.Worms.Worms2.csproj" />
|
||||
<ProjectReference Include="..\Syroot.Worms\Syroot.Worms.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="OWServerConfig.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -6,6 +6,7 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Syroot.Worms.Armageddon.ProjectX\Syroot.Worms.Armageddon.ProjectX.csproj" />
|
||||
<ProjectReference Include="..\Syroot.Worms.Armageddon\Syroot.Worms.Armageddon.csproj" />
|
||||
<ProjectReference Include="..\Syroot.Worms.Mgame.GameServer\Syroot.Worms.Mgame.GameServer.csproj" />
|
||||
<ProjectReference Include="..\Syroot.Worms.Mgame\Syroot.Worms.Mgame.csproj" />
|
||||
<ProjectReference Include="..\Syroot.Worms.WorldParty\Syroot.Worms.WorldParty.csproj" />
|
||||
<ProjectReference Include="..\Syroot.Worms.Worms2\Syroot.Worms.Worms2.csproj" />
|
||||
|
@ -9,8 +9,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Syroot.Worms.Scratchpad", "
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Syroot.Worms.Mgame.Launcher", "Syroot.Worms.Mgame.Launcher\Syroot.Worms.Mgame.Launcher.csproj", "{510EE83E-9C52-40FD-AC7E-C4981EBF4182}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Syroot.Worms.Mgame.Server", "Syroot.Worms.Mgame.Server\Syroot.Worms.Mgame.Server.csproj", "{2A06124C-EA75-4946-9959-4AD3DC754B90}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Syroot.Worms.Armageddon", "Syroot.Worms.Armageddon\Syroot.Worms.Armageddon.csproj", "{D9694108-539C-4DEE-B5DD-284208D48B46}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Syroot.Worms.Armageddon.ProjectX", "Syroot.Worms.Armageddon.ProjectX\Syroot.Worms.Armageddon.ProjectX.csproj", "{AC9F52FA-F552-49E0-83AE-79759BF44FED}"
|
||||
@ -37,6 +35,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Syroot.Worms.Armageddon.Pro
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Syroot.Worms.Test", "Syroot.Worms.Test\Syroot.Worms.Test.csproj", "{351B93B0-301F-42E1-82A0-7FA217154F5D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Syroot.Worms.Mgame.GameServer", "Syroot.Worms.Mgame.GameServer\Syroot.Worms.Mgame.GameServer.csproj", "{392E4CA2-61D9-4BE1-B065-8801A9F102B8}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -55,10 +55,6 @@ Global
|
||||
{510EE83E-9C52-40FD-AC7E-C4981EBF4182}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{510EE83E-9C52-40FD-AC7E-C4981EBF4182}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{510EE83E-9C52-40FD-AC7E-C4981EBF4182}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2A06124C-EA75-4946-9959-4AD3DC754B90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2A06124C-EA75-4946-9959-4AD3DC754B90}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2A06124C-EA75-4946-9959-4AD3DC754B90}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2A06124C-EA75-4946-9959-4AD3DC754B90}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D9694108-539C-4DEE-B5DD-284208D48B46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D9694108-539C-4DEE-B5DD-284208D48B46}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D9694108-539C-4DEE-B5DD-284208D48B46}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@ -99,6 +95,10 @@ Global
|
||||
{351B93B0-301F-42E1-82A0-7FA217154F5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{351B93B0-301F-42E1-82A0-7FA217154F5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{351B93B0-301F-42E1-82A0-7FA217154F5D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{392E4CA2-61D9-4BE1-B065-8801A9F102B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{392E4CA2-61D9-4BE1-B065-8801A9F102B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{392E4CA2-61D9-4BE1-B065-8801A9F102B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{392E4CA2-61D9-4BE1-B065-8801A9F102B8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -106,7 +106,6 @@ Global
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{DD76B6AA-5A5A-4FCD-95AA-9552977525A1} = {9E964426-D744-405A-9EAA-D8D7A310B614}
|
||||
{510EE83E-9C52-40FD-AC7E-C4981EBF4182} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
|
||||
{2A06124C-EA75-4946-9959-4AD3DC754B90} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
|
||||
{D9694108-539C-4DEE-B5DD-284208D48B46} = {9E964426-D744-405A-9EAA-D8D7A310B614}
|
||||
{AC9F52FA-F552-49E0-83AE-79759BF44FED} = {9E964426-D744-405A-9EAA-D8D7A310B614}
|
||||
{4E124BBA-15B5-422E-93D5-96EA7D4180F3} = {9E964426-D744-405A-9EAA-D8D7A310B614}
|
||||
@ -117,6 +116,7 @@ Global
|
||||
{3AAC4992-DDB9-4175-82B9-C57F22E12FF6} = {99E56312-A064-4AD3-8443-0B56A5F76E6B}
|
||||
{EF308D4E-26A0-471C-B764-9C4EB713BEAE} = {99E56312-A064-4AD3-8443-0B56A5F76E6B}
|
||||
{351B93B0-301F-42E1-82A0-7FA217154F5D} = {99E56312-A064-4AD3-8443-0B56A5F76E6B}
|
||||
{392E4CA2-61D9-4BE1-B065-8801A9F102B8} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {1CD4EDE2-A5FB-4A58-A850-3506AB7E7B69}
|
||||
|
@ -19,7 +19,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Syroot.BinaryData.Serialization" Version="5.0.0" />
|
||||
<PackageReference Include="Syroot.BinaryData" Version="5.0.0" />
|
||||
<PackageReference Include="Syroot.BinaryData" Version="5.1.0-beta1" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="4.5.1" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
|
||||
</ItemGroup>
|
||||
|
Loading…
x
Reference in New Issue
Block a user