colyseus.js
Version:
Colyseus Multiplayer SDK for JavaScript/TypeScript
638 lines • 21.2 kB
JavaScript
"use strict";
/**
* Copyright (c) 2014 Ion Drive Software Ltd.
* https://github.com/darrachequesne/notepack/
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.decode = exports.encode = void 0;
/**
* Patch for Colyseus:
* -------------------
* notepack.io@3.0.1
*
* added `offset` on Decoder constructor, for messages arriving with a code
* before actual msgpack data
*/
//
// DECODER
//
function Decoder(buffer, offset) {
this._offset = offset;
if (buffer instanceof ArrayBuffer) {
this._buffer = buffer;
this._view = new DataView(this._buffer);
}
else if (ArrayBuffer.isView(buffer)) {
this._buffer = buffer.buffer;
this._view = new DataView(this._buffer, buffer.byteOffset, buffer.byteLength);
}
else {
throw new Error('Invalid argument');
}
}
function utf8Read(view, offset, length) {
var string = '', chr = 0;
for (var i = offset, end = offset + length; i < end; i++) {
var byte = view.getUint8(i);
if ((byte & 0x80) === 0x00) {
string += String.fromCharCode(byte);
continue;
}
if ((byte & 0xe0) === 0xc0) {
string += String.fromCharCode(((byte & 0x1f) << 6) |
(view.getUint8(++i) & 0x3f));
continue;
}
if ((byte & 0xf0) === 0xe0) {
string += String.fromCharCode(((byte & 0x0f) << 12) |
((view.getUint8(++i) & 0x3f) << 6) |
((view.getUint8(++i) & 0x3f) << 0));
continue;
}
if ((byte & 0xf8) === 0xf0) {
chr = ((byte & 0x07) << 18) |
((view.getUint8(++i) & 0x3f) << 12) |
((view.getUint8(++i) & 0x3f) << 6) |
((view.getUint8(++i) & 0x3f) << 0);
if (chr >= 0x010000) { // surrogate pair
chr -= 0x010000;
string += String.fromCharCode((chr >>> 10) + 0xD800, (chr & 0x3FF) + 0xDC00);
}
else {
string += String.fromCharCode(chr);
}
continue;
}
throw new Error('Invalid byte ' + byte.toString(16));
}
return string;
}
Decoder.prototype._array = function (length) {
var value = new Array(length);
for (var i = 0; i < length; i++) {
value[i] = this._parse();
}
return value;
};
Decoder.prototype._map = function (length) {
var key = '', value = {};
for (var i = 0; i < length; i++) {
key = this._parse();
value[key] = this._parse();
}
return value;
};
Decoder.prototype._str = function (length) {
var value = utf8Read(this._view, this._offset, length);
this._offset += length;
return value;
};
Decoder.prototype._bin = function (length) {
var value = this._buffer.slice(this._offset, this._offset + length);
this._offset += length;
return value;
};
Decoder.prototype._parse = function () {
var prefix = this._view.getUint8(this._offset++);
var value, length = 0, type = 0, hi = 0, lo = 0;
if (prefix < 0xc0) {
// positive fixint
if (prefix < 0x80) {
return prefix;
}
// fixmap
if (prefix < 0x90) {
return this._map(prefix & 0x0f);
}
// fixarray
if (prefix < 0xa0) {
return this._array(prefix & 0x0f);
}
// fixstr
return this._str(prefix & 0x1f);
}
// negative fixint
if (prefix > 0xdf) {
return (0xff - prefix + 1) * -1;
}
switch (prefix) {
// nil
case 0xc0:
return null;
// false
case 0xc2:
return false;
// true
case 0xc3:
return true;
// bin
case 0xc4:
length = this._view.getUint8(this._offset);
this._offset += 1;
return this._bin(length);
case 0xc5:
length = this._view.getUint16(this._offset);
this._offset += 2;
return this._bin(length);
case 0xc6:
length = this._view.getUint32(this._offset);
this._offset += 4;
return this._bin(length);
// ext
case 0xc7:
length = this._view.getUint8(this._offset);
type = this._view.getInt8(this._offset + 1);
this._offset += 2;
if (type === -1) {
// timestamp 96
var ns = this._view.getUint32(this._offset);
hi = this._view.getInt32(this._offset + 4);
lo = this._view.getUint32(this._offset + 8);
this._offset += 12;
return new Date((hi * 0x100000000 + lo) * 1e3 + ns / 1e6);
}
return [type, this._bin(length)];
case 0xc8:
length = this._view.getUint16(this._offset);
type = this._view.getInt8(this._offset + 2);
this._offset += 3;
return [type, this._bin(length)];
case 0xc9:
length = this._view.getUint32(this._offset);
type = this._view.getInt8(this._offset + 4);
this._offset += 5;
return [type, this._bin(length)];
// float
case 0xca:
value = this._view.getFloat32(this._offset);
this._offset += 4;
return value;
case 0xcb:
value = this._view.getFloat64(this._offset);
this._offset += 8;
return value;
// uint
case 0xcc:
value = this._view.getUint8(this._offset);
this._offset += 1;
return value;
case 0xcd:
value = this._view.getUint16(this._offset);
this._offset += 2;
return value;
case 0xce:
value = this._view.getUint32(this._offset);
this._offset += 4;
return value;
case 0xcf:
hi = this._view.getUint32(this._offset) * Math.pow(2, 32);
lo = this._view.getUint32(this._offset + 4);
this._offset += 8;
return hi + lo;
// int
case 0xd0:
value = this._view.getInt8(this._offset);
this._offset += 1;
return value;
case 0xd1:
value = this._view.getInt16(this._offset);
this._offset += 2;
return value;
case 0xd2:
value = this._view.getInt32(this._offset);
this._offset += 4;
return value;
case 0xd3:
hi = this._view.getInt32(this._offset) * Math.pow(2, 32);
lo = this._view.getUint32(this._offset + 4);
this._offset += 8;
return hi + lo;
// fixext
case 0xd4:
type = this._view.getInt8(this._offset);
this._offset += 1;
if (type === 0x00) {
// custom encoding for 'undefined' (kept for backward-compatibility)
this._offset += 1;
return void 0;
}
return [type, this._bin(1)];
case 0xd5:
type = this._view.getInt8(this._offset);
this._offset += 1;
return [type, this._bin(2)];
case 0xd6:
type = this._view.getInt8(this._offset);
this._offset += 1;
if (type === -1) {
// timestamp 32
value = this._view.getUint32(this._offset);
this._offset += 4;
return new Date(value * 1e3);
}
return [type, this._bin(4)];
case 0xd7:
type = this._view.getInt8(this._offset);
this._offset += 1;
if (type === 0x00) {
// custom date encoding (kept for backward-compatibility)
hi = this._view.getInt32(this._offset) * Math.pow(2, 32);
lo = this._view.getUint32(this._offset + 4);
this._offset += 8;
return new Date(hi + lo);
}
if (type === -1) {
// timestamp 64
hi = this._view.getUint32(this._offset);
lo = this._view.getUint32(this._offset + 4);
this._offset += 8;
var s = (hi & 0x3) * 0x100000000 + lo;
return new Date(s * 1e3 + (hi >>> 2) / 1e6);
}
return [type, this._bin(8)];
case 0xd8:
type = this._view.getInt8(this._offset);
this._offset += 1;
return [type, this._bin(16)];
// str
case 0xd9:
length = this._view.getUint8(this._offset);
this._offset += 1;
return this._str(length);
case 0xda:
length = this._view.getUint16(this._offset);
this._offset += 2;
return this._str(length);
case 0xdb:
length = this._view.getUint32(this._offset);
this._offset += 4;
return this._str(length);
// array
case 0xdc:
length = this._view.getUint16(this._offset);
this._offset += 2;
return this._array(length);
case 0xdd:
length = this._view.getUint32(this._offset);
this._offset += 4;
return this._array(length);
// map
case 0xde:
length = this._view.getUint16(this._offset);
this._offset += 2;
return this._map(length);
case 0xdf:
length = this._view.getUint32(this._offset);
this._offset += 4;
return this._map(length);
}
throw new Error('Could not parse');
};
function decode(buffer, offset = 0) {
var decoder = new Decoder(buffer, offset);
var value = decoder._parse();
if (decoder._offset !== buffer.byteLength) {
throw new Error((buffer.byteLength - decoder._offset) + ' trailing bytes');
}
return value;
}
exports.decode = decode;
//
// ENCODER
//
var TIMESTAMP32_MAX_SEC = 0x100000000 - 1; // 32-bit unsigned int
var TIMESTAMP64_MAX_SEC = 0x400000000 - 1; // 34-bit unsigned int
function utf8Write(view, offset, str) {
var c = 0;
for (var i = 0, l = str.length; i < l; i++) {
c = str.charCodeAt(i);
if (c < 0x80) {
view.setUint8(offset++, c);
}
else if (c < 0x800) {
view.setUint8(offset++, 0xc0 | (c >> 6));
view.setUint8(offset++, 0x80 | (c & 0x3f));
}
else if (c < 0xd800 || c >= 0xe000) {
view.setUint8(offset++, 0xe0 | (c >> 12));
view.setUint8(offset++, 0x80 | (c >> 6) & 0x3f);
view.setUint8(offset++, 0x80 | (c & 0x3f));
}
else {
i++;
c = 0x10000 + (((c & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
view.setUint8(offset++, 0xf0 | (c >> 18));
view.setUint8(offset++, 0x80 | (c >> 12) & 0x3f);
view.setUint8(offset++, 0x80 | (c >> 6) & 0x3f);
view.setUint8(offset++, 0x80 | (c & 0x3f));
}
}
}
function utf8Length(str) {
var c = 0, length = 0;
for (var i = 0, l = str.length; i < l; i++) {
c = str.charCodeAt(i);
if (c < 0x80) {
length += 1;
}
else if (c < 0x800) {
length += 2;
}
else if (c < 0xd800 || c >= 0xe000) {
length += 3;
}
else {
i++;
length += 4;
}
}
return length;
}
function _encode(bytes, defers, value) {
var type = typeof value, i = 0, l = 0, hi = 0, lo = 0, length = 0, size = 0;
if (type === 'string') {
length = utf8Length(value);
// fixstr
if (length < 0x20) {
bytes.push(length | 0xa0);
size = 1;
}
// str 8
else if (length < 0x100) {
bytes.push(0xd9, length);
size = 2;
}
// str 16
else if (length < 0x10000) {
bytes.push(0xda, length >> 8, length);
size = 3;
}
// str 32
else if (length < 0x100000000) {
bytes.push(0xdb, length >> 24, length >> 16, length >> 8, length);
size = 5;
}
else {
throw new Error('String too long');
}
defers.push({ _str: value, _length: length, _offset: bytes.length });
return size + length;
}
if (type === 'number') {
// TODO: encode to float 32?
// float 64
if (Math.floor(value) !== value || !isFinite(value)) {
bytes.push(0xcb);
defers.push({ _float: value, _length: 8, _offset: bytes.length });
return 9;
}
if (value >= 0) {
// positive fixnum
if (value < 0x80) {
bytes.push(value);
return 1;
}
// uint 8
if (value < 0x100) {
bytes.push(0xcc, value);
return 2;
}
// uint 16
if (value < 0x10000) {
bytes.push(0xcd, value >> 8, value);
return 3;
}
// uint 32
if (value < 0x100000000) {
bytes.push(0xce, value >> 24, value >> 16, value >> 8, value);
return 5;
}
// uint 64
hi = (value / Math.pow(2, 32)) >> 0;
lo = value >>> 0;
bytes.push(0xcf, hi >> 24, hi >> 16, hi >> 8, hi, lo >> 24, lo >> 16, lo >> 8, lo);
return 9;
}
else {
// negative fixnum
if (value >= -0x20) {
bytes.push(value);
return 1;
}
// int 8
if (value >= -0x80) {
bytes.push(0xd0, value);
return 2;
}
// int 16
if (value >= -0x8000) {
bytes.push(0xd1, value >> 8, value);
return 3;
}
// int 32
if (value >= -0x80000000) {
bytes.push(0xd2, value >> 24, value >> 16, value >> 8, value);
return 5;
}
// int 64
hi = Math.floor(value / Math.pow(2, 32));
lo = value >>> 0;
bytes.push(0xd3, hi >> 24, hi >> 16, hi >> 8, hi, lo >> 24, lo >> 16, lo >> 8, lo);
return 9;
}
}
if (type === 'object') {
// nil
if (value === null) {
bytes.push(0xc0);
return 1;
}
if (Array.isArray(value)) {
length = value.length;
// fixarray
if (length < 0x10) {
bytes.push(length | 0x90);
size = 1;
}
// array 16
else if (length < 0x10000) {
bytes.push(0xdc, length >> 8, length);
size = 3;
}
// array 32
else if (length < 0x100000000) {
bytes.push(0xdd, length >> 24, length >> 16, length >> 8, length);
size = 5;
}
else {
throw new Error('Array too large');
}
for (i = 0; i < length; i++) {
size += _encode(bytes, defers, value[i]);
}
return size;
}
if (value instanceof Date) {
var ms = value.getTime();
var s = Math.floor(ms / 1e3);
var ns = (ms - s * 1e3) * 1e6;
if (s >= 0 && ns >= 0 && s <= TIMESTAMP64_MAX_SEC) {
if (ns === 0 && s <= TIMESTAMP32_MAX_SEC) {
// timestamp 32
bytes.push(0xd6, 0xff, s >> 24, s >> 16, s >> 8, s);
return 6;
}
else {
// timestamp 64
hi = s / 0x100000000;
lo = s & 0xffffffff;
bytes.push(0xd7, 0xff, ns >> 22, ns >> 14, ns >> 6, hi, lo >> 24, lo >> 16, lo >> 8, lo);
return 10;
}
}
else {
// timestamp 96
hi = Math.floor(s / 0x100000000);
lo = s >>> 0;
bytes.push(0xc7, 0x0c, 0xff, ns >> 24, ns >> 16, ns >> 8, ns, hi >> 24, hi >> 16, hi >> 8, hi, lo >> 24, lo >> 16, lo >> 8, lo);
return 15;
}
}
if (value instanceof ArrayBuffer) {
length = value.byteLength;
// bin 8
if (length < 0x100) {
bytes.push(0xc4, length);
size = 2;
}
else
// bin 16
if (length < 0x10000) {
bytes.push(0xc5, length >> 8, length);
size = 3;
}
else
// bin 32
if (length < 0x100000000) {
bytes.push(0xc6, length >> 24, length >> 16, length >> 8, length);
size = 5;
}
else {
throw new Error('Buffer too large');
}
defers.push({ _bin: value, _length: length, _offset: bytes.length });
return size + length;
}
if (typeof value.toJSON === 'function') {
return _encode(bytes, defers, value.toJSON());
}
var keys = [], key = '';
var allKeys = Object.keys(value);
for (i = 0, l = allKeys.length; i < l; i++) {
key = allKeys[i];
if (value[key] !== undefined && typeof value[key] !== 'function') {
keys.push(key);
}
}
length = keys.length;
// fixmap
if (length < 0x10) {
bytes.push(length | 0x80);
size = 1;
}
// map 16
else if (length < 0x10000) {
bytes.push(0xde, length >> 8, length);
size = 3;
}
// map 32
else if (length < 0x100000000) {
bytes.push(0xdf, length >> 24, length >> 16, length >> 8, length);
size = 5;
}
else {
throw new Error('Object too large');
}
for (i = 0; i < length; i++) {
key = keys[i];
size += _encode(bytes, defers, key);
size += _encode(bytes, defers, value[key]);
}
return size;
}
// false/true
if (type === 'boolean') {
bytes.push(value ? 0xc3 : 0xc2);
return 1;
}
if (type === 'undefined') {
bytes.push(0xc0);
return 1;
}
// custom types like BigInt (typeof value === 'bigint')
if (typeof value.toJSON === 'function') {
return _encode(bytes, defers, value.toJSON());
}
throw new Error('Could not encode');
}
function encode(value) {
var bytes = [];
var defers = [];
var size = _encode(bytes, defers, value);
var buf = new ArrayBuffer(size);
var view = new DataView(buf);
var deferIndex = 0;
var deferWritten = 0;
var nextOffset = -1;
if (defers.length > 0) {
nextOffset = defers[0]._offset;
}
var defer, deferLength = 0, offset = 0;
for (var i = 0, l = bytes.length; i < l; i++) {
view.setUint8(deferWritten + i, bytes[i]);
if (i + 1 !== nextOffset) {
continue;
}
defer = defers[deferIndex];
deferLength = defer._length;
offset = deferWritten + nextOffset;
if (defer._bin) {
var bin = new Uint8Array(defer._bin);
for (var j = 0; j < deferLength; j++) {
view.setUint8(offset + j, bin[j]);
}
}
else if (defer._str) {
utf8Write(view, offset, defer._str);
}
else if (defer._float !== undefined) {
view.setFloat64(offset, defer._float);
}
deferIndex++;
deferWritten += deferLength;
if (defers[deferIndex]) {
nextOffset = defers[deferIndex]._offset;
}
}
return buf;
}
exports.encode = encode;
//# sourceMappingURL=index.js.map