mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-03-04 01:15:21 +03:00
Push current additions to Worms 2 server with test proxy.
This commit is contained in:
parent
ebdf7f5168
commit
17b0edf67f
113
src/tool/Syroot.Worms.Worms2.GameServer/Nation.cs
Normal file
113
src/tool/Syroot.Worms.Worms2.GameServer/Nation.cs
Normal file
@ -0,0 +1,113 @@
|
||||
namespace Syroot.Worms.Worms2.GameServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the flag sent with in a <see cref="SessionInfo"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Names are in ISO 3166 Alpha-2 notation.</remarks>
|
||||
public enum Nation : byte
|
||||
{
|
||||
None,
|
||||
/// <summary>United Kingdom</summary>
|
||||
UK,
|
||||
/// <summary>Argentinia</summary>
|
||||
AR,
|
||||
/// <summary>Australia</summary>
|
||||
AU,
|
||||
/// <summary>Austria</summary>
|
||||
AT,
|
||||
/// <summary>Beglium</summary>
|
||||
BE,
|
||||
/// <summary>Brazil</summary>
|
||||
BR,
|
||||
/// <summary>Canada</summary>
|
||||
CA,
|
||||
/// <summary>Croatia</summary>
|
||||
HR,
|
||||
/// <summary>Bosnia and Herzegovina (old flag)</summary>
|
||||
BA,
|
||||
/// <summary>Cyprus</summary>
|
||||
CY,
|
||||
/// <summary>Czech Republic</summary>
|
||||
CZ,
|
||||
/// <summary>Denmark</summary>
|
||||
DK,
|
||||
/// <summary>Finland</summary>
|
||||
FI,
|
||||
/// <summary>France</summary>
|
||||
FR,
|
||||
/// <summary>Georgia</summary>
|
||||
GE,
|
||||
/// <summary>Germany</summary>
|
||||
DE,
|
||||
/// <summary>Greece</summary>
|
||||
GR,
|
||||
/// <summary>Hong Kong SAR</summary>
|
||||
HK,
|
||||
/// <summary>Hungary</summary>
|
||||
HU,
|
||||
/// <summary>Iceland</summary>
|
||||
IS,
|
||||
/// <summary>India</summary>
|
||||
IN,
|
||||
/// <summary>Indonesia</summary>
|
||||
ID,
|
||||
/// <summary>Iran</summary>
|
||||
IR,
|
||||
/// <summary>Iraq</summary>
|
||||
IQ,
|
||||
/// <summary>Ireland</summary>
|
||||
IE,
|
||||
/// <summary>Israel</summary>
|
||||
IL,
|
||||
/// <summary>Italy</summary>
|
||||
IT,
|
||||
/// <summary>Japan</summary>
|
||||
JP,
|
||||
/// <summary>Liechtenstein</summary>
|
||||
LI,
|
||||
/// <summary>Luxembourg</summary>
|
||||
LU,
|
||||
/// <summary>Malaysia</summary>
|
||||
MY,
|
||||
/// <summary>Malta</summary>
|
||||
MT,
|
||||
/// <summary>Mexico</summary>
|
||||
MX,
|
||||
/// <summary>Morocco</summary>
|
||||
MA,
|
||||
/// <summary>Netherlands</summary>
|
||||
NL,
|
||||
/// <summary>New Zealand</summary>
|
||||
NZ,
|
||||
/// <summary>Norway</summary>
|
||||
NO,
|
||||
/// <summary>Poland</summary>
|
||||
PL,
|
||||
/// <summary>Portugal</summary>
|
||||
PT,
|
||||
/// <summary>Puerto Rico</summary>
|
||||
PR,
|
||||
/// <summary>Romania</summary>
|
||||
RO,
|
||||
/// <summary>Russian Federation</summary>
|
||||
RU,
|
||||
/// <summary>Singapore</summary>
|
||||
SG,
|
||||
/// <summary>South Africa</summary>
|
||||
ZA,
|
||||
/// <summary>Spain</summary>
|
||||
ES,
|
||||
/// <summary>Sweden</summary>
|
||||
SE,
|
||||
/// <summary>Switzerland</summary>
|
||||
CH,
|
||||
/// <summary>Turkey</summary>
|
||||
TR,
|
||||
/// <summary>United States</summary>
|
||||
US,
|
||||
/// <summary>Custom skull flag</summary>
|
||||
Skull,
|
||||
/// <summary>Custom Team17 flag</summary>
|
||||
Team17
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Syroot.BinaryData;
|
||||
using Syroot.Worms.IO;
|
||||
@ -8,88 +9,187 @@ namespace Syroot.Worms.Worms2.GameServer
|
||||
{
|
||||
internal class Packet
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
internal int Value10;
|
||||
internal PacketCode Code;
|
||||
internal PacketFlags Flags;
|
||||
internal int Value4;
|
||||
internal int Value0;
|
||||
internal int Value3;
|
||||
internal int Value1;
|
||||
internal int Value2;
|
||||
internal int Value7;
|
||||
internal string String20 = String.Empty;
|
||||
internal string String50 = String.Empty;
|
||||
internal byte[] Data = Array.Empty<byte>();
|
||||
internal Packet() { }
|
||||
|
||||
internal Packet(PacketCode code,
|
||||
int? value0 = null, int? value1 = null, int? value2 = null, int? value3 = null, int? value4 = null,
|
||||
int? value10 = null, byte[]? data = null, int? error = null,
|
||||
string? name = null, SessionInfo? session = null)
|
||||
{
|
||||
Code = code;
|
||||
Value0 = value0;
|
||||
Value1 = value1;
|
||||
Value2 = value2;
|
||||
Value3 = value3;
|
||||
Value4 = value4;
|
||||
Value10 = value10;
|
||||
Data = data;
|
||||
Error = error;
|
||||
Name = name;
|
||||
Session = session;
|
||||
}
|
||||
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
|
||||
internal PacketCode Code { get; set; }
|
||||
internal int? Value0 { get; set; }
|
||||
internal int? Value1 { get; set; }
|
||||
internal int? Value2 { get; set; }
|
||||
internal int? Value3 { get; set; }
|
||||
internal int? Value4 { get; set; }
|
||||
internal int? Value10 { get; set; }
|
||||
internal byte[]? Data { get; set; }
|
||||
internal int? Error { get; set; }
|
||||
internal string? Name { get; set; }
|
||||
internal SessionInfo? Session { get; set; }
|
||||
|
||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine($"{Code:D} {Code}");
|
||||
if (Value0.HasValue) sb.AppendLine($"{nameof(Value0)} = {Value0:X8}");
|
||||
if (Value1.HasValue) sb.AppendLine($"{nameof(Value1)} = {Value1:X8}");
|
||||
if (Value2.HasValue) sb.AppendLine($"{nameof(Value2)} = {Value2:X8}");
|
||||
if (Value3.HasValue) sb.AppendLine($"{nameof(Value3)} = {Value3:X8}");
|
||||
if (Value4.HasValue) sb.AppendLine($"{nameof(Value4)} = {Value4:X8}");
|
||||
if (Value10.HasValue) sb.AppendLine($"{nameof(Value10)} = {Value10:X8}");
|
||||
if (Data != null)
|
||||
{
|
||||
sb.Append($"{nameof(Data)}[{Data.Length}] = ");
|
||||
foreach (byte b in Data)
|
||||
sb.Append($"{b:X2} ");
|
||||
sb.AppendLine();
|
||||
}
|
||||
if (Error.HasValue) sb.AppendLine($"{nameof(Error)} = {Error:X8}");
|
||||
if (Name != null) sb.AppendLine($"{nameof(Name)} = {Name}");
|
||||
if (Session.HasValue) sb.AppendLine($"{nameof(Session)} = {Session}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal void Receive(Stream stream)
|
||||
{
|
||||
int dataLength = 0;
|
||||
Code = (PacketCode)stream.ReadInt32();
|
||||
Flags flags = (Flags)stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasValue0)) Value0 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasValue1)) Value1 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasValue2)) Value2 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasValue3)) Value3 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasValue4)) Value4 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasValue10)) Value10 = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasDataLength)) dataLength = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasData) && dataLength != 0) Data = stream.ReadBytes(dataLength);
|
||||
if (flags.HasFlag(Flags.HasError)) Error = stream.ReadInt32();
|
||||
if (flags.HasFlag(Flags.HasUserName)) Name = stream.ReadFixedString(20);
|
||||
if (flags.HasFlag(Flags.HasUserInfo)) Session = stream.ReadStruct<SessionInfo>();
|
||||
}
|
||||
|
||||
internal async Task ReceiveAsync(Stream stream)
|
||||
{
|
||||
int dataLength = 0;
|
||||
Code = (PacketCode)await stream.ReadInt32Async();
|
||||
Flags = (PacketFlags)await stream.ReadInt32Async();
|
||||
if (Flags.HasFlag(PacketFlags.HasValue0)) Value0 = await stream.ReadInt32Async();
|
||||
if (Flags.HasFlag(PacketFlags.HasValue1)) Value1 = await stream.ReadInt32Async();
|
||||
if (Flags.HasFlag(PacketFlags.HasValue2)) Value2 = await stream.ReadInt32Async();
|
||||
if (Flags.HasFlag(PacketFlags.HasValue3)) Value3 = await stream.ReadInt32Async();
|
||||
if (Flags.HasFlag(PacketFlags.HasValue4)) Value4 = await stream.ReadInt32Async();
|
||||
if (Flags.HasFlag(PacketFlags.HasValue10)) Value10 = await stream.ReadInt32Async();
|
||||
if (Flags.HasFlag(PacketFlags.HasDataLength)) dataLength = await stream.ReadInt32Async();
|
||||
if (Flags.HasFlag(PacketFlags.HasData) && dataLength != 0) Data = await stream.ReadBytesAsync(dataLength);
|
||||
if (Flags.HasFlag(PacketFlags.HasValue7)) Value7 = await stream.ReadInt32Async();
|
||||
if (Flags.HasFlag(PacketFlags.HasString20)) String20 = await stream.ReadFixedStringAsync(20);
|
||||
if (Flags.HasFlag(PacketFlags.HasString50)) String50 = await stream.ReadFixedStringAsync(50);
|
||||
Flags flags = (Flags)await stream.ReadInt32Async();
|
||||
if (flags.HasFlag(Flags.HasValue0)) Value0 = await stream.ReadInt32Async();
|
||||
if (flags.HasFlag(Flags.HasValue1)) Value1 = await stream.ReadInt32Async();
|
||||
if (flags.HasFlag(Flags.HasValue2)) Value2 = await stream.ReadInt32Async();
|
||||
if (flags.HasFlag(Flags.HasValue3)) Value3 = await stream.ReadInt32Async();
|
||||
if (flags.HasFlag(Flags.HasValue4)) Value4 = await stream.ReadInt32Async();
|
||||
if (flags.HasFlag(Flags.HasValue10)) Value10 = await stream.ReadInt32Async();
|
||||
if (flags.HasFlag(Flags.HasDataLength)) dataLength = await stream.ReadInt32Async();
|
||||
if (flags.HasFlag(Flags.HasData) && dataLength != 0) Data = await stream.ReadBytesAsync(dataLength);
|
||||
if (flags.HasFlag(Flags.HasError)) Error = await stream.ReadInt32Async();
|
||||
if (flags.HasFlag(Flags.HasUserName)) Name = await stream.ReadFixedStringAsync(20);
|
||||
if (flags.HasFlag(Flags.HasUserInfo)) Session = stream.ReadStruct<SessionInfo>();
|
||||
}
|
||||
|
||||
internal void Send(Stream stream)
|
||||
{
|
||||
stream.WriteInt32((int)Code);
|
||||
stream.WriteInt32((int)GetFlags());
|
||||
if (Value0.HasValue) stream.WriteInt32(Value0.Value);
|
||||
if (Value1.HasValue) stream.WriteInt32(Value1.Value);
|
||||
if (Value2.HasValue) stream.WriteInt32(Value2.Value);
|
||||
if (Value3.HasValue) stream.WriteInt32(Value3.Value);
|
||||
if (Value4.HasValue) stream.WriteInt32(Value4.Value);
|
||||
if (Value10.HasValue) stream.WriteInt32(Value10.Value);
|
||||
if (Data != null)
|
||||
{
|
||||
stream.WriteInt32(Data.Length);
|
||||
stream.WriteBytes(Data);
|
||||
}
|
||||
if (Error.HasValue) stream.WriteInt32(Error.Value);
|
||||
if (Name != null) stream.WriteFixedString(Name, 20);
|
||||
if (Session.HasValue) stream.WriteStruct(Session.Value);
|
||||
}
|
||||
|
||||
internal async Task SendAsync(Stream stream)
|
||||
{
|
||||
await stream.WriteInt32Async((int)Code);
|
||||
await stream.WriteInt32Async((int)Flags);
|
||||
if (Flags.HasFlag(PacketFlags.HasValue0)) await stream.WriteInt32Async(Value0);
|
||||
if (Flags.HasFlag(PacketFlags.HasValue1)) await stream.WriteInt32Async(Value1);
|
||||
if (Flags.HasFlag(PacketFlags.HasValue2)) await stream.WriteInt32Async(Value2);
|
||||
if (Flags.HasFlag(PacketFlags.HasValue3)) await stream.WriteInt32Async(Value3);
|
||||
if (Flags.HasFlag(PacketFlags.HasValue4)) await stream.WriteInt32Async(Value4);
|
||||
if (Flags.HasFlag(PacketFlags.HasValue10)) await stream.WriteInt32Async(Value10);
|
||||
if (Flags.HasFlag(PacketFlags.HasDataLength)) await stream.WriteInt32Async(Data.Length);
|
||||
if (Flags.HasFlag(PacketFlags.HasData) && Data.Length != 0) await stream.WriteBytesAsync(Data);
|
||||
if (Flags.HasFlag(PacketFlags.HasValue7)) await stream.WriteInt32Async(Value7);
|
||||
if (Flags.HasFlag(PacketFlags.HasString20)) await stream.WriteFixedStringAsync(String20, 20);
|
||||
if (Flags.HasFlag(PacketFlags.HasString50)) await stream.WriteFixedStringAsync(String50, 50);
|
||||
await stream.WriteInt32Async((int)GetFlags());
|
||||
if (Value0.HasValue) await stream.WriteInt32Async(Value0.Value);
|
||||
if (Value1.HasValue) await stream.WriteInt32Async(Value1.Value);
|
||||
if (Value2.HasValue) await stream.WriteInt32Async(Value2.Value);
|
||||
if (Value3.HasValue) await stream.WriteInt32Async(Value3.Value);
|
||||
if (Value4.HasValue) await stream.WriteInt32Async(Value4.Value);
|
||||
if (Value10.HasValue) await stream.WriteInt32Async(Value10.Value);
|
||||
if (Data != null)
|
||||
{
|
||||
await stream.WriteInt32Async(Data.Length);
|
||||
await stream.WriteBytesAsync(Data);
|
||||
}
|
||||
if (Error.HasValue) await stream.WriteInt32Async(Error.Value);
|
||||
if (Name != null) await stream.WriteFixedStringAsync(Name, 20);
|
||||
if (Session.HasValue) stream.WriteStruct(Session.Value);
|
||||
}
|
||||
|
||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||
|
||||
private Flags GetFlags()
|
||||
{
|
||||
Flags flags = Flags.None;
|
||||
if (Value0.HasValue) flags |= Flags.HasValue0;
|
||||
if (Value1.HasValue) flags |= Flags.HasValue1;
|
||||
if (Value2.HasValue) flags |= Flags.HasValue2;
|
||||
if (Value3.HasValue) flags |= Flags.HasValue3;
|
||||
if (Value4.HasValue) flags |= Flags.HasValue4;
|
||||
if (Value10.HasValue) flags |= Flags.HasValue10;
|
||||
if (Data != null)
|
||||
{
|
||||
flags |= Flags.HasDataLength;
|
||||
flags |= Flags.HasData;
|
||||
}
|
||||
if (Error.HasValue) flags |= Flags.HasError;
|
||||
if (Name != null) flags |= Flags.HasUserName;
|
||||
if (Session.HasValue) flags |= Flags.HasUserInfo;
|
||||
return flags;
|
||||
}
|
||||
|
||||
// ---- CLASSES, STRUCTS & ENUMS -------------------------------------------------------------------------------
|
||||
|
||||
[Flags]
|
||||
private enum Flags : int
|
||||
{
|
||||
None,
|
||||
HasValue0 = 1 << 0,
|
||||
HasValue1 = 1 << 1,
|
||||
HasValue2 = 1 << 2,
|
||||
HasValue3 = 1 << 3,
|
||||
HasValue4 = 1 << 4,
|
||||
HasValue10 = 1 << 10,
|
||||
HasDataLength = 1 << 5,
|
||||
HasData = 1 << 6,
|
||||
HasError = 1 << 7,
|
||||
HasUserName = 1 << 8,
|
||||
HasUserInfo = 1 << 9
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the description of the packet contents, as seen from client-side (thus a "reply" comes from the
|
||||
/// server).
|
||||
/// </summary>
|
||||
internal enum PacketCode : int
|
||||
{
|
||||
Login = 600,
|
||||
LoginReply = 601,
|
||||
CreateRoom = 700,
|
||||
CreateRoomReply = 701
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents which <see cref="Packet"/> fields are available, as specified at the start of each.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
internal enum PacketFlags
|
||||
{
|
||||
None,
|
||||
HasValue0 = 1 << 0,
|
||||
HasValue1 = 1 << 1,
|
||||
HasValue2 = 1 << 2,
|
||||
HasValue3 = 1 << 3,
|
||||
HasValue4 = 1 << 4,
|
||||
HasValue10 = 1 << 10,
|
||||
HasDataLength = 1 << 5,
|
||||
HasData = 1 << 6,
|
||||
HasValue7 = 1 << 7,
|
||||
HasString20 = 1 << 8,
|
||||
HasString50 = 1 << 9
|
||||
}
|
||||
}
|
||||
|
30
src/tool/Syroot.Worms.Worms2.GameServer/PacketCode.cs
Normal file
30
src/tool/Syroot.Worms.Worms2.GameServer/PacketCode.cs
Normal file
@ -0,0 +1,30 @@
|
||||
namespace Syroot.Worms.Worms2.GameServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the description of the packet contents, as seen from client-side (thus a "reply" comes from the
|
||||
/// server).
|
||||
/// </summary>
|
||||
internal enum PacketCode : int
|
||||
{
|
||||
ListRooms = 200,
|
||||
ListItem = 350,
|
||||
ListEnd = 351,
|
||||
ListUsers = 400,
|
||||
ListGames = 500,
|
||||
Login = 600,
|
||||
LoginReply = 601,
|
||||
CreateRoom = 700,
|
||||
CreateRoomReply = 701,
|
||||
Join = 800,
|
||||
JoinReply = 801,
|
||||
LeaveRoom = 900,
|
||||
LeaveRoomReply = 901,
|
||||
LeaveRoom2 = 1100,
|
||||
LeaveRoomReply2 = 1101,
|
||||
CreateGame = 1200,
|
||||
CreateGameReply = 1201,
|
||||
ChatRoom = 1300,
|
||||
ChatRoomReply = 1301,
|
||||
JoinGame = 1326,
|
||||
}
|
||||
}
|
87
src/tool/Syroot.Worms.Worms2.GameServer/PacketConnection.cs
Normal file
87
src/tool/Syroot.Worms.Worms2.GameServer/PacketConnection.cs
Normal file
@ -0,0 +1,87 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Syroot.Worms.Worms2.GameServer
|
||||
{
|
||||
internal class PacketConnection
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private readonly Stream _stream;
|
||||
private readonly SemaphoreSlim _recvSemaphore = new SemaphoreSlim(1, 1);
|
||||
private readonly SemaphoreSlim _sendSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
internal PacketConnection(TcpClient tcpClient)
|
||||
{
|
||||
_stream = tcpClient.GetStream();
|
||||
RemoteEndPoint = (IPEndPoint)tcpClient.Client.RemoteEndPoint;
|
||||
}
|
||||
|
||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||
|
||||
internal IPEndPoint RemoteEndPoint { get; }
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal Packet Receive()
|
||||
{
|
||||
_recvSemaphore.Wait();
|
||||
try
|
||||
{
|
||||
Packet packet = new Packet();
|
||||
packet.Receive(_stream);
|
||||
return packet;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_recvSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<Packet> ReceiveAsync()
|
||||
{
|
||||
await _recvSemaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
Packet packet = new Packet();
|
||||
await packet.ReceiveAsync(_stream);
|
||||
return packet;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_recvSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
internal void Send(Packet packet)
|
||||
{
|
||||
_sendSemaphore.Wait();
|
||||
try
|
||||
{
|
||||
packet.Send(_stream);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sendSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task SendAsync(Packet packet)
|
||||
{
|
||||
await _sendSemaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
await packet.SendAsync(_stream);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_sendSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,88 +1,18 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net;
|
||||
|
||||
namespace Syroot.Worms.Worms2.GameServer
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private static readonly object _logLock = new object();
|
||||
|
||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||
|
||||
private static async Task Main(string[] args)
|
||||
private static void Main()
|
||||
{
|
||||
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Any, 17000);
|
||||
TcpListener listener = new TcpListener(serverEndPoint);
|
||||
listener.Start();
|
||||
Console.WriteLine($"Listening under {serverEndPoint}...");
|
||||
Proxy.Start();
|
||||
return;
|
||||
|
||||
TcpClient? client;
|
||||
while ((client = await listener.AcceptTcpClientAsync()) != null)
|
||||
{
|
||||
_ = HandleClient(client);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task HandleClient(TcpClient client)
|
||||
{
|
||||
IPEndPoint clientEndPoint = (IPEndPoint)client.Client.RemoteEndPoint;
|
||||
Stream stream = client.GetStream();
|
||||
|
||||
Packet query = new Packet();
|
||||
while (true)
|
||||
{
|
||||
// Receive query.
|
||||
await query.ReceiveAsync(stream);
|
||||
LogPacket(query, true, clientEndPoint);
|
||||
|
||||
// Send reply.
|
||||
switch (query.Code)
|
||||
{
|
||||
case PacketCode.Login:
|
||||
query.Code = PacketCode.LoginReply;
|
||||
break;
|
||||
case PacketCode.CreateRoom:
|
||||
break;
|
||||
}
|
||||
LogPacket(query, false, clientEndPoint);
|
||||
await query.SendAsync(stream);
|
||||
}
|
||||
}
|
||||
|
||||
private static void LogPacket(Packet packet, bool received, IPEndPoint endPoint)
|
||||
{
|
||||
static string arrayToString(byte[] array)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (byte b in array)
|
||||
sb.Append($"{b:X2} ");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
lock (_logLock) // if I'd only get laid for lines like this
|
||||
{
|
||||
ConsoleColor prevColor = Console.ForegroundColor;
|
||||
Console.ForegroundColor = received ? ConsoleColor.Cyan : ConsoleColor.Magenta;
|
||||
Console.WriteLine($"{endPoint} {(received ? ">>" : "<<")} {packet.Code}");
|
||||
if (packet.Flags.HasFlag(PacketFlags.HasValue0)) Console.WriteLine($" Value0 = {packet.Value0:X8}");
|
||||
if (packet.Flags.HasFlag(PacketFlags.HasValue1)) Console.WriteLine($" Value1 = {packet.Value1:X8}");
|
||||
if (packet.Flags.HasFlag(PacketFlags.HasValue2)) Console.WriteLine($" Value2 = {packet.Value2:X8}");
|
||||
if (packet.Flags.HasFlag(PacketFlags.HasValue3)) Console.WriteLine($" Value3 = {packet.Value3:X8}");
|
||||
if (packet.Flags.HasFlag(PacketFlags.HasValue4)) Console.WriteLine($" Value4 = {packet.Value4:X8}");
|
||||
if (packet.Flags.HasFlag(PacketFlags.HasValue10)) Console.WriteLine($" Value10 = {packet.Value10:X8}");
|
||||
if (packet.Flags.HasFlag(PacketFlags.HasDataLength)) Console.WriteLine($" DataLength = {packet.Data.Length}");
|
||||
if (packet.Flags.HasFlag(PacketFlags.HasData)) Console.WriteLine($" Data = {arrayToString(packet.Data)}");
|
||||
if (packet.Flags.HasFlag(PacketFlags.HasValue7)) Console.WriteLine($" Value7 = {packet.Value7:X8}");
|
||||
if (packet.Flags.HasFlag(PacketFlags.HasString20)) Console.WriteLine($" String20 = {packet.String20}");
|
||||
if (packet.Flags.HasFlag(PacketFlags.HasString50)) Console.WriteLine($" String50 = {packet.String50}");
|
||||
Console.ForegroundColor = prevColor;
|
||||
}
|
||||
Server server = new Server(new IPEndPoint(IPAddress.Any, 17000));
|
||||
server.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
49
src/tool/Syroot.Worms.Worms2.GameServer/Proxy.cs
Normal file
49
src/tool/Syroot.Worms.Worms2.GameServer/Proxy.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System.Drawing;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Syroot.ColoredConsole;
|
||||
|
||||
namespace Syroot.Worms.Worms2.GameServer
|
||||
{
|
||||
internal class Proxy
|
||||
{
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal static void Start()
|
||||
{
|
||||
// Start listening for clients to intercept.
|
||||
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 17000);
|
||||
TcpListener listener = new TcpListener(localEndPoint);
|
||||
listener.Start();
|
||||
ColorConsole.WriteLine($"Listening under {localEndPoint}...");
|
||||
|
||||
TcpClient? client;
|
||||
while ((client = listener.AcceptTcpClient()) != null)
|
||||
{
|
||||
// Connect to server.
|
||||
TcpClient server = new TcpClient();
|
||||
server.Connect("uk1.servers.worms2.com", 17171);
|
||||
|
||||
PacketConnection clientConnection = new PacketConnection(client);
|
||||
PacketConnection serverConnection = new PacketConnection(server);
|
||||
|
||||
Task.Run(() => Forward(clientConnection, serverConnection, true));
|
||||
Task.Run(() => Forward(serverConnection, clientConnection, false));
|
||||
}
|
||||
}
|
||||
|
||||
private static void Forward(PacketConnection from, PacketConnection to, bool fromClient)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
Packet packet = from.Receive();
|
||||
if (fromClient)
|
||||
ColorConsole.WriteLine(Color.Cyan, $"{from.RemoteEndPoint} >> {to.RemoteEndPoint} | {packet}");
|
||||
else
|
||||
ColorConsole.WriteLine(Color.Magenta, $"{to.RemoteEndPoint} << {from.RemoteEndPoint} | {packet}");
|
||||
to.Send(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
172
src/tool/Syroot.Worms.Worms2.GameServer/Server.cs
Normal file
172
src/tool/Syroot.Worms.Worms2.GameServer/Server.cs
Normal file
@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Syroot.ColoredConsole;
|
||||
|
||||
namespace Syroot.Worms.Worms2.GameServer
|
||||
{
|
||||
internal class Server
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
private int _idCounter;
|
||||
private readonly TcpListener _listener;
|
||||
private readonly Dictionary<PacketCode, Action<PacketConnection, Packet>> _packetHandlers;
|
||||
|
||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||
|
||||
internal Server(IPEndPoint localEndPoint)
|
||||
{
|
||||
_listener = new TcpListener(localEndPoint);
|
||||
_packetHandlers = new Dictionary<PacketCode, Action<PacketConnection, Packet>>
|
||||
{
|
||||
[PacketCode.ListRooms] = OnListRooms,
|
||||
[PacketCode.ListUsers] = OnListUsers,
|
||||
[PacketCode.ListGames] = OnListGames,
|
||||
[PacketCode.Login] = OnLogin,
|
||||
[PacketCode.CreateRoom] = OnCreateRoom,
|
||||
[PacketCode.Join] = OnJoinRoom,
|
||||
[PacketCode.LeaveRoom] = OnLeaveRoom,
|
||||
[PacketCode.CreateGame] = OnCreateGame,
|
||||
[PacketCode.ChatRoom] = OnChatRoom,
|
||||
};
|
||||
}
|
||||
|
||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||
|
||||
internal void Run()
|
||||
{
|
||||
_listener.Start();
|
||||
Console.WriteLine($"Listening under {_listener.LocalEndpoint}...");
|
||||
|
||||
TcpClient? client;
|
||||
while ((client = _listener.AcceptTcpClient()) != null)
|
||||
Task.Run(() => HandleClient(client));
|
||||
}
|
||||
|
||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||
|
||||
private static void SendPacket(PacketConnection connection, Packet packet)
|
||||
{
|
||||
LogPacket(connection, packet, false);
|
||||
connection.Send(packet);
|
||||
}
|
||||
|
||||
private static void LogPacket(PacketConnection connection, Packet packet, bool fromClient)
|
||||
{
|
||||
if (fromClient)
|
||||
ColorConsole.WriteLine(Color.Cyan, $"{connection.RemoteEndPoint} >> {packet}");
|
||||
else
|
||||
ColorConsole.WriteLine(Color.Magenta, $"{connection.RemoteEndPoint} << {packet}");
|
||||
}
|
||||
|
||||
private void HandleClient(TcpClient client)
|
||||
{
|
||||
PacketConnection connection = new PacketConnection(client);
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Receive and log query.
|
||||
Packet packet = connection.Receive();
|
||||
LogPacket(connection, packet, true);
|
||||
|
||||
// Handle query.
|
||||
if (_packetHandlers.TryGetValue(packet.Code, out Action<PacketConnection, Packet>? handler))
|
||||
handler(connection, packet);
|
||||
else
|
||||
ColorConsole.WriteLine(Color.Red, $"Unhandled packet {packet.Code}.");
|
||||
}
|
||||
}
|
||||
catch (EndOfStreamException) { }
|
||||
}
|
||||
|
||||
// ---- Packet Handlers ----
|
||||
|
||||
private void OnListRooms(PacketConnection connection, Packet packet)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.ListItem,
|
||||
value1: 1234,
|
||||
data: Encoding.ASCII.GetBytes("12.34.45.56"),
|
||||
name: "Room Name",
|
||||
session: new SessionInfo
|
||||
{
|
||||
Unknown0 = 0x17171717,
|
||||
Unknown4 = 0x02010101,
|
||||
Nation = Nation.CY,
|
||||
GameVersion = 49,
|
||||
GameRelease = 49,
|
||||
Action0 = 0x01,
|
||||
Action1 = 0x01,
|
||||
Action2 = 0x01,
|
||||
Action3 = 0x00
|
||||
}));
|
||||
|
||||
SendPacket(connection, new Packet(PacketCode.ListEnd));
|
||||
}
|
||||
|
||||
private void OnListUsers(PacketConnection connection, Packet packet)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.ListEnd));
|
||||
}
|
||||
|
||||
private void OnListGames(PacketConnection connection, Packet packet)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.ListItem,
|
||||
value1: 12, // user ID,
|
||||
data: Encoding.ASCII.GetBytes("12.34.45.67"),
|
||||
name: "SomeUser",
|
||||
session: new SessionInfo
|
||||
{
|
||||
GameVersion = 49,
|
||||
Nation = Nation.IT
|
||||
}));
|
||||
SendPacket(connection, new Packet(PacketCode.ListEnd));
|
||||
}
|
||||
|
||||
private void OnLogin(PacketConnection connection, Packet packet)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.LoginReply,
|
||||
value1: Interlocked.Increment(ref _idCounter), // user ID
|
||||
error: 0));
|
||||
}
|
||||
|
||||
private void OnCreateRoom(PacketConnection connection, Packet packet)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.CreateRoomReply,
|
||||
value1: Interlocked.Increment(ref _idCounter), // room ID
|
||||
error: 0));
|
||||
}
|
||||
|
||||
private void OnJoinRoom(PacketConnection connection, Packet packet)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.JoinReply,
|
||||
error: 0));
|
||||
}
|
||||
|
||||
private void OnLeaveRoom(PacketConnection connection, Packet packet)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.LeaveRoomReply,
|
||||
error: 0));
|
||||
}
|
||||
|
||||
private void OnCreateGame(PacketConnection connection, Packet packet)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.CreateGameReply,
|
||||
value1: 123456,
|
||||
error: 0));
|
||||
}
|
||||
|
||||
private void OnChatRoom(PacketConnection connection, Packet packet)
|
||||
{
|
||||
SendPacket(connection, new Packet(PacketCode.ChatRoomReply,
|
||||
error: 0));
|
||||
}
|
||||
}
|
||||
}
|
33
src/tool/Syroot.Worms.Worms2.GameServer/SessionInfo.cs
Normal file
33
src/tool/Syroot.Worms.Worms2.GameServer/SessionInfo.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Syroot.Worms.Worms2.GameServer
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 50)]
|
||||
internal struct SessionInfo
|
||||
{
|
||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||
|
||||
internal uint Unknown0;
|
||||
internal uint Unknown4;
|
||||
internal Nation Nation;
|
||||
internal byte GameVersion;
|
||||
internal byte GameRelease;
|
||||
internal byte Action0;
|
||||
internal byte Action1;
|
||||
internal byte Action2;
|
||||
internal byte Action3;
|
||||
internal byte Unused15;
|
||||
internal ulong Unused16;
|
||||
internal ulong Unused24;
|
||||
internal ulong Unused32;
|
||||
internal ulong Unused40;
|
||||
internal ushort Unused48;
|
||||
|
||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => $"{Unknown0:X8}-{Unknown4:X8} {Nation} {GameVersion}/{GameRelease} "
|
||||
+ $"{Action0:X2}-{Action1:X2}-{Action2:X2}-{Action3:X2} "
|
||||
+ $"{Unused15:X2}{Unused16:X8}{Unused24:X8}{Unused24:X8}{Unused40:X8}{Unused48:X2}";
|
||||
}
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Syroot.ColoredConsole" Version="1.0.0-beta.1" />
|
||||
<ProjectReference Include="..\..\library\Syroot.Worms\Syroot.Worms.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
76
src/tool/Syroot.Worms.Worms2.GameServer/app.manifest
Normal file
76
src/tool/Syroot.Worms.Worms2.GameServer/app.manifest
Normal file
@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<!-- UAC Manifest Options
|
||||
If you want to change the Windows User Account Control level replace the
|
||||
requestedExecutionLevel node with one of the following.
|
||||
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||
|
||||
Specifying requestedExecutionLevel element will disable file and registry virtualization.
|
||||
Remove this element if your application requires this virtualization for backwards
|
||||
compatibility.
|
||||
-->
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- A list of the Windows versions that this application has been tested on
|
||||
and is designed to work with. Uncomment the appropriate elements
|
||||
and Windows will automatically select the most compatible environment. -->
|
||||
|
||||
<!-- Windows Vista -->
|
||||
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
|
||||
|
||||
<!-- Windows 7 -->
|
||||
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
|
||||
|
||||
<!-- Windows 8 -->
|
||||
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
|
||||
|
||||
<!-- Windows 8.1 -->
|
||||
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
|
||||
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
|
||||
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
|
||||
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
|
||||
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
|
||||
<!--
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
-->
|
||||
|
||||
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||
<!--
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
</assembly>
|
Loading…
x
Reference in New Issue
Block a user