mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-05-05 11:19:29 +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;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.IO;
|
using Syroot.Worms.IO;
|
||||||
@ -8,76 +9,174 @@ namespace Syroot.Worms.Worms2.GameServer
|
|||||||
{
|
{
|
||||||
internal class Packet
|
internal class Packet
|
||||||
{
|
{
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
internal int Value10;
|
internal Packet() { }
|
||||||
internal PacketCode Code;
|
|
||||||
internal PacketFlags Flags;
|
internal Packet(PacketCode code,
|
||||||
internal int Value4;
|
int? value0 = null, int? value1 = null, int? value2 = null, int? value3 = null, int? value4 = null,
|
||||||
internal int Value0;
|
int? value10 = null, byte[]? data = null, int? error = null,
|
||||||
internal int Value3;
|
string? name = null, SessionInfo? session = null)
|
||||||
internal int Value1;
|
{
|
||||||
internal int Value2;
|
Code = code;
|
||||||
internal int Value7;
|
Value0 = value0;
|
||||||
internal string String20 = String.Empty;
|
Value1 = value1;
|
||||||
internal string String50 = String.Empty;
|
Value2 = value2;
|
||||||
internal byte[] Data = Array.Empty<byte>();
|
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) -------------------------------------------------------------------------------------
|
// ---- 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)
|
internal async Task ReceiveAsync(Stream stream)
|
||||||
{
|
{
|
||||||
int dataLength = 0;
|
int dataLength = 0;
|
||||||
Code = (PacketCode)await stream.ReadInt32Async();
|
Code = (PacketCode)await stream.ReadInt32Async();
|
||||||
Flags = (PacketFlags)await stream.ReadInt32Async();
|
Flags flags = (Flags)await stream.ReadInt32Async();
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue0)) Value0 = await stream.ReadInt32Async();
|
if (flags.HasFlag(Flags.HasValue0)) Value0 = await stream.ReadInt32Async();
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue1)) Value1 = await stream.ReadInt32Async();
|
if (flags.HasFlag(Flags.HasValue1)) Value1 = await stream.ReadInt32Async();
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue2)) Value2 = await stream.ReadInt32Async();
|
if (flags.HasFlag(Flags.HasValue2)) Value2 = await stream.ReadInt32Async();
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue3)) Value3 = await stream.ReadInt32Async();
|
if (flags.HasFlag(Flags.HasValue3)) Value3 = await stream.ReadInt32Async();
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue4)) Value4 = await stream.ReadInt32Async();
|
if (flags.HasFlag(Flags.HasValue4)) Value4 = await stream.ReadInt32Async();
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue10)) Value10 = await stream.ReadInt32Async();
|
if (flags.HasFlag(Flags.HasValue10)) Value10 = await stream.ReadInt32Async();
|
||||||
if (Flags.HasFlag(PacketFlags.HasDataLength)) dataLength = await stream.ReadInt32Async();
|
if (flags.HasFlag(Flags.HasDataLength)) dataLength = await stream.ReadInt32Async();
|
||||||
if (Flags.HasFlag(PacketFlags.HasData) && dataLength != 0) Data = await stream.ReadBytesAsync(dataLength);
|
if (flags.HasFlag(Flags.HasData) && dataLength != 0) Data = await stream.ReadBytesAsync(dataLength);
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue7)) Value7 = await stream.ReadInt32Async();
|
if (flags.HasFlag(Flags.HasError)) Error = await stream.ReadInt32Async();
|
||||||
if (Flags.HasFlag(PacketFlags.HasString20)) String20 = await stream.ReadFixedStringAsync(20);
|
if (flags.HasFlag(Flags.HasUserName)) Name = await stream.ReadFixedStringAsync(20);
|
||||||
if (Flags.HasFlag(PacketFlags.HasString50)) String50 = await stream.ReadFixedStringAsync(50);
|
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)
|
internal async Task SendAsync(Stream stream)
|
||||||
{
|
{
|
||||||
await stream.WriteInt32Async((int)Code);
|
await stream.WriteInt32Async((int)Code);
|
||||||
await stream.WriteInt32Async((int)Flags);
|
await stream.WriteInt32Async((int)GetFlags());
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue0)) await stream.WriteInt32Async(Value0);
|
if (Value0.HasValue) await stream.WriteInt32Async(Value0.Value);
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue1)) await stream.WriteInt32Async(Value1);
|
if (Value1.HasValue) await stream.WriteInt32Async(Value1.Value);
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue2)) await stream.WriteInt32Async(Value2);
|
if (Value2.HasValue) await stream.WriteInt32Async(Value2.Value);
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue3)) await stream.WriteInt32Async(Value3);
|
if (Value3.HasValue) await stream.WriteInt32Async(Value3.Value);
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue4)) await stream.WriteInt32Async(Value4);
|
if (Value4.HasValue) await stream.WriteInt32Async(Value4.Value);
|
||||||
if (Flags.HasFlag(PacketFlags.HasValue10)) await stream.WriteInt32Async(Value10);
|
if (Value10.HasValue) await stream.WriteInt32Async(Value10.Value);
|
||||||
if (Flags.HasFlag(PacketFlags.HasDataLength)) await stream.WriteInt32Async(Data.Length);
|
if (Data != null)
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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,
|
await stream.WriteInt32Async(Data.Length);
|
||||||
LoginReply = 601,
|
await stream.WriteBytesAsync(Data);
|
||||||
CreateRoom = 700,
|
}
|
||||||
CreateRoomReply = 701
|
if (Error.HasValue) await stream.WriteInt32Async(Error.Value);
|
||||||
|
if (Name != null) await stream.WriteFixedStringAsync(Name, 20);
|
||||||
|
if (Session.HasValue) stream.WriteStruct(Session.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
/// Represents which <see cref="Packet"/> fields are available, as specified at the start of each.
|
|
||||||
/// </summary>
|
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]
|
[Flags]
|
||||||
internal enum PacketFlags
|
private enum Flags : int
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
HasValue0 = 1 << 0,
|
HasValue0 = 1 << 0,
|
||||||
@ -88,8 +187,9 @@ namespace Syroot.Worms.Worms2.GameServer
|
|||||||
HasValue10 = 1 << 10,
|
HasValue10 = 1 << 10,
|
||||||
HasDataLength = 1 << 5,
|
HasDataLength = 1 << 5,
|
||||||
HasData = 1 << 6,
|
HasData = 1 << 6,
|
||||||
HasValue7 = 1 << 7,
|
HasError = 1 << 7,
|
||||||
HasString20 = 1 << 8,
|
HasUserName = 1 << 8,
|
||||||
HasString50 = 1 << 9
|
HasUserInfo = 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.Net;
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.Worms2.GameServer
|
namespace Syroot.Worms.Worms2.GameServer
|
||||||
{
|
{
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
private static readonly object _logLock = new object();
|
|
||||||
|
|
||||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private static async Task Main(string[] args)
|
private static void Main()
|
||||||
{
|
{
|
||||||
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Any, 17000);
|
Proxy.Start();
|
||||||
TcpListener listener = new TcpListener(serverEndPoint);
|
return;
|
||||||
listener.Start();
|
|
||||||
Console.WriteLine($"Listening under {serverEndPoint}...");
|
|
||||||
|
|
||||||
TcpClient? client;
|
Server server = new Server(new IPEndPoint(IPAddress.Any, 17000));
|
||||||
while ((client = await listener.AcceptTcpClientAsync()) != null)
|
server.Run();
|
||||||
{
|
|
||||||
_ = 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp3</TargetFramework>
|
<TargetFramework>netcoreapp3</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Syroot.ColoredConsole" Version="1.0.0-beta.1" />
|
||||||
<ProjectReference Include="..\..\library\Syroot.Worms\Syroot.Worms.csproj" />
|
<ProjectReference Include="..\..\library\Syroot.Worms\Syroot.Worms.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</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