minecraft-protocolo-py
A multi-version Minecraft Java Edition client/server protocol library for Python, built on top of two existing pieces instead of reinventing them:
- protolib — the generic
binary protocol engine (reads/writes packets from JSON/YAML schema
definitions, in the
node-protodef/minecraft-dataformat). - minecraft-data —
the actual per-version packet definitions (
protocol.json), the same dataset used bynode-minecraft-protocol.
minecraft_protocolo is the missing glue layer: pick a Minecraft
version, get a working Client or Server that speaks that
version's wire protocol — handshake, login, compression, keep-alive,
and error handling included.
protolib (generic protocol engine)
│
minecraft-data (per-version protocol.json definitions)
│
minecraft_protocolo (this library: Client / Server / version resolver)
│
your own app (game logic only)
Install
pip install protolib
pip install minecraft-data
pip install minecraft-protocolo-py
protolib and minecraft-data are separate, real dependencies —
neither is vendored or copied into this package. minecraft-data in
particular carries the full per-version block/item/protocol dataset
for both Java and Bedrock Edition (~100 MB) and is genuinely useless
to duplicate inside every project that needs it; VersionResolver
locates it automatically wherever pip installed it.
Supported versions
Any Minecraft Java Edition version your installed minecraft-data
package tracks — 52+ versions as of minecraft-data 3.20.0, from
early snapshots through 1.19.2, likely more in newer releases since
that dataset keeps growing. Well-known build aliases that people
actually type — 1.8.9, 1.7.10 — resolve automatically to the
entry minecraft-data tracks internally (1.8, 1.7), since the
network protocol never changed between those builds.
from minecraft_protocolo import VersionResolver
resolver = VersionResolver()
resolver.list_versions() # ['1.7', '1.8', ..., '1.16.5']
resolver.protocol_version_number("1.8.9") # 47
Quick start — client
from minecraft_protocolo import Client
client = Client(host="localhost", port=25565, username="Xd", version="1.8.9")
@client.on("connect")
def on_connect():
print("connected!")
@client.on("packet")
def on_packet(name, params):
if name == "chat":
print("chat:", params["message"])
client.on("error", lambda e: print("error:", e))
client.on("kicked", lambda reason: print("kicked:", reason))
client.connect() # blocks; pass background=True to run in a thread
Keep-alive is handled automatically (the client answers the server's
keep_alive packets on its own — no need to do it in your handler).
Compression is detected and applied automatically as soon as the
server sends set_compression during login.
Quick start — server
from minecraft_protocolo import Server
server = Server(host="0.0.0.0", port=25565, version="1.8.9",
motd="my server", max_players=20)
@server.on("login")
def on_login(conn):
print(conn.username, "joined —", conn.uuid)
@server.on("packet")
def on_packet(conn, name, params):
if name == "chat":
print(conn.username, ":", params["message"])
server.on("error", lambda e, conn=None: print("error:", e))
server.listen() # blocks
The server answers server-list pings (MOTD, player count, protocol
version) and sends its own periodic keep_alive, kicking players who
stop responding — same defaults as node-minecraft-protocol
(keepAlive=True, 4s ping interval, 10s kick timeout).
online_mode=True is implemented -- RSA key exchange,
AES-128-CFB8 encryption, and Mojang session verification, matching
node-minecraft-protocol's server-side flow exactly (see "Online-mode
(real Minecraft accounts)" below). Requires the cryptography package.
Online-mode (real Minecraft accounts)
Both Server and Client support online_mode -- RSA key exchange,
AES-128-CFB8 stream encryption, and verification against Mojang's
session server, the same flow real Minecraft uses. Requires
pip install cryptography.
Running on Termux/Android?
pip install cryptographyinstalls without error, but importing it can fail with:dlopen failed: cannot locate symbol "PyBaseObject_Type" referenced by ".../cryptography/hazmat/bindings/_rust.abi3.so". This is a known ABI mismatch between the prebuilt PyPI wheel (compiled for glibc Linux) and Termux's Python (built for Android/bionic libc) -- not a bug in this library, and not somethingpip install cryptographyitself will warn you about. Fix: use Termux's own build instead of the PyPI wheel:pip uninstall cryptography -y pkg install python-cryptographyThis installs a version built specifically for Termux's Python and resolves the import error. Confirmed working end-to-end (RSA + AES-128-CFB8 online-mode login against a real vanilla server) on Termux with
python-cryptography48.0.1.
Server, hosting for real accounts (rejects cracked/offline clients):
server = mp.Server(host="0.0.0.0", port=25565, version="1.8.9", online_mode=True)
# per-username exceptions if you need them:
# server = mp.Server(..., online_mode=True, online_mode_exceptions=["mytestbot"])
server.listen()
Client, connecting to a real online-mode server: this library
does NOT perform the Microsoft/Mojang login flow itself (that needs a
browser round-trip and belongs in something like prismarine-auth or
your launcher's own auth, not a protocol library). Get a valid
access_token and the account's session_uuid from wherever you
already authenticate, then:
client = mp.Client(
host="mc.example.com", port=25565, username="YourRealUsername", version="1.8.9",
auth=True, access_token="<a valid, unexpired Mojang/MSA access token>",
session_uuid="<the account's real profile UUID>",
)
client.connect()
Without auth=True, both sides behave like vanilla does with no
credentials: the RSA/AES handshake still completes, but the server's
own Mojang check will reject the login right after -- exactly like
trying to join a real server with a cracked client.
Note on this implementation: the RSA key exchange and AES-128-CFB8
encryption are fully implemented and tested end-to-end against real
protolib over real TCP sockets in this repo's test suite. The actual
HTTP calls to sessionserver.mojang.com are implemented against the
publicly documented API (https://wiki.vg/Protocol_Encryption) but
untested against the live service (no network access in the
environment this was built in) -- give the full flow one real-world
smoke test against an actual Mojang-authenticated client/server before
relying on it in production.
Examples
examples/client.py and examples/server.py are runnable, tested
reference implementations built on the library — game logic only,
none of the networking (that's all handled by Client/Server):
# a chat bot that responds to !hello and !ping
python examples/client.py <host> <port> <username> [version]
# a genuinely playable flat-world server: real client can log in,
# walk around, and use chat commands (!ping, !online, !spawn,
# !creative, !survival)
python examples/server.py --port 25565 --version 1.8.9 --motd "my server"
# same flat-world server, but online_mode=True -- only real,
# logged-in Minecraft accounts can join (see "Online-mode" above)
python examples/server_online_mode.py --port 25565 --version 1.8.9
# query a server's status (MOTD/players/version/latency) without logging in
python examples/ping_server.py <host> [port] [version]
These are meant as a starting point to copy from, not a CLI you're
expected to use as-is — real projects put their own game logic on top
of Client/Server directly.
Error handling
Deserialization failures are wrapped to match the format used by
node-minecraft-protocol (PartialReadError), so anyone familiar
with that library will recognize the shape immediately:
PartialReadError: Deserialization error for play.toClient : Read error : buffer exhausted at offset=5: needed 1 bytes, 0 remaining
Client/Server never raise these directly into your code — they're
emitted through the error event, same as Node's EventEmitter
convention (an unhandled 'error' event still gets printed to
stderr instead of silently disappearing).
Discovering the packet API
The API is the protocol.json for whatever version you loaded — not
a fixed list this README could show (74 packets just for play.toClient
in 1.8, times every version your minecraft-data install tracks).
Field names are also case-sensitive and don't always match what the
wiki or another bot would lead you to expect (gameMode not
gamemode, flyingSpeed not flySpeed). Two ways to find the real
name for the version you're using:
# From a live connection (Client or Server's ClientConnection) --
# always exact for whatever version was actually negotiated:
print(client.list_packet_names()) # every valid play.toServer packet name
print(conn.list_packet_names("play", "toClient")) # play.toClient for a server connection
# Or straight from minecraft-data itself, no connection needed (strip
# the "packet_" prefix and the internal "packet" dispatcher entry):
import json
from minecraft_protocolo.version_resolver import _default_data_root
with open(f"{_default_data_root()}/pc/1.8/protocol.json") as f:
proto = json.load(f)
names = [k[len("packet_"):] for k in proto["play"]["toClient"]["types"] if k.startswith("packet_")]
print(names)
Sending a name that doesn't exist raises UnknownPacketError instead
of a confusing low-level error, and lists the valid names for that
state. A wrong or missing field inside otherwise-correct params
raises SerializationError with a hint pointing at protocol.json.
What's implemented
- Handshake, login (offline-mode), status/server-list ping
ping_status(host, port, version)— standalone status query (MOTD, player count/sample, version, favicon, latency) without a full login, same asnode-minecraft-protocol'smc.ping()- Correct offline-mode UUID (
uuid_utils.offline_uuid) — byte-for-byte identical to vanilla/node-minecraft-protocol'snameToMcOfflineUUID(plain MD5 with patched version/variant bits, notuuid.uuid3()with a namespace — those produce different UUIDs and will desync with anything that computes its own) - zlib packet compression, detected and applied automatically
- Automatic keep-alive, both directions, with timeout/kick handling
- Multi-version support in a single
Server(version=None) — resolves each connecting client's version from its handshakeprotocolVersion - Plugin channels (
custom_payload) —register_channel()/unregister_channel()/send_channel()on bothClientand server-sideClientConnection login_plugin_request/responsehandled automatically (1.13+, protocol ≥ 385) so modern servers don't hang the login waiting for a reply this client doesn't have a plugin forstateevent (fires on every handshaking → login → play transition),rawevent (every packet, name + params together), and every packet is also emitted under its own name (client.on("chat", ...)) in addition to the generic"packet"event — all three always fire together, pick whichever fitswrite(name, params)alias forsend_packet()on bothClientandClientConnection, matchingnode-minecraft-protocol's spellingServer(favicon=..., before_ping=...)— server-list icon and a hook to customize/reject the status response per-connectiononline_mode=True— RSA key exchange, AES-128-CFB8 encryption, Mojang session verification (server_hash()matches wiki.vg's published test vectors exactly),online_mode_exceptionsallowlist, on bothServer(hosting) andClient(auth=True+ a token you obtained elsewhere — see "Online-mode" above)node-minecraft-protocol-style error formatting
What's not implemented yet
- The Microsoft/Mojang OAuth login flow itself (getting an
access_tokenin the first place) — intentionally out of scope, see "Online-mode" above for why. - Bedrock Edition (only Java/"pc" protocols are wired up) — a library limitation.
- Automatic parsing of arbitrary plugin-channel payloads (e.g. a
Forge/Fabric mod's custom binary format) —
register_channel()hands you the raw bytes; decoding them requires a schema only the mod author has, same tradeoffnode-minecraft-protocolmakes.
None of the above is the library's job, and intentionally so — world
state, chunks, entity spawning, and physics are game logic, which
lives in your own code on top of Client/Server, not in
minecraft_protocolo itself. examples/server.py shows a working
approach (flat world, chunks, tab list) that a real client can
actually join and walk around in — extend that pattern for anything
beyond a flat world.
Project layout
minecraft_protocolo/
├── __init__.py
├── version_resolver.py # "1.12.2" -> a loaded protolib.Protocol
├── client.py # Client + ping_status(): connect, handshake, login, events
├── server.py # Server: accept connections, per-player events
├── errors.py # PartialReadError and friends
├── events.py # small Node-style EventEmitter
├── uuid_utils.py # correct offline-mode UUID (not uuid.uuid3!)
├── encryption.py # AES-128-CFB8 for online-mode
└── mojang_auth.py # server RSA keypair + Mojang session verification
# (no data/ here -- protocol.json/version.json etc. come from the
# minecraft-data PyPI package, located automatically at runtime)
examples/
├── client.py # chat bot example
├── server.py # playable flat-world server example
├── server_online_mode.py # same, but online_mode=True (real accounts only)
└── ping_server.py # query a server's status without logging in
pyproject.toml
Metadata
Release files for minecraft-protocolo-py 0.1.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| minecraft_protocolo_py-0.1.4.tar.gz | 42.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| minecraft_protocolo_py-0.1.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 82.8 kB
Release files / minecraft_protocolo_py-0.1.4.tar.gz
| Download URL | minecraft_protocolo_py-0.1.4.tar.gz |
|---|---|
| Size | 42.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e03f685f8f609c98ff027c1cbd00fd165671b6e364554f2f51630db50aed59b8
|
|
BLAKE2b-256 checksum How to use checksums |
36e113ad0fa32c96b273959f7d7deea12c3f57d9cd343933630366081b6850c9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.13
|
Release files / minecraft_protocolo_py-0.1.4-py3-none-any.whl
| Download URL | minecraft_protocolo_py-0.1.4-py3-none-any.whl |
|---|---|
| Size | 40.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ba31bad8b4a1f3d563d9bcc3d12d2af182cfeaebebd2382ffb641ee970d31d09
|
|
BLAKE2b-256 checksum How to use checksums |
24e95358b97adfcffac9c0caec1e823e6fed992f8c5cb40d44d0893a19b04e70
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.13
|