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 client can watch a match that a server streams to it over the same connection a player uses, rather than playing in it.

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 address, the port, the key, a match number and a player number. The key works as a player’s does, with one difference: it can instead be given as base64, deciphered under the match number written out in decimal.

A watching client registers as a player’s client does (see Joining a match), and is answered the same way, save for the client id. A server MUST answer a watching client with a client id that no player in the streamed match holds: a watching client given a player’s client id takes that player’s champion as its own, and fails as soon as it has caught up with the stream. From then on it takes only StreamMetadata, StreamChunkRange and StreamChunk, on any channel, and drops every other message a server sends it directly, VersionSync included. Of what a player’s client sends while joining, it sends only JoinSide, once the stream’s own VersionSync has played, then GameOptions, CharacterSelected and ClientReady: no ClientVersion, no QueryStatus and no LoadingProgress.

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.

What a chunk holds

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

In 4.17

Each record is a StreamRecord, written against the one before it to save space. A reader keeps the last record’s time, command and object, and recovers a record’s own:

  • its time, from a time difference, as the last record’s time plus that many milliseconds;
  • its command, where the record leaves it out, as the last record’s;
  • its object, from an object difference, as the last record’s object plus that signed difference.

A message is the command, the object and the body, laid out as a game message is (see Messages). A record never holds a container.

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 client whose own build differs from the one VersionSync names leaves.

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

StreamMetadata

What a server streaming a match to a watching client sends first, within 30 seconds of the client’s request: how long its chunks and key frames run. A client waits on it, and stops taking the stream’s messages once the last chunk has arrived. The header carries zero.

Definition.

In every range of this book

Carried on events, sent by the server, reliable, under these commands:

BuildsCommand
4.1–4.10234
4.11–4.20235
FieldTypeWhat it is
unusedu32Unused; a server writes zero.
texttext, sized by remainingA JSON object with integer members chunkTimeInterval and keyFrameTimeInterval, in milliseconds, to the end of the message. A server sends both.
Vectors (3)

one-on-4-10, one-on-4-17, one-on-4-20

StreamChunkRange

What a server streaming a match sends to say where the match proper begins in its chunks, and, once known, where it ends. The header carries zero.

Definition.

In every range of this book

Carried on events, sent by the server, reliable, under these commands:

BuildsCommand
4.1–4.10235
4.11–4.20236
FieldTypeWhat it is
startGameChunku32The chunk the match proper begins with.
endGameChunku32The match’s last chunk, or zero while it runs.
Vectors (4)

another-on-4-10, one-on-4-10, one-on-4-17, one-on-4-20

StreamChunk

What a server streaming a match sends to carry one fragment of a chunk or key frame of recorded messages (see Watching a match). A chunk’s facts come from its fragment 0. The fragments of a chunk come in order from 0, and the chunk is complete at index fragments − 1. A new fragment 0 drops an unfinished chunk, and a fragment with no chunk begun has no effect. The header carries zero.

Definition.

In 4.1–4.13

Carried on events, sent by the server, reliable, under these commands:

BuildsCommand
4.1–4.10236
4.11–4.13237
FieldTypeWhat it is
chunku32The chunk’s id, or the key frame’s. Chunk 0 is dropped; chunks play in order from the one a client starts at, and a missing one after it stalls the stream. Key frames count separately, from 1.
fragmentsu32How many fragments the chunk is cut into.
indexu32This fragment’s place, from 0.
kindu8What the chunk is. Values: 1 ordinary, 2 keyFrame, 3 endOfStartup, 4 gameStart, 5 startup.
sizeu32The whole chunk’s size in bytes.
durationi32Seconds the chunk covers. A client reads it only from fragment 0, and uses it only for the match’s last chunk: the match ends that many seconds after that chunk starts. A value too large holds a client that joins near the end on its loading screen.
nextChunku32For a key frame, the ordinary chunk to go on from. Where zero or less, a client takes key frame interval over chunk interval, times the key frame’s id less one, plus the start-game chunk: the one StreamChunkRange names, or the last chunk of kind 4 to arrive.
databytes, sized by remainingThis fragment’s bytes of the chunk, to the end of the message.
Vectors (2)

startup-on-4-1, startup-on-4-11

In 4.14–4.17

Command 237 on events, sent by the server, reliable.

FieldTypeWhat it is
chunku32The chunk’s id, or the key frame’s. Chunk 0 is dropped; chunks play in order from the one a client starts at, and a missing one after it stalls the stream. Key frames count separately, from 1.
fragmentsu32How many fragments the chunk is cut into.
indexu32This fragment’s place, from 0.
kindu8What the chunk is. Values: 1 ordinary, 2 keyFrame, 3 endOfStartup, 4 gameStart, 5 startup.
sizeu32The whole chunk’s size in bytes.
durationu32Seconds the chunk covers. A client reads it only from fragment 0, and uses it only for the match’s last chunk: the match ends that many seconds after that chunk starts. A value too large holds a client that joins near the end on its loading screen.
nextChunku32For a key frame, the ordinary chunk to go on from. Where zero or less, a client takes key frame interval over chunk interval, times the key frame’s id less one, plus the start-game chunk: the one StreamChunkRange names, or the last chunk of kind 4 to arrive.
databytes, sized by remainingThis fragment’s bytes of the chunk, to the end of the message.
Vectors (1)

one-on-4-17

In 4.18–4.20

Command 237 on events, sent by the server, reliable.

FieldTypeWhat it is
chunku32The chunk’s id, or the key frame’s. Chunk 0 is dropped; chunks play in order from the one a client starts at, and a missing one after it stalls the stream. Key frames count separately, from 1.
fragmentsu32How many fragments the chunk is cut into.
indexu32This fragment’s place, from 0.
kindu8What the chunk is. Values: 1 ordinary, 2 keyFrame, 3 endOfStartup, 4 gameStart, 5 startup.
sizeu32The whole chunk’s size in bytes.
durationi32Seconds the chunk covers. A client reads it only from fragment 0, and uses it only for the match’s last chunk: the match ends that many seconds after that chunk starts. A value too large holds a client that joins near the end on its loading screen.
nextChunku32For a key frame, the ordinary chunk to go on from. Where zero or less, a client takes key frame interval over chunk interval, times the key frame’s id less one, plus the start-game chunk: the one StreamChunkRange names, or the last chunk of kind 4 to arrive.
databytes, sized by remainingThis fragment’s bytes of the chunk, to the end of the message.
Vectors (1)

startup-on-4-18

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 every range of this book

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

BuildsCommand
4.1–4.10233
4.11–4.20234
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 (3)

one-on-4-10, one-on-4-17, one-on-4-20