Server API Room API

Room API

The Room class is meant to implement a game session, and/or serve as the communication channel between a group of clients.

  • Rooms are created on demand during matchmaking by default
  • Room classes must be exposed using .define()
MyRoom.ts
import http from "http";
import { Room, Client } from "colyseus";
 
export class MyRoom extends Room {
    // (optional) Validate client auth token before joining/creating the room
    static async onAuth (token: string, request: http.IncomingMessage) { }
 
    // When room is initialized
    onCreate (options: any) { }
 
    // When client successfully join the room
    onJoin (client: Client, options: any, auth: any) { }
 
    // When a client leaves the room
    onLeave (client: Client, consented: boolean) { }
 
    // Cleanup callback, called after there are no more clients in the room. (see `autoDispose`)
    onDispose () { }
}

Room Lifecycle Events

  • The room lifecycle events are called automatically.
  • Optional async/await is supported on every lifecycle event.

On Create

Called once, when the room is created by the matchmaker. options is the merged values specified on Server#define() with the options provided from the SDK at .joinOrCreate() or .join().

MyRoom.ts
onCreate (options) {
    /**
     * This is a good place to initialize your room state.
     */
}

The server may overwrite options during .define() for authortity over client-provided options:

app.config.ts
// (server-side)
// ...
gameServer.define("my_room", MyRoom, {
    map: "cs_assault"
})

On this example, the map option is "cs_assault" during onCreate(), and "de_dust2" during onJoin().


On Auth

The onAuth method is called before onJoin, and it is responsible for validating the client’s request to join a room.

MyRoom.ts
async onAuth (client, options, context) {
    /**
     * This is a good place to validate the client's auth token.
     */
}

Arguments

  • client: A reference to the client that is trying to join the room.
  • options: Options provided by the client-side SDK.
  • context: The request context, containing:
    • .token - the authentication token sent by the client.
    • .headers - the headers sent by the client.
    • .ip - the IP address of the client.

See Authentication for more details.


On Join

Triggered when a client successfully joins the room, after a successful onAuth.

Parameters:

  • client: A reference to the client that joined the room.
  • options: Options provided by the Client SDK. (Merged with default values provided by Server#define())
  • auth: (optional) auth data returned by onAuth method.
MyRoom.ts
async onJoin (client, options, auth?) {
    /**
     * This is a good place to add the client to the state.
     */
}

See Client SDK on .joinOrCreate() and .join().


On Leave

Triggered when a client leaves the room.

If the client leaves the room by closing the browser tab, the onLeave method is called with consented set to false. If the client leaves the room by calling .leave(), the onLeave method is called with consented set to true.

MyRoom.s
async onLeave (client, consented) {
    /**
     * This is a good place to remove the client from the state.
     */
}

You may define this function as async:

onLeave(client, consented) {
    if (this.state.players.has(client.sessionId)) {
        this.state.players.delete(client.sessionId);
    }
}

At Graceful Shutdown, onLeave is called with consented set to true for all clients.


On Dispose

The onDispose() method is called before the room is destroyed, which happens when:

  • there are no more clients left in the room, and autoDispose is set to true (default)
  • you manually call .disconnect().
MyRoom.ts
async onDispose() {
    /**
     * This is a good place to perform cleanup tasks.
     */
}

You may define async onDispose() an asynchronous method in order to persist some data in the database. In fact, this is a great place to persist player’s data in the database after a game match ends.

At Graceful Shutdown, onDispose is called for all rooms.


On Unhandled Exception

Opt-in to catch unhandled exceptions in your room. This method is called when an unhandled exception occurs in any of the lifecycle methods.

MyRoom.ts
onUncaughtException (err: Error, methodName: string) {
    console.error("An error ocurred in", methodName, ":", err);
    err.cause // original unhandled error
    err.message // original error message
}

See Exception Handling for more details.


On Before Patch

The onBeforePatch lifecycle hook is triggered before state synchronization, at patch rate frequency. (see setPatchRate())

MyRoom.ts
onBeforePatch() {
    /*
     * Here you can mutate something in the state just before it is encoded &
     * synchronized with all clients
     */
}

On Cache Room (devMode)

An optional hook to cache external data when devMode is enabled. (See restoring data outside the room’s state)

MyRoom.ts
export class MyRoom extends Room<MyRoomState> {
  // ...
 
  onCacheRoom() {
    return { foo: "bar" };
  }
}

On Restore Room (devMode)

An optional hook to reprocess/restore data which was returned and stored from the previous hook onCacheRoom when Development Mode is enabled.

MyRoom.ts
export class MyRoom extends Room<MyRoomState> {
  // ...
 
  onRestoreRoom(cachedData: any): void {
    console.log("ROOM HAS BEEN RESTORED!", cachedData);
 
    this.state.players.forEach(player => {
      player.method(cachedData["foo"]);
    });
  }
}

On Before Shutdown

The onBeforeShutdown lifecycle hook is called as part of the Graceful Shutdown process. The process will only truly shutdown after all rooms have been disposed.

By default, the room will disconnect all clients and dispose itself immediately.

You may customize how the room should behave during the shutdown process:

MyRoom.ts
onBeforeShutdown() {
    //
    // Notify users that process is shutting down, they may need to save their progress and join a new room
    //
    this.broadcast("going-down", "Server is shutting down. Please save your progress and join a new room.");
 
    //
    // Disconnect all clients after 5 minutes
    //
    this.clock.setTimeout(() => this.disconnect(), 5 * 60 * 1000);
}

See graceful shutdown for more details.


Public methods

Room handlers have these methods available.

On Message

Register a callback to process a type of message sent by the client-side.

Signature
this.onMessage (type, callback, validation)
  • The type argument can be either string or number.
  • You can only define a single callback per message type. (Defining a callback more than once will result in overriding the previous one)

Callback for specific type of message

MyRoom.ts
onCreate () {
    this.onMessage("action", (client, payload) => {
        console.log(client.sessionId, "sent 'action' message: ", payload);
    });
}

Use room.send(type, payload) from the client SDK to send messages to the server.

Fallback for all messages

You can register a single callback as a fallback to handle other types of messages.

MyRoom.ts
onCreate () {
    this.onMessage("action", (client, payload) => {
        //
        // Triggers when 'action' message is sent.
        //
    });
 
    this.onMessage("*", (client, type, payload) => {
        //
        // Triggers when any other type of message is sent,
        // excluding "action", which has its own specific handler defined above.
        //
        console.log(client.sessionId, "sent", type, payload);
    });
}

Message input validation

You may provide a validation function as the third argument to onMessage. This function will be called before the message is processed, and if it throws an error, the message will be ignored.

The data returned by the validation function will be passed as payload on the message handler.

MyRoom.ts
onCreate () {
    this.onMessage("action", (client, payload) => {
        //
        // payload.x and payload.y are guaranteed to be numbers here.
        //
        console.log({ x: payload.x, y: payload.y });
 
    }, (payload) => {
        if (typeof(payload.x) !== "number" || typeof(payload.y) !== "number") {
            throw new Error("Invalid payload");
        }
        return payload;
    });
}

Set State

Set the synchronizable room state. See State Synchronization and Schema for more details.

import { Room } from "colyseus";
import { MyState } from "./MyState";
 
export class MyRoom extends Room {
    state = new MyState();
}
⚠️

The room’s state is mutable. You should not reassign the state object, but rather mutate it directly when updating the state.

🚫

The .setState() method is deprecated. Use this.state directly instead.


Set Simulation Interval

Optional: Set a simulation interval that can change the state of the game. The simulation interval is your game loop. Default simulation interval: 16.6ms (60fps)

Signature
this.setSimulationInterval (callback[, milliseconds=16.6])
MyRoom.ts
onCreate () {
    this.setSimulationInterval((deltaTime) => this.update(deltaTime));
}
 
update (deltaTime) {
    // implement your physics or world updates here!
    // this is a good place to update the room state
}

Set Patch Rate

Set frequency the patched state should be sent to all clients. Default is 50ms (20fps)

Signature
this.setPatchRate (milliseconds)

Set Private

Set the room listing as private - or revert it to public, if false is provided.

Signature
this.setPrivate (bool)

Set Metadata

Set metadata to this room. Each room instance may have metadata attached to it - the only purpose for attaching metadata is to differentiate one room from another when getting the list of available rooms via Lobby Room or Match-maker API.

Signature
this.setMetadata (metadata)
MyRoom.ts
onCreate(options) {
    this.setMetadata({ friendlyFire: options.friendlyFire });
}

Set Seat Reservation Time

Set the number of seconds a room can wait for a client to effectively join the room. You should consider how long your onAuth() will have to wait for setting a different seat reservation time. The default value is 15 seconds.

Signature
this.setSeatReservationTime (seconds)

You may set the COLYSEUS_SEAT_RESERVATION_TIME environment variable if you’d like to change the seat reservation time globally.


Broadcast Message

Send a message to all connected clients.

Signature
this.broadcast (type, payload, options?)

Available options are:

  • except: a Client, or array of Client instances not to send the message to
  • afterNextPatch: waits until next patch to broadcast the message

Broadcasting a message to all clients:

MyRoom.ts
onCreate() {
    this.onMessage("action", (client, payload) => {
        // broadcast a message to all clients
        this.broadcast("action-taken", "an action has been taken!");
    });
}

The client will receive the message in the onMessage() callback.


Lock Room

Locking the room will remove it from the pool of available rooms for new clients to connect to.

Signature
this.lock ()

Unlock Room

Unlocking the room returns it to the pool of available rooms for new clients to connect to.

Signature
this.unlock ()

Allow Reconnection

Allow the specified client to reconnect into the room. Must be used inside onLeave() method.

Signature
this.allowReconnection (client, seconds)
  • client: the disconnecting Client instance
  • seconds: number of seconds to wait for client to perform .reconnect(), or "manual", to allow for manual reconnection rejection (see second example)

Return type:

  • allowReconnection() returns a Deferred<Client> instance.
  • The returned Deferred instance is a promise-like structure, you can forcibly reject the reconnection by calling .reject() on it. (see second example)
  • Deferred type can forcibly reject the promise by calling .reject() (see second example)

Example: Rejecting the reconnection after a 20 second timeout.

MyRoom.ts
async onLeave (client: Client, consented: boolean) {
    // flag client as inactive for other users
    this.state.players.get(client.sessionId).connected = false;
 
    try {
        if (consented) {
            throw new Error("consented leave");
        }
 
        // allow disconnected client to reconnect into this room until 20 seconds
        await this.allowReconnection(client, 20);
 
        // client returned! let's re-activate it.
        this.state.players.get(client.sessionId).connected = true;
 
    } catch (e) {
 
        // 20 seconds expired. let's remove the client.
        this.state.players.delete(client.sessionId);
    }
}

Disconnect

Disconnect all clients, then dispose.

Signature
this.disconnect ()

Broadcast Patch

⚠️

You may not need this! - This method is called automatically by the framework.

This method will check whether mutations have occurred in the state, and broadcast them to all connected clients.

Signature
this.broadcastPatch ()

If you’d like to have control over when to broadcast patches, you can do this by disabling the default patch interval:

MyRoom.ts
onCreate() {
    // disable automatic patches
    this.setPatchRate(null);
 
    // ensure clock timers are enabled
    this.setSimulationInterval(() => {/* */});
 
    this.clock.setInterval(() => {
        // only broadcast patches if your custom conditions are met.
        if (yourCondition) {
            this.broadcastPatch();
        }
    }, 2000);
}

Public properties

roomId

A unique, auto-generated, 9-character-long identifier of the room.

You may replace this.roomId during onCreate().

Using a custom roomId

Check out the guide How-to » Customize room id


roomName

The name of the room you provided as first argument for gameServer.define().


state

The synchronized state of the room.


clients

The array of connected clients. See Client instance.

Sending a message to a specific clients.

MyRoom.ts
// ...
this.clients.forEach((client) => {
    if (client.userData.team === "red") {
        client.send("hello", "world");
    }
});
// ...

Getting a client by sessionId.

MyRoom.ts
// ...
const client = this.clients.getById("UEsBFUBhK");
// ...

maxClients

Maximum number of clients allowed to connect into the room. When room reaches this limit, it is locked automatically. Unless the room was explicitly locked by you via lock() method, the room will be unlocked as soon as a client disconnects from it.


patchRate

Frequency to send the room state to connected clients, in milliseconds. Default is 50ms (20fps)


autoDispose

Automatically dispose the room when last client disconnects. Default is true


locked (read-only)

This property will change on these situations:

  • The maximum number of allowed clients has been reached (maxClients)
  • You manually locked, or unlocked the room using lock() or unlock().

clock

It is recommended to use the clock instance for setTimeout and setInterval methods, as timers and intervals are automatically cleared when the room is disposed - preventing memory leaks.

MyRoom.ts
// ...
onCreate() {
    this.clock.setTimeout(() => {
        console.log("This message will be printed after 5 seconds");
    }, 5000);
 
    this.clock.setInterval(() => {
        console.log("Current time:", this.clock.currentTime);
    }, 1000);
}
// ...

presence

The presence is used as a shared in-memory database for your cluster, and for pub/sub operations between rooms.

You may access the global presence instance from your Room code.

MyRoom.ts
// ...
onCreate() {
    // publish an event to all rooms listening to "event-from-another-room"
    this.presence.publish("event-name-from-another-room", { hello: "world" });
 
    // subscribe to events from another room
    this.presence.subscribe("event-name-from-another-room", (payload) => {
        console.log("Received event from another room!", payload);
    });
 
    // set arbitrary value to the presence
    this.presence.set("arbitrary-key", "value");
}
// ...

Client

The client instance from the server-side is responsible for the transport layer between the server and the client. It should not be confused with the Client from the client-side SDK, as they have completely different purposes.

You operate on client instances from this.clients, Room#onJoin(), Room#onLeave() and Room#onMessage().

Properties

sessionId

Unique identifier of the client connection.

MyRoom.ts
// ...
onJoin(client, options) {
    console.log(client.sessionId);
}
// ...

In the client-side, you can find the sessionId in the room instance.


userData

The client.userData can be used to store player-specific data easily accessible via the client instance. This property is meant for convenience.

MyRoom.ts
// ...
onJoin(client, options) {
  client.userData = { team: (this.clients.length % 2 === 0) ? "red" : "blue" };
}
onLeave(client)  {
  console.log(client.userData.playerNumber);
}
// ...

auth

The client.auth property holds the data returned by the onAuth() method.

MyRoom.ts
onAuth(token, request) {
    return { userId: "123" };
}
 
onJoin(client, options) {
    console.log(client.auth.userId);
}

See Authentication for more details.


Methods

Send Message

Send a type of message to the client. Messages are encoded with MsgPack and can hold any JSON-seriazeable data structure.

Signature
client.send(type, payload)

The type can be either a string or a number.

MyRoom.ts
//
// sending message of string type ("powerup")
//
client.send("powerup", { kind: "ammo" });
 
//
// sending message of number type (1)
//
client.send(1, { kind: "ammo"});

Send Message (in bytes)

Send a raw byte array message to the client.

Signature
client.sendBytes(type, bytes)

The type can be either a string or a number.

This is useful if you’d like to manually encode a message, rather than the default encoding (MsgPack).

MyRoom.ts
//
// sending message of string type ("powerup")
//
client.sendBytes("powerup", [ 172, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33 ]);
 
//
// sending message of number type (1)
//
client.sendBytes(1, [ 172, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33 ]);

Send Error

Send an error with code and message to the client. The client can handle it on onError

Signature
client.error(code, payload)

Leave Room

Force disconnection of the client with the room. You may send a custom code when closing the connection, with values betweeen 4000 and 4999 (see table of WebSocket close codes)

Signature
client.leave(code?: number)

This will trigger room.onLeave event on the client-side.

Table of WebSocket close codes

Close code (uint16)CodenameInternalCustomizableDescription
0 - 999YesNoUnused
1000CLOSE_NORMALNoNoSuccessful operation / regular socket shutdown
1001CLOSE_GOING_AWAYNoNoClient is leaving (browser tab closing)
1002CLOSE_PROTOCOL_ERRORYesNoEndpoint received a malformed frame
1003CLOSE_UNSUPPORTEDYesNoEndpoint received an unsupported frame (e.g. binary-only endpoint received text frame)
1004YesNoReserved
1005CLOSED_NO_STATUSYesNoExpected close status, received none
1006CLOSE_ABNORMALYesNoNo close code frame has been receieved
1007Unsupported payloadYesNoEndpoint received inconsistent message (e.g. malformed UTF-8)
1008Policy violationNoNoGeneric code used for situations other than 1003 and 1009
1009CLOSE_TOO_LARGENoNoEndpoint won’t process large frame
1010Mandatory extensionNoNoClient wanted an extension which server did not negotiate
1011Server errorNoNoInternal server error while operating
1012Service restartNoNoServer/service is restarting
1013Try again laterNoNoTemporary server condition forced blocking client’s request
1014Bad gatewayNoNoServer acting as gateway received an invalid response
1015TLS handshake failYesNoTransport Layer Security handshake failure
1016 - 1999YesNoReserved for future use by the WebSocket standard.
2000 - 2999YesYesReserved for use by WebSocket extensions
3000 - 3999NoYesAvailable for use by libraries and frameworks. May not be used by applications. Available for registration at the IANA via first-come, first-serve.
4000 - 4999NoYesAvailable for applications
Last updated on