Skip to content

Migrating to version 0.11 (from 0.10)

The version 0.11 improves match-making and scalability, and introduces breaking changes in both client-side and server-side.

Client-side has been removed!

If you're using in the client-side, you should replace it with room.sessionId.

New match-making methods available in the client-side!

A few methods have been added to the client-side allowing to explicitly create rooms or join them.

  • joinOrCreate(roomName, options) - joins or creates a room by name (previously known as join())
  • create(roomName, options) - only creates new rooms
  • join(roomName, options) - only joins existing rooms by name
  • joinById(roomId, options) - only joins existing rooms by id
  • reconnect(roomId, sessionId) - re-establish a previously lost connection (previously known as rejoin())

Also, the Room instance is not returned immediatelly in the client-side. A promise is returned instead, and it is fulfilled whenever the onJoin() has finished successfully.

Replace your existing client.join() calls with its new client.joinOrCreate():

client.joinOrCreate("battle", {/* options */}).then(room => {
  console.log("joined successfully", room);
}).catch(e => {
  console.error("join error", e);
try {
  Room<YourStateClass> room = await client.JoinOrCreate<YourStateClass>("battle", /* Dictionary of options */);
  Debug.Log("joined successfully");

} catch (ex) {
  Debug.Log("join error");
client:join_or_create("battle", {--[[options]]}, function(err, room)
  if (err ~= nil) then
    print("join error: " .. err)

  print("joined successfully")
client.joinOrCreate("battle", [/* options */], YourStateClass, function(err, room) {
  if (err != null) {
    trace("join error: " + err);

  trace("joined successfully");
client->joinOrCreate<YourStateClass>("battle", {/* options */}, [=](std::string err, Room<State>* room) {
  if (err != "") {
    std::cout << "join error: " << err << std::endl;

  std::cout << "joined successfully" << std::endl;

Lua, Haxe and C++

In languages that doesn't provide an async mechanism out of the box, a callback is expected as last argument for the matchmaking functions. The callback gets invoked whenever the onJoin() has been finished successfully.

room.onJoin is not necessary anymore in the client-side

The room.onJoin is now only used internally. When the promise (or callback) fulfils returning the room instance, it has already been joined successfully.

The reconnect() now expects the room id instead of room name.

Previously, the rejoin() method accepted the room name and sessionId. Now, with reconnect() you should pass the room id instead of the room name:

client.reconnect(roomId, sessionId).then(room => {/* ... */});

JavaScript/TypeScript: Signals API has changed slighly

The room signals are onLeave, onStateChange, onMessage and onError.

  • Use onStateChange(callback) instead of onStateChange.add(callback)
  • Use onStateChange.once(callback) instead of onStateChange.addOnce(callback)


The sender object has been removed from all Schema callbacks and events.

Schema callbacks API has changed slighly

  • Use players.OnAdd += (Player player, string key) => {}.
  • Use players.OnRemove += (Player player, string key) => {}.
  • ... and so on!

Events API has changed slighly

The events are onLeave, onStateChange, onMessage and onError.

  • No need to use client.Connect(), room.ReadyToConnect(), room.Connect(), or client.Recv() anymore.
  • Use onStateChange += (State state, bool isFirstState) => {} instead of onStateChange += (sender, e) => {}
  • Use onMessage += (object message) => {} instead of onMessage += (sender, e) => {}
  • Use onLeave += (int code) => {} instead of onLeave += (sender, e) => {}
  • Use onError += (string message) => {} instead of onError += (sender, e) => {}

arraySchema.GetItems() now returns a Dictionary<int, MySchemaType> instead of a List<MySchemaType>. Replace any cases of (List<MySchemaType>) state.myArraySchema.GetItems() with ((Dictionary<int, MySchemaType>) state.myArraySchema.GetItems()).Values.ToList().


Usage with express

Before creating the Colyseus.Server instance, you'll need to:

  • use the express.json() middleware
  • use the cors() middleware (if you're testing server/client from different port or domain)
  • pass both server and express to the Colyseus.Server constructor.
import http from "http";
import express from "express";
import cors from "cors";
import { Server } from "colyseus";

const app = express();


const server = http.createServer(app);
const gameServer = new Server({
  server: server
const http = require("http");
const express = require("express");
const cors = require("cors");
const colyseus = require("colyseus");

const app = express();


const server = http.createServer(app);
const gameServer = new colyseus.Server({
  server: server

gameServer.register has been renamed to gameServer.define

onInit(options) has been renamed to onCreate(options)

Replace your onInit(options) method in your room with onCreate(options).

onAuth(options) is now onAuth(client, options)

Replace your onAuth(options) method in your room with onAuth(client, options). is now an alias to client.sessionId

As the has been removed from the client-side, it is now just an alias to client.sessionId (available in the client-side as room.sessionId).

The was not a reliable source to identify unique users. If you need an efficient way to determine if the user is the same in multiple browser tabs, consider using some form of authentication. The anonymous authentication from @colyseus/social can serve this purpose very well.

The requestJoin() method has been deprecated.

Instead of using requestJoin() to determine wheter a player is allowed to join a room, you should use matchmaking filters for your defined rooms.

Take this example of using requestJoin() from version 0.10, and how to translate it to 0.11:

// version 0.10
class MyRoom extends Room {
  onInit(options) {
    this.progress = options.progress;

  requestJoin(options, isNew) {
    return this.progress === options.progress;

You can have the same behaviour by defining a progress filter when defining your room. The requestJoin() method should be removed.

// version 0.11
  .define("dungeon", DungeonRoom)

Avoid using this.clients inside onJoin() or onAuth()

The client instance will be automatically added to the this.clients list only after onJoin() has been completed.

If you have a piece of code like this:

onJoin(client, options) {
  if (this.clients.length === 2) {
    // do something!

It's encouraged to replace with something else, like this:

onJoin(client, options) {
  this.state.players[client.sessionId] = new Player(/*...*/);
  if (Object.keys(this.state.players).length === 2) {
    // do something!

onLeave(client, options) {
  delete this.state.players[client.sessionId];

Back to top