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
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
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.
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:
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 updatesproperties: (optional) list of properties that will be assigned totargetObject. By default, every@type()’d property will be used.
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.
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.
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.
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:
npx schema-codegen --helpOutput:
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 codeExample: Unity / C#
Below is a real example to generate the C# schema files from the demo Unity project.
npx schema-codegen src/rooms/schema/* --csharp --output ../Assets/Scripts/States/"
generated: Player.cs
generated: State.csUsing npm scripts:
For short, it is recommended to have your schema-codegen arguments configured under a npm script in your 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:
npm run schema-codegen
generated: Player.cs
generated: State.cs