diff --git a/package.json b/package.json index 6ac9579..066a241 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "scripts": { "prepublish": "require-self", - "test": "mocha --recursive --reporter spec", + "test": "mocha --recursive --reporter spec --exit", "lint": "standard", "fix": "standard --fix", "pretest": "npm run lint" @@ -39,7 +39,7 @@ "mocha": "^7.0.1", "power-assert": "^1.0.0", "require-self": "^0.2.1", - "standard": "^14.0.0" + "standard": "^14.3.4" }, "dependencies": { "yggdrasil": "^1.3.0", @@ -50,8 +50,8 @@ "lodash.merge": "^4.3.0", "minecraft-data": "^2.44.0", "node-rsa": "^0.4.2", - "prismarine-nbt": "^1.2.1", - "protodef": "^1.6.7", + "prismarine-nbt": "^1.3.0", + "protodef": "^1.7.0", "readable-stream": "^3.0.6", "uuid-1345": "^1.0.1" } diff --git a/src/datatypes/compiler-minecraft.js b/src/datatypes/compiler-minecraft.js new file mode 100644 index 0000000..f91d667 --- /dev/null +++ b/src/datatypes/compiler-minecraft.js @@ -0,0 +1,71 @@ +const UUID = require('uuid-1345') +const minecraft = require('./minecraft') + +module.exports = { + Read: { + UUID: ['native', (buffer, offset) => { + return { + value: UUID.stringify(buffer.slice(offset, 16 + offset)), + size: 16 + } + }], + restBuffer: ['native', (buffer, offset) => { + return { + value: buffer.slice(offset), + size: buffer.length - offset + } + }], + nbt: ['native', minecraft.nbt[0]], + optionalNbt: ['native', minecraft.optionalNbt[0]], + compressedNbt: ['native', minecraft.compressedNbt[0]], + entityMetadataLoop: ['parametrizable', (compiler, { type, endVal }) => { + let code = 'let cursor = offset\n' + code += 'const data = []\n' + code += 'while (true) {\n' + code += ` if (ctx.u8(buffer, cursor).value === ${endVal}) return { value: data, size: cursor + 1 - offset }\n` + code += ' const elem = ' + compiler.callType(type, 'cursor') + '\n' + code += ' data.push(elem.value)\n' + code += ' cursor += elem.size\n' + code += '}' + return compiler.wrapCode(code) + }] + }, + Write: { + UUID: ['native', (value, buffer, offset) => { + const buf = UUID.parse(value) + buf.copy(buffer, offset) + return offset + 16 + }], + restBuffer: ['native', (value, buffer, offset) => { + value.copy(buffer, offset) + return offset + value.length + }], + nbt: ['native', minecraft.nbt[1]], + optionalNbt: ['native', minecraft.optionalNbt[1]], + compressedNbt: ['native', minecraft.compressedNbt[1]], + entityMetadataLoop: ['parametrizable', (compiler, { type, endVal }) => { + let code = 'for (const i in value) {\n' + code += ' offset = ' + compiler.callType('value[i]', type) + '\n' + code += '}\n' + code += `return offset + ctx.u8(${endVal}, buffer, offset)` + return compiler.wrapCode(code) + }] + }, + SizeOf: { + UUID: ['native', 16], + restBuffer: ['native', (value) => { + return value.length + }], + nbt: ['native', minecraft.nbt[2]], + optionalNbt: ['native', minecraft.optionalNbt[2]], + compressedNbt: ['native', minecraft.compressedNbt[2]], + entityMetadataLoop: ['parametrizable', (compiler, { type }) => { + let code = 'let size = 1\n' + code += 'for (const i in value) {\n' + code += ' size += ' + compiler.callType('value[i]', type) + '\n' + code += '}\n' + code += 'return size' + return compiler.wrapCode(code) + }] + } +} diff --git a/src/transforms/serializer.js b/src/transforms/serializer.js index de5f2d4..3a6d5ac 100644 --- a/src/transforms/serializer.js +++ b/src/transforms/serializer.js @@ -3,6 +3,7 @@ const ProtoDef = require('protodef').ProtoDef const Serializer = require('protodef').Serializer const Parser = require('protodef').FullPacketParser +const { ProtoDefCompiler } = require('protodef').Compiler const minecraft = require('../datatypes/minecraft') const states = require('../states') @@ -11,23 +12,33 @@ const get = require('lodash.get') const protocols = {} -function createProtocol (state, direction, version, customPackets) { - const key = state + ';' + direction + ';' + version +function createProtocol (state, direction, version, customPackets, compiled = true) { + const key = state + ';' + direction + ';' + version + (compiled ? ';c' : '') if (protocols[key]) { return protocols[key] } + const mcData = require('minecraft-data')(version) + + if (compiled) { + const compiler = new ProtoDefCompiler() + compiler.addTypes(require('../datatypes/compiler-minecraft')) + compiler.addProtocol(merge(mcData.protocol, get(customPackets, [mcData.version.majorVersion])), [state, direction]) + const proto = compiler.compileProtoDefSync() + protocols[key] = proto + return proto + } + const proto = new ProtoDef(false) proto.addTypes(minecraft) - const mcData = require('minecraft-data')(version) proto.addProtocol(merge(mcData.protocol, get(customPackets, [mcData.version.majorVersion])), [state, direction]) protocols[key] = proto return proto } -function createSerializer ({ state = states.HANDSHAKING, isServer = false, version, customPackets } = {}) { - return new Serializer(createProtocol(state, !isServer ? 'toServer' : 'toClient', version, customPackets), 'packet') +function createSerializer ({ state = states.HANDSHAKING, isServer = false, version, customPackets, compiled = true } = {}) { + return new Serializer(createProtocol(state, !isServer ? 'toServer' : 'toClient', version, customPackets, compiled), 'packet') } -function createDeserializer ({ state = states.HANDSHAKING, isServer = false, version, customPackets } = {}) { - return new Parser(createProtocol(state, isServer ? 'toServer' : 'toClient', version, customPackets), 'packet') +function createDeserializer ({ state = states.HANDSHAKING, isServer = false, version, customPackets, compiled = true } = {}) { + return new Parser(createProtocol(state, isServer ? 'toServer' : 'toClient', version, customPackets, compiled), 'packet') } module.exports = { diff --git a/test/packetTest.js b/test/packetTest.js index bfa1893..2c610f2 100644 --- a/test/packetTest.js +++ b/test/packetTest.js @@ -77,7 +77,7 @@ const values = { results[typeArgs[index].name] = v } }) - delete context['..'] + delete results['..'] return results }, count: 1, // TODO : might want to set this to a correct value @@ -268,8 +268,10 @@ mc.supportedVersions.forEach(function (supportedVersion, i) { } }) Object.keys(p2).forEach(function (field) { - assert.ok(field in p1, 'field ' + field + ' missing in p1, in p2 it has value ' + - JSON.stringify(p2[field])) + if (p2[field] !== undefined) { + assert.ok(field in p1, 'field ' + field + ' missing in p1, in p2 it has value ' + + JSON.stringify(p2[field])) + } }) } })