
Security News
vlt Launches "reproduce": A New Tool Challenging the Limits of Package Provenance
vlt's new "reproduce" tool verifies npm packages against their source code, outperforming traditional provenance adoption in the JavaScript ecosystem.
@colyseus/schema
Advanced tools
A binary schema-based serialization algorithm.
Although it was born to be used on Colyseus, this library can be used as standalone.
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.
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 |
string
, number
, boolean
, etc)@type("string")
name: string;
@type("int32")
name: number;
Schema
type@type(Player)
player: Player;
Schema
type@type([ Player ])
arrayOfPlayers: ArraySchema<Player>;
You can't mix types inside arrays.
@type([ "number" ])
arrayOfNumbers: ArraySchema<number>;
@type([ "string" ])
arrayOfStrings: ArraySchema<string>;
Schema
type@type({ map: Player })
mapOfPlayers: MapSchema<Player>;
You can't mix types inside maps.
@type({ map: "number" })
mapOfNumbers: MapSchema<number>;
@type({ map: "string" })
mapOfStrings: MapSchema<string>;
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);
When using with Colyseus 0.10, you may provide a @filter
per field, to filter out what you don't want to serialize for a specific client.
On the example below, we are filtering entities which are close to the player entity.
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>();
}
NaN
or null
numbers are encoded as 0
null
strings are encoded as ""
Infinity
numbers are encoded as Number.MAX_SAFE_INTEGER
@colyseus/schema
encodes only field values in the specified order.
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.2
extra bytes per key move.TODO: describe how changes will arrive on array and map types
import { DataChange } from "@colyseus/schema";
import { State } from "./YourStateDefinition";
const decodedState = new State();
decodedState.onChange = function(changes: DataChange[]) {
assert.equal(changes.length, 1);
assert.equal(changes[0].field, "fieldNumber");
assert.equal(changes[0].value, 50);
assert.equal(changes[0].previousValue, undefined);
}
decodedState.decode(incomingData);
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/ --cs
# C/C++
schema-codegen ./schemas/State.ts --output ./cpp-project/ --cpp
# Haxe
schema-codegen ./schemas/State.ts --output ./haxe-project/ --hx
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 |
Decoders for each target language are located at /decoders/
. They have no third party dependencies.
Initial thoghts/assumptions, for Colyseus:
Practical Colyseus issues this should solve:
MIT
FAQs
Binary state serializer with delta encoding for games
The npm package @colyseus/schema receives a total of 7,655 weekly downloads. As such, @colyseus/schema popularity was classified as popular.
We found that @colyseus/schema demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
vlt's new "reproduce" tool verifies npm packages against their source code, outperforming traditional provenance adoption in the JavaScript ecosystem.
Research
Security News
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
Research
The Socket Research Team discovered a malicious npm package, '@ton-wallet/create', stealing cryptocurrency wallet keys from developers and users in the TON ecosystem.