From 457df31b0bee98ec93943000aaa7b59b1c883be3 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 4 Apr 2014 10:32:20 +0200 Subject: [PATCH] Container, array, buffer and count types added --- lib/protocol.js | 823 ++++++++++++------------------------------------ test/test.js | 62 ++-- 2 files changed, 240 insertions(+), 645 deletions(-) diff --git a/lib/protocol.js b/lib/protocol.js index a349b3b..62ddc80 100644 --- a/lib/protocol.js +++ b/lib/protocol.js @@ -50,8 +50,10 @@ var packets = { ]}, encryption_begin: {id: 0x01, fields: [ { name: "serverId", type: "string" }, - { name: "publicKey", type: "byteArray16" }, - { name: "verifyToken", type: "byteArray16" } + { name: "publicKeyLength", type: "count", typeArgs: { type: "short", countFor: "publicKey" } }, + { name: "publicKey", type: "buffer", typeArgs: { count: "publicKeyLength" } }, + { name: "verifyTokenLength", type: "count", typeArgs: { type: "short", countFor: "verifyToken" } }, + { name: "verifyToken", type: "buffer", typeArgs: { count: "verifyTokenLength" } }, ]}, success: {id: 0x02, fields: [ { name: "uuid", type: "string" }, @@ -63,8 +65,10 @@ var packets = { { name: "username", type: "string" } ]}, encryption_begin: {id: 0x01, fields: [ - { name: "sharedSecret", type: "byteArray16" }, - { name: "verifyToken", type: "byteArray16" } + { name: "sharedSecretLength", type: "count", typeArgs: { type: "short", countFor: "sharedSecret" } }, + { name: "sharedSecret", type: "buffer", typeArgs: { count: "sharedSecretLength" } }, + { name: "verifyTokenLength", type: "count", typeArgs: { type: "short", countFor: "verifyToken" } }, + { name: "verifyToken", type: "buffer", typeArgs: { count: "verifyTokenLength" } }, ]} } }, @@ -154,7 +158,18 @@ var packets = { { name: "z", type: "int" }, { name: "pitch", type: "byte" }, { name: "yaw", type: "byte" }, - { name: "objectData", type: "objectData" } + { name: "objectData", type: "container", typeArgs: { fields: [ + { name: "intField", type: "int" }, + { name: "velocityX", type: "short", condition: function(field_values) { + return field_values['this']['intField'] != 0; + }}, + { name: "velocityY", type: "short", condition: function(field_values) { + return field_values['this']['intField'] != 0; + }}, + { name: "velocityZ", type: "short", condition: function(field_values) { + return field_values['this']['intField'] != 0; + }} + ]}} ]}, spawn_entity_living: {id: 0x0f, fields: [ { name: "entityId", type: "varint" }, @@ -192,7 +207,8 @@ var packets = { { name: "velocityZ", type: "short" } ]}, entity_destroy: {id: 0x13, fields: [ - { name: "entityIds", type: "intArray8" } + { name: "count", type: "byte" }, + { name: "entityIds", type: "array", typeArgs: { type: "int", count: "count" } } ]}, entity: {id: 0x14, fields: [ { name: "entityId", type: "int" } @@ -258,7 +274,20 @@ var packets = { ]}, update_attributes: {id: 0x20, fields: [ { name: "entityId", type: "int" }, - { name: "properties", type: "propertyArray" } + { name: "count", type: "count", typeArgs: { type: "int", countFor: "properties" } }, + { name: "properties", type: "array", typeArgs: { count: "count", + type: "container", typeArgs: { fields: [ + { name: "key", type: "string" }, + { name: "value", type: "double" }, + { name: "listLength", type: "count", typeArgs: { type: "short", countFor: "this.modifiers" } }, + { name: "modifiers", type: "array", typeArgs: { count: "this.listLength", + type: "container", typeArgs: { fields: [ + { name: "UUID", type: "UUID" }, + { name: "amount", type: "double" }, + { name: "operation", type: "byte" } + ]}}} + ]} + }} ]}, map_chunk: {id: 0x21, fields: [ { name: "x", type: "int" }, @@ -266,13 +295,15 @@ var packets = { { name: "groundUp", type: "bool" }, { name: "bitMap", type: "ushort" }, { name: "addBitMap", type: "ushort" }, - { name: "compressedChunkData", type: "byteArray32" } + { name: "compressedChunkDataLength", type: "count", typeArgs: { type: "int", countFor: "compressedChunkData" } }, + { name: "compressedChunkData", type: "buffer", typeArgs: { count: "compressedChunkDataLength" } }, ]}, multi_block_change: {id: 0x22, fields: [ { name: "chunkX", type: "int" }, { name: "chunkZ", type: "int" }, { name: "recordCount", type: "short" }, - { name: "data", type: "byteArray32" } + { name: "dataLength", type: "count", typeArgs: { type: "int", countFor: "data" } }, + { name: "data", type: "buffer", typeArgs: { count: "dataLength" } }, ]}, block_change: {id: 0x23, fields: [ { name: "x", type: "int" }, @@ -297,14 +328,31 @@ var packets = { { name: "destroyStage", type: "byte" } ]}, map_chunk_bulk: {id: 0x26, fields: [ - { name: "data", type: "mapChunkBulk" } + { name: "chunkColumnCount", type: "count", typeArgs: { type: "short", countFor: "meta" } }, + { name: "dataLength", type: "count", typeArgs: { type: "int", countFor: "compressedChunkData" } }, + { name: "skyLightSent", type: "bool" }, + { name: "compressedChunkData", type: "buffer", typeArgs: { count: "dataLength" } }, + { name: "meta", type: "array", typeArgs: { count: "chunkColumnCount", + type: "container", typeArgs: { fields: [ + { name: "x", type: "int" }, + { name: "z", type: "int" }, + { name: "bitMap", type: "ushort" }, + { name: "addBitMap", type: "ushort" } + ] } } } ]}, explosion: {id: 0x27, fields: [ { name: "x", type: "float" }, { name: "y", type: "float" }, { name: "z", type: "float" }, { name: "radius", type: "float" }, - { name: "affectedBlockOffsets", type: "byteVectorArray" }, + { name: "count", type: "count", typeArgs: { type: "int", countFor: "affectedBlockOffsets" } }, + { name: "affectedBlockOffsets", type: "array", typeArgs: { count: "count", type: "container", typeArgs: { + fields: [ + { name: "x", type: "byte" }, + { name: "y", type: "byte" }, + { name: "z", type: "byte" } + ] + }}}, { name: "playerMotionX", type: "float" }, { name: "playerMotionY", type: "float" }, { name: "playerMotionZ", type: "float" } @@ -367,7 +415,8 @@ var packets = { ]}, window_items: {id: 0x30, fields: [ { name: "windowId", type: "ubyte" }, - { name: "items", type: "slotArray" } + { name: "count", type: "count", typeArgs: { type: "short", countFor: "items" } }, + { name: "items", type: "array", typeArgs: { type: "slot", count: "count" } } ]}, craft_progress_bar: {id: 0x31, fields: [ { name: "windowId", type: "ubyte" }, @@ -390,14 +439,16 @@ var packets = { ]}, map: {id: 0x34, fields: [ { name: "itemDamage", type: "varint" }, - { name: "data", type: "byteArray16" }, + { name: "dataLength", type: "count", typeArgs: { type: "short", countFor: "data" } }, + { name: "data", type: "buffer", typeArgs: { count: "dataLength" } }, ]}, tile_entity_data:{id: 0x35, fields: [ { name: "x", type: "int" }, { name: "y", type: "short" }, { name: "z", type: "int" }, { name: "action", type: "ubyte" }, - { name: "nbtData", type: "byteArray16" } + { name: "nbtDataLength", type: "count", typeArgs: { type: "short", countFor: "nbtData" } }, + { name: "nbtData", type: "buffer", typeArgs: { count: "nbtDataLength" } }, ]}, open_sign_entity: {id: 0x36, fields: [ { name: "x", type: "int" }, @@ -405,7 +456,13 @@ var packets = { { name: "z", type: "int" } ]}, statistics: {id: 0x37, fields: [ - { name: "count", type: "statisticArray" } + { name: "count", type: "count", typeArgs: { type: "varint", countFor: "entries" } }, + { name: "entries", type: "array", typeArgs: { count: "count", + type: "container", typeArgs: { fields: [ + { name: "name", type: "string" }, + { name: "value", type: "varint" } + ]} + }} ]}, player_info: {id: 0x38, fields: [ { name: "playerName", type: "string" }, @@ -418,7 +475,8 @@ var packets = { { name: "walkingSpeed", type: "float" } ]}, tab_complete: {id: 0x3a, fields: [ - { name: "matches", type: "matchArray" } + { name: "count", type: "count", typeArgs: { type: "varint", countFor: "matches" } }, + { name: "matches", type: "array", typeArgs: { type: "string", count: "count" } } ]}, scoreboard_objective: {id: 0x3b, fields: [ { name: "name", type: "string" }, @@ -443,7 +501,7 @@ var packets = { { name: "team", type: "string" }, { name: "mode", type: "byte" }, { name: "name", type: "string", condition: function(field_values) { - return field_values['mode'] == 0 || field_values['mode'] == 2; + return field_values['mode'] == 0 || field_values['mode'] == 2; } }, { name: "prefix", type: "string", condition: function(field_values) { return field_values['mode'] == 0 || field_values['mode'] == 2; @@ -460,7 +518,8 @@ var packets = { ]}, custom_payload: {id: 0x3f, fields: [ { name: "channel", type: "string" }, - { name: "data", type: "byteArray16" } + { name: "dataCount", type: 'count', typeArgs: { type: "short", countFor: "data" } }, + { name: "data", type: "buffer", typeArgs: { count: "dataCount" } } ]}, kick_disconnect: {id: 0x40, fields: [ { name: "reason", type: "string" } @@ -590,7 +649,8 @@ var packets = { ]}, custom_payload: {id: 0x17, fields: [ { name: "channel", type: "string" }, - { name: "data", type: "byteArray16" } + { name: "dataLength", type: "count", typeArgs: { type: "short", countFor: "data" } }, + { name: "data", type: "buffer", typeArgs: { count: "dataLength" } }, ]} } } @@ -645,24 +705,14 @@ var types = { 'bool': [readBool, writeBool, 1], 'string': [readString, writeString, sizeOfString], 'ustring': [readString, writeString, sizeOfUString], // TODO : remove ustring + 'UUID': [readUUID, writeUUID, 16], + 'container': [readContainer, writeContainer, sizeOfContainer], + 'array': [readArray, writeArray, sizeOfArray], + 'buffer': [readBuffer, writeBuffer, sizeOfBuffer], + 'count': [readCount, writeCount, sizeOfCount], // TODO : remove type-specific, replace with generic containers and arrays. 'slot': [readSlot, writeSlot, sizeOfSlot], - 'byteArray16': [readByteArray16, writeByteArray16, sizeOfByteArray16], - 'ascii': [readAscii, writeAscii, sizeOfAscii], 'entityMetadata': [readEntityMetadata, writeEntityMetadata, sizeOfEntityMetadata], - 'byteArray32': [readByteArray32, writeByteArray32, sizeOfByteArray32], - 'slotArray': [readSlotArray, writeSlotArray, sizeOfSlotArray], - 'mapChunkBulk': [readMapChunkBulk, writeMapChunkBulk, sizeOfMapChunkBulk], - 'objectData': [readObjectData, writeObjectData, sizeOfObjectData], - 'intArray8': [readIntArray8, writeIntArray8, sizeOfIntArray8], - 'intVector': [readIntVector, writeIntVector, 12], - 'byteVector': [readByteVector, writeByteVector, 3], - 'byteVectorArray': [readByteVectorArray, writeByteVectorArray, sizeOfByteVectorArray], - 'stringArray': [readStringArray, writeStringArray, sizeOfStringArray], - 'UUID': [readUUID, writeUUID, 16], - 'propertyArray': [readPropertyArray, writePropertyArray, sizeOfPropertyArray], - 'statisticArray': [readStatisticArray, writeStatisticArray, sizeOfStatisticArray], - 'matchArray': [readMatchArray, writeMatchArray, sizeOfMatchArray] }; var debug; @@ -679,41 +729,18 @@ if (process.env.NODE_DEBUG && /(minecraft-protocol|mc-proto)/.test(process.env.N debug = function() { }; } -function sizeOfByteArray32(value) { - return 4 + value.length; -} - -function writeByteArray32(value, buffer, offset) { - buffer.writeInt32BE(value.length, offset); - value.copy(buffer, offset + 4); - return offset + 4 + value.length; -} - -function sizeOfSlotArray(value) { - var size = 2; - for (var i = 0; i < value.length; ++i) { - size += sizeOfSlot(value[i]); - } - return size; -} - -function writeSlotArray(value, buffer, offset) { - buffer.writeInt16BE(value.length, offset); - offset += 2; - value.forEach(function(slot) { - offset = writeSlot(slot, buffer, offset); - }); - return offset; -} - var entityMetadataTypes = { - 0: 'byte', - 1: 'short', - 2: 'int', - 3: 'float', - 4: 'string', - 5: 'slot', - 6: 'intVector' + 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' } + ]}} }; // maps string type name to number @@ -721,7 +748,7 @@ var entityMetadataTypeBytes = {}; for (var n in entityMetadataTypes) { if (!entityMetadataTypes.hasOwnProperty(n)) continue; - entityMetadataTypeBytes[entityMetadataTypes[n]] = n; + entityMetadataTypeBytes[entityMetadataTypes[n].type] = n; } function sizeOfEntityMetadata(value) { @@ -729,7 +756,7 @@ function sizeOfEntityMetadata(value) { var item; for (var i = 0; i < value.length; ++i) { item = value[i]; - size += sizeOf(item.value, { type: item.type }, {}); + size += sizeOf(item.value, entityMetadataTypes[entityMetadataTypeBytes[item.type]], {}); } return size; } @@ -740,134 +767,12 @@ function writeEntityMetadata(value, buffer, offset) { var headerByte = (type << 5) | item.key; buffer.writeUInt8(headerByte, offset); offset += 1; - offset = types[item.type][1](item.value, buffer, offset); + offset = write(item.value, buffer, offset, entityMetadataTypes[type], {}); }); buffer.writeUInt8(0x7f, offset); return offset + 1; } -function sizeOfObjectData(value) { - return value.intField === 0 ? 4 : 10; -} - -function writeObjectData(value, buffer, offset) { - buffer.writeInt32BE(value.intField, offset); - if (value.intField === 0) return -1; - offset += 4; - - buffer.writeInt16BE(value.velocityX, offset); - offset += 2; - buffer.writeInt16BE(value.velocityY, offset); - offset += 2; - buffer.writeInt16BE(value.velocityZ, offset); - return offset + 2; -} - -function sizeOfMapChunkBulk(value) { - return 7 + value.compressedChunkData.length + 12 * value.meta.length; -} - -function writeMapChunkBulk(value, buffer, offset) { - buffer.writeInt16BE(value.meta.length, offset); - offset += 2; - buffer.writeInt32BE(value.compressedChunkData.length, offset); - offset += 4; - buffer.writeInt8(+value.skyLightSent, offset); - offset += 1; - - value.compressedChunkData.copy(buffer, offset); - offset += value.compressedChunkData.length; - - var meta; - for (var i = 0; i < value.meta.length; ++i) { - meta = value.meta[i]; - buffer.writeInt32BE(meta.x, offset); - offset += 4; - buffer.writeInt32BE(meta.z, offset); - offset += 4; - buffer.writeUInt16BE(meta.bitMap, offset); - offset += 2; - buffer.writeUInt16BE(meta.addBitMap, offset); - offset += 2; - } - return offset; -} - -function sizeOfIntArray8(value) { - return 1 + 4 * value.length; -} - -function writeIntArray8(value, buffer, offset) { - buffer.writeInt8(value.length, offset); - offset += 1; - - value.forEach(function(item) { - buffer.writeInt32BE(item, offset); - offset += 4; - }); - return offset; -} - -function writeIntVector(value, buffer, offset) { - buffer.writeInt32BE(value.x, offset); - buffer.writeInt32BE(value.y, offset + 4); - buffer.writeInt32BE(value.z, offset + 8); - return offset + 12; -} - -function writeByteVector(value, buffer, offset) { - buffer.writeInt8(value.x, offset); - buffer.writeInt8(value.y, offset + 1); - buffer.writeInt8(value.z, offset + 2); - return offset + 3; -} - -function sizeOfByteVectorArray(value) { - return 4 + 3 * value.length; -} - -function writeByteVectorArray(value, buffer, offset) { - buffer.writeInt32BE(value.length, offset); - offset += 4; - value.forEach(function(vec) { - buffer.writeInt8(vec.x, offset); - offset += 1; - buffer.writeInt8(vec.y, offset); - offset += 1; - buffer.writeInt8(vec.z, offset); - offset += 1; - }); - return offset; -} - -function sizeOfStringArray(value) { - var size = 2; - for (var i = 0; i < value.length; ++i) { - size += sizeOfString(value[i]); - } - return size; -} - -function writeStringArray(value, buffer, offset) { - buffer.writeInt16BE(value.length, offset); - offset += 2; - value.forEach(function(string) { - offset = writeString(string, buffer, offset); - }); - return offset; -} - -function sizeOfPropertyArray(value) { - var size = 4; - for (var i = 0; i < value.length; ++i) { - size += sizeOfString(value[i].key) + types['double'][2] + types['short'][2]; - for (var j = 0; j < value[i].elementList.length; j++) { - size += types['UUID'][2] + types['double'][2] + types['byte'][2]; - } - } - return size; -} - function writeUUID(value, buffer, offset) { buffer.writeInt32BE(value[0], offset); buffer.writeInt32BE(value[1], offset + 4); @@ -876,90 +781,6 @@ function writeUUID(value, buffer, offset) { return offset + 16; } -function writePropertyArray(value, buffer, offset) { - buffer.writeInt32BE(value.length, offset); - offset += 4; - for (var i = 0; i < value.length; ++i) { - offset = writeString(value[i].key, buffer, offset); - offset = writeDouble(value[i].value, buffer, offset); - offset = writeShort(value[i].elementList.length, buffer, offset); - for (var j = 0; j < value[i].elementList.length; j++) { - offset = writeUUID(value[i].elementList[j].uuid, buffer, offset); - offset = writeDouble(value[i].elementList[j].amount, buffer, offset); - offset = writeByte(value[i].elementList[j].operation, buffer, offset); - } - } - return offset; -} - -function readIntArray8(buffer, offset) { - var results = readByte(buffer, offset); - if (! results) return null; - var count = results.value; - var cursor = offset + results.size; - - var cursorEnd = cursor + 4 * count; - if (cursorEnd > buffer.length) return null; - var array = []; - for (var i = 0; i < count; ++i) { - array.push(buffer.readInt32BE(cursor)); - cursor += 4; - } - - return { - value: array, - size: cursorEnd - offset, - }; -} - - -function readByteVectorArray(buffer, offset) { - var results = readInt(buffer, offset); - if (! results) return null; - var count = results.value; - var cursor = offset + results.size; - var cursorEnd = cursor + 3 * count; - if (cursorEnd > buffer.length) return null; - - var array = []; - for (var i = 0; i < count; ++i) { - array.push({ - x: buffer.readInt8(cursor), - y: buffer.readInt8(cursor + 1), - z: buffer.readInt8(cursor + 2), - }); - cursor += 3; - } - return { - value: array, - size: cursorEnd - offset, - }; -} - -function readByteVector(buffer, offset) { - if (offset + 3 > buffer.length) return null; - return { - value: { - x: buffer.readInt8(offset), - y: buffer.readInt8(offset + 1), - z: buffer.readInt8(offset + 2), - }, - size: 3, - }; -} - -function readIntVector(buffer, offset) { - if (offset + 12 > buffer.length) return null; - return { - value: { - x: buffer.readInt32BE(offset), - y: buffer.readInt32BE(offset + 4), - z: buffer.readInt32BE(offset + 8), - }, - size: 12, - }; -} - function readEntityMetadata(buffer, offset) { var cursor = offset; var metadata = []; @@ -976,26 +797,14 @@ function readEntityMetadata(buffer, offset) { } key = item & 0x1f; type = item >> 5; - typeName = entityMetadataTypes[type]; - debug("Reading entity metadata type " + type + " (" + ( typeName || "unknown" ) + ")"); - if (!typeName) { + dataType = entityMetadataTypes[type]; + debug("Reading entity metadata type " + dataType + " (" + ( typeName || "unknown" ) + ")"); + if (!dataType) { return { error: new Error("unrecognized entity metadata type " + type) } } - dataType = types[typeName]; - if (!dataType) { - return { - error: new Error("unrecognized entity metadata type name " + typeName) - } - } - reader = dataType[0]; - if (!reader) { - return { - error: new Error("missing reader for entity metadata type name " + typeName) - } - } - results = reader(buffer, cursor); + results = read(buffer, cursor, dataType, {}); if (! results) return null; metadata.push({ key: key, @@ -1006,105 +815,6 @@ function readEntityMetadata(buffer, offset) { } } -function readObjectData(buffer, offset) { - var cursor = offset + 4; - if (cursor > buffer.length) return null; - var intField = buffer.readInt32BE(offset); - - if (intField === 0) { - return { - value: { - intField: intField, - }, - size: cursor - offset, - }; - } - - if (cursor + 6 > buffer.length) return null; - var velocityX = buffer.readInt16BE(cursor); - cursor += 2; - var velocityY = buffer.readInt16BE(cursor); - cursor += 2; - var velocityZ = buffer.readInt16BE(cursor); - cursor += 2; - - return { - value: { - intField: intField, - velocityX: velocityX, - velocityY: velocityY, - velocityZ: velocityZ, - }, - size: cursor - offset, - }; -} - -function readMapChunkBulk (buffer, offset) { - var cursor = offset + 7; - if (cursor > buffer.length) return null; - var chunkColumnCount = buffer.readInt16BE(offset); - var dataSize = buffer.readInt32BE(offset + 2); - var skyLightSent = !!buffer.readInt8(offset + 6); - - var cursorEnd = cursor + dataSize + 12 * chunkColumnCount; - if (cursorEnd > buffer.length) return null; - - var compressedChunkDataEnd = cursor + dataSize; - var compressedChunkData = buffer.slice(cursor, compressedChunkDataEnd); - cursor = compressedChunkDataEnd; - - var meta = []; - var i, chunkX, chunkZ, bitMap, addBitMap; - for (i = 0; i < chunkColumnCount; ++i) { - chunkX = buffer.readInt32BE(cursor); - cursor += 4; - chunkZ = buffer.readInt32BE(cursor); - cursor += 4; - bitMap = buffer.readUInt16BE(cursor); - cursor += 2; - addBitMap = buffer.readUInt16BE(cursor); - cursor += 2; - - meta.push({ - x: chunkX, - z: chunkZ, - bitMap: bitMap, - addBitMap: addBitMap, - }); - } - - if (chunkColumnCount !== meta.length) { - return { - error: new Error("ChunkColumnCount different from length of meta") - } - } - - return { - value: { - skyLightSent: skyLightSent, - compressedChunkData: compressedChunkData, - meta: meta, - }, - size: cursorEnd - offset, - }; -} - -function readAscii (buffer, offset) { - var results = readShort(buffer, offset); - if (! results) return null; - - var strBegin = offset + results.size; - var strLen = results.value; - var strEnd = strBegin + strLen; - if (strEnd > buffer.length) return null; - var str = buffer.slice(strBegin, strEnd).toString('ascii'); - - return { - value: str, - size: strEnd - offset, - }; -} - function readString (buffer, offset) { var length = readVarInt(buffer, offset); if (!!!length) return null; @@ -1122,80 +832,7 @@ function readString (buffer, offset) { }; } -function readByteArray16 (buffer, offset) { - var results = readShort(buffer, offset); - if (! results) return null; - - var bytesBegin = offset + results.size; - var bytesSize = results.value; - var bytesEnd = bytesBegin + bytesSize; - if (bytesEnd > buffer.length) return null; - var bytes = buffer.slice(bytesBegin, bytesEnd); - - return { - value: bytes, - size: bytesEnd - offset, - }; -} - -function readByteArray32(buffer, offset) { - var results = readInt(buffer, offset); - if (! results) return null; - - var bytesBegin = offset + results.size; - var bytesSize = results.value; - var bytesEnd = bytesBegin + bytesSize; - if (bytesEnd > buffer.length) return null; - var bytes = buffer.slice(bytesBegin, bytesEnd); - - return { - value: bytes, - size: bytesEnd - offset, - }; -} - -function readSlotArray (buffer, offset) { - var results = readShort(buffer, offset); - if (! results) return null; - var count = results.value; - var cursor = offset + results.size; - - var slotArray = []; - for (var i = 0; i < count; ++i) { - results = readSlot(buffer, cursor); - if (! results) return null; - slotArray.push(results.value); - cursor += results.size; - } - - return { - value: slotArray, - size: cursor - offset, - }; -} - -function readStringArray (buffer, offset) { - var results = readShort(buffer, offset); - if (! results) return null; - var count = results.value; - var cursor = offset + results.size; - - var stringArray = []; - for (var i = 0; i < count; ++i) { - results = readString(buffer, cursor); - if (! results) return null; - stringArray.push(results.value); - cursor += results.size; - } - - return { - value: stringArray, - size: cursor - offset, - }; -} - function readUUID(buffer, offset) { - if (offset + 16 > buffer.length) return null; return { value: [ buffer.readInt32BE(offset), @@ -1207,62 +844,6 @@ function readUUID(buffer, offset) { }; } -function readPropertyArray (buffer, offset) { - var results = readInt(buffer, offset); - if (! results) return null; - var count = results.value; - var cursor = offset + results.size; - - var propertyArray = []; - for (var i = 0; i < count; ++i) { - var property = {}; - var elementListLength; - - results = readString(buffer, cursor); - if (! results) return null; - property.key = results.value; - cursor += results.size; - - results = readDouble(buffer, cursor); - if (! results) return null; - property.value = results.value; - cursor += results.size; - - results = readShort(buffer, cursor); - if (! results) return null; - elementListLength = results.value; - cursor += results.size; - - property.elementList = []; - for (var j = 0; j < elementListLength ; j++) { - property.elementList[j] = {}; - - results = readUUID(buffer, cursor); - if (! results) return null; - property.elementList[j].uuid = results.value; - cursor += results.size; - - results = readDouble(buffer, cursor); - if (! results) return null; - property.elementList[j].amount = results.value; - cursor += results.size; - - results = readByte(buffer, cursor); - if (! results) return null; - property.elementList[j].operation = results.value; - cursor += results.size; - - } - - propertyArray.push(property); - } - - return { - value: propertyArray, - size: cursor - offset, - }; -} - function readShort(buffer, offset) { if (offset + 2 > buffer.length) return null; var value = buffer.readInt16BE(offset); @@ -1412,32 +993,6 @@ function writeString(value, buffer, offset) { return offset + length; } -function sizeOfAscii(value) { - return 2 + value.length; -} - -function writeAscii(value, buffer, offset) { - buffer.writeInt16BE(value.length, offset); - offset += 2; - - for (var i = 0; i < value.length; ++i) { - buffer.writeUInt8(value.charCodeAt(i), offset); - offset += 1; - } - return offset; -} - -function sizeOfByteArray16(value) { - assert.ok(Buffer.isBuffer(value), "non buffer passed to ByteArray16Writer"); - return 2 + value.length; -} - -function writeByteArray16(value, buffer, offset) { - buffer.writeInt16BE(value.length, offset); - value.copy(buffer, offset + 2); - return offset + 2 + value.length; -} - function writeByte(value, buffer, offset) { buffer.writeInt8(value, offset); return offset + 1; @@ -1525,79 +1080,114 @@ function writeVarInt(value, buffer, offset) { return offset + cursor + 1; } -function readStatisticArray(buffer, offset) { - var lenWrapper = readVarInt(buffer, offset); - if (!lenWrapper) return null; - var len = lenWrapper.value; - var cursor = offset + lenWrapper.size; - var returnVal = {}; - for (var i = 0; i < len; i++) { - var statNameWrapper = readString(buffer, cursor); - if (!statNameWrapper) return null; - cursor += statNameWrapper.size; - - var valueWrapper = readVarInt(buffer, cursor); - if (!valueWrapper) return null; - cursor += valueWrapper.size; - - returnVal[statNameWrapper.value] = valueWrapper.value; - } - - return { - value: returnVal, - size: cursor - offset - } -} - -function sizeOfStatisticArray(value) { - return Object.keys(value).reduce(function(size, key) { - size += sizeOfString(key); - size += sizeOfVarInt(value[key]); - return size; - }, sizeOfVarInt(Object.keys(value).length)); -} - -function writeStatisticArray(value, buffer, offset) { - var cursor = offset; - cursor = writeVarInt(Object.keys(value).length, buffer, cursor); - Object.keys(value).forEach(function(key) { - cursor = writeString(key, buffer, cursor); - cursor = writeVarInt(value[key], buffer, cursor); - }); - return cursor; -} - -function readMatchArray(buffer, offset) { - var lengthWrapper = readVarInt(buffer, offset); - if (!!!lengthWrapper) return null; - var cursor = offset + lengthWrapper.size; - var matches = []; - for (var i = 0;i < lengthWrapper.value;i++) { - var match = readString(buffer, cursor); - if (!!!match) return null; - cursor += match.size; - matches[i] = match.value; +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). + rootNode.this = results.value; + for (var index in typeArgs.fields) { + var readResults = read(buffer, offset, typeArgs.fields[index], rootNode); + 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) { + rootNode.this = value; + for (var index in typeArgs.fields) { + offset = write(value[typeArgs.fields[index].name], buffer, offset, typeArgs.fields[index], rootNode); + } + delete rootNode.this; + return offset; +} + +function sizeOfContainer(value, typeArgs, rootNode) { + var size = 0; + rootNode.this = value; + for (var index in typeArgs.fields) { + size += sizeOf(value[typeArgs.fields[index].name], typeArgs.fields[index], rootNode); + } + delete rootNode.this; + return size; +} + +function readBuffer(buffer, offset, typeArgs, rootNode) { + var count = getField(typeArgs.count, rootNode); return { - value: matches, - size: cursor - offset + value: buffer.slice(offset, offset + count), + size: count }; } -function sizeOfMatchArray(value) { - var size = sizeOfVarInt(value.length); - for (var s in value) { - size += sizeOfString(s); +function writeBuffer(value, buffer, offset) { + value.copy(buffer, offset); + return offset + value.length; +} + +function sizeOfBuffer(value) { + return value.length; +} + +function readArray(buffer, offset, typeArgs, rootNode) { + var results = { + value: [], + size: 0 + } + var 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; } -function writeMatchArray(value, buffer, offset) { - offset = writeVarInt(value.length, buffer, offset); - for (var s in value) { - offset = writeString(s, buffer, offset); +function getField(countField, rootNode) { + var countFieldArr = countField.split("."); + var count = rootNode; + for (var index in countFieldArr) { + count = count[countFieldArr[index]]; } - return offset; + return count; +} + +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 ? + /*console.log(rootNode); + console.log(typeArgs);*/ + return sizeOf(getField(typeArgs.countFor, rootNode).length, { type: typeArgs.type }, rootNode); } function read(buffer, cursor, fieldInfo, rootNodes) { @@ -1612,12 +1202,11 @@ function read(buffer, cursor, fieldInfo, rootNodes) { } var readResults = type[0](buffer, cursor, fieldInfo.typeArgs, rootNodes); if (readResults.error) return { error: readResults.error }; - return readResults; } -function write(value, buffer, offset, fieldInfo, rootNodes) { - if (fieldInfo.condition && !fieldInfo.condition(rootNodes)) { +function write(value, buffer, offset, fieldInfo, rootNode) { + if (fieldInfo.condition && !fieldInfo.condition(rootNode)) { return null; } var type = types[fieldInfo.type]; @@ -1626,11 +1215,11 @@ function write(value, buffer, offset, fieldInfo, rootNodes) { error: new Error("missing data type: " + fieldInfo.type) }; } - return type[1](value, buffer, offset, fieldInfo.typeArgs); + return type[1](value, buffer, offset, fieldInfo.typeArgs, rootNode); } -function sizeOf(value, fieldInfo, rootNodes) { - if (fieldInfo.condition && !fieldInfo.condition(rootNodes)) { +function sizeOf(value, fieldInfo, rootNode) { + if (fieldInfo.condition && !fieldInfo.condition(rootNode)) { return 0; } var type = types[fieldInfo.type]; @@ -1638,7 +1227,7 @@ function sizeOf(value, fieldInfo, rootNodes) { throw new Error("missing data type: " + fieldInfo.type); } if (typeof type[2] === 'function') { - return type[2](value, fieldInfo.typeArgs); + return type[2](value, fieldInfo.typeArgs, rootNode); } else { return type[2]; } diff --git a/test/test.js b/test/test.js index 8923326..2097bc2 100644 --- a/test/test.js +++ b/test/test.js @@ -61,7 +61,31 @@ var values = { 'ubyte': 8, 'string': "hi hi this is my client string", 'ustring': "hi hi this is my server string", - 'byteArray16': new Buffer(8), + 'buffer': new Buffer(8), + 'array': function(typeArgs) { + if (typeof values[typeArgs.type] === "undefined") { + throw new Error("No data type for " + typeArgs.type); + } + if (typeof values[typeArgs.type] === "function") { + return [values[typeArgs.type](typeArgs.typeArgs)]; + } + return [values[typeArgs.type]]; + }, + 'container': function(typeArgs) { + var results = {}; + for (var index in typeArgs.fields) { + if (typeof values[typeArgs.fields[index].type] === "undefined") { + throw new Error("No data type for " + typeArgs.fields[index].type); + } + if (typeof values[typeArgs.fields[index].type] === "function") { + results[typeArgs.fields[index].name] = values[typeArgs.fields[index].type](typeArgs.fields[index].typeArgs); + } else { + results[typeArgs.fields[index].name] = values[typeArgs.fields[index].type]; + } + } + return results; + }, + 'count': 1, // TODO : might want to set this to a correct value 'bool': true, 'double': 99999.2222, 'float': -333.444, @@ -71,27 +95,7 @@ var values = { itemDamage: 2, nbtData: new Buffer(90), }, - 'ascii': "hello", - 'byteArray32': new Buffer(10), 'long': [0, 1], - 'slotArray': [{ - id: 41, - itemCount: 2, - itemDamage: 3, - nbtData: new Buffer(0), - }], - 'stringArray': ['hello', 'dude'], - 'propertyArray': [{ key: 'generic.maxHealth', value: 1.5, elementList: [ { uuid: [ 123, 456, 78, 90 ], amount: 0.5, operation: 1 } ] }], - 'mapChunkBulk': { - skyLightSent: true, - compressedChunkData: new Buffer(1234), - meta: [{ - x: 23, - z: 64, - bitMap: 3, - addBitMap: 10, - }], - }, 'entityMetadata': [ { key: 17, value: 0, type: 'int' }, { key: 0, value: 0, type: 'byte' }, @@ -106,12 +110,7 @@ var values = { velocityY: 2, velocityZ: 3, }, - 'intArray8': [1, 2, 3, 4], - 'intVector': {x: 1, y: 2, z: 3}, - 'byteVector': {x: 1, y: 2, z: 3}, - 'byteVectorArray': [{x: 1, y: 2, z: 3}], - 'statisticArray': {"stuff": 13, "anotherstuff": 6392}, - 'matchArray': ["hallo", "heya"] + 'UUID': [42, 42, 42, 42] }; describe("packets", function() { @@ -165,7 +164,14 @@ describe("packets", function() { var packet = {}; packetInfo.forEach(function(field) { if (!field.hasOwnProperty("condition") || field.condition(packet)) { - packet[field.name] = values[field.type]; + var fieldVal = values[field.type]; + if (typeof fieldVal === "undefined") { + throw new Error("No value for type " + field.type); + } + if (typeof fieldVal === "function") { + fieldVal = fieldVal(field.typeArgs); + } + packet[field.name] = fieldVal; } }); if (toServer) {