Ribbon is the name for the websocket networking system used by TETR.IO since version 4.2.0.
When this documentation is unclear, incomplete, or out of date, reference tetrio.js. Most of it is obfuscated and/or minified, but the Ribbon code is not, even including comments.
To collect data exchanged between an official TETR.IO client and a Ribbon, see Observing Ribbon.
Within the context of this documentation:
- A packet is a binary blob sent over the websocket connection.
- A message is a data object. Packets may contain one or more messages, and certain types of messages may contain nested messages within them.
This is NOT the terminology that TETR.IO uses in its message format and code—it uses these two terms interchangeably.
The generic Ribbon endpoint URI is wss://tetr.io/ribbon
. However, connecting to this endpoint directly is discouraged. Instead, clients should make an API call to https://tetr.io/api/server/ribbon
(with OAuth, see Chat) to obtain the recommended worker endpoint before attempting to connect:
- (object):
- (boolean)
success
: Whether the request succeeded. - (string)
endpoint
: The worker endpoint to use.
- (boolean)
Official TETR.IO clients will only connect to the generic /ribbon
endpoint if this request fails in some way.
one byte:
+------+
| |
+------+
a variable number of bytes:
+======+
| |
+======+
Packets contain a header followed by one or more messages in MessagePack format. MessagePack has numerous implementations covering every language under the sun. The MessagePack implementation used by both TETR.IO's client and server is msgpack-lite (npm). If you wish to reinvent the wheel or need to inspect binary data for debugging purposes, the binary specification can be found here.
All top-level MessagePack objects contained within a packet are maps (i.e. fixmap
, map 16
, or map 32
).
Packets will always begin with one of four bytes, signifying the header type. Both a Ribbon and a client may send any of these four packet types, but the client can probably get away with only sending 0x45
.
A packet beginning with 0x45
is a standard packet and everything after the first byte can be unpacked and processed.
+------+==============+
| 0x45 | msgpack blob |
+------+==============+
A packet beginning with 0xAE
is an extracted-id packet, containing a big-endian 32-bit unsigned integer message id to be injected into the unpacked object before processing.
+------+-----+-----+-----+-----+==============+
| 0xAE | extracted id (uint32) | msgpack blob |
+------+-----+-----+-----+-----+==============+
Note: The unpacked object might not contain an id
key, so don't rely on its presence.
I don't know why this packet type exists.
A packet beginning with 0x58
is a batch packet, containing a zero-terminated array of big-endian uint32
s. These values represent the length of the following packets.
+------+------+------+------+------+------+------+------+------+===================+
| 0x58 | N lengths (uint32)... | 0x00 0x00 0x00 0x00 | N packet blobs... |
+------+------+------+------+------+------+------+------+------+===================+
The blobs stored in this packet format are packets, not messages, meaning that they themselves contain one of these three headers (though probably always the first two).
This packet type was added in TETR.IO 5.1.3.
A packet beginning with 0xB0
is an extension packet, used to relay terse messages via the second byte. It's currently only used for pings and pongs.
+------+------+
| 0xB0 | type |
+------+------+
The current possible values for the second byte include:
0x0B
ping (client)0x0C
pong (server)
The 0x0B
ping and 0x0C
pong packets replace the ping
message and pong
message used prior to TETR.IO 6.0.3.
If this protocol is violated, the server will usually send either a nope
message or kick
message and end the connection.
As a given, to keep the connection alive, the client should periodically send a 0x0B
ping extension packet, which will be responded to with a 0x0C
pong extension packet. The official TETR.IO client sends a ping every 5000ms.
Upon connecting to a Ribbon, the client must send a new
message. The server will respond with a hello
message. The client must then send an authorize
message, which will be responded to with an authorize
message. At this point, the client is free to do whatever.
When the client is done, it should send the Ribbon a die
message to indicate a graceful closure. Poetic.
Most Ribbon messages have an integer id
property. For gameplay especially, it is crucial to process these messages in order, so both client and server messages may have this property, incrementing the value every time a message is sent. For a possible implementation, see tetrio.js. Note that the ids of client messages and the ids of server messages will fall out of alignment because not every message from one side will result in a response from the other.
If too many messages are out of order, it's advisable to close the connection. The official TETR.IO client will close the connection if more than 5200 packets are out of order.
Sometimes, the server will send Buffer messages. It's very important that any client handle these properly.
Note: There are several messages not documented because they are only relevant to non-bot users (e.g. Tetra League/Quick Play).