Version-aware C++ packet codecs for the Minecraft: Bedrock protocol, generated from a Python schema.
Bedrock's wire format moves between protocol versions: fields appear, packets get
reordered, and whole packets migrate to BDS's Cereal serialization. This repository
holds one schema covering every version it supports, and a protoc-shaped compiler
(bpc) that turns it into C++ types and serializers — one shape per version, so a
consumer names the version it wants and gets a compile error if it asks for a field
that era never had.
Modelled today: protocol 975 (1.26.20) and 1001 (1.26.30).
The schema lives in protocol/*.py. A packet whose shape changed is declared once
per era; anything smaller carries its own version range:
@packet(id=175, until=1001)
class SubChunkRequestPacket:
dimension_type: DimensionType
center_pos: SubChunkPos
sub_chunk_pos_offsets: list[SubChunkPosOffset] = field(prefix=uint32)
@packet(id=175, since=1001)
class SubChunkRequestPacket:
dimension_type: DimensionType
sub_chunk_pos_offsets: list[SubChunkPosOffset]
center_pos: SubChunkPosThe compiler emits one struct per version behind a selector alias, so both eras are reachable from a single name:
#include <bedrock/protocol.hpp>
namespace bp = bedrock::protocol;
using Packet = bp::SubChunkRequestPacket_<975>;
Packet packet;
packet.dimension_type = static_cast<bp::DimensionType>(0);
std::string buffer;
bp::BinaryWriter writer{buffer};
bp::Serializer<Packet>::serialize(writer, packet);
bp::BinaryReader reader{buffer};
std::expected<Packet, std::error_code> back = bp::Serializer<Packet>::deserialize(reader);Unversioned types keep their plain name; bp::SubChunkRequestPacket without the
suffix is the latest version.
Requires CMake, a C++23 compiler, and uv, which runs the compiler during the build. CI covers GCC (libstdc++), Clang 18 (libc++) and MSVC.
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build --output-on-failureCode generation is wired into the build, but bpc can be run directly:
uv run bpc --language cpp --out build/protocol --import-path . protocol/inventory.pyadd_subdirectory(bedrock-protocol)
target_link_libraries(my_target PRIVATE bedrock::protocol)bedrock::protocol is a static library holding the generated serializer bodies;
<bedrock/protocol.hpp> includes every generated header.
| path | |
|---|---|
protocol/ |
the schema — one module per packet family |
src/bedrock_protocol/ |
the compiler: compiler/ (parser, descriptors, pool) and compiler/cpp/ (backend) |
include/bedrock/ |
hand-written runtime — binary streams, the Serializer entry point, NBT |
tests/ |
per-packet round-trip tests against goldens generated by running gophertunnel |
Wire shapes are taken from protocol-docs,
names and C++ types from reverse-engineered BDS headers, and golden bytes from
gophertunnel. CLAUDE.md documents those
sources and the conventions the schema follows.