From 17dea74357ee965b9fa4f0adf8814aa8b4d28f4d Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 21 May 2015 20:35:03 +0000 Subject: [PATCH 1/9] Rewrite of the internal serializer using transform streams --- src/client.js | 211 ++++++++++++++++------------------ src/index.js | 8 +- src/protocol.js | 7 +- src/transforms/compression.js | 69 +++++++++++ src/transforms/framing.js | 47 ++++++++ src/transforms/serializer.js | 59 ++++++++++ 6 files changed, 279 insertions(+), 122 deletions(-) create mode 100644 src/transforms/compression.js create mode 100644 src/transforms/framing.js create mode 100644 src/transforms/serializer.js diff --git a/src/client.js b/src/client.js index 6f3730c..3220283 100644 --- a/src/client.js +++ b/src/client.js @@ -11,6 +11,10 @@ var EventEmitter = require('events').EventEmitter , packetNames = protocol.packetNames , states = protocol.states , debug = require('./debug') + , serializer = require('./transforms/serializer') + , compression = require('./transforms/compression') + , framing = require('./transforms/framing') + , crypto = require('crypto') ; module.exports = Client; @@ -18,23 +22,37 @@ module.exports = Client; function Client(isServer) { EventEmitter.call(this); + var socket; + + this.serializer = serializer.createSerializer({ isServer }); + this.compressor = null; + this.framer = framing.createFramer(); + this.cipher = null; + + this.decipher = null; + this.splitter = framing.createSplitter(); + this.decompressor = null; + this.deserializer = serializer.createDeserializer({ isServer }); + this._state = states.HANDSHAKING; Object.defineProperty(this, "state", { get: function() { - return this._state; + return this.serializer.protocolState; }, set: function(newProperty) { - var oldProperty = this._state; - this._state = newProperty; + var oldProperty = this.serializer.protocolState; + this.serializer.protocolState = newProperty; + this.deserializer.protocolState = newProperty; this.emit('state', newProperty, oldProperty); } }); + Object.defineProperty(this, "compressionThreshold", { + get: () => this.compressor == null ? -2 : this.compressor.compressionThreshold, + set: (threshold) => this.setCompressionThreshold(threshold) + }); + this.isServer = !!isServer; - this.socket = null; - this.encryptionEnabled = false; - this.cipher = null; - this.decipher = null; - this.compressionThreshold = -2; + this.packetsToParse = {}; this.on('newListener', function(event, listener) { var direction = this.isServer ? 'toServer' : 'toClient'; @@ -78,77 +96,46 @@ Client.prototype.onRaw = function(type, func) { }; Client.prototype.setSocket = function(socket) { - var self = this; - - function afterParse(err, parsed) { - if(err || (parsed && parsed.error)) { - self.emit('error', err || parsed.error); - self.end("ProtocolError"); - return; - } - if(!parsed) { - return; - } - var packet = parsed.results; - //incomingBuffer = incomingBuffer.slice(parsed.size); TODO: Already removed in prepare - - var packetName = protocol.packetNames[self.state][self.isServer ? 'toServer' : 'toClient'][packet.id]; - var packetState = self.state; - self.emit(packetName, packet); - self.emit('packet', packet); - self.emit('raw.' + packetName, parsed.buffer, packetState); - self.emit('raw', parsed.buffer, packetState); - prepareParse(); - } - - function prepareParse() { - var packetLengthField = protocol.types["varint"][0](incomingBuffer, 0); - if(packetLengthField && packetLengthField.size + packetLengthField.value <= incomingBuffer.length) { - var buf = incomingBuffer.slice(packetLengthField.size, packetLengthField.size + packetLengthField.value); - // TODO : Slice as early as possible to avoid processing same data twice. - incomingBuffer = incomingBuffer.slice(packetLengthField.size + packetLengthField.value); - if(self.compressionThreshold == -2) { - afterParse(null, parsePacketData(buf, self.state, self.isServer, self.packetsToParse)); - } else { - parseNewStylePacket(buf, self.state, self.isServer, self.packetsToParse, afterParse); - } - } - } - - self.socket = socket; - if(self.socket.setNoDelay) - self.socket.setNoDelay(true); - var incomingBuffer = new Buffer(0); - self.socket.on('data', function(data) { - if(self.encryptionEnabled) data = new Buffer(self.decipher.update(data), 'binary'); - incomingBuffer = Buffer.concat([incomingBuffer, data]); - prepareParse() - }); - - self.socket.on('connect', function() { - self.emit('connect'); - }); - - self.socket.on('error', onError); - self.socket.on('close', endSocket); - self.socket.on('end', endSocket); - self.socket.on('timeout', endSocket); - - function onError(err) { - self.emit('error', err); - endSocket(); - } - var ended = false; - function endSocket() { + // TODO : A lot of other things needs to be done. + var endSocket = () => { if(ended) return; ended = true; - self.socket.removeListener('close', endSocket); - self.socket.removeListener('end', endSocket); - self.socket.removeListener('timeout', endSocket); - self.emit('end', self._endReason); - } + this.socket.removeListener('close', endSocket); + this.socket.removeListener('end', endSocket); + this.socket.removeListener('timeout', endSocket); + this.emit('end', this._endReason); + }; + + var onError = (err) => { + this.emit('error', err); + endSocket(); + }; + + this.socket = socket; + + if(this.socket.setNoDelay) + this.socket.setNoDelay(true); + + this.socket.on('connect', () => this.emit('connect')); + + this.socket.on('error', onError); + this.socket.on('close', endSocket); + this.socket.on('end', endSocket); + this.socket.on('timeout', endSocket); + + this.socket.pipe(this.splitter).pipe(this.deserializer); + this.serializer.pipe(this.framer).pipe(this.socket); + + this.deserializer.on('data', (parsed) => { + var packet = parsed.results; + var packetName = protocol.packetNames[packet.state][this.isServer ? 'toServer' : 'toClient'][packet.id]; + this.emit('packet', packet); + this.emit(packetName, packet); + this.emit('raw.' + packetName, parsed.buffer, packet.state); + this.emit('raw', parsed.buffer, packet.state); + }); }; Client.prototype.end = function(reason) { @@ -156,52 +143,45 @@ Client.prototype.end = function(reason) { this.socket.end(); }; +Client.prototype.setEncryption = function(sharedSecret) { + if (this.cipher != null) + throw new Error("Set encryption twice !"); + this.cipher = crypto.createCipheriv('aes-128-cfb8', sharedSecret, sharedSecret); + this.framer.unpipe(this.socket); + this.framer.pipe(this.cipher).pipe(this.socket); + this.decipher = crypto.createDecipheriv('aes-128-cfb8', sharedSecret, sharedSecret); + this.socket.unpipe(this.splitter); + this.socket.pipe(this.decipher).pipe(this.splitter); +} + +Client.prototype.setCompressionThreshold = function(threshold) { + if (this.compressor == null) { + this.compressor = compression.createCompressor(threshold); + this.serializer.unpipe(this.framer); + this.serializer.pipe(this.compressor).pipe(this.framer); + this.decompressor = compression.createDecompressor(threshold); + this.splitter.unpipe(this.deserializer); + this.splitter.pipe(this.decompressor).pipe(this.deserializer); + } else { + this.decompressor.threshold = threshold; + this.compressor.threshold = threshold; + } +} + function noop(err) { if(err) throw err; } -Client.prototype.write = function(packetId, params, cb) { - cb = cb || noop; - if(Array.isArray(packetId)) { - if(packetId[0] !== this.state) - return false; - packetId = packetId[1]; - } - if(typeof packetId === "string") - packetId = packetIds[this.state][this.isServer ? "toClient" : "toServer"][packetId]; - var that = this; - - var finishWriting = function(err, buffer) { - if(err) { - console.log(err); - throw err; // TODO : Handle errors gracefully, if possible - } - var packetName = packetNames[that.state][that.isServer ? "toClient" : "toServer"][packetId]; - debug("writing packetId " + that.state + "." + packetName + " (0x" + packetId.toString(16) + ")"); - debug(params); - var out = that.encryptionEnabled ? new Buffer(that.cipher.update(buffer), 'binary') : buffer; - that.socket.write(out,cb); - return true; - }; - - var buffer = createPacketBuffer(packetId, this.state, params, this.isServer); - if(this.compressionThreshold >= 0 && buffer.length >= this.compressionThreshold) { - debug("Compressing packet"); - compressPacketBuffer(buffer, finishWriting); - } else if(this.compressionThreshold >= -1) { - debug("New-styling packet"); - newStylePacket(buffer, 0, finishWriting); - } else { - debug("Old-styling packet"); - oldStylePacket(buffer, finishWriting); - } +Client.prototype.write = function(packetId, params, cb = noop) { + var packetName = protocol.packetNames[this.state][this.isServer ? "toClient" : "toServer"][packetId]; + debug("writing packetId " + this.state + "." + packetName + " (0x" + packetId.toString(16) + ")"); + debug(params); + this.serializer.write({ packetId, params }, cb); }; -// TODO : Perhaps this should only accept buffers without length, so we can -// handle compression ourself ? Needs to ask peopl who actually use this feature -// like @deathcap +// TODO : Write to the correct stream. This is currently broken. Client.prototype.writeRaw = function(buffer) { - var self = this; + /*var self = this; var finishWriting = function(error, buffer) { if(error) @@ -215,5 +195,6 @@ Client.prototype.writeRaw = function(buffer) { newStylePacket(buffer, 0, finishWriting); } else { oldStylePacket(buffer, finishWriting); - } + }*/ + throw new Error("Pending refactorisation"); }; diff --git a/src/index.js b/src/index.js index ef69bfe..c3aab91 100644 --- a/src/index.js +++ b/src/index.js @@ -183,11 +183,9 @@ function createServer(options) { client.end('DidNotEncryptVerifyTokenProperly'); return; } - client.cipher = crypto.createCipheriv('aes-128-cfb8', sharedSecret, sharedSecret); - client.decipher = crypto.createDecipheriv('aes-128-cfb8', sharedSecret, sharedSecret); + client.setEncryption(sharedSecret); hash.update(sharedSecret); hash.update(client.publicKey); - client.encryptionEnabled = true; var isException = !!server.onlineModeExceptions[client.username.toLowerCase()]; var needToVerify = (onlineMode && !isException) || (!onlineMode && isException); @@ -365,16 +363,14 @@ function createClient(options) { var pubKey = mcPubKeyToURsa(packet.publicKey); var encryptedSharedSecretBuffer = pubKey.encrypt(sharedSecret, undefined, undefined, ursa.RSA_PKCS1_PADDING); var encryptedVerifyTokenBuffer = pubKey.encrypt(packet.verifyToken, undefined, undefined, ursa.RSA_PKCS1_PADDING); - client.cipher = crypto.createCipheriv('aes-128-cfb8', sharedSecret, sharedSecret); - client.decipher = crypto.createDecipheriv('aes-128-cfb8', sharedSecret, sharedSecret); client.write(0x01, { sharedSecret: encryptedSharedSecretBuffer, verifyToken: encryptedVerifyTokenBuffer, },function(err){ if(err) throw err; - client.encryptionEnabled = true; }); + client.setEncryption(sharedSecret); } } } diff --git a/src/protocol.js b/src/protocol.js index f0ce3b2..52bcad8 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -161,7 +161,8 @@ function newStylePacket(buffer, dataSize, callback) { callback(null, packet); } -function parsePacketData(buffer, state, isServer, packetsToParse) { +// By default, parse every packets. +function parsePacketData(buffer, state, isServer, packetsToParse = {"packet": true}) { var cursor = 0; var packetIdField = utils.varint[0](buffer, cursor); var packetId = packetIdField.value; @@ -207,6 +208,10 @@ function parsePacketData(buffer, state, isServer, packetsToParse) { results: results }; }*/ + // TODO : investigate readResults returning null : shouldn't happen. + // When there is not enough data to read, we should return an error. + // As a general rule, it would be a good idea to introduce a whole bunch + // of new error classes to differenciate the errors. if(readResults === null || readResults.value == null) continue; if(readResults.error) { return readResults; diff --git a/src/transforms/compression.js b/src/transforms/compression.js new file mode 100644 index 0000000..6a1ded0 --- /dev/null +++ b/src/transforms/compression.js @@ -0,0 +1,69 @@ +var [readVarInt, writeVarInt, sizeOfVarInt] = require("../datatypes/utils").varint; +var zlib = require("zlib"); +var Transform = require("stream").Transform; + +module.exports.createCompressor = function(threshold) { + return new Compressor(threshold); +} + +module.exports.createDecompressor = function(threshold) { + return new Decompressor(threshold); +} + +class Compressor extends Transform { + constructor(compressionThreshold = -1) { + super(); + this.compressionThreshold = compressionThreshold; + } + + _transform(chunk, enc, cb) { + if (chunk.length >= this.compressionThreshold) + { + zlib.deflate(chunk, (err, newChunk) => { + if (err) + return cb(err); + var buf = new Buffer(sizeOfVarInt(chunk.length) + newChunk.length); + var offset = writeVarInt(chunk.length, buf, 0); + newChunk.copy(buf, offset); + this.push(buf); + return cb(); + }); + } + else + { + var buf = new Buffer(sizeOfVarInt(0) + chunk.length); + var offset = writeVarInt(0, buf, 0); + chunk.copy(buf, offset); + this.push(buf); + return cb(); + } + } +} + +class Decompressor extends Transform { + constructor(compressionThreshold = -1) { + super(); + this.compressionThreshold = compressionThreshold; + } + + _transform(chunk, enc, cb) { + var size, value, error; + ({ size, value, error } = readVarInt(chunk, 0)); + if (error) + return cb(error); + if (value === 0) + { + this.push(chunk.slice(size)); + return cb(); + } + else + { + zlib.inflate(chunk.slice(size), (err, newBuf) => { + if (err) + return cb(err); + this.push(newBuf); + return cb(); + }); + } + } +} diff --git a/src/transforms/framing.js b/src/transforms/framing.js new file mode 100644 index 0000000..b6607ad --- /dev/null +++ b/src/transforms/framing.js @@ -0,0 +1,47 @@ +var [readVarInt, writeVarInt, sizeOfVarInt] = require("../datatypes/utils").varint; +var Transform = require("stream").Transform; + +module.exports.createSplitter = function() { + return new Splitter(); +} + +module.exports.createFramer = function() { + return new Framer(); +} + +class Framer extends Transform { + constructor() { + super(); + } + + _transform(chunk, enc, cb) { + var buffer = new Buffer(sizeOfVarInt(chunk.length)); + writeVarInt(chunk.length, buffer, 0); + this.push(buffer); + this.push(chunk); + return cb(); + } +} + +class Splitter extends Transform { + constructor() { + super(); + this.buffer = new Buffer(0); + } + _transform(chunk, enc, cb) { + this.buffer = Buffer.concat([this.buffer, chunk]); + var value, size, error; + var offset = 0; + + ({ value, size, error } = readVarInt(this.buffer, offset) || { error: "Not enough data" }); + while (!error && this.buffer.length >= offset + size + value) + { + this.push(this.buffer.slice(offset + size, offset + size + value)); + offset += size + value; + ({ value, size, error } = readVarInt(this.buffer, offset) || { error: "Not enough data" }); + } + this.buffer = this.buffer.slice(offset); + return cb(); + } +} + diff --git a/src/transforms/serializer.js b/src/transforms/serializer.js new file mode 100644 index 0000000..99136c0 --- /dev/null +++ b/src/transforms/serializer.js @@ -0,0 +1,59 @@ +var [readVarInt, writeVarInt, sizeOfVarInt] = require("../datatypes/utils").varint; +var protocol = require("../protocol"); +var Transform = require("stream").Transform; + +module.exports.createSerializer = function(obj) { + return new Serializer(obj); +} + +module.exports.createDeserializer = function(obj) { + return new Deserializer(obj); +} + +class Serializer extends Transform { + constructor({ state = protocol.states.HANDSHAKING, isServer = false } = {}) { + super({ writableObjectMode: true }); + this.protocolState = state; + this.isServer = isServer; + } + + // TODO : Might make sense to make createPacketBuffer async. + _transform(chunk, enc, cb) { + try { + var buf = protocol.createPacketBuffer(chunk.packetId, this.protocolState, chunk.params, this.isServer); + this.push(buf); + return cb(); + } catch (e) { + return cb(e); + } + } +} + +class Deserializer extends Transform { + constructor({ state = protocol.states.HANDSHAKING, isServer = false } = {}) { + super({ readableObjectMode: true }); + this.protocolState = state; + this.isServer = isServer; + this.calls = 0; + } + + _transform(chunk, enc, cb) { + this.calls++; + var packet; + try { + packet = protocol.parsePacketData(chunk, this.protocolState, this.isServer, this.packetsToParse); + } catch (e) { + return cb(e); + } + if (packet.error) + { + packet.error.packet = packet; + return cb(packet.error) + } + else + { + this.push(packet); + return cb(); + } + } +} From ced091b2b2f412b5384d223f4f383b6190438797 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 22 May 2015 17:26:48 +0000 Subject: [PATCH 2/9] Rewrite of the internal serializer using transform streams --- test/benchmark.js | 43 +++++++------------------------------------ 1 file changed, 7 insertions(+), 36 deletions(-) diff --git a/test/benchmark.js b/test/benchmark.js index 5a7e8c9..66126ca 100644 --- a/test/benchmark.js +++ b/test/benchmark.js @@ -1,21 +1,8 @@ var ITERATIONS = 100000; -var Client = require('../dist/client'), - EventEmitter = require('events').EventEmitter, +var protocol = require('../dist/protocol'), util = require('util'), - states = require('../dist/protocol').states; - -var FakeSocket = function() { - EventEmitter.call(this); -}; -util.inherits(FakeSocket, EventEmitter); -FakeSocket.prototype.write = function() { -}; - -var client = new Client(); -var socket = new FakeSocket(); -client.setSocket(socket); -client.state = states.PLAY; + states = protocol.states; var testDataWrite = [ {id: 0x00, params: {keepAliveId: 957759560}}, @@ -24,37 +11,21 @@ var testDataWrite = [ // TODO: add more packets for better quality data ]; +var inputData = []; + var start, i, j; console.log('Beginning write test'); start = Date.now(); for(i = 0; i < ITERATIONS; i++) { for(j = 0; j < testDataWrite.length; j++) { - client.write(testDataWrite[j].id, testDataWrite[j].params); + inputData.push(protocol.createPacketBuffer(testDataWrite[j].id, states.PLAY, testDataWrite[j].params, false)); } } console.log('Finished write test in ' + (Date.now() - start) / 1000 + ' seconds'); -var testDataRead = [ - {id: 0x00, params: {keepAliveId: 957759560}}, - {id: 0x02, params: {message: ' Hello World!', position: 0}}, - {id: 0x08, params: {x: 6.5, y: 65.62, z: 7.5, yaw: 0, pitch: 0, flags: 0}}, -]; - -client.isServer = true; - -var inputData = new Buffer(0); -socket.write = function(data) { - inputData = Buffer.concat([inputData, data]); -}; -for(i = 0; i < testDataRead.length; i++) { - client.write(testDataRead[i].id, testDataRead[i].params); -} - -client.isServer = false; - console.log('Beginning read test'); start = Date.now(); -for(i = 0; i < ITERATIONS; i++) { - socket.emit('data', inputData); +for (j = 0; j < inputData.length; j++) { + protocol.parsePacketData(inputData[j], states.PLAY, true); } console.log('Finished read test in ' + (Date.now() - start) / 1000 + ' seconds'); From 6e6b51ca10a2d1cd0bb80d693c33341d2204c3ef Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 22 May 2015 19:00:01 +0000 Subject: [PATCH 3/9] Better node compatibility : Use readable-stream --- package.json | 1 + src/transforms/compression.js | 2 +- src/transforms/framing.js | 2 +- src/transforms/serializer.js | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 479a97e..168d562 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "buffer-equal": "0.0.0", "node-uuid": "~1.4.1", "prismarine-nbt": "0.0.1", + "readable-stream": "^1.1.0", "superagent": "~0.10.0", "ursa-purejs": "0.0.3" }, diff --git a/src/transforms/compression.js b/src/transforms/compression.js index 6a1ded0..7049542 100644 --- a/src/transforms/compression.js +++ b/src/transforms/compression.js @@ -1,6 +1,6 @@ var [readVarInt, writeVarInt, sizeOfVarInt] = require("../datatypes/utils").varint; var zlib = require("zlib"); -var Transform = require("stream").Transform; +var Transform = require("readable-stream").Transform; module.exports.createCompressor = function(threshold) { return new Compressor(threshold); diff --git a/src/transforms/framing.js b/src/transforms/framing.js index b6607ad..5e8d45b 100644 --- a/src/transforms/framing.js +++ b/src/transforms/framing.js @@ -1,5 +1,5 @@ var [readVarInt, writeVarInt, sizeOfVarInt] = require("../datatypes/utils").varint; -var Transform = require("stream").Transform; +var Transform = require("readable-stream").Transform; module.exports.createSplitter = function() { return new Splitter(); diff --git a/src/transforms/serializer.js b/src/transforms/serializer.js index 99136c0..fb896ae 100644 --- a/src/transforms/serializer.js +++ b/src/transforms/serializer.js @@ -1,6 +1,6 @@ var [readVarInt, writeVarInt, sizeOfVarInt] = require("../datatypes/utils").varint; var protocol = require("../protocol"); -var Transform = require("stream").Transform; +var Transform = require("readable-stream").Transform; module.exports.createSerializer = function(obj) { return new Serializer(obj); From f5dda55f81166d42414f5f500ddb26d8717ae506 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 22 May 2015 19:22:21 +0000 Subject: [PATCH 4/9] Reintroduce writeRaw --- src/client.js | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/client.js b/src/client.js index 3220283..f127918 100644 --- a/src/client.js +++ b/src/client.js @@ -179,22 +179,9 @@ Client.prototype.write = function(packetId, params, cb = noop) { this.serializer.write({ packetId, params }, cb); }; -// TODO : Write to the correct stream. This is currently broken. Client.prototype.writeRaw = function(buffer) { - /*var self = this; - - var finishWriting = function(error, buffer) { - if(error) - throw error; // TODO : How do we handle this error ? - var out = self.encryptionEnabled ? new Buffer(self.cipher.update(buffer), 'binary') : buffer; - self.socket.write(out); - }; - if(this.compressionThreshold >= 0 && buffer.length >= this.compressionThreshold) { - compressPacketBuffer(buffer, finishWriting); - } else if(this.compressionThreshold >= -1) { - newStylePacket(buffer, 0, finishWriting); - } else { - oldStylePacket(buffer, finishWriting); - }*/ - throw new Error("Pending refactorisation"); + if (this.compressor === null) + this.framer.write(buffer); + else + this.compressor.write(buffer); }; From 3c0a8eee8fb5c067176618029688b5d9f1cba660 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 22 May 2015 19:24:09 +0000 Subject: [PATCH 5/9] Remove now unused functions --- src/protocol.js | 49 ------------------------------------------------- 1 file changed, 49 deletions(-) diff --git a/src/protocol.js b/src/protocol.js index 52bcad8..1b9d073 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -133,34 +133,6 @@ function createPacketBuffer(packetId, state, params, isServer) { return buffer; } -function compressPacketBuffer(buffer, callback) { - var dataLength = buffer.size; - zlib.deflate(buffer, function(err, buf) { - if(err) - callback(err); - else - newStylePacket(buf, buffer.length, callback); - }); -} - -function oldStylePacket(buffer, callback) { - var packet = new Buffer(utils.varint[2](buffer.length) + buffer.length); - var cursor = utils.varint[1](buffer.length, packet, 0); - utils.buffer[1](buffer, packet, cursor); - callback(null, packet); -} - -function newStylePacket(buffer, dataSize, callback) { - var sizeOfDataLength = utils.varint[2](dataSize); - var sizeOfLength = utils.varint[2](buffer.length + sizeOfDataLength); - var size = sizeOfLength + sizeOfDataLength + buffer.length; - var packet = new Buffer(size); - var cursor = utils.varint[1](size - sizeOfLength, packet, 0); - cursor = utils.varint[1](dataSize, packet, cursor); - utils.buffer[1](buffer, packet, cursor); - callback(null, packet); -} - // By default, parse every packets. function parsePacketData(buffer, state, isServer, packetsToParse = {"packet": true}) { var cursor = 0; @@ -228,33 +200,12 @@ function parsePacketData(buffer, state, isServer, packetsToParse = {"packet": tr }; } -function parseNewStylePacket(buffer, state, isServer, packetsToParse, cb) { - var dataLengthField = utils.varint[0](buffer, 0); - var buf = buffer.slice(dataLengthField.size); - if(dataLengthField.value != 0) { - zlib.inflate(buf, function(err, newbuf) { - if(err) { - console.log(err); - cb(err); - } else { - cb(null, parsePacketData(newbuf, state, isServer, packetsToParse)); - } - }); - } else { - cb(null, parsePacketData(buf, state, isServer, packetsToParse)); - } -} - module.exports = { version: 47, minecraftVersion: '1.8.1', sessionVersion: 13, parsePacketData: parsePacketData, - parseNewStylePacket: parseNewStylePacket, createPacketBuffer: createPacketBuffer, - compressPacketBuffer: compressPacketBuffer, - oldStylePacket: oldStylePacket, - newStylePacket: newStylePacket, packetIds: packetIds, packetNames: packetNames, packetFields: packetFields, From 2862ea20345c72f130389102ae3f57b63317de90 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 22 May 2015 19:27:58 +0000 Subject: [PATCH 6/9] Bump MINOR version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 168d562..b7dcda6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "minecraft-protocol", - "version": "0.13.5-GH", + "version": "0.14.0-GH", "description": "Parse and serialize minecraft packets, plus authentication and encryption.", "main": "index.js", "repository": { From f5192ba8b7fc39f2189aaca15716168420c4c01b Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 22 May 2015 21:32:10 +0000 Subject: [PATCH 7/9] remove debug in serializer transform --- src/transforms/serializer.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/transforms/serializer.js b/src/transforms/serializer.js index fb896ae..e92530f 100644 --- a/src/transforms/serializer.js +++ b/src/transforms/serializer.js @@ -34,11 +34,9 @@ class Deserializer extends Transform { super({ readableObjectMode: true }); this.protocolState = state; this.isServer = isServer; - this.calls = 0; } _transform(chunk, enc, cb) { - this.calls++; var packet; try { packet = protocol.parsePacketData(chunk, this.protocolState, this.isServer, this.packetsToParse); From 64f24858dd003e276094e5a6d40dfec9a05c4365 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 22 May 2015 22:24:47 +0000 Subject: [PATCH 8/9] Fix writing packets by name or [state,id] --- src/client.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/client.js b/src/client.js index f127918..9029325 100644 --- a/src/client.js +++ b/src/client.js @@ -173,6 +173,13 @@ function noop(err) { } Client.prototype.write = function(packetId, params, cb = noop) { + if(Array.isArray(packetId)) { + if(packetId[0] !== this.state) + return false; + packetId = packetId[1]; + } + if(typeof packetId === "string") + packetId = protocol.packetIds[this.state][this.isServer ? "toClient" : "toServer"][packetId]; var packetName = protocol.packetNames[this.state][this.isServer ? "toClient" : "toServer"][packetId]; debug("writing packetId " + this.state + "." + packetName + " (0x" + packetId.toString(16) + ")"); debug(params); From f6a16bfdff8f3680b342c95cfd0c6e5de0bc03cb Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 22 May 2015 22:25:12 +0000 Subject: [PATCH 9/9] Reintroduce packetsToParse --- src/client.js | 4 ++-- src/transforms/serializer.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/client.js b/src/client.js index 9029325..36ad6d9 100644 --- a/src/client.js +++ b/src/client.js @@ -23,6 +23,7 @@ function Client(isServer) { EventEmitter.call(this); var socket; + this.packetsToParse = {}; this.serializer = serializer.createSerializer({ isServer }); this.compressor = null; @@ -32,7 +33,7 @@ function Client(isServer) { this.decipher = null; this.splitter = framing.createSplitter(); this.decompressor = null; - this.deserializer = serializer.createDeserializer({ isServer }); + this.deserializer = serializer.createDeserializer({ isServer, packetsToParse: this.packetsToParse }); this._state = states.HANDSHAKING; Object.defineProperty(this, "state", { @@ -53,7 +54,6 @@ function Client(isServer) { this.isServer = !!isServer; - this.packetsToParse = {}; this.on('newListener', function(event, listener) { var direction = this.isServer ? 'toServer' : 'toClient'; if(protocol.packetStates[direction].hasOwnProperty(event) || event === "packet") { diff --git a/src/transforms/serializer.js b/src/transforms/serializer.js index e92530f..b9159bc 100644 --- a/src/transforms/serializer.js +++ b/src/transforms/serializer.js @@ -30,10 +30,11 @@ class Serializer extends Transform { } class Deserializer extends Transform { - constructor({ state = protocol.states.HANDSHAKING, isServer = false } = {}) { + constructor({ state = protocol.states.HANDSHAKING, isServer = false, packetsToParse = {"packet": true} } = {}) { super({ readableObjectMode: true }); this.protocolState = state; this.isServer = isServer; + this.packetsToParse = packetsToParse; } _transform(chunk, enc, cb) {