From 6660a3aa39628e1fa8f7a582dd3ab57663c0589f Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 13 May 2015 02:04:26 +0200 Subject: [PATCH 01/12] put getField in a utils.js file, create a datatypes dir in src/ make it compile by gulp, move byte to numeric.js --- gulpfile.js | 7 +++++++ src/datatypes/numeric.js | 16 ++++++++++++++++ src/protocol.js | 33 ++++++++------------------------- src/utils.js | 10 ++++++++++ 4 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 src/datatypes/numeric.js create mode 100644 src/utils.js diff --git a/gulpfile.js b/gulpfile.js index 2a84d799..48595f8c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -14,6 +14,13 @@ gulp.task('compile', function() { .pipe(babel(options)) .pipe(sourcemaps.write('maps/')) .pipe(gulp.dest('dist/')); + + gulp + .src('src/datatypes/*.js') + .pipe(sourcemaps.init()) + .pipe(babel(options)) + .pipe(sourcemaps.write('maps/')) + .pipe(gulp.dest('dist/datatypes/')); }); gulp.task('watch', function() { diff --git a/src/datatypes/numeric.js b/src/datatypes/numeric.js new file mode 100644 index 00000000..372abd6d --- /dev/null +++ b/src/datatypes/numeric.js @@ -0,0 +1,16 @@ +module.exports={'byte': [readByte, writeByte, 1]}; + +function readByte(buffer, offset) { + if (offset + 1 > buffer.length) return null; + var value = buffer.readInt8(offset); + return { + value: value, + size: 1, + }; +} + + +function writeByte(value, buffer, offset) { + buffer.writeInt8(value, offset); + return offset + 1; +} diff --git a/src/protocol.js b/src/protocol.js index 54444476..d3cddb42 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -3,6 +3,8 @@ var util = require('util'); var zlib = require('zlib'); var nbt = require('prismarine-nbt'); +var getField= require("./utils").getField; + var STRING_MAX_LENGTH = 240; var SRV_STRING_MAX_LENGTH = 32767; @@ -50,10 +52,10 @@ var packetStates = {toClient: {}, toServer: {}}; } })(); - +var numeric=require("./datatypes/numeric"); var types = { - 'byte': [readByte, writeByte, 1], + 'byte': numeric.byte, 'ubyte': [readUByte, writeUByte, 1], 'short': [readShort, writeShort, 2], 'ushort': [readUShort, writeUShort, 2], @@ -314,14 +316,7 @@ function readLong(buffer, offset) { }; } -function readByte(buffer, offset) { - if (offset + 1 > buffer.length) return null; - var value = buffer.readInt8(offset); - return { - value: value, - size: 1, - }; -} + function readUByte(buffer, offset) { if (offset + 1 > buffer.length) return null; @@ -448,11 +443,6 @@ function writeString(value, buffer, offset) { return offset + length; } -function writeByte(value, buffer, offset) { - buffer.writeInt8(value, offset); - return offset + 1; -} - function writeBool(value, buffer, offset) { buffer.writeInt8(+value, offset); return offset + 1; @@ -606,6 +596,7 @@ function readRestBuffer(buffer, offset, typeArgs, rootNode) { }; } +// begin array function evalCount(count,fields) { if(fields[count["field"]] in count["map"]) @@ -623,7 +614,7 @@ function readArray(buffer, offset, typeArgs, rootNode) { count = evalCount(typeArgs.count,rootNode); } else - count = getField(typeArgs.count, rootNode); + count = getField(typeArgs.count, rootNode); for (var i = 0; i < count; i++) { var readResults = read(buffer, offset, { type: typeArgs.type, typeArgs: typeArgs.typeArgs }, rootNode); results.size += readResults.size; @@ -647,15 +638,7 @@ function sizeOfArray(value, typeArgs, rootNode) { } return size; } - -function getField(countField, rootNode) { - var countFieldArr = countField.split("."); - var count = rootNode; - for (var index = 0; index < countFieldArr.length; index++) { - count = count[countFieldArr[index]]; - } - return count; -} +// end array function readCount(buffer, offset, typeArgs, rootNode) { return read(buffer, offset, { type: typeArgs.type }, rootNode); diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 00000000..f92ef5a3 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,10 @@ +module.exports= {getField:getField}; + +function getField(countField, rootNode) { + var countFieldArr = countField.split("."); + var count = rootNode; + for (var index = 0; index < countFieldArr.length; index++) { + count = count[countFieldArr[index]]; + } + return count; +} From 355a3cb1cd7c3fa4adb844580ea45656109ee34f Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 13 May 2015 02:19:56 +0200 Subject: [PATCH 02/12] more numeric types move --- src/datatypes/numeric.js | 115 ++++++++++++++++++++++++++++++++++++- src/protocol.js | 120 ++++----------------------------------- 2 files changed, 123 insertions(+), 112 deletions(-) diff --git a/src/datatypes/numeric.js b/src/datatypes/numeric.js index 372abd6d..1d440a0e 100644 --- a/src/datatypes/numeric.js +++ b/src/datatypes/numeric.js @@ -1,4 +1,13 @@ -module.exports={'byte': [readByte, writeByte, 1]}; +module.exports={ + 'byte': [readByte, writeByte, 1], + 'ubyte': [readUByte, writeUByte, 1], + 'short': [readShort, writeShort, 2], + 'ushort': [readUShort, writeUShort, 2], + 'int': [readInt, writeInt, 4], + 'long': [readLong, writeLong, 8], + 'float': [readFloat, writeFloat, 4], + 'double': [readDouble, writeDouble, 8] +}; function readByte(buffer, offset) { if (offset + 1 > buffer.length) return null; @@ -9,8 +18,110 @@ function readByte(buffer, offset) { }; } - function writeByte(value, buffer, offset) { buffer.writeInt8(value, offset); return offset + 1; } + +function readUByte(buffer, offset) { + if (offset + 1 > buffer.length) return null; + var value = buffer.readUInt8(offset); + return { + value: value, + size: 1, + }; +} + +function writeUByte(value, buffer, offset) { + buffer.writeUInt8(value, offset); + return offset + 1; +} + +function readFloat(buffer, offset) { + if (offset + 4 > buffer.length) return null; + var value = buffer.readFloatBE(offset); + return { + value: value, + size: 4, + }; +} + +function writeFloat(value, buffer, offset) { + buffer.writeFloatBE(value, offset); + return offset + 4; +} + +function readDouble(buffer, offset) { + if (offset + 8 > buffer.length) return null; + var value = buffer.readDoubleBE(offset); + return { + value: value, + size: 8, + }; +} + +function writeDouble(value, buffer, offset) { + buffer.writeDoubleBE(value, offset); + return offset + 8; +} + + +function readShort(buffer, offset) { + if (offset + 2 > buffer.length) return null; + var value = buffer.readInt16BE(offset); + return { + value: value, + size: 2, + }; +} +function writeShort(value, buffer, offset) { + buffer.writeInt16BE(value, offset); + return offset + 2; +} + +function readUShort(buffer, offset) { + if (offset + 2 > buffer.length) return null; + var value = buffer.readUInt16BE(offset); + return { + value: value, + size: 2, + }; +} + +function writeUShort(value, buffer, offset) { + buffer.writeUInt16BE(value, offset); + return offset + 2; +} + +function readInt(buffer, offset) { + if (offset + 4 > buffer.length) return null; + var value = buffer.readInt32BE(offset); + return { + value: value, + size: 4, + }; +} + +function writeInt(value, buffer, offset) { + buffer.writeInt32BE(value, offset); + return offset + 4; +} + + +function readLong(buffer, offset) { + if (offset + 8 > buffer.length) return null; + return { + value: [buffer.readInt32BE(offset), buffer.readInt32BE(offset + 4)], + size: 8, + }; +} + +function writeLong(value, buffer, offset) { + buffer.writeInt32BE(value[0], offset); + buffer.writeInt32BE(value[1], offset + 4); + return offset + 8; +} + + + + diff --git a/src/protocol.js b/src/protocol.js index d3cddb42..ecaeec5a 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -56,14 +56,14 @@ var numeric=require("./datatypes/numeric"); var types = { 'byte': numeric.byte, - 'ubyte': [readUByte, writeUByte, 1], - 'short': [readShort, writeShort, 2], - 'ushort': [readUShort, writeUShort, 2], - 'int': [readInt, writeInt, 4], - 'long': [readLong, writeLong, 8], + 'ubyte':numeric.ubyte, + 'short': numeric.short, + 'ushort': numeric.ushort, + 'int': numeric.int, + 'long': numeric.long, 'varint': [readVarInt, writeVarInt, sizeOfVarInt], - 'float': [readFloat, writeFloat, 4], - 'double': [readDouble, writeDouble, 8], + 'float': numeric.float, + 'double': numeric.double, 'bool': [readBool, writeBool, 1], 'string': [readString, writeString, sizeOfString], 'ustring': [readString, writeString, sizeOfUString], // TODO : remove ustring @@ -263,70 +263,6 @@ function readUUID(buffer, offset) { }; } -function readShort(buffer, offset) { - if (offset + 2 > buffer.length) return null; - var value = buffer.readInt16BE(offset); - return { - value: value, - size: 2, - }; -} - -function readUShort(buffer, offset) { - if (offset + 2 > buffer.length) return null; - var value = buffer.readUInt16BE(offset); - return { - value: value, - size: 2, - }; -} - -function readInt(buffer, offset) { - if (offset + 4 > buffer.length) return null; - var value = buffer.readInt32BE(offset); - return { - value: value, - size: 4, - }; -} - -function readFloat(buffer, offset) { - if (offset + 4 > buffer.length) return null; - var value = buffer.readFloatBE(offset); - return { - value: value, - size: 4, - }; -} - -function readDouble(buffer, offset) { - if (offset + 8 > buffer.length) return null; - var value = buffer.readDoubleBE(offset); - return { - value: value, - size: 8, - }; -} - -function readLong(buffer, offset) { - if (offset + 8 > buffer.length) return null; - return { - value: [buffer.readInt32BE(offset), buffer.readInt32BE(offset + 4)], - size: 8, - }; -} - - - -function readUByte(buffer, offset) { - if (offset + 1 > buffer.length) return null; - var value = buffer.readUInt8(offset); - return { - value: value, - size: 1, - }; -} - function readBool(buffer, offset) { if (offset + 1 > buffer.length) return null; var value = buffer.readInt8(offset); @@ -337,7 +273,7 @@ function readBool(buffer, offset) { } function readPosition(buffer, offset) { - var longVal = readLong(buffer, offset).value; + var longVal = numeric.long[0](buffer, offset).value; var x = signExtend26(longVal[0] >> 6); var y = signExtend12(((longVal[0] & 0x3f) << 6) | ((longVal[1] >> 26) & 0x3f)); var z = signExtend26(longVal[1] & 0x3FFFFFF); @@ -357,7 +293,7 @@ function signExtend12(value) { function readSlot(buffer, offset) { var value = {}; - var results = readShort(buffer, offset); + var results = numeric.short[0](buffer, offset); if (! results) return null; value.blockId = results.value; @@ -401,7 +337,7 @@ function writePosition(value, buffer, offset) { var longVal = []; longVal[0] = ((value.x & 0x3FFFFFF) << 6) | ((value.y & 0xFFF) >> 6); longVal[1] = ((value.y & 0x3F) << 26) | (value.z & 0x3FFFFFF); - return writeLong(longVal, buffer, offset); + return numeric.long[1](longVal, buffer, offset); } function writeSlot(value, buffer, offset) { @@ -448,42 +384,6 @@ function writeBool(value, buffer, offset) { return offset + 1; } -function writeUByte(value, buffer, offset) { - buffer.writeUInt8(value, offset); - return offset + 1; -} - -function writeFloat(value, buffer, offset) { - buffer.writeFloatBE(value, offset); - return offset + 4; -} - -function writeDouble(value, buffer, offset) { - buffer.writeDoubleBE(value, offset); - return offset + 8; -} - -function writeShort(value, buffer, offset) { - buffer.writeInt16BE(value, offset); - return offset + 2; -} - -function writeUShort(value, buffer, offset) { - buffer.writeUInt16BE(value, offset); - return offset + 2; -} - -function writeInt(value, buffer, offset) { - buffer.writeInt32BE(value, offset); - return offset + 4; -} - -function writeLong(value, buffer, offset) { - buffer.writeInt32BE(value[0], offset); - buffer.writeInt32BE(value[1], offset + 4); - return offset + 8; -} - function readVarInt(buffer, offset) { var result = 0; var shift = 0; From 6f460e1626d5f42db09ba2f5bd0e512100b4b4c4 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 13 May 2015 02:43:52 +0200 Subject: [PATCH 03/12] move varint, bool, string, ustring and buffer to utils.js --- src/datatypes/utils.js | 125 +++++++++++++++++++++++++++++++++ src/protocol.js | 152 +++++++---------------------------------- 2 files changed, 148 insertions(+), 129 deletions(-) create mode 100644 src/datatypes/utils.js diff --git a/src/datatypes/utils.js b/src/datatypes/utils.js new file mode 100644 index 00000000..70b94bd3 --- /dev/null +++ b/src/datatypes/utils.js @@ -0,0 +1,125 @@ +var assert = require('assert'); + +var STRING_MAX_LENGTH = 240; +var SRV_STRING_MAX_LENGTH = 32767; +var getField= require("../utils").getField; + +module.exports = { + 'varint': [readVarInt, writeVarInt, sizeOfVarInt], + 'bool': [readBool, writeBool, 1], + 'string': [readString, writeString, sizeOfString], + 'ustring': [readString, writeString, sizeOfUString], // TODO : remove ustring + 'buffer': [readBuffer, writeBuffer, sizeOfBuffer] +}; + +function readVarInt(buffer, offset) { + var result = 0; + var shift = 0; + var cursor = offset; + + while (true) { + if (cursor + 1 > buffer.length) return null; + var b = buffer.readUInt8(cursor); + result |= ((b & 0x7f) << shift); // Add the bits to our number, except MSB + cursor++; + if (!(b & 0x80)) { // If the MSB is not set, we return the number + return { + value: result, + size: cursor - offset + }; + } + shift += 7; // we only have 7 bits, MSB being the return-trigger + assert.ok(shift < 64, "varint is too big"); // Make sure our shift don't overflow. + } +} + +function sizeOfVarInt(value) { + var cursor = 0; + while (value & ~0x7F) { + value >>>= 7; + cursor++; + } + return cursor + 1; +} + +function writeVarInt(value, buffer, offset) { + var cursor = 0; + while (value & ~0x7F) { + buffer.writeUInt8((value & 0xFF) | 0x80, offset + cursor); + cursor++; + value >>>= 7; + } + buffer.writeUInt8(value, offset + cursor); + return offset + cursor + 1; +} + + +function readString (buffer, offset) { + var length = readVarInt(buffer, offset); + if (!!!length) return null; + var cursor = offset + length.size; + var stringLength = length.value; + var strEnd = cursor + stringLength; + if (strEnd > buffer.length) return null; + + var value = buffer.toString('utf8', cursor, strEnd); + cursor = strEnd; + + return { + value: value, + size: cursor - offset, + }; +} + +function writeString(value, buffer, offset) { + var length = Buffer.byteLength(value, 'utf8'); + offset = writeVarInt(length, buffer, offset); + buffer.write(value, offset, length, 'utf8'); + return offset + length; +} + + +function sizeOfString(value) { + var length = Buffer.byteLength(value, 'utf8'); + assert.ok(length < STRING_MAX_LENGTH, "string greater than max length"); + return sizeOfVarInt(length) + length; +} + +function readBool(buffer, offset) { + if (offset + 1 > buffer.length) return null; + var value = buffer.readInt8(offset); + return { + value: !!value, + size: 1, + }; +} + +function writeBool(value, buffer, offset) { + buffer.writeInt8(+value, offset); + return offset + 1; +} + + +function readBuffer(buffer, offset, typeArgs, rootNode) { + var count = getField(typeArgs.count, rootNode); + return { + value: buffer.slice(offset, offset + count), + size: count + }; +} + +function writeBuffer(value, buffer, offset) { + value.copy(buffer, offset); + return offset + value.length; +} + +function sizeOfBuffer(value) { + return value.length; +} + + +function sizeOfUString(value) { + var length = Buffer.byteLength(value, 'utf8'); + assert.ok(length < SRV_STRING_MAX_LENGTH, "string greater than max length"); + return sizeOfVarInt(length) + length; +} diff --git a/src/protocol.js b/src/protocol.js index ecaeec5a..d7fb5c45 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -6,7 +6,6 @@ var nbt = require('prismarine-nbt'); var getField= require("./utils").getField; var STRING_MAX_LENGTH = 240; -var SRV_STRING_MAX_LENGTH = 32767; // This is really just for the client. var states = { @@ -53,6 +52,7 @@ var packetStates = {toClient: {}, toServer: {}}; })(); var numeric=require("./datatypes/numeric"); +var utils=require("./datatypes/utils"); var types = { 'byte': numeric.byte, @@ -61,22 +61,22 @@ var types = { 'ushort': numeric.ushort, 'int': numeric.int, 'long': numeric.long, - 'varint': [readVarInt, writeVarInt, sizeOfVarInt], + 'varint': utils.varint, 'float': numeric.float, 'double': numeric.double, - 'bool': [readBool, writeBool, 1], - 'string': [readString, writeString, sizeOfString], - 'ustring': [readString, writeString, sizeOfUString], // TODO : remove ustring + 'bool': utils.bool, + 'string': utils.string, + 'ustring': utils.ustring, // TODO : remove ustring 'UUID': [readUUID, writeUUID, 16], 'container': [readContainer, writeContainer, sizeOfContainer], 'array': [readArray, writeArray, sizeOfArray], - 'buffer': [readBuffer, writeBuffer, sizeOfBuffer], - 'restBuffer': [readRestBuffer, writeBuffer, sizeOfBuffer], + 'buffer': utils.buffer, + 'restBuffer': [readRestBuffer, utils.buffer[1], utils.buffer[2]], 'count': [readCount, writeCount, sizeOfCount], // TODO : remove type-specific, replace with generic containers and arrays. 'position': [readPosition, writePosition, 8], 'slot': [readSlot, writeSlot, sizeOfSlot], - 'nbt': [readNbt, writeBuffer, sizeOfBuffer], + 'nbt': [readNbt, utils.buffer[1], utils.buffer[2]], 'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata], 'condition': [readCondition, writeCondition, sizeOfCondition] }; @@ -234,23 +234,6 @@ function sizeOfNbt(value) { return nbt.writeUncompressed(value).length; } -function readString (buffer, offset) { - var length = readVarInt(buffer, offset); - if (!!!length) return null; - var cursor = offset + length.size; - var stringLength = length.value; - var strEnd = cursor + stringLength; - if (strEnd > buffer.length) return null; - - var value = buffer.toString('utf8', cursor, strEnd); - cursor = strEnd; - - return { - value: value, - size: cursor - offset, - }; -} - function readUUID(buffer, offset) { return { value: [ @@ -263,15 +246,6 @@ function readUUID(buffer, offset) { }; } -function readBool(buffer, offset) { - if (offset + 1 > buffer.length) return null; - var value = buffer.readInt8(offset); - return { - value: !!value, - size: 1, - }; -} - function readPosition(buffer, offset) { var longVal = numeric.long[0](buffer, offset).value; var x = signExtend26(longVal[0] >> 6); @@ -360,70 +334,6 @@ function writeSlot(value, buffer, offset) { return offset + 5 + nbtDataLen; } -function sizeOfString(value) { - var length = Buffer.byteLength(value, 'utf8'); - assert.ok(length < STRING_MAX_LENGTH, "string greater than max length"); - return sizeOfVarInt(length) + length; -} - -function sizeOfUString(value) { - var length = Buffer.byteLength(value, 'utf8'); - assert.ok(length < SRV_STRING_MAX_LENGTH, "string greater than max length"); - return sizeOfVarInt(length) + length; -} - -function writeString(value, buffer, offset) { - var length = Buffer.byteLength(value, 'utf8'); - offset = writeVarInt(length, buffer, offset); - buffer.write(value, offset, length, 'utf8'); - return offset + length; -} - -function writeBool(value, buffer, offset) { - buffer.writeInt8(+value, offset); - return offset + 1; -} - -function readVarInt(buffer, offset) { - var result = 0; - var shift = 0; - var cursor = offset; - - while (true) { - if (cursor + 1 > buffer.length) return null; - var b = buffer.readUInt8(cursor); - result |= ((b & 0x7f) << shift); // Add the bits to our number, except MSB - cursor++; - if (!(b & 0x80)) { // If the MSB is not set, we return the number - return { - value: result, - size: cursor - offset - }; - } - shift += 7; // we only have 7 bits, MSB being the return-trigger - assert.ok(shift < 64, "varint is too big"); // Make sure our shift don't overflow. - } -} - -function sizeOfVarInt(value) { - var cursor = 0; - while (value & ~0x7F) { - value >>>= 7; - cursor++; - } - return cursor + 1; -} - -function writeVarInt(value, buffer, offset) { - var cursor = 0; - while (value & ~0x7F) { - buffer.writeUInt8((value & 0xFF) | 0x80, offset + cursor); - cursor++; - value >>>= 7; - } - buffer.writeUInt8(value, offset + cursor); - return offset + cursor + 1; -} function readContainer(buffer, offset, typeArgs, rootNode) { var results = { @@ -472,22 +382,6 @@ function sizeOfContainer(value, typeArgs, rootNode) { return size; } -function readBuffer(buffer, offset, typeArgs, rootNode) { - var count = getField(typeArgs.count, rootNode); - return { - value: buffer.slice(offset, offset + count), - size: count - }; -} - -function writeBuffer(value, buffer, offset) { - value.copy(buffer, offset); - return offset + value.length; -} - -function sizeOfBuffer(value) { - return value.length; -} function readRestBuffer(buffer, offset, typeArgs, rootNode) { return { @@ -623,11 +517,11 @@ function createPacketBuffer(packetId, state, params, isServer) { throw e; } }); - length += sizeOfVarInt(packetId); - var size = length;// + sizeOfVarInt(length); + length += utils.varint[2](packetId); + var size = length;// + utils.varint[2](length); var buffer = new Buffer(size); - var offset = 0;//writeVarInt(length, buffer, 0); - offset = writeVarInt(packetId, buffer, offset); + var offset = 0;//utils.varint[1](length, buffer, 0); + offset = utils.varint[1](packetId, buffer, offset); packet.forEach(function(fieldInfo) { var value = params[fieldInfo.name]; // TODO : A better check is probably needed @@ -649,26 +543,26 @@ function compressPacketBuffer(buffer, callback) { } function oldStylePacket(buffer, callback) { - var packet = new Buffer(sizeOfVarInt(buffer.length) + buffer.length); - var cursor = writeVarInt(buffer.length, packet, 0); - writeBuffer(buffer, packet, cursor); + 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, callback) { - var sizeOfDataLength = sizeOfVarInt(0); - var sizeOfLength = sizeOfVarInt(buffer.length + sizeOfDataLength); + var sizeOfDataLength = utils.varint[2](0); + var sizeOfLength = utils.varint[2](buffer.length + sizeOfDataLength); var size = sizeOfLength + sizeOfDataLength + buffer.length; var packet = new Buffer(size); - var cursor = writeVarInt(size - sizeOfLength, packet, 0); - cursor = writeVarInt(0, packet, cursor); - writeBuffer(buffer, packet, cursor); + var cursor = utils.varint[1](size - sizeOfLength, packet, 0); + cursor = utils.varint[1](0, packet, cursor); + utils.buffer[1](buffer, packet, cursor); callback(null, packet); } function parsePacketData(buffer, state, isServer, packetsToParse) { var cursor = 0; - var packetIdField = readVarInt(buffer, cursor); + var packetIdField = utils.varint[0](buffer, cursor); var packetId = packetIdField.value; cursor += packetIdField.size; @@ -731,7 +625,7 @@ function parsePacketData(buffer, state, isServer, packetsToParse) { function parsePacket(buffer, state, isServer, packetsToParse) { if (state == null) state = states.PLAY; var cursor = 0; - var lengthField = readVarInt(buffer, 0); + var lengthField = utils.varint[0](buffer, 0); if (!!!lengthField) return null; var length = lengthField.value; cursor += lengthField.size; @@ -742,7 +636,7 @@ function parsePacket(buffer, state, isServer, packetsToParse) { } function parseNewStylePacket(buffer, state, isServer, packetsToParse, cb) { - var dataLengthField = readVarInt(buffer, 0); + var dataLengthField = utils.varint[0](buffer, 0); var buf = buffer.slice(dataLengthField.size); if(dataLengthField.value != 0) { zlib.inflate(buf, function(err, newbuf) { From 3b7ae54b88344c5404ac52e084797a74be53f6d7 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 13 May 2015 02:58:07 +0200 Subject: [PATCH 04/12] add minecraft datatype file for minecraft specific packets --- src/datatypes/minecraft.js | 138 ++++++++++++++++++++++ src/protocol.js | 226 +++++++++---------------------------- 2 files changed, 192 insertions(+), 172 deletions(-) create mode 100644 src/datatypes/minecraft.js diff --git a/src/datatypes/minecraft.js b/src/datatypes/minecraft.js new file mode 100644 index 00000000..5907b2ef --- /dev/null +++ b/src/datatypes/minecraft.js @@ -0,0 +1,138 @@ +var nbt = require('prismarine-nbt'); +var utils = require("./utils"); +var numeric = require("./numeric"); + +module.exports = { + 'UUID': [readUUID, writeUUID, 16], + 'position': [readPosition, writePosition, 8], + 'slot': [readSlot, writeSlot, sizeOfSlot], + 'nbt': [readNbt, utils.buffer[1], utils.buffer[2]] +}; + +function readUUID(buffer, offset) { + return { + value: [ + buffer.readUInt32BE(offset), + buffer.readUInt32BE(offset + 4), + buffer.readUInt32BE(offset + 8), + buffer.readUInt32BE(offset + 12), + ], + size: 16, + }; +} + +function writeUUID(value, buffer, offset) { + buffer.writeUInt32BE(value[0], offset); + buffer.writeUInt32BE(value[1], offset + 4); + buffer.writeUInt32BE(value[2], offset + 8); + buffer.writeUInt32BE(value[3], offset + 12); + return offset + 16; +} + + +function readPosition(buffer, offset) { + var longVal = numeric.long[0](buffer, offset).value; + var x = signExtend26(longVal[0] >> 6); + var y = signExtend12(((longVal[0] & 0x3f) << 6) | ((longVal[1] >> 26) & 0x3f)); + var z = signExtend26(longVal[1] & 0x3FFFFFF); + return { + value: { x: x, y: y, z: z }, + size: 8 + }; +} +function signExtend26(value) { + if (value > 0x2000000) value -= 0x4000000; + return value; +} +function signExtend12(value) { + if (value > 0x800) value -= 0x1000; + return value; +} + + +function writePosition(value, buffer, offset) { + var longVal = []; + longVal[0] = ((value.x & 0x3FFFFFF) << 6) | ((value.y & 0xFFF) >> 6); + longVal[1] = ((value.y & 0x3F) << 26) | (value.z & 0x3FFFFFF); + return numeric.long[1](longVal, buffer, offset); +} + + +function readSlot(buffer, offset) { + var value = {}; + var results = numeric.short[0](buffer, offset); + if (! results) return null; + value.blockId = results.value; + + if (value.blockId === -1) { + return { + value: value, + size: 2, + }; + } + + var cursorEnd = offset + 6; + if (cursorEnd > buffer.length) return null; + value.itemCount = buffer.readInt8(offset + 2); + value.itemDamage = buffer.readInt16BE(offset + 3); + var nbtData = buffer.readInt8(offset + 5); + if (nbtData == 0) { + return { + value: value, + size: 6 + } + } + var nbtData = readNbt(buffer, offset + 5); + value.nbtData = nbtData.value; + return { + value: value, + size: nbtData.size + 5 + }; +} + +function writeSlot(value, buffer, offset) { + buffer.writeInt16BE(value.blockId, offset); + if (value.blockId === -1) return offset + 2; + buffer.writeInt8(value.itemCount, offset + 2); + buffer.writeInt16BE(value.itemDamage, offset + 3); + var nbtDataLen; + if (value.nbtData) + { + var newbuf = nbt.writeUncompressed(value.nbtData); + newbuf.copy(buffer, offset + 5); + nbtDataLen = newbuf.length; + } + else + { + buffer.writeInt8(0, offset + 5); + nbtDataLen = 1; + } + return offset + 5 + nbtDataLen; +} + +function sizeOfSlot(value) { + if (value.blockId === -1) + return (2); + else if (!value.nbtData) { + return (6); + } else { + return (5 + sizeOfNbt(value.nbtData)); + } +} + + +function readNbt(buffer, offset) { + buffer = buffer.slice(offset); + return nbt.parseUncompressed(buffer); +} + +function writeNbt(value, buffer, offset) { + var newbuf = nbt.writeUncompressed(value); + newbuf.copy(buffer, offset); + return offset + newbuf.length; +} + +function sizeOfNbt(value) { + return nbt.writeUncompressed(value).length; +} + diff --git a/src/protocol.js b/src/protocol.js index d7fb5c45..d910435d 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -1,7 +1,6 @@ var assert = require('assert'); var util = require('util'); var zlib = require('zlib'); -var nbt = require('prismarine-nbt'); var getField= require("./utils").getField; @@ -53,6 +52,7 @@ var packetStates = {toClient: {}, toServer: {}}; var numeric=require("./datatypes/numeric"); var utils=require("./datatypes/utils"); +var minecraft=require("./datatypes/minecraft"); var types = { 'byte': numeric.byte, @@ -66,17 +66,17 @@ var types = { 'double': numeric.double, 'bool': utils.bool, 'string': utils.string, - 'ustring': utils.ustring, // TODO : remove ustring - 'UUID': [readUUID, writeUUID, 16], + 'ustring': utils.ustring, + 'UUID': minecraft.UUID, 'container': [readContainer, writeContainer, sizeOfContainer], 'array': [readArray, writeArray, sizeOfArray], 'buffer': utils.buffer, 'restBuffer': [readRestBuffer, utils.buffer[1], utils.buffer[2]], 'count': [readCount, writeCount, sizeOfCount], // TODO : remove type-specific, replace with generic containers and arrays. - 'position': [readPosition, writePosition, 8], - 'slot': [readSlot, writeSlot, sizeOfSlot], - 'nbt': [readNbt, utils.buffer[1], utils.buffer[2]], + 'position': minecraft.position, + 'slot': minecraft.slot, + 'nbt': minecraft.nbt, 'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata], 'condition': [readCondition, writeCondition, sizeOfCondition] }; @@ -154,184 +154,66 @@ function evalCondition(condition,field_values) return b; } -function sizeOfEntityMetadata(value) { - var size = 1 + value.length; - var item; - for (var i = 0; i < value.length; ++i) { - item = value[i]; - size += sizeOf(item.value, entityMetadataTypes[entityMetadataTypeBytes[item.type]], {}); - } - return size; -} - -function writeEntityMetadata(value, buffer, offset) { - value.forEach(function(item) { - var type = entityMetadataTypeBytes[item.type]; - var headerByte = (type << 5) | item.key; - buffer.writeUInt8(headerByte, offset); - offset += 1; - offset = write(item.value, buffer, offset, entityMetadataTypes[type], {}); - }); - buffer.writeUInt8(0x7f, offset); - return offset + 1; -} - -function writeUUID(value, buffer, offset) { - buffer.writeUInt32BE(value[0], offset); - buffer.writeUInt32BE(value[1], offset + 4); - buffer.writeUInt32BE(value[2], offset + 8); - buffer.writeUInt32BE(value[3], offset + 12); - return offset + 16; -} function readEntityMetadata(buffer, offset) { - var cursor = offset; - var metadata = []; - var item, key, type, results, reader, typeName, dataType; - while (true) { - if (cursor + 1 > buffer.length) return null; - item = buffer.readUInt8(cursor); - cursor += 1; - if (item === 0x7f) { - return { - value: metadata, - size: cursor - offset, - }; - } - key = item & 0x1f; - type = item >> 5; - dataType = entityMetadataTypes[type]; - typeName = dataType.type; - //debug("Reading entity metadata type " + dataType + " (" + ( typeName || "unknown" ) + ")"); - if (!dataType) { - return { - error: new Error("unrecognized entity metadata type " + type) - } + var cursor = offset; + var metadata = []; + var item, key, type, results, reader, typeName, dataType; + while (true) { + if (cursor + 1 > buffer.length) return null; + item = buffer.readUInt8(cursor); + cursor += 1; + if (item === 0x7f) { + return { + value: metadata, + size: cursor - offset, + }; + } + key = item & 0x1f; + type = item >> 5; + dataType = entityMetadataTypes[type]; + typeName = dataType.type; + //debug("Reading entity metadata type " + dataType + " (" + ( typeName || "unknown" ) + ")"); + if (!dataType) { + return { + error: new Error("unrecognized entity metadata type " + type) + } + } + results = read(buffer, cursor, dataType, {}); + if (! results) return null; + metadata.push({ + key: key, + value: results.value, + type: typeName, + }); + cursor += results.size; } - results = read(buffer, cursor, dataType, {}); - if (! results) return null; - metadata.push({ - key: key, - value: results.value, - type: typeName, - }); - cursor += results.size; - } } -function readNbt(buffer, offset) { - buffer = buffer.slice(offset); - return nbt.parseUncompressed(buffer); -} - -function writeNbt(value, buffer, offset) { - var newbuf = nbt.writeUncompressed(value); - newbuf.copy(buffer, offset); - return offset + newbuf.length; -} -function sizeOfNbt(value) { - return nbt.writeUncompressed(value).length; -} - -function readUUID(buffer, offset) { - return { - value: [ - buffer.readUInt32BE(offset), - buffer.readUInt32BE(offset + 4), - buffer.readUInt32BE(offset + 8), - buffer.readUInt32BE(offset + 12), - ], - size: 16, - }; -} -function readPosition(buffer, offset) { - var longVal = numeric.long[0](buffer, offset).value; - var x = signExtend26(longVal[0] >> 6); - var y = signExtend12(((longVal[0] & 0x3f) << 6) | ((longVal[1] >> 26) & 0x3f)); - var z = signExtend26(longVal[1] & 0x3FFFFFF); - return { - value: { x: x, y: y, z: z }, - size: 8 - }; -} -function signExtend26(value) { - if (value > 0x2000000) value -= 0x4000000; - return value; -} -function signExtend12(value) { - if (value > 0x800) value -= 0x1000; - return value; +function writeEntityMetadata(value, buffer, offset) { + value.forEach(function(item) { + var type = entityMetadataTypeBytes[item.type]; + var headerByte = (type << 5) | item.key; + buffer.writeUInt8(headerByte, offset); + offset += 1; + offset = write(item.value, buffer, offset, entityMetadataTypes[type], {}); + }); + buffer.writeUInt8(0x7f, offset); + return offset + 1; } -function readSlot(buffer, offset) { - var value = {}; - var results = numeric.short[0](buffer, offset); - if (! results) return null; - value.blockId = results.value; - if (value.blockId === -1) { - return { - value: value, - size: 2, - }; - } - var cursorEnd = offset + 6; - if (cursorEnd > buffer.length) return null; - value.itemCount = buffer.readInt8(offset + 2); - value.itemDamage = buffer.readInt16BE(offset + 3); - var nbtData = buffer.readInt8(offset + 5); - if (nbtData == 0) { - return { - value: value, - size: 6 +function sizeOfEntityMetadata(value) { + var size = 1 + value.length; + var item; + for (var i = 0; i < value.length; ++i) { + item = value[i]; + size += sizeOf(item.value, entityMetadataTypes[entityMetadataTypeBytes[item.type]], {}); } - } - var nbtData = readNbt(buffer, offset + 5); - value.nbtData = nbtData.value; - return { - value: value, - size: nbtData.size + 5 - }; -} - -function sizeOfSlot(value) { - if (value.blockId === -1) - return (2); - else if (!value.nbtData) { - return (6); - } else { - return (5 + sizeOfNbt(value.nbtData)); - } -} - -function writePosition(value, buffer, offset) { - var longVal = []; - longVal[0] = ((value.x & 0x3FFFFFF) << 6) | ((value.y & 0xFFF) >> 6); - longVal[1] = ((value.y & 0x3F) << 26) | (value.z & 0x3FFFFFF); - return numeric.long[1](longVal, buffer, offset); -} - -function writeSlot(value, buffer, offset) { - buffer.writeInt16BE(value.blockId, offset); - if (value.blockId === -1) return offset + 2; - buffer.writeInt8(value.itemCount, offset + 2); - buffer.writeInt16BE(value.itemDamage, offset + 3); - var nbtDataLen; - if (value.nbtData) - { - var newbuf = nbt.writeUncompressed(value.nbtData); - newbuf.copy(buffer, offset + 5); - nbtDataLen = newbuf.length; - } - else - { - buffer.writeInt8(0, offset + 5); - nbtDataLen = 1; - } - return offset + 5 + nbtDataLen; + return size; } From 17d0aa1a224393da92f0eca7c0f81b20eadbbb61 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 13 May 2015 03:08:05 +0200 Subject: [PATCH 05/12] move rest buffer to minecraft.js --- src/datatypes/minecraft.js | 10 +++++++++- src/protocol.js | 17 +++++------------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/datatypes/minecraft.js b/src/datatypes/minecraft.js index 5907b2ef..6215450e 100644 --- a/src/datatypes/minecraft.js +++ b/src/datatypes/minecraft.js @@ -6,7 +6,8 @@ module.exports = { 'UUID': [readUUID, writeUUID, 16], 'position': [readPosition, writePosition, 8], 'slot': [readSlot, writeSlot, sizeOfSlot], - 'nbt': [readNbt, utils.buffer[1], utils.buffer[2]] + 'nbt': [readNbt, utils.buffer[1], utils.buffer[2]], + 'restBuffer': [readRestBuffer, utils.buffer[1], utils.buffer[2]] }; function readUUID(buffer, offset) { @@ -136,3 +137,10 @@ function sizeOfNbt(value) { return nbt.writeUncompressed(value).length; } + +function readRestBuffer(buffer, offset, typeArgs, rootNode) { + return { + value: buffer.slice(offset), + size: buffer.length - offset + }; +} diff --git a/src/protocol.js b/src/protocol.js index d910435d..ed7661a3 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -12,7 +12,7 @@ var states = { "STATUS": "status", "LOGIN": "login", "PLAY": "play" -} +}; var packets=require("../protocol/protocol"); @@ -67,18 +67,18 @@ var types = { 'bool': utils.bool, 'string': utils.string, 'ustring': utils.ustring, - 'UUID': minecraft.UUID, 'container': [readContainer, writeContainer, sizeOfContainer], 'array': [readArray, writeArray, sizeOfArray], 'buffer': utils.buffer, - 'restBuffer': [readRestBuffer, utils.buffer[1], utils.buffer[2]], 'count': [readCount, writeCount, sizeOfCount], + 'condition': [readCondition, writeCondition, sizeOfCondition], // TODO : remove type-specific, replace with generic containers and arrays. + 'restBuffer': minecraft.restBuffer, + 'UUID': minecraft.UUID, 'position': minecraft.position, 'slot': minecraft.slot, 'nbt': minecraft.nbt, - 'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata], - 'condition': [readCondition, writeCondition, sizeOfCondition] + 'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata] }; var debug; @@ -265,13 +265,6 @@ function sizeOfContainer(value, typeArgs, rootNode) { } -function readRestBuffer(buffer, offset, typeArgs, rootNode) { - return { - value: buffer.slice(offset), - size: buffer.length - offset - }; -} - // begin array function evalCount(count,fields) { From fe04239090f52c10eb45dc122287088c71e79616 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 13 May 2015 03:56:51 +0200 Subject: [PATCH 06/12] create structure.js file with array, container and count --- src/datatypes/structures.js | 113 +++++++++++++++++++++++++++ src/protocol.js | 151 +++++++----------------------------- 2 files changed, 140 insertions(+), 124 deletions(-) create mode 100644 src/datatypes/structures.js diff --git a/src/datatypes/structures.js b/src/datatypes/structures.js new file mode 100644 index 00000000..4a0620cd --- /dev/null +++ b/src/datatypes/structures.js @@ -0,0 +1,113 @@ +var getField= require("../utils").getField; + +module.exports = { + 'array':[readArray,writeArray,sizeOfArray], + 'count': [readCount, writeCount, sizeOfCount], + 'container': [readContainer, writeContainer, sizeOfContainer] +}; + + +function evalCount(count,fields) +{ + if(fields[count["field"]] in count["map"]) + return count["map"][fields[count["field"]]]; + return count["default"]; +} + +function readArray(buffer, offset, typeArgs, rootNode) { + var results = { + value: [], + size: 0 + }; + var count; + if (typeof typeArgs.count === "object") { + count = evalCount(typeArgs.count,rootNode); + } + else + count = getField(typeArgs.count, rootNode); + for (var i = 0; i < count; i++) { + var readResults = this.read(buffer, offset, { type: typeArgs.type, typeArgs: typeArgs.typeArgs }, rootNode); + results.size += readResults.size; + offset += readResults.size; + results.value.push(readResults.value); + } + return results; +} + +function writeArray(value, buffer, offset, typeArgs, rootNode) { + for (var index in value) { + offset = this.write(value[index], buffer, offset, { type: typeArgs.type, typeArgs: typeArgs.typeArgs }, rootNode); + } + return offset; +} + +function sizeOfArray(value, typeArgs, rootNode) { + var size = 0; + for (var index in value) { + size += this.sizeOf(value[index], { type: typeArgs.type, typeArgs: typeArgs.typeArgs }, rootNode); + } + return size; +} + + +function readContainer(buffer, offset, typeArgs, rootNode) { + var results = { + value: {}, + size: 0 + }; + // BLEIGH. Huge hack because I have no way of knowing my current name. + // TODO : either pass fieldInfo instead of typeArgs as argument (bleigh), or send name as argument (verybleigh). + // TODO : what I do inside of roblabla/Protocols is have each "frame" create a new empty slate with just a "super" object pointing to the parent. + rootNode.this = results.value; + for (var index in typeArgs.fields) { + var readResults = this.read(buffer, offset, typeArgs.fields[index], rootNode); + if (readResults == null || readResults.value==null) { continue; } + results.size += readResults.size; + offset += readResults.size; + results.value[typeArgs.fields[index].name] = readResults.value; + } + delete rootNode.this; + return results; +} + +function writeContainer(value, buffer, offset, typeArgs, rootNode) { + var context = value.this ? value.this : value; + rootNode.this = value; + for (var index in typeArgs.fields) { + if (!context.hasOwnProperty(typeArgs.fields[index].name) && typeArgs.fields[index].type != "count" && + (typeArgs.fields[index].type !="condition" || evalCondition(typeArgs.fields[index].typeArgs,rootNode))) + { + debug(new Error("Missing Property " + typeArgs.fields[index].name).stack); + console.log(context); + } + offset = this.write(context[typeArgs.fields[index].name], buffer, offset, typeArgs.fields[index], rootNode); + } + delete rootNode.this; + return offset; +} + +function sizeOfContainer(value, typeArgs, rootNode) { + var size = 0; + var context = value.this ? value.this : value; + rootNode.this = value; + for (var index in typeArgs.fields) { + size += this.sizeOf(context[typeArgs.fields[index].name], typeArgs.fields[index], rootNode); + } + delete rootNode.this; + return size; +} + +function readCount(buffer, offset, typeArgs, rootNode) { + return this.read(buffer, offset, { type: typeArgs.type }, rootNode); +} + +function writeCount(value, buffer, offset, typeArgs, rootNode) { + // Actually gets the required field, and writes its length. Value is unused. + // TODO : a bit hackityhack. + return this.write(getField(typeArgs.countFor, rootNode).length, buffer, offset, { type: typeArgs.type }, rootNode); +} + +function sizeOfCount(value, typeArgs, rootNode) { + // TODO : should I use value or getField().length ? + return this.sizeOf(getField(typeArgs.countFor, rootNode).length, { type: typeArgs.type }, rootNode); +} diff --git a/src/protocol.js b/src/protocol.js index ed7661a3..2d117484 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -53,6 +53,7 @@ var packetStates = {toClient: {}, toServer: {}}; var numeric=require("./datatypes/numeric"); var utils=require("./datatypes/utils"); var minecraft=require("./datatypes/minecraft"); +var structures=require("./datatypes/structures"); var types = { 'byte': numeric.byte, @@ -67,10 +68,10 @@ var types = { 'bool': utils.bool, 'string': utils.string, 'ustring': utils.ustring, - 'container': [readContainer, writeContainer, sizeOfContainer], - 'array': [readArray, writeArray, sizeOfArray], + 'container': structures.container, + 'array':structures.array, 'buffer': utils.buffer, - 'count': [readCount, writeCount, sizeOfCount], + 'count': structures.count, 'condition': [readCondition, writeCondition, sizeOfCondition], // TODO : remove type-specific, replace with generic containers and arrays. 'restBuffer': minecraft.restBuffer, @@ -81,6 +82,7 @@ var types = { 'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata] }; + var debug; if (process.env.NODE_DEBUG && /(minecraft-protocol|mc-proto)/.test(process.env.NODE_DEBUG)) { var pid = process.pid; @@ -126,21 +128,21 @@ function readCondition(buffer,offset,typeArgs, rootNode) { if(!evalCondition(typeArgs,rootNode)) return { value: null, size: 0 }; - return read(buffer, offset, { type: typeArgs.type, typeArgs:typeArgs.typeArgs }, rootNode); + return proto.read(buffer, offset, { type: typeArgs.type, typeArgs:typeArgs.typeArgs }, rootNode); } function writeCondition(value, buffer, offset, typeArgs, rootNode) { if(!evalCondition(typeArgs,rootNode)) return offset; - return write(value, buffer, offset, { type: typeArgs.type, typeArgs:typeArgs.typeArgs }, rootNode); + return proto.write(value, buffer, offset, { type: typeArgs.type, typeArgs:typeArgs.typeArgs }, rootNode); } function sizeOfCondition(value, fieldInfo, rootNode) { if(!evalCondition(fieldInfo,rootNode)) return 0; - return sizeOf(value,fieldInfo, rootNode); + return proto.sizeOf(value,fieldInfo, rootNode); } @@ -179,7 +181,7 @@ function readEntityMetadata(buffer, offset) { error: new Error("unrecognized entity metadata type " + type) } } - results = read(buffer, cursor, dataType, {}); + results = proto.read(buffer, cursor, dataType, {}); if (! results) return null; metadata.push({ key: key, @@ -198,7 +200,7 @@ function writeEntityMetadata(value, buffer, offset) { var headerByte = (type << 5) | item.key; buffer.writeUInt8(headerByte, offset); offset += 1; - offset = write(item.value, buffer, offset, entityMetadataTypes[type], {}); + offset = proto.write(item.value, buffer, offset, entityMetadataTypes[type], {}); }); buffer.writeUInt8(0x7f, offset); return offset + 1; @@ -211,155 +213,56 @@ function sizeOfEntityMetadata(value) { var item; for (var i = 0; i < value.length; ++i) { item = value[i]; - size += sizeOf(item.value, entityMetadataTypes[entityMetadataTypeBytes[item.type]], {}); + size += proto.sizeOf(item.value, entityMetadataTypes[entityMetadataTypeBytes[item.type]], {}); } return size; } -function readContainer(buffer, offset, typeArgs, rootNode) { - var results = { - value: {}, - size: 0 - }; - // BLEIGH. Huge hack because I have no way of knowing my current name. - // TODO : either pass fieldInfo instead of typeArgs as argument (bleigh), or send name as argument (verybleigh). - // TODO : what I do inside of roblabla/Protocols is have each "frame" create a new empty slate with just a "super" object pointing to the parent. - rootNode.this = results.value; - for (var index in typeArgs.fields) { - var readResults = read(buffer, offset, typeArgs.fields[index], rootNode); - if (readResults == null || readResults.value==null) { continue; } - results.size += readResults.size; - offset += readResults.size; - results.value[typeArgs.fields[index].name] = readResults.value; - } - delete rootNode.this; - return results; -} - -function writeContainer(value, buffer, offset, typeArgs, rootNode) { - var context = value.this ? value.this : value; - rootNode.this = value; - for (var index in typeArgs.fields) { - if (!context.hasOwnProperty(typeArgs.fields[index].name) && typeArgs.fields[index].type != "count" && - (typeArgs.fields[index].type !="condition" || evalCondition(typeArgs.fields[index].typeArgs,rootNode))) - { - debug(new Error("Missing Property " + typeArgs.fields[index].name).stack); - console.log(context); - } - offset = write(context[typeArgs.fields[index].name], buffer, offset, typeArgs.fields[index], rootNode); - } - delete rootNode.this; - return offset; -} -function sizeOfContainer(value, typeArgs, rootNode) { - var size = 0; - var context = value.this ? value.this : value; - rootNode.this = value; - for (var index in typeArgs.fields) { - size += sizeOf(context[typeArgs.fields[index].name], typeArgs.fields[index], rootNode); - } - delete rootNode.this; - return size; -} - - -// begin array -function evalCount(count,fields) +function NMProtocols() { - if(fields[count["field"]] in count["map"]) - return count["map"][fields[count["field"]]]; - return count["default"]; -} - -function readArray(buffer, offset, typeArgs, rootNode) { - var results = { - value: [], - size: 0 - } - var count; - if (typeof typeArgs.count === "object") { - count = evalCount(typeArgs.count,rootNode); - } - else - count = getField(typeArgs.count, rootNode); - for (var i = 0; i < count; i++) { - var readResults = read(buffer, offset, { type: typeArgs.type, typeArgs: typeArgs.typeArgs }, rootNode); - results.size += readResults.size; - offset += readResults.size; - results.value.push(readResults.value); - } - return results; -} - -function writeArray(value, buffer, offset, typeArgs, rootNode) { - for (var index in value) { - offset = write(value[index], buffer, offset, { type: typeArgs.type, typeArgs: typeArgs.typeArgs }, rootNode); - } - return offset; -} - -function sizeOfArray(value, typeArgs, rootNode) { - var size = 0; - for (var index in value) { - size += sizeOf(value[index], { type: typeArgs.type, typeArgs: typeArgs.typeArgs }, rootNode); - } - return size; -} -// end array - -function readCount(buffer, offset, typeArgs, rootNode) { - return read(buffer, offset, { type: typeArgs.type }, rootNode); -} -function writeCount(value, buffer, offset, typeArgs, rootNode) { - // Actually gets the required field, and writes its length. Value is unused. - // TODO : a bit hackityhack. - return write(getField(typeArgs.countFor, rootNode).length, buffer, offset, { type: typeArgs.type }, rootNode); } -function sizeOfCount(value, typeArgs, rootNode) { - // TODO : should I use value or getField().length ? - return sizeOf(getField(typeArgs.countFor, rootNode).length, { type: typeArgs.type }, rootNode); -} - -function read(buffer, cursor, fieldInfo, rootNodes) { +NMProtocols.prototype.read = function(buffer, cursor, fieldInfo, rootNodes) { var type = types[fieldInfo.type]; if (!type) { return { error: new Error("missing data type: " + fieldInfo.type) }; } - var readResults = type[0](buffer, cursor, fieldInfo.typeArgs, rootNodes); + var readResults = type[0].call(this,buffer, cursor, fieldInfo.typeArgs, rootNodes); if (readResults == null) { throw new Error("Reader returned null : " + JSON.stringify(fieldInfo)); } if (readResults && readResults.error) return { error: readResults.error }; return readResults; -} +}; -function write(value, buffer, offset, fieldInfo, rootNode) { +NMProtocols.prototype.write = function(value, buffer, offset, fieldInfo, rootNode) { var type = types[fieldInfo.type]; if (!type) { return { error: new Error("missing data type: " + fieldInfo.type) }; } - return type[1](value, buffer, offset, fieldInfo.typeArgs, rootNode); -} + return type[1].call(this,value, buffer, offset, fieldInfo.typeArgs, rootNode); +}; -function sizeOf(value, fieldInfo, rootNode) { +NMProtocols.prototype.sizeOf = function(value, fieldInfo, rootNode) { var type = types[fieldInfo.type]; if (!type) { throw new Error("missing data type: " + fieldInfo.type); } if (typeof type[2] === 'function') { - return type[2](value, fieldInfo.typeArgs, rootNode); + return type[2].call(this,value, fieldInfo.typeArgs, rootNode); } else { return type[2]; } -} +}; + +var proto=new NMProtocols(); function get(packetId, state, toServer) { var direction = toServer ? "toServer" : "toClient"; @@ -385,7 +288,7 @@ function createPacketBuffer(packetId, state, params, isServer) { assert.notEqual(packet, null); packet.forEach(function(fieldInfo) { try { - length += sizeOf(params[fieldInfo.name], fieldInfo, params); + length += proto.sizeOf(params[fieldInfo.name], fieldInfo, params); } catch (e) { console.log("fieldInfo : " + JSON.stringify(fieldInfo)); console.log("params : " + JSON.stringify(params)); @@ -402,7 +305,7 @@ function createPacketBuffer(packetId, state, params, isServer) { // TODO : A better check is probably needed if(typeof value === "undefined" && fieldInfo.type != "count" && (fieldInfo.type !="condition" || evalCondition(fieldInfo.typeArgs,params))) debug(new Error("Missing Property " + fieldInfo.name).stack); - offset = write(value, buffer, offset, fieldInfo, params); + offset = proto.write(value, buffer, offset, fieldInfo, params); }); return buffer; } @@ -468,8 +371,8 @@ function parsePacketData(buffer, state, isServer, packetsToParse) { var i, fieldInfo, readResults; for (i = 0; i < packetInfo.length; ++i) { fieldInfo = packetInfo[i]; - readResults = read(buffer, cursor, fieldInfo, results); - /* A deserializer cannot return null anymore. Besides, read() returns + readResults = proto.read(buffer, cursor, fieldInfo, results); + /* A deserializer cannot return null anymore. Besides, proto.read() returns * null when the condition is not fulfilled. if (!!!readResults) { var error = new Error("A deserializer returned null"); From 877ede063d822872bd94520d64541d0f8feb7e6b Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 13 May 2015 04:02:46 +0200 Subject: [PATCH 07/12] move condition type to conditional.js --- src/datatypes/conditional.js | 26 +++++++++++++++++++++++++ src/protocol.js | 37 +++--------------------------------- src/utils.js | 16 +++++++++++++++- 3 files changed, 44 insertions(+), 35 deletions(-) create mode 100644 src/datatypes/conditional.js diff --git a/src/datatypes/conditional.js b/src/datatypes/conditional.js new file mode 100644 index 00000000..766cae96 --- /dev/null +++ b/src/datatypes/conditional.js @@ -0,0 +1,26 @@ +var evalCondition= require("../utils").evalCondition; + +module.exports= { + 'condition': [readCondition, writeCondition, sizeOfCondition] +}; + +function readCondition(buffer,offset,typeArgs, rootNode) +{ + if(!evalCondition(typeArgs,rootNode)) + return { value: null, size: 0 }; + return this.read(buffer, offset, { type: typeArgs.type, typeArgs:typeArgs.typeArgs }, rootNode); +} + +function writeCondition(value, buffer, offset, typeArgs, rootNode) { + if(!evalCondition(typeArgs,rootNode)) + return offset; + + return this.write(value, buffer, offset, { type: typeArgs.type, typeArgs:typeArgs.typeArgs }, rootNode); +} + +function sizeOfCondition(value, fieldInfo, rootNode) { + if(!evalCondition(fieldInfo,rootNode)) + return 0; + + return this.sizeOf(value,fieldInfo, rootNode); +} diff --git a/src/protocol.js b/src/protocol.js index 2d117484..652147d9 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -2,7 +2,7 @@ var assert = require('assert'); var util = require('util'); var zlib = require('zlib'); -var getField= require("./utils").getField; +var evalCondition= require("./utils").evalCondition; var STRING_MAX_LENGTH = 240; @@ -54,6 +54,7 @@ var numeric=require("./datatypes/numeric"); var utils=require("./datatypes/utils"); var minecraft=require("./datatypes/minecraft"); var structures=require("./datatypes/structures"); +var conditional=require("./datatypes/conditional"); var types = { 'byte': numeric.byte, @@ -72,7 +73,7 @@ var types = { 'array':structures.array, 'buffer': utils.buffer, 'count': structures.count, - 'condition': [readCondition, writeCondition, sizeOfCondition], + 'condition': conditional.condition, // TODO : remove type-specific, replace with generic containers and arrays. 'restBuffer': minecraft.restBuffer, 'UUID': minecraft.UUID, @@ -124,38 +125,6 @@ for (var n in entityMetadataTypes) { entityMetadataTypeBytes[entityMetadataTypes[n].type] = n; } -function readCondition(buffer,offset,typeArgs, rootNode) -{ - if(!evalCondition(typeArgs,rootNode)) - return { value: null, size: 0 }; - return proto.read(buffer, offset, { type: typeArgs.type, typeArgs:typeArgs.typeArgs }, rootNode); -} - -function writeCondition(value, buffer, offset, typeArgs, rootNode) { - if(!evalCondition(typeArgs,rootNode)) - return offset; - - return proto.write(value, buffer, offset, { type: typeArgs.type, typeArgs:typeArgs.typeArgs }, rootNode); -} - -function sizeOfCondition(value, fieldInfo, rootNode) { - if(!evalCondition(fieldInfo,rootNode)) - return 0; - - return proto.sizeOf(value,fieldInfo, rootNode); -} - - -function evalCondition(condition,field_values) -{ - var field_value_to_test="this" in condition && condition["this"] ? field_values["this"][condition.field] : field_values[condition.field]; - var b=condition.values.some(function(value) {return field_value_to_test===value;}); - if("different" in condition && condition["different"]) - return !b; - else - return b; -} - function readEntityMetadata(buffer, offset) { var cursor = offset; diff --git a/src/utils.js b/src/utils.js index f92ef5a3..1f0a5f43 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,4 +1,7 @@ -module.exports= {getField:getField}; +module.exports={ + getField:getField, + evalCondition:evalCondition +}; function getField(countField, rootNode) { var countFieldArr = countField.split("."); @@ -8,3 +11,14 @@ function getField(countField, rootNode) { } return count; } + + +function evalCondition(condition,field_values) +{ + var field_value_to_test="this" in condition && condition["this"] ? field_values["this"][condition.field] : field_values[condition.field]; + var b=condition.values.some(function(value) {return field_value_to_test===value;}); + if("different" in condition && condition["different"]) + return !b; + else + return b; +} From fe443e84ff5333f69c3b3dad90ac0d0cfc9a988d Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 13 May 2015 04:06:14 +0200 Subject: [PATCH 08/12] move entity metadata type to minecraft.js --- src/datatypes/minecraft.js | 94 +++++++++++++++++++++++++++++++++++++- src/protocol.js | 93 +------------------------------------ 2 files changed, 94 insertions(+), 93 deletions(-) diff --git a/src/datatypes/minecraft.js b/src/datatypes/minecraft.js index 6215450e..8a56c20e 100644 --- a/src/datatypes/minecraft.js +++ b/src/datatypes/minecraft.js @@ -7,7 +7,8 @@ module.exports = { 'position': [readPosition, writePosition, 8], 'slot': [readSlot, writeSlot, sizeOfSlot], 'nbt': [readNbt, utils.buffer[1], utils.buffer[2]], - 'restBuffer': [readRestBuffer, utils.buffer[1], utils.buffer[2]] + 'restBuffer': [readRestBuffer, utils.buffer[1], utils.buffer[2]], + 'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata] }; function readUUID(buffer, offset) { @@ -144,3 +145,94 @@ function readRestBuffer(buffer, offset, typeArgs, rootNode) { size: buffer.length - offset }; } + + +var entityMetadataTypes = { + 0: { type: 'byte' }, + 1: { type: 'short' }, + 2: { type: 'int' }, + 3: { type: 'float' }, + 4: { type: 'string' }, + 5: { type: 'slot' }, + 6: { type: 'container', typeArgs: { fields: [ + { name: 'x', type: 'int' }, + { name: 'y', type: 'int' }, + { name: 'z', type: 'int' } + ]}}, + 7: { type: 'container', typeArgs: { fields: [ + { name: 'pitch', type: 'float' }, + { name: 'yaw', type: 'float' }, + { name: 'roll', type: 'float' } + ]}} +}; + +// maps string type name to number +var entityMetadataTypeBytes = {}; +for (var n in entityMetadataTypes) { + if (!entityMetadataTypes.hasOwnProperty(n)) continue; + + entityMetadataTypeBytes[entityMetadataTypes[n].type] = n; +} + + +function readEntityMetadata(buffer, offset) { + var cursor = offset; + var metadata = []; + var item, key, type, results, reader, typeName, dataType; + while (true) { + if (cursor + 1 > buffer.length) return null; + item = buffer.readUInt8(cursor); + cursor += 1; + if (item === 0x7f) { + return { + value: metadata, + size: cursor - offset, + }; + } + key = item & 0x1f; + type = item >> 5; + dataType = entityMetadataTypes[type]; + typeName = dataType.type; + //debug("Reading entity metadata type " + dataType + " (" + ( typeName || "unknown" ) + ")"); + if (!dataType) { + return { + error: new Error("unrecognized entity metadata type " + type) + } + } + results = this.read(buffer, cursor, dataType, {}); + if (! results) return null; + metadata.push({ + key: key, + value: results.value, + type: typeName, + }); + cursor += results.size; + } +} + + + +function writeEntityMetadata(value, buffer, offset) { + var self=this; + value.forEach(function(item) { + var type = entityMetadataTypeBytes[item.type]; + var headerByte = (type << 5) | item.key; + buffer.writeUInt8(headerByte, offset); + offset += 1; + offset = self.write(item.value, buffer, offset, entityMetadataTypes[type], {}); + }); + buffer.writeUInt8(0x7f, offset); + return offset + 1; +} + + + +function sizeOfEntityMetadata(value) { + var size = 1 + value.length; + var item; + for (var i = 0; i < value.length; ++i) { + item = value[i]; + size += this.sizeOf(item.value, entityMetadataTypes[entityMetadataTypeBytes[item.type]], {}); + } + return size; +} diff --git a/src/protocol.js b/src/protocol.js index 652147d9..05d05f1f 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -80,7 +80,7 @@ var types = { 'position': minecraft.position, 'slot': minecraft.slot, 'nbt': minecraft.nbt, - 'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata] + 'entityMetadata': minecraft.entityMetadata }; @@ -98,97 +98,6 @@ if (process.env.NODE_DEBUG && /(minecraft-protocol|mc-proto)/.test(process.env.N debug = function() { }; } -var entityMetadataTypes = { - 0: { type: 'byte' }, - 1: { type: 'short' }, - 2: { type: 'int' }, - 3: { type: 'float' }, - 4: { type: 'string' }, - 5: { type: 'slot' }, - 6: { type: 'container', typeArgs: { fields: [ - { name: 'x', type: 'int' }, - { name: 'y', type: 'int' }, - { name: 'z', type: 'int' } - ]}}, - 7: { type: 'container', typeArgs: { fields: [ - { name: 'pitch', type: 'float' }, - { name: 'yaw', type: 'float' }, - { name: 'roll', type: 'float' } - ]}} -}; - -// maps string type name to number -var entityMetadataTypeBytes = {}; -for (var n in entityMetadataTypes) { - if (!entityMetadataTypes.hasOwnProperty(n)) continue; - - entityMetadataTypeBytes[entityMetadataTypes[n].type] = n; -} - - -function readEntityMetadata(buffer, offset) { - var cursor = offset; - var metadata = []; - var item, key, type, results, reader, typeName, dataType; - while (true) { - if (cursor + 1 > buffer.length) return null; - item = buffer.readUInt8(cursor); - cursor += 1; - if (item === 0x7f) { - return { - value: metadata, - size: cursor - offset, - }; - } - key = item & 0x1f; - type = item >> 5; - dataType = entityMetadataTypes[type]; - typeName = dataType.type; - //debug("Reading entity metadata type " + dataType + " (" + ( typeName || "unknown" ) + ")"); - if (!dataType) { - return { - error: new Error("unrecognized entity metadata type " + type) - } - } - results = proto.read(buffer, cursor, dataType, {}); - if (! results) return null; - metadata.push({ - key: key, - value: results.value, - type: typeName, - }); - cursor += results.size; - } -} - - - -function writeEntityMetadata(value, buffer, offset) { - value.forEach(function(item) { - var type = entityMetadataTypeBytes[item.type]; - var headerByte = (type << 5) | item.key; - buffer.writeUInt8(headerByte, offset); - offset += 1; - offset = proto.write(item.value, buffer, offset, entityMetadataTypes[type], {}); - }); - buffer.writeUInt8(0x7f, offset); - return offset + 1; -} - - - -function sizeOfEntityMetadata(value) { - var size = 1 + value.length; - var item; - for (var i = 0; i < value.length; ++i) { - item = value[i]; - size += proto.sizeOf(item.value, entityMetadataTypes[entityMetadataTypeBytes[item.type]], {}); - } - return size; -} - - - function NMProtocols() { From 78e70dad8e732ab121e0c44f18dca371095b6039 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 13 May 2015 14:47:28 +0200 Subject: [PATCH 09/12] reorganize code a bit : debug code going to debug.js, put packets reading code in a function, add types using a addTypes function --- src/client.js | 2 +- src/datatypes/minecraft.js | 1 + src/debug.js | 17 ++++ src/protocol.js | 173 +++++++++++++++++-------------------- 4 files changed, 100 insertions(+), 93 deletions(-) create mode 100644 src/debug.js diff --git a/src/client.js b/src/client.js index 35fdca18..1627309f 100644 --- a/src/client.js +++ b/src/client.js @@ -11,7 +11,7 @@ var EventEmitter = require('events').EventEmitter , packetIds = protocol.packetIds , packetNames = protocol.packetNames , states = protocol.states - , debug = protocol.debug + , debug = require('./debug') ; module.exports = Client; diff --git a/src/datatypes/minecraft.js b/src/datatypes/minecraft.js index 8a56c20e..d43ddb32 100644 --- a/src/datatypes/minecraft.js +++ b/src/datatypes/minecraft.js @@ -2,6 +2,7 @@ var nbt = require('prismarine-nbt'); var utils = require("./utils"); var numeric = require("./numeric"); +// TODO : remove type-specific, replace with generic containers and arrays. module.exports = { 'UUID': [readUUID, writeUUID, 16], 'position': [readPosition, writePosition, 8], diff --git a/src/debug.js b/src/debug.js new file mode 100644 index 00000000..f9b9b6a0 --- /dev/null +++ b/src/debug.js @@ -0,0 +1,17 @@ +var util = require('util') + +var debug; +if (process.env.NODE_DEBUG && /(minecraft-protocol|mc-proto)/.test(process.env.NODE_DEBUG)) { + var pid = process.pid; + debug = function(x) { + // if console is not set up yet, then skip this. + if (!console.error) + return; + console.error('MC-PROTO: %d', pid, + util.format.apply(util, arguments).slice(0, 500)); + }; +} else { + debug = function() { }; +} + +module.exports = debug; diff --git a/src/protocol.js b/src/protocol.js index 05d05f1f..51b6ad70 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -1,110 +1,88 @@ var assert = require('assert'); -var util = require('util'); var zlib = require('zlib'); var evalCondition= require("./utils").evalCondition; var STRING_MAX_LENGTH = 240; + +function readPackets(packets,states) +{ + var packetFields = {}; + var packetNames = {}; + var packetIds = {}; + var packetStates = {toClient: {}, toServer: {}}; + for (var stateName in states) { + var state = states[stateName]; + + packetFields[state] = {toClient: [], toServer: []}; + packetNames[state] = {toClient: [], toServer: []}; + packetIds[state] = {toClient: [], toServer: []}; + + ['toClient', 'toServer'].forEach(function(direction) { + for (var name in packets[state][direction]) { + var info = packets[state][direction][name]; + var id = parseInt(info.id); + var fields = info.fields; + + assert(id !== undefined, 'missing id for packet '+name); + assert(fields !== undefined, 'missing fields for packet '+name); + assert(!packetNames[state][direction].hasOwnProperty(id), 'duplicate packet id '+id+' for '+name); + assert(!packetIds[state][direction].hasOwnProperty(name), 'duplicate packet name '+name+' for '+id); + assert(!packetFields[state][direction].hasOwnProperty(id), 'duplicate packet id '+id+' for '+name); + assert(!packetStates[direction].hasOwnProperty(name), 'duplicate packet name '+name+' for '+id+', must be unique across all states'); + + packetNames[state][direction][id] = name; + packetIds[state][direction][name] = id; + packetFields[state][direction][id] = fields; + packetStates[direction][name] = state; + } + }); + } + return { + packetFields:packetFields, + packetNames:packetNames, + packetIds:packetIds, + packetStates:packetStates + }; +} + // This is really just for the client. var states = { - "HANDSHAKING": "handshaking", - "STATUS": "status", - "LOGIN": "login", - "PLAY": "play" + "HANDSHAKING": "handshaking", + "STATUS": "status", + "LOGIN": "login", + "PLAY": "play" }; - var packets=require("../protocol/protocol"); +var packetIndexes=readPackets(packets,states); -var packetFields = {}; -var packetNames = {}; -var packetIds = {}; -var packetStates = {toClient: {}, toServer: {}}; -(function() { - for (var stateName in states) { - var state = states[stateName]; - - packetFields[state] = {toClient: [], toServer: []}; - packetNames[state] = {toClient: [], toServer: []}; - packetIds[state] = {toClient: [], toServer: []}; - - ['toClient', 'toServer'].forEach(function(direction) { - for (var name in packets[state][direction]) { - var info = packets[state][direction][name]; - var id = parseInt(info.id); - var fields = info.fields; - - assert(id !== undefined, 'missing id for packet '+name); - assert(fields !== undefined, 'missing fields for packet '+name); - assert(!packetNames[state][direction].hasOwnProperty(id), 'duplicate packet id '+id+' for '+name); - assert(!packetIds[state][direction].hasOwnProperty(name), 'duplicate packet name '+name+' for '+id); - assert(!packetFields[state][direction].hasOwnProperty(id), 'duplicate packet id '+id+' for '+name); - assert(!packetStates[direction].hasOwnProperty(name), 'duplicate packet name '+name+' for '+id+', must be unique across all states'); - - packetNames[state][direction][id] = name; - packetIds[state][direction][name] = id; - packetFields[state][direction][id] = fields; - packetStates[direction][name] = state; - } - }); - } -})(); +var packetFields = packetIndexes.packetFields; +var packetNames = packetIndexes.packetNames; +var packetIds = packetIndexes.packetIds; +var packetStates = packetIndexes.packetStates; -var numeric=require("./datatypes/numeric"); -var utils=require("./datatypes/utils"); -var minecraft=require("./datatypes/minecraft"); -var structures=require("./datatypes/structures"); -var conditional=require("./datatypes/conditional"); +function NMProtocols() +{ + this.types={}; +} -var types = { - 'byte': numeric.byte, - 'ubyte':numeric.ubyte, - 'short': numeric.short, - 'ushort': numeric.ushort, - 'int': numeric.int, - 'long': numeric.long, - 'varint': utils.varint, - 'float': numeric.float, - 'double': numeric.double, - 'bool': utils.bool, - 'string': utils.string, - 'ustring': utils.ustring, - 'container': structures.container, - 'array':structures.array, - 'buffer': utils.buffer, - 'count': structures.count, - 'condition': conditional.condition, - // TODO : remove type-specific, replace with generic containers and arrays. - 'restBuffer': minecraft.restBuffer, - 'UUID': minecraft.UUID, - 'position': minecraft.position, - 'slot': minecraft.slot, - 'nbt': minecraft.nbt, - 'entityMetadata': minecraft.entityMetadata +NMProtocols.prototype.addType = function(name,functions) +{ + this.types[name]=functions; }; - -var debug; -if (process.env.NODE_DEBUG && /(minecraft-protocol|mc-proto)/.test(process.env.NODE_DEBUG)) { - var pid = process.pid; - debug = function(x) { - // if console is not set up yet, then skip this. - if (!console.error) - return; - console.error('MC-PROTO: %d', pid, - util.format.apply(util, arguments).slice(0, 500)); - }; -} else { - debug = function() { }; -} - -function NMProtocols() +NMProtocols.prototype.addTypes = function(types) { + var self=this; + Object.keys(types).forEach(function(name){ + self.addType(name,types[name]); + }); +}; -} NMProtocols.prototype.read = function(buffer, cursor, fieldInfo, rootNodes) { - var type = types[fieldInfo.type]; + var type = this.types[fieldInfo.type]; if (!type) { return { error: new Error("missing data type: " + fieldInfo.type) @@ -119,7 +97,7 @@ NMProtocols.prototype.read = function(buffer, cursor, fieldInfo, rootNodes) { }; NMProtocols.prototype.write = function(value, buffer, offset, fieldInfo, rootNode) { - var type = types[fieldInfo.type]; + var type = this.types[fieldInfo.type]; if (!type) { return { error: new Error("missing data type: " + fieldInfo.type) @@ -129,7 +107,7 @@ NMProtocols.prototype.write = function(value, buffer, offset, fieldInfo, rootNod }; NMProtocols.prototype.sizeOf = function(value, fieldInfo, rootNode) { - var type = types[fieldInfo.type]; + var type = this.types[fieldInfo.type]; if (!type) { throw new Error("missing data type: " + fieldInfo.type); } @@ -140,7 +118,19 @@ NMProtocols.prototype.sizeOf = function(value, fieldInfo, rootNode) { } }; + +var numeric=require("./datatypes/numeric"); +var utils=require("./datatypes/utils"); +var minecraft=require("./datatypes/minecraft"); +var structures=require("./datatypes/structures"); +var conditional=require("./datatypes/conditional"); + var proto=new NMProtocols(); +proto.addTypes(numeric); +proto.addTypes(utils); +proto.addTypes(minecraft); +proto.addTypes(structures); +proto.addTypes(conditional); function get(packetId, state, toServer) { var direction = toServer ? "toServer" : "toClient"; @@ -282,7 +272,7 @@ function parsePacket(buffer, state, isServer, packetsToParse) { if (state == null) state = states.PLAY; var cursor = 0; var lengthField = utils.varint[0](buffer, 0); - if (!!!lengthField) return null; + if (!lengthField) return null; var length = lengthField.value; cursor += lengthField.size; if (length + lengthField.size > buffer.length) return null; // fail early @@ -324,9 +314,8 @@ module.exports = { packetNames: packetNames, packetFields: packetFields, packetStates: packetStates, - types: types, + types: proto.types, states: states, get: get, - debug: debug, evalCondition:evalCondition }; From 778752f09734a4f129e01b4d289de1b7a052f7a1 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 13 May 2015 15:15:16 +0200 Subject: [PATCH 10/12] move packets reading to packets.js, remove some dead code --- src/client.js | 1 - src/datatypes/structures.js | 1 + src/index.js | 7 ++--- src/packets.js | 44 ++++++++++++++++++++++++++ src/protocol.js | 63 ++----------------------------------- 5 files changed, 50 insertions(+), 66 deletions(-) create mode 100644 src/packets.js diff --git a/src/client.js b/src/client.js index 1627309f..59baf659 100644 --- a/src/client.js +++ b/src/client.js @@ -5,7 +5,6 @@ var EventEmitter = require('events').EventEmitter , compressPacketBuffer = protocol.compressPacketBuffer , oldStylePacket = protocol.oldStylePacket , newStylePacket = protocol.newStylePacket - , parsePacket = protocol.parsePacket , parsePacketData = protocol.parsePacketData , parseNewStylePacket = protocol.parseNewStylePacket , packetIds = protocol.packetIds diff --git a/src/datatypes/structures.js b/src/datatypes/structures.js index 4a0620cd..a008808e 100644 --- a/src/datatypes/structures.js +++ b/src/datatypes/structures.js @@ -1,4 +1,5 @@ var getField= require("../utils").getField; +var debug = require("../debug"); module.exports = { 'array':[readArray,writeArray,sizeOfArray], diff --git a/src/index.js b/src/index.js index 8fb415a4..3f35d543 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,6 @@ -var EventEmitter = require('events').EventEmitter - , util = require('util') - , assert = require('assert') +var assert = require('assert') , crypto = require('crypto') , bufferEqual = require('buffer-equal') - , superagent = require('superagent') , protocol = require('./protocol') , Client = require('./client') , dns = require('dns') @@ -14,7 +11,7 @@ var EventEmitter = require('events').EventEmitter , validateSession = Yggdrasil.validateSession , joinServer = Yggdrasil.joinServer , states = protocol.states - , debug = protocol.debug + , debug = require("./debug") ; var ursa; try { diff --git a/src/packets.js b/src/packets.js new file mode 100644 index 00000000..17c8aa92 --- /dev/null +++ b/src/packets.js @@ -0,0 +1,44 @@ +var assert=require("assert"); + +module.exports={readPackets:readPackets}; + +function readPackets(packets,states) +{ + var packetFields = {}; + var packetNames = {}; + var packetIds = {}; + var packetStates = {toClient: {}, toServer: {}}; + for (var stateName in states) { + var state = states[stateName]; + + packetFields[state] = {toClient: [], toServer: []}; + packetNames[state] = {toClient: [], toServer: []}; + packetIds[state] = {toClient: [], toServer: []}; + + ['toClient', 'toServer'].forEach(function(direction) { + for (var name in packets[state][direction]) { + var info = packets[state][direction][name]; + var id = parseInt(info.id); + var fields = info.fields; + + assert(id !== undefined, 'missing id for packet '+name); + assert(fields !== undefined, 'missing fields for packet '+name); + assert(!packetNames[state][direction].hasOwnProperty(id), 'duplicate packet id '+id+' for '+name); + assert(!packetIds[state][direction].hasOwnProperty(name), 'duplicate packet name '+name+' for '+id); + assert(!packetFields[state][direction].hasOwnProperty(id), 'duplicate packet id '+id+' for '+name); + assert(!packetStates[direction].hasOwnProperty(name), 'duplicate packet name '+name+' for '+id+', must be unique across all states'); + + packetNames[state][direction][id] = name; + packetIds[state][direction][name] = id; + packetFields[state][direction][id] = fields; + packetStates[direction][name] = state; + } + }); + } + return { + packetFields:packetFields, + packetNames:packetNames, + packetIds:packetIds, + packetStates:packetStates + }; +} diff --git a/src/protocol.js b/src/protocol.js index 51b6ad70..84519c9e 100644 --- a/src/protocol.js +++ b/src/protocol.js @@ -1,51 +1,10 @@ var assert = require('assert'); var zlib = require('zlib'); -var evalCondition= require("./utils").evalCondition; +var evalCondition = require("./utils").evalCondition; +var readPackets = require("./packets").readPackets; +var debug = require("./debug"); -var STRING_MAX_LENGTH = 240; - - -function readPackets(packets,states) -{ - var packetFields = {}; - var packetNames = {}; - var packetIds = {}; - var packetStates = {toClient: {}, toServer: {}}; - for (var stateName in states) { - var state = states[stateName]; - - packetFields[state] = {toClient: [], toServer: []}; - packetNames[state] = {toClient: [], toServer: []}; - packetIds[state] = {toClient: [], toServer: []}; - - ['toClient', 'toServer'].forEach(function(direction) { - for (var name in packets[state][direction]) { - var info = packets[state][direction][name]; - var id = parseInt(info.id); - var fields = info.fields; - - assert(id !== undefined, 'missing id for packet '+name); - assert(fields !== undefined, 'missing fields for packet '+name); - assert(!packetNames[state][direction].hasOwnProperty(id), 'duplicate packet id '+id+' for '+name); - assert(!packetIds[state][direction].hasOwnProperty(name), 'duplicate packet name '+name+' for '+id); - assert(!packetFields[state][direction].hasOwnProperty(id), 'duplicate packet id '+id+' for '+name); - assert(!packetStates[direction].hasOwnProperty(name), 'duplicate packet name '+name+' for '+id+', must be unique across all states'); - - packetNames[state][direction][id] = name; - packetIds[state][direction][name] = id; - packetFields[state][direction][id] = fields; - packetStates[direction][name] = state; - } - }); - } - return { - packetFields:packetFields, - packetNames:packetNames, - packetIds:packetIds, - packetStates:packetStates - }; -} // This is really just for the client. var states = { @@ -80,7 +39,6 @@ NMProtocols.prototype.addTypes = function(types) }); }; - NMProtocols.prototype.read = function(buffer, cursor, fieldInfo, rootNodes) { var type = this.types[fieldInfo.type]; if (!type) { @@ -268,19 +226,6 @@ function parsePacketData(buffer, state, isServer, packetsToParse) { }; } -function parsePacket(buffer, state, isServer, packetsToParse) { - if (state == null) state = states.PLAY; - var cursor = 0; - var lengthField = utils.varint[0](buffer, 0); - if (!lengthField) return null; - var length = lengthField.value; - cursor += lengthField.size; - if (length + lengthField.size > buffer.length) return null; // fail early - var result = parsePacketData(buffer.slice(cursor, length + cursor), state, isServer, packetsToParse); - result.size = lengthField.size + length; - return result; -} - function parseNewStylePacket(buffer, state, isServer, packetsToParse, cb) { var dataLengthField = utils.varint[0](buffer, 0); var buf = buffer.slice(dataLengthField.size); @@ -302,14 +247,12 @@ module.exports = { version: 47, minecraftVersion: '1.8.1', sessionVersion: 13, - parsePacket: parsePacket, parsePacketData: parsePacketData, parseNewStylePacket: parseNewStylePacket, createPacketBuffer: createPacketBuffer, compressPacketBuffer: compressPacketBuffer, oldStylePacket: oldStylePacket, newStylePacket: newStylePacket, - STRING_MAX_LENGTH: STRING_MAX_LENGTH, packetIds: packetIds, packetNames: packetNames, packetFields: packetFields, From f87dd9595dd7f63094e8291964d4b57fe2e98639 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Thu, 14 May 2015 12:58:05 +0200 Subject: [PATCH 11/12] remove unneeded assert require --- src/server.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/server.js b/src/server.js index 91750877..db8844d2 100644 --- a/src/server.js +++ b/src/server.js @@ -1,7 +1,6 @@ var net = require('net') , EventEmitter = require('events').EventEmitter , util = require('util') - , assert = require('assert') , Client = require('./client') , states = require('./protocol').states ; From b42e13972cee1123e6985d258f55f9edce1673a3 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Thu, 14 May 2015 19:25:20 +0200 Subject: [PATCH 12/12] fix gulp --- gulpfile.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 48595f8c..15ebc66f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -9,22 +9,15 @@ var sourcemaps = require('gulp-sourcemaps'); gulp.task('compile', function() { gulp - .src('src/*.js') + .src('src/**/*.js') .pipe(sourcemaps.init()) .pipe(babel(options)) .pipe(sourcemaps.write('maps/')) .pipe(gulp.dest('dist/')); - - gulp - .src('src/datatypes/*.js') - .pipe(sourcemaps.init()) - .pipe(babel(options)) - .pipe(sourcemaps.write('maps/')) - .pipe(gulp.dest('dist/datatypes/')); }); gulp.task('watch', function() { - gulp.watch('src/*.js', ['compile']); + gulp.watch('src/**/*.js', ['compile']); }); gulp.task('default', ['compile']);