# Room.launch & options

> Launch a room from Node with @disko-game/room, and every launch option.

A room is a Node program built on the `@disko-game/room` package. The
package does the heavy lifting: networking, the game engine and the physics
run inside it, in a fast native part that comes prebuilt. Your code just
starts a room and reacts to what happens in it.

It needs Node.js 22 or later and ES modules:

```sh
npm install @disko-game/room
```

## What launching does

`Room.launch(options)` does everything needed to open a room, in an order
chosen so that nothing half-ready is ever visible to players:

```mermaid alt="Room.launch checks the options, opens the network port, then loads the game. Only when the game has loaded does it register the room in the directory, and then the launch resolves with the Room."
flowchart TB
  check["Check the<br/>options"] --> listen["Open the<br/>network port"]
  listen --> load["Load the game"]
  load --> register["Register in<br/>the directory"]
  register --> ready["Resolves<br/>with the Room"]
```

The game loads before the room is listed, so players never find a room that
has no game. If any step fails, `launch` rejects with a `DiskoError` and
cleans up everything it had started.

```js title="launch.js"
import { Room } from "@disko-game/room";

// The library reads no environment variables: pass everything as options.
const { ROOM_ID, ROOM_TOKEN } = process.env;

if (!ROOM_ID || !ROOM_TOKEN) {
  throw new Error("Set ROOM_ID and ROOM_TOKEN from the dashboard");
}

const room = await Room.launch({
  // Identity and game
  id: ROOM_ID,
  token: ROOM_TOKEN,
  name: "Friday league",
  game: "gabriel/arena@1.4.2",
  players: { name: "display" },

  // Directory and admission
  description: "Weekly matches. Be kind.",
  public: true,
  guests: false,
  capacity: 16,

  // Network: defaults shown
  listen: "0.0.0.0:4433",
});

console.log(`Room ${room.id} is live at ${room.link}`);
```

The package never reads environment variables itself. Read your own
configuration and pass it as options. The room template's `main.js` does this
with `ROOM_ID`, `ROOM_TOKEN`, `GAME`, and the optional `LISTEN` and
`PUBLIC_URL`. It targets the production disko services; `DISKO_PROFILE=local`
targets a local stack, and `DISKO_API_URL`, `DISKO_REGISTRY_URL` and
`DISKO_PLAY_URL` override single services.

```js title="main.js"
import host from "./room.js";

// Production entry. The room id and hosting token come from the dashboard;
// GAME is a release such as "gabriel/arena@1.0.0".
const { ROOM_ID, ROOM_TOKEN, GAME, LISTEN, PUBLIC_URL } = process.env;

if (!ROOM_ID || !ROOM_TOKEN || !GAME) {
  console.error(
    "Set ROOM_ID and ROOM_TOKEN from the dashboard, and GAME to a release such as gabriel/arena@1.0.0",
  );
  process.exit(2);
}

await host({
  id: ROOM_ID,
  token: ROOM_TOKEN,
  game: GAME,
  ...(LISTEN ? { listen: LISTEN } : {}),
  ...(PUBLIC_URL ? { publicUrl: PUBLIC_URL } : {}),
});
```

The same host function runs in a [test room](/get-started/test-rooms) when you
copy it into a game project as `server.js`.

The options are grouped below. Each table lists the default and the rules a
value must follow.

## Identity and game

| Option    | Default               | Rules                                                                                  |
| --------- | --------------------- | -------------------------------------------------------------------------------------- |
| `id`      | required              | 32 lowercase hex characters, from the Creator Portal or a test room                    |
| `token`   | required              | the room's hosting token; never logged or exposed on the `Room`                        |
| `name`    | required              | 1–96 UTF-8 bytes, not blank, no control characters                                     |
| `game`    | required              | a release reference `"ns/slug@version"`, or `{ path }` for a local directory or `.tgz` |
| `players` | `{ name: "display" }` | the name policy: `"display"` or `"username"`                                           |

## Directory and admission

| Option        | Default | Rules                                                  |
| ------------- | ------- | ------------------------------------------------------ |
| `description` | `""`    | at most 1024 UTF-8 bytes                               |
| `public`      | `true`  | listed in the directory; test rooms are never listed   |
| `guests`      | `true`  | admit players without an account                       |
| `location`    | `null`  | `{ latitude, longitude }`                              |
| `password`    | `null`  | 1–4096 UTF-8 bytes; read it from your own secret store |
| `capacity`    | `64`    | 1–65535 admitted players                               |

## Network

| Option          | Default          | Rules                                                           |
| --------------- | ---------------- | --------------------------------------------------------------- |
| `listen`        | `"0.0.0.0:4433"` | UDP bind address; see [ports](/rooms/running-on-a-server#ports) |
| `publicUrl`     | derived          | the WebTransport URL players connect to                         |
| `transportPath` | `"/room"`        |                                                                 |
| `certificate`   | automatic        | `{ certificatePem, privateKeyPem }` as PEM strings              |

## Operations

| Option         | Default                                      | Rules                                                        |
| -------------- | -------------------------------------------- | ------------------------------------------------------------ |
| `endpoints`    | production hosts                             | `{ api?, registry?, play? }` base URLs                       |
| `cacheDir`     | OS cache dir + `/disko-game/releases`        | release cache, keyed by digest                               |
| `leaseSeconds` | `30`                                         | 5–300                                                        |
| `logging`      | `{ filter: "disko=info", format: "pretty" }` | engine logs go to stderr; `format` is `"pretty"` or `"json"` |
| `limits`       | server defaults                              | queue and rate overrides, such as `eventQueue` (4096)        |

Unknown option keys reject with `invalid_option`, so typos are never ignored.

## The Room object

The `Room` that `launch` resolves to is your handle on the running room.

Its properties always show the current state: `id`, `url`, `link` (the
`play.disko.ooo/?room=<id>` link you share), `state`, `game`, `players`, and
the directory settings `name`, `description`, `public`, `guests`,
`location` and `capacity`.

To change settings while the room runs, use `setName`, `setDescription`,
`setPublic`, `setGuests` and `setLocation`. Directory changes show up for
players within about 200 ms.

Every method returns a Promise that settles once the change has been applied,
and changes to one room apply in the order you make them. A setter checks its
value like the matching launch option and rejects with `invalid_option`.
After `close()`, every method rejects with `closed`.

The room also has `permissions`, `actions`, `options` and `bans`; see
[Permissions & moderation](/rooms/permissions-and-moderation). Every member is
in the [`@disko-game/room` reference](/reference/room).

## Errors

Rejections are `DiskoError` with a `code`: `invalid_option`,
`invalid_state`, `invalid_message`, `invalid_event`, `unauthorized`,
`game_not_found`, `game_removed`, `game_invalid`, `game_build_failed`,
`game_start_failed`, `registry_unavailable`, `listen_failed`,
`certificate_invalid` and `closed`. `game_invalid` and `game_build_failed`
carry `problems`, each linking to its [error page](/errors). The code is
stable; the message is not. See
[`DiskoErrorCode`](/reference/room#diskoerrorcode) for what each code means.
