Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Watching a match

How a client is started to watch a match rather than play in it, and how the match reaches it.

A watching client takes the match over HTTP from a spectator service, not over a game connection.

What marks a watching client

Nothing on the wire does. A client watches because it was started to watch: it is given the word spectator where a player’s client is given its server’s address. Its key can be given as more than 16 characters of base64: the first 24 bytes it encodes, deciphered as Blowfish ECB under the match number written out, give the key as their first 16 bytes.

The word is followed by the spectator service’s address, with no port of its own, the key, a match number and a platform name; there is no player number. A key is deciphered under the low 32 bits of the match number, written out as a signed decimal.

The word replay starts a watching client as spectator does.

The order of the stream

  1. A client answered on registration sends StreamRequest.

  2. A server sends StreamMetadata, within 30 seconds: until it arrives the client waits.

  3. A server sends StreamChunkRange, with the match’s end left at zero while it runs.

  4. A server sends the chunks, each cut into StreamChunk fragments sent in order and not mixed with another chunk’s. It either sends every chunk from chunk 1 upward, or joins the client late:

    • the chunks of setting up, from 1 to the last of them (the chunk of kind 3), and no chunk after it;
    • then the key frame nearest the present, the first key frame sent on the connection;
    • then the chunks from the one that key frame leads to.

    Either way it skips none after that. A client takes a key frame only where it is the first one sent and arrives before the chunk after the last chunk of setting up; otherwise it keeps it but plays on from that chunk. A server SHOULD join a client late once a match is more than a few minutes in: a client sent every chunk from 1 fails partway through a long catch-up.

  5. As the match ends, a server sends StreamChunkRange again naming its last chunk, then that chunk. Once it has arrived the client takes no more of the stream.

The stream’s chunks run for StreamMetadata’s chunkTimeInterval each and its key frames for its keyFrameTimeInterval, both in milliseconds.

A watching client asks the spectator service for the match under /observer-mode/rest/consumer/, each request naming the platform and the match number: getGameMetaData for the match’s metadata, getLastChunkInfo for how far the match has come, and getGameDataChunk and getKeyFrame for a chunk or a key frame by number. It gives up on the metadata after a time, fetches the chunks of setting up, then catches up to the last chunk on offer and waits there for the next. Key frames arrive compressed. The metadata gives gameKey.gameId, gameEnded and chunkTimeInterval; the last chunk’s information gives chunkId, nextChunkId, availableSince, nextAvailableChunk, endStartupChunkId and endGameChunkId.

In 1.0.0.130–1.0.0.140

A client asks getLastKeyFrameInfo for the last key frame.

In 1.0.0.140–3.1.0.0

A watching client first asks the service for its version, and ends by asking it to stop. The metadata also gives keyFrameTimeInterval, a whole multiple of chunkTimeInterval, and clientAddedLag; the last chunk’s information also gives keyFrameId.

In 1.0.0.140

A client fetches the first key frame’s information after the chunks of setting up. The last chunk’s information also gives duration and startGameChunkId.

What a chunk holds

A chunk is the messages a server sent the players of the match, one record after another.

A client plays records as the stream’s clock reaches their time, records of time zero at once, and the first record with a time above zero sets its clock. It skips a record of command 0 on the events channel.

The chunks of setting up carry what a player’s client is sent from its version answer on: VersionSync first. A client holds the stream back while it loads after VersionSync, and again while it builds the world after SpawnStart, and goes on by itself afterwards.

A key frame is laid out as a chunk is, and holds the whole world as it stands at that point. A client joined late goes on after it from the chunk the key frame names, or, where it names none, from the chunk its number implies (see StreamChunk).

Messages

Each message of this system in this book, with each layout it has across the book’s patches and every note its definition carries. A layout is the message’s body, after the header its channel’s family puts in front (see Messages).

StreamRequest

What a client watching a match streamed over its connection (see Watching a match) sends each time the server answers its registration, asking for the stream from a point. The header carries zero.

Definition.

In 1.0.0.141–3.1.0.0

Carried on requests, sent by the client, reliable, under these commands:

BuildsCommand
1.0.0.141–1.0.0.142248
1.0.0.143247
1.0.0.144–1.0.0.149234
1.0.0.150–3.1.0.0231
FieldTypeWhat it is
wantsMetadatau8One: the client wants StreamMetadata.
jumpToLatestu8One where the client has no key frame yet and nothing past the end of the startup chunks: a client that can be joined late (see Watching a match).
fromChunku32The first chunk wanted: one past the highest the client holds, 1 at first.
fromKeyFrameu32The first key frame wanted: one past the latest the client holds, 1 at first.
Vectors (4)

one-on-1-0-0-142, one-on-1-0-0-143, one-on-1-0-0-149, one-on-1-0-0-154