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. |