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 818 (1.21.90), 819 (1.21.93), 827 (1.21.100), 844 (1.21.111), 859 (1.21.120), 898 (1.21.132), 924 (1.26.3), 944 (1.26.14), 975 (1.26.20), 1001 (1.26.30), 2168 (1.26.40), 2192 (1.26.50.27) and 2208 (1.26.60.23).
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-failureBEDROCK_PROTOCOL_BUILD_TESTS and BEDROCK_PROTOCOL_INSTALL both default to on for a
top-level build and off when the project is added as a subdirectory.
Code generation is wired into the build, but bpc can be run directly:
uv run bpc --language cpp --out build/protocol --import-path . protocol/inventory.pyAs a subproject. Code generation runs during the build, so this needs uv on the consumer's machine too:
add_subdirectory(bedrock-protocol)
target_link_libraries(my_target PRIVATE bedrock::protocol)Or install it once and consume the built artifacts, which needs no Python at all:
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/opt/bedrock-protocol
cmake --build build
cmake --install buildfind_package(bedrock-protocol CONFIG REQUIRED)
target_link_libraries(my_target PRIVATE bedrock::protocol)The install tree:
| path | |
|---|---|
include/bedrock/protocol.hpp |
umbrella — the runtime plus every generated header |
include/bedrock/protocol/*.h |
one generated header per packet family, each paired with a compiled .cpp |
include/bedrock/protocol/*.hpp |
header-only runtime — binary streams, Serializer, NBT, UUID |
include/bedrock/protocol/detail/ |
plumbing the generated code leans on, not meant to be included directly |
lib/libbedrock_protocol.a |
the generated serializer bodies |
lib/cmake/bedrock-protocol/ |
the CMake package find_package resolves |
A consumer that only touches one family can include <bedrock/protocol/inventory.h>
rather than the umbrella.
| path | |
|---|---|
protocol/ |
the schema — one module per BDS domain, named after its folder in the game tree |
src/bedrock_protocol/ |
the compiler: compiler/ (parser, descriptors, pool) and compiler/cpp/ (backend) |
include/bedrock/protocol/ |
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.
MIT. See LICENSE.