# JS5 protocol

The JS5 remote protocol is used by the client to download groups of files from
the server as they are required, as well as prefetching certain groups in
advance. Groups contain the client's assets and scripts, and are stored in the
cache.

The client opens a connection to the primary game server port and sends the
`INIT_JS5REMOTE_CONNECTION` packet, which consists of a single byte opcode
(`15`) followed by the client's build number as an integer.

The server responds with a single `0` byte if the connection attempt is
successful. It may also respond with `CLIENT_OUT_OF_DATE` (`6`), `SERVER_FULL`
(`7`) or `IP_LIMIT` (`9`), after which the connection will be closed.

After a successful connection attempt, the connection remains in JS5 mode for
the rest of its life.

JS5 is a request/response protocol with support for pipelining - the 550 client
is capable of handling up to 20 prefetch and 20 urgent in-flight requests.

## Upstream

| Opcode | Description              |
|-------:|--------------------------|
|      0 | Request group (prefetch) |
|      1 | Request group (urgent)   |
|      2 | Logged in                |
|      3 | Logged out               |
|      4 | Rekey                    |
|      6 | Connected                |
|      7 | Disconnect               |

All upstream packets are exactly 4 bytes long, including the opcode. Unused
payload bytes are set to zero.

### Request group (prefetch/urgent)

| Data type     | Description |
|---------------|-------------|
| UnsignedByte  | Archive ID  |
| UnsignedShort | Group ID    |

Requests a group. Urgent requests have a higher priority than prefetch requests,
as the client needs the group immediately. Prefetch requests are used to
prepopulate most of the cache in the background.

### Rekey

| Data type     | Description          |
|---------------|----------------------|
| UnsignedByte  | Key                  |
| UnsignedShort | Unused (set to zero) |

Sent to set the encryption key.

### Logged in/out

Sent whenever the player logs in or out of the game. Consensus in the community
is that the logged in/out state is probably used for prioritisation, much like
the distinction between prefetch/urgent requests.

### Connected

| Data type      | Description                 |
|----------------|-----------------------------|
| UnsignedMedium | Unknown (always set to `3`) |

Sent immediately after the JS5 connection is established. Its purpose is not
known.

### Disconnect

Requests that the server closes the connection. Sent by the `::serverjs5drop`
command.

## Downstream

There is only a single type of response packet, and as such, there are no
response opcodes. The packet contains a group requested by the client.

| Data type      | Description                                               |
|----------------|-----------------------------------------------------------|
| UnsignedByte   | Archive ID                                                |
| UnsignedShort  | Group ID                                                  |
| UnsignedByte   | Compression type (ORed with `0x80` for prefetch requests) |
| Int            | Compressed length                                         |
| Int (optional) | Uncompressed length (present iff the group is compressed) |
| Byte[]         | Compressed data (with `0xFF` markers, see below)          |

If encryption is enabled, all downstream bytes are XORed with the encryption
key. The client enables encryption if the checksum of a group does not match the
expected checksum in the master index or index.

The encryption scheme is probably used to defend against middle boxes tampering
with the plaintext traffic.

After the first 512 bytes of a response (including the header), every subsequent
block of 511 bytes is prefixed with a single `0xFF` byte.
