Client SDKState Sync Callbacks

State Sync Callbacks (Schema Callbacks)

When applying state changes coming from the server, the frontend is going to trigger callbacks on local instances according to the changes being applied.

In order to register callbacks to Schema instances, you must access the instances through a “callbacks handler”.

Read more about State Synchronization

Overview

Get the callback handler

client.ts
import { Client, Callbacks } from "@colyseus/sdk";
 
// initialize SDK
const client = new Client("http://localhost:2567");
 
// join room
const room = await client.joinOrCreate("my_room");
 
// get state callbacks handler
const callbacks = Callbacks.get(room);

Register the callbacks

client.ts
callbacks.listen("currentTurn", (currentValue, previousValue) => {
    // ...
});
 
// when an entity was added (ArraySchema or MapSchema)
callbacks.onAdd("entities", (entity, sessionId) => {
    // ...
    console.log("entity added", entity);
 
    callbacks.listen(entity, "hp", (currentHp, previousHp) => {
        console.log("entity", sessionId, "changed hp to", currentHp);
    })
});
 
// when an entity was removed (ArraySchema or MapSchema)
callbacks.onRemove("entities", (entity, sessionId) => {
    // ...
    console.log("entity removed", entity);
});
⚠️

C#, C++, Haxe - When using statically typed languages, you need to generate the frontend schema files based on your TypeScript schema definitions. See generating schema on the frontend.

How to use

On Schema instances

Listen

Listens for a single property change within a Schema instance.

client.ts
callbacks.listen("currentTurn", (currentValue, previousValue) => {
    console.log(`currentTurn is now ${currentValue}`);
    console.log(`previous value was: ${previousValue}`);
});

Removing the callback: The .listen() method returns a function that, when called, removes the attached callback:

client.ts
const unbindCallback = callbacks.listen("currentTurn", (currentValue, previousValue) => {
    // ...
});
 
// stop listening for `"currentTurn"` changes.
unbindCallback();

Bind To

Bind properties directly to targetObject, whenever the client receives an update from the server.

Parameters:

  • targetObject: the object that will receive updates
  • properties: (optional) list of properties that will be assigned to targetObject. By default, every @type()’d property will be used.
client.ts
callbacks.onAdd("players", (player, sessionId) => {
    const playerVisual = PIXI.Sprite.from('player');
    callbacks.bindTo(player, playerVisual);
});

On Change

The On Change callback is invoked whenever a direct property of a Schema instance is modified.

  • Triggers only for direct property changes: It does not cascade or propagate changes from nested properties within the Schema.
  • The callback fires after the changes have been applied to the Schema instance. This means you’re dealing with the updated instance when the callback executes.
client.ts
callbacks.onAdd("entities", (entity, sessionId) => {
    // ...
    callbacks.onChange(entity, () => {
        // some property changed inside `entity`
    })
});

On Maps or Arrays

On Add

Register the onAdd callback is called whenever a new instance is added to a collection.

By default, the callback is called immediately for existing items in the collection.

client.ts
callbacks.onAdd("players", (player, sessionId) => {
    console.log(player, "has been added at", sessionId);
 
    // add your player entity to the game world!
 
    // detecting changes on object properties
    callbacks.listen(player, "field_name", (value, previousValue) => {
        console.log(value);
        console.log(previousValue);
    });
});

On Remove

The onRemove callback is called with the removed item and its key on holder object as argument.

client.ts
callbacks.onRemove("players", (player, sessionId) => {
    console.log(player, "has been removed at", sessionId);
 
    // remove your player entity from the game world!
});

Frontend Schema Generation

⚠️

Not required when using JavaScript SDK or Defold SDK - The following section is only required when using statically typed languages in your front-end, such as C#, Haxe, etc.

The schema-codegen is a command-line tool designed to convert your backend schema definitions into compatible frontend schemas.

To decode the state on the client side, its local schema definitions must be compatible with those on the server.

Usage:

To see the usage, From your terminal, cd into your server’s directory and run the following command:

Terminal
npx schema-codegen --help

Output:

Terminal
schema-codegen [path/to/Schema.ts]
 
Usage (C#/Unity)
    schema-codegen src/Schema.ts --output frontend/ --csharp --namespace MyGame.Schema
 
Valid options:
    --output: fhe output directory for generated frontend schema files
    --csharp: generate for C#/Unity
    --cpp: generate for C++
    --haxe: generate for Haxe
    --ts: generate for TypeScript
    --js: generate for JavaScript
    --java: generate for Java
 
Optional:
    --namespace: generate namespace on output code

Example: Unity / C#

Below is a real example to generate the C# schema files from the demo Unity project.

Terminal
npx schema-codegen src/rooms/schema/* --csharp --output ../Assets/Scripts/States/"
generated: Player.cs
generated: State.cs

Using npm scripts:

For short, it is recommended to have your schema-codegen arguments configured under a npm script in your package.json:

package.json
"scripts": {
    "schema-codegen": "schema-codegen src/rooms/schema/* --csharp --output ../Assets/Scripts/States/"
}

This way you can run npm run schema-codegen rather than the full command:

Terminal
npm run schema-codegen
generated: Player.cs
generated: State.cs
Last updated on