I'm in the process of making the state synchronization easier to apply in the client-side, since the usage of delta-listener often causes confusion. (colyseus.js#12, colyseus-unity3d#28, ...)
The idea is to use decorators to annotate which properties should be synched. The delta-listener will be used under the hood by the auto-sync tool.
To initialize the synchronization, you would provide the room instance, and the root instance holding the annotations. The initializeSync method would register all listeners automatically, based on the annotations.
let client = new Client("ws://localhost:2657");
let pongRoom = client.join("pong");
let game = new PongGame();
initializeSync(pongRoom, game); // registers all listeners automatically
Alright, so game is our root instance (of PongGame) holding the annotations. The names of the properties annotated in the client-side should be exactly the same as it is in the room state, in the server-side.
let addToStage = (app, player) => app.stage.addChild(player);
let removeFromStage = (app, player) => app.stage.removeChild(player);
export class PongGame extends PIXI.Application {
@syncMap(Player, addToStage, removeFromStage)
players: EntityMap<Player> = {};
@syncObject(Ball, addToStage, removeFromStage)
ball: Ball;
}
The annotations used here are @syncMap and @syncObject. Both of them accept a callback for when the object is created and removed. Unfortunately, it's not possible to use an instance method as an argument in the decorator, because this is the class scope at that point.
Alright, so we mapped the Player and Ball as child of PongGame. They need to sync its properties as well. We'll use @sync for direct data mappings.
export class Player extends PIXI.Graphics {
@sync() x: number;
@sync() y: number;
}
Given that Player is mapped through PongGame's players property, the sync tool will register listeners for "players/:id/x" and "players/:id/y".
What's interesting is that you can provide name aliases, and even use setters when synching data directly:
export class Ball extends PIXI.Graphics {
/* mapping "x" property to "nextX" */
@sync('x') nextX: number;
/* mapping "y" property to "nextY" setter */
@sync('y')
set nextY (value) {
this._nextY = value;
}
}
That's it for now. I still need to figure out how to deal with deeper objects, like "players/:id/items/:id/x" - which still doesn't work at the moment.
The API is still work in progress, feedback is very welcome! :rocket:
Cheers!
