#include "vf/Protocol.h" namespace vf { void PacketWriter::writePosition(int32_t x, int32_t y, int32_t z) { uint64_t packed = ((static_cast(x) & 0x3FFFFFFull) << 38) | ((static_cast(z) & 0x3FFFFFFull) << 12) | (static_cast(y) & 0xFFFull); writer.putU64(packed); } void PacketWriter::writeUuid(uint64_t hi, uint64_t lo) { writer.putU64(hi); writer.putU64(lo); } namespace { int32_t signExtend(uint64_t value, int bits) { uint64_t signBit = 1ull << (bits - 1); if (value & signBit) value |= ~((1ull << bits) - 1ull); return static_cast(static_cast(value)); } } // namespace bool PacketReader::readI32(int32_t& out) { uint32_t raw; if (!reader.getU32(raw)) return false; out = static_cast(raw); return true; } bool PacketReader::readI64(int64_t& out) { uint64_t raw; if (!reader.getU64(raw)) return false; out = static_cast(raw); return true; } bool PacketReader::readBool(bool& out) { uint8_t raw; if (!reader.getU8(raw)) return false; out = raw != 0; return true; } bool PacketReader::readBytes(std::vector& out, std::size_t n) { out.resize(n); return reader.getBytes(out.data(), n); } bool PacketReader::readPosition(int32_t& x, int32_t& y, int32_t& z) { uint64_t packed; if (!reader.getU64(packed)) return false; x = signExtend(packed >> 38, 26); z = signExtend((packed >> 12) & 0x3FFFFFFull, 26); y = signExtend(packed & 0xFFFull, 12); return true; } bool PacketReader::readUuid(uint64_t& hi, uint64_t& lo) { return reader.getU64(hi) && reader.getU64(lo); } void Handshake::encode(PacketWriter& w) const { w.writeVarInt(protocolVersion); w.writeString(serverAddress); w.writeU16(serverPort); w.writeVarInt(nextState); } bool Handshake::decode(PacketReader& r) { return r.readVarInt(protocolVersion) && r.readString(serverAddress) && r.readU16(serverPort) && r.readVarInt(nextState); } void LoginStart::encode(PacketWriter& w) const { w.writeString(name); w.writeUuid(uuidHi, uuidLo); } bool LoginStart::decode(PacketReader& r) { return r.readString(name) && r.readUuid(uuidHi, uuidLo); } void JoinGame::encode(PacketWriter& w) const { w.writeVarInt(entityId); w.writeU8(gameMode); w.writeVarInt(dimension); w.writeI64(static_cast(seedHash)); w.writeU8(maxPlayers); w.writeString(levelType); w.writeVarInt(viewDistance); w.writeBool(reducedDebugInfo); } bool JoinGame::decode(PacketReader& r) { int64_t seed; if (!r.readVarInt(entityId) || !r.readU8(gameMode) || !r.readVarInt(dimension) || !r.readI64(seed) || !r.readU8(maxPlayers) || !r.readString(levelType) || !r.readVarInt(viewDistance) || !r.readBool(reducedDebugInfo)) return false; seedHash = static_cast(seed); return true; } void ChunkData::setFromChunk(int32_t cx, int32_t cy, int32_t cz, const Chunk& chunk) { chunkX = cx; chunkY = cy; chunkZ = cz; palette.clear(); std::array(Block::Count)> indexOf; indexOf.fill(-1); std::vector indices; indices.reserve(chunkVolume); for (Block block : chunk.raw()) { int& slot = indexOf[static_cast(block)]; if (slot < 0) { slot = static_cast(palette.size()); palette.push_back(block); } indices.push_back(static_cast(slot)); } bitWidth = 1; while ((static_cast(1) << bitWidth) < palette.size()) ++bitWidth; int perWord = 64 / bitWidth; std::size_t wordCount = (indices.size() + static_cast(perWord) - 1) / static_cast(perWord); packed.assign(wordCount, 0); uint64_t mask = bitWidth >= 64 ? ~0ull : ((1ull << bitWidth) - 1ull); for (std::size_t i = 0; i < indices.size(); ++i) { std::size_t word = i / static_cast(perWord); int inWord = static_cast(i % static_cast(perWord)); packed[word] |= (static_cast(indices[i]) & mask) << (inWord * bitWidth); } } void ChunkData::toChunk(Chunk& out) const { int perWord = 64 / bitWidth; uint64_t mask = bitWidth >= 64 ? ~0ull : ((1ull << bitWidth) - 1ull); std::array& blocks = out.raw(); for (std::size_t i = 0; i < blocks.size(); ++i) { std::size_t word = i / static_cast(perWord); int inWord = static_cast(i % static_cast(perWord)); uint32_t index = word < packed.size() ? static_cast((packed[word] >> (inWord * bitWidth)) & mask) : 0; blocks[i] = index < palette.size() ? palette[index] : Block::Air; } } void ChunkData::encode(PacketWriter& w) const { w.writeVarInt(chunkX); w.writeVarInt(chunkY); w.writeVarInt(chunkZ); ByteWriter raw; raw.putVarU32(static_cast(palette.size())); for (Block block : palette) raw.putU8(static_cast(block)); raw.putU8(static_cast(bitWidth)); raw.putVarU32(static_cast(packed.size())); for (uint64_t word : packed) raw.putU64(word); ByteBuffer compressed = compress(raw.bytes()); w.writeVarUint(static_cast(compressed.size())); w.writeBytes(compressed.data(), compressed.size()); } bool ChunkData::decode(PacketReader& r) { if (!r.readVarInt(chunkX) || !r.readVarInt(chunkY) || !r.readVarInt(chunkZ)) return false; uint32_t compressedLen; if (!r.readVarUint(compressedLen)) return false; std::vector compressed; if (!r.readBytes(compressed, compressedLen)) return false; ByteBuffer raw; if (!decompress(compressed.data(), compressed.size(), raw)) return false; ByteReader body(raw.data(), raw.size()); uint32_t paletteSize; if (!body.getVarU32(paletteSize)) return false; palette.clear(); palette.reserve(paletteSize); for (uint32_t i = 0; i < paletteSize; ++i) { uint8_t block; if (!body.getU8(block)) return false; palette.push_back(static_cast(block)); } uint8_t width; if (!body.getU8(width) || width < 1 || width > 64) return false; bitWidth = width; uint32_t wordCount; if (!body.getVarU32(wordCount)) return false; packed.clear(); packed.reserve(wordCount); for (uint32_t i = 0; i < wordCount; ++i) { uint64_t word; if (!body.getU64(word)) return false; packed.push_back(word); } return true; } void BlockChange::encode(PacketWriter& w) const { w.writePosition(x, y, z); w.writeVarUint(static_cast(block)); } bool BlockChange::decode(PacketReader& r) { uint32_t rawBlock; if (!r.readPosition(x, y, z) || !r.readVarUint(rawBlock)) return false; block = static_cast(rawBlock); return true; } void MultiBlockChange::encode(PacketWriter& w) const { w.writeVarInt(chunkX); w.writeVarInt(chunkY); w.writeVarInt(chunkZ); w.writeVarUint(static_cast(changes.size())); for (const Change& change : changes) { w.writeU16(change.localIndex); w.writeU8(static_cast(change.block)); } } bool MultiBlockChange::decode(PacketReader& r) { if (!r.readVarInt(chunkX) || !r.readVarInt(chunkY) || !r.readVarInt(chunkZ)) return false; uint32_t count; if (!r.readVarUint(count)) return false; changes.clear(); changes.reserve(count); for (uint32_t i = 0; i < count; ++i) { Change change; uint8_t block; if (!r.readU16(change.localIndex) || !r.readU8(block)) return false; change.block = static_cast(block); changes.push_back(change); } return true; } void EntityMove::encode(PacketWriter& w) const { w.writeVarInt(entityId); w.writeFloat(x); w.writeFloat(y); w.writeFloat(z); w.writeFloat(yaw); w.writeFloat(pitch); w.writeBool(onGround); } bool EntityMove::decode(PacketReader& r) { return r.readVarInt(entityId) && r.readFloat(x) && r.readFloat(y) && r.readFloat(z) && r.readFloat(yaw) && r.readFloat(pitch) && r.readBool(onGround); } void PlayerState::encode(PacketWriter& w) const { w.writeFloat(x); w.writeFloat(y); w.writeFloat(z); w.writeFloat(yaw); w.writeFloat(pitch); w.writeFloat(health); w.writeVarInt(food); w.writeVarInt(xpLevel); w.writeU8(selectedSlot); } bool PlayerState::decode(PacketReader& r) { return r.readFloat(x) && r.readFloat(y) && r.readFloat(z) && r.readFloat(yaw) && r.readFloat(pitch) && r.readFloat(health) && r.readVarInt(food) && r.readVarInt(xpLevel) && r.readU8(selectedSlot); } void KeepAlive::encode(PacketWriter& w) const { w.writeVarLong(token); } bool KeepAlive::decode(PacketReader& r) { return r.readVarLong(token); } void writeFramed(ByteWriter& stream, PacketId id, const PacketWriter& body) { uint32_t frameLength = static_cast(1 + body.size()); stream.putVarU32(frameLength); stream.putU8(static_cast(id)); stream.putBytes(body.bytes().data(), body.bytes().size()); } namespace { bool peekVarU32(const uint8_t* data, std::size_t size, std::size_t pos, uint32_t& value, std::size_t& next) { value = 0; int shift = 0; while (shift < 35) { if (pos >= size) return false; uint8_t byte = data[pos++]; value |= static_cast(byte & 0x7Fu) << shift; if ((byte & 0x80u) == 0) { next = pos; return true; } shift += 7; } return false; } } // namespace void splitFrames(const uint8_t* data, std::size_t size, std::vector& frames, std::size_t& consumed) { frames.clear(); std::size_t pos = 0; for (;;) { std::size_t afterLength = 0; uint32_t frameLength = 0; if (!peekVarU32(data, size, pos, frameLength, afterLength)) break; if (frameLength == 0) break; if (afterLength + frameLength > size) break; Frame frame; frame.id = static_cast(data[afterLength]); frame.payload.assign(data + afterLength + 1, data + afterLength + frameLength); frames.push_back(std::move(frame)); pos = afterLength + frameLength; } consumed = pos; } } // namespace vf