diff --git a/src/NLPacketCodec.cpp b/src/NLPacketCodec.cpp new file mode 100644 index 0000000..ba303cc --- /dev/null +++ b/src/NLPacketCodec.cpp @@ -0,0 +1,149 @@ +// NLPacketCodec.cpp +#include "NLPacketCodec.hpp" +#include + +namespace Overte { + +// Control bit masks for sequence number field +static constexpr uint32_t CONTROL_BIT_MASK = 0x80000000; // Bit 31 +static constexpr uint32_t RELIABLE_BIT_MASK = 0x40000000; // Bit 30 +static constexpr uint32_t MESSAGE_BIT_MASK = 0x20000000; // Bit 29 +static constexpr uint32_t OBFUSCATION_MASK = 0x18000000; // Bits 27-28 +static constexpr uint32_t SEQUENCE_NUMBER_MASK = 0x07FFFFFF; // Bits 0-26 + +NLPacket::NLPacket(PacketType type, PacketVersion version, bool isReliable) + : m_type(type) + , m_version(version) + , m_isReliable(isReliable) +{ + // Determine header size (sourced packets have LocalID) + m_isSourced = false; // Most client packets aren't sourced + m_headerSize = m_isSourced ? SOURCED_HEADER_SIZE : BASE_HEADER_SIZE; + + // Reserve space for header + m_data.resize(m_headerSize); + writeHeader(); +} + +void NLPacket::writeHeader() { + size_t offset = 0; + + // Write sequence number and flags + uint32_t seqAndFlags = m_sequenceNumber & SEQUENCE_NUMBER_MASK; + if (m_isReliable) { + seqAndFlags |= RELIABLE_BIT_MASK; + } + // Convert to network byte order + uint32_t netSeqAndFlags = htonl(seqAndFlags); + std::memcpy(m_data.data() + offset, &netSeqAndFlags, sizeof(uint32_t)); + offset += sizeof(uint32_t); + + // Write packet type + m_data[offset++] = static_cast(m_type); + + // Write version + m_data[offset++] = m_version; + + // Write source ID if sourced + if (m_isSourced) { + uint16_t netSourceID = htons(m_sourceID); + std::memcpy(m_data.data() + offset, &netSourceID, sizeof(uint16_t)); + offset += sizeof(uint16_t); + } +} + +void NLPacket::write(const void* data, size_t size) { + const uint8_t* bytes = static_cast(data); + m_data.insert(m_data.end(), bytes, bytes + size); +} + +void NLPacket::writeUInt8(uint8_t value) { + m_data.push_back(value); +} + +void NLPacket::writeUInt16(uint16_t value) { + uint16_t netValue = htons(value); + write(&netValue, sizeof(netValue)); +} + +void NLPacket::writeUInt32(uint32_t value) { + uint32_t netValue = htonl(value); + write(&netValue, sizeof(netValue)); +} + +void NLPacket::writeUInt64(uint64_t value) { + // Network byte order for 64-bit (big-endian) + uint64_t netValue = + ((value & 0xFF00000000000000ULL) >> 56) | + ((value & 0x00FF000000000000ULL) >> 40) | + ((value & 0x0000FF0000000000ULL) >> 24) | + ((value & 0x000000FF00000000ULL) >> 8) | + ((value & 0x00000000FF000000ULL) << 8) | + ((value & 0x0000000000FF0000ULL) << 24) | + ((value & 0x000000000000FF00ULL) << 40) | + ((value & 0x00000000000000FFULL) << 56); + write(&netValue, sizeof(netValue)); +} + +void NLPacket::writeString(const std::string& str, bool nullTerminated) { + write(str.data(), str.size()); + if (nullTerminated) { + writeUInt8(0); + } +} + +void NLPacket::setSequenceNumber(SequenceNumber seq) { + m_sequenceNumber = seq & SEQUENCE_NUMBER_MASK; + writeHeader(); +} + +void NLPacket::setSourceID(LocalID id) { + m_sourceID = id; + m_isSourced = true; + // Resize if needed + if (m_headerSize != SOURCED_HEADER_SIZE) { + m_headerSize = SOURCED_HEADER_SIZE; + m_data.resize(m_headerSize); + } + writeHeader(); +} + +bool NLPacket::parseHeader(const uint8_t* data, size_t size, Header& header) { + if (size < BASE_HEADER_SIZE) { + return false; + } + + size_t offset = 0; + + // Read sequence and flags + uint32_t netSeqAndFlags; + std::memcpy(&netSeqAndFlags, data + offset, sizeof(uint32_t)); + header.sequenceAndFlags = ntohl(netSeqAndFlags); + offset += sizeof(uint32_t); + + // Read packet type + header.type = static_cast(data[offset++]); + + // Read version + header.version = data[offset++]; + + // Read source ID if present (check if packet is sourced) + if (size >= SOURCED_HEADER_SIZE) { + uint16_t netSourceID; + std::memcpy(&netSourceID, data + offset, sizeof(uint16_t)); + header.sourceID = ntohs(netSourceID); + } else { + header.sourceID = NULL_LOCAL_ID; + } + + return true; +} + +PacketType NLPacket::getType(const uint8_t* data, size_t size) { + if (size < sizeof(uint32_t) + 1) { + return PacketType::Unknown; + } + return static_cast(data[sizeof(uint32_t)]); +} + +} // namespace Overte diff --git a/src/NLPacketCodec.hpp b/src/NLPacketCodec.hpp new file mode 100644 index 0000000..8a89175 --- /dev/null +++ b/src/NLPacketCodec.hpp @@ -0,0 +1,91 @@ +// NLPacketCodec.hpp +// Minimal NLPacket protocol implementation for Overte domain communication +// Based on Overte's NLPacket.h specification + +#pragma once + +#include +#include +#include + +namespace Overte { + +// Packet types from Overte protocol +enum class PacketType : uint8_t { + Unknown = 0, + Ping = 1, + PingReply = 2, + DomainList = 3, + DomainListRequest = 4, + DomainConnectionDenied = 6, + DomainServerRequireDTLS = 7, + DomainConnectRequest = 8, + DomainServerPathQuery = 9, + DomainServerPathResponse = 10, + DomainServerAddedNode = 11, + DomainServerConnectionToken = 12, + DomainSettingsRequest = 13, + DomainSettings = 14, + // ... many more packet types + EntityAdd = 0x41, + EntityEdit = 0x42, + EntityErase = 0x43, + EntityQuery = 0x44, + EntityData = 0x45, +}; + +using PacketVersion = uint8_t; +using LocalID = uint16_t; +using SequenceNumber = uint32_t; + +// NLPacket structure (minimal implementation) +class NLPacket { +public: + // Packet header components + struct Header { + uint32_t sequenceAndFlags; // Sequence number (29 bits) + flags (3 bits) + PacketType type; + PacketVersion version; + LocalID sourceID; // Only for sourced packets + }; + + static constexpr LocalID NULL_LOCAL_ID = 0; + static constexpr size_t BASE_HEADER_SIZE = sizeof(uint32_t) + sizeof(PacketType) + sizeof(PacketVersion); + static constexpr size_t SOURCED_HEADER_SIZE = BASE_HEADER_SIZE + sizeof(LocalID); + + NLPacket(PacketType type, PacketVersion version = 0, bool isReliable = false); + + // Write data to packet payload + void write(const void* data, size_t size); + void writeUInt8(uint8_t value); + void writeUInt16(uint16_t value); + void writeUInt32(uint32_t value); + void writeUInt64(uint64_t value); + void writeString(const std::string& str, bool nullTerminated = true); + + // Get packet data for sending + const std::vector& getData() const { return m_data; } + size_t getSize() const { return m_data.size(); } + + // Parse received packet + static bool parseHeader(const uint8_t* data, size_t size, Header& header); + static PacketType getType(const uint8_t* data, size_t size); + + void setSequenceNumber(SequenceNumber seq); + void setSourceID(LocalID id); + +private: + void writeHeader(); + + PacketType m_type; + PacketVersion m_version; + SequenceNumber m_sequenceNumber{0}; + LocalID m_sourceID{NULL_LOCAL_ID}; + bool m_isReliable; + bool m_isSourced{false}; + + std::vector m_data; + size_t m_headerSize; +}; + +} // namespace Overte