Codecs are responsible for encoding your event data into an 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 cases are already addressed by the simple codecs that follow:
Packaged codecs are available in the netcode/server and netcode/client packages, or as source in the src/encoder/codec folder (see Note on packaging).
| Class | Data format | Example | Size (bytes) |
|---|---|---|---|
NullCodec |
No data (just send the event name) | ['pause', new NullCodec()]send('pause') |
0 |
BooleanCodec |
true / false |
['active', new BooleanCodec()]send('active', true) |
1 |
UInt8Codec |
Integer from 0 to 255 | ['id', new UInt8Codec()]send('id', 42) |
1 |
UInt16Codec |
Integer from 0 to 65535 | ['score', new UInt16Codec()]send('score', 9999) |
2 |
UInt32Codec |
Integer from 0 to 4294967295 | ['position', new UInt32Codec()]send('position', 4294967295) |
4 |
UIntLongCodec(byteLength) |
Unsigned integer on N bytes | ['timestamp', new UIntLongCodec(6)]send('timestamp', Date.now()) |
byteLength |
Int8Codec |
Integer from -128 to 127 | ['value', new Int8Codec()]send('value', -42) |
1 |
Int16Codec |
Integer from -32768 to 32767 | ['malus', new Int16Codec()]send('malus', -999) |
2 |
Int32Codec |
Integer from -2147483648 to 2147483647 | ['position', new Int32Codec()]send('position', -123456) |
4 |
Float32Codec⚠ Subject to precision issues |
Float on 32 bits | ['position', new Float32Codec()]send('position', 84.19999694824219) |
4 |
Float64Codec |
Float on 64 bits | ['position', new Float64Codec()]send('position', -12456789.123456789087654321012345678901) |
8 |
FloatPrecisionCodec(IntCodec, precision) |
Float scaled to a fixed-precision integer | ['position', new FloatPrecisionCodec(new Int8Codec(), 3)]send('position', -0.128) |
size of IntCodec |
StringCodec |
UTF-8 string up to 255 bytes | ['player:name', new StringCodec()]send('player:name', 'DarkShadow73') |
1 + UTF-8 byte length |
StringLongCodec |
UTF-8 string up to 65535 bytes | ['url', new StringLongCodec()]send('url', 'https://my.long.url/hash/xxx...') |
2 + UTF-8 byte length |
Some of your events will have a more complex data structure than just one of the scalar examples above. Good news: you can write your own codecs.
Your custom codec must extend the Codec class of Netcode and implement the three following methods:
getByteLength(data): return the number of bytes the event will occupy for the given data (the byte length may vary based on the data — for arbitrary strings, for example).encode(buffer, offset, data): encode the given data into the provided buffer, starting at the given offset (in bytes).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 write a codec that can transmit these values in an ArrayBuffer.
For this example, we'll encode:
- the player ID as an unsigned integer on 1 byte (
UInt8); - the
xandypositions on 2 bytes each (UInt16).
So the byte length of the event is fixed: 1 + 2 + 2 = 5 bytes.
Full working example using composition:
import { Codec, UInt8Codec, UInt16Codec } from 'netcode/encoder';
export default class PositionCodec extends Codec {
constructor() {
super();
this.int8Codec = new UInt8Codec();
this.int16Codec = new UInt16Codec();
}
/**
* @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 x = this.int16Codec.decode(buffer, offset);
offset += this.int16Codec.getByteLength();
const y = this.int16Codec.decode(buffer, offset);
return { id, x, y };
}
}The BinaryEncoder should emit errors when something goes wrong during 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 your code, to switch to JSON-based communication and help you find out what's wrong with the transmitted data.