-
Notifications
You must be signed in to change notification settings - Fork 0
Regenerating Protobufs
Steam periodically changes its wire format — new messages, renamed fields, deleted RPCs. Keeping the fork current means regenerating the _pb2.py + _pb2.pyi files under steam/protobufs/ from fresh .proto sources.
This page covers the four poetry-registered scripts that do the work.
-
protocon PATH.poetry run pb-compilerefuses to run without it.- macOS:
brew install protobuf - Debian / Ubuntu:
sudo apt install protobuf-compiler - Verify:
protoc --version— expect libprotoc 3.19+, ideally 5.x or 6.x.
- macOS:
- Dev deps:
poetry install --with dev— providesmypy-protobuf(emits the.pyicompanions) andtypes-protobuf(typeshed for the runtime). - Internet —
pb-fetchdownloads from GitHub.
poetry run pb-updateRuns the four sub-scripts in order (pb-fetch → pb-compile → pb-services → pb-gen-enums). Short-circuits on the first non-zero exit code so a partial refresh doesn't leave the tree in a half-migrated state.
For most changes — Valve added a new message, renamed a field, added a service — that's all you need.
Downloads .proto files listed in protobuf_list.txt into protobufs/.
Reads URLs from protobuf_list.txt line by line, skipping blank lines and # comments. Downloads them in parallel (8 concurrent workers). For each downloaded file:
- Rename
*.steamclient.proto→*.proto. - Prepend
syntax = "proto2";if missing (idempotent). - Swap
cc_generic_services→py_generic_services. - Rewrite intra-file
.steamclient.protoreferences to.proto.
Locally-maintained protos are protected. gc.proto and test_messages.proto are renamed to *.notouch before the fetch runs and restored after, so an upstream 404 or the file being missing from protobuf_list.txt can never nuke them.
Output: prints downloaded N ok, M missing at the end.
Invokes protoc to turn protobufs/*.proto → steam/protobufs/*_pb2.{py,pyi}.
- Wipes any existing
steam/protobufs/*_pb2.{py,pyi}first — a failure surfaces as "no files written" instead of a mix of stale and new. - Single
protocinvocation over all.protofiles:protoc --python_out=steam/protobufs --mypy_out=steam/protobufs --proto_path=protobufs protobufs/*.proto - Post-process:
-
.py— sibling protobuf imports get thesteam.protobufs.prefix so runtime import works withoutsteam/protobufs/onsys.path. -
.pyi— sibling imports get the same prefix; per-messageDESCRIPTOR: _descriptor.Descriptoroverrides are stripped. See Type Checking for why.
-
Output: compiled N .py + M .pyi under steam/protobufs.
Regenerates the ServiceName -> _pb2 module map in steam/core/msg/unified.py.
Scans every protobufs/*.proto for top-level service Foo { rpc Bar(...); } declarations and rewrites the dict body between MARK_SERVICE_START and MARK_SERVICE_END inline-comment markers.
The service_lookup dict is what makes client.send_um('Player.GetGameBadgeLevels#1', ...) work — it maps the service name (Player) to the module that defines its methods.
Output: wrote N service registrations to unified.py.
Regenerates steam/enums/proto.py from every compiled *_pb2 module.
Scans steam/protobufs/*_pb2.py for top-level EnumTypeWrapper instances, filters out any already declared in steam/enums/common.py (which is hand-written), strips the k_<EnumName>_ prefix that Valve puts on member names, and emits a Python file of proper SteamIntEnum classes.
Members that collide with Python keywords or start with a digit fall back to the factory form SteamIntEnum(name, {member: value}) — the class X(SteamIntEnum): NAME = VALUE syntax can't express those.
Output: wrote N enum classes to steam/enums/proto.py.
Say Valve just added a new proto file at SteamDatabase/Protobufs/steam/foo_bar_baz.proto.
-
Edit
protobuf_list.txt— add the raw GitHub URL:https://raw.githubusercontent.com/SteamDatabase/Protobufs/master/steam/foo_bar_baz.protoKeep the file sorted by domain / directory to match the existing structure. Comments (
# lines) are ignored, feel free to annotate. -
Run the full refresh:
poetry run pb-update
-
Confirm the new module appears:
ls steam/protobufs/foo_bar_baz_pb2.py
-
If it declared any
services,pb-serviceswill have already picked them up. If it declared any enums,pb-gen-enumsfolded them intosteam/enums/proto.py. -
Run the test suite to make sure nothing regressed:
poetry run pytest
Delete the URL from protobuf_list.txt, delete protobufs/foo.proto and steam/protobufs/foo_pb2.{py,pyi}, then re-run pb-update. pb-services and pb-gen-enums rescan from scratch so stale entries drop out automatically.
Two protos in protobufs/ are not in protobuf_list.txt because they don't exist upstream in fetchable form:
-
gc.proto— game-coordinator base messages. -
test_messages.proto— used by the test suite.
pb-fetch protects both via .notouch renaming.
protobufs/ # .proto sources (fetched)
protobuf_list.txt # URL list for pb-fetch
steam/protobufs/ # generated *_pb2.py + *_pb2.pyi (do not hand-edit)
steam/core/msg/unified.py # service map (regen'd by pb-services)
steam/enums/proto.py # protobuf enums (regen'd by pb-gen-enums)
scripts/pb_*.py # the four scripts + pb_postprocess + pb_update runner
- Type checking hiccups after regeneration? Type Checking covers the mypy-protobuf / pyright interplay.
- Wondering what changed vs. upstream in the last regen? Fork Changes has the fork's protobuf-modernization diff.
H47R15/steam — maintained fork of ValvePython/steam. MIT licensed.