diff --git a/index.js b/index.js index 9b18bfd..5f25446 100644 --- a/index.js +++ b/index.js @@ -2,14 +2,13 @@ var net = require('net') , EventEmitter = require('events').EventEmitter , util = require('util') , assert = require('assert') - , Iconv = require('iconv').Iconv , ursa = require('ursa') , crypto = require('crypto') , superagent = require('superagent') , Batch = require('batch') - , packets = require('./packets') - , toUcs2 = new Iconv('UTF-8', 'utf16be') - , fromUcs2 = new Iconv('utf16be', 'UTF-8') + , protocol = require('./lib/protocol') + , createPacketBuffer = protocol.createPacketBuffer + , parsePacket = protocol.parsePacket exports.createClient = createClient; @@ -25,7 +24,7 @@ function createClient(options) { client.username = options.username; client.on('connect', function() { client.write(0x02, { - protocolVersion: packets.meta.protocolVersion, + protocolVersion: protocol.version, username: options.username, serverHost: host, serverPort: port, @@ -158,7 +157,6 @@ Client.prototype.connect = function(port, host) { parsed = parsePacket(incomingBuffer); if (! parsed) break; packet = parsed.results; - hax(packet); // fuck you, notch incomingBuffer = incomingBuffer.slice(parsed.size); self.emit(packet.id, packet); } @@ -183,567 +181,6 @@ Client.prototype.write = function(packetId, params) { this.socket.write(out); }; -var writers = { - 'int': IntWriter, - 'short': ShortWriter, - 'byte': ByteWriter, - 'ubyte': UByteWriter, - 'string': StringWriter, - 'byteArray16': ByteArray16Writer, - 'bool': BoolWriter, - 'double': DoubleWriter, - 'float': FloatWriter, - 'slot': SlotWriter, -}; - -var readers = { - 'string': readString, - 'ascii': readAscii, - 'byteArray16': readByteArray16, - 'byteArray32': readByteArray32, - 'short': readShort, - 'ushort': readUShort, - 'int': readInt, - 'byte': readByte, - 'ubyte': readUByte, - 'long': readLong, - 'slot': readSlot, - 'bool': readBool, - 'double': readDouble, - 'float': readFloat, - 'slotArray': readSlotArray, - 'mapChunkBulk': readMapChunkBulk, - 'entityMetadata': readEntityMetadata, - 'objectData': readObjectData, - 'intArray': readIntArray, - 'intVector': readIntVector, - 'byteVector': readByteVector, - 'byteVectorArray': readByteVectorArray, -}; - -function readIntArray(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, - }; -} - -var entityMetadataReaders = { - 0: readByte, - 1: readShort, - 2: readInt, - 3: readFloat, - 4: readString, - 5: readSlot, - 6: readIntVector, -}; - -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 = {}; - var item, key, type, results, reader; - while (true) { - if (cursor + 1 > buffer.length) return null; - item = buffer.readUInt8(cursor); - cursor += 1; - if (item === 0x7f) break; - key = item & 0x1f; - type = item >> 5; - reader = entityMetadataReaders[type]; - assert.ok(reader, "missing reader for entity metadata type " + type); - results = reader(buffer, cursor); - if (! results) return null; - metadata[key] = results.value; - cursor += results.size; - } - return { - value: metadata, - size: cursor - 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 chunkCount = buffer.readInt16BE(offset); - var dataSize = buffer.readInt32BE(offset + 2); - var skyLightSent = !!buffer.readInt8(offset + 6); - - var cursorEnd = cursor + dataSize + 12 * chunkCount; - if (cursorEnd > buffer.length) return null; - - var compressedChunkDataEnd = cursor + dataSize; - var compressedChunkData = buffer.slice(cursor, compressedChunkDataEnd); - cursor = compressedChunkDataEnd; - - var meta = []; - var i, chunkX, chunkZ, primaryBitMap, addBitMap; - for (i = 0; i < chunkCount; ++i) { - chunkX = buffer.readInt32BE(cursor); - cursor += 4; - chunkZ = buffer.readInt32BE(cursor); - cursor += 4; - primaryBitMap = buffer.readUInt16BE(cursor); - cursor += 2; - addBitMap = buffer.readUInt16BE(cursor); - cursor += 2; - - meta.push({ - chunkX: chunkX, - chunkZ: chunkZ, - primaryBitMap: primaryBitMap, - addBitMap: addBitMap, - }); - } - - 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 results = readShort(buffer, offset); - if (! results) return null; - - var strBegin = offset + results.size; - var strLen = results.value; - var strEnd = strBegin + strLen * 2; - if (strEnd > buffer.length) return null; - var str = fromUcs2.convert(buffer.slice(strBegin, strEnd)).toString('utf8'); - - return { - value: str, - size: strEnd - 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 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 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; - 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); - return { - value: !!value, - size: 1, - }; -} - -function readSlot(buffer, offset) { - var results = readShort(buffer, offset); - if (! results) return null; - var blockId = results.value; - var cursor = offset + results.size; - - if (blockId === -1) { - return { - value: { id: blockId }, - size: cursor - offset, - }; - } - - var cursorEnd = cursor + 5; - if (cursorEnd > buffer.length) return null; - var itemCount = buffer.readInt8(cursor); - var itemDamage = buffer.readInt16BE(cursor + 1); - var nbtDataSize = buffer.readInt16BE(cursor + 3); - if (nbtDataSize === -1) nbtDataSize = 0; - var nbtDataEnd = cursorEnd + nbtDataSize; - var nbtData = buffer.slice(cursorEnd, nbtDataEnd); - - return { - value: { - id: blockId, - itemCount: itemCount, - itemDamage: itemDamage, - nbtData: nbtData, - }, - size: nbtDataEnd - offset, - }; -} - -function SlotWriter(value) { - this.value = value; - this.size = value.id === -1 ? 2 : 7 + this.value.nbtData.length; -} - -SlotWriter.prototype.write = function(buffer, offset) { - buffer.writeInt16BE(this.value.id, offset); - if (this.value.id === -1) return; - buffer.writeInt8(this.value.itemCount, offset + 2); - buffer.writeInt16BE(this.value.itemDamage, offset + 3); - var nbtDataSize = this.value.nbtData.length; - if (nbtDataSize === 0) nbtDataSize = -1; // I don't know wtf mojang smokes - buffer.writeInt16BE(nbtDataSize, offset + 5); - this.value.nbtData.copy(buffer, offset + 7); -}; - -function StringWriter(value) { - this.value = value; - this.encoded = toUcs2.convert(value); - this.size = 2 + this.encoded.length; -} - -StringWriter.prototype.write = function(buffer, offset) { - buffer.writeInt16BE(this.value.length, offset); - this.encoded.copy(buffer, offset + 2); -}; - -function ByteArray16Writer(value) { - assert.ok(Buffer.isBuffer(value), "non buffer passed to ByteArray16Writer"); - this.value = value; - this.size = 2 + value.length; -} - -ByteArray16Writer.prototype.write = function(buffer, offset) { - buffer.writeInt16BE(this.value.length, offset); - this.value.copy(buffer, offset + 2); -}; - -function ByteWriter(value) { - this.value = value; - this.size = 1; -} - -ByteWriter.prototype.write = function(buffer, offset) { - buffer.writeInt8(this.value, offset); -} - -function BoolWriter(value) { - this.value = value; - this.size = 1; -} - -BoolWriter.prototype.write = function(buffer, offset) { - buffer.writeInt8(this.value ? 1 : 0, offset); -} - -function UByteWriter(value) { - this.value = value; - this.size = 1; -} - -UByteWriter.prototype.write = function(buffer, offset) { - buffer.writeUInt8(this.value, offset); -}; - -function FloatWriter(value) { - this.value = value; - this.size = 4; -} - -FloatWriter.prototype.write = function(buffer, offset) { - buffer.writeFloatBE(this.value, offset); -} - -function DoubleWriter(value) { - this.value = value; - this.size = 8; -} - -DoubleWriter.prototype.write = function(buffer, offset) { - buffer.writeDoubleBE(this.value, offset); -} - -function ShortWriter(value) { - this.value = value; - this.size = 2; -} - -ShortWriter.prototype.write = function(buffer, offset) { - buffer.writeInt16BE(this.value, offset); -} - -function IntWriter(value) { - this.value = value; - this.size = 4; -} - -IntWriter.prototype.write = function(buffer, offset) { - buffer.writeInt32BE(this.value, offset); -} - -function createPacketBuffer(packetId, params) { - var size = 1; - var fields = [ new UByteWriter(packetId) ]; - var packet = packets[packetId]; - packet.forEach(function(fieldInfo) { - var value = params[fieldInfo.name]; - var Writer = writers[fieldInfo.type]; - assert.ok(Writer, "missing writer for data type: " + fieldInfo.type); - var field = new Writer(value); - size += field.size; - fields.push(field); - }); - var buffer = new Buffer(size); - var cursor = 0; - fields.forEach(function(field) { - field.write(buffer, cursor); - cursor += field.size; - }); - return buffer; -} - -function parsePacket(buffer) { - if (buffer.length < 1) return null; - var packetId = buffer.readUInt8(0); - var size = 1; - var results = { id: packetId }; - var packetInfo = packets[packetId]; - assert.ok(packetInfo, "Unrecognized packetId: " + packetId); - var i, fieldInfo, read, readResults; - for (i = 0; i < packetInfo.length; ++i) { - fieldInfo = packetInfo[i]; - read = readers[fieldInfo.type]; - assert.ok(read, "missing reader for data type: " + fieldInfo.type); - readResults = read(buffer, size); - if (readResults) { - results[fieldInfo.name] = readResults.value; - size += readResults.size; - } else { - // buffer needs to be more full - return null; - } - } - return { - size: size, - results: results, - }; -} function mcPubKeyToURsa(mcPubKeyBuffer) { var pem = "-----BEGIN PUBLIC KEY-----\n"; @@ -790,7 +227,7 @@ function getLoginSession(email, password, cb) { req.send({ user: email, password: password, - version: packets.meta.sessionVersion, + version: protocol.sessionVersion, }); req.end(function(err, resp) { var myErr; @@ -822,14 +259,3 @@ function getLoginSession(email, password, cb) { } }); } - -function hax(packet) { - // bullshit post-parsing hax that we have to do due to mojang incompetence - var tmp; - if (packet.id === 0x0d) { - // when 0x0d is sent from the server, the Y and the Stance fields are swapped. - tmp = packet.y; - packet.y = packet.stance; - packet.stance = tmp; - } -} diff --git a/lib/protocol.js b/lib/protocol.js new file mode 100644 index 0000000..a6467be --- /dev/null +++ b/lib/protocol.js @@ -0,0 +1,1019 @@ +var Iconv = require('iconv').Iconv + , assert = require('assert') + , toUcs2 = new Iconv('UTF-8', 'utf16be') + , fromUcs2 = new Iconv('utf16be', 'UTF-8') + +var STRING_MAX_LENGTH = 240; + +exports.version = 51; +exports.sessionVersion = 13; +exports.parsePacket = parsePacket; +exports.createPacketBuffer = createPacketBuffer; + +var packets = { + 0x00: [ + { name: "keepAliveId", type: "int" } + ], + 0x01: [ + { name: "entityId", type: "int" }, + { name: "levelType", type: "string" }, + { name: "gameMode", type: "byte" }, + { name: "dimension", type: "byte" }, + { name: "difficulty", type: "byte" }, + { name: null, type: "byte" }, + { name: "maxPlayers", type: "byte" } + ], + 0x02: [ + { name: "protocolVersion", type: "byte" }, + { name: "username", type: "string" }, + { name: "serverHost", type: "string" }, + { name: "serverPort", type: "int" } + ], + 0x03: [ + { name: "message", type: "string" } + ], + 0x04: [ + { name: "age", type: "long" }, + { name: "time", type: "long" } + ], + 0x05: [ + { name: "entityId", type: "int" }, + { name: "slot", type: "short" }, + { name: "item", type: "slot" } + ], + 0x06: [ + { name: "x", type: "int" }, + { name: "y", type: "int" }, + { name: "z", type: "int" } + ], + 0x07: [ + { name: "user", type: "int" }, + { name: "target", type: "int" }, + { name: "leftClick", type: "bool" } + ], + 0x08: [ + { name: "health", type: "short" }, + { name: "food", type: "short" }, + { name: "foodSaturation", type: "float" } + ], + 0x09: [ + { name: "dimension", type: "int" }, + { name: "difficulty", type: "byte" }, + { name: "gameMode", type: "byte" }, + { name: "worldHeight", type: "short" }, + { name: "levelType", type: "string" } + ], + 0x0a: [ + { name: "onGround", type: "bool" } + ], + 0x0b: [ + { name: "x", type: "double" }, + { name: "y", type: "double" }, + { name: "stance", type: "double" }, + { name: "z", type: "double" }, + { name: "onGround", type: "bool" } + ], + 0x0c: [ + { name: "yaw", type: "float" }, + { name: "pitch", type: "float" }, + { name: "onGround", type: "bool" } + ], + 0x0d: { + toServer: [ + { name: "x", type: "double" }, + { name: "y", type: "double" }, + { name: "stance", type: "double" }, + { name: "z", type: "double" }, + { name: "yaw", type: "float" }, + { name: "pitch", type: "float" }, + { name: "onGround", type: "bool" } + ], + toClient: [ + { name: "x", type: "double" }, + { name: "stance", type: "double" }, + { name: "y", type: "double" }, + { name: "z", type: "double" }, + { name: "yaw", type: "float" }, + { name: "pitch", type: "float" }, + { name: "onGround", type: "bool" } + ], + }, + 0x0e: [ + { name: "status", type: "byte" }, + { name: "x", type: "int" }, + { name: "y", type: "byte" }, + { name: "z", type: "int" }, + { name: "face", type: "byte" } + ], + 0x0f: [ + { name: "x", type: "int" }, + { name: "y", type: "ubyte" }, + { name: "z", type: "int" }, + { name: "direction", type: "byte" }, + { name: "heldItem", type: "slot" }, + { name: "cursorX", type: "byte" }, + { name: "cursorY", type: "byte" }, + { name: "cursorZ", type: "byte" } + ], + 0x10: [ + { name: "slotId", type: "short" } + ], + 0x11: [ + { name: "entityId", type: "int" }, + { name: null, type: "byte" }, + { name: "x", type: "int" }, + { name: "y", type: "byte" }, + { name: "z", type: "int" } + ], + 0x12: [ + { name: "entityId", type: "int" }, + { name: "animation", type: "byte" } + ], + 0x13: [ + { name: "entityId", type: "int" }, + { name: "actionId", type: "byte" } + ], + 0x14: [ + { name: "entityId", type: "int" }, + { name: "name", type: "string" }, + { name: "x", type: "int" }, + { name: "y", type: "int" }, + { name: "z", type: "int" }, + { name: "yaw", type: "byte" }, + { name: "pitch", type: "byte" }, + { name: "currentItem", type: "short" }, + { name: "metadata", type: "entityMetadata" } + ], + 0x16: [ + { name: "collectedId", type: "int" }, + { name: "collectorId", type: "int" } + ], + 0x17: [ + { name: "entityId", type: "int" }, + { name: "type", type: "byte" }, + { name: "x", type: "int" }, + { name: "y", type: "int" }, + { name: "z", type: "int" }, + { name: "yaw", type: "byte" }, + { name: "pitch", type: "byte" }, + { name: "objectData", type: "objectData" } + ], + 0x18: [ + { name: "entityId", type: "int" }, + { name: "type", type: "byte" }, + { name: "x", type: "int" }, + { name: "y", type: "int" }, + { name: "z", type: "int" }, + { name: "yaw", type: "byte" }, + { name: "pitch", type: "byte" }, + { name: "headYaw", type: "byte" }, + { name: "velocityX", type: "short" }, + { name: "velocityY", type: "short" }, + { name: "velocityZ", type: "short" }, + { name: "metadata", type: "entityMetadata" } + ], + 0x19: [ + { name: "entityId", type: "int" }, + { name: "name", type: "string" }, + { name: "x", type: "int" }, + { name: "y", type: "int" }, + { name: "z", type: "int" }, + { name: "direction", type: "int" } + ], + 0x1a: [ + { name: "entityId", type: "int" }, + { name: "x", type: "int" }, + { name: "y", type: "int" }, + { name: "z", type: "int" }, + { name: "count", type: "short" } + ], + 0x1c: [ + { name: "entityId", type: "int" }, + { name: "velocityX", type: "short" }, + { name: "velocityY", type: "short" }, + { name: "velocityZ", type: "short" } + ], + 0x1d: [ + { name: "entityIds", type: "intArray8" } + ], + 0x1e: [ + { name: "entityId", type: "int" } + ], + 0x1f: [ + { name: "entityId", type: "int" }, + { name: "dx", type: "byte" }, + { name: "dy", type: "byte" }, + { name: "dz", type: "byte" } + ], + 0x20: [ + { name: "entityId", type: "int" }, + { name: "yaw", type: "byte" }, + { name: "pitch", type: "byte" } + ], + 0x21: [ + { name: "entityId", type: "int" }, + { name: "dx", type: "byte" }, + { name: "dy", type: "byte" }, + { name: "dz", type: "byte" }, + { name: "yaw", type: "byte" }, + { name: "pitch", type: "byte" } + ], + 0x22: [ + { name: "entityId", type: "int" }, + { name: "x", type: "int" }, + { name: "y", type: "int" }, + { name: "z", type: "int" }, + { name: "yaw", type: "byte" }, + { name: "pitch", type: "byte" } + ], + 0x23: [ + { name: "entityId", type: "int" }, + { name: "headYaw", type: "byte" } + ], + 0x26: [ + { name: "entityId", type: "int" }, + { name: "status", type: "byte" } + ], + 0x27: [ + { name: "entityId", type: "int" }, + { name: "vehicleId", type: "int" } + ], + 0x28: [ + { name: "entityId", type: "int" }, + { name: "metadata", type: "entityMetadata" } + ], + 0x29: [ + { name: "entityId", type: "int" }, + { name: "effectId", type: "byte" }, + { name: "amplifier", type: "byte" }, + { name: "duration", type: "short" } + ], + 0x2a: [ + { name: "entityId", type: "int" }, + { name: "effectId", type: "byte" } + ], + 0x2b: [ + { name: "experienceBar", type: "float" }, + { name: "level", type: "short" }, + { name: "totalExperience", type: "short" } + ], + 0x33: [ + { name: "x", type: "int" }, + { name: "z", type: "int" }, + { name: "groundUp", type: "bool" }, + { name: "bitMap", type: "ushort" }, + { name: "addBitMap", type: "ushort" }, + { name: "compressedData", type: "byteArray32" } + ], + 0x34: [ + { name: "chunkX", type: "int" }, + { name: "chunkZ", type: "int" }, + { name: "recordCount", type: "short" }, + { name: "data", type: "byteArray32" } + ], + 0x35: [ + { name: "x", type: "int" }, + { name: "y", type: "byte" }, + { name: "z", type: "int" }, + { name: "type", type: "short" }, + { name: "metadata", type: "byte" } + ], + 0x36: [ + { name: "x", type: "int" }, + { name: "y", type: "short" }, + { name: "z", type: "int" }, + { name: "byte1", type: "byte" }, + { name: "byte2", type: "byte" }, + { name: "blockId", type: "short" } + ], + 0x37: [ + { name: "entityId", type: "int" }, + { name: "x", type: "int" }, + { name: "y", type: "int" }, + { name: "z", type: "int" }, + { name: "destroyStage", type: "byte" } + ], + 0x38: [ + { name: "data", type: "mapChunkBulk" } + ], + 0x3c: [ + { name: "x", type: "double" }, + { name: "y", type: "double" }, + { name: "z", type: "double" }, + { name: "radius", type: "float" }, + { name: "affectedBlockOffsets", type: "byteVectorArray" }, + { name: "playerMotionX", type: "float" }, + { name: "playerMotionY", type: "float" }, + { name: "playerMotionZ", type: "float" } + ], + 0x3d: [ + { name: "effectId", type: "int" }, + { name: "x", type: "int" }, + { name: "y", type: "byte" }, + { name: "z", type: "int" }, + { name: "data", type: "int" }, + { name: "global", type: "bool" } + ], + 0x3e: [ + { name: "soundName", type: "string" }, + { name: "x", type: "int" }, + { name: "y", type: "int" }, + { name: "z", type: "int" }, + { name: "volume", type: "float" }, + { name: "pitch", type: "byte" } + ], + 0x46: [ + { name: "reason", type: "byte" }, + { name: "gameMode", type: "byte" } + ], + 0x47: [ + { name: "entityId", type: "int" }, + { name: "type", type: "byte" }, + { name: "x", type: "int" }, + { name: "y", type: "int" }, + { name: "z", type: "int" } + ], + 0x64: [ + { name: "windowId", type: "byte" }, + { name: "inventoryType", type: "byte" }, + { name: "windowTitle", type: "string" }, + { name: "slotCount", type: "byte" } + ], + 0x65: [ + { name: "windowId", type: "byte" } + ], + 0x66: [ + { name: "windowId", type: "byte" }, + { name: "slot", type: "short" }, + { name: "mouseButton", type: "byte" }, + { name: "action", type: "short" }, + { name: "shift", type: "bool" }, + { name: "item", type: "slot" } + ], + 0x67: [ + { name: "windowId", type: "byte" }, + { name: "slot", type: "short" }, + { name: "item", type: "slot" } + ], + 0x68: [ + { name: "windowId", type: "byte" }, + { name: "items", type: "slotArray" } + ], + 0x69: [ + { name: "windowId", type: "byte" }, + { name: "property", type: "short" }, + { name: "value", type: "short" } + ], + 0x6a: [ + { name: "windowId", type: "byte" }, + { name: "action", type: "short" }, + { name: "accepted", type: "bool" } + ], + 0x6b: [ + { name: "slot", type: "short" }, + { name: "item", type: "slot" } + ], + 0x6c: [ + { name: "windowId", type: "byte" }, + { name: "enchantment", type: "byte" } + ], + 0x82: [ + { name: "x", type: "int" }, + { name: "y", type: "short" }, + { name: "z", type: "int" }, + { name: "text1", type: "string" }, + { name: "text2", type: "string" }, + { name: "text3", type: "string" }, + { name: "text4", type: "string" } + ], + 0x83: [ + { name: "type", type: "short" }, + { name: "itemId", type: "short" }, + { name: "text", type: "ascii" } + ], + 0x84: [ + { name: "x", type: "int" }, + { name: "y", type: "short" }, + { name: "z", type: "int" }, + { name: "action", type: "byte" }, + { name: "nbtData", type: "byteArray16" } + ], + 0xc8: [ + { name: "statisticId", type: "int" }, + { name: "amount", type: "byte" } + ], + 0xc9: [ + { name: "playerName", type: "string" }, + { name: "online", type: "bool" }, + { name: "ping", type: "short" } + ], + 0xca: [ + { name: "flags", type: "byte" }, + { name: "flyingSpeed", type: "byte" }, + { name: "walkingSpeed", type: "byte" } + ], + 0xcb: [ + { name: "text", type: "string" } + ], + 0xcc: [ + { name: "locale", type: "string" }, + { name: "viewDistance", type: "byte" }, + { name: "chatFlags", type: "byte" }, + { name: "difficulty", type: "byte" }, + { name: "showCape", type: "bool" } + ], + 0xcd: [ + { name: "payload", type: "byte" } + ], + 0xfa: [ + { name: "channel", type: "string" }, + { name: "data", type: "byteArray16" } + ], + 0xfc: [ + { name: "sharedSecret", type: "byteArray16" }, + { name: "verifyToken", type: "byteArray16" } + ], + 0xfd: [ + { name: "serverId", type: "string" }, + { name: "publicKey", type: "byteArray16" }, + { name: "verifyToken", type: "byteArray16" } + ], + 0xfe: [ + { name: "magic", type: "byte" } + ], + 0xff: [ + { name: "reason", type: "string" } + ] +}; + +function get(packetId, toServer) { + var packetInfo = packets[packetId]; + return Array.isArray(packetInfo) ? + packetInfo : + toServer ? + packetInfo.toServer : + packetInfo.toClient; +} + +var writers = { + 'int': IntWriter, + 'short': ShortWriter, + 'byte': ByteWriter, + 'ubyte': UByteWriter, + 'string': StringWriter, + 'byteArray16': ByteArray16Writer, + 'bool': BoolWriter, + 'double': DoubleWriter, + 'float': FloatWriter, + 'slot': SlotWriter, +}; + +var readers = { + 'string': readString, + 'ascii': readAscii, + 'byteArray16': readByteArray16, + 'byteArray32': readByteArray32, + 'short': readShort, + 'ushort': readUShort, + 'int': readInt, + 'byte': readByte, + 'ubyte': readUByte, + 'long': readLong, + 'slot': readSlot, + 'bool': readBool, + 'double': readDouble, + 'float': readFloat, + 'slotArray': readSlotArray, + 'mapChunkBulk': readMapChunkBulk, + 'entityMetadata': readEntityMetadata, + 'objectData': readObjectData, + 'intArray8': readIntArray8, + 'intVector': readIntVector, + 'byteVector': readByteVector, + 'byteVectorArray': readByteVectorArray, +}; + +var entityMetadataReaders = { + 0: readByte, + 1: readShort, + 2: readInt, + 3: readFloat, + 4: readString, + 5: readSlot, + 6: readIntVector, +}; + +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 = {}; + var item, key, type, results, reader; + while (true) { + if (cursor + 1 > buffer.length) return null; + item = buffer.readUInt8(cursor); + cursor += 1; + if (item === 0x7f) break; + key = item & 0x1f; + type = item >> 5; + reader = entityMetadataReaders[type]; + assert.ok(reader, "missing reader for entity metadata type " + type); + results = reader(buffer, cursor); + if (! results) return null; + metadata[key] = results.value; + cursor += results.size; + } + return { + value: metadata, + size: cursor - 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 chunkCount = buffer.readInt16BE(offset); + var dataSize = buffer.readInt32BE(offset + 2); + var skyLightSent = !!buffer.readInt8(offset + 6); + + var cursorEnd = cursor + dataSize + 12 * chunkCount; + if (cursorEnd > buffer.length) return null; + + var compressedChunkDataEnd = cursor + dataSize; + var compressedChunkData = buffer.slice(cursor, compressedChunkDataEnd); + cursor = compressedChunkDataEnd; + + var meta = []; + var i, chunkX, chunkZ, primaryBitMap, addBitMap; + for (i = 0; i < chunkCount; ++i) { + chunkX = buffer.readInt32BE(cursor); + cursor += 4; + chunkZ = buffer.readInt32BE(cursor); + cursor += 4; + primaryBitMap = buffer.readUInt16BE(cursor); + cursor += 2; + addBitMap = buffer.readUInt16BE(cursor); + cursor += 2; + + meta.push({ + chunkX: chunkX, + chunkZ: chunkZ, + primaryBitMap: primaryBitMap, + addBitMap: addBitMap, + }); + } + + 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 results = readShort(buffer, offset); + if (! results) return null; + + var strBegin = offset + results.size; + var strLen = results.value; + var strEnd = strBegin + strLen * 2; + if (strEnd > buffer.length) return null; + var str = fromUcs2.convert(buffer.slice(strBegin, strEnd)).toString('utf8'); + + return { + value: str, + size: strEnd - 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 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 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; + 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); + return { + value: !!value, + size: 1, + }; +} + +function readSlot(buffer, offset) { + var results = readShort(buffer, offset); + if (! results) return null; + var blockId = results.value; + var cursor = offset + results.size; + + if (blockId === -1) { + return { + value: { id: blockId }, + size: cursor - offset, + }; + } + + var cursorEnd = cursor + 5; + if (cursorEnd > buffer.length) return null; + var itemCount = buffer.readInt8(cursor); + var itemDamage = buffer.readInt16BE(cursor + 1); + var nbtDataSize = buffer.readInt16BE(cursor + 3); + if (nbtDataSize === -1) nbtDataSize = 0; + var nbtDataEnd = cursorEnd + nbtDataSize; + var nbtData = buffer.slice(cursorEnd, nbtDataEnd); + + return { + value: { + id: blockId, + itemCount: itemCount, + itemDamage: itemDamage, + nbtData: nbtData, + }, + size: nbtDataEnd - offset, + }; +} + +function SlotWriter(value) { + this.value = value; + this.size = value.id === -1 ? 2 : 7 + this.value.nbtData.length; +} + +SlotWriter.prototype.write = function(buffer, offset) { + buffer.writeInt16BE(this.value.id, offset); + if (this.value.id === -1) return; + buffer.writeInt8(this.value.itemCount, offset + 2); + buffer.writeInt16BE(this.value.itemDamage, offset + 3); + var nbtDataSize = this.value.nbtData.length; + if (nbtDataSize === 0) nbtDataSize = -1; // I don't know wtf mojang smokes + buffer.writeInt16BE(nbtDataSize, offset + 5); + this.value.nbtData.copy(buffer, offset + 7); +}; + +function StringWriter(value) { + this.value = value; + this.encoded = toUcs2.convert(value); + this.size = 2 + this.encoded.length; +} + +StringWriter.prototype.write = function(buffer, offset) { + buffer.writeInt16BE(this.value.length, offset); + this.encoded.copy(buffer, offset + 2); +}; + +function ByteArray16Writer(value) { + assert.ok(Buffer.isBuffer(value), "non buffer passed to ByteArray16Writer"); + this.value = value; + this.size = 2 + value.length; +} + +ByteArray16Writer.prototype.write = function(buffer, offset) { + buffer.writeInt16BE(this.value.length, offset); + this.value.copy(buffer, offset + 2); +}; + +function ByteWriter(value) { + this.value = value; + this.size = 1; +} + +ByteWriter.prototype.write = function(buffer, offset) { + buffer.writeInt8(this.value, offset); +} + +function BoolWriter(value) { + this.value = value; + this.size = 1; +} + +BoolWriter.prototype.write = function(buffer, offset) { + buffer.writeInt8(this.value ? 1 : 0, offset); +} + +function UByteWriter(value) { + this.value = value; + this.size = 1; +} + +UByteWriter.prototype.write = function(buffer, offset) { + buffer.writeUInt8(this.value, offset); +}; + +function FloatWriter(value) { + this.value = value; + this.size = 4; +} + +FloatWriter.prototype.write = function(buffer, offset) { + buffer.writeFloatBE(this.value, offset); +} + +function DoubleWriter(value) { + this.value = value; + this.size = 8; +} + +DoubleWriter.prototype.write = function(buffer, offset) { + buffer.writeDoubleBE(this.value, offset); +} + +function ShortWriter(value) { + this.value = value; + this.size = 2; +} + +ShortWriter.prototype.write = function(buffer, offset) { + buffer.writeInt16BE(this.value, offset); +} + +function IntWriter(value) { + this.value = value; + this.size = 4; +} + +IntWriter.prototype.write = function(buffer, offset) { + buffer.writeInt32BE(this.value, offset); +} + +function createPacketBuffer(packetId, params) { + var size = 1; + var fields = [ new UByteWriter(packetId) ]; + var packet = get(packetId); + packet.forEach(function(fieldInfo) { + var value = params[fieldInfo.name]; + var Writer = writers[fieldInfo.type]; + assert.ok(Writer, "missing writer for data type: " + fieldInfo.type); + var field = new Writer(value); + size += field.size; + fields.push(field); + }); + var buffer = new Buffer(size); + var cursor = 0; + fields.forEach(function(field) { + field.write(buffer, cursor); + cursor += field.size; + }); + return buffer; +} + +function parsePacket(buffer) { + if (buffer.length < 1) return null; + var packetId = buffer.readUInt8(0); + var size = 1; + var results = { id: packetId }; + var packetInfo = get(packetId); + assert.ok(packetInfo, "Unrecognized packetId: " + packetId); + var i, fieldInfo, read, readResults; + for (i = 0; i < packetInfo.length; ++i) { + fieldInfo = packetInfo[i]; + read = readers[fieldInfo.type]; + assert.ok(read, "missing reader for data type: " + fieldInfo.type); + readResults = read(buffer, size); + if (readResults) { + results[fieldInfo.name] = readResults.value; + size += readResults.size; + } else { + // buffer needs to be more full + return null; + } + } + return { + size: size, + results: results, + }; +} diff --git a/packets.js b/packets.js deleted file mode 100644 index e6ebc29..0000000 --- a/packets.js +++ /dev/null @@ -1,1250 +0,0 @@ -module.exports = { - meta: { - protocolVersion: 51, - sessionVersion: 13 - }, - 0x00: [ - { - name: "keepAliveId", - type: "int" - } - ], - 0x01: [ - { - name: "entityId", - type: "int" - }, - { - name: "levelType", - type: "string" - }, - { - name: "gameMode", - type: "byte" - }, - { - name: "dimension", - type: "byte" - }, - { - name: "difficulty", - type: "byte" - }, - { - name: "_notUsed1", - type: "byte" - }, - { - name: "maxPlayers", - type: "byte" - } - ], - 0x02: [ - { - name: "protocolVersion", - type: "byte" - }, - { - name: "username", - type: "string" - }, - { - name: "serverHost", - type: "string" - }, - { - name: "serverPort", - type: "int" - } - ], - 0x03: [ - { - name: "message", - type: "string" - } - ], - 0x04: [ - { - name: "ageOfWorld", - type: "long" - }, - { - name: "timeOfDay", - type: "long" - } - ], - 0x05: [ - { - name: "entityId", - type: "int" - }, - { - name: "slot", - type: "short" - }, - { - name: "item", - type: "slot" - } - ], - 0x06: [ - { - name: "x", - type: "int" - }, - { - name: "y", - type: "int" - }, - { - name: "z", - type: "int" - } - ], - 0x07: [ - { - name: "user", - type: "int" - }, - { - name: "target", - type: "int" - }, - { - name: "mouseButton", - type: "bool" - } - ], - 0x08: [ - { - name: "health", - type: "short" - }, - { - name: "food", - type: "short" - }, - { - name: "foodSaturation", - type: "float" - } - ], - 0x09: [ - { - name: "dimension", - type: "int" - }, - { - name: "difficulty", - type: "byte" - }, - { - name: "gameMode", - type: "byte" - }, - { - name: "worldHeight", - type: "short" - }, - { - name: "levelType", - type: "string" - } - ], - 0x0a: [ - { - name: "onGround", - type: "bool" - } - ], - 0x0b: [ - { - name: "x", - type: "double" - }, - { - name: "y", - type: "double" - }, - { - name: "stance", - type: "double" - }, - { - name: "z", - type: "double" - }, - { - name: "onGround", - type: "bool" - } - ], - 0x0c: [ - { - name: "yaw", - type: "float" - }, - { - name: "pitch", - type: "float" - }, - { - name: "onGround", - type: "bool" - } - ], - 0x0d: [ - { - name: "x", - type: "double" - }, - { - name: "y", - type: "double" - }, - { - name: "stance", - type: "double" - }, - { - name: "z", - type: "double" - }, - { - name: "yaw", - type: "float" - }, - { - name: "pitch", - type: "float" - }, - { - name: "onGround", - type: "bool" - } - ], - 0x0e: [ - { - name: "status", - type: "byte" - }, - { - name: "x", - type: "int" - }, - { - name: "y", - type: "byte" - }, - { - name: "z", - type: "int" - }, - { - name: "face", - type: "byte" - } - ], - 0x0f: [ - { - name: "x", - type: "int" - }, - { - name: "y", - type: "ubyte" - }, - { - name: "z", - type: "int" - }, - { - name: "direction", - type: "byte" - }, - { - name: "heldItem", - type: "slot" - }, - { - name: "cursorX", - type: "byte" - }, - { - name: "cursorY", - type: "byte" - }, - { - name: "cursorZ", - type: "byte" - } - ], - 0x10: [ - { - name: "slotId", - type: "short" - } - ], - 0x11: [ - { - name: "entityId", - type: "int" - }, - { - name: "_unknown", - type: "byte" - }, - { - name: "x", - type: "int" - }, - { - name: "y", - type: "byte" - }, - { - name: "z", - type: "int" - } - ], - 0x12: [ - { - name: "entityId", - type: "int" - }, - { - name: "animation", - type: "byte" - } - ], - 0x13: [ - { - name: "entityId", - type: "int" - }, - { - name: "actionId", - type: "byte" - } - ], - 0x14: [ - { - name: "entityId", - type: "int" - }, - { - name: "playerName", - type: "string" - }, - { - name: "x", - type: "int" - }, - { - name: "y", - type: "int" - }, - { - name: "z", - type: "int" - }, - { - name: "yaw", - type: "byte" - }, - { - name: "pitch", - type: "byte" - }, - { - name: "currentItem", - type: "short" - }, - { - name: "metadata", - type: "entityMetadata" - } - ], - 0x16: [ - { - name: "collectedEntityId", - type: "int" - }, - { - name: "collectorEntityId", - type: "int" - } - ], - 0x17: [ - { - name: "entityId", - type: "int" - }, - { - name: "type", - type: "byte" - }, - { - name: "x", - type: "int" - }, - { - name: "y", - type: "int" - }, - { - name: "z", - type: "int" - }, - { - name: "yaw", - type: "byte" - }, - { - name: "pitch", - type: "byte" - }, - { - name: "objectData", - type: "objectData" - } - ], - 0x18: [ - { - name: "entityId", - type: "int" - }, - { - name: "type", - type: "byte" - }, - { - name: "x", - type: "int" - }, - { - name: "y", - type: "int" - }, - { - name: "z", - type: "int" - }, - { - name: "yaw", - type: "byte" - }, - { - name: "pitch", - type: "byte" - }, - { - name: "headYaw", - type: "byte" - }, - { - name: "velocityX", - type: "short" - }, - { - name: "velocityY", - type: "short" - }, - { - name: "velocityZ", - type: "short" - }, - { - name: "metadata", - type: "entityMetadata" - } - ], - 0x19: [ - { - name: "entityId", - type: "int" - }, - { - name: "title", - type: "string" - }, - { - name: "x", - type: "int" - }, - { - name: "y", - type: "int" - }, - { - name: "z", - type: "int" - }, - { - name: "direction", - type: "int" - } - ], - 0x1a: [ - { - name: "entityId", - type: "int" - }, - { - name: "x", - type: "int" - }, - { - name: "y", - type: "int" - }, - { - name: "z", - type: "int" - }, - { - name: "count", - type: "short" - } - ], - 0x1c: [ - { - name: "entityId", - type: "int" - }, - { - name: "velocityX", - type: "short" - }, - { - name: "velocityY", - type: "short" - }, - { - name: "velocityZ", - type: "short" - } - ], - 0x1d: [ - { - name: "entityIds", - type: "intArray" - } - ], - 0x1e: [ - { - name: "entityId", - type: "int" - } - ], - 0x1f: [ - { - name: "entityId", - type: "int" - }, - { - name: "deltaX", - type: "byte" - }, - { - name: "deltaY", - type: "byte" - }, - { - name: "deltaZ", - type: "byte" - } - ], - 0x20: [ - { - name: "entityId", - type: "int" - }, - { - name: "yaw", - type: "byte" - }, - { - name: "pitch", - type: "byte" - } - ], - 0x21: [ - { - name: "entityId", - type: "int" - }, - { - name: "deltaX", - type: "byte" - }, - { - name: "deltaY", - type: "byte" - }, - { - name: "deltaZ", - type: "byte" - }, - { - name: "yaw", - type: "byte" - }, - { - name: "pitch", - type: "byte" - } - ], - 0x22: [ - { - name: "entityId", - type: "int" - }, - { - name: "x", - type: "int" - }, - { - name: "y", - type: "int" - }, - { - name: "z", - type: "int" - }, - { - name: "yaw", - type: "byte" - }, - { - name: "pitch", - type: "byte" - } - ], - 0x23: [ - { - name: "entityId", - type: "int" - }, - { - name: "headYaw", - type: "byte" - } - ], - 0x26: [ - { - name: "entityId", - type: "int" - }, - { - name: "entityStatus", - type: "byte" - } - ], - 0x27: [ - { - name: "entityId", - type: "int" - }, - { - name: "vehicleId", - type: "int" - } - ], - 0x28: [ - { - name: "entityId", - type: "int" - }, - { - name: "metadata", - type: "entityMetadata" - } - ], - 0x29: [ - { - name: "entityId", - type: "int" - }, - { - name: "effectId", - type: "byte" - }, - { - name: "amplifier", - type: "byte" - }, - { - name: "duration", - type: "short" - } - ], - 0x2a: [ - { - name: "entityId", - type: "int" - }, - { - name: "effectId", - type: "byte" - } - ], - 0x2b: [ - { - name: "experienceBar", - type: "float" - }, - { - name: "level", - type: "short" - }, - { - name: "totalExperience", - type: "short" - } - ], - 0x33: [ - { - name: "x", - type: "int" - }, - { - name: "z", - type: "int" - }, - { - name: "groundUpContinuous", - type: "bool" - }, - { - name: "primaryBitMap", - type: "ushort" - }, - { - name: "addBitMap", - type: "ushort" - }, - { - name: "compressedData", - type: "byteArray32" - } - ], - 0x34: [ - { - name: "chunkX", - type: "int" - }, - { - name: "chunkZ", - type: "int" - }, - { - name: "recordCount", - type: "short" - }, - { - name: "data", - type: "byteArray32" - } - ], - 0x35: [ - { - name: "x", - type: "int" - }, - { - name: "y", - type: "byte" - }, - { - name: "z", - type: "int" - }, - { - name: "blockType", - type: "short" - }, - { - name: "blockMetadata", - type: "byte" - } - ], - 0x36: [ - { - name: "x", - type: "int" - }, - { - name: "y", - type: "short" - }, - { - name: "z", - type: "int" - }, - { - name: "byte1", - type: "byte" - }, - { - name: "byte2", - type: "byte" - }, - { - name: "blockId", - type: "short" - } - ], - 0x37: [ - { - name: "entityId", - type: "int" - }, - { - name: "x", - type: "int" - }, - { - name: "y", - type: "int" - }, - { - name: "z", - type: "int" - }, - { - name: "destroyStage", - type: "byte" - } - ], - 0x38: [ - { - name: "data", - type: "mapChunkBulk" - } - ], - 0x3c: [ - { - name: "x", - type: "double" - }, - { - name: "y", - type: "double" - }, - { - name: "z", - type: "double" - }, - { - name: "radius", - type: "float" - }, - { - name: "affectedBlockOffsets", - type: "byteVectorArray" - }, - { - name: "playerMotionX", - type: "float" - }, - { - name: "playerMotionY", - type: "float" - }, - { - name: "playerMotionZ", - type: "float" - } - ], - 0x3d: [ - { - name: "effectId", - type: "int" - }, - { - name: "x", - type: "int" - }, - { - name: "y", - type: "byte" - }, - { - name: "z", - type: "int" - }, - { - name: "data", - type: "int" - }, - { - name: "disableRelativeVolume", - type: "bool" - } - ], - 0x3e: [ - { - name: "soundName", - type: "string" - }, - { - name: "x", - type: "int" - }, - { - name: "y", - type: "int" - }, - { - name: "z", - type: "int" - }, - { - name: "volume", - type: "float" - }, - { - name: "pitch", - type: "byte" - } - ], - 0x46: [ - { - name: "reason", - type: "byte" - }, - { - name: "gameMode", - type: "byte" - } - ], - 0x47: [ - { - name: "entityId", - type: "int" - }, - { - name: "type", - type: "byte" - }, - { - name: "x", - type: "int" - }, - { - name: "y", - type: "int" - }, - { - name: "z", - type: "int" - } - ], - 0x64: [ - { - name: "windowId", - type: "byte" - }, - { - name: "inventoryType", - type: "byte" - }, - { - name: "windowTitle", - type: "string" - }, - { - name: "slotCount", - type: "byte" - } - ], - 0x65: [ - { - name: "windowId", - type: "byte" - } - ], - 0x66: [ - { - name: "windowId", - type: "byte" - }, - { - name: "slot", - type: "short" - }, - { - name: "mouseButton", - type: "byte" - }, - { - name: "actionNumber", - type: "short" - }, - { - name: "shift", - type: "bool" - }, - { - name: "clickedItem", - type: "slot" - } - ], - 0x67: [ - { - name: "windowId", - type: "byte" - }, - { - name: "slotId", - type: "short" - }, - { - name: "slot", - type: "slot" - } - ], - 0x68: [ - { - name: "windowId", - type: "byte" - }, - { - name: "slots", - type: "slotArray" - } - ], - 0x69: [ - { - name: "windowId", - type: "byte" - }, - { - name: "property", - type: "short" - }, - { - name: "value", - type: "short" - } - ], - 0x6a: [ - { - name: "windowId", - type: "byte" - }, - { - name: "actionNumber", - type: "short" - }, - { - name: "accepted", - type: "bool" - } - ], - 0x6b: [ - { - name: "slot", - type: "short" - }, - { - name: "clickedItem", - type: "slot" - } - ], - 0x6c: [ - { - name: "windowId", - type: "byte" - }, - { - name: "enchantment", - type: "byte" - } - ], - 0x82: [ - { - name: "x", - type: "int" - }, - { - name: "y", - type: "short" - }, - { - name: "z", - type: "int" - }, - { - name: "text1", - type: "string" - }, - { - name: "text2", - type: "string" - }, - { - name: "text3", - type: "string" - }, - { - name: "text4", - type: "string" - } - ], - 0x83: [ - { - name: "itemType", - type: "short" - }, - { - name: "itemId", - type: "short" - }, - { - name: "text", - type: "ascii" - } - ], - 0x84: [ - { - name: "x", - type: "int" - }, - { - name: "y", - type: "short" - }, - { - name: "z", - type: "int" - }, - { - name: "action", - type: "byte" - }, - { - name: "nbtData", - type: "byteArray16" - } - ], - 0xc8: [ - { - name: "statisticId", - type: "int" - }, - { - name: "amount", - type: "byte" - } - ], - 0xc9: [ - { - name: "playerName", - type: "string" - }, - { - name: "online", - type: "bool" - }, - { - name: "ping", - type: "short" - } - ], - 0xca: [ - { - name: "flags", - type: "byte" - }, - { - name: "flyingSpeed", - type: "byte" - }, - { - name: "walkingSpeed", - type: "byte" - } - ], - 0xcb: [ - { - name: "text", - type: "string" - } - ], - 0xcc: [ - { - name: "locale", - type: "string" - }, - { - name: "viewDistance", - type: "byte" - }, - { - name: "chatFlags", - type: "byte" - }, - { - name: "difficulty", - type: "byte" - }, - { - name: "showCape", - type: "bool" - } - ], - 0xcd: [ - { - name: "payload", - type: "byte" - } - ], - 0xfa: [ - { - name: "channel", - type: "string" - }, - { - name: "data", - type: "byteArray16" - } - ], - 0xfc: [ - { - name: "sharedSecret", - type: "byteArray16" - }, - { - name: "verifyToken", - type: "byteArray16" - } - ], - 0xfd: [ - { - name: "serverId", - type: "string" - }, - { - name: "publicKey", - type: "byteArray16" - }, - { - name: "verifyToken", - type: "byteArray16" - } - ], - 0xfe: [ - { - name: "magic", - type: "byte" - } - ], - 0xff: [ - { - name: "reason", - type: "string" - } - ] -};