UNPKG

colyseus.js

Version:

Colyseus Multiplayer SDK for JavaScript/TypeScript

638 lines 21.2 kB
"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