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!