Upgrading Versions0.16

Upgrading to version 0.16 (from 0.15)

Use this guide to migrate your existing 0.15 projects to version 0.16.

Make sure colyseus and all @colyseus/* packages on package.json are pointing to ^0.16.0. Having different versions of colyseus and @colyseus/* packages may lead to unexpected behavior.

Server-side:

package.json
    "@colyseus/schema": "^3.0.0",
    "colyseus": "^0.16.0",
    "@colyseus/auth": "^0.16.0",
    "@colyseus/core": "^0.16.0",
    "@colyseus/loadtest": "^0.16.0",
    "@colyseus/monitor": "^0.16.0",
    "@colyseus/playground": "^0.16.0",
    "@colyseus/testing": "^0.16.0",
    "@colyseus/tools": "^0.16.0",
    "@colyseus/uwebsockets-transport": "^0.16.0",

Client-side:

package.json
    "colyseus.js": "^0.16.0",

Make sure you don’t have any packages under 0.15.x, neither @colyseus/schema under 2.0.x. You may need to remove node_modules and package-lock.json before running npm install.


Schema callbacks and $(instance) proxy

As the callback methods have been extracted out of the Schema instances, you now have to get a proxied version of your Schema instances via $(yourInstance) in order to attach callbacks into them.

client.ts
import { getStateCallbacks } from "colyseus.js";
 
const room = await client.joinOrCreate("my_room");
const $ = getStateCallbacks(room);
 
$(room.state).players.onAdd((player, sessionId) => {
    // this is the raw Schema instance
    console.log(player);
 
    // this is the proxied version, where you can attach the callbacks
    $(player).listen("x", (value, previousValue) => {
        console.log("player.x has been updated!", value, previousValue);
    });
});

The proxy is capable of entering nested objects, even if they’re not available at the time you’re attaching the callback.

client.ts
$(room.state)
    .yourInstance // this is a Schema instance (may not be immediately available)
    .yourItems // this is a MapSchema or ArraySchema (may also not be immediately available)
    .onAdd((item, index) => {
        console.log("item added at index", index);
    });

For more context on why these changes are being applied, see colyseus/colyseus#709

@filter() and @filterChildren() are now deprecated!

The StateView feature has been introduced in version 0.16, which allows you to create a view of your state with a subset of the data.

MyState.ts
import { view, type, Schema, MapSchema } from "@colyseus/schema";
 
class MyState extends Schema {
    @type({ map: Player }) players = new MapSchema<Player>();
}
 
class Player extends Schema {
    // this property is visible to all
    @type("string") name: string;
 
    // this property is only visible a `StateView` that has added this Player instance.
    @view() @type("number") position: number;
}

The StateView must be assigned to the client.view. The room serializer is going to use the client.view to serialize the state before sending it to the client.

MyRoom.ts
import { StateView } from "@colyseus/schema";
 
// ...
    onJoin(client, options) {
        const player = new Player();
 
        client.view = new StateView();
        client.view.add(player);
 
        this.state.players.set(client.sessionId, player);
    }

Each StateView instance is going to add a new encoding step for state serialization. You may re-use the same StateView instance for multiple clients, or create a new one for each client.

See more in the StateView documentation.

Breaking changes

.onChange callback on collections (MapSchema, ArraySchema, etc) has been removed!

Collections no longer have the .onChange callback. You can use a combination of .onAdd, .onRemove to achieve the same behavior. (See discussion)

-schema.collection.onChange(callback)
+$(schema).collection.onAdd(callback)
+$(schema).collection.onRemove(callback)

Attaching playground routes:

The playground routes are now returned from a method call instead:

src/app.config.ts
import { playground } from "@colyseus/playground";
 
// ...
if (process.env.NODE_ENV !== "production") {
-   app.use("/", playground);
+   app.use("/", playground());
}

This change was necessary to avoid the internal monkey-patching coming from the playground module when using in production environment.

SDKs: getAvailableRooms() method has been removed!

The getAvailableRooms() method has been removed from the SDKs. The usage of getAvailableRooms() was considered a security risk for many use-cases, as it was exposing the full list of rooms to any client.

If you rely on this method, you can easily re-implement it on your server-side code, although it’s recommended to implement a more secure way to query rooms, such as using the LobbyRoom.

app.config.ts
import { matchMaker } from "colyseus";
 
// ...
initializeExpress (app) {
    app.get("/rooms/:roomName?", (req, res) => {
        const conditions: any = {
            locked: false,
            private: false,
        };
        if (roomName) {
            conditions["name"] = roomName;
        }
        res.json(await matchMaker.query(conditions));
    });
}

From the client SDK, you can use client.http.get("/rooms/:roomName") to get the list of rooms.

matchMaker.getRoomById()

  • matchMaker.getRoomById() now returns the cached room data. This method will return properly when called from any process.
  • matchMaker.getLocalRoomById() returns the room instance itself, if it’s available in the current process.

ArraySchema.deleteAt() has been removed.

You can use ArraySchema.splice() instead:

- array.deleteAt(index);
+ array.splice(index, 1);

Sending schema-encoded messages is now deprecated

Sending schema instances as messages via client.send(instance) or this.broadcast(instance) is now deprecated. This feature it was introduced in previous versions due to lack of strong types in C# for messages.

.encode(), .encodeAll() and .decode() methods have been moved

The Schema instances used to provide .encode(), .encodeAll() and .decode() methods. These methods are called internally by the framework, and if you were not using them, you don’t need to worry about this change.

If you were using these methods, you can now use them from the Encoder and Decoder classes:

import { Encoder, Decoder } from "@colyseus/schema";
 
const encoder = new Encoder(new MyState());
const encodeAllBytes = encoder.encodeAll();
 
const decoded = new Decoder(new MyState());
decoded.decode(encodeAllBytes);