diff --git a/lib/client.js b/lib/client.js index 58f5bbe..eb88eb9 100644 --- a/lib/client.js +++ b/lib/client.js @@ -30,9 +30,48 @@ function Client(isServer) { this.encryptionEnabled = false; this.cipher = null; this.decipher = null; + this.packetsToParse = {}; + this.on('newListener', function(event, listener) { + var direction = this.isServer ? 'toServer' : 'toClient'; + if (protocol.packetStates[direction].hasOwnProperty(event)) { + if (typeof this.packetsToParse[event] === "undefined") this.packetsToParse[event] = 1; + else this.packetsToParse[event] += 1; + } + }); + this.on('removeListener', function(event, listener) { + var direction = this.isServer ? 'toServer' : 'toClient'; + if (protocol.packetStates[direction].hasOwnProperty(event)) { + this.packetsToParse[event] -= 1; + } + }); } + util.inherits(Client, EventEmitter); +// Transform weird "packet" types into string representing their type. Should be mostly retro-compatible +Client.prototype.on = function(type, func) { + var direction = this.isServer ? 'toServer' : 'toClient'; + if (Array.isArray(type)) { + arguments[0] = protocol.packetNames[type[0]][direction][type[1]]; + } else if (typeof type === "number") { + arguments[0] = protocol.packetNames[this.state][direction][type]; + } + EventEmitter.prototype.on.apply(this, arguments); +}; + +Client.prototype.onRaw = function(type, func) { + var arg = "raw."; + if (Array.isArray(type)) { + arg += protocol.packetNames[type[0]][direction][type[1]]; + } else if (typeof type === "number") { + arg += protocol.packetNames[this.state][direction][type]; + } else { + arg += type; + } + arguments[0] = arg; + EventEmitter.prototype.on.apply(this, arguments); +}; + Client.prototype.setSocket = function(socket) { var self = this; self.socket = socket; @@ -42,7 +81,7 @@ Client.prototype.setSocket = function(socket) { incomingBuffer = Buffer.concat([incomingBuffer, data]); var parsed, packet; while (true) { - parsed = parsePacket(incomingBuffer, self.state, self.isServer); + parsed = parsePacket(incomingBuffer, self.state, self.isServer, self.packetsToParse); if (! parsed) break; if (parsed.error) { this.emit('error', parsed.error); @@ -53,11 +92,8 @@ Client.prototype.setSocket = function(socket) { incomingBuffer = incomingBuffer.slice(parsed.size); var packetName = protocol.packetNames[self.state][self.isServer ? 'toServer' : 'toClient'][packet.id]; - if (packetName !== undefined) self.emit(packetName, packet); - - self.emit([self.state, packet.id], packet); - self.emit(packet.id, packet); - self.emit('packet', packet); + self.emit(packetName, packet); + self.emit('raw.' + packetName, parsed.buffer); } }); @@ -120,3 +156,11 @@ Client.prototype.write = function(packetId, params) { this.socket.write(out); return true; }; + +Client.prototype.writeRaw = function(buffer, shouldEncrypt) { + if (shouldEncrypt === null) { + shouldEncrypt = true; + } + var out = (shouldEncrypt && this.encryptionEnabled) ? new Buffer(this.cipher.update(buffer), 'binary') : buffer; + this.socket.write(out); +}; \ No newline at end of file diff --git a/lib/protocol.js b/lib/protocol.js index 62ddc80..d20e85e 100644 --- a/lib/protocol.js +++ b/lib/protocol.js @@ -1270,7 +1270,7 @@ function createPacketBuffer(packetId, state, params, isServer) { return buffer; } -function parsePacket(buffer, state, isServer) { +function parsePacket(buffer, state, isServer, packetsToParse) { if (state == null) state == states.PLAY; var cursor = 0; var lengthField = readVarInt(buffer, 0); @@ -1279,19 +1279,30 @@ function parsePacket(buffer, state, isServer) { cursor += lengthField.size; if (length + lengthField.size > buffer.length) return null; var buffer = buffer.slice(0, length + cursor); // fail early if too much is read. - - var packetIdField = readVarInt(buffer, lengthField.size); + + var packetIdField = readVarInt(buffer, cursor); var packetId = packetIdField.value; cursor += packetIdField.size; - + var results = { id: packetId }; + // Only parse the packet if there is a need for it, AKA if there is a listener attached to it + var name = packetNames[state][isServer ? "toServer" : "toClient"][packetId]; + if (!packetsToParse.hasOwnProperty(name) || packetsToParse[name] <= 0) { + return { + size: length + lengthField.size, + buffer: buffer, + results: results + }; + } + var packetInfo = get(packetId, state, isServer); if (packetInfo === null) { return { error: new Error("Unrecognized packetId: " + packetId + " (0x" + packetId.toString(16) + ")"), size: length + lengthField.size, + buffer: buffer, results: results - } + }; } else { debug("read packetId " + packetId + " (0x" + packetId.toString(16) + ")"); } @@ -1323,6 +1334,7 @@ function parsePacket(buffer, state, isServer) { return { size: length + lengthField.size, results: results, + buffer: buffer }; } @@ -1336,6 +1348,7 @@ module.exports = { packetIds: packetIds, packetNames: packetNames, packetFields: packetFields, + packetStates: packetStates, states: states, get: get, debug: debug,