• Stars
    star
    129
  • Rank 279,262 (Top 6 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created almost 6 years ago
  • Updated 2 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An incremental binary state serializer with delta encoding for games.


An incremental binary state serializer with delta encoding for games.
Although it was born to be used on Colyseus, this library can be used as standalone.

Defining Schema

As Colyseus is written in TypeScript, the schema is defined as type annotations inside the state class. Additional server logic may be added to that class, but client-side generated (not implemented) files will consider only the schema itself.

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

export class Player extends Schema {
  @type("string")
  name: string;

  @type("number")
  x: number;

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

export class State extends Schema {
  @type('string')
  fieldString: string;

  @type('number') // varint
  fieldNumber: number;

  @type(Player)
  player: Player;

  @type([ Player ])
  arrayOfPlayers: ArraySchema<Player>;

  @type({ map: Player })
  mapOfPlayers: MapSchema<Player>;
}

See example.

Supported types

Primitive Types

Type Description Limitation
string utf8 strings maximum byte size of 4294967295
number auto-detects int or float type. (extra byte on output) 0 to 18446744073709551615
boolean true or false 0 or 1
int8 signed 8-bit integer -128 to 127
uint8 unsigned 8-bit integer 0 to 255
int16 signed 16-bit integer -32768 to 32767
uint16 unsigned 16-bit integer 0 to 65535
int32 signed 32-bit integer -2147483648 to 2147483647
uint32 unsigned 32-bit integer 0 to 4294967295
int64 signed 64-bit integer -9223372036854775808 to 9223372036854775807
uint64 unsigned 64-bit integer 0 to 18446744073709551615
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

Declaration:

Primitive types (string, number, boolean, etc)

@type("string")
name: string;

@type("int32")
name: number;

Custom Schema type

@type(Player)
player: Player;

Array of custom Schema type

@type([ Player ])
arrayOfPlayers: ArraySchema<Player>;

Array of a primitive type

You can't mix types inside arrays.

@type([ "number" ])
arrayOfNumbers: ArraySchema<number>;

@type([ "string" ])
arrayOfStrings: ArraySchema<string>;

Map of custom Schema type

@type({ map: Player })
mapOfPlayers: MapSchema<Player>;

Map of a primitive type

You can't mix types inside maps.

@type({ map: "number" })
mapOfNumbers: MapSchema<number>;

@type({ map: "string" })
mapOfStrings: MapSchema<string>;

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.

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.

Reflection

The Schema definitions can encode itself through Reflection. You can have the definition implementation in the server-side, and just send the encoded reflection to the client-side, for example:

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

class MyState extends Schema {
  @type("string")
  currentTurn: string;

  // more definitions relating to more Schema types.
}

// send `encodedStateSchema` across the network
const encodedStateSchema = Reflection.encode(new MyState());

// instantiate `MyState` in the client-side, without having its definition:
const myState = Reflection.decode(encodedStateSchema);

Data filters

On the example below, considering we're making a card game, we are filtering the cards to be available only for the owner of the cards, or if the card has been flagged as "revealed".

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

export class State extends Schema {
  @filterChildren(function(client: any, key: string, value: Card, root: State) {
      return (value.ownerId === client.sessionId) || value.revealed;
  })
  @type({ map: Card })
  cards = new MapSchema<Card>();
}

Limitations and best practices

  • Each Schema structure can hold up to 64 fields. If you need more fields, use nested structures.
  • NaN or null numbers are encoded as 0
  • null strings are encoded as ""
  • Infinity numbers are encoded as Number.MAX_SAFE_INTEGER
  • Multi-dimensional arrays are not supported.
  • Items inside Arrays and Maps must be all instance of the same type.
  • @colyseus/schema encodes only field values in the specified order.
    • Both encoder (server) and decoder (client) must have same schema definition.
    • The order of the fields must be the same.
  • Avoid manipulating indexes of an array. This result in at least 2 extra bytes for each index change. Example: If you have an array of 20 items, and remove the first item (through shift()) this means 38 extra bytes to be serialized.

Generating client-side schema files (for strictly typed languages)

If you're using JavaScript or LUA, there's no need to bother about this. Interpreted programming languages are able to re-build the Schema locally through the use of Reflection.

You can generate the client-side schema files based on the TypeScript schema definitions automatically.

# C#/Unity
schema-codegen ./schemas/State.ts --output ./unity-project/ --csharp

# C/C++
schema-codegen ./schemas/State.ts --output ./cpp-project/ --cpp

# Haxe
schema-codegen ./schemas/State.ts --output ./haxe-project/ --haxe

Benchmarks:

Scenario @colyseus/schema msgpack + fossil-delta
Initial state size (100 entities) 2671 3283
Updating x/y of 1 entity after initial state 9 26
Updating x/y of 50 entities after initial state 342 684
Updating x/y of 100 entities after initial state 668 1529

Decoder implementations

Decoders for each target language are located at /decoders/. They have no third party dependencies.

Why

Initial thoghts/assumptions, for Colyseus:

  • little to no bottleneck for detecting state changes.
  • have a schema definition on both server and client
  • better experience on staticaly-typed languages (C#, C++)
  • mutations should be cheap.

Practical Colyseus issues this should solve:

  • Avoid decoding large objects that haven't been patched
  • Allow to send different patches for each client
  • Better developer experience on statically-typed languages

Inspiration:

License

MIT

More Repositories

1

colyseus

βš” Multiplayer Framework for Node.js
TypeScript
5,772
star
2

colyseus.js

βš” Colyseus Multiplayer SDK for JavaScript/TypeScript
TypeScript
407
star
3

colyseus-unity-sdk

βš” Colyseus Multiplayer SDK for Unity
C#
371
star
4

colyseus-examples

Examples for learning how Colyseus works
HTML
177
star
5

colyseus-haxe

βš” Colyseus Multiplayer SDK for Haxe
Haxe
74
star
6

colyseus-defold

βš” Colyseus SDK for Defold Engine
Lua
61
star
7

uWebSockets-express

Express API compatibility layer for uWebSockets.js
TypeScript
57
star
8

tutorial-phaser

Tutorial: Real-time Multiplayer with Phaser and Colyseus
TypeScript
47
star
9

proxy

πŸ”€βš” Proxy and Service Discovery for Colyseus 0.10 ~ 0.14 (Not recommended on 0.15+)
TypeScript
37
star
10

colyseus-monitor

Web Monitoring Panel for Colyseus
TypeScript
36
star
11

create-colyseus-app

Create an empty Colyseus server project ready to bring your own implementation
JavaScript
30
star
12

discord-embedded-app-sdk

Template: Multiplayer Discord Activity with Colyseus + PixiJS (Discord Embedded App SDK)
TypeScript
25
star
13

timer

Timing Events tied to @colyseus/clock
TypeScript
24
star
14

unity-demo-mmo

Colyseus + Unity: Basic MMO with a chat system, player persistence, and multiple flowing rooms
C#
22
star
15

colyseus-construct3

βš” Colyseus Multiplayer SDK for Construct 3
JavaScript
22
star
16

colyseus-social

Authentication and Social features
TypeScript
21
star
17

unity-demo-shooting-gallery

Colyseus Multiplayer Shooting Gallery (Tech Demo)
C#
21
star
18

tutorial-babylonjs-server

Server-side code for Tutorial: Real-time Multiplayer with BabylonJS and Colyseus
TypeScript
20
star
19

clock

A simple clock/ticker implementation to track elapsed/delta time.
JavaScript
19
star
20

tutorial-babylonjs-client

Client-side code for Tutorial: Real-time Multiplayer with BabylonJS and Colyseus
TypeScript
15
star
21

colyseus-cocos2d-x

βš”οΈ Colyseus Multiplayer SDK for Cocos2d-x
C++
15
star
22

colyseus-loadtest

Utility tool for load testing Colyseus.
15
star
23

command

Command Pattern for Colyseus
TypeScript
14
star
24

decentraland

Colyseus + Decentraland demonstration
TypeScript
14
star
25

state-listener

Deeply compare JavaScript objects and listen to changes. Used in colyseus.js (https://github.com/gamestdio/colyseus.js)
TypeScript
12
star
26

babylonjs-hide-and-seek

Multiplayer Hide-and-Seek made with BabylonJS and Colyseus
TypeScript
12
star
27

cocos-demo-mmo

Colyseus + Cocos: Basic MMO with a chat system, player persistence, and multiple flowing rooms
JavaScript
12
star
28

unity-demo-starboss

Colyseus + Unity: Turning the "Procedural Boss Demo" into Multiplayer
C#
11
star
29

docs

Documentation for Colyseus
HTML
10
star
30

colyseus-gml

(WORK IN PROGRESS, NOT READY TO USE) Multiplayer Game Client for Game Maker Studio 2
Game Maker Language
10
star
31

tutorial-playcanvas-server

Server code for Tutorial: Real-time Multiplayer with PlayCanvas and Colyseus
TypeScript
10
star
32

cocos-demo-tictactoe

Cocos Creator + Colyseus: Multiplayer Tic Tac Toe Example
JavaScript
9
star
33

colyseus-traefik

Experimental: Load Balancing Colyseus servers with Traefik
TypeScript
7
star
34

demo-push-to-talk

PoC: Simple "Push To Talk" example
TypeScript
6
star
35

webgame-template

Webgame Backend + Frontend Template with Colyseus + Authentication
TypeScript
6
star
36

unity-demo-tanks

Colyseus + Unity: Multiplayer Turn-based demonstration
C#
5
star
37

colyseus.lua

(DEPRECATED) LUA/CoronaSDK client for Colyseus Multiplayer Game Server (v0.3.x). Use colyseus-defold instead.
Lua
4
star
38

playground

Client-side playground tool for Colyseus
TypeScript
4
star
39

demo-firebase-authentication

Authentication using Colyseus and Firebase
HTML
2
star
40

construct3-demo

https://docs.colyseus.io/getting-started/construct3-sdk/
TypeScript
1
star
41

arena

Colyseus Arena Tools
1
star