diff --git a/src/Syroot.Worms.OnlineWorms.Server/Client.cs b/src/Syroot.Worms.OnlineWorms.Server/Client.cs
index 386a43e..dcb4deb 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Client.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Client.cs
@@ -74,7 +74,7 @@ namespace Syroot.Worms.OnlineWorms.Server
EndPoint = new IPEndPoint(IPAddress.Loopback, 1),
Type = ChannelType.Normal,
Color = Color.LightGreen,
- UserLoad = 5
+ Load = ChannelLoad.Highest
},
new ChannelInfo
{
@@ -87,7 +87,7 @@ namespace Syroot.Worms.OnlineWorms.Server
Name = "Boredom Time",
EndPoint = new IPEndPoint(IPAddress.Loopback, 3),
Type = ChannelType.Normal,
- UserLoad = 3
+ Load = ChannelLoad.Medium
},
new ChannelInfo
{
@@ -96,7 +96,7 @@ namespace Syroot.Worms.OnlineWorms.Server
Type = ChannelType.Roping,
Color = Color.Orange,
Coins = 2,
- UserLoad = 6
+ Load = ChannelLoad.Closed
},
new ChannelInfo
{
@@ -105,7 +105,7 @@ namespace Syroot.Worms.OnlineWorms.Server
Type = ChannelType.Roping,
Color = Color.Orange,
Coins = 1,
- UserLoad = 5
+ Load = ChannelLoad.Full
},
new ChannelInfo
{
@@ -126,8 +126,12 @@ namespace Syroot.Worms.OnlineWorms.Server
});
}
+ public void HandleChannelConnect(ChannelConnectQuery packet)
+ {
+ }
+
#if DEBUG
- public void HandleRaw(RawQuery packet) { }
+ public void HandleRaw(RawPacket packet) { }
#endif
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/GameConnection.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/GameConnection.cs
index 4f24b49..f9ac5cb 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/GameConnection.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/GameConnection.cs
@@ -16,6 +16,9 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
private const int _maxDataSize = 2048;
+ private const ushort _channelPacketStartTag = 0xFFFE;
+ private const byte _channelPacketStartTag2 = 1;
+ private const ushort _channelPacketEndTag = 0xFEFF;
// ---- FIELDS -------------------------------------------------------------------------------------------------
@@ -123,19 +126,39 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
private Packet ReceivePacket()
{
// Receive the raw packet data.
- ushort id;
+ PacketType type;
+ int id;
ushort dataSize;
try
{
- id = _receiveStream.ReadUInt16();
- dataSize = _receiveStream.ReadUInt16();
- _receiveStream.ReadAll(_receiveBuffer, 0, dataSize);
+ // Handle both server and channel specific packet formats.
+ ushort tag = _receiveStream.ReadUInt16();
+ if (tag == _channelPacketStartTag)
+ {
+ type = PacketType.Channel;
+ if (_receiveStream.Read1Byte() != 1)
+ throw new IOException("Invalid channel packet start tag.");
+ dataSize = _receiveStream.ReadUInt16();
+ id = _receiveStream.Read1Byte();
+ _receiveStream.ReadAll(_receiveBuffer, 0, dataSize - sizeof(byte));
+ _ = _receiveStream.Read1Byte(); // ?
+ ushort endTag = _receiveStream.ReadUInt16();
+ if (endTag != _channelPacketEndTag)
+ throw new IOException("Invalid channel packet end tag.");
+ }
+ else
+ {
+ type = PacketType.Server;
+ id = tag;
+ dataSize = _receiveStream.ReadUInt16();
+ _receiveStream.ReadAll(_receiveBuffer, 0, dataSize);
+ }
}
catch (IOException) { return null; } // The underlying socket closed.
catch (ObjectDisposedException) { return null; } // The underlying stream closed.
// Deserialize and return the packet.
- Packet packet = PacketFactory.Create(id);
+ Packet packet = PacketFactory.Create(type, id);
using (PacketStream readStream = new PacketStream(new MemoryStream(_receiveBuffer, 0, dataSize, false)))
packet.Deserialize(readStream);
return packet;
@@ -153,9 +176,25 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
// Send the data and return success.
try
{
- _receiveStream.WriteUInt16(PacketFactory.GetID(packet));
- _receiveStream.WriteUInt16(dataSize);
- _receiveStream.Write(_sendDataBuffer, 0, dataSize);
+ PacketAttribute attribute = PacketFactory.GetAttribute(packet);
+ switch (attribute.PacketType)
+ {
+ case PacketType.Channel:
+ _receiveStream.WriteUInt16(_channelPacketStartTag);
+ _receiveStream.WriteByte(1);
+ _receiveStream.WriteUInt16(dataSize);
+ _receiveStream.Write(_sendDataBuffer, 0, dataSize);
+ _receiveStream.WriteByte(0); // ?
+ _receiveStream.Write(_channelPacketEndTag);
+ break;
+ case PacketType.Server:
+ _receiveStream.WriteUInt16((ushort)attribute.PacketID);
+ _receiveStream.WriteUInt16(dataSize);
+ _receiveStream.Write(_sendDataBuffer, 0, dataSize);
+ break;
+ default:
+ throw new InvalidOperationException("Cannot send unknown packet type.");
+ }
return true;
}
catch (IOException) { return false; } // A network error appeared, and communication should end.
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/PacketAttribute.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/PacketAttribute.cs
index 0785227..1447d34 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/PacketAttribute.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/PacketAttribute.cs
@@ -15,9 +15,10 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
/// packet it represents.
///
/// The ID of the packet which the decorated class represents.
- public PacketAttribute(ushort id)
+ public PacketAttribute(PacketType type, int id)
{
- ID = id;
+ PacketType = type;
+ PacketID = id;
}
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
@@ -25,6 +26,14 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
///
/// Gets the ID of the packet the decorated class represents.
///
- public ushort ID { get; }
+ public int PacketID { get; }
+
+ public PacketType PacketType { get; }
+ }
+
+ internal enum PacketType
+ {
+ Server,
+ Channel
}
}
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/PacketFactory.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/PacketFactory.cs
index cb69057..64bc670 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/PacketFactory.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/PacketFactory.cs
@@ -12,7 +12,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
{
// ---- FIELDS -------------------------------------------------------------------------------------------------
- private static readonly Dictionary _packetTypes = new Dictionary();
+ private static readonly Dictionary _packetMetas = new Dictionary();
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
@@ -21,11 +21,14 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
// Cache the ID-to-packet-class map.
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
- foreach (PacketAttribute packetAttribute in type.GetCustomAttributes())
+ foreach (PacketAttribute attrib in type.GetCustomAttributes())
{
- if (_packetTypes.ContainsKey(packetAttribute.ID))
- throw new InvalidOperationException($"Packet {packetAttribute.ID} mapped to multiple classes.");
- _packetTypes.Add(packetAttribute.ID, type);
+ if (_packetMetas.ContainsKey(attrib))
+ {
+ throw new InvalidOperationException(
+ $"{attrib.PacketType} packet with ID {attrib.PacketID} mapped to multiple classes.");
+ }
+ _packetMetas.Add(attrib, type);
}
}
}
@@ -38,30 +41,33 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
/// The ID of the packet class to instantiate.
/// The created instance.
/// No class was mapped to the given packet ID.
- internal static Packet Create(ushort id)
+ internal static Packet Create(PacketType type, int id)
{
+ foreach (KeyValuePair packetMeta in _packetMetas)
+ {
+ if (packetMeta.Key.PacketType == type && packetMeta.Key.PacketID == id)
+ return (Packet)Activator.CreateInstance(packetMeta.Value, true);
+ }
+ // No packet metadata matching.
#if DEBUG
- return _packetTypes.TryGetValue(id, out Type type)
- ? (Packet)Activator.CreateInstance(type, true)
- : new RawQuery(id);
-#else
- return (Packet)Activator.CreateInstance(_packetTypes[id], true);
+ return new RawPacket(type, id);
#endif
+ throw new ArgumentException($"{type} packet with ID {id} could not be created.");
}
///
- /// Gets the ID for the class of the given .
+ /// Gets the metadata for the class of the given .
///
- /// The packet, whose class ID will be returned.
- /// The ID of the class.
- internal static ushort GetID(Packet packet)
+ /// The whose metadata will be returned.
+ /// The metadata of the packet class.
+ internal static PacketAttribute GetAttribute(Packet packet)
{
#if DEBUG
- if (packet is RawQuery rawPacket)
- return rawPacket.ID;
+ if (packet is RawPacket rawPacket)
+ return new PacketAttribute(rawPacket.Type, rawPacket.ID);
else
#endif
- return _packetTypes.Where(x => x.Value == packet.GetType()).First().Key;
+ return _packetMetas.Where(x => x.Value == packet.GetType()).First().Key;
}
}
}
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/PacketStream.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/PacketStream.cs
index 2ef13b6..d886d27 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/PacketStream.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/PacketStream.cs
@@ -91,6 +91,15 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
}
}
+ internal string ReadString(int bufferSize)
+ {
+ // Ensure to not try to decode any bytes after the 0 termination.
+ byte[] bytes = _baseStream.ReadBytes(bufferSize);
+ int length = bufferSize;
+ while (bytes[--length] == 0 && length > 0) { }
+ return _win949Encoding.GetString(bytes, 0, length + 1);
+ }
+
///
/// Reads a 2-byte length-prefixed, Windows-949 enoded string.
///
@@ -121,6 +130,13 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
_baseStream.WriteByte(0);
}
+ internal void WriteString(string value, int bufferSize)
+ {
+ byte[] bytes = new byte[bufferSize];
+ _win949Encoding.GetBytes(value, 0, value.Length, bytes, 0);
+ _baseStream.WriteBytes(bytes);
+ }
+
///
/// Writes a 2-byte length-prefixed, Windows-949 encoded string.
///
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelConnectQuery.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelConnectQuery.cs
new file mode 100644
index 0000000..17fcdcf
--- /dev/null
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelConnectQuery.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using Syroot.BinaryData;
+
+namespace Syroot.Worms.OnlineWorms.Server.Net
+{
+ ///
+ /// Represents the client request for a .
+ ///
+ [Packet(PacketType.Channel, 0x10)]
+ internal class ChannelConnectQuery : Packet
+ {
+ // ---- PROPERTIES ---------------------------------------------------------------------------------------------
+
+ public IList Players { get; set; }
+
+ public IPAddress ClientIP { get; set; }
+
+ public ushort ClientVersion { get; set; }
+
+ public ushort UnknownA { get; set; } // Always 1?
+
+ // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
+
+ internal override void Deserialize(PacketStream stream)
+ {
+ Players = new List
+ {
+ new ChannelConnectPlayer
+ {
+ ID = stream.ReadString(12),
+ Password = stream.ReadString(12)
+ }
+ };
+ ClientIP = IPAddress.Parse(stream.ReadString(15));
+ ClientVersion = stream.ReadUInt16();
+ UnknownA = stream.ReadUInt16();
+
+ ushort additionalPlayers = stream.ReadUInt16();
+ for (int i = 0; i < additionalPlayers; i++)
+ {
+ Players.Add(new ChannelConnectPlayer
+ {
+ ID = stream.ReadString(12),
+ Password = stream.ReadString(12)
+ });
+ }
+ }
+
+ internal override void Serialize(PacketStream stream) => throw new NotImplementedException();
+ }
+
+ internal class ChannelConnectPlayer
+ {
+ public string ID { get; set; } // Max. 12 characters.
+ public string Password { get; set; } // Max. 12 characters.
+ }
+}
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelEnterQuery.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelEnterQuery.cs
index 49604e7..274923c 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelEnterQuery.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelEnterQuery.cs
@@ -7,7 +7,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
///
/// Represents the client request for a .
///
- [Packet(0x8034)]
+ [Packet(PacketType.Server, 0x8034)]
internal class ChannelEnterQuery : Packet
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelEnterReply.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelEnterReply.cs
index 7f7629e..cb538da 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelEnterReply.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelEnterReply.cs
@@ -7,7 +7,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
///
/// Represents the server response to a .
///
- [Packet(0x8035)]
+ [Packet(PacketType.Server, 0x8035)]
internal class ChannelEnterReply : Packet
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelInfosReply.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelInfosReply.cs
index 5d4d4a5..f3038b4 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelInfosReply.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelInfosReply.cs
@@ -9,7 +9,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
///
/// Represents an additional server response to a , providing available server channels.
///
- [Packet(0x80C9)]
+ [Packet(PacketType.Server, 0x80C9)]
internal class ChannelInfosReply : Packet
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
@@ -35,7 +35,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
stream.WriteBytes(channel.EndPoint.Address.GetAddressBytes());
stream.WriteUInt16((ushort)channel.EndPoint.Port);
- stream.WriteByte(channel.UserLoad);
+ stream.WriteEnum(channel.Load);
}
}
}
@@ -68,10 +68,9 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
internal Color Color { get; set; } = Color.White;
///
- /// Gets or sets an indicator of the number of players in this channel. 0 hides the channel, and 6 represents a
- /// full channel.
+ /// Gets or sets an indicator or state visualizing the number of players in this channel.
///
- internal byte UserLoad { get; set; } = 1;
+ internal ChannelLoad Load { get; set; } = ChannelLoad.Lowest;
}
internal enum ChannelType : byte
@@ -80,4 +79,16 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
Roping = 1,
Special = 2
}
+
+ internal enum ChannelLoad : byte
+ {
+ Hidden,
+ Lowest,
+ Low,
+ Medium,
+ High,
+ Highest,
+ Full,
+ Closed = 99
+ }
}
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ConnectQuery.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ConnectQuery.cs
index 91bd18f..851b711 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ConnectQuery.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ConnectQuery.cs
@@ -5,7 +5,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
///
/// Represents the client request for a .
///
- [Packet(0x800E)]
+ [Packet(PacketType.Server, 0x800E)]
internal class ConnectQuery : Packet
{
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ConnectReply.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ConnectReply.cs
index e510f2d..91f4646 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ConnectReply.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ConnectReply.cs
@@ -6,7 +6,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
///
/// Represents the server response to a .
///
- [Packet(0x800F)]
+ [Packet(PacketType.Server, 0x800F)]
internal class ConnectReply : Packet
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/LoginQuery.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/LoginQuery.cs
index 2029cc6..fd7d397 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/LoginQuery.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/LoginQuery.cs
@@ -7,7 +7,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
///
/// Represents the client request for a .
///
- [Packet(0x8000)]
+ [Packet(PacketType.Server, 0x8000)]
internal class LoginQuery : Packet
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/LoginReply.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/LoginReply.cs
index a128406..e9dd26f 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/LoginReply.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/LoginReply.cs
@@ -6,7 +6,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
///
/// Represents the server response to a .
///
- [Packet(0x8001)]
+ [Packet(PacketType.Server, 0x8001)]
internal class LoginReply : Packet
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/RawQuery.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/RawQuery.cs
index e20e868..5f8385b 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/RawQuery.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/RawQuery.cs
@@ -1,36 +1,33 @@
#if DEBUG
+using Syroot.BinaryData;
+
namespace Syroot.Worms.OnlineWorms.Server.Net
{
///
- /// Represents a special fallback packet which simply stores any ID and the raw client packet data.
+ /// Represents a special fallback packet which simply stores any ID and the raw packet data.
///
- internal class RawQuery : Packet
+ internal class RawPacket : Packet
{
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
- internal RawQuery(ushort id)
+ internal RawPacket(PacketType type, int id)
{
+ Type = type;
ID = id;
}
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
- internal ushort ID { get; }
+ internal PacketType Type { get; }
+
+ internal int ID { get; }
internal byte[] Data { get; set; }
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
- internal override void Deserialize(PacketStream stream)
- {
- Data = new byte[stream.Length];
- stream.Read(Data, 0, Data.Length);
- }
-
- internal override void Serialize(PacketStream stream)
- {
- stream.Write(Data, 0, Data.Length);
- }
+ internal override void Deserialize(PacketStream stream) => Data = stream.ReadBytes((int)stream.Length);
+ internal override void Serialize(PacketStream stream) => stream.WriteBytes(Data);
}
}
#endif
diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ServerInfoReply.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ServerInfoReply.cs
index 17a6094..fc71baf 100644
--- a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ServerInfoReply.cs
+++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ServerInfoReply.cs
@@ -6,7 +6,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net
/// Represents an additional server response to a , providing informational server
/// screen text.
///
- [Packet(0x8033)]
+ [Packet(PacketType.Server, 0x8033)]
internal class ServerInfoReply : Packet
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------