Colyseus 0.10: Introducing the New State Serialization Algorithm

I’m really excited to announce the release 0.10 of Colyseus. This version basically introduces a more performant state serialization algorithm and a different way of decoding the state in the client-side. It’s currently available for JavaScript, Defold Engine and Unity3D.

What was the problem with the previous serializer?

Besides being pretty easy to use, the previous serializer had some disadvantages both when encoding AND decoding large state objects.

  • To apply the changes in the client-side, the patches were applied on top of the last snapshot sent by the server — which means the whole state needed to be deserialized again, creating a new copy of the state per patch and having a performance penalty if your state was slightly big.

The New Serializer — Server-side

In the state definition below, you’ll see a map of Player instances.

import { Schema, MapSchema, type } from "@colyseus/schema";

class Player extends Schema {
@type("number")
width: number;

@type("number")
height: number;
}

class MyState extends Schema {
@type({ map: Player })
players = new MapSchema<Player>();
}
import { Room } from "colyseus";
import { MyState } from "./MyState";
class MyRoom extends Room {
onInit() {
this.setState(new MyState());
}
}

The New Serializer — Client-side (JavaScript)

The way you listen for a change in the client-side depends on the structure you’re interested in. Variable changes within a container are available through the “onChange” callback:

var client = new Colyseus.Client("ws://localhost:2567");
var room = client.join("my_room");
room.onJoin.add(() => {
room.state.onChange = (changes) => {
changes.forEach(change => {
console.log(change.field);
console.log(change.value);
console.log(change.previousValue);
});
};
})
room.state.players.onAdd = (player, key) => {
console.log(player, "has been added at", key);
};
room.state.players.onRemove = (player, key) => {
console.log(player, "has been removed at", key);
};
// callback for a whole object change 
// not possible to know which properties have changed here
room.state.players.onChange = (player, key) => {
console.log(player, "has been changed at", key);
};
// callback for checking particular property changes
// this way you know exactly which properties have changed
room.state.players.onAdd = (player, key) => {
player.onChange = function(changes) {
changes.forEach(change => {
console.log(change.field);
console.log(change.value);
console.log(change.previousValue);
});
}
};

That’s it for 0.10

I highly suggest you upgrade to version 0.10, even if you’re not going to use the new serializer yet. The previous serializer is still available and won’t be deprecated any time soon.

Next challenge: data filters! (work in progress)

I’ve started experimenting to allow filtering the data in the state for each client. The plan is to have different callbacks in the client-side whenever the data becomes available or unavailable.

import { Schema, type, filter } from "@colyseus/schema";

export class State extends Schema {
@filter(function(this: State, client: any, value: Entity) {
const currentPlayer = this.entities[client.sessionId]

var a = value.x - currentPlayer.x;
var b = value.y - currentPlayer.y;

return (Math.sqrt(a * a + b * b)) <= 10;

})
@type({ map: Entity })
entities = new MapSchema<Entity>();
}

Feedback is always very welcome!

Feel free to join the Discord server if you have any suggestions. If you just want to hang out with us, you’re welcome too!

Software Engineer & Indie Game Developer

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store