Dynamically implement channel commands.

This commit is contained in:
Ray Koopa 2019-01-22 01:23:20 +01:00
parent a8073367f4
commit 38035742d6
12 changed files with 70 additions and 199 deletions

View File

@ -7,17 +7,30 @@ namespace Syroot.Worms.Mgame.GameServer
{ {
// ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- // ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
public void HandleWwpaChannelCmd(CmdQuery packet) public void HandleWwpaChannelCmd(CmdStartSingleGameQuery packet)
{ {
switch (packet.Data) SendPacket(new CmdStartSingleGameReply
{ {
case StartSingleGameQueryData startSingleGame: Stuff = new List<StartSingleGameReplyStuff>
HandleWwpaChannelCmdStartGame(startSingleGame); {
break; new StartSingleGameReplyStuff
case Unknown6QueryData unknown6: {
HandleWwpaChannelCmdUnknown6(unknown6); UnknownA = 1,
break; UnknownB = 2,
} UnknownC = 3,
UnknownD = 4,
UnknownE = 5
}
}
});
}
public void HandleWwpaChannelCmd(CmdUnknown6Query packet)
{
SendPacket(new CmdUnknown6Reply
{
Result = Unknown6Status.Disconnect
});
} }
public void HandleWwpaChannelDisconnect(DisconnectNotice packet) { } public void HandleWwpaChannelDisconnect(DisconnectNotice packet) { }
@ -56,35 +69,5 @@ namespace Syroot.Worms.Mgame.GameServer
UnknownF = 0x55, UnknownF = 0x55,
}); });
} }
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
// ---- Cmds ----
private void HandleWwpaChannelCmdStartGame(StartSingleGameQueryData data)
{
SendPacket(new CmdReply(new StartSingleGameReplyData
{
Stuff = new List<StartSingleGameReplyStuff>
{
new StartSingleGameReplyStuff
{
UnknownA = 1,
UnknownB = 2,
UnknownC = 3,
UnknownD = 4,
UnknownE = 5
}
}
}));
}
private void HandleWwpaChannelCmdUnknown6(Unknown6QueryData unknown6)
{
SendPacket(new CmdReply(new Unknown6ReplyData
{
Result = Unknown6Status.Disconnect
}));
}
} }
} }

View File

@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
/// Decorates an <see cref="IPacket"/> child class with the ID of the OW channel packet it represents. /// Decorates an <see cref="IPacket"/> child class with the ID of the OW channel packet it represents.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
internal class OWChannelPacketAttribute : Attribute public class OWChannelPacketAttribute : Attribute
{ {
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
@ -15,7 +15,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
/// ID of the packet it represents. /// ID of the packet it represents.
/// </summary> /// </summary>
/// <param name="id">The ID of the packet which the decorated class represents.</param> /// <param name="id">The ID of the packet which the decorated class represents.</param>
internal OWChannelPacketAttribute(byte id) public OWChannelPacketAttribute(byte id)
{ {
ID = id; ID = id;
} }
@ -25,6 +25,6 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
/// <summary> /// <summary>
/// Gets the ID of the packet the decorated class represents. /// Gets the ID of the packet the decorated class represents.
/// </summary> /// </summary>
internal byte ID { get; } public byte ID { get; }
} }
} }

View File

@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
/// Decorates an <see cref="IPacket"/> child class with the ID of the OW server packet it represents. /// Decorates an <see cref="IPacket"/> child class with the ID of the OW server packet it represents.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
internal class OWServerPacketAttribute : Attribute public class OWServerPacketAttribute : Attribute
{ {
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
@ -15,7 +15,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
/// ID of the packet it represents. /// ID of the packet it represents.
/// </summary> /// </summary>
/// <param name="id">The ID of the packet which the decorated class represents.</param> /// <param name="id">The ID of the packet which the decorated class represents.</param>
internal OWServerPacketAttribute(ushort id) public OWServerPacketAttribute(ushort id)
{ {
ID = id; ID = id;
} }
@ -25,6 +25,6 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
/// <summary> /// <summary>
/// Gets the ID of the packet the decorated class represents. /// Gets the ID of the packet the decorated class represents.
/// </summary> /// </summary>
internal ushort ID { get; } public ushort ID { get; }
} }
} }

View File

@ -1,75 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Syroot.BinaryData.Memory;
using Syroot.Worms.Mgame.GameServer.Core.IO;
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
{
// TODO: Ugly, but requires bigger redesign to allow a second identifier in the packets.
/// <summary>
/// Represents the base class for <see cref="CmdQuery"/> and <see cref="CmdReply"/> packets.
/// </summary>
internal abstract class CmdPacket : IPacket
{
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
public ICmdData Data { get; set; }
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
public void Load(ref SpanReader reader)
{
Cmd cmd = reader.ReadEnum<Cmd>();
Data = GetDataClasses().TryGetValue(cmd, out Type type)
? (ICmdData)Activator.CreateInstance(type)
: new RawCmdData(cmd);
Data.Load(ref reader);
}
public void Save(ref SpanWriter writer)
{
Cmd cmd;
switch (Data)
{
case RawCmdData rawChannelCmdData:
cmd = rawChannelCmdData.Cmd;
break;
default:
Type dataType = Data.GetType();
cmd = GetDataClasses().First(x => x.Value == dataType).Key;
break;
}
writer.WriteEnum(cmd);
Data.Save(ref writer);
}
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
protected abstract IDictionary<Cmd, Type> GetDataClasses();
}
internal enum Cmd : int
{
StartSingleGameQuery = 5,
Unknown6 = 6
}
internal interface ICmdData
{
void Load(ref SpanReader reader);
void Save(ref SpanWriter writer);
}
internal class RawCmdData : ICmdData
{
public RawCmdData(Cmd cmd) => Cmd = cmd;
internal Cmd Cmd { get; }
internal byte[] Data { get; set; }
public void Load(ref SpanReader reader) => Data = reader.ReadToEnd();
public void Save(ref SpanWriter writer) => writer.WriteBytes(Data);
}
}

View File

@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
{
/// <summary>
/// Represents the client request for a <see cref="CmdReply"/>.
/// </summary>
[WwpaPacket(0x10A)]
internal class CmdQuery : CmdPacket
{
// ---- FIELDS -------------------------------------------------------------------------------------------------
private static readonly Dictionary<Cmd, Type> _queryClasses = new Dictionary<Cmd, Type>
{
[Cmd.StartSingleGameQuery] = typeof(StartSingleGameQueryData),
[Cmd.Unknown6] = typeof(Unknown6QueryData)
};
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
protected override IDictionary<Cmd, Type> GetDataClasses() => _queryClasses;
}
}

View File

@ -1,31 +0,0 @@
using System;
using System.Collections.Generic;
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
{
/// <summary>
/// Represents the server response to a <see cref="CmdQuery"/>.
/// </summary>
[WwpaPacket(0x10B)]
internal class CmdReply : CmdPacket
{
// ---- FIELDS -------------------------------------------------------------------------------------------------
private static readonly Dictionary<Cmd, Type> _replyClasses = new Dictionary<Cmd, Type>
{
[Cmd.StartSingleGameQuery] = typeof(StartSingleGameReplyData),
[Cmd.Unknown6] = typeof(Unknown6ReplyData)
};
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
internal CmdReply(ICmdData data)
{
Data = data;
}
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
protected override IDictionary<Cmd, Type> GetDataClasses() => _replyClasses;
}
}

View File

@ -3,9 +3,10 @@
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
{ {
/// <summary> /// <summary>
/// Represents the <see cref="CmdQuery"/> client request for a <see cref="StartSingleGameReplyData"/>. /// Represents the <see cref="CmdQuery"/> client request for a <see cref="CmdStartSingleGameReply"/>.
/// </summary> /// </summary>
internal class StartSingleGameQueryData : ICmdData [WwpaPacket(0x10A, Command = 5)]
internal class CmdStartSingleGameQuery : IPacket
{ {
// ---- METHODS (PUBLIC) --------------------------------------------------------------------------------------- // ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------

View File

@ -5,9 +5,10 @@ using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
{ {
/// <summary> /// <summary>
/// Represents the <see cref="CmdQuery"/> server response to a <see cref="StartSingleGameQueryData"/>. /// Represents the <see cref="CmdQuery"/> server response to a <see cref="CmdStartSingleGameQuery"/>.
/// </summary> /// </summary>
internal class StartSingleGameReplyData : ICmdData [WwpaPacket(0x10B, Command = 5)]
internal class CmdStartSingleGameReply : IPacket
{ {
// ---- PROPERTIES --------------------------------------------------------------------------------------------- // ---- PROPERTIES ---------------------------------------------------------------------------------------------

View File

@ -4,9 +4,10 @@ using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
{ {
/// <summary> /// <summary>
/// Represents the <see cref="ChannelCmdQuery"/> client request for a <see cref="Unknown6ReplyData"/>. /// Represents the client request for a <see cref="CmdUnknown6Reply"/>.
/// </summary> /// </summary>
internal class Unknown6QueryData : ICmdData [WwpaPacket(0x10A, Command = 6)]
internal class CmdUnknown6Query : IPacket
{ {
// ---- PROPERTIES --------------------------------------------------------------------------------------------- // ---- PROPERTIES ---------------------------------------------------------------------------------------------

View File

@ -4,9 +4,10 @@ using Syroot.BinaryData.Memory;
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
{ {
/// <summary> /// <summary>
/// Represents the <see cref="CmdQuery"/> server response to a <see cref="Unknown6QueryData"/>. /// Represents the server response to a <see cref="CmdUnknown6Query"/>.
/// </summary> /// </summary>
internal class Unknown6ReplyData : ICmdData [WwpaPacket(0x10B, Command = 6)]
internal class CmdUnknown6Reply : IPacket
{ {
// ---- PROPERTIES --------------------------------------------------------------------------------------------- // ---- PROPERTIES ---------------------------------------------------------------------------------------------

View File

@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
/// Decorates an <see cref="IPacket"/> child class with the ID of the OW server packet it represents. /// Decorates an <see cref="IPacket"/> child class with the ID of the OW server packet it represents.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
internal class WwpaPacketAttribute : Attribute public class WwpaPacketAttribute : Attribute
{ {
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
@ -15,7 +15,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
/// the packet it represents. /// the packet it represents.
/// </summary> /// </summary>
/// <param name="id">The ID of the packet which the decorated class represents.</param> /// <param name="id">The ID of the packet which the decorated class represents.</param>
internal WwpaPacketAttribute(int id) public WwpaPacketAttribute(int id)
{ {
ID = id; ID = id;
} }
@ -25,11 +25,11 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
/// <summary> /// <summary>
/// Gets the ID of the packet the decorated class represents. /// Gets the ID of the packet the decorated class represents.
/// </summary> /// </summary>
internal int ID { get; } public int ID { get; }
/// <summary> /// <summary>
/// Gets or sets an optional command number which the packet represents. /// Gets or sets an optional command number which the packet represents.
/// </summary> /// </summary>
internal int Command { get; set; } public int Command { get; set; }
} }
} }

View File

@ -17,6 +17,8 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
private const ushort _tagStart = 0x2E9E; private const ushort _tagStart = 0x2E9E;
private const ushort _tagEnd = 0x7F3F; private const ushort _tagEnd = 0x7F3F;
private const int _channelCmdQueryID = 0x10A;
private const int _channelCmdReplyID = 0x10B;
private const int _maxDataSize = 1024; private const int _maxDataSize = 1024;
// ---- FIELDS ------------------------------------------------------------------------------------------------- // ---- FIELDS -------------------------------------------------------------------------------------------------
@ -55,8 +57,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
// Instantiate, deserialize, and return packet. // Instantiate, deserialize, and return packet.
SpanReader reader = new SpanReader(PacketCompression.Decompress(compressedData), encoding: Encodings.Korean); SpanReader reader = new SpanReader(PacketCompression.Decompress(compressedData), encoding: Encodings.Korean);
int id = reader.ReadInt32(); IPacket packet = GetPacket(ref reader);
IPacket packet = GetPacket(id);
if (packet == null) if (packet == null)
return null; return null;
SpanReader dataReader = reader.Slice(); SpanReader dataReader = reader.Slice();
@ -66,13 +67,15 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
public bool TrySave(Stream stream, IPacket packet) public bool TrySave(Stream stream, IPacket packet)
{ {
int? id = GetID(packet); WwpaPacketAttribute attrib = GetAttribute(packet);
if (!id.HasValue) if (attrib == null)
return false; return false;
// 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.Value); writer.WriteInt32(attrib.ID);
if (attrib.Command != default)
writer.WriteInt32(attrib.Command);
SpanWriter dataWriter = writer.Slice(); SpanWriter dataWriter = writer.Slice();
packet.Save(ref dataWriter); packet.Save(ref dataWriter);
writer.Position += dataWriter.Position; writer.Position += dataWriter.Position;
@ -107,18 +110,29 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
// ---- METHODS (PRIVATE) -------------------------------------------------------------------------------------- // ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
private IPacket GetPacket(int id) private IPacket GetPacket(ref SpanReader reader)
{ {
Type packetType = _cachedClasses.Where(x => x.attrib.ID == id).FirstOrDefault().type; // Try to find a unique packet via ID only.
return packetType == null int id = reader.ReadInt32();
? new RawPacket { ID = id } var packets = _cachedClasses.Where(x => x.attrib.ID == id).ToList();
: (IPacket)Activator.CreateInstance(packetType); if (packets.Count == 1)
return (IPacket)Activator.CreateInstance(packets[0].type);
// No unique packet found, check for matching command.
int command = reader.ReadInt32();
packets = packets.Where(x => x.attrib.Command == command).ToList();
if (packets.Count == 1)
return (IPacket)Activator.CreateInstance(packets[0].type);
// No unique packet found, return fallback.
reader.Position -= sizeof(int);
return new RawPacket { ID = id };
} }
private int? GetID(IPacket packet) private WwpaPacketAttribute GetAttribute(IPacket packet)
{ {
Type packetType = packet.GetType(); Type packetType = packet.GetType();
return _cachedClasses.Where(x => x.type == packetType).FirstOrDefault().attrib?.ID; return _cachedClasses.Where(x => x.type == packetType).FirstOrDefault().attrib;
} }
} }
} }