Codecs are responsible for encoding your events data into ArrayBuffer that will be transmitted over the binary websocket connection and decoded back into usable data on the other side.
You will probably need to write your own codecs but a few standard needs are alreay adressed by the simple codecs that follow:
Packaged codecs are available in the netcode/server
and netcode/client
packages or as source code in netcode/src/encoder/codec
folder (see [Note on packaging](#Note on packaging))
Class | Data format | Example | Size (in byte) |
---|---|---|---|
Codec |
No data (just send the event name) | ['pause', new Codec()] send('pause') |
0 |
BooleanCodec |
true|false |
['active', new BooleanCodec()] send('active', true) |
1 |
StringCodec |
String up to 255 characters | ['player:name', new StringCodec()] send('player:name', 'DarkShadow73') |
1 + (String length * 2) |
LongStringCodec |
String up to 65536 characters | ['url', new LongStringCodec()] send('url', 'https://my.long.url/hash/xxx...') |
2 + (String length * 2) |
Int8Codec |
Integer from 0 to 255 | ['id', new Int8Codec()] send('id', 42) |
1 |
Int16Codec |
Integer from 0 to 65536 | ['score', new Int16Codec()] send('score', 9999) |
2 |
Int32Codec |
Integer from 0 to 4294967295 | ['position', new Int32Codec()] send('position', 4294967295) |
4 |
LongIntCodec(byteLength) |
Integer encoded as string | ['timestamp', new LongIntCodec(13) ]send('timestamp', Date.now()) |
byteLength |
Some of your events will certainly have more complex data structure that just one of the above single scalar example. Good news is you can write your own codecs!
Your custom codec must extends the Codec
class of Netcode and implement the 3 following methods:
getByteLength(data)
: return the number of byte in the event for the given data (your event byte length may vary over the data, like for abitrary strings for example).encode(buffer, offset, data)
encode the given data into the provided buffer, starting at the given offset (in byte).decode(buffer, offset)
: read and return the data contained in the provided buffer, starting at the given offset.
Let's say we want to send an event with the given format:
send('position', { id: player.id, x: 12345, y: 2354 });
We'll need to rite a codec that can transmit these values in an ArrayBuffer.
For this example, I'm gonna chose to encode:
- The player ID as an unsigned integer on 1 byte (UInt8)
- The x and y position on 2 byte each (Uint16)
So the byte length of my event is fixed: 1 + 2 + 2 = 5 bytes.
Full working example using composition:
import { Codec, Int8Codec, Int16Codec } from 'netcode/src/encoder/codec';
export default class PositionCodec extends Codec {
constructor() {
super();
this.int8Codec = new Int8Codec();
this.int16Codec = new Int16Codec();
}
/**
* @type {Number}
*/
getByteLength() {
return this.int8Codec.getByteLength() + this.int16Codec.getByteLength() * 2;
}
/**
* {@inheritdoc}
*/
encode(buffer, offset, data) {
const { id, x, y } = data;
this.int8Codec.encode(buffer, offset, id);
offset += this.int8Codec.getByteLength();
this.int16Codec.encode(buffer, offset, x);
offset += this.int16Codec.getByteLength();
this.int16Codec.encode(buffer, offset, y);
}
/**
* {@inheritdoc}
*/
decode(buffer, offset) {
const id = this.int8Codec.decode(buffer, offset);
offset += this.int8Codec.getByteLength();
const duration = this.int16Codec.decode(buffer, offset);
offset += this.int16Codec.getByteLength();
const position = this.int8Codec.decode(buffer, offset);
return { id, x, y };
}
}
The BinaryEncoder should emit errors if anything wrong happen durring encoding or decoding, but even so, working with binary data can be tricky.
You can replace your BinaryEncoder
with a JsonEncoder
at any time without any other change to you code to switch to a JSON communication and to help you find out what's wrong with transmitted data.