Implement ChannelList and ChannelLoginQuery

This commit is contained in:
Ray Koopa 2019-01-19 15:38:48 +01:00
parent 587ac06330
commit b527021df4
14 changed files with 253 additions and 77 deletions

View File

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Net; using System.Net;
using Syroot.Worms.Mgame.GameServer.Packets.Data;
using Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms; using Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms;
namespace Syroot.Worms.Mgame.GameServer namespace Syroot.Worms.Mgame.GameServer
@ -23,11 +24,11 @@ namespace Syroot.Worms.Mgame.GameServer
{ {
// Send login result. // Send login result.
// Create player infos from the given credentials. This would be the place to check for actual accounts. // Create player infos from the given credentials. This would be the place to check for actual accounts.
LoginPlayerInfo[] playerInfos = new LoginPlayerInfo[packet.Logins.Length]; LoginPlayerInfo[] playerInfos = new LoginPlayerInfo[packet.Players.Length];
for (int i = 0; i < packet.Logins.Length; i++) for (int i = 0; i < packet.Players.Length; i++)
{ {
LoginCredentials credentials = packet.Logins[i]; PlayerCredentials credentials = packet.Players[i];
playerInfos[i] = new LoginPlayerInfo { ID = credentials.ID, Rank = 19 }; playerInfos[i] = new LoginPlayerInfo { ID = credentials.UserName, Rank = 19 };
} }
SendPacket(new LoginReply SendPacket(new LoginReply
{ {
@ -104,14 +105,14 @@ namespace Syroot.Worms.Mgame.GameServer
}); });
} }
public void HandleOWChannelConnect(ChannelConnectQuery packet) public void HandleOWChannelConnect(ChannelLoginQuery packet)
{ {
SendPacket(new ChannelConnectReply SendPacket(new ChannelLoginReply
{ {
Result = ChannelConnectResult.Success, Result = ChannelConnectResult.Success,
Player = new ChannelConnectPlayerInfo Player = new ChannelConnectPlayerInfo
{ {
ID = packet.Players[0].ID, ID = packet.Players[0].UserName,
Name = "Your Name", Name = "Your Name",
Experience = 1337, Experience = 1337,
Gold = 1000000, Gold = 1000000,

View File

@ -9,6 +9,7 @@ namespace Syroot.Worms.Mgame.GameServer
public void HandleWwpaConnect(ConnectQuery packet) public void HandleWwpaConnect(ConnectQuery packet)
{ {
// Client fails to detect reply at rare times due to unknown reasons.
SendPacket(new ConnectReply { ServerVersion = _server.Config.Version }); SendPacket(new ConnectReply { ServerVersion = _server.Config.Version });
} }
@ -17,9 +18,82 @@ namespace Syroot.Worms.Mgame.GameServer
SendPacket(new LoginReply { Result = LoginResult.Success }); SendPacket(new LoginReply { Result = LoginResult.Success });
} }
public void HandleWwpaLoginAcknowledge(ChannelListQuery packet) public void HandleWwpaChannelList(ChannelListQuery packet)
{ {
SendPacket(new ChannelListReply { Channels = new List<ChannelInfo>() }); SendPacket(new ChannelListReply
{
Channels = new List<ChannelInfo>
{
new ChannelInfo
{
UnknownA = 0x11,
UnknownD = 0x22,
Name = "Party Hard",
EndPoint = _server.Config.EndPoint,
Type = ChannelType.Normal,
Status = ChannelStatus.Load2
},
new ChannelInfo
{
UnknownA = 0x33,
UnknownD = 0x44,
Name = "Pay 2 Win",
EndPoint = _server.Config.EndPoint,
Type = ChannelType.Normal,
Status = ChannelStatus.Closed
},
new ChannelInfo
{
UnknownA = 0x55,
UnknownD = 0x66,
Name = "Free 4 None",
EndPoint = _server.Config.EndPoint,
Type = ChannelType.Normal,
Status = ChannelStatus.Load1
},
new ChannelInfo
{
UnknownA = 0x77,
UnknownD = 0x88,
Name = "Nothing Goes",
EndPoint = _server.Config.EndPoint,
Type = ChannelType.Roping,
Status = ChannelStatus.Full
},
new ChannelInfo
{
UnknownA = 0x99,
UnknownD = 0xAA,
Name = "Ropers Hell",
EndPoint = _server.Config.EndPoint,
Type = ChannelType.Roping,
Status = ChannelStatus.Load3
},
new ChannelInfo
{
UnknownA = 0xBB,
UnknownD = 0xCC,
Name = "Gay Guilds",
EndPoint = _server.Config.EndPoint,
Type = ChannelType.Guild,
Status = ChannelStatus.Load2
},
new ChannelInfo
{
UnknownA = 0xDD,
UnknownD = 0xEE,
Name = "Enormous Event",
EndPoint = _server.Config.EndPoint,
Type = ChannelType.Event,
Status = ChannelStatus.Closed
}
}
});
}
public void HandleWwpaChannelConnect(ChannelLoginQuery packet)
{
} }
public void HandleWwpaDisconnectQuery(DisconnectQuery packet) { } public void HandleWwpaDisconnectQuery(DisconnectQuery packet) { }

View File

@ -1,4 +1,5 @@
using System.Drawing; using System.Drawing;
using System.Net;
using Syroot.BinaryData.Memory; using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Core.IO namespace Syroot.Worms.Mgame.GameServer.Core.IO
@ -11,10 +12,10 @@ namespace Syroot.Worms.Mgame.GameServer.Core.IO
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
/// <summary> /// <summary>
/// Writes the given <paramref name="color"/> as an RGB0 integer value. /// Writes the given <see cref="Color"/> as an RGB0 integer value.
/// </summary> /// </summary>
/// <param name="self">The extended instance.</param> /// <param name="self">The extended instance.</param>
/// <param name="color">The <see cref="Color"/> to write.</param> /// <param name="value">The value to write.</param>
internal static void WriteColor(this ref SpanWriter self, Color color) internal static void WriteColor(this ref SpanWriter self, Color color)
{ {
self.WriteByte(color.R); self.WriteByte(color.R);
@ -22,5 +23,16 @@ namespace Syroot.Worms.Mgame.GameServer.Core.IO
self.WriteByte(color.B); self.WriteByte(color.B);
self.WriteByte(0); self.WriteByte(0);
} }
/// <summary>
/// Writes the given <see cref="IPEndPoint"/> as 4 separate address bytes and a 2-byte port.
/// </summary>
/// <param name="self">The extended instance.</param>
/// <param name="value">The value to write.</param>
internal static void WriteIPEndPoint(this ref SpanWriter self, IPEndPoint value)
{
self.WriteBytes(value.Address.GetAddressBytes());
self.WriteUInt16((ushort)value.Port);
}
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics;
namespace Syroot.Worms.Mgame.GameServer namespace Syroot.Worms.Mgame.GameServer
{ {
@ -19,6 +20,7 @@ namespace Syroot.Worms.Mgame.GameServer
private void Write(ConsoleColor color, string text) private void Write(ConsoleColor color, string text)
{ {
Debug.WriteLine(text);
lock (_lock) lock (_lock)
{ {
ConsoleColor prevColor = Console.ForegroundColor; ConsoleColor prevColor = Console.ForegroundColor;

View File

@ -238,7 +238,9 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
// Retrieve (decompressed) data. // Retrieve (decompressed) data.
SpanWriter writer = new SpanWriter(new byte[_maxDataSize], encoding: Encodings.Korean); SpanWriter writer = new SpanWriter(new byte[_maxDataSize], encoding: Encodings.Korean);
writer.WriteInt32(id); writer.WriteInt32(id);
packet.Save(ref writer); SpanWriter dataWriter = writer.Slice();
packet.Save(ref dataWriter);
writer.Position += dataWriter.Position;
// Send head and data. // Send head and data.
_tcpStream.WriteUInt16(_wwpaTagStart); _tcpStream.WriteUInt16(_wwpaTagStart);

View File

@ -0,0 +1,32 @@
using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.Data
{
/// <summary>
/// Represents the authentication credentials for a player account sent with LoginQuery and ChanneLoginQuery
/// packets.
/// </summary>
internal class PlayerCredentials
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
/// <summary>
/// Gets or sets the name of the player to login.
/// </summary>
internal string UserName { get; set; }
/// <summary>
/// Gets or sets the password with which the player profile is authenticated.
/// </summary>
internal string Password { get; set; }
}
internal static partial class SpanReaderExtensions
{
internal static PlayerCredentials ReadCredentials(this ref SpanReader self) => new PlayerCredentials
{
UserName = self.ReadString2(),
Password = self.ReadString2()
};
}
}

View File

@ -20,8 +20,8 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
internal override void Load(ref SpanReader reader) internal override void Load(ref SpanReader reader)
{ {
PlayerID = reader.ReadString(); PlayerID = reader.ReadString2();
ChannelEndPoint = new IPEndPoint(IPAddress.Parse(reader.ReadString()), reader.ReadUInt16()); ChannelEndPoint = new IPEndPoint(IPAddress.Parse(reader.ReadString2()), reader.ReadUInt16());
} }
internal override void Save(ref SpanWriter writer) => throw new NotImplementedException(); internal override void Save(ref SpanWriter writer) => throw new NotImplementedException();

View File

@ -2,18 +2,19 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using Syroot.BinaryData.Memory; using Syroot.BinaryData.Memory;
using Syroot.Worms.Mgame.GameServer.Packets.Data;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
/// <summary> /// <summary>
/// Represents the client request for a <see cref="ChannelConnectReply"/>. /// Represents the client request for a <see cref="ChannelLoginReply"/>.
/// </summary> /// </summary>
[Packet(PacketFormat.OWChannel, 0x10)] [Packet(PacketFormat.OWChannel, 0x10)]
internal class ChannelConnectQuery : Packet internal class ChannelLoginQuery : Packet
{ {
// ---- PROPERTIES --------------------------------------------------------------------------------------------- // ---- PROPERTIES ---------------------------------------------------------------------------------------------
public IList<ChannelConnectPlayerCredentials> Players { get; set; } public IList<PlayerCredentials> Players { get; set; }
public IPAddress ClientIP { get; set; } public IPAddress ClientIP { get; set; }
@ -27,11 +28,11 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
internal override void Load(ref SpanReader reader) internal override void Load(ref SpanReader reader)
{ {
Players = new List<ChannelConnectPlayerCredentials> Players = new List<PlayerCredentials>
{ {
new ChannelConnectPlayerCredentials new PlayerCredentials
{ {
ID = reader.ReadStringFix(12), UserName = reader.ReadStringFix(12),
Password = reader.ReadStringFix(12) Password = reader.ReadStringFix(12)
} }
}; };
@ -42,9 +43,9 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
ushort additionalPlayers = reader.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 PlayerCredentials
{ {
ID = reader.ReadStringFix(12), UserName = reader.ReadStringFix(12),
Password = reader.ReadStringFix(12) Password = reader.ReadStringFix(12)
}); });
} }
@ -53,10 +54,4 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
internal override void Save(ref SpanWriter writer) => throw new NotImplementedException(); internal override void Save(ref SpanWriter writer) => throw new NotImplementedException();
} }
internal class ChannelConnectPlayerCredentials
{
public string ID { get; set; } // Max. 12 characters.
public string Password { get; set; } // Max. 12 characters.
}
} }

View File

@ -4,10 +4,10 @@ using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
/// <summary> /// <summary>
/// Represents the server response to a <see cref="ChannelConnectQuery"/>. /// Represents the server response to a <see cref="ChannelLoginQuery"/>.
/// </summary> /// </summary>
[Packet(PacketFormat.OWChannel, 0x11)] [Packet(PacketFormat.OWChannel, 0x11)]
internal class ChannelConnectReply : Packet internal class ChannelLoginReply : Packet
{ {
// ---- PROPERTIES --------------------------------------------------------------------------------------------- // ---- PROPERTIES ---------------------------------------------------------------------------------------------

View File

@ -4,7 +4,7 @@ using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
/// <summary> /// <summary>
/// Represents the client request for a <see cref="ChannelConnectReply"/>. /// Represents the client request for a <see cref="ChannelLoginReply"/>.
/// </summary> /// </summary>
[Packet(PacketFormat.OWChannel, 0x37)] [Packet(PacketFormat.OWChannel, 0x37)]
internal class ChannelTop20Query : Packet internal class ChannelTop20Query : Packet

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Net; using System.Net;
using Syroot.BinaryData.Memory; using Syroot.BinaryData.Memory;
using Syroot.Worms.Mgame.GameServer.Packets.Data;
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
{ {
@ -14,7 +15,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
internal ushort Unknown1 { get; set; } internal ushort Unknown1 { get; set; }
internal LoginCredentials[] Logins { get; set; } internal PlayerCredentials[] Players { get; set; }
internal IPAddress ClientIP { get; set; } internal IPAddress ClientIP { get; set; }
@ -23,24 +24,12 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
internal override void Load(ref SpanReader reader) internal override void Load(ref SpanReader reader)
{ {
Unknown1 = reader.ReadUInt16(); Unknown1 = reader.ReadUInt16();
Logins = new LoginCredentials[reader.ReadUInt16()]; Players = new PlayerCredentials[reader.ReadUInt16()];
for (int i = 0; i < Logins.Length; i++) for (int i = 0; i < Players.Length; i++)
{ Players[i] = reader.ReadCredentials();
Logins[i] = new LoginCredentials ClientIP = IPAddress.Parse(reader.ReadString2());
{
ID = reader.ReadString(),
Password = reader.ReadString()
};
}
ClientIP = IPAddress.Parse(reader.ReadString());
} }
internal override void Save(ref SpanWriter writer) => throw new NotImplementedException(); internal override void Save(ref SpanWriter writer) => throw new NotImplementedException();
} }
internal class LoginCredentials
{
internal string ID { get; set; }
internal string Password { get; set; }
}
} }

View File

@ -1,6 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net;
using Syroot.BinaryData.Memory; using Syroot.BinaryData.Memory;
using Syroot.Worms.Mgame.GameServer.Core.IO;
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
{ {
@ -14,8 +17,6 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
public IList<ChannelInfo> Channels { get; set; } public IList<ChannelInfo> Channels { get; set; }
public byte Unknown { get; set; }
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void Load(ref SpanReader reader) => throw new NotImplementedException(); internal override void Load(ref SpanReader reader) => throw new NotImplementedException();
@ -23,16 +24,65 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
internal override void Save(ref SpanWriter writer) internal override void Save(ref SpanWriter writer)
{ {
writer.WriteUInt16((ushort)Channels.Count); writer.WriteUInt16((ushort)Channels.Count);
writer.WriteByte(Unknown); writer.WriteByte((byte)Channels.Count(x => x.Type == ChannelType.Normal));
writer.Align(4); writer.WriteByte((byte)Channels.Count(x => x.Type == ChannelType.Roping));
writer.WriteByte((byte)Channels.Count(x => x.Type == ChannelType.Guild));
writer.WriteByte((byte)Channels.Count(x => x.Type == ChannelType.Event));
foreach (ChannelInfo channel in Channels) foreach (ChannelInfo channel in Channels)
{ {
writer.WriteUInt16(channel.UnknownA);
writer.WriteString2(channel.Name);
writer.WriteIPEndPoint(channel.EndPoint);
writer.WriteEnum(channel.Type);
writer.WriteByte(channel.UnknownD);
writer.WriteEnum(channel.Status);
} }
} }
} }
internal class ChannelInfo internal class ChannelInfo
{ {
internal ushort UnknownA { get; set; }
/// <summary>
/// Gets or sets the name of the channel. Should not exceed 32 characters.
/// </summary>
internal string Name { get; set; }
/// <summary>
/// Gets or sets the IP address to connect to.
/// </summary>
internal IPEndPoint EndPoint { get; set; }
/// <summary>
/// Gets or sets the category in which the channel will appear.
/// </summary>
internal ChannelType Type { get; set; }
internal byte UnknownD { get; set; }
/// <summary>
/// Gets or sets the channel load or status.
/// </summary>
internal ChannelStatus Status { get; set; }
}
internal enum ChannelType : byte
{
Normal = 0,
Roping = 1,
Guild = 2,
Event = 3
}
internal enum ChannelStatus : byte
{
Closed = 0,
Load1 = 1,
Load2 = 2,
Load3 = 3,
Load4 = 4,
Load5 = 5,
Full = 6
} }
} }

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Net;
using Syroot.BinaryData.Memory;
using Syroot.Worms.Mgame.GameServer.Core.IO;
using Syroot.Worms.Mgame.GameServer.Packets.Data;
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
{
/// <summary>
/// Represents the client request for a <see cref="ChannelConnectReply"/>.
/// </summary>
[Packet(PacketFormat.Wwpa, 0x101)]
internal class ChannelLoginQuery : Packet
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
public IPAddress IPAddress { get; set; }
public IList<PlayerCredentials> Players { get; set; }
public byte UnknownA { get; set; }
public byte UnknownB { get; set; }
public byte[] Remain { get; set; }
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void Load(ref SpanReader reader)
{
IPAddress = IPAddress.Parse(reader.ReadString2());
Players = new List<PlayerCredentials> { reader.ReadCredentials() };
if (reader.ReadBoolean())
Players.Add(reader.ReadCredentials());
UnknownA = reader.ReadByte();
UnknownB = reader.ReadByte();
Remain = reader.ReadToEnd();
}
internal override void Save(ref SpanWriter writer) => throw new NotImplementedException();
}
}

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Syroot.BinaryData.Memory; using Syroot.BinaryData.Memory;
using Syroot.Worms.Mgame.GameServer.Packets.Data;
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
{ {
@ -15,38 +16,17 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
/// <summary> /// <summary>
/// Gets or sets the names and passwords of the players to login. /// Gets or sets the names and passwords of the players to login.
/// </summary> /// </summary>
public IList<LoginCredentials> Logins { get; set; } public IList<PlayerCredentials> Players { get; set; }
// ---- METHODS (INTERNAL) ------------------------------------------------------------------------------------- // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
internal override void Load(ref SpanReader reader) internal override void Load(ref SpanReader reader)
{ {
Logins = new List<LoginCredentials> { ReadCredentials(ref reader) }; Players = new List<PlayerCredentials> { reader.ReadCredentials() };
if (reader.ReadBoolean()) if (reader.ReadBoolean())
Logins.Add(ReadCredentials(ref reader)); Players.Add(reader.ReadCredentials());
} }
internal override void Save(ref SpanWriter writer) => throw new NotImplementedException(); internal override void Save(ref SpanWriter writer) => throw new NotImplementedException();
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
private static LoginCredentials ReadCredentials(ref SpanReader reader) => new LoginCredentials
{
UserName = reader.ReadString2(),
Password = reader.ReadString2()
};
}
internal class LoginCredentials
{
/// <summary>
/// Gets or sets the name of the player to login.
/// </summary>
internal string UserName { get; set; }
/// <summary>
/// Gets or sets the password with which the player profile is authenticated.
/// </summary>
internal string Password { get; set; }
} }
} }