Skip to content

Repository files navigation

bedrock-protocol

Version-aware C++ packet codecs for the Minecraft: Bedrock protocol, generated from a Python schema.

CI

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).

Example

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: SubChunkPos

The 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.

Building

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-failure

BEDROCK_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.py

Using it

As 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 build
find_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.

Layout

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.

License

MIT. See LICENSE.

About

A Python DSL that generates version-aware C++ packet codecs for the Minecraft: Bedrock protocol

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages