Skip to content

Game Protocol

The game protocol starts on the existing connection after a successful LOGIN_OK response. This document only describes the common packets and not the game-specific ones.

Packet framing and ISAAC

A game packet has this wire shape:

┌────────────────────────────┬────────────────────────────────┬─────────┐
│ ISAAC-encoded packet ID    │ length prefix, when required   │ payload │
└────────────────────────────┴────────────────────────────────┴─────────┘

Packet IDs are one byte. The packet ID is ISAAC-ciphered; the length prefix and payload are not. The client-to-server and server-to-client directions use separate ISAAC streams:

Direction Packet ID on the wire
Client → server (packet ID + next ISAAC value) & 0xFF
Server → client (packet ID + next ISAAC value) & 0xFF

For the server's inbound stream, the ISAAC seed is the four 32-bit values from the login payload. For its outbound stream, 50 is added to each of those four seed values before initializing the cipher. To recover an incoming packet ID, subtract the next inbound ISAAC value from the wire byte and keep the low 8 bits. Each packet consumes one value from the corresponding directional stream.

The ID is followed by a length prefix only for variable-size packets. Lengths are unsigned and big-endian; they count payload bytes, including any checksum trailer, but exclude the ID and the length prefix.

Packet size form Client → server Server → client
Fixed No prefix; size is defined by the packet No prefix; size is defined by the packet
Variable byte 1-byte payload length 1-byte payload length
Variable short 2-byte payload length 2-byte payload length
Variable any Not used for inbound game packets 1-byte length below 255 bytes; otherwise 2-byte length

Shared packet set

The shared set applies to the game services. Game-specific extensions are listed in the following sections. IDs are shown as decoded packet IDs, before ISAAC is applied.

Client → server packets

Packet ID Payload size Purpose / payload notes
HEARTBEAT 0 0 Keep-alive; answered with server HEARTBEAT.
DISCONNECTING 1 0 Client disconnect notification.
XTEA_KEYS 2 3 Key request; the three payload bytes have no documented field mapping.
HISCORES 3 Variable byte Hiscore list or score update; see Hiscores opcodes.
ACHIEVEMENTS 4 Variable byte Achievement list or update; see Achievements opcodes.
PROGRESSION 5 Variable byte Progression fetch or update; see Progression opcodes.
PROFILE 6 Variable byte Profile fetch or update; see Profile opcodes.
RATINGS 7 2 Game mode (1 byte), page size (1 byte).
LOBBY_JOIN 9 0 Join-lobby request.
LOBBY_LEAVE 10 0 Leave-lobby request.
LOBBY 11 Variable byte Lobby service request; see Lobby Protocol.
CHAT_MESSAGE 12 Variable byte Chat message; see Chat and quick-chat messages.
SOCIAL 13 Variable byte Social action; see Social opcodes.
QUICK_CHAT_MESSAGE 15 Variable byte Quick-chat message; see Chat and quick-chat messages.
UNKNOWN_16 16 Variable byte Payload meaning is not identified.

Server → client packets

Packet ID Payload size Payload description
HEARTBEAT 0 0 No payload.
XTEA_KEY_REPLY 1 16 bytes Four 32-bit zero values.
HISCORES_REPLY 2 Variable short Reply opcode followed by a list or acknowledgement; see Hiscores reply payloads.
ACHIEVEMENTS_REPLY 3 Variable byte Reply opcode followed by a list, acknowledgement, or reset; see Achievements reply payloads.
PROGRESSION_REPLY 4 Variable byte Reply opcode followed by progression data or an acknowledgement; see Progression reply payloads.
PROFILE_REPLY 5 Variable byte Reply opcode followed by profile data or a CRC acknowledgement; see Profile reply payloads.
RATINGS_REPLY 6 Variable short Game mode, entry count, and a bit-packed ratings record; see Ratings reply payload.
LOBBY_JOIN_ERROR 9 Variable byte Zero-terminated JStr error message.
LOBBY_REPLY 10 Variable byte Lobby sub-operation and its payload; see Lobby Protocol.
CHAT_MESSAGE 11 Variable byte Server chat-message fields and Huffman-encoded text; see Chat and quick-chat messages.
QUICK_CHAT_MESSAGE 12 Variable byte Server chat-message fields and a quick-chat ID; see Chat and quick-chat messages.
SOCIAL_REPLY 13 Variable byte Reply opcode followed by social-state data; see Social reply payloads.
LOBBY_JOIN_SUCCESS 14 0 No payload.
LOBBY_LEAVE 15 0 No payload.

The LOBBY_REPLY sub-operations are described in Lobby Protocol. Where a reply uses sub-opcodes, the opcode is the first payload byte and its values are specific to that packet type. Multibyte values are big-endian unless otherwise noted.

Hiscores reply payloads

Hiscores reply opcode 0: List payload

Order Field Encoding / description
1 Reply opcode 0 (1 byte).
2 Game mode 2 bytes.
3 Display-name count 1 byte; includes the implicit self entry at index 0.
4 Other display names For each non-self name: a JStr, then a 1-byte previous-name flag; a previous-name JStr follows when the flag is set.
5 Entry count 1 byte.
6 Entries For each entry: display-name index (1 byte), account ID (8 bytes), then the configured number of 4-byte scores.

Hiscores reply opcode 1: Acknowledgement payload

Order Field Encoding
1 Reply opcode 1 (1 byte).
2 Sequence 2 bytes.
3 Entry ID 8 bytes.

Achievements reply payloads

Achievements reply opcode 0: List payload

Order Field Encoding
1 Reply opcode 0 (1 byte).
2 Achievement count 1 byte.
3 Packed achievements That many 4-byte values; the packed bit meanings are unspecified.

Achievements reply opcode 1: Acknowledgement payload

Only the reply opcode (1, 1 byte) is present.

Achievements reply opcode 2: Reset payload

Only the reply opcode (2, 1 byte) is present.

Progression reply payloads

Progression reply opcode 0: Send payload

Order Field Encoding
1 Reply opcode 0 (1 byte).
2 Game mode 1 byte.
3 Progression value 1 byte.

Progression reply opcode 1: Acknowledgement payload

Order Field Encoding / description
1 Reply opcode 1 (1 byte).
2 Game mode 1 byte.
3 Progression value Protocol smart encoding; see Progression opcode 1.

Profile reply payloads

Profile reply opcode 0: Send payload

Order Field Encoding
1 Reply opcode 0 (1 byte).
2 Profile-data length 1 byte.
3 Profile data The number of bytes given by the preceding length.
4 CRC-32 trailer 4-byte big-endian standard CRC-32 of the preceding payload fields.

Profile reply opcode 1: Acknowledgement payload

Order Field Encoding
1 Reply opcode 1 (1 byte).
2 CRC-32 value 4 bytes.

Ratings reply payload

Order Field Encoding / description
1 Game mode 1 byte.
2 Entry count 1 byte; the reply contains one entry.
3 Ratings record 10 bytes bit-packed as wins (21 bits), draws (21 bits), losses (21 bits), and rating (17 bits).

Social reply payloads

Social reply opcode 0: Update-ignored payload

Order Field Encoding / description
1 Reply opcode 0 (1 byte).
2 Previous-name flag 1 byte; nonzero when a previous name is present.
3 Display name Zero-terminated JStr.
4 Normalized display name Zero-terminated JStr.
5 Previous display name Zero-terminated JStr; empty when absent.

Social reply opcode 1: Update-friend payload

Order Field Encoding
1 Reply opcode 1 (1 byte).
2 Game name Zero-terminated JStr.
3 Display name Zero-terminated JStr.
4 Previous display name Zero-terminated JStr; empty when absent.

Social reply opcode 2: Ready payload

Only the reply opcode (2, 1 byte) is present.

Social reply opcode 3: Disconnected payload

Only the reply opcode (3, 1 byte) is present.

Social reply opcode 4: Initialization payload

Order Field Encoding
1 Reply opcode 4 (1 byte).
2 Game name Zero-terminated JStr.
3 Privacy level 1 byte.

Chat and quick-chat messages

Client chat requests use packet ID 12 for CHAT_MESSAGE and 15 for QUICK_CHAT_MESSAGE. Server chat messages use ID 11 for CHAT_MESSAGE and 12 for QUICK_CHAT_MESSAGE. All four packets have variable-byte payloads.

Chat-type IDs are:

ID Chat type
0 Lobby
1 Game room
2 Private
3 Game
4 Unknown

Client → server payloads

Packet Fields in payload order
CHAT_MESSAGE Chat type (1 byte); recipient (JStr) if the type is private; Huffman-encoded message text.
QUICK_CHAT_MESSAGE Chat type (1 byte); recipient (JStr) if the type is private; quick-chat message ID (2 bytes; the low 15 bits identify the message).

The Huffman-encoded text is preceded by the uncompressed text length: lengths below 128 use one byte; larger lengths use a two-byte value with 0x8000 added. The compressed data follows that length.

Server → client payloads

Both server packet types share this field order, with the final field depending on the packet type:

Order Field Encoding / condition
1 Chat type 1 byte; bit 7 marks a system message, and the lower 7 bits contain the chat-type ID.
2 Staff level 1 byte.
3 Timestamp 8-byte integer.
4 Private-message metadata Private messages only: private-from flag (1 byte), then private-message ID (3 bytes).
5 Reply metadata Reply-to-player flag (1 byte), then the sender name (JStr); a reply-to name (JStr) follows when the flag is set.
6 Room metadata Game-room and unknown chat types only: room key (2 bytes), then room owner (JStr).
7 Message content CHAT_MESSAGE: Huffman-encoded text; QUICK_CHAT_MESSAGE: quick-chat message ID (2 bytes).

Hiscores opcodes

Hiscores opcode 0: List payload

Order Field Encoding
1 Game mode 2 bytes.
2 Page size 1 byte.
3 Scores per entry 1 byte.

Hiscores opcode 1: Update payload

Order Field Encoding
1 Sequence 2 bytes.
2 Game mode 2 bytes.
3 Additional mode value 2 bytes.
4 Opaque data 16 bytes; field meanings are unspecified.
5 Entry count 1 byte.
6 Scores That many 4-byte values; at most 10 score values are accepted.
7 CRC-32 trailer 4-byte big-endian standard CRC-32 of the preceding payload bytes.

Achievements opcodes

Achievements opcode 2: List payload

No bytes follow the opcode.

Achievements opcode 1: Update payload

Order Field Encoding
1 Achievement ID 1 byte.
2 Additional ID value 1 byte.
3 Opaque data 12 bytes; field meanings are unspecified.
4 CRC-32 trailer 4-byte big-endian standard CRC-32 of the preceding payload bytes.

Progression opcodes

Progression opcode 0: Fetch payload

Order Field Encoding
1 Game mode 1 byte.

Progression opcode 1: Update payload

Order Field Encoding / description
1 Game mode 1 byte.
2 Progression value Protocol smart encoding; see below.
3 Opaque data 12 bytes; field meanings are unspecified.
4 CRC-32 trailer 4-byte big-endian standard CRC-32 of the preceding payload bytes.

The smart progression value uses one byte for values from -64 through 63, stored as the value plus 64. Otherwise, it uses two bytes for values from -16384 through 16383, stored as the value plus 49152.

Profile opcodes

Profile opcode 0: Fetch payload

No bytes follow the opcode.

Profile opcode 1: Update payload

Order Field Encoding
1 Profile-data length 1 byte.
2 Profile data The number of bytes given by the preceding length.
3 CRC-32 value 4-byte big-endian standard CRC-32 of the preceding payload bytes.

Social opcodes

Social opcode 0: Add-friend payload

Order Field Encoding
1 Display name Zero-terminated JStr.

Social opcode 1: Remove-friend payload

Order Field Encoding
1 Display name Zero-terminated JStr.

Social opcode 2: Add-ignored payload

Order Field Encoding
1 Display name Zero-terminated JStr.

Social opcode 3: Remove-ignored payload

Order Field Encoding
1 Display name Zero-terminated JStr.

Social opcode 4: Privacy-settings payload

Order Field Encoding
1 Privacy levels One packed byte: lobby level in bits 4–5, game level in bits 2–3, and private-message level in bits 0–1.