using System.Buffers.Binary; namespace RPG.Services.Gateserver.Network; internal class NetPacket { public const int Overhead = 16; private const uint HeadMagic = 0x01234567; private const uint TailMagic = 0x89ABCDEF; public ushort CmdType { get; } public ReadOnlyMemory Head { get; } public ReadOnlyMemory Body { get; } public int Size => Overhead + Head.Length + Body.Length; public NetPacket(ushort cmdType, ReadOnlyMemory head, ReadOnlyMemory body) { CmdType = cmdType; Head = head; Body = body; } public static DeserializationResult TryDeserialize(ReadOnlyMemory buffer, out NetPacket? packet, out int bytesRead) { packet = null; bytesRead = 0; if (buffer.Length < Overhead) return DeserializationResult.BufferExceeded; ReadOnlySpan span = buffer.Span; if (BinaryPrimitives.ReadUInt32BigEndian(span[..4]) != HeadMagic) return DeserializationResult.Corrupted; ushort cmdType = BinaryPrimitives.ReadUInt16BigEndian(span[4..6]); int headSize = BinaryPrimitives.ReadUInt16BigEndian(span[6..8]); int bodySize = BinaryPrimitives.ReadInt32BigEndian(span[8..12]); if (buffer.Length < Overhead + headSize + bodySize) return DeserializationResult.BufferExceeded; if (BinaryPrimitives.ReadUInt32BigEndian(span[(12 + headSize + bodySize)..]) != TailMagic) return DeserializationResult.Corrupted; packet = new(cmdType, buffer.Slice(12, headSize), buffer.Slice(12 + headSize, bodySize)); bytesRead = Overhead + headSize + bodySize; return DeserializationResult.Success; } public void Serialize(Memory buffer) { Span span = buffer.Span; BinaryPrimitives.WriteUInt32BigEndian(span[..4], HeadMagic); BinaryPrimitives.WriteUInt16BigEndian(span[4..6], CmdType); BinaryPrimitives.WriteUInt16BigEndian(span[6..8], (ushort)Head.Length); BinaryPrimitives.WriteInt32BigEndian(span[8..12], Body.Length); Head.CopyTo(buffer[12..]); Body.CopyTo(buffer[(12 + Head.Length)..]); BinaryPrimitives.WriteUInt32BigEndian(span[(12 + Head.Length + Body.Length)..], TailMagic); } public enum DeserializationResult { Success, BufferExceeded, Corrupted } }