mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-03-04 09:25:22 +03:00
Delegate packet handling to IPacketFormat.
This commit is contained in:
parent
cc84ed3d5f
commit
a8073367f4
@ -0,0 +1,59 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Channel;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Mgame.GameServer
|
||||||
|
{
|
||||||
|
internal partial class Client
|
||||||
|
{
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public void HandleOWChannelConnect(LoginQuery packet)
|
||||||
|
{
|
||||||
|
SendPacket(new LoginReply
|
||||||
|
{
|
||||||
|
Result = ChannelConnectResult.Success,
|
||||||
|
Player = new ChannelConnectPlayerInfo
|
||||||
|
{
|
||||||
|
ID = packet.Players[0].UserName,
|
||||||
|
Name = "Your Name",
|
||||||
|
Experience = 1337,
|
||||||
|
Gold = 1000000,
|
||||||
|
Rank = 19,
|
||||||
|
GuildMarkIndex = 1,
|
||||||
|
GuildName = "Your Guild"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleOWChannelTop20(Top20Query packet)
|
||||||
|
{
|
||||||
|
// Send some simulated player rank infos.
|
||||||
|
Top20Reply reply = new Top20Reply
|
||||||
|
{
|
||||||
|
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 EnterFinishReply());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleOWChannelStartSingleGameQuery(StartSingleGameQuery packet)
|
||||||
|
{
|
||||||
|
SendPacket(new StartSingleGameReply
|
||||||
|
{
|
||||||
|
Success = packet.RoundType == GameStartRoundType.First
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,7 @@
|
|||||||
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.Data;
|
||||||
using Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms;
|
using Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Server;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer
|
namespace Syroot.Worms.Mgame.GameServer
|
||||||
{
|
{
|
||||||
@ -10,7 +9,7 @@ namespace Syroot.Worms.Mgame.GameServer
|
|||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
public void HandleOWConnect(ConnectQuery packet)
|
public void HandleOWServerConnect(ConnectQuery packet)
|
||||||
{
|
{
|
||||||
SendPacket(new ConnectReply
|
SendPacket(new ConnectReply
|
||||||
{
|
{
|
||||||
@ -20,7 +19,7 @@ namespace Syroot.Worms.Mgame.GameServer
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleOWLogin(LoginQuery packet)
|
public void HandleOWServerLogin(LoginQuery packet)
|
||||||
{
|
{
|
||||||
// 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.
|
||||||
@ -37,7 +36,7 @@ namespace Syroot.Worms.Mgame.GameServer
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Send info text.
|
// Send info text.
|
||||||
SendPacket(new ServerInfoReply
|
SendPacket(new InfoReply
|
||||||
{
|
{
|
||||||
Text = "Welcome to the Online Worms Server."
|
Text = "Welcome to the Online Worms Server."
|
||||||
});
|
});
|
||||||
@ -96,7 +95,7 @@ namespace Syroot.Worms.Mgame.GameServer
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleOWChannelEnter(ChannelEnterQuery packet)
|
public void HandleOWServerChannelEnter(ChannelEnterQuery packet)
|
||||||
{
|
{
|
||||||
// Simply allow joining a channel running on the same server port.
|
// Simply allow joining a channel running on the same server port.
|
||||||
SendPacket(new ChannelEnterReply
|
SendPacket(new ChannelEnterReply
|
||||||
@ -104,54 +103,5 @@ namespace Syroot.Worms.Mgame.GameServer
|
|||||||
EndPoint = _server.Config.EndPoint
|
EndPoint = _server.Config.EndPoint
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleOWChannelConnect(ChannelLoginQuery packet)
|
|
||||||
{
|
|
||||||
SendPacket(new ChannelLoginReply
|
|
||||||
{
|
|
||||||
Result = ChannelConnectResult.Success,
|
|
||||||
Player = new ChannelConnectPlayerInfo
|
|
||||||
{
|
|
||||||
ID = packet.Players[0].UserName,
|
|
||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua;
|
||||||
using Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server;
|
using Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer
|
namespace Syroot.Worms.Mgame.GameServer
|
||||||
@ -107,5 +108,7 @@ namespace Syroot.Worms.Mgame.GameServer
|
|||||||
{
|
{
|
||||||
SendPacket(new OptionsSaveReply { Success = true });
|
SendPacket(new OptionsSaveReply { Success = true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void HandleWwpaRawPacket(RawPacket packet) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,15 @@ 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;
|
||||||
|
using Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua;
|
||||||
|
|
||||||
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 a game client which replies to received packets appropriately.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal partial class Client : AppConnection
|
internal partial class Client : PacketConnection
|
||||||
{
|
{
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -24,25 +26,15 @@ namespace Syroot.Worms.Mgame.GameServer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tcpClient">The <see cref="TcpClient"/> representing the connection to the client.</param>
|
/// <param name="tcpClient">The <see cref="TcpClient"/> representing the connection to the client.</param>
|
||||||
/// <param name="server">The <see cref="Server"/> instance with which this client communicates.</param>
|
/// <param name="server">The <see cref="Server"/> instance with which this client communicates.</param>
|
||||||
internal Client(TcpClient tcpClient, Server server)
|
internal Client(TcpClient tcpClient, Server server) : base(tcpClient,
|
||||||
: base(tcpClient)
|
WwpaPacketFormat.Instance, OWChannelPacketFormat.Instance, OWServerPacketFormat.Instance)
|
||||||
{
|
{
|
||||||
_server = server;
|
_server = server;
|
||||||
PrePacketHandle += (s, e) => _server.Log.Write(LogCategory.Client, FormatPacket(e, ">>"));
|
PrePacketHandle += OnPrePacketHandle;
|
||||||
PrePacketSend += (s, e) =>
|
PrePacketSend += OnPrePacketSend;
|
||||||
{
|
UnhandledException += OnUnhandledException;
|
||||||
_server.Log.Write(LogCategory.Server, FormatPacket(e, "<<"));
|
|
||||||
if (_server.Config.SendDelay > 0)
|
|
||||||
Thread.Sleep(_server.Config.SendDelay);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
public void HandleRawPacket(RawPacket packet) { }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private string FormatPacket(IPacket packet, string direction)
|
private string FormatPacket(IPacket packet, string direction)
|
||||||
@ -50,5 +42,24 @@ namespace Syroot.Worms.Mgame.GameServer
|
|||||||
string packetName = String.Join(' ', packet.GetType().FullName.Split('.').TakeLast(2));
|
string packetName = String.Join(' ', packet.GetType().FullName.Split('.').TakeLast(2));
|
||||||
return $"{TcpClient.Client.RemoteEndPoint} {direction} {packetName}{ObjectDumper.Dump(packet)}";
|
return $"{TcpClient.Client.RemoteEndPoint} {direction} {packetName}{ObjectDumper.Dump(packet)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- EVENTHANDLERS ------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private void OnPrePacketHandle(object sender, IPacket packet)
|
||||||
|
{
|
||||||
|
_server.Log.Write(LogCategory.Client, FormatPacket(packet, ">>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPrePacketSend(object sender, IPacket packet)
|
||||||
|
{
|
||||||
|
_server.Log.Write(LogCategory.Server, FormatPacket(packet, "<<"));
|
||||||
|
if (_server.Config.SendDelay > 0)
|
||||||
|
Thread.Sleep(_server.Config.SendDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnhandledException(object sender, Exception ex)
|
||||||
|
{
|
||||||
|
_server.Log.Write(LogCategory.Error, $"{TcpClient.Client.RemoteEndPoint} {ex.Message}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
63
src/Syroot.Worms.Mgame.GameServer/Core/IO/CachedStream.cs
Normal file
63
src/Syroot.Worms.Mgame.GameServer/Core/IO/CachedStream.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Mgame.GameServer.Core.IO
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a <see cref="Stream"/> which caches the last bytes in a buffer and allows virtual back-seeking even
|
||||||
|
/// in non-seekable streams.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the wrapped <see cref="Stream"/>.</typeparam>
|
||||||
|
internal class CachedStream<T> : WrapperStream<T>
|
||||||
|
where T : Stream
|
||||||
|
{
|
||||||
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private readonly byte[] _cache;
|
||||||
|
private int _virtualPosition;
|
||||||
|
private int _cacheEnd;
|
||||||
|
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
internal CachedStream(T baseStream, int cacheSize, bool leaveOpen = false) : base(baseStream, leaveOpen)
|
||||||
|
{
|
||||||
|
_cache = new byte[cacheSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
// Return bytes from cache first, if possible.
|
||||||
|
int cacheBytes = Math.Min(_cacheEnd - _virtualPosition, count);
|
||||||
|
if (cacheBytes > 0)
|
||||||
|
{
|
||||||
|
Buffer.BlockCopy(_cache, _virtualPosition, buffer, offset, cacheBytes);
|
||||||
|
_virtualPosition += cacheBytes;
|
||||||
|
offset += cacheBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read more bytes from the stream if the cache is not covering all.
|
||||||
|
int streamBytes = count - cacheBytes;
|
||||||
|
if (streamBytes > 0)
|
||||||
|
{
|
||||||
|
if (_cache.Length - _cacheEnd < streamBytes)
|
||||||
|
throw new IOException("Cache cannot hold more bytes.");
|
||||||
|
streamBytes = BaseStream.Read(_cache, _cacheEnd, streamBytes);
|
||||||
|
_cacheEnd += streamBytes;
|
||||||
|
|
||||||
|
Buffer.BlockCopy(_cache, _virtualPosition, buffer, offset, count);
|
||||||
|
_virtualPosition += streamBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cacheBytes + streamBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
internal void MoveToCacheStart()
|
||||||
|
{
|
||||||
|
_virtualPosition = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,7 @@ namespace Syroot.Worms.Mgame.GameServer.Core.IO
|
|||||||
{
|
{
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
internal SafeWrapperStream(T baseStream) : base(baseStream) { }
|
internal SafeWrapperStream(T baseStream, bool leaveOpen = false) : base(baseStream, leaveOpen) { }
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -9,11 +9,23 @@ namespace Syroot.Worms.Mgame.GameServer.Core.IO
|
|||||||
internal class WrapperStream<T> : Stream
|
internal class WrapperStream<T> : Stream
|
||||||
where T : Stream
|
where T : Stream
|
||||||
{
|
{
|
||||||
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private readonly bool _leaveOpen;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
internal WrapperStream(T baseStream)
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="WrapperStream{T}"/> class wrapping the given
|
||||||
|
/// <paramref name="baseStream"/> which it may <paramref name="leaveOpen"/> on dispose.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baseStream">The <see cref="Stream"/> to wrap.</param>
|
||||||
|
/// <param name="leaveOpen"><see langword="true"/> to leave the stream open on <see cref="Dispose()"/>.</param>
|
||||||
|
internal WrapperStream(T baseStream, bool leaveOpen = false)
|
||||||
{
|
{
|
||||||
BaseStream = baseStream;
|
BaseStream = baseStream;
|
||||||
|
_leaveOpen = leaveOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
@ -40,5 +52,17 @@ namespace Syroot.Worms.Mgame.GameServer.Core.IO
|
|||||||
public override long Seek(long offset, SeekOrigin origin) => BaseStream.Seek(offset, origin);
|
public override long Seek(long offset, SeekOrigin origin) => BaseStream.Seek(offset, origin);
|
||||||
public override void SetLength(long value) => BaseStream.SetLength(value);
|
public override void SetLength(long value) => BaseStream.SetLength(value);
|
||||||
public override void Write(byte[] buffer, int offset, int count) => BaseStream.Write(buffer, offset, count);
|
public override void Write(byte[] buffer, int offset, int count) => BaseStream.Write(buffer, offset, count);
|
||||||
|
|
||||||
|
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
return;
|
||||||
|
if (disposing && !_leaveOpen)
|
||||||
|
BaseStream.Dispose();
|
||||||
|
_disposed = true;
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Mgame.GameServer.Core.Reflection
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents utility methods helping with reflection.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ReflectionTools
|
||||||
|
{
|
||||||
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
internal static IList<(Type type, TAttrib attrib)> GetAttributedClasses<TAttrib>()
|
||||||
|
where TAttrib : Attribute
|
||||||
|
{
|
||||||
|
List<(Type, TAttrib)> classes = new List<(Type, TAttrib)>();
|
||||||
|
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
|
||||||
|
foreach (TAttrib attrib in type.GetCustomAttributes<TAttrib>())
|
||||||
|
classes.Add((type, attrib));
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -34,6 +34,7 @@ namespace Syroot.Worms.Mgame.GameServer
|
|||||||
internal enum LogCategory
|
internal enum LogCategory
|
||||||
{
|
{
|
||||||
Info = ConsoleColor.White,
|
Info = ConsoleColor.White,
|
||||||
|
Error = ConsoleColor.Red,
|
||||||
Connect = ConsoleColor.Cyan,
|
Connect = ConsoleColor.Cyan,
|
||||||
Disconnect = ConsoleColor.Magenta,
|
Disconnect = ConsoleColor.Magenta,
|
||||||
Client = ConsoleColor.DarkCyan,
|
Client = ConsoleColor.DarkCyan,
|
||||||
|
@ -1,271 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using Syroot.BinaryData;
|
|
||||||
using Syroot.BinaryData.Memory;
|
|
||||||
using Syroot.Worms.Mgame.GameServer.Core.Net;
|
|
||||||
using Syroot.Worms.Mgame.GameServer.Core.Reflection;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a class capable of dispatching received <see cref="IPacket"/> instances to a corresponding method.
|
|
||||||
/// </summary>
|
|
||||||
internal class AppConnection : IDisposable
|
|
||||||
{
|
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
private const int _maxDataSize = 1024;
|
|
||||||
private const ushort _owTagStartChannel = 0xFFFE;
|
|
||||||
private const ushort _owTagEndChannel = 0xFEFF;
|
|
||||||
private const ushort _wwpaTagStart = 0x2E9E;
|
|
||||||
private const ushort _wwpaTagEnd = 0x7F3F;
|
|
||||||
|
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
private readonly MethodHandler<IPacket> _methodHandler;
|
|
||||||
private readonly SafeNetworkStream _tcpStream;
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="AppConnection"/> class, handling the given
|
|
||||||
/// <paramref name="tcpClient"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tcpClient">The <see cref="System.Net.Sockets.TcpClient"/> to communicate with.</param>
|
|
||||||
internal AppConnection(TcpClient tcpClient)
|
|
||||||
{
|
|
||||||
_methodHandler = new MethodHandler<IPacket>(this);
|
|
||||||
TcpClient = tcpClient;
|
|
||||||
_tcpStream = TcpClient.GetSafeStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- EVENTS -------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
protected event EventHandler<IPacket> PrePacketHandle;
|
|
||||||
protected event EventHandler<IPacket> PrePacketSend;
|
|
||||||
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the <see cref="TcpClient"/> with which the connection communicates.
|
|
||||||
/// </summary>
|
|
||||||
internal TcpClient TcpClient { get; }
|
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
// Do not change this code. Put cleanup code in Dispose(bool disposing).
|
|
||||||
Dispose(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Starts handling packets incoming from this connection and dispatches them to corresponding methods.
|
|
||||||
/// </summary>
|
|
||||||
internal void Listen()
|
|
||||||
{
|
|
||||||
IPacket packet;
|
|
||||||
while ((packet = ReceivePacket()) != null)
|
|
||||||
{
|
|
||||||
PrePacketHandle?.Invoke(this, packet);
|
|
||||||
_methodHandler.Handle(packet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (!_disposed)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
TcpClient.Dispose();
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected bool SendPacket(IPacket packet)
|
|
||||||
{
|
|
||||||
PrePacketSend?.Invoke(this, packet);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Handle specific packet formats.
|
|
||||||
PacketAttribute attribute = PacketFactory.GetAttribute(packet);
|
|
||||||
switch (attribute.Format)
|
|
||||||
{
|
|
||||||
case PacketFormat.OWChannel:
|
|
||||||
SendOWChannelPacket(attribute.ID, packet);
|
|
||||||
break;
|
|
||||||
case PacketFormat.OWServer:
|
|
||||||
SendOWServerPacket(attribute.ID, packet);
|
|
||||||
break;
|
|
||||||
case PacketFormat.Wwpa:
|
|
||||||
SendWwpaPacket(attribute.ID, packet, false);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IOException("Cannot send unknown packet format.");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (IOException) { return false; } // A network error appeared, and communication should end.
|
|
||||||
catch (ObjectDisposedException) { return false; } // The underlying stream was most apparently closed.
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
private IPacket ReceivePacket()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Handle specific packet formats.
|
|
||||||
ushort tag = _tcpStream.ReadUInt16();
|
|
||||||
switch (tag)
|
|
||||||
{
|
|
||||||
case _owTagStartChannel:
|
|
||||||
return ReceiveOWChannelPacket();
|
|
||||||
case _wwpaTagStart:
|
|
||||||
return ReceiveWwpaPacket();
|
|
||||||
default:
|
|
||||||
return ReceiveOWServerPacket(tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException) { return null; } // The underlying socket closed or sent invalid data.
|
|
||||||
catch (ObjectDisposedException) { return null; } // The underlying stream closed.
|
|
||||||
}
|
|
||||||
|
|
||||||
private IPacket ReceiveOWChannelPacket()
|
|
||||||
{
|
|
||||||
// Read head.
|
|
||||||
if (_tcpStream.Read1Byte() != 1)
|
|
||||||
throw new IOException("Invalid OW channel packet start tag.");
|
|
||||||
int dataSize = _tcpStream.ReadUInt16();
|
|
||||||
int id = _tcpStream.Read1Byte();
|
|
||||||
// Read data.
|
|
||||||
SpanReader reader = new SpanReader(_tcpStream.ReadBytes(dataSize), encoding: Encodings.Korean);
|
|
||||||
// Read tail.
|
|
||||||
ushort endTag = _tcpStream.ReadUInt16();
|
|
||||||
if (endTag != _owTagEndChannel)
|
|
||||||
throw new IOException("Invalid OW channel packet end tag.");
|
|
||||||
|
|
||||||
// Instantiate, deserialize, and return packet.
|
|
||||||
IPacket packet = PacketFactory.Create(PacketFormat.OWChannel, id);
|
|
||||||
packet.Load(ref reader);
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IPacket ReceiveOWServerPacket(int tag)
|
|
||||||
{
|
|
||||||
// Read head.
|
|
||||||
int dataSize = _tcpStream.ReadUInt16();
|
|
||||||
// Read data.
|
|
||||||
SpanReader reader = new SpanReader(_tcpStream.ReadBytes(dataSize), encoding: Encodings.Korean);
|
|
||||||
|
|
||||||
// Instantiate, deserialize, and return packet.
|
|
||||||
IPacket packet = PacketFactory.Create(PacketFormat.OWServer, tag);
|
|
||||||
packet.Load(ref reader);
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IPacket ReceiveWwpaPacket()
|
|
||||||
{
|
|
||||||
// Read head.
|
|
||||||
if (!_tcpStream.ReadBoolean())
|
|
||||||
throw new IOException("Unexpected WWPA packet bHead2.");
|
|
||||||
if (_tcpStream.ReadBoolean())
|
|
||||||
throw new IOException("Unexpected WWPA packet bHead3.");
|
|
||||||
if (!_tcpStream.ReadBoolean())
|
|
||||||
throw new IOException("Unexpected WWPA packet bIsCompressed.");
|
|
||||||
int decompressedSize = _tcpStream.ReadUInt16();
|
|
||||||
int compressedSize = _tcpStream.ReadInt32();
|
|
||||||
int idxPacket = _tcpStream.ReadInt32();
|
|
||||||
// Read data.
|
|
||||||
byte[] compressedData = _tcpStream.ReadBytes(compressedSize);
|
|
||||||
// Read tail.
|
|
||||||
if (_tcpStream.ReadInt32() != idxPacket)
|
|
||||||
throw new IOException("Invalid WWPA packet index.");
|
|
||||||
if (_tcpStream.ReadUInt16() != _wwpaTagEnd)
|
|
||||||
throw new IOException("Invalid WWPA packet end tag.");
|
|
||||||
|
|
||||||
// Instantiate, deserialize, and return packet.
|
|
||||||
SpanReader reader = new SpanReader(PacketCompression.Decompress(compressedData), encoding: Encodings.Korean);
|
|
||||||
int id = reader.ReadInt32();
|
|
||||||
IPacket packet = PacketFactory.Create(PacketFormat.Wwpa, id);
|
|
||||||
SpanReader dataReader = reader.Slice();
|
|
||||||
packet.Load(ref dataReader);
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendOWChannelPacket(int id, IPacket packet)
|
|
||||||
{
|
|
||||||
// Retrieve data. Must have at least 1 byte.
|
|
||||||
SpanWriter writer = new SpanWriter(new byte[_maxDataSize], encoding: Encodings.Korean);
|
|
||||||
packet.Save(ref writer);
|
|
||||||
writer.Position = Math.Max(1, writer.Position);
|
|
||||||
|
|
||||||
// Send head.
|
|
||||||
_tcpStream.WriteUInt16(_owTagStartChannel);
|
|
||||||
_tcpStream.WriteByte(1);
|
|
||||||
_tcpStream.WriteUInt16((ushort)writer.Position);
|
|
||||||
_tcpStream.WriteByte((byte)id);
|
|
||||||
|
|
||||||
// Send data.
|
|
||||||
_tcpStream.Write(writer.Span);
|
|
||||||
|
|
||||||
// Send tail.
|
|
||||||
_tcpStream.WriteUInt16(_owTagEndChannel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendOWServerPacket(int id, IPacket packet)
|
|
||||||
{
|
|
||||||
// Retrieve data.
|
|
||||||
SpanWriter writer = new SpanWriter(new byte[_maxDataSize], encoding: Encodings.Korean);
|
|
||||||
packet.Save(ref writer);
|
|
||||||
|
|
||||||
// Send head.
|
|
||||||
_tcpStream.WriteUInt16((ushort)id);
|
|
||||||
_tcpStream.WriteUInt16((ushort)writer.Position);
|
|
||||||
|
|
||||||
// Write data.
|
|
||||||
_tcpStream.Write(writer.Span);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendWwpaPacket(int id, IPacket packet, bool compress)
|
|
||||||
{
|
|
||||||
// Retrieve (decompressed) data.
|
|
||||||
SpanWriter writer = new SpanWriter(new byte[_maxDataSize], encoding: Encodings.Korean);
|
|
||||||
writer.WriteInt32(id);
|
|
||||||
SpanWriter dataWriter = writer.Slice();
|
|
||||||
packet.Save(ref dataWriter);
|
|
||||||
writer.Position += dataWriter.Position;
|
|
||||||
|
|
||||||
// Send head and data.
|
|
||||||
_tcpStream.WriteUInt16(_wwpaTagStart);
|
|
||||||
_tcpStream.WriteBoolean(true); // bHead2 (unknown)
|
|
||||||
_tcpStream.WriteBoolean(false); // bHead3 (unknown)
|
|
||||||
_tcpStream.WriteBoolean(compress);
|
|
||||||
if (compress)
|
|
||||||
{
|
|
||||||
ReadOnlySpan<byte> compressedData = PacketCompression.Compress(writer.Span);
|
|
||||||
_tcpStream.WriteUInt16((ushort)writer.Position); // decompressed size
|
|
||||||
_tcpStream.WriteUInt32((uint)compressedData.Length); // compressed size
|
|
||||||
_tcpStream.WriteInt32(1); // index
|
|
||||||
_tcpStream.Write(compressedData);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_tcpStream.WriteUInt16(0); // unused
|
|
||||||
_tcpStream.WriteUInt32((ushort)writer.Position); // raw size
|
|
||||||
_tcpStream.WriteInt32(1); // index
|
|
||||||
_tcpStream.Write(writer.Span.Slice(0, writer.Position));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send tail.
|
|
||||||
_tcpStream.WriteInt32(1); // index (must equal other)
|
|
||||||
_tcpStream.WriteUInt16(_wwpaTagEnd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Channel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an additional server response to a <see cref="ChannelTop20Query"/>, causing the client to switch
|
/// Represents an additional server response to a <see cref="Top20Query"/>, causing the client to switch
|
||||||
/// to the channel screen (game lobby).
|
/// to the channel screen (game lobby).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWChannel, 0x44)]
|
[OWChannelPacket(0x44)]
|
||||||
internal class ChannelEnterFinishReply : IPacket
|
internal class EnterFinishReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
@ -4,13 +4,13 @@ using System.Net;
|
|||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
using Syroot.Worms.Mgame.GameServer.Packets.Data;
|
using Syroot.Worms.Mgame.GameServer.Packets.Data;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Channel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="ChannelLoginReply"/>.
|
/// Represents the client request for a <see cref="LoginReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWChannel, 0x10)]
|
[OWChannelPacket(0x10)]
|
||||||
internal class ChannelLoginQuery : IPacket
|
internal class LoginQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
@ -1,13 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Channel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="ChannelLoginQuery"/>.
|
/// Represents the server response to a <see cref="LoginQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWChannel, 0x11)]
|
[OWChannelPacket(0x11)]
|
||||||
internal class ChannelLoginReply : IPacket
|
internal class LoginReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
@ -1,12 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Channel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="StartSingleGameReply"/>.
|
/// Represents the client request for a <see cref="StartSingleGameReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWChannel, 0x38)]
|
[OWChannelPacket(0x38)]
|
||||||
internal class StartSingleGameQuery : IPacket
|
internal class StartSingleGameQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
@ -1,12 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Channel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="StartSingleGameQuery"/>.
|
/// Represents the server response to a <see cref="StartSingleGameQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWChannel, 0x39)]
|
[OWChannelPacket(0x39)]
|
||||||
internal class StartSingleGameReply : IPacket
|
internal class StartSingleGameReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
@ -1,13 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Channel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="ChannelLoginReply"/>.
|
/// Represents the client request for a <see cref="LoginReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWChannel, 0x37)]
|
[OWChannelPacket(0x37)]
|
||||||
internal class ChannelTop20Query : IPacket
|
internal class Top20Query : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
@ -2,13 +2,13 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Channel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="ChannelTop20Query"/>.
|
/// Represents the server response to a <see cref="Top20Query"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWChannel, 0x36)]
|
[OWChannelPacket(0x36)]
|
||||||
internal class ChannelTop20Reply : IPacket
|
internal class Top20Reply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Decorates an <see cref="IPacket"/> child class with the ID of the OW channel packet it represents.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||||
|
internal class OWChannelPacketAttribute : Attribute
|
||||||
|
{
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="OWChannelPacketAttribute"/> class, decorating a class with the
|
||||||
|
/// ID of the packet it represents.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The ID of the packet which the decorated class represents.</param>
|
||||||
|
internal OWChannelPacketAttribute(byte id)
|
||||||
|
{
|
||||||
|
ID = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the ID of the packet the decorated class represents.
|
||||||
|
/// </summary>
|
||||||
|
internal byte ID { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,98 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Syroot.BinaryData;
|
||||||
|
using Syroot.BinaryData.Memory;
|
||||||
|
using Syroot.Worms.Mgame.GameServer.Core.Reflection;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the factory of packets in the OW channel packet format.
|
||||||
|
/// </summary>
|
||||||
|
internal class OWChannelPacketFormat : IPacketFormat
|
||||||
|
{
|
||||||
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private const ushort _tagStart = 0xFFFE;
|
||||||
|
private const ushort _tagEnd = 0xFEFF;
|
||||||
|
private const int _maxDataSize = 1024;
|
||||||
|
|
||||||
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static readonly IList<(Type type, OWChannelPacketAttribute attrib)> _cachedClasses
|
||||||
|
= ReflectionTools.GetAttributedClasses<OWChannelPacketAttribute>();
|
||||||
|
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private OWChannelPacketFormat() { }
|
||||||
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
internal static OWChannelPacketFormat Instance { get; } = new OWChannelPacketFormat();
|
||||||
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public IPacket TryLoad(Stream stream)
|
||||||
|
{
|
||||||
|
// Read head.
|
||||||
|
if (stream.ReadUInt16() != _tagStart || stream.Read1Byte() != 1)
|
||||||
|
return null;
|
||||||
|
int dataSize = stream.ReadUInt16();
|
||||||
|
IPacket packet = GetPacket(stream.Read1Byte());
|
||||||
|
if (packet == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Read data.
|
||||||
|
SpanReader reader = new SpanReader(stream.ReadBytes(dataSize), encoding: Encodings.Korean);
|
||||||
|
|
||||||
|
// Read tail.
|
||||||
|
if (stream.ReadUInt16() != _tagEnd)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Deserialize and return packet.
|
||||||
|
packet.Load(ref reader);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TrySave(Stream stream, IPacket packet)
|
||||||
|
{
|
||||||
|
byte? id = GetID(packet);
|
||||||
|
if (!id.HasValue)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Retrieve data. Must have at least 1 byte.
|
||||||
|
SpanWriter writer = new SpanWriter(new byte[_maxDataSize], encoding: Encodings.Korean);
|
||||||
|
packet.Save(ref writer);
|
||||||
|
writer.Position = Math.Max(1, writer.Position);
|
||||||
|
|
||||||
|
// Send head.
|
||||||
|
stream.WriteUInt16(_tagStart);
|
||||||
|
stream.WriteByte(1);
|
||||||
|
stream.WriteUInt16((ushort)writer.Position);
|
||||||
|
stream.WriteByte(id.Value);
|
||||||
|
|
||||||
|
// Send data.
|
||||||
|
stream.Write(writer.Span);
|
||||||
|
|
||||||
|
// Send tail.
|
||||||
|
stream.WriteUInt16(_tagEnd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private IPacket GetPacket(byte id)
|
||||||
|
{
|
||||||
|
Type packetType = _cachedClasses.Where(x => x.attrib.ID == id).FirstOrDefault().type;
|
||||||
|
return packetType == null ? null : (IPacket)Activator.CreateInstance(packetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte? GetID(IPacket packet)
|
||||||
|
{
|
||||||
|
Type packetType = packet.GetType();
|
||||||
|
return _cachedClasses.Where(x => x.type == packetType).FirstOrDefault().attrib?.ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Decorates an <see cref="IPacket"/> child class with the ID of the OW server packet it represents.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||||
|
internal class OWServerPacketAttribute : Attribute
|
||||||
|
{
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="OWServerPacketAttribute"/> class, decorating a class with the
|
||||||
|
/// ID of the packet it represents.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The ID of the packet which the decorated class represents.</param>
|
||||||
|
internal OWServerPacketAttribute(ushort id)
|
||||||
|
{
|
||||||
|
ID = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the ID of the packet the decorated class represents.
|
||||||
|
/// </summary>
|
||||||
|
internal ushort ID { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Syroot.BinaryData;
|
||||||
|
using Syroot.BinaryData.Memory;
|
||||||
|
using Syroot.Worms.Mgame.GameServer.Core.Reflection;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the factory of packets in the OW server packet format.
|
||||||
|
/// </summary>
|
||||||
|
internal class OWServerPacketFormat : IPacketFormat
|
||||||
|
{
|
||||||
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private const int _maxDataSize = 1024;
|
||||||
|
|
||||||
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static readonly IList<(Type type, OWServerPacketAttribute attrib)> _cachedClasses
|
||||||
|
= ReflectionTools.GetAttributedClasses<OWServerPacketAttribute>();
|
||||||
|
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private OWServerPacketFormat() { }
|
||||||
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
internal static OWServerPacketFormat Instance { get; } = new OWServerPacketFormat();
|
||||||
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public IPacket TryLoad(Stream stream)
|
||||||
|
{
|
||||||
|
// Read head.
|
||||||
|
ushort id = stream.ReadUInt16();
|
||||||
|
IPacket packet = GetPacket(id);
|
||||||
|
if (packet == null)
|
||||||
|
return null;
|
||||||
|
int dataSize = stream.ReadUInt16();
|
||||||
|
|
||||||
|
// Read data.
|
||||||
|
SpanReader reader = new SpanReader(stream.ReadBytes(dataSize), encoding: Encodings.Korean);
|
||||||
|
|
||||||
|
// Deserialize and return packet.
|
||||||
|
packet.Load(ref reader);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TrySave(Stream stream, IPacket packet)
|
||||||
|
{
|
||||||
|
ushort? id = GetID(packet);
|
||||||
|
if (!id.HasValue)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Retrieve data.
|
||||||
|
SpanWriter writer = new SpanWriter(new byte[_maxDataSize], encoding: Encodings.Korean);
|
||||||
|
packet.Save(ref writer);
|
||||||
|
|
||||||
|
// Send head.
|
||||||
|
stream.WriteUInt16(id.Value);
|
||||||
|
stream.WriteUInt16((ushort)writer.Position);
|
||||||
|
|
||||||
|
// Write data.
|
||||||
|
stream.Write(writer.Span);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private IPacket GetPacket(ushort id)
|
||||||
|
{
|
||||||
|
Type packetType = _cachedClasses.Where(x => x.attrib.ID == id).FirstOrDefault().type;
|
||||||
|
return packetType == null ? null : (IPacket)Activator.CreateInstance(packetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ushort? GetID(IPacket packet)
|
||||||
|
{
|
||||||
|
Type packetType = packet.GetType();
|
||||||
|
return _cachedClasses.Where(x => x.type == packetType).FirstOrDefault().attrib?.ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,12 +2,12 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Server
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="ChannelEnterReply"/>.
|
/// Represents the client request for a <see cref="ChannelEnterReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWServer, 0x8034)]
|
[OWServerPacket(0x8034)]
|
||||||
internal class ChannelEnterQuery : IPacket
|
internal class ChannelEnterQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
@ -2,12 +2,12 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Server
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="ChannelEnterQuery"/>.
|
/// Represents the server response to a <see cref="ChannelEnterQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWServer, 0x8035)]
|
[OWServerPacket(0x8035)]
|
||||||
internal class ChannelEnterReply : IPacket
|
internal class ChannelEnterReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
@ -5,12 +5,12 @@ using System.Net;
|
|||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
using Syroot.Worms.Mgame.GameServer.Core.IO;
|
using Syroot.Worms.Mgame.GameServer.Core.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Server
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an additional server response to a <see cref="LoginQuery"/>, providing available server channels.
|
/// Represents an additional server response to a <see cref="LoginQuery"/>, providing available server channels.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWServer, 0x80C9)]
|
[OWServerPacket(0x80C9)]
|
||||||
internal class ChannelListReply : IPacket
|
internal class ChannelListReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
@ -1,12 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Server
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="ConnectReply"/>.
|
/// Represents the client request for a <see cref="ConnectReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWServer, 0x800E)]
|
[OWServerPacket(0x800E)]
|
||||||
internal class ConnectQuery : IPacket
|
internal class ConnectQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
@ -1,12 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Server
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="ConnectQuery"/>.
|
/// Represents the server response to a <see cref="ConnectQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWServer, 0x800F)]
|
[OWServerPacket(0x800F)]
|
||||||
internal class ConnectReply : IPacket
|
internal class ConnectReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
@ -1,14 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Server
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an additional server response to a <see cref="LoginQuery"/>, providing informational server
|
/// Represents an additional server response to a <see cref="LoginQuery"/>, providing informational server
|
||||||
/// screen text.
|
/// screen text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWServer, 0x8033)]
|
[OWServerPacket(0x8033)]
|
||||||
internal class ServerInfoReply : IPacket
|
internal class InfoReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
@ -3,12 +3,12 @@ using System.Net;
|
|||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
using Syroot.Worms.Mgame.GameServer.Packets.Data;
|
using Syroot.Worms.Mgame.GameServer.Packets.Data;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Server
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="LoginReply"/>.
|
/// Represents the client request for a <see cref="LoginReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWServer, 0x8000)]
|
[OWServerPacket(0x8000)]
|
||||||
internal class LoginQuery : IPacket
|
internal class LoginQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
@ -1,12 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using Syroot.BinaryData.Memory;
|
using Syroot.BinaryData.Memory;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms
|
namespace Syroot.Worms.Mgame.GameServer.Packets.OnlineWorms.Server
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="LoginQuery"/>.
|
/// Represents the server response to a <see cref="LoginQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.OWServer, 0x8001)]
|
[OWServerPacket(0x8001)]
|
||||||
internal class LoginReply : IPacket
|
internal class LoginReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
@ -3,10 +3,10 @@
|
|||||||
namespace Syroot.Worms.Mgame.GameServer.Packets
|
namespace Syroot.Worms.Mgame.GameServer.Packets
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Decorates a <see cref="IPacket"/> child class with the ID of the packet it represents.
|
/// Decorates an <see cref="IPacket"/> child class with the ID of the packet it represents.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
|
||||||
internal class PacketAttribute : Attribute
|
internal abstract class PacketAttribute : Attribute
|
||||||
{
|
{
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -15,9 +15,8 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
|
|||||||
/// packet it represents.
|
/// 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>
|
||||||
public PacketAttribute(PacketFormat format, int id)
|
internal PacketAttribute(int id)
|
||||||
{
|
{
|
||||||
Format = format;
|
|
||||||
ID = id;
|
ID = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,24 +25,6 @@ namespace Syroot.Worms.Mgame.GameServer.Packets
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the ID of the packet the decorated class represents.
|
/// Gets the ID of the packet the decorated class represents.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int ID { get; }
|
internal int ID { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the format in which the packet is serialized and sent over the network.
|
|
||||||
/// </summary>
|
|
||||||
public PacketFormat Format { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the known packet formats.
|
|
||||||
/// </summary>
|
|
||||||
internal enum PacketFormat
|
|
||||||
{
|
|
||||||
/// <summary>An Online Worms server packet.</summary>
|
|
||||||
OWServer,
|
|
||||||
/// <summary>An Online Worms channel packet.</summary>
|
|
||||||
OWChannel,
|
|
||||||
/// <summary>A Worms World Party Aqua packet.</summary>
|
|
||||||
Wwpa
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
138
src/Syroot.Worms.Mgame.GameServer/Packets/PacketConnection.cs
Normal file
138
src/Syroot.Worms.Mgame.GameServer/Packets/PacketConnection.cs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using Syroot.Worms.Mgame.GameServer.Core.IO;
|
||||||
|
using Syroot.Worms.Mgame.GameServer.Core.Net;
|
||||||
|
using Syroot.Worms.Mgame.GameServer.Core.Reflection;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Mgame.GameServer.Packets
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a class capable of dispatching received <see cref="IPacket"/> instances to a corresponding method.
|
||||||
|
/// </summary>
|
||||||
|
internal class PacketConnection : IDisposable
|
||||||
|
{
|
||||||
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private const int _maxCacheSize = 2048;
|
||||||
|
|
||||||
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private readonly MethodHandler<IPacket> _methodHandler;
|
||||||
|
private readonly SafeNetworkStream _tcpStream;
|
||||||
|
private readonly IPacketFormat[] _packetFormatPipe;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PacketConnection"/> class, handling the given
|
||||||
|
/// <paramref name="tcpClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tcpClient">The <see cref="System.Net.Sockets.TcpClient"/> to communicate with.</param>
|
||||||
|
/// <param name="packetFormatPipe">The array of <see cref="IPacketFormat"/> instances which are called in order
|
||||||
|
/// to handle a packet.</param>
|
||||||
|
internal PacketConnection(TcpClient tcpClient, params IPacketFormat[] packetFormatPipe)
|
||||||
|
{
|
||||||
|
TcpClient = tcpClient;
|
||||||
|
_packetFormatPipe = packetFormatPipe;
|
||||||
|
_tcpStream = TcpClient.GetSafeStream();
|
||||||
|
_methodHandler = new MethodHandler<IPacket>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- EVENTS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
protected event EventHandler<IPacket> PrePacketHandle;
|
||||||
|
protected event EventHandler<IPacket> PrePacketSend;
|
||||||
|
protected event EventHandler<Exception> UnhandledException;
|
||||||
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="TcpClient"/> with which the connection communicates.
|
||||||
|
/// </summary>
|
||||||
|
internal TcpClient TcpClient { get; }
|
||||||
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
// Do not change this code. Put cleanup code in Dispose(bool disposing).
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts handling packets incoming from this connection and dispatches them to corresponding methods.
|
||||||
|
/// </summary>
|
||||||
|
internal void Listen()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
IPacket packet;
|
||||||
|
while ((packet = ReceivePacket()) != null)
|
||||||
|
{
|
||||||
|
PrePacketHandle?.Invoke(this, packet);
|
||||||
|
_methodHandler.Handle(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
UnhandledException?.Invoke(this, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
TcpClient.Dispose();
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void SendPacket(IPacket packet)
|
||||||
|
{
|
||||||
|
PrePacketSend?.Invoke(this, packet);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Let each packet format try to serialize the data.
|
||||||
|
foreach (IPacketFormat format in _packetFormatPipe)
|
||||||
|
{
|
||||||
|
if (format.TrySave(_tcpStream, packet))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new NotImplementedException("Cannot send unhandled packet format.");
|
||||||
|
}
|
||||||
|
catch (IOException) { } // A network error appeared, and communication should end.
|
||||||
|
catch (ObjectDisposedException) { } // The underlying stream was most apparently closed.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private IPacket ReceivePacket()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Let each packet format read from the stream without consuming data.
|
||||||
|
using (var cachedStream = new CachedStream<SafeNetworkStream>(_tcpStream, _maxCacheSize, true))
|
||||||
|
{
|
||||||
|
foreach (IPacketFormat format in _packetFormatPipe)
|
||||||
|
{
|
||||||
|
IPacket packet = format.TryLoad(cachedStream);
|
||||||
|
if (packet != null)
|
||||||
|
return packet;
|
||||||
|
cachedStream.MoveToCacheStart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new NotImplementedException("Cannot receive unhandled packet format.");
|
||||||
|
}
|
||||||
|
catch (IOException) { return null; } // The underlying socket closed or sent invalid data.
|
||||||
|
catch (ObjectDisposedException) { return null; } // The underlying stream closed.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,75 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a factory creating <see cref="IPacket"/> instances by mapping their ID to types to instantiate.
|
|
||||||
/// </summary>
|
|
||||||
internal static class PacketFactory
|
|
||||||
{
|
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
private static readonly Dictionary<PacketAttribute, Type> _packetCache;
|
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
static PacketFactory()
|
|
||||||
{
|
|
||||||
// Cache the ID-to-packet-class map.
|
|
||||||
_packetCache = new Dictionary<PacketAttribute, Type>();
|
|
||||||
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
|
|
||||||
{
|
|
||||||
foreach (PacketAttribute attrib in type.GetCustomAttributes<PacketAttribute>())
|
|
||||||
{
|
|
||||||
if (_packetCache.ContainsKey(attrib))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException(
|
|
||||||
$"{attrib.Format} packet with ID {attrib.ID} mapped to multiple classes.");
|
|
||||||
}
|
|
||||||
_packetCache.Add(attrib, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="IPacket"/> instance mapped to the given <paramref name="id"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="format">The <see cref="PacketFormat"/> to instantiate.</param>
|
|
||||||
/// <param name="id">The ID of the packet class to instantiate.</param>
|
|
||||||
/// <returns>The created <see cref="IPacket"/> instance.</returns>
|
|
||||||
/// <exception cref="KeyNotFoundException">No class was mapped to the given packet ID.</exception>
|
|
||||||
internal static IPacket Create(PacketFormat format, int id)
|
|
||||||
{
|
|
||||||
foreach (KeyValuePair<PacketAttribute, Type> packetMeta in _packetCache)
|
|
||||||
{
|
|
||||||
if (packetMeta.Key.ID == id)
|
|
||||||
return (IPacket)Activator.CreateInstance(packetMeta.Value, true);
|
|
||||||
}
|
|
||||||
// No packet metadata matching.
|
|
||||||
#if DEBUG
|
|
||||||
return new RawPacket(format, id);
|
|
||||||
#endif
|
|
||||||
throw new ArgumentException($"{format} packet with ID {id} could not be created.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <see cref="PacketAttribute"/> metadata for the class of the given <paramref name="packet"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="packet">The <see cref="IPacket"/> whose metadata will be returned.</param>
|
|
||||||
/// <returns>The metadata of the packet class.</returns>
|
|
||||||
internal static PacketAttribute GetAttribute(IPacket packet)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
if (packet is RawPacket rawPacket)
|
|
||||||
return new PacketAttribute(rawPacket.Type, rawPacket.ID);
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
return _packetCache.Where(x => x.Value == packet.GetType()).First().Key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
29
src/Syroot.Worms.Mgame.GameServer/Packets/PacketFormat.cs
Normal file
29
src/Syroot.Worms.Mgame.GameServer/Packets/PacketFormat.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Mgame.GameServer.Packets
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the implementation of a specific packet format which can instantiate corresponding classes.
|
||||||
|
/// </summary>
|
||||||
|
internal interface IPacketFormat
|
||||||
|
{
|
||||||
|
// ---- METHODS ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an <see cref="IPacket"/> instance read from the given <paramref name="stream"/> or
|
||||||
|
/// <see langword="null"/> if the data was not compatible with this format.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> to read data from.</param
|
||||||
|
/// <returns>The instantiated <see cref="IPacket"/> or <see langword="null"/>.</returns>
|
||||||
|
IPacket TryLoad(Stream stream);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a value indicating whether this format could save the <paramref name="packet"/> to the given
|
||||||
|
/// <paramref name="stream"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> to write data to.</param>
|
||||||
|
/// <param name="packet">The <see cref="IPacket"/> to serialize, if possibile.</param>
|
||||||
|
/// <returns><see langword="true"/> if a packet was serialized; otherwise, <see langword="false"/>.</returns>
|
||||||
|
bool TrySave(Stream stream, IPacket packet);
|
||||||
|
}
|
||||||
|
}
|
@ -1,35 +0,0 @@
|
|||||||
#if DEBUG
|
|
||||||
using Syroot.BinaryData.Memory;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame.GameServer.Packets
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a special fallback packet which simply stores any ID and the raw packet data.
|
|
||||||
/// </summary>
|
|
||||||
internal class RawPacket : IPacket
|
|
||||||
{
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
internal RawPacket(PacketFormat type, int id, params byte[] data)
|
|
||||||
{
|
|
||||||
Type = type;
|
|
||||||
ID = id;
|
|
||||||
Data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
internal PacketFormat Type { get; }
|
|
||||||
|
|
||||||
internal int ID { get; }
|
|
||||||
|
|
||||||
internal byte[] Data { get; private set; }
|
|
||||||
|
|
||||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
public void Load(ref SpanReader reader) => Data = reader.ReadBytes(reader.Length);
|
|
||||||
|
|
||||||
public void Save(ref SpanWriter writer) => writer.WriteBytes(Data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="CmdReply"/>.
|
/// Represents the client request for a <see cref="CmdReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x10A)]
|
[WwpaPacket(0x10A)]
|
||||||
internal class CmdQuery : CmdPacket
|
internal class CmdQuery : CmdPacket
|
||||||
{
|
{
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="CmdQuery"/>.
|
/// Represents the server response to a <see cref="CmdQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x10B)]
|
[WwpaPacket(0x10B)]
|
||||||
internal class CmdReply : CmdPacket
|
internal class CmdReply : CmdPacket
|
||||||
{
|
{
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
@ -5,7 +5,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client notice of disconnecting.
|
/// Represents the client notice of disconnecting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x105)]
|
[WwpaPacket(0x105)]
|
||||||
internal class DisconnectNotice : IPacket
|
internal class DisconnectNotice : IPacket
|
||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
@ -10,7 +10,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="LoginReply"/>.
|
/// Represents the client request for a <see cref="LoginReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x101)]
|
[WwpaPacket(0x101)]
|
||||||
internal class LoginQuery : IPacket
|
internal class LoginQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="LoginQuery"/>.
|
/// Represents the server response to a <see cref="LoginQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x102)]
|
[WwpaPacket(0x102)]
|
||||||
internal class LoginReply : IPacket
|
internal class LoginReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -5,7 +5,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="LogoutReply"/>.
|
/// Represents the client request for a <see cref="LogoutReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x103)]
|
[WwpaPacket(0x103)]
|
||||||
internal class LogoutQuery : IPacket
|
internal class LogoutQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="LogoutQuery"/>.
|
/// Represents the server response to a <see cref="LogoutQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x104)]
|
[WwpaPacket(0x104)]
|
||||||
internal class LogoutReply : IPacket
|
internal class LogoutReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -7,7 +7,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
|
|||||||
/// Invalid description: "Represents the server response to a <see cref="LoginQuery"/>."
|
/// Invalid description: "Represents the server response to a <see cref="LoginQuery"/>."
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Obsolete("While this packet exists, it is currently unclear where it is used.")]
|
[Obsolete("While this packet exists, it is currently unclear where it is used.")]
|
||||||
[Packet(PacketFormat.Wwpa, 0x145)]
|
[WwpaPacket(0x145)]
|
||||||
internal class NotLoginReply : IPacket
|
internal class NotLoginReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
@ -5,7 +5,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="Unknown11FReply"/>.
|
/// Represents the client request for a <see cref="Unknown11FReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x11E)]
|
[WwpaPacket(0x11E)]
|
||||||
internal class Unknown11EQuery : IPacket
|
internal class Unknown11EQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="Unknown11EQuery"/>.
|
/// Represents the server response to a <see cref="Unknown11EQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x11F)]
|
[WwpaPacket(0x11F)]
|
||||||
internal class Unknown11FReply : IPacket
|
internal class Unknown11FReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -5,7 +5,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="Unknown15BReply"/>.
|
/// Represents the client request for a <see cref="Unknown15BReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x15A)]
|
[WwpaPacket(0x15A)]
|
||||||
internal class Unknown15AQuery : IPacket
|
internal class Unknown15AQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Channel
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="Unknown15AQuery"/>.
|
/// Represents the server response to a <see cref="Unknown15AQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x15B)]
|
[WwpaPacket(0x15B)]
|
||||||
internal class Unknown15BReply : IPacket
|
internal class Unknown15BReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
using Syroot.BinaryData.Memory;
|
||||||
|
using Syroot.Worms.Mgame.GameServer.Core.IO;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a fallback WWPA packet.
|
||||||
|
/// </summary>
|
||||||
|
public class RawPacket : IPacket
|
||||||
|
{
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public int ID { get; set; }
|
||||||
|
|
||||||
|
public byte[] Data { get; set; }
|
||||||
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public void Load(ref SpanReader reader)
|
||||||
|
{
|
||||||
|
Data = reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Save(ref SpanWriter writer)
|
||||||
|
{
|
||||||
|
writer.WriteBytes(Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="ChannelListReply"/>.
|
/// Represents the client request for a <see cref="ChannelListReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x801E)]
|
[WwpaPacket(0x801E)]
|
||||||
internal class ChannelListQuery : IPacket
|
internal class ChannelListQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
@ -10,7 +10,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server reply to a <see cref="ChannelListQuery"/>.
|
/// Represents the server reply to a <see cref="ChannelListQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x801F)]
|
[WwpaPacket(0x801F)]
|
||||||
internal class ChannelListReply : IPacket
|
internal class ChannelListReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -5,7 +5,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="ConnectReply"/>.
|
/// Represents the client request for a <see cref="ConnectReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x8001)]
|
[WwpaPacket(0x8001)]
|
||||||
internal class ConnectQuery : IPacket
|
internal class ConnectQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="ConnectQuery"/>.
|
/// Represents the server response to a <see cref="ConnectQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x8002)]
|
[WwpaPacket(0x8002)]
|
||||||
internal class ConnectReply : IPacket
|
internal class ConnectReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -5,7 +5,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client notice of disconnecting.
|
/// Represents the client notice of disconnecting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x801B)]
|
[WwpaPacket(0x801B)]
|
||||||
internal class DisconnectNotice : IPacket
|
internal class DisconnectNotice : IPacket
|
||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
@ -8,7 +8,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for a <see cref="LoginReply"/>.
|
/// Represents the client request for a <see cref="LoginReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x8019)]
|
[WwpaPacket(0x8019)]
|
||||||
internal class LoginQuery : IPacket
|
internal class LoginQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="LoginQuery"/>.
|
/// Represents the server response to a <see cref="LoginQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x801A)]
|
[WwpaPacket(0x801A)]
|
||||||
internal class LoginReply : IPacket
|
internal class LoginReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for an <see cref="OptionsReply"/>.
|
/// Represents the client request for an <see cref="OptionsReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x8046)]
|
[WwpaPacket(0x8046)]
|
||||||
internal class OptionsQuery : IPacket
|
internal class OptionsQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -7,7 +7,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to a <see cref="OptionsQuery"/>.
|
/// Represents the server response to a <see cref="OptionsQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x8047)]
|
[WwpaPacket(0x8047)]
|
||||||
internal class OptionsReply : IPacket
|
internal class OptionsReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -7,7 +7,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the client request for an <see cref="OptionsSaveReply"/>.
|
/// Represents the client request for an <see cref="OptionsSaveReply"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x8048)]
|
[WwpaPacket(0x8048)]
|
||||||
internal class OptionsSaveQuery : IPacket
|
internal class OptionsSaveQuery : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -6,7 +6,7 @@ namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the server response to an <see cref="OptionsSaveQuery"/>.
|
/// Represents the server response to an <see cref="OptionsSaveQuery"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Packet(PacketFormat.Wwpa, 0x8049)]
|
[WwpaPacket(0x8049)]
|
||||||
internal class OptionsSaveReply : IPacket
|
internal class OptionsSaveReply : IPacket
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Decorates an <see cref="IPacket"/> child class with the ID of the OW server packet it represents.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||||
|
internal class WwpaPacketAttribute : Attribute
|
||||||
|
{
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="WwpaPacketAttribute"/> class, decorating a class with the ID of
|
||||||
|
/// the packet it represents.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The ID of the packet which the decorated class represents.</param>
|
||||||
|
internal WwpaPacketAttribute(int id)
|
||||||
|
{
|
||||||
|
ID = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the ID of the packet the decorated class represents.
|
||||||
|
/// </summary>
|
||||||
|
internal int ID { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets an optional command number which the packet represents.
|
||||||
|
/// </summary>
|
||||||
|
internal int Command { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,124 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Syroot.BinaryData;
|
||||||
|
using Syroot.BinaryData.Memory;
|
||||||
|
using Syroot.Worms.Mgame.GameServer.Core.Reflection;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Mgame.GameServer.Packets.WorldPartyAqua
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the implementation of a specific packet format which can instantiate corresponding classes.
|
||||||
|
/// </summary>
|
||||||
|
internal class WwpaPacketFormat : IPacketFormat
|
||||||
|
{
|
||||||
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private const ushort _tagStart = 0x2E9E;
|
||||||
|
private const ushort _tagEnd = 0x7F3F;
|
||||||
|
private const int _maxDataSize = 1024;
|
||||||
|
|
||||||
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static readonly IList<(Type type, WwpaPacketAttribute attrib)> _cachedClasses
|
||||||
|
= ReflectionTools.GetAttributedClasses<WwpaPacketAttribute>();
|
||||||
|
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private WwpaPacketFormat() { }
|
||||||
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
internal static WwpaPacketFormat Instance { get; } = new WwpaPacketFormat();
|
||||||
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public IPacket TryLoad(Stream stream)
|
||||||
|
{
|
||||||
|
// Read head.
|
||||||
|
if (stream.ReadUInt16() != _tagStart
|
||||||
|
|| !stream.ReadBoolean() // bHead2, always true
|
||||||
|
|| stream.ReadBoolean() // bHead3, always false
|
||||||
|
|| !stream.ReadBoolean()) // bCompressed, always true
|
||||||
|
return null;
|
||||||
|
int decompressedSize = stream.ReadUInt16();
|
||||||
|
int compressedSize = stream.ReadInt32();
|
||||||
|
int idxPacket = stream.ReadInt32();
|
||||||
|
|
||||||
|
// Read data.
|
||||||
|
byte[] compressedData = stream.ReadBytes(compressedSize);
|
||||||
|
|
||||||
|
// Read tail.
|
||||||
|
if (stream.ReadInt32() != idxPacket || stream.ReadUInt16() != _tagEnd)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Instantiate, deserialize, and return packet.
|
||||||
|
SpanReader reader = new SpanReader(PacketCompression.Decompress(compressedData), encoding: Encodings.Korean);
|
||||||
|
int id = reader.ReadInt32();
|
||||||
|
IPacket packet = GetPacket(id);
|
||||||
|
if (packet == null)
|
||||||
|
return null;
|
||||||
|
SpanReader dataReader = reader.Slice();
|
||||||
|
packet.Load(ref dataReader);
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TrySave(Stream stream, IPacket packet)
|
||||||
|
{
|
||||||
|
int? id = GetID(packet);
|
||||||
|
if (!id.HasValue)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Retrieve (decompressed) data.
|
||||||
|
SpanWriter writer = new SpanWriter(new byte[_maxDataSize], encoding: Encodings.Korean);
|
||||||
|
writer.WriteInt32(id.Value);
|
||||||
|
SpanWriter dataWriter = writer.Slice();
|
||||||
|
packet.Save(ref dataWriter);
|
||||||
|
writer.Position += dataWriter.Position;
|
||||||
|
|
||||||
|
// Send head and data.
|
||||||
|
stream.WriteUInt16(_tagStart);
|
||||||
|
stream.WriteBoolean(true); // bHead2 (unknown)
|
||||||
|
stream.WriteBoolean(false); // bHead3 (unknown)
|
||||||
|
bool compress = false; // TODO: Does not get accepted by client if compressed.
|
||||||
|
stream.WriteBoolean(compress);
|
||||||
|
if (compress)
|
||||||
|
{
|
||||||
|
ReadOnlySpan<byte> compressedData = PacketCompression.Compress(writer.Span);
|
||||||
|
stream.WriteUInt16((ushort)writer.Position); // decompressed size
|
||||||
|
stream.WriteUInt32((uint)compressedData.Length); // compressed size
|
||||||
|
stream.WriteInt32(1); // index
|
||||||
|
stream.Write(compressedData);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stream.WriteUInt16(0); // unused
|
||||||
|
stream.WriteUInt32((uint)writer.Position); // raw size
|
||||||
|
stream.WriteInt32(1); // index
|
||||||
|
stream.Write(writer.Span.Slice(0, writer.Position));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send tail.
|
||||||
|
stream.WriteInt32(1); // index (must equal other)
|
||||||
|
stream.WriteUInt16(_tagEnd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private IPacket GetPacket(int id)
|
||||||
|
{
|
||||||
|
Type packetType = _cachedClasses.Where(x => x.attrib.ID == id).FirstOrDefault().type;
|
||||||
|
return packetType == null
|
||||||
|
? new RawPacket { ID = id }
|
||||||
|
: (IPacket)Activator.CreateInstance(packetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int? GetID(IPacket packet)
|
||||||
|
{
|
||||||
|
Type packetType = packet.GetType();
|
||||||
|
return _cachedClasses.Where(x => x.type == packetType).FirstOrDefault().attrib?.ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user