State SynchronizationSchema Definition

Schema Definition

  • Schema structures are defined on the server side and represent the state of your game within a room.
  • Only fields decorated with @type() are going to be considered for synchronization.
⚠️

Schema structures should be used within the state only. Do not use Schema structures for messages, or other data that is not related to your state.

Overview

Defining a Schema structure

MyState.ts
import { Schema, type } from "@colyseus/schema";
 
export class MyState extends Schema {
    @type("string") currentTurn: string;
}

Using the state in your Room

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

What is this @type() keyword? I’ve never seen this before!

The @type() you see heavily used on this page is an upcoming JavaScript feature that is yet to be formally established by TC39. type is actually just a function imported from @colyseus/schema module. By calling type with the @ prefix at the property level means we’re calling it as a property decorator. See the decorators proposal here. Make sure your tsconfig.json includes "experimentalDecorators": true, and "useDefineForClassFields": false when using target ES2022 or higher (see #510 for discussion).

Data Types

Primitive types

  • string: utf8 string type
  • boolean: true or false
  • number: auto-detects the number type to use. (may use one extra byte when encoding)
  • int8, int16, int32, int64: signed number types.
  • uint8, uint16, uint32, uint64: unsigned number types.
  • float32, float64: floating-point number types.
  • bigInt64, bigUint64: unsigned / signed 64-bit bigint type.

Table of types and their limitations

TypeDescriptionLimitation
"string"length-prefixed utf8 stringsmaximum byte size of 4294967295
"number"also known as “variable length encoding”. Auto-detects the number type to use. (may use one extra byte when encoding to identify the type)5e-324 to 5e+324 (float64 limits)
"boolean"true or false0 or 1
"int64" and "uint64"JavaScript numbers are 64 bit floats and thus cannot represent full 64 bit integers safelyThe minimum/maximum integer that can be safely represented by float64 is -9007199254740991 to 9007199254740991 (53 bits of precision)

Specialized number types:

TypeDescriptionLimitation
"int8"signed 8-bit integer-128 to 127
"uint8"unsigned 8-bit integer0 to 255
"int16"signed 16-bit integer-32768 to 32767
"uint16"unsigned 16-bit integer0 to 65535
"int32"signed 32-bit integer-2147483648 to 2147483647
"uint32"unsigned 32-bit integer0 to 4294967295
"int64"signed 64-bit integer (number type)-2^53-1 (-9007199254740991) to 2^53-1 (9007199254740991) (safely)
"uint64"unsigned 64-bit integer (number type)0 to 2^53-1 (9007199254740991) (safely)
"float32"single-precision floating-point number-3.40282347e+38 to 3.40282347e+38
"float64"double-precision floating-point number-1.7976931348623157e+308 to 1.7976931348623157e+308
"bigInt64"signed 64-bit integer (bigint type)-2^63 (-9223372036854775808) to 2^63-1 (9223372036854775807)
"bigUint64"unsigned 64-bit integer (bigint type)0 to 2^64-1 (18446744073709551615)

Schema type

A Schema type can define properties as primitives, other Schema types, or collections (e.g., arrays or maps) that may contain nested types.

MyState.ts
import { Schema, type } from "@colyseus/schema";
 
class World extends Schema {
    @type("number") width: number;
    @type("number") height: number;
    @type("number") items: number = 10;
}
 
class MyState extends Schema {
    @type(World) world: World = new World();
}

A Schema type may hold up to 64 synchronizable properties.

Array (ArraySchema)

The ArraySchema is a synchronizeable version of the built-in JavaScript Array type.

Example: Array of custom Schema type

MyState.ts
import { Schema, ArraySchema, type } from "@colyseus/schema";
 
class Block extends Schema {
    @type("number") x: number;
    @type("number") y: number;
}
 
class MyState extends Schema {
    @type([ Block ]) blocks = new ArraySchema<Block>();
}

Example: Array of a primitive type

You can’t mix types inside arrays.

MyState.ts
import { Schema, ArraySchema, type } from "@colyseus/schema";
 
class MyState extends Schema {
    @type([ "string" ]) animals = new ArraySchema<string>();
}

array.push()

Adds one or more elements to the end of an array and returns the new length of the array.

const animals = new ArraySchema<string>();
animals.push("pigs", "goats");
animals.push("sheeps");
animals.push("cows");
// output: 4

array.pop()

Removes the last element from an array and returns that element. This method changes the length of the array.

animals.pop();
// output: "cows"
 
animals.length
// output: 3

array.shift()

Removes the first element from an array and returns that removed element. This method changes the length of the array.

animals.shift();
// output: "pigs"
 
animals.length
// output: 2

array.unshift()

Adds one or more elements to the beginning of an array and returns the new length of the array.

animals.unshift("pigeon");
// output: 3

array.indexOf()

Returns the first index at which a given element can be found in the array, or -1 if it is not present

const itemIndex = animals.indexOf("sheeps");

array.splice()

Changes the contents of an array by removing or replacing existing elements and/or adding new elements in place.

// find the index of the item you'd like to remove
const itemIndex = animals.findIndex((animal) => animal === "sheeps");
 
// remove it!
animals.splice(itemIndex, 1);

array.forEach()

Iterates over each element of the array.

this.state.array1 = new ArraySchema<string>('a', 'b', 'c');
 
this.state.array1.forEach(element => {
    console.log(element);
});
// output: "a"
// output: "b"
// output: "c"

More methods available for Array - Have a look at the MDN Documentation.


array.clear()

Empties the array. The client-side will trigger the onRemove callback for each element.


Map (MapSchema)

The MapSchema is a synchronizeable version of the built-in JavaScript Map type.

Maps are recommended to track your game entities by id, such as players, enemies, etc.

⚠️

Only string keys are supported - Currently, the MapSchema only allows you to customize the value type. The key type is always string.

MyState.ts
import { Schema, MapSchema, type } from "@colyseus/schema";
 
class Player extends Schema {
    @type("number") x: number;
    @type("number") y: number;
}
 
class MyState extends Schema {
    @type({ map: Player }) players = new MapSchema<Player>();
}

map.get()

Getting a map item by its key:

const map = new MapSchema<string>();
const item = map.get("key");

map.set()

Setting a map item by key:

const map = new MapSchema<string>();
map.set("key", "value");

map.delete()

Removes a map item by key:

map.delete("key");

map.size

Return the number of elements in a MapSchema object.

const map = new MapSchema<number>();
map.set("one", 1);
map.set("two", 2);
 
console.log(map.size);
// output: 2

map.forEach()

Iterates over each key/value pair of the map, in insertion order.

this.state.players.forEach((value, key) => {
    console.log("key =>", key)
    console.log("value =>", value)
});

More methods available for Map - Have a look at the MDN Documentation.


map.clear()

Empties the Map. (Client-side will trigger onRemove for each element.)


Set (SetSchema)

⚠️

SetSchema is only available for JavaScript SDK - Haxe, C# and Lua SDKs are not implemented.

The SetSchema is a synchronizeable version of the built-in JavaScript Set type.

The usage of SetSchema is very similar to [CollectionSchema], the biggest difference is that Sets hold unique values. Sets do not have a way to access a value directly. (like collection.at())

MyState.ts
import { Schema, SetSchema, type } from "@colyseus/schema";
 
class Effect extends Schema {
    @type("number") radius: number;
}
 
class Player extends Schema {
    @type({ set: Effect }) effects = new SetSchema<Effect>();
}

set.add()

Appends an item to the SetSchema object.

const set = new SetSchema<number>();
set.add(1);
set.add(2);
set.add(3);

set.delete()

Delete an item by its value.

set.delete("three");

set.has()

Returns a boolean value wheter an item exists in the Collection or not.

if (set.has("two")) {
    console.log("Exists!");
} else {
    console.log("Does not exist!");
}

set.size

Return the number of elements in a SetSchema object.

const set = new SetSchema<number>();
set.add(10);
set.add(20);
set.add(30);
 
console.log(set.size);
// output: 3

More methods available for Set - Have a look at the MDN Documentation.


set.clear()

Empties the Set. (Client-side will trigger onRemove for each element.)


CollectionSchema

⚠️

CollectionSchema is only available for JavaScript SDK - Haxe, C#, Lua and C++ clients are not implemented.

The CollectionSchema works similarly as the ArraySchema, with the caveat that you don’t have control over its indexes.

MyState.ts
import { Schema, CollectionSchema, type } from "@colyseus/schema";
 
class Item extends Schema {
    @type("number") damage: number;
}
 
class Player extends Schema {
    @type({ collection: Item }) items = new CollectionSchema<Item>();
}

collection.add()

Appends an item to the CollectionSchema object.

const collection = new CollectionSchema<number>();
collection.add(1);
collection.add(2);
collection.add(3);

collection.at()

Gets an item at the specified index.

const collection = new CollectionSchema<string>();
collection.add("one");
collection.add("two");
collection.add("three");
 
collection.at(1);
// output: "two"

collection.delete()

Delete an item by its value.

collection.delete("three");

collection.has()

Returns a boolean value wheter an item exists in the Collection or not.

if (collection.has("two")) {
    console.log("Exists!");
} else {
    console.log("Does not exist!");
}

collection.size

Return the number of elements in a CollectionSchema object.

const collection = new CollectionSchema<number>();
collection.add(10);
collection.add(20);
collection.add(30);
 
console.log(collection.size);
// output: 3

collection.forEach()

The forEach() method executes a provided function once per each index/value pair in the CollectionSchema object, in insertion order.

collection.forEach((value, at) => {
    console.log("at =>", at)
    console.log("value =>", value)
});

collection.clear()

Empties the Collection. (Client-side will trigger onRemove for each element.)


Versioning and backwards/forwards compability

Backwards/fowards compatibility is possible by declaring new fields at the end of existing structures, and earlier declarations to not be removed, but be marked @deprecated() when needed. See a versioning example below.

MyState.ts
import { Schema, type, deprecated } from "@colyseus/schema";
 
class MyState extends Schema {
    @type("string") myField: string;
}

This is particularly useful for native-compiled targets, such as C#, C++, Haxe, etc - where the client-side can potentially not have the most up-to-date version of the schema definitions.


Inheritance support

The collection types (ArraySchema, MapSchema, etc) must hold items of the same type. They support inherited types from the same base instance. These inherited types may define their own serialized fields.

The following example is supported:

MyState.ts
import { Schema, type } from "@colyseus/schema";
 
class Item extends Schema {/* base Item fields */}
class Weapon extends Item {/* specialized Weapon fields */}
class Shield extends Item {/* specialized Shield fields */}
 
class Inventory extends Schema {
    @type({ map: Item }) items = new MapSchema<Item>();
}
 
const inventory = new Inventory();
inventory.items.set("left", new Weapon());
inventory.items.set("right", new Shield());
Last updated on