Use new BinaryData package to work on spans rather than streams.

This commit is contained in:
Ray Koopa 2019-01-18 20:44:02 +01:00
parent fcc2c74399
commit 7852ef9d5a
30 changed files with 421 additions and 715 deletions

View File

@ -0,0 +1,156 @@
using System.Collections.Generic;
using System.Drawing;
using System.Net;
using Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms;
namespace Syroot.Worms.Mgame.GameServer
{
internal partial class Client
{
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
public void HandleOWConnect(ConnectQuery packet)
{
SendPacket(new ConnectReply
{
Unknown = _server.Config.Name,
Unknown2 = _server.Config.Region,
Version = _server.Config.Version
});
}
public void HandleOWLogin(LoginQuery packet)
{
// Send login result.
// Create player infos from the given credentials. This would be the place to check for actual accounts.
LoginPlayerInfo[] playerInfos = new LoginPlayerInfo[packet.Logins.Length];
for (int i = 0; i < packet.Logins.Length; i++)
{
LoginCredentials credentials = packet.Logins[i];
playerInfos[i] = new LoginPlayerInfo { ID = credentials.ID, Rank = 19 };
}
SendPacket(new LoginReply
{
Result = LoginResult.Success,
PlayerInfos = playerInfos
});
// Send info text.
SendPacket(new ServerInfoReply
{
Text = "Welcome to the Online Worms Server."
});
// Send channels. The port is abused to identify the channel later on.
SendPacket(new ChannelInfosReply
{
Channels = new[]
{
new ChannelInfo
{
Name = "Test Channel",
EndPoint = new IPEndPoint(_server.Config.IPAddress, 1),
Type = ChannelType.Normal,
Color = Color.LightGreen,
Load = ChannelLoad.Highest
},
new ChannelInfo
{
Name = "Real Channel",
EndPoint = new IPEndPoint(_server.Config.IPAddress, 2),
Type = ChannelType.Normal
},
new ChannelInfo
{
Name = "Boredom Time",
EndPoint = new IPEndPoint(_server.Config.IPAddress, 3),
Type = ChannelType.Normal,
Load = ChannelLoad.Medium
},
new ChannelInfo
{
Name = "Nothing Goes",
EndPoint = new IPEndPoint(_server.Config.IPAddress, 4),
Type = ChannelType.Roping,
Color = Color.Orange,
Coins = 2,
Load = ChannelLoad.Closed
},
new ChannelInfo
{
Name = "Doper's Heaven",
EndPoint = new IPEndPoint(_server.Config.IPAddress, 5),
Type = ChannelType.Roping,
Color = Color.Orange,
Coins = 1,
Load = ChannelLoad.Full
},
new ChannelInfo
{
Name = "Unhelpful Channel",
EndPoint = new IPEndPoint(_server.Config.IPAddress, 6),
Type = ChannelType.Special
}
}
});
}
public void HandleOWChannelEnter(ChannelEnterQuery packet)
{
// Simply allow joining a channel running on the same server port.
SendPacket(new ChannelEnterReply
{
EndPoint = _server.Config.EndPoint
});
}
public void HandleOWChannelConnect(ChannelConnectQuery packet)
{
SendPacket(new ChannelConnectReply
{
Result = ChannelConnectResult.Success,
Player = new ChannelConnectPlayerInfo
{
ID = packet.Players[0].ID,
Name = "Your Name",
Experience = 1337,
Gold = 1000000,
Rank = 19,
GuildMarkIndex = 1,
GuildName = "Your Guild"
}
});
}
public void HandleOWChannelTop20(ChannelTop20Query packet)
{
// Send some simulated player rank infos.
ChannelTop20Reply reply = new ChannelTop20Reply
{
UnknownA = "Test",
Top20 = new List<ChannelTop20Player>(20)
};
for (int i = 0; i < 20; i++)
{
reply.Top20.Add(new ChannelTop20Player
{
Name = $"GoodPlayer{(char)('A' + i)}",
Rank = (ushort)((i + 6) / 3),
Experience = (ulong)(20 - i) * 957
});
}
SendPacket(reply);
// This is the last channel info packet, tell the client to go to the channel screen.
SendPacket(new ChannelEnterFinishReply());
}
public void HandleOWStartSingleGameQuery(StartSingleGameQuery packet)
{
SendPacket(new StartSingleGameReply
{
Success = packet.RoundType == GameStartRoundType.First
});
}
}
}

View File

@ -0,0 +1,14 @@
using Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua;
namespace Syroot.Worms.Mgame.GameServer
{
internal partial class Client
{
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
public void HandleWWPAConnect(ConnectQuery packet)
{
}
}
}

View File

@ -1,18 +1,14 @@
using System.Collections.Generic; using System.Net.Sockets;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Threading; using System.Threading;
using Syroot.Worms.Mgame.GameServer.Core; using Syroot.Worms.Mgame.GameServer.Core;
using Syroot.Worms.Mgame.GameServer.Packets; using Syroot.Worms.Mgame.GameServer.Packets;
using Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms;
namespace Syroot.Worms.Mgame.GameServer namespace Syroot.Worms.Mgame.GameServer
{ {
/// <summary> /// <summary>
/// Represents a connection with an Online Worms client which replies to received packets appropriately. /// Represents a connection with an Online Worms client which replies to received packets appropriately.
/// </summary> /// </summary>
internal class Client : AppConnection internal partial class Client : AppConnection
{ {
// ---- FIELDS ------------------------------------------------------------------------------------------------- // ---- FIELDS -------------------------------------------------------------------------------------------------
@ -30,174 +26,13 @@ namespace Syroot.Worms.Mgame.GameServer
: base(tcpClient) : base(tcpClient)
{ {
_server = server; _server = server;
} PrePacketHandle += (s, e) => _server.Log.Write(LogCategory.Client, FormatPacket(e, ">>"));
PrePacketSend += (s, e) =>
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
public void HandleConnect(ConnectQuery packet)
{
SendPacket(new ConnectReply
{ {
Unknown = _server.Config.Name, _server.Log.Write(LogCategory.Server, FormatPacket(e, "<<"));
Unknown2 = _server.Config.Region, if (_server.Config.SendDelay > 0)
Version = _server.Config.Version Thread.Sleep(_server.Config.SendDelay);
});
}
public void HandleLogin(LoginQuery packet)
{
// Send login result.
// Create player infos from the given credentials. This would be the place to check for actual accounts.
LoginPlayerInfo[] playerInfos = new LoginPlayerInfo[packet.Logins.Length];
for (int i = 0; i < packet.Logins.Length; i++)
{
LoginCredentials credentials = packet.Logins[i];
playerInfos[i] = new LoginPlayerInfo { ID = credentials.ID, Rank = 19 };
}
SendPacket(new LoginReply
{
Result = LoginResult.Success,
PlayerInfos = playerInfos
});
// Send info text.
SendPacket(new ServerInfoReply
{
Text = "Welcome to the Online Worms Server."
});
// Send channels. The port is abused to identify the channel later on.
SendPacket(new ChannelInfosReply
{
Channels = new[]
{
new ChannelInfo
{
Name = "Test Channel",
EndPoint = new IPEndPoint(_server.Config.IPAddress, 1),
Type = ChannelType.Normal,
Color = Color.LightGreen,
Load = ChannelLoad.Highest
},
new ChannelInfo
{
Name = "Real Channel",
EndPoint = new IPEndPoint(_server.Config.IPAddress, 2),
Type = ChannelType.Normal
},
new ChannelInfo
{
Name = "Boredom Time",
EndPoint = new IPEndPoint(_server.Config.IPAddress, 3),
Type = ChannelType.Normal,
Load = ChannelLoad.Medium
},
new ChannelInfo
{
Name = "Nothing Goes",
EndPoint = new IPEndPoint(_server.Config.IPAddress, 4),
Type = ChannelType.Roping,
Color = Color.Orange,
Coins = 2,
Load = ChannelLoad.Closed
},
new ChannelInfo
{
Name = "Doper's Heaven",
EndPoint = new IPEndPoint(_server.Config.IPAddress, 5),
Type = ChannelType.Roping,
Color = Color.Orange,
Coins = 1,
Load = ChannelLoad.Full
},
new ChannelInfo
{
Name = "Unhelpful Channel",
EndPoint = new IPEndPoint(_server.Config.IPAddress, 6),
Type = ChannelType.Special
}
}
});
}
public void HandleChannelEnter(ChannelEnterQuery packet)
{
// Simply allow joining a channel running on the same server port.
SendPacket(new ChannelEnterReply
{
EndPoint = _server.Config.EndPoint
});
}
public void HandleChannelConnect(ChannelConnectQuery packet)
{
SendPacket(new ChannelConnectReply
{
Result = ChannelConnectResult.Success,
Player = new ChannelConnectPlayerInfo
{
ID = packet.Players[0].ID,
Name = "Your Name",
Experience = 1337,
Gold = 1000000,
Rank = 19,
GuildMarkIndex = 1,
GuildName = "Your Guild"
}
});
}
public void HandleChannelTop20(ChannelTop20Query packet)
{
// Send some simulated player rank infos.
ChannelTop20Reply reply = new ChannelTop20Reply
{
UnknownA = "Test",
Top20 = new List<ChannelTop20Player>(20)
}; };
for (int i = 0; i < 20; i++)
{
reply.Top20.Add(new ChannelTop20Player
{
Name = $"GoodPlayer{(char)('A' + i)}",
Rank = (ushort)((i + 6) / 3),
Experience = (ulong)(20 - i) * 957
});
}
SendPacket(reply);
// This is the last channel info packet, tell the client to go to the channel screen.
SendPacket(new ChannelEnterFinishReply());
}
public void HandleStartSingleGameQuery(StartSingleGameQuery packet)
{
SendPacket(new StartSingleGameReply
{
Success = packet.RoundType == GameStartRoundType.First
});
}
#if DEBUG
public void HandleRaw(RawPacket packet)
{
SendPacket(new RawPacket(PacketFormat.Wwpa, 0x0000,
new byte[] { 0x01, 0xC0, 0x01, 0x80, 0x00, 0x00, 0x00 }));
}
#endif
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
protected override void OnPrePacketHandle(Packet packet)
{
_server.Log.Write(LogCategory.Client, FormatPacket(packet, ">>"));
}
protected override void OnPrePacketSend(Packet packet)
{
_server.Log.Write(LogCategory.Server, FormatPacket(packet, "<<"));
if (_server.Config.SendDelay > 0)
Thread.Sleep(_server.Config.SendDelay);
} }
// ---- METHODS (PRIVATE) -------------------------------------------------------------------------------------- // ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------

View File

@ -0,0 +1,26 @@
using System.Drawing;
using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Core.IO
{
/// <summary>
/// Represents extension methods for <see cref="SpanWriter"/> instances.
/// </summary>
internal static class SpanWriterExtensions
{
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
/// <summary>
/// Writes the given <paramref name="color"/> as an RGB0 integer value.
/// </summary>
/// <param name="self">The extended instance.</param>
/// <param name="color">The <see cref="Color"/> to write.</param>
internal static void WriteColor(this SpanWriter self, Color color)
{
self.WriteByte(color.R);
self.WriteByte(color.G);
self.WriteByte(color.B);
self.WriteByte(0);
}
}
}

View File

@ -2,9 +2,9 @@
using System.IO; using System.IO;
using System.Net.Sockets; using System.Net.Sockets;
using Syroot.BinaryData; using Syroot.BinaryData;
using Syroot.BinaryData.Memory;
using Syroot.Worms.Mgame.GameServer.Core.Net; using Syroot.Worms.Mgame.GameServer.Core.Net;
using Syroot.Worms.Mgame.GameServer.Core.Reflection; using Syroot.Worms.Mgame.GameServer.Core.Reflection;
using Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua;
namespace Syroot.Worms.Mgame.GameServer.Packets namespace Syroot.Worms.Mgame.GameServer.Packets
{ {
@ -15,7 +15,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
{ {
// ---- CONSTANTS ---------------------------------------------------------------------------------------------- // ---- CONSTANTS ----------------------------------------------------------------------------------------------
private const int _maxDataSize = 2048; private const int _maxDataSize = 1024;
private const ushort _owTagStartChannel = 0xFFFE; private const ushort _owTagStartChannel = 0xFFFE;
private const ushort _owTagEndChannel = 0xFEFF; private const ushort _owTagEndChannel = 0xFEFF;
private const ushort _wwpaTagStart = 0x2E9E; private const ushort _wwpaTagStart = 0x2E9E;
@ -25,7 +25,6 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
private readonly MethodHandler<Packet> _methodHandler; private readonly MethodHandler<Packet> _methodHandler;
private readonly SafeNetworkStream _tcpStream; private readonly SafeNetworkStream _tcpStream;
private readonly PacketDataStream _packetDataStream;
private bool _disposed; private bool _disposed;
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
@ -40,9 +39,13 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
_methodHandler = new MethodHandler<Packet>(this); _methodHandler = new MethodHandler<Packet>(this);
TcpClient = tcpClient; TcpClient = tcpClient;
_tcpStream = TcpClient.GetSafeStream(); _tcpStream = TcpClient.GetSafeStream();
_packetDataStream = new PacketDataStream();
} }
// ---- EVENTS -------------------------------------------------------------------------------------------------
protected event EventHandler<Packet> PrePacketHandle;
protected event EventHandler<Packet> PrePacketSend;
// ---- PROPERTIES --------------------------------------------------------------------------------------------- // ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary> /// <summary>
@ -68,7 +71,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
Packet packet; Packet packet;
while ((packet = ReceivePacket()) != null) while ((packet = ReceivePacket()) != null)
{ {
OnPrePacketHandle(packet); PrePacketHandle?.Invoke(this, packet);
_methodHandler.Handle(packet); _methodHandler.Handle(packet);
} }
} }
@ -80,41 +83,28 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
if (!_disposed) if (!_disposed)
{ {
if (disposing) if (disposing)
{
TcpClient.Dispose(); TcpClient.Dispose();
_packetDataStream.Dispose();
}
_disposed = true; _disposed = true;
} }
} }
protected virtual void OnPrePacketHandle(Packet packet) { }
protected virtual void OnPrePacketSend(Packet packet) { }
protected bool SendPacket(Packet packet) protected bool SendPacket(Packet packet)
{ {
OnPrePacketSend(packet); PrePacketSend?.Invoke(this, 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 try
{ {
// Handle specific packet formats.
PacketAttribute attribute = PacketFactory.GetAttribute(packet); PacketAttribute attribute = PacketFactory.GetAttribute(packet);
switch (attribute.Format) switch (attribute.Format)
{ {
case PacketFormat.OWChannel: case PacketFormat.OWChannel:
SendOWChannelPacket(attribute.ID); SendOWChannelPacket(attribute.ID, packet);
break; break;
case PacketFormat.OWServer: case PacketFormat.OWServer:
SendOWServerPacket(attribute.ID); SendOWServerPacket(attribute.ID, packet);
break; break;
case PacketFormat.Wwpa: case PacketFormat.Wwpa:
SaveWwpaPacket(packet); SaveWwpaPacket(attribute.ID, packet);
break; break;
default: default:
throw new IOException("Cannot send unknown packet format."); throw new IOException("Cannot send unknown packet format.");
@ -155,7 +145,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
int dataSize = _tcpStream.ReadUInt16(); int dataSize = _tcpStream.ReadUInt16();
int id = _tcpStream.Read1Byte(); int id = _tcpStream.Read1Byte();
// Read data. // Read data.
byte[] data = _tcpStream.ReadBytes(dataSize); SpanReader reader = new SpanReader(_tcpStream.ReadBytes(dataSize), encoding: Encodings.Win949);
// Read tail. // Read tail.
ushort endTag = _tcpStream.ReadUInt16(); ushort endTag = _tcpStream.ReadUInt16();
if (endTag != _owTagEndChannel) if (endTag != _owTagEndChannel)
@ -163,8 +153,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
// Instantiate, deserialize, and return packet. // Instantiate, deserialize, and return packet.
Packet packet = PacketFactory.Create(PacketFormat.OWChannel, id); Packet packet = PacketFactory.Create(PacketFormat.OWChannel, id);
using (PacketDataStream stream = new PacketDataStream(data)) packet.LoadData(reader);
packet.LoadData(stream);
return packet; return packet;
} }
@ -173,12 +162,11 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
// Read head. // Read head.
int dataSize = _tcpStream.ReadUInt16(); int dataSize = _tcpStream.ReadUInt16();
// Read data. // Read data.
byte[] data = _tcpStream.ReadBytes(dataSize); SpanReader reader = new SpanReader(_tcpStream.ReadBytes(dataSize), encoding: Encodings.Win949);
// Instantiate, deserialize, and return packet. // Instantiate, deserialize, and return packet.
Packet packet = PacketFactory.Create(PacketFormat.OWServer, tag); Packet packet = PacketFactory.Create(PacketFormat.OWServer, tag);
using (PacketDataStream stream = new PacketDataStream(data)) packet.LoadData(reader);
packet.LoadData(stream);
return packet; return packet;
} }
@ -203,59 +191,60 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
throw new PacketException("Invalid WWPA packet end tag."); throw new PacketException("Invalid WWPA packet end tag.");
// Instantiate, deserialize, and return packet. // Instantiate, deserialize, and return packet.
int id = 0; SpanReader reader = new SpanReader(PacketCompression.Decompress(compressedData), encoding: Encodings.Win949);
int id = reader.ReadInt32();
Packet packet = PacketFactory.Create(PacketFormat.Wwpa, id); Packet packet = PacketFactory.Create(PacketFormat.Wwpa, id);
ReadOnlySpan<byte> decompressedData = PacketCompression.Decompress(compressedData); packet.LoadData(reader.Slice());
using (PacketDataStream stream = new PacketDataStream(decompressedData.ToArray()))
packet.LoadData(stream);
return packet; return packet;
} }
private void SendOWChannelPacket(int id) private void SendOWChannelPacket(int id, Packet packet)
{ {
// Retrieve data size. Must have at least 1 byte. // Retrieve data. Must have at least 1 byte.
if (_packetDataStream.Position == 0) SpanWriter writer = new SpanWriter(new byte[_maxDataSize], encoding: Encodings.Win949);
_packetDataStream.WriteByte(0); packet.SaveData(writer);
ushort dataSize = (ushort)_packetDataStream.Position; writer.Position = Math.Max(1, writer.Position);
// Send head. // Send head.
_tcpStream.WriteUInt16(_owTagStartChannel); _tcpStream.WriteUInt16(_owTagStartChannel);
_tcpStream.WriteByte(1); _tcpStream.WriteByte(1);
_tcpStream.WriteUInt16(dataSize); _tcpStream.WriteUInt16((ushort)writer.Position);
_tcpStream.WriteByte((byte)id); _tcpStream.WriteByte((byte)id);
// Send data. // Send data.
_tcpStream.Write(_packetDataStream.GetSpan()); _tcpStream.Write(writer.Span);
// Send tail. // Send tail.
_tcpStream.WriteUInt16(_owTagEndChannel); _tcpStream.WriteUInt16(_owTagEndChannel);
} }
private void SendOWServerPacket(int id) private void SendOWServerPacket(int id, Packet packet)
{ {
// Retrieve data size. // Retrieve data.
ushort dataSize = (ushort)_packetDataStream.Position; SpanWriter writer = new SpanWriter(new byte[_maxDataSize], encoding: Encodings.Win949);
packet.SaveData(writer);
// Send head. // Send head.
_tcpStream.WriteUInt16((ushort)id); _tcpStream.WriteUInt16((ushort)id);
_tcpStream.WriteUInt16(dataSize); _tcpStream.WriteUInt16((ushort)writer.Position);
// Write data. // Write data.
_tcpStream.Write(_packetDataStream.GetSpan()); _tcpStream.Write(writer.Span);
} }
private void SaveWwpaPacket(Packet packet) private void SaveWwpaPacket(int id, Packet packet)
{ {
// Retrieve (decompressed) data size. // Retrieve (decompressed) data size.
ReadOnlySpan<byte> decompressedData = _packetDataStream.GetSpan(); SpanWriter writer = new SpanWriter(new byte[_maxDataSize], encoding: Encodings.Win949);
ReadOnlySpan<byte> compressedData = PacketCompression.Compress(decompressedData); packet.SaveData(writer);
ReadOnlySpan<byte> compressedData = PacketCompression.Compress(writer.Span);
// Send head. // Send head.
_tcpStream.WriteUInt16(_wwpaTagStart); _tcpStream.WriteUInt16(_wwpaTagStart);
_tcpStream.WriteBoolean(true); _tcpStream.WriteBoolean(true);
_tcpStream.WriteBoolean(false); _tcpStream.WriteBoolean(false);
_tcpStream.WriteBoolean(true); // isCompressed _tcpStream.WriteBoolean(true); // isCompressed
_tcpStream.WriteUInt16((ushort)decompressedData.Length); _tcpStream.WriteUInt16((ushort)writer.Span.Length);
_tcpStream.WriteUInt32((uint)compressedData.Length); _tcpStream.WriteUInt32((uint)compressedData.Length);
_tcpStream.WriteInt32(1); // Apparently only needs to be same as in tail. _tcpStream.WriteInt32(1); // Apparently only needs to be same as in tail.

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using Syroot.BinaryData; using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -25,33 +25,33 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) internal override void LoadData(SpanReader reader)
{ {
Players = new List<ChannelConnectPlayerCredentials> Players = new List<ChannelConnectPlayerCredentials>
{ {
new ChannelConnectPlayerCredentials new ChannelConnectPlayerCredentials
{ {
ID = stream.ReadString(12), ID = reader.ReadStringFix(12),
Password = stream.ReadString(12) Password = reader.ReadStringFix(12)
} }
}; };
ClientIP = IPAddress.Parse(stream.ReadString(15)); ClientIP = IPAddress.Parse(reader.ReadStringFix(15));
ClientVersion = stream.ReadUInt16(); ClientVersion = reader.ReadUInt16();
UnknownA = stream.ReadUInt16(); UnknownA = reader.ReadUInt16();
ushort additionalPlayers = stream.ReadUInt16(); ushort additionalPlayers = reader.ReadUInt16();
for (int i = 0; i < additionalPlayers; i++) for (int i = 0; i < additionalPlayers; i++)
{ {
Players.Add(new ChannelConnectPlayerCredentials Players.Add(new ChannelConnectPlayerCredentials
{ {
ID = stream.ReadString(12), ID = reader.ReadStringFix(12),
Password = stream.ReadString(12) Password = reader.ReadStringFix(12)
}); });
} }
UnknownB = stream.Read1Byte(); UnknownB = reader.ReadByte();
} }
internal override void SaveData(PacketDataStream stream) => throw new NotImplementedException(); internal override void SaveData(SpanWriter writer) => throw new NotImplementedException();
} }
internal class ChannelConnectPlayerCredentials internal class ChannelConnectPlayerCredentials

View File

@ -1,6 +1,5 @@
using System; using System;
using Syroot.BinaryData; using Syroot.BinaryData.Memory;
using Syroot.Worms.Core.IO;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -18,55 +17,55 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException(); internal override void LoadData(SpanReader reader) => throw new NotImplementedException();
internal override void SaveData(PacketDataStream stream) internal override void SaveData(SpanWriter writer)
{ {
if (Result == ChannelConnectResult.Success) if (Result == ChannelConnectResult.Success)
{ {
stream.WriteBoolean(true, BooleanCoding.Word); writer.WriteBoolean2(true);
stream.WriteUInt16(0); // field_1E710 writer.WriteUInt16(0); // field_1E710
stream.WriteUInt32(0); // field_1E714 writer.WriteUInt32(0); // field_1E714
stream.WriteString(Player.ID, 12); writer.WriteStringFix(Player.ID, 12);
stream.WriteString(Player.Name, 10); writer.WriteStringFix(Player.Name, 10);
stream.WriteUInt16(Player.UnknownB); writer.WriteUInt16(Player.UnknownB);
stream.WriteUInt16(Player.UnknownC); writer.WriteUInt16(Player.UnknownC);
stream.WriteUInt16(Player.UnknownD); writer.WriteUInt16(Player.UnknownD);
stream.WriteUInt16(Player.UnknownE); writer.WriteUInt16(Player.UnknownE);
stream.WriteUInt16(Player.UnknownF); writer.WriteUInt16(Player.UnknownF);
stream.WriteUInt16(Player.UnknownG); writer.WriteUInt16(Player.UnknownG);
stream.WriteUInt16(Player.UnknownH); writer.WriteUInt16(Player.UnknownH);
stream.WriteUInt16(Player.UnknownI); writer.WriteUInt16(Player.UnknownI);
stream.WriteUInt16(Player.UnknownJ); writer.WriteUInt16(Player.UnknownJ);
stream.WriteUInt16(Player.UnknownK); writer.WriteUInt16(Player.UnknownK);
stream.WriteUInt16(Player.UnknownL); writer.WriteUInt16(Player.UnknownL);
stream.WriteUInt16(Player.UnknownM); writer.WriteUInt16(Player.UnknownM);
stream.WriteUInt16(Player.UnknownN); writer.WriteUInt16(Player.UnknownN);
stream.WriteUInt16(Player.UnknownO); writer.WriteUInt16(Player.UnknownO);
stream.WriteUInt64(Player.Experience); writer.WriteUInt64(Player.Experience);
stream.WriteUInt64(Player.Gold); writer.WriteUInt64(Player.Gold);
stream.WriteUInt16(Player.Rank); writer.WriteUInt16(Player.Rank);
stream.WriteUInt16(Player.GuildMarkIndex); writer.WriteUInt16(Player.GuildMarkIndex);
stream.WriteString(Player.GuildName, 12); writer.WriteStringFix(Player.GuildName, 12);
stream.WriteUInt32(Player.UnknownS); writer.WriteUInt32(Player.UnknownS);
stream.WriteUInt32(0); // v29 writer.WriteUInt32(0); // v29
stream.WriteUInt16(0); // v30 writer.WriteUInt16(0); // v30
stream.WriteUInt16(Player.UnknownT); writer.WriteUInt16(Player.UnknownT);
stream.WriteUInt16(Player.UnknownU); writer.WriteUInt16(Player.UnknownU);
stream.WriteUInt16(Player.UnknownV); writer.WriteUInt16(Player.UnknownV);
stream.WriteUInt16(Player.UnknownW); writer.WriteUInt16(Player.UnknownW);
stream.WriteString(Player.UnknownX, 20); writer.WriteStringFix(Player.UnknownX, 20);
stream.WriteString(Player.UnknownY, 20); writer.WriteStringFix(Player.UnknownY, 20);
stream.WriteString(Player.UnknownZ, 20); writer.WriteStringFix(Player.UnknownZ, 20);
stream.WriteString(Player.UnknownAA, 20); writer.WriteStringFix(Player.UnknownAA, 20);
stream.WriteUInt16(Player.UnknownAB); writer.WriteUInt16(Player.UnknownAB);
stream.WriteUInt16(Player.UnknownAC); writer.WriteUInt16(Player.UnknownAC);
// TODO: Handle UnknownAC counting something. // TODO: Handle UnknownAC counting something.
} }
else else
{ {
stream.WriteBoolean(false, BooleanCoding.Word); writer.WriteBoolean2(false);
stream.WriteEnum(Result, true); writer.WriteEnumSafe(Result);
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -11,8 +12,8 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException(); internal override void LoadData(SpanReader reader) => throw new NotImplementedException();
internal override void SaveData(PacketDataStream stream) { } internal override void SaveData(SpanWriter writer) { }
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Net; using System.Net;
using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -17,12 +18,12 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) internal override void LoadData(SpanReader reader)
{ {
PlayerID = stream.ReadString(); PlayerID = reader.ReadString();
ChannelEndPoint = new IPEndPoint(IPAddress.Parse(stream.ReadString()), stream.ReadUInt16()); ChannelEndPoint = new IPEndPoint(IPAddress.Parse(reader.ReadString()), reader.ReadUInt16());
} }
internal override void SaveData(PacketDataStream stream) => throw new NotImplementedException(); internal override void SaveData(SpanWriter writer) => throw new NotImplementedException();
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Net; using System.Net;
using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -18,12 +19,12 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException(); internal override void LoadData(SpanReader reader) => throw new NotImplementedException();
internal override void SaveData(PacketDataStream stream) internal override void SaveData(SpanWriter writer)
{ {
stream.WriteString(EndPoint.Address.ToString()); writer.WriteString(EndPoint.Address.ToString());
stream.WriteUInt16((ushort)EndPoint.Port); writer.WriteUInt16((ushort)EndPoint.Port);
} }
} }
} }

View File

@ -2,7 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Net; using System.Net;
using Syroot.BinaryData; using Syroot.BinaryData.Memory;
using Syroot.Worms.Mgame.GameServer.Core.IO;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -18,24 +19,24 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException(); internal override void LoadData(SpanReader reader) => throw new NotImplementedException();
internal override void SaveData(PacketDataStream stream) internal override void SaveData(SpanWriter writer)
{ {
stream.WriteUInt16((ushort)Channels.Count); writer.WriteUInt16((ushort)Channels.Count);
foreach (ChannelInfo channel in Channels) foreach (ChannelInfo channel in Channels)
{ {
stream.WriteEnum(channel.Type, true); writer.WriteEnumSafe(channel.Type);
stream.WriteByte(channel.Coins); writer.WriteByte(channel.Coins);
stream.WriteColor(channel.Color); writer.WriteColor(channel.Color);
stream.WriteUInt16(1); // ? writer.WriteUInt16(1); // ?
stream.WriteString(channel.Name); writer.WriteString(channel.Name);
stream.WriteByte(0); // ? writer.WriteByte(0); // ?
stream.WriteBytes(channel.EndPoint.Address.GetAddressBytes()); writer.WriteBytes(channel.EndPoint.Address.GetAddressBytes());
stream.WriteUInt16((ushort)channel.EndPoint.Port); writer.WriteUInt16((ushort)channel.EndPoint.Port);
stream.WriteEnum(channel.Load, true); writer.WriteEnumSafe(channel.Load);
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -17,11 +18,11 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) internal override void LoadData(SpanReader reader)
{ {
Count = stream.ReadUInt16(); Count = reader.ReadUInt16();
} }
internal override void SaveData(PacketDataStream stream) => throw new NotImplementedException(); internal override void SaveData(SpanWriter writer) => throw new NotImplementedException();
} }
} }

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Syroot.BinaryData; using Syroot.BinaryData.Memory;
using Syroot.Worms.Core.IO;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -19,16 +18,16 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException(); internal override void LoadData(SpanReader reader) => throw new NotImplementedException();
internal override void SaveData(PacketDataStream stream) internal override void SaveData(SpanWriter writer)
{ {
stream.WriteString(UnknownA, 30); writer.WriteStringFix(UnknownA, 30);
foreach (ChannelTop20Player top20Player in Top20) foreach (ChannelTop20Player top20Player in Top20)
{ {
stream.WriteUInt16(top20Player.Rank); writer.WriteUInt16(top20Player.Rank);
stream.WriteString(top20Player.Name, 12); writer.WriteStringFix(top20Player.Name, 12);
stream.WriteUInt64(top20Player.Experience); writer.WriteUInt64(top20Player.Experience);
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -10,8 +11,8 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) { } internal override void LoadData(SpanReader reader) { }
internal override void SaveData(PacketDataStream stream) => throw new NotImplementedException(); internal override void SaveData(SpanWriter writer) => throw new NotImplementedException();
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -18,14 +19,14 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException(); internal override void LoadData(SpanReader reader) => throw new NotImplementedException();
internal override void SaveData(PacketDataStream stream) internal override void SaveData(SpanWriter writer)
{ {
stream.WriteString(Unknown); writer.WriteString(Unknown);
stream.WriteByte(0); writer.WriteByte(0);
stream.WriteString(Unknown2); writer.WriteString(Unknown2);
stream.WriteUInt16(Version); writer.WriteUInt16(Version);
} }
} }
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Net; using System.Net;
using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -19,22 +20,22 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) internal override void LoadData(SpanReader reader)
{ {
Unknown1 = stream.ReadUInt16(); Unknown1 = reader.ReadUInt16();
Logins = new LoginCredentials[stream.ReadUInt16()]; Logins = new LoginCredentials[reader.ReadUInt16()];
for (int i = 0; i < Logins.Length; i++) for (int i = 0; i < Logins.Length; i++)
{ {
Logins[i] = new LoginCredentials Logins[i] = new LoginCredentials
{ {
ID = stream.ReadString(), ID = reader.ReadString(),
Password = stream.ReadString() Password = reader.ReadString()
}; };
} }
ClientIP = IPAddress.Parse(stream.ReadString()); ClientIP = IPAddress.Parse(reader.ReadString());
} }
internal override void SaveData(PacketDataStream stream) => throw new NotImplementedException(); internal override void SaveData(SpanWriter writer) => throw new NotImplementedException();
} }
internal class LoginCredentials internal class LoginCredentials

View File

@ -1,5 +1,5 @@
using System; using System;
using Syroot.BinaryData; using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -23,25 +23,25 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException(); internal override void LoadData(SpanReader reader) => throw new NotImplementedException();
internal override void SaveData(PacketDataStream stream) internal override void SaveData(SpanWriter writer)
{ {
bool loginSuccessful = Result == LoginResult.Success; bool loginSuccessful = Result == LoginResult.Success;
stream.WriteBoolean(loginSuccessful); writer.WriteBoolean(loginSuccessful);
if (loginSuccessful) if (loginSuccessful)
{ {
stream.WriteBoolean(!HasMessageRights, BooleanCoding.Word); writer.WriteBoolean2(!HasMessageRights);
stream.WriteUInt16((ushort)PlayerInfos.Length); writer.WriteUInt16((ushort)PlayerInfos.Length);
foreach (LoginPlayerInfo playerInfo in PlayerInfos) foreach (LoginPlayerInfo playerInfo in PlayerInfos)
{ {
stream.WriteString(playerInfo.ID); writer.WriteString(playerInfo.ID);
stream.WriteUInt16(playerInfo.Rank); writer.WriteUInt16(playerInfo.Rank);
} }
} }
else else
{ {
stream.WriteEnum(Result, true); writer.WriteEnumSafe(Result);
} }
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -18,11 +19,11 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException(); internal override void LoadData(SpanReader reader) => throw new NotImplementedException();
internal override void SaveData(PacketDataStream stream) internal override void SaveData(SpanWriter writer)
{ {
stream.WriteString(Text.Replace(Environment.NewLine, "\n")); writer.WriteString(Text.Replace(Environment.NewLine, "\n"));
} }
} }
} }

View File

@ -1,5 +1,5 @@
using System; using System;
using Syroot.BinaryData; using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -17,14 +17,14 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) internal override void LoadData(SpanReader reader)
{ {
RoundType = stream.ReadEnum<GameStartRoundType>(true); RoundType = reader.ReadEnumSafe<GameStartRoundType>();
UnknownB = stream.Read1Byte(); UnknownB = reader.ReadByte();
UnknownC = stream.Read1Byte(); UnknownC = reader.ReadByte();
} }
internal override void SaveData(PacketDataStream stream) => throw new NotImplementedException(); internal override void SaveData(SpanWriter writer) => throw new NotImplementedException();
} }
internal enum GameStartRoundType : byte internal enum GameStartRoundType : byte

View File

@ -1,5 +1,5 @@
using System; using System;
using Syroot.BinaryData; using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -15,11 +15,11 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) => throw new NotImplementedException(); internal override void LoadData(SpanReader reader) => throw new NotImplementedException();
internal override void SaveData(PacketDataStream stream) internal override void SaveData(SpanWriter writer)
{ {
stream.WriteBoolean(Success, BooleanCoding.Word); writer.WriteBoolean2(Success);
} }
} }
} }

View File

@ -1,4 +1,6 @@
namespace Syroot.Worms.Mgame.GameServer.Packets using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets
{ {
/// <summary> /// <summary>
/// Represents a packet with an ID specifying its contents. To allow the server to instantiate child packet classes, /// Represents a packet with an ID specifying its contents. To allow the server to instantiate child packet classes,
@ -8,8 +10,8 @@
{ {
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal abstract void LoadData(PacketDataStream stream); internal abstract void LoadData(SpanReader reader);
internal abstract void SaveData(PacketDataStream stream); internal abstract void SaveData(SpanWriter writer);
} }
} }

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Drawing;
using System.IO; using System.IO;
using System.Text; using System.Text;
using Syroot.BinaryData; using Syroot.BinaryData;
@ -22,16 +21,19 @@ namespace Syroot.Worms.Mgame.GameServer
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
} }
internal PacketDataStream() : this(new MemoryStream()) { } internal PacketDataStream(MemoryStream baseStream)
internal PacketDataStream(byte[] buffer) : this(new MemoryStream(buffer)) { }
private PacketDataStream(MemoryStream baseStream)
: base(baseStream, encoding: Encoding.GetEncoding(949), stringCoding: StringCoding.Int16CharCount) : base(baseStream, encoding: Encoding.GetEncoding(949), stringCoding: StringCoding.Int16CharCount)
{ {
_baseStream = baseStream; _baseStream = baseStream;
} }
internal PacketDataStream(MemoryStream baseStream, int offset)
: this(new MemoryStream(baseStream.GetBuffer(), offset, (int)baseStream.Length - offset)) { }
internal PacketDataStream() : this(new MemoryStream()) { }
internal PacketDataStream(byte[] buffer) : this(new MemoryStream(buffer)) { }
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
/// <summary> /// <summary>
@ -48,17 +50,5 @@ namespace Syroot.Worms.Mgame.GameServer
{ {
return ReadBytes((int)(Length - Position)); 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);
}
} }
} }

View File

@ -1,5 +1,5 @@
#if DEBUG #if DEBUG
using Syroot.BinaryData; using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets namespace Syroot.Worms.Mgame.GameServer.Packets
{ {
@ -27,14 +27,14 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(PacketDataStream stream) internal override void LoadData(SpanReader reader)
{ {
Data = stream.ReadBytes((int)stream.Length); Data = reader.ReadBytes(reader.Length);
} }
internal override void SaveData(PacketDataStream stream) internal override void SaveData(SpanWriter writer)
{ {
stream.WriteBytes(Data); writer.WriteBytes(Data);
} }
} }
} }

View File

@ -0,0 +1,17 @@
using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
{
/// <summary>
/// Represents the client request for a <see cref="ConnectReply"/>.
/// </summary>
[Packet(PacketFormat.Wwpa, 0x8001)]
internal class ConnectQuery : Packet
{
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void LoadData(SpanReader reader) { }
internal override void SaveData(SpanWriter writer) { }
}
}

View File

@ -1,329 +0,0 @@
using System;
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
{
/// <summary>
/// Represents packet data encryption as used in WWPA. Note that most code is a direct translation from the original
/// game assembly.
/// </summary>
/// <remarks>
/// The original implementation has the following bugs caused by specific, but rare patterns (in 1000 random blobs
/// of sizes between 1 and 1000 bytes, ~0.5% were decompressed badly):
/// - Bad calculation of decompressed bytes. The same wrong bytes are calculated by this implementation. Such data
/// may be discarded by the game as soon as it causes the final length to differ.
/// - Bad negative source buffer indices cause out-of-bounds memory to be written. Combatted in this implementation
/// by ignoring such writes and keeping 0 bytes instead.
/// </remarks>
public static class PacketCompression
{
// ---- FIELDS -------------------------------------------------------------------------------------------------
private static int _numBytesTransferred = 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) -------------------------------------------------------------------------------------
/// <summary>
/// Compresses the given <paramref name="decompressed"/> data.
/// </summary>
/// <param name="decompressed">The data to compress.</param>
/// <returns>The compressed data.</returns>
public static ReadOnlySpan<byte> Compress(ReadOnlySpan<byte> decompressed)
{
Compressor compressor = new Compressor();
int idx;
int bytesToRepeat;
int idxDword;
int i;
int offset;
int v11;
int v12;
int field_8;
int field_10;
byte c;
int shiftValue1;
idx = 0;
while (idx < decompressed.Length)
{
bytesToRepeat = idx - 0xFF;
if (idx - 0xFF < 0)
bytesToRepeat = 0;
idxDword = 0;
shiftValue1 = -1;
if (bytesToRepeat >= idx)
{
c = decompressed[idx++];
Write(compressor, c);
}
else
{
do
{
for (i = idx; i < decompressed.Length; ++i)
{
if (i - idx >= 0x111)
break;
if (decompressed[bytesToRepeat] != decompressed[i])
break;
++bytesToRepeat;
}
offset = idx - i;
v11 = i - idx;
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_10 = _field_10;
++_field_C;
_field_10 = idxDword - 3 + field_10;
}
else
{
compressor.Shift(idxDword - 2, 4);
compressor.Shift(shiftValue1 - 1, 8);
field_8 = _field_8;
++_field_4;
_field_8 = idxDword - 2 + field_8;
}
idx += idxDword;
++_bufferDwords[idxDword];
}
else
{
c = decompressed[idx++];
Write(compressor, c);
}
}
}
TransferBuffer(compressor);
compressor.Compress(true);
compressor.Shift(0, 4);
compressor.Shift(0, 8);
return compressor.Compressed.AsSpan(0, compressor.Cursor);
}
public static ReadOnlySpan<byte> Decompress(ReadOnlySpan<byte> compressed)
{
Decompressor decompressor = new Decompressor(compressed);
byte[] decompressed = new byte[2048];
int idx;
int bytesToCopyRemain;
int idxCopySrc;
int v12;
int v13;
int bytesToCopy;
idx = 0;
while (true)
{
while (true)
{
while (decompressor.sub_4C0BA0() == 0)
{
bytesToCopy = decompressor.GetBytesToCopy(8);
if (bytesToCopy != -1)
{
++bytesToCopy;
do
{
decompressed[idx++] = decompressor.Read();
--bytesToCopy;
} while (bytesToCopy != 0);
}
}
bytesToCopy = decompressor.GetBytesToCopy(4) + 2;
if (bytesToCopy <= 2)
break;
bytesToCopy = decompressor.GetBytesToCopy(8) + 1;
bytesToCopyRemain = bytesToCopy - 1;
if (bytesToCopy != 0)
{
++bytesToCopyRemain;
do
{
idxCopySrc = idx++ - bytesToCopy;
--bytesToCopyRemain;
if (idxCopySrc >= 0) // Bugfix: Original implementation may calculate negative indices.
decompressed[idx - 1] = decompressed[idxCopySrc];
} while (bytesToCopyRemain != 0);
}
}
bytesToCopy = decompressor.GetBytesToCopy(8);
if (bytesToCopy == 0)
break;
v12 = decompressor.GetBytesToCopy(8);
v13 = v12 + 18;
bytesToCopyRemain = v12 + 17;
if (v13 != 0)
{
++bytesToCopyRemain;
do
{
idxCopySrc = idx++ - bytesToCopy;
--bytesToCopyRemain;
decompressed[idx - 1] = decompressed[idxCopySrc];
} while (bytesToCopyRemain != 0);
}
}
return decompressed.AsSpan(0, idx);
}
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
private static void TransferBuffer(Compressor compressor)
{
int bufferCursor;
int i;
bufferCursor = _bufferCursor;
if (bufferCursor != 0)
{
_numBytesTransferred += bufferCursor;
compressor.Compress(false);
compressor.Shift(_bufferCursor - 1, 8);
for (i = 0; i < _bufferCursor; ++i)
compressor.Write(_buffer[i]);
_bufferCursor = 0;
}
}
private static void Write(Compressor compressor, byte c)
{
_buffer[_bufferCursor++] = c;
if (_bufferCursor >= _buffer.Length)
TransferBuffer(compressor);
}
// ---- CLASSES, STRUCTS & ENUMS -------------------------------------------------------------------------------
private class Compressor
{
internal byte[] Compressed = new byte[1024];
internal int Cursor = 1;
private int _cursorCompress = 0;
private int _shift = 0;
internal void Compress(bool bShift)
{
int shift;
int cursor;
int cursorCompress;
if (bShift)
{
Compressed[_cursorCompress] |= (byte)(0x80 >> _shift);
}
shift = _shift + 1;
_shift = shift;
if (shift == 8)
{
cursor = Cursor;
_cursorCompress = Cursor++;
cursorCompress = _cursorCompress;
Cursor = cursor + 1;
Compressed[cursorCompress] = 0;
_shift = 0;
}
}
internal void Shift(int value, int shiftPlus1)
{
int shift;
int prevShift;
shift = shiftPlus1 - 1;
if (shiftPlus1 != 0)
{
do
{
Compress(((value >> shift) & 1) != 0);
prevShift = shift--;
} while (prevShift != 0);
}
}
internal void Write(byte c)
{
Compressed[Cursor++] = c;
}
}
private class Decompressor
{
private readonly byte[] _compressed;
private int _compressedCursor = 0;
private byte _field_C;
private byte _field_D;
internal Decompressor(ReadOnlySpan<byte> compressed)
{
_compressed = compressed.ToArray();
byte c = Read();
_field_C = (byte)(2 * c | 1);
_field_D = (byte)((uint)c >> 7);
}
internal byte Read()
{
return _compressed[_compressedCursor++];
}
internal int GetBytesToCopy(int numCalls)
{
if (numCalls == 0)
return 0;
int bytesToCopy = 0;
do
{
numCalls--;
bytesToCopy = sub_4C0BA0() + 2 * bytesToCopy;
} while (numCalls != 0);
return bytesToCopy;
}
internal int sub_4C0BA0()
{
byte field_D = _field_D;
_field_D = (byte)(_field_C >> 7);
_field_C *= 2;
if (_field_C == 0)
{
byte c = Read();
_field_C = (byte)(2 * c | 1);
_field_D = (byte)(c >> 7);
}
return field_D;
}
}
}
}

View File

@ -10,7 +10,9 @@
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" 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="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.2.0" />
<PackageReference Include="Syroot.BinaryData.Memory" Version="5.2.0-alpha1" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
<ProjectReference Include="..\Syroot.Worms.Mgame\Syroot.Worms.Mgame.csproj" />
<ProjectReference Include="..\Syroot.Worms\Syroot.Worms.csproj" /> <ProjectReference Include="..\Syroot.Worms\Syroot.Worms.csproj" />
<None Update="ServerConfig.json"> <None Update="ServerConfig.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>

View File

@ -5,11 +5,11 @@ namespace Syroot.Worms.Mgame
/// <summary> /// <summary>
/// Represents instances of common <see cref="Encoding"/> pages. /// Represents instances of common <see cref="Encoding"/> pages.
/// </summary> /// </summary>
internal static class Encodings public static class Encodings
{ {
// ---- FIELDS ------------------------------------------------------------------------------------------------- // ---- FIELDS -------------------------------------------------------------------------------------------------
internal static readonly Encoding Win949; public static readonly Encoding Win949;
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------

View File

@ -27,7 +27,7 @@ namespace Syroot.Worms.Mgame
private static int _field_C = 0; private static int _field_C = 0;
private static int _field_10 = 0; private static int _field_10 = 0;
private static int[] _bufferDwords = new int[512]; private static readonly int[] _bufferDwords = new int[512];
private static byte[] _buffer = new byte[256]; private static byte[] _buffer = new byte[256];
private static int _bufferCursor = 0; private static int _bufferCursor = 0;
@ -39,7 +39,7 @@ namespace Syroot.Worms.Mgame
/// </summary> /// </summary>
/// <param name="decompressed">The data to compress.</param> /// <param name="decompressed">The data to compress.</param>
/// <returns>The compressed data.</returns> /// <returns>The compressed data.</returns>
public static ReadOnlySpan<byte> Compress(ReadOnlySpan<byte> decompressed) public static Span<byte> Compress(ReadOnlySpan<byte> decompressed)
{ {
Compressor compressor = new Compressor(); Compressor compressor = new Compressor();
int idx; int idx;
@ -129,10 +129,9 @@ namespace Syroot.Worms.Mgame
compressor.Shift(0, 8); compressor.Shift(0, 8);
return compressor.Compressed.AsSpan(0, compressor.Cursor); return compressor.Compressed.AsSpan(0, compressor.Cursor);
} }
public static ReadOnlySpan<byte> Decompress(ReadOnlySpan<byte> compressed) public static Span<byte> Decompress(ReadOnlySpan<byte> compressed)
{ {
Decompressor decompressor = new Decompressor(compressed); Decompressor decompressor = new Decompressor(compressed);
byte[] decompressed = new byte[2048]; byte[] decompressed = new byte[2048];
@ -150,7 +149,7 @@ namespace Syroot.Worms.Mgame
{ {
while (true) while (true)
{ {
while (decompressor.sub_4C0BA0() == 0) while (decompressor.Sub_4C0BA0() == 0)
{ {
bytesToCopy = decompressor.GetBytesToCopy(8); bytesToCopy = decompressor.GetBytesToCopy(8);
if (bytesToCopy != -1) if (bytesToCopy != -1)
@ -309,12 +308,12 @@ namespace Syroot.Worms.Mgame
do do
{ {
numCalls--; numCalls--;
bytesToCopy = sub_4C0BA0() + 2 * bytesToCopy; bytesToCopy = Sub_4C0BA0() + 2 * bytesToCopy;
} while (numCalls != 0); } while (numCalls != 0);
return bytesToCopy; return bytesToCopy;
} }
internal int sub_4C0BA0() internal int Sub_4C0BA0()
{ {
byte field_D = _field_D; byte field_D = _field_D;
_field_D = (byte)(_field_C >> 7); _field_D = (byte)(_field_C >> 7);

View File

@ -4,9 +4,7 @@
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Syroot.BinaryData" Version="5.1.0-beta1" /> <PackageReference Include="Syroot.BinaryData" Version="5.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Syroot.Worms.Armageddon.ProjectX\Syroot.Worms.Armageddon.ProjectX.csproj" /> <ProjectReference Include="..\Syroot.Worms.Armageddon.ProjectX\Syroot.Worms.Armageddon.ProjectX.csproj" />
<ProjectReference Include="..\Syroot.Worms.Armageddon\Syroot.Worms.Armageddon.csproj" /> <ProjectReference Include="..\Syroot.Worms.Armageddon\Syroot.Worms.Armageddon.csproj" />
<ProjectReference Include="..\Syroot.Worms.Mgame\Syroot.Worms.Mgame.csproj" /> <ProjectReference Include="..\Syroot.Worms.Mgame\Syroot.Worms.Mgame.csproj" />

View File

@ -19,7 +19,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Syroot.BinaryData.Serialization" Version="5.0.0" /> <PackageReference Include="Syroot.BinaryData.Serialization" Version="5.0.0" />
<PackageReference Include="Syroot.BinaryData" Version="5.1.0-beta1" /> <PackageReference Include="Syroot.BinaryData" Version="5.1.0" />
<PackageReference Include="System.Drawing.Common" Version="4.5.1" /> <PackageReference Include="System.Drawing.Common" Version="4.5.1" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" />
</ItemGroup> </ItemGroup>