Skip to content

Networking

The client and the server talk over ENet, a thin reliability layer on top of UDP. Each side reads and writes buffers. Every packet starts with a 16-bit unsigned integer that names its ID.

A client goes through one of two exchanges after it connects: a status ping, or a full login. Both exchanges start the same way. Both end with the server closing the connection or keeping it open.

Status pings

A client can connect just to ask a server about itself: protocol version, MOTD, player count, and slot limit. The server does not keep this connection open.

Flow

Step Sender Receiver Description
1 Client Server The client connects
2 Client Server The client sends a status request packet
3 Server Client The server responds with a status response packet
4 Server Client The server closes the connection

Authentication

If the client sends a login request instead of a status request, the server starts an authentication exchange.

Flow

Step Sender Receiver Description
1 Client Server The client connects
2 Client Server The client sends a login request packet: username, public Ed25519 key, invite code, protocol version, and registry hashes
3 Server Client If the server accepts the request, it responds with a challenge packet holding a random nonce
4 Client Server The client signs the nonce, the server's password, and the current UTC time, and sends the signature back in a challenge response packet
5 Server Client If the signature matches, the server admits the client with a packet holding its assigned username
6 N/A N/A Entity exchange and game packets start here
Identity

The client proves its identity with an Ed25519 keypair. The server challenges the client with a random nonce, and the client signs it.

Server passwords

If the server has a password, the client folds it into the signed message. This way, the exchange also proves the client knows the password. The client never sends the password itself.

Invite codes

The invite code is a one-time code. On a successful login, the server consumes the invite code. The server then adds the client's public key to its whitelist. The same client can then reconnect later without a new invite.

Registry hashes

The client sends its block, biome, fluid, and tint registry hashes. The server checks each hash against its own. A mismatch means the two sides disagree on what a given ID means. When that happens, the server disconnects the client with a checksum mismatch reason. Neither side acts on the disagreement.

This check protects both sides:

  • It stops a mismatched client from receiving chunk data it would render as corrupted or nonsensical.
  • It stops that same client from placing a block ID the server does not recognize.

World

Once the server admits a client, the client asks for chunks and reports block and entity actions. Only the server can make a world change final.

Chunk transfer

The client sends RequestChunk for any chunk position it needs. The server answers with ChunkBlocks, holding that chunk's full block data. The server also sends ChunkBlocks on its own initiative, as a full resync.

Block and entity actions

The client sends PlayerAttack or PlayerInteract to act on its current target. A target field names an entity. If the client is targeting a block instead, this field carries a null entity.

For a block target, the packet also carries the block's position and the ID the client expects to find there. It also carries the hit face, normal, and point. These three fields match the physics::BlockHit fields the block callbacks expect.

The server replies to every PlayerAttack or PlayerInteract with a SetBlock for the position involved. The server sends this reply whether or not the block changed. If the block changed, SetBlock also reaches every other client with that chunk loaded.

Packet reference

Each packet starts with a 16-bit unsigned integer that names its ID. The packets are named and structured as follows:

0x0001 StatusRequest

Type Name Description
uint32 major Major game version
uint32 minor Minor game version
uint32 patch Patch game version

0x0002 StatusResponse

Type Name Description
uint32 major Major game version
uint32 minor Minor game version
uint32 patch Patch game version
uint32 tags Server tags
string motd One-line MOTD no longer than 32 characters
uint16 players Current player count
uint16 slots Maximum player slots
Server flags
Value Description
0x00000001 Server is password-protected
0x00000002 Server has whitelist enabled

0x0003 AuthRequest

Type Name Description
uint32 major Major game version
uint32 minor Minor game version
uint32 patch Patch game version
uint8[32] pkey Public Ed25519 key
uint64 invite Invite-code
uint64 biomes_hash Biome registry hash
uint64 blocks_hash Block registry hash
uint64 fluids_hash Fluid registry hash
uint64 tints_hash Tint registry hash
string username Desired username

0x0004 AuthChallenge

Type Name Description
uint8[64] nonce Nonce for the client to sign

0x0005 AuthResponse

Type Name Description
uint8[64] signature Signed nonce + server password + UNIX minutes

0x0006 AuthAdmission

Type Name Description
string username Assigned username

0x0007 Disconnect

Type Name Description
uint32 reason Disconnect reason
Reason values
Value Description When
0x00000000 Unspecified reason Anything not specified below
0x00000001 Client disconnect Client manually disconnects
0x00000002 Client shutdown Client closes the game
0x00000003 Entity ID desync EnTT registry goes nuts
0x00000004 Checksum mismatch Client isn't synced up with server mods
0x00000005 Not whitelisted Client's public key is not in the whitelist
0x00000006 Invalid signature Client messed up nonce signing
0x00000007 Outdated client Client is too old for the server
0x00000008 Outdated server Client is too new for the server
0x00000009 Server is full No more free slots available
0x0000000A Server shutdown Server is terminating

0x0008 RequestChunk

Type Name Description
vector3<int32> cpos Requested chunk position

0x0009 ChunkBlocks

Type Name Description
vector3<int32> cpos Chunk position
data voxels Serialized and compressed block storage contents

0x000A ChunkBiomes

Type Name Description
uint32 realm Biome realm
vector2<int32> cpos XZ-position
uint32<256> biomes A list of biome IDs

0x000B SetBlock

Type Name Description
vector3<int64> bpos World-scale block position
uint32 id Resulting numeric block ID

0x000C PlayerAttack

Type Name Description
entity target Targeted entity, or null if the target is a block
vector3<int64> bpos Targeted block position, used only when target is null
uint32 expected_id Block ID the client expects at bpos, used only when target is null

0x000D PlayerInteract

Type Name Description
entity target Targeted entity, or null if the target is a block
vector3<int64> bpos Targeted block position, used only when target is null
uint32 expected_id Block ID the client expects at bpos, used only when target is null
uint8 face Hit face (one of blocks.FACE_XXXX), used only when target is null
vector3<float> normal Hit normal, used only when target is null
vector3<float> point Hit point, local to the block, used only when target is null