diff --git a/README.md b/README.md index 69a779fc8..12ee3a2bd 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,8 @@ Using opusscript is only recommended for development environments where node-opu For production bots, using node-opus should be considered a necessity, especially if they're going to be running on multiple servers. ### Optional packages -- [zlib-sync](https://www.npmjs.com/package/zlib-sync) for significantly faster WebSocket data inflation (`npm install zlib-sync`) +- [zlib-sync](https://www.npmjs.com/package/zlib-sync) for faster WebSocket data inflation (`npm install zlib-sync`) +- [zucc](https://www.npmjs.com/package/zucc) for significantly faster WebSocket data inflation (`npm install zucc`) - [erlpack](https://github.com/discordapp/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install discordapp/erlpack`) - One of the following packages can be installed for faster voice packet encryption and decryption: - [sodium](https://www.npmjs.com/package/sodium) (`npm install sodium`) diff --git a/package.json b/package.json index fc5d8d1e0..717fe0656 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,8 @@ "erlpack": "discordapp/erlpack", "libsodium-wrappers": "^0.7.4", "sodium": "^3.0.2", - "zlib-sync": "^0.1.4" + "zlib-sync": "^0.1.4", + "zucc": "^0.1.0" }, "devDependencies": { "@types/node": "^10.12.24", @@ -80,6 +81,7 @@ "sodium": false, "worker_threads": false, "zlib-sync": false, + "zucc": false, "src/sharding/Shard.js": false, "src/sharding/ShardClientUtil.js": false, "src/sharding/ShardingManager.js": false, diff --git a/src/client/websocket/WebSocketShard.js b/src/client/websocket/WebSocketShard.js index 67d494e2c..861462c5a 100644 --- a/src/client/websocket/WebSocketShard.js +++ b/src/client/websocket/WebSocketShard.js @@ -4,12 +4,21 @@ const EventEmitter = require('events'); const WebSocket = require('../../WebSocket'); const { Status, Events, ShardEvents, OPCodes, WSEvents } = require('../../util/Constants'); +let zstd; +let decoder; + let zlib; + try { - zlib = require('zlib-sync'); - if (!zlib.Inflate) zlib = require('pako'); -} catch (err) { - zlib = require('pako'); + zstd = require('zucc'); + decoder = new TextDecoder('utf8'); +} catch (e) { + try { + zlib = require('zlib-sync'); + if (!zlib.Inflate) zlib = require('pako'); + } catch (err) { + zlib = require('pako'); + } } /** @@ -206,11 +215,15 @@ class WebSocketShard extends EventEmitter { return; } - this.inflate = new zlib.Inflate({ - chunkSize: 65535, - flush: zlib.Z_SYNC_FLUSH, - to: WebSocket.encoding === 'json' ? 'string' : '', - }); + if (zstd) { + this.inflate = new zstd.DecompressStream(); + } else { + this.inflate = new zlib.Inflate({ + chunkSize: 65535, + flush: zlib.Z_SYNC_FLUSH, + to: WebSocket.encoding === 'json' ? 'string' : '', + }); + } this.debug(`Trying to connect to ${gateway}, version ${client.options.ws.version}`); @@ -219,7 +232,7 @@ class WebSocketShard extends EventEmitter { const ws = this.connection = WebSocket.create(gateway, { v: client.options.ws.version, - compress: 'zlib-stream', + compress: zstd ? 'zstd-stream' : 'zlib-stream', }); ws.onopen = this.onOpen.bind(this); ws.onmessage = this.onMessage.bind(this); @@ -243,19 +256,26 @@ class WebSocketShard extends EventEmitter { * @private */ onMessage({ data }) { - if (data instanceof ArrayBuffer) data = new Uint8Array(data); - const l = data.length; - const flush = l >= 4 && - data[l - 4] === 0x00 && - data[l - 3] === 0x00 && - data[l - 2] === 0xFF && - data[l - 1] === 0xFF; + let raw; + if (zstd) { + const ab = this.inflate.decompress(new Uint8Array(data).buffer); + raw = decoder.decode(ab); + } else { + if (data instanceof ArrayBuffer) data = new Uint8Array(data); + const l = data.length; + const flush = l >= 4 && + data[l - 4] === 0x00 && + data[l - 3] === 0x00 && + data[l - 2] === 0xFF && + data[l - 1] === 0xFF; - this.inflate.push(data, flush && zlib.Z_SYNC_FLUSH); - if (!flush) return; + this.inflate.push(data, flush && zlib.Z_SYNC_FLUSH); + if (!flush) return; + raw = this.inflate.result; + } let packet; try { - packet = WebSocket.unpack(this.inflate.result); + packet = WebSocket.unpack(raw); this.manager.client.emit(Events.RAW, packet, this.id); if (packet.op === OPCodes.DISPATCH) this.manager.emit(packet.t, packet.d, this.id); } catch (err) {