💾 Archived View for thingvellir.net › protocol-a112.gmi captured on 2023-04-26 at 13:11:36. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-04-19)
-=-=-=-=-=-=-
Minecraft is an unimaginably bad mess. If you want to make a network protocol for your game, please take this as a lesson on what *not* to do. If you want to make an old-school Minecraft client or server, consider targeting the Classic protocol instead.
NOTE: This information was reverse-engineered from a decompiled Mojang client and server. This document contains no original Mojang program code.
All integers and floats are big-endian. All integers are signed. Text is encoded in a variant of UTF-8, length prefixed with a signed big-endian 16-bit integer.
Java InputStream 'modified UTF-8'
This packet serves no purpose. The Mojang client and server send this to eachother once per second.
struct { pid: i8 = 0x00, }
The client sends this packet to the server after recieving 02 Handshake Response.
struct { pid: i8 = 0x01, version: i32 = 0x02, nick_len: i16, nick: []i8, key_len: i16, key: []i8, }
The server sends this packet to the client after recieving 01 Client Info.
struct { pid: i8 = 0x01, player_id: i32, unused_1: i16 = 0x00, unused_2: i16 = 0x00, }
The client sends this to the server immediately upon connection. The Mojang server uses this to verify an account's premium status and and immediately sends FF Disconnect if it cannot verify the username.
struct { pid: i8 = 0x02, name_len: i16, name: []i8, }
The server sends this to the client in response to 02 Handshake Start.
The Mojang server sets 'key' to a random hex string or to `-` to indicate the server is not verifying usernames.
struct { pid: i8 = 0x02, key_len: i16, key: []i8, }
The client sends a user-input string to the server. The server can send this to any client to add a line of arbitrary text to that client's chat history box.
The server must send this packet to each connected client individually to echo a message to all of them.
struct { pid: i8 = 0x03, msg_len: i16, msg: []i8, }
The client uses this to update the skybox's state.
struct { pid: i8 = 0x04, time: i64, }
The server sends this to the client upon connection to initialize the content of its inventory. The client sends this to the server the to inform it of an inventory update.
Inventory type -1 is the player's main 9x4 inventory space. Type -2 is the player's 2x2 crafting space. Type -3 is the player's 1x4 armor space.
The `data_len` field describes the length of an array of 'item' structures, not individual bytes. If the item structure's `id` field is -1, the `amount` and `meta` fields are not sent and the next byte is the start of the next item in the array. Why the `amount` and `meta` fields are only conditionally sent is anyone's guess.
struct item { item: i16, // 00-FF block ID or 100-7FFF item ID amount: i8, // ! These two fields may not be sent meta: i16, // Item damage or block variant } struct { pid: i8 = 0x05, type: i32, data_len: i16, // ! Array length, not byte length data: []item, }
The server sends this to the client to tell the them to prepare to spawn and update the location the compass item points to.
struct { pid: i8 = 0x06, x: i32, // Block position y: i32, z: i32, }
These packet IDs are unused.
The Mojang client sends this every tick while they are not moving or rotating. It is optional for the client to send this.
struct { pid: i8 = 0x0a, flying: bool, }
The Mojang client sends this every tick while they are moving and not rotating. It is optional for the client to send this.
struct { pid: i8 = 0x0b, x: f64, y: f64, top: f64, // ! The `y` field plus the height of the player's hitbox. z: f64, flying: bool, }
The Mojang client sends this every tick while they are rotating and not moving. It is optional for the client to send this.
struct { pid: i8 = 0x0c, yaw: f32, pitch: f32, flying: bool, }
The Mojang client sends this every tick while the player is both moving and rotating. If implementing this protocol, the client or server may send this packet exclusively rather than packets 0A-0C.
struct { pid: i8 = 0x0d, x: f64, y: f64, top: f64, // ! The `y` field plus the height of the player's hitbox. z: f64, yaw: f32, pitch: f32, flying: bool, }
The client sends this when the player starts breaking or using a block.
enum mode: i8 { Click = 0, // Right click sends this mode only DigContinue = 1, Cancel = 2, Break = 3, } struct { pid: i8 = 0x0e, mode: i8, // See the 'mode' enum above x: i32, y: i8, z: i32, side: i8, // Undocumented, allowed to be 0x00 }
The client sends this when the player places a block in the world or right-clicks with an item on a block surface.
struct { pid: i8 = 0x0f, item: i16, // Either a 00-FF block or 100-7FFF item x: i32, y: i8, z: i32, dir: i8, // Undocumented, allowed to be 0x00 }
The client sends this when they change their selected item slot. The server sends this to update the held item model in another player's hand. The unmodified Mojang client only allows the player to select slots 0-8.
struct { pid: i8 = 0x10, entity: i32, // Entity ID of the player to update, client sets this to 0 slot: i16, // Client sends inventory slot, server sends block/item ID }
The server sends this to the client to add an item to their inventory. It appears the client decides which slot the new item goes into. What happens when a client with a full inventory recieves this packet is unknown.
struct { pid: i8 = 0x11, item: i16, // 00-FF block ID or 100-7FFF item ID amount: i8, // How many items of this type are given meta: i16, // Block variant or item damage }
The server sends this to show a punching animation on an entity. The client sends this to tell the server to show a punching animation on their model.
struct { pid: i8 = 0x12, entity: i32, unused: i8, // Almost always 0x01 }
The server sends this packet to create a player model visible to the recieving client.
struct { pid: i8 = 0x14, name_len: i16, name: []i8, entity: i32, x: i32, // ! Fixed-point position, divide by 32 to get real position y: i32, z: i32, yaw: i8, // ! Fixed-point rotation, -128 to 127 is -π to π pitch: i8, equipped_item: i16, // 00-FF block ID or 100-7FFF item ID, visible in hand }
The server sends this to create an item entity that can be taken by a player.
struct { pid: i8 = 0x15, item: i16, // 00-FF block ID or 100-7FFF item ID amount: i8, x: i32, // ! Fixed-point position, divide by 32 to get real position y: i32, z: i32, yaw: i8, // ! Fixed-point rotation, -128 to 127 is -π to π unused1: i8, unused2: i8, }
The server sends this to remove an item entity and show a 'picking up the item' animation.
struct { pid: i8 = 0x16, item_entity: i32, // The entity ID of the item being picked up player_enity: i32, // The entity ID of the player picking up the item }
The server sends this to create a vehicle entity on the client.
enum type { Boat = 1, Minecart1 = 10, // minecart variants, probably furnace and chest Minecart2 = 11, Minecart3 = 12, } struct { pid: i8 = 0x17, entity: i32, type: i8, // See the 'type' enum above x: i32, // ! Fixed-point position, divide by 32 to get real position y: i32, z: i32, }
The server sends this to create a monster entity on the client.
struct { pid: i8 = 0x18, entity: i32, type: i8, // TODO: entity types are a mess x: i32, // ! Fixed-point position, divide by 32 to get real position y: i32, z: i32, yaw: i8, // ! Fixed-point rotation, -128 to 127 is -π to π pitch: i8, }
This packet is similar to 0A Player Still, but for non-player entities.
struct { pid: i8 = 0x1e, entity: i32, }
This packet is similar to 0B Player Move, but for non-player entities.
struct { pid: i8 = 0x1f, entity: i32, dx: i8, // ! Fixed-point position offset, divide by 32 to get real offset dy: i8, dz: i8, }
This packet is similar to 0C Player Rotate, but for non-player entities.
struct { pid: i8 = 0x20, entity: i32, yaw: i8, // ! Fixed-point rotation, -128 to 127 is -π to π pitch: i8, }
This packet offsets an entity.
struct { pid: i8 = 0x21, entity: i32, dx: i8, // ! Fixed-point position offset, divide by 32 to get real offset dy: i8, dz: i8, yaw: i8, // ! Fixed-point rotation, -128 to 127 is -π to π pitch: i8, }
This sets the absolute position of an entity in world-space.
struct { pid: i8 = 0x22, entity: i32, x: i32, // ! Fixed-point position, divide by 32 to get real position y: i32, z: i32, yaw: i8, // ! Fixed-point rotation, -128 to 127 is -π to π pitch: i8, }
This tells a client to prepare to load or unload a world chunk. Use of this packet is optional and clients should prepare to recieve chunk data upon recieving 33 Chunk Data.
struct { pid: i8 = 0x32, x: i32, z: i32, load: bool, // If false, the client unloads this chunk }
This sends world chunk data to the client.
struct { pid: i8 = 0x33, x: i32, y: i16, z: i32, x_size: i8, y_size: i8, z_size: i8, data_len: i32, data: []u8, // DEFLATE-compressed data }
This updates a large portion of a world chunk.
struct { pid: i8 = 0x34, x: i32, z: i32, data_len: i16, // ! This is an array-length for the following three fields indices: []i16, // TODO: figure out how these are arranged blocks: []i8, metas: []i8, }
This updates a single block in a world chunk.
struct { pid: i8 = 0x35, x: i32, y: i8, z: i32, block: i8, // 00-7F block ID meta: i8, }
This sets special NBT data on a single block in a world chunk.
struct { pid: i8 = 0x3b, x: i32, y: i16, // ! Most block packets use an i8 for the y field, this doesn't z: i32, data_len: i16, data: []u8, // Gzip compressed NBT data }
This is sent by a server to kick a player. This is sent by a client to tell the server it is about to disconnect.
struct { pid: i8 = 0xff, msg_len: i16, msg: []u8, }