diff --git a/src/Syroot.Worms.OnlineWorms.Server/Client.cs b/src/Syroot.Worms.OnlineWorms.Server/Client.cs index cc728aa..3ba9607 100644 --- a/src/Syroot.Worms.OnlineWorms.Server/Client.cs +++ b/src/Syroot.Worms.OnlineWorms.Server/Client.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using System.Collections.Generic; +using System.Drawing; using System.Net; using System.Net.Sockets; using Syroot.Worms.OnlineWorms.Server.Net; @@ -53,7 +54,7 @@ namespace Syroot.Worms.OnlineWorms.Server SendPacket(new LoginReply { Unknown1 = 1, - LoginResult = LoginResult.Success, + Result = LoginResult.Success, PlayerInfos = playerInfos }); @@ -128,12 +129,37 @@ namespace Syroot.Worms.OnlineWorms.Server public void HandleChannelConnect(ChannelConnectQuery packet) { - SendPacket(new RawPacket(PacketType.Channel, 0x11) + SendPacket(new ChannelConnectReply { - Data = new byte[] { 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF } + Result = ChannelConnectResult.Success, + Player = new ChannelConnectPlayerInfo + { + ID = packet.Players[0].ID, + Rank = 19, + GuildMarkIndex = 1 + } }); } + public void HandleChannelUnkInfo(ChannelUnkInfoQuery packet) + { + ChannelUnkInfoReply reply = new ChannelUnkInfoReply + { + UnknownA = "Test", + UnkInfos = new List(20) + }; + for (int i = 0; i < 20; i++) + { + reply.UnkInfos.Add(new ChannelUnkInfo + { + UnknownA = (ushort)i, + UnknownB = $"Bla {i + 1}", + UnknownC = (ulong)i + }); + } + SendPacket(reply); + } + #if DEBUG public void HandleRaw(RawPacket packet) { } #endif @@ -142,7 +168,7 @@ namespace Syroot.Worms.OnlineWorms.Server protected override void OnPrePacketHandle(Packet packet) { - _server.Log.Write(LogCategory.Client, $"{ TcpClient.Client.RemoteEndPoint } >> { packet }"); + _server.Log.Write(LogCategory.Client, $"{TcpClient.Client.RemoteEndPoint} >> {packet}"); } protected override void OnPrePacketSend(Packet packet) diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/PacketStream.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/PacketStream.cs index d886d27..7918cad 100644 --- a/src/Syroot.Worms.OnlineWorms.Server/Net/PacketStream.cs +++ b/src/Syroot.Worms.OnlineWorms.Server/Net/PacketStream.cs @@ -91,6 +91,11 @@ namespace Syroot.Worms.OnlineWorms.Server.Net } } + /// + /// Reads a raw, Windows-949 encoded string using the given number of bytes. + /// + /// The number of bytes to use for the string. + /// The read value. internal string ReadString(int bufferSize) { // Ensure to not try to decode any bytes after the 0 termination. @@ -130,10 +135,16 @@ namespace Syroot.Worms.OnlineWorms.Server.Net _baseStream.WriteByte(0); } + /// + /// Writes a raw, Windows-949 encoded string using the given number of bytes. + /// + /// The value to write. + /// The number of bytes to reserve for the string. internal void WriteString(string value, int bufferSize) { byte[] bytes = new byte[bufferSize]; - _win949Encoding.GetBytes(value, 0, value.Length, bytes, 0); + if (value != null) + _win949Encoding.GetBytes(value, 0, value.Length, bytes, 0); _baseStream.WriteBytes(bytes); } diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelConnectQuery.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelConnectQuery.cs index 6512f9b..3db4985 100644 --- a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelConnectQuery.cs +++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelConnectQuery.cs @@ -6,14 +6,14 @@ using Syroot.BinaryData; namespace Syroot.Worms.OnlineWorms.Server.Net { /// - /// Represents the client request for a . + /// Represents the client request for a . /// [Packet(PacketType.Channel, 0x10)] internal class ChannelConnectQuery : Packet { // ---- PROPERTIES --------------------------------------------------------------------------------------------- - public IList Players { get; set; } + public IList Players { get; set; } public IPAddress ClientIP { get; set; } @@ -27,9 +27,9 @@ namespace Syroot.Worms.OnlineWorms.Server.Net internal override void Deserialize(PacketStream stream) { - Players = new List + Players = new List { - new ChannelConnectPlayer + new ChannelConnectPlayerCredentials { ID = stream.ReadString(12), Password = stream.ReadString(12) @@ -42,7 +42,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net ushort additionalPlayers = stream.ReadUInt16(); for (int i = 0; i < additionalPlayers; i++) { - Players.Add(new ChannelConnectPlayer + Players.Add(new ChannelConnectPlayerCredentials { ID = stream.ReadString(12), Password = stream.ReadString(12) @@ -54,7 +54,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net internal override void Serialize(PacketStream stream) => throw new NotImplementedException(); } - internal class ChannelConnectPlayer + internal class ChannelConnectPlayerCredentials { 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/ChannelConnectReply.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelConnectReply.cs new file mode 100644 index 0000000..45c5088 --- /dev/null +++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelConnectReply.cs @@ -0,0 +1,122 @@ +using System; +using Syroot.BinaryData; + +namespace Syroot.Worms.OnlineWorms.Server.Net +{ + /// + /// Represents the server response to a . + /// + [Packet(PacketType.Channel, 0x11)] + internal class ChannelConnectReply : Packet + { + // ---- PROPERTIES --------------------------------------------------------------------------------------------- + + public ChannelConnectResult Result { get; set; } + + public ChannelConnectPlayerInfo Player { get; set; } + + // ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- + + internal override void Deserialize(PacketStream stream) => throw new NotImplementedException(); + + internal override void Serialize(PacketStream stream) + { + if (Result == ChannelConnectResult.Success) + { + stream.WriteBoolean(true, BooleanCoding.Word); + stream.WriteUInt16(0); // field_1E710 + stream.WriteUInt32(0); // field_1E714 + stream.WriteString(Player.ID, 12); + stream.WriteString(Player.UnknownA, 10); + stream.WriteUInt16(Player.UnknownB); + stream.WriteUInt16(Player.UnknownC); + stream.WriteUInt16(Player.UnknownD); + stream.WriteUInt16(Player.UnknownE); + stream.WriteUInt16(Player.UnknownF); + stream.WriteUInt16(Player.UnknownG); + stream.WriteUInt16(Player.UnknownH); + stream.WriteUInt16(Player.UnknownI); + stream.WriteUInt16(Player.UnknownJ); + stream.WriteUInt16(Player.UnknownK); + stream.WriteUInt16(Player.UnknownL); + stream.WriteUInt16(Player.UnknownM); + stream.WriteUInt16(Player.UnknownN); + stream.WriteUInt16(Player.UnknownO); + stream.WriteUInt64(Player.UnknownP); + stream.WriteUInt64(Player.UnknownQ); + stream.WriteUInt16(Player.Rank); + stream.WriteUInt16(Player.GuildMarkIndex); + stream.WriteString(Player.UnknownR, 12); + stream.WriteUInt32(Player.UnknownS); + stream.WriteUInt32(0); // v29 + stream.WriteUInt16(0); // v30 + stream.WriteUInt16(Player.UnknownT); + stream.WriteUInt16(Player.UnknownU); + stream.WriteUInt16(Player.UnknownV); + stream.WriteUInt16(Player.UnknownW); + stream.WriteString(Player.UnknownX, 20); + stream.WriteString(Player.UnknownY, 20); + stream.WriteString(Player.UnknownZ, 20); + stream.WriteString(Player.UnknownAA, 20); + stream.WriteUInt16(Player.UnknownAB); + stream.WriteUInt16(Player.UnknownAC); + // TODO: Handle UnknownAC counting something. + } + else + { + stream.WriteBoolean(false, BooleanCoding.Word); + stream.WriteEnum(Result, true); + } + } + } + + internal class ChannelConnectPlayerInfo + { + public string ID { get; set; } // Max. 12 chars + public string UnknownA { get; set; } // Max. 10 chars + public ushort UnknownB { get; set; } + public ushort UnknownC { get; set; } + public ushort UnknownD { get; set; } + public ushort UnknownE { get; set; } + public ushort UnknownF { get; set; } + public ushort UnknownG { get; set; } + public ushort UnknownH { get; set; } + public ushort UnknownI { get; set; } + public ushort UnknownJ { get; set; } + public ushort UnknownK { get; set; } + public ushort UnknownL { get; set; } + public ushort UnknownM { get; set; } + public ushort UnknownN { get; set; } + public ushort UnknownO { get; set; } + public ulong UnknownP { get; set; } + public ulong UnknownQ { get; set; } + public ushort Rank { get; set; } + public ushort GuildMarkIndex { get; set; } + public string UnknownR { get; set; } // Max. 12 chars + public uint UnknownS { get; set; } // 4 bytes + public ushort UnknownT { get; set; } + public ushort UnknownU { get; set; } + public ushort UnknownV { get; set; } + public ushort UnknownW { get; set; } + public string UnknownX { get; set; } // Max. 20 chars + public string UnknownY { get; set; } // Max. 20 chars + public string UnknownZ { get; set; } // Max. 20 chars + public string UnknownAA { get; set; } // Max. 20 chars + public ushort UnknownAB { get; set; } + public ushort UnknownAC { get; set; } // counts something + } + + internal enum ChannelConnectResult : ushort + { + Success, + MissingID = 1, + TooManyUsers = 2, + IDInUse = 4, + BadVersion = 5, + Banned = 6, + TooManyIDs = 16, + IDNotRegisteredForCompetition = 17, + TooManyPoints = 18, + Unspecified = UInt16.MaxValue + } +} diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelInfosReply.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelInfosReply.cs index f3038b4..edfe321 100644 --- a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelInfosReply.cs +++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelInfosReply.cs @@ -25,7 +25,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net stream.WriteUInt16((ushort)Channels.Count); foreach (ChannelInfo channel in Channels) { - stream.WriteEnum(channel.Type); + stream.WriteEnum(channel.Type, true); stream.WriteByte(channel.Coins); stream.WriteColor(channel.Color); stream.WriteUInt16(1); // ? @@ -35,7 +35,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net stream.WriteBytes(channel.EndPoint.Address.GetAddressBytes()); stream.WriteUInt16((ushort)channel.EndPoint.Port); - stream.WriteEnum(channel.Load); + stream.WriteEnum(channel.Load, true); } } } diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelUnkInfoQuery.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelUnkInfoQuery.cs new file mode 100644 index 0000000..16c585a --- /dev/null +++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelUnkInfoQuery.cs @@ -0,0 +1,28 @@ +using System; +using Syroot.BinaryData; + +namespace Syroot.Worms.OnlineWorms.Server.Net +{ + /// + /// Represents the client request for a . + /// + [Packet(PacketType.Channel, 0x37)] + internal class ChannelUnkInfoQuery : Packet + { + // ---- PROPERTIES --------------------------------------------------------------------------------------------- + + /// + /// Gets or sets a value determining how many infos are requested. The client apparently always requests 20. + /// + public ushort Count { get; set; } + + // ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- + + internal override void Deserialize(PacketStream stream) + { + Count = stream.ReadUInt16(); + } + + internal override void Serialize(PacketStream stream) => throw new NotImplementedException(); + } +} diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelUnkInfoReply.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelUnkInfoReply.cs new file mode 100644 index 0000000..3076662 --- /dev/null +++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/ChannelUnkInfoReply.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using Syroot.BinaryData; + +namespace Syroot.Worms.OnlineWorms.Server.Net +{ + /// + /// Represents the server response to a . + /// + [Packet(PacketType.Channel, 0x36)] + internal class ChannelUnkInfoReply : Packet + { + // ---- PROPERTIES --------------------------------------------------------------------------------------------- + + public string UnknownA { get; set; } // Max. 30 chars + + public IList UnkInfos { get; set; } // Client always requests 20 elements. + + // ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- + + internal override void Deserialize(PacketStream stream) => throw new NotImplementedException(); + + internal override void Serialize(PacketStream stream) + { + stream.WriteString(UnknownA, 30); + foreach (ChannelUnkInfo unkInfo in UnkInfos) + { + stream.WriteUInt16(unkInfo.UnknownA); + stream.WriteString(unkInfo.UnknownB, 12); + stream.WriteUInt64(unkInfo.UnknownC); + } + } + } + + public class ChannelUnkInfo + { + public ushort UnknownA { get; set; } + public string UnknownB { get; set; } // Max. 12 chars + public ulong UnknownC { get; set; } + } +} diff --git a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/LoginReply.cs b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/LoginReply.cs index e9dd26f..8d78354 100644 --- a/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/LoginReply.cs +++ b/src/Syroot.Worms.OnlineWorms.Server/Net/Packets/LoginReply.cs @@ -11,7 +11,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net { // ---- PROPERTIES --------------------------------------------------------------------------------------------- - internal LoginResult LoginResult { get; set; } + internal LoginResult Result { get; set; } internal ushort Unknown1 { get; set; } @@ -23,7 +23,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net internal override void Serialize(PacketStream stream) { - bool loginSuccessful = LoginResult == LoginResult.Success; + bool loginSuccessful = Result == LoginResult.Success; stream.WriteBoolean(loginSuccessful); if (loginSuccessful) { @@ -37,7 +37,7 @@ namespace Syroot.Worms.OnlineWorms.Server.Net } else { - stream.WriteEnum(LoginResult); + stream.WriteEnum(Result, true); } } }