using System; using System.Buffers.Binary; namespace ShrinkNetwork { internal enum ShrinkKcpEnvelopeKind : byte { ConnectRequest = 1, ConnectAccept = 2, Disconnect = 3, Data = 4 } internal static class ShrinkKcpTransportProtocol { private const int KindSize = 1; private const int ConversationSize = 4; private const int NonceSize = 8; public static byte[] CreateConnectRequest(long nonce, uint requestedConversationId) { var buffer = new byte[KindSize + NonceSize + ConversationSize]; buffer[0] = (byte)ShrinkKcpEnvelopeKind.ConnectRequest; BinaryPrimitives.WriteInt64LittleEndian(buffer.AsSpan(1, NonceSize), nonce); BinaryPrimitives.WriteUInt32LittleEndian(buffer.AsSpan(1 + NonceSize, ConversationSize), requestedConversationId); return buffer; } public static bool TryReadConnectRequest(byte[] datagram, out long nonce, out uint requestedConversationId) { nonce = default; requestedConversationId = default; if (datagram == null || datagram.Length != KindSize + NonceSize + ConversationSize) return false; if (datagram[0] != (byte)ShrinkKcpEnvelopeKind.ConnectRequest) return false; nonce = BinaryPrimitives.ReadInt64LittleEndian(datagram.AsSpan(1, NonceSize)); requestedConversationId = BinaryPrimitives.ReadUInt32LittleEndian(datagram.AsSpan(1 + NonceSize, ConversationSize)); return true; } public static byte[] CreateConnectAccept(long nonce, uint conversationId) { var buffer = new byte[KindSize + NonceSize + ConversationSize]; buffer[0] = (byte)ShrinkKcpEnvelopeKind.ConnectAccept; BinaryPrimitives.WriteInt64LittleEndian(buffer.AsSpan(1, NonceSize), nonce); BinaryPrimitives.WriteUInt32LittleEndian(buffer.AsSpan(1 + NonceSize, ConversationSize), conversationId); return buffer; } public static bool TryReadConnectAccept(byte[] datagram, out long nonce, out uint conversationId) { nonce = default; conversationId = default; if (datagram == null || datagram.Length != KindSize + NonceSize + ConversationSize) return false; if (datagram[0] != (byte)ShrinkKcpEnvelopeKind.ConnectAccept) return false; nonce = BinaryPrimitives.ReadInt64LittleEndian(datagram.AsSpan(1, NonceSize)); conversationId = BinaryPrimitives.ReadUInt32LittleEndian(datagram.AsSpan(1 + NonceSize, ConversationSize)); return true; } public static byte[] CreateDisconnect(uint conversationId) { var buffer = new byte[KindSize + ConversationSize]; buffer[0] = (byte)ShrinkKcpEnvelopeKind.Disconnect; BinaryPrimitives.WriteUInt32LittleEndian(buffer.AsSpan(1, ConversationSize), conversationId); return buffer; } public static bool TryReadDisconnect(byte[] datagram, out uint conversationId) { conversationId = default; if (datagram == null || datagram.Length != KindSize + ConversationSize) return false; if (datagram[0] != (byte)ShrinkKcpEnvelopeKind.Disconnect) return false; conversationId = BinaryPrimitives.ReadUInt32LittleEndian(datagram.AsSpan(1, ConversationSize)); return true; } public static byte[] CreateDataPacket(uint conversationId, byte[] payload) { payload ??= Array.Empty(); var buffer = new byte[KindSize + ConversationSize + payload.Length]; buffer[0] = (byte)ShrinkKcpEnvelopeKind.Data; BinaryPrimitives.WriteUInt32LittleEndian(buffer.AsSpan(1, ConversationSize), conversationId); if (payload.Length > 0) Buffer.BlockCopy(payload, 0, buffer, KindSize + ConversationSize, payload.Length); return buffer; } public static bool TryReadDataPacket(byte[] datagram, out uint conversationId, out int payloadOffset, out int payloadLength) { conversationId = default; payloadOffset = default; payloadLength = default; if (datagram == null || datagram.Length < KindSize + ConversationSize) return false; if (datagram[0] != (byte)ShrinkKcpEnvelopeKind.Data) return false; conversationId = BinaryPrimitives.ReadUInt32LittleEndian(datagram.AsSpan(1, ConversationSize)); payloadOffset = KindSize + ConversationSize; payloadLength = datagram.Length - payloadOffset; return payloadLength >= 0; } } }