# Classic protocol Based on [the ancient texts from wiki.vg](https://wiki.vg/Classic_Protocol), with some modern changes # Classicube.net communication ## Heartbeats Servers can advertise themselves to the ClassiCube server list by sending a GET or POST request to `https://www.classicube.net/server/heartbeat` or `https://www.classicube.net/heartbeat.jsp` with certain specific request parameters. It doesn’t make a huge difference if you use POST over GET, but POST is generally regarded as “more secure” when transmitting sensitive information. The parameters the heartbeat endpoint expects are... | Field | Description | | - | - | | port | `int 1-65535` Which port the server is using. For public servers, this *must* be accessible or the server won’t be listed publicly. | | max | `int 1-?` Maximum amount of players allowed on the server. | | name | `string` The name of the server. Should be relatively short, preferably under 256 characters, and should only contain alphanumeric and some basic ASCII symbols. | | public | `bool true/false` Whether or not the server should be publicly listed. The server will be tested by an automated port tester to ensure that it’s public - if the connection fails, the server will not be listed and an error will be returned. | | version | `int 7` Latest protocol version supported by the server. For all existing clients, this is either 6 or 7, and ClassiCube.net officially only supports protocol 7 at this time. Note: [[public/Classic_Protocol_Extension|CPE extensions]] don’t affect the server’s `version` number. | | salt | `string` A random 16-character base-62 string that will be used in authentication. Users that obtain this string can log into your server using any name, so keep this secret. It’s recommended to rotate salts every 24-48 hours. More details on how the salt plays into authentication below... | | users | `int 0-?` The number of users currently playing on the server. | | software | `string` A name to identify the server software being used. | The heartbeat endpoint returns a URL for the server if everything is valid. ## User authentication/validation The `mppass` that is provided during the network handshake by the user is used to validate that the user is who they claim to be. If the user received the mppass from ClassiCube.net (e.g. they are logged in and want to join your server), the `mppass` will match an md5sum of `salt + username`. In other words: ``` if (md5(salt + player_name) == mppass) { // valid login } else { // invalid or expired mppass } ``` Note: since the salt is used for user authentication, you should make sure to keep it a secret. Since md5 is [generally not very secure](https://docs.datadoghq.com/code_analysis/static_analysis_rules/go-security/import-md5/), the official recommendation is to generate long salts and change your server salt every 24-48 hours to ensure that even the fastest md5 hash bruteforcers can’t reverse it in time to use it. ## Skins Skins are png files that can be found at `https://www.classicube.net/skins/{case-sensitive-username}.png` They’re uploaded by visiting [the account page on ClassiCube.net](https://www.classicube.net/acc/) while logged in as the user you would like to change the skin for. The aforementioned path will either directly serve or redirect you to the canonical path for a skin, or redirect you to the default skin. To skip a redirect step in the process, you can directly request a skin from our CDN at `https://cdn.classicube.net/skin/{case-sensitive-username}.png` - note that a missing skin will return a `403` response code at this time. **Note:** A missing skin will return a `403` response code **Note:** The redirect from ClassiCube.net to the CDN will always redirect to `http` at this time, even when originally requested from `https`. ![Goodly’s skin](//cdn.classicube.net/skin/Goodly.png) ![AndrewPH’s skin](//cdn.classicube.net/skin/AndrewPH.png) ![UnknownShadow200’s skin](//cdn.classicube.net/skin/UnknownShadow200.png) ## Faces Similar to skins, ClassiCube.net provides a rendered head for use in avatars, etc. They can be found at `https://www.classicube.net/face/{case-sensitive-username}.png` - which will redirect you to an image file every time - or directly at the CDN via `https://cdn.classicube.net/face/{case-sensitive-username}.png`. **Note:** The ClassiCube.net path will always take you to an image, but entails a redirect. Calling the CDN directly will skip that redirect but return a `403` response code for faces that don’t exist/haven’t been created yet. ![Goodly’s face](https://cdn.classicube.net/face/Goodly.png) ![AndrewPH’s face](https://cdn.classicube.net/face/AndrewPH.png) ![UnknownShadow200’s face](https://cdn.classicube.net/face/UnknownShadow200.png) # Packet protocol All communication is done over TCP on one port. Every packet begins with a single byte representing the Packet ID ## Protocol data types Everything in the base Classic Protocol uses these definitions when encoding and decoding packet structures. All numbers are in [network order](http://en.wikipedia.org/wiki/Endianness#Networking) (big-endian). Signed integers are in [two’s compliment](https://en.wikipedia.org/wiki/Two%27s_complement). The last 5 bits of the fixed-point types are fractional, i.e. they come after the decimal point. You can think of these as 32nds of a block (2^5 = 32). To convert to a block position (integer), it needs to be rounded down; this is equivalent to an arithmetic shift right by 5. | Type | Byte length | Description | | - | - | - | | Byte | 1 | Unsigned byte representing 0 to 255 | | SByte | 1 | Signed byte representing -128 to 127 | | FByte | 1 | Signed byte representing fixed-point number with 5 fractional bits from -4 to 3.96875 | | Short | 2 | Signed integer representing -32768 to 32767 | | FShort | 2 | Signed fixed-point integer representing number with 5 fractional bits from -1024 to 1023.96875 | | String | 64 | [US ASCII encoded](https://en.wikipedia.org/wiki/ASCII) string padded with space characters (`0x20`) to 64 bytes in length. | | Byte array | 1024 | Binary data padded with null bytes (`0x00`) | **Note:** Some packets will use alternative byte lengths when certain [[public/Classic_Protocol|CPE extensions]] are enabled. ## Client -> Server packets ### Player identification **ID:** `0x00` Sent by a player joining a server with relevant information. The protocol version is `0x07`, unless you're using a client below 0.28. | Field name | Field type | | - | - | | Packet ID | Byte | | Protocol version | Byte (`0x07` for clients compatible with 0.28 and higher) | | Username | String | | MPPass | String | | Unused (Used to indicate [[public/Classic_Protocol_Extension|CPE]] support) | Byte (`0x00` for the core protocol) | ### Set block **ID:** `0x05` Sent when a user changes a block. The mode field indicates whether a block was created (`0x01`) or destroyed (`0x00`). Block type is always the type player is holding, even when destroying. Client assumes that this command packet always succeeds, and so draws the new block immediately. To disallow block creation, server must send back a `Set block` packet with the old block type. | Field name | Field type | | - | - | | Packet ID | Byte | | X | Short | | Y | Short | | Z | Short | | Mode | Byte (`0x00` or `0x01`) | | Block type | Byte | ### Position and orientation **ID:** `0x08` Sent frequently (even while not moving) by the player with the player's current location and orientation. Player ID provided in this packet is always `-1` (or `255` depending on how you interpret it), referring to itself. | Field name | Field type | | - | - | | Packet ID | Byte | | Player ID | SByte (`0xFF` AKA `-1` AKA `255`) | | X | FShort | | Y | FShort | | Z | FShort | | Yaw (Heading) | Byte | | Pitch | Byte | ### Message **ID:** `0x0d` Contains chat messages sent by player. Player ID provided in this packet is always `-1` (`255` depending on how you interpret it), referring to itself. **Note:** May contain _public/Classic_Colors_. **Note:** The meaning of the Player ID field may change based on CPE extension support. | Field name | Field type | | - | - | | Packet ID | Byte | | Player ID | SByte (`0xFF`) | | Message | String | ## Server -> Client packets ### Server Identification **ID:** `0x00` Response to a joining player. The user type indicates whether a player is an op (`0x64`) or not (`0x00`), which indicates if the player can delete bedrock. The protocol version is 0x07, unless you’re targeting a client that implements an earlier protocol, e.g. a version of Minecraft Classic prior to 0.28 |Field name|Field type| |-|-| |Packet ID|Byte| |Protocol version|Byte| |Server name|String| |Server MOTD|String| |User type|Byte| ### Ping **ID:** `0x01` Sent to clients periodically. The only way a client can disconnect in the base protocol is to force the connection closed, which does not usually let the server know immediately. The ping packet is used to determine if the connection is still open. |Field name|Field type| |-|-| |Packet ID|Byte| ### Level Initialize **ID:** `0x02` Notifies the player that level data is about to come down the pipe. |Field name|Field type| |-|-| |Packet ID|Byte| ### Level data chunk **ID:** `0x03` Contains a chunk of the gzipped level data, to be concatenated with the rest. Chunk data is 1024 bytes, padded at the end with `0x00`s if less than 1024 bytes is left. |Field name|Field type| |-|-| |Packet ID|Byte| |Chunk length|Short| |Chunk data|Level Data| |Percent complete|Byte| ### Level finalize **ID:** `0x04` Sent after the level data has been fully sent to the client. Provides map dimensions, where `Y` is up. |Field name|Field type| |-|-| |Packet ID|Byte| |X size|Short| |Y size|Short| |Z size|Short| ### Set block **ID:** `0x06` Sent to indicate a block change by either physics or another player. In the case of a player-initiated change, the server should also send this packet to the player that caused the event. |Field name|Field type| |-|-| |Packet ID|Byte| |X|Short| |Y|Short| |Z|Short| |Block type|Byte| ### Spawn player **ID:** `0x07` Sent to indicate where a new player is spawning in the world. This will set the player’s spawn point. |Field name|Field type| |-|-| |Packet ID|Byte| |Player ID|SByte| |Player name|String| |X|FShort| |Y|FShort| |Z|FShort| |Yaw|Byte| |Pitch|Byte| ### Set position and orientation/Player teleport **ID:** `0x08` Sent to change the position and rotation of players. Also used to send the initial position on the map, as well as teleportation. |Field name|Field type| |-|-| |Packet ID|Byte| |Player ID|SByte| |X|FShort| |Y|FShort| |Z|FShort| |Yaw|Byte| |Pitch|Byte| ### Relative position + orientation update **ID:** `0x09` **Optional.** Relative update of player position since last position update, as well as an orientation update. Not required for the server to operate. |Field name|Field type| |-|-| |Packet ID|Byte| |Player ID|SByte| |Change in X|FByte| |Change in Y|FByte| |Change in Z|FByte| |Yaw|Byte| |Pitch|Byte| ### Relative position update **ID:** `0x0A` **Optional.** Relative-position-only version of the position/orientation packet. Only changes the players position, relative to their prior position. Not required for the server to operate. |Field name|Field type| |-|-| |Packet ID|Byte| |Player ID|SByte| |Change in X|FByte| |Change in Y|FByte| |Change in Z|FByte| ### Orientation update **ID:** `0x0B` **Optional.** Orientation-only version of the position/orientation packet. Only changes the player’s orientation. Not required for the server to operate. |Field name|Field type| |-|-| |Packet ID|Byte| |Player ID|SByte| |Yaw|Byte| |Pitch|Byte| ### Despawn player **ID:** `0x0C` Sent to other players when a player disconnects to indicate that the player entity should be deleted. |Field name|Field type| |-|-| |Packet ID|Byte| |Player ID|SByte| ### Message **ID:** `0x0D` Messages from other players or the console. May contain color codes. |Field name|Field type| |-|-| |Packet ID|Byte| |Player ID (Unused in core protocol)|SByte| |Message|String| ### Disconnect player **ID:** `0x0E` Sent to a player to indicate that they should display a screen telling the player why they’ve been disconnected. **Note:** The server must assume the client won’t respect this packet, and must terminate the connection after sending it. |Field name|Field type| |-|-| |Packet ID|Byte| |Disconnect reason|String| ### Update user type **ID:** `0x0F` Sent when a player’s admin status has changed (whether or not they can delete bedrock). User type is `0x64` for op (can delete bedrock), or `0x00` for normal user (cannot delete bedrock). |Field name|Field type| |-|-| |Packet ID|Byte| |User type|Byte|