namespace SilkroadBot.Core.Networking; /// /// Represents a Silkroad packet with opcode and payload. /// public class Packet { public ushort Opcode { get; } public byte[] Payload { get; } public bool IsEncrypted { get; } public DateTime Timestamp { get; } public PacketDirection Direction { get; } public Packet(ushort opcode, byte[] payload, PacketDirection direction, bool isEncrypted = false) { Opcode = opcode; Payload = payload ?? Array.Empty(); IsEncrypted = isEncrypted; Timestamp = DateTime.UtcNow; Direction = direction; } /// Create a client-to-server packet. public static Packet ToServer(ushort opcode, byte[] payload, bool encrypted = false) => new(opcode, payload, PacketDirection.ClientToServer, encrypted); /// Create a server-to-client packet. public static Packet FromServer(ushort opcode, byte[] payload, bool encrypted = false) => new(opcode, payload, PacketDirection.ServerToClient, encrypted); public override string ToString() => $"[{Direction}] 0x{Opcode:X4} ({Payload.Length} bytes)"; } public enum PacketDirection { ClientToServer, ServerToClient } /// /// Packet reader utility for extracting data from packet payloads. /// public class PacketReader { private readonly byte[] _data; private int _position; public PacketReader(byte[] data) { _data = data; _position = 0; } public int Remaining => _data.Length - _position; public int Position => _position; public byte ReadByte() => _data[_position++]; public ushort ReadUInt16() { var value = BitConverter.ToUInt16(_data, _position); _position += 2; return value; } public uint ReadUInt32() { var value = BitConverter.ToUInt32(_data, _position); _position += 4; return value; } public ulong ReadUInt64() { var value = BitConverter.ToUInt64(_data, _position); _position += 8; return value; } public float ReadSingle() { var value = BitConverter.ToSingle(_data, _position); _position += 4; return value; } public string ReadAscii() { ushort length = ReadUInt16(); var text = System.Text.Encoding.ASCII.GetString(_data, _position, length); _position += length; return text; } public string ReadUnicode() { ushort length = ReadUInt16(); var text = System.Text.Encoding.Unicode.GetString(_data, _position, length * 2); _position += length * 2; return text; } public byte[] ReadBytes(int count) { var bytes = new byte[count]; Array.Copy(_data, _position, bytes, 0, count); _position += count; return bytes; } public void Skip(int count) => _position += count; } /// /// Packet writer utility for constructing packet payloads. /// public class PacketWriter { private readonly MemoryStream _stream; public PacketWriter(int initialCapacity = 64) { _stream = new MemoryStream(initialCapacity); } public PacketWriter WriteByte(byte value) { _stream.WriteByte(value); return this; } public PacketWriter WriteUInt16(ushort value) { _stream.Write(BitConverter.GetBytes(value)); return this; } public PacketWriter WriteUInt32(uint value) { _stream.Write(BitConverter.GetBytes(value)); return this; } public PacketWriter WriteUInt64(ulong value) { _stream.Write(BitConverter.GetBytes(value)); return this; } public PacketWriter WriteSingle(float value) { _stream.Write(BitConverter.GetBytes(value)); return this; } public PacketWriter WriteAscii(string text) { var bytes = System.Text.Encoding.ASCII.GetBytes(text); WriteUInt16((ushort)bytes.Length); _stream.Write(bytes); return this; } public PacketWriter WriteUnicode(string text) { var bytes = System.Text.Encoding.Unicode.GetBytes(text); WriteUInt16((ushort)(bytes.Length / 2)); _stream.Write(bytes); return this; } public PacketWriter WriteBytes(byte[] data) { _stream.Write(data); return this; } public byte[] ToArray() => _stream.ToArray(); }