diff --git a/src/transforms/serializer.js b/src/transforms/serializer.js index 76f7ef27..ca22805d 100644 --- a/src/transforms/serializer.js +++ b/src/transforms/serializer.js @@ -11,6 +11,23 @@ const merge = require('lodash.merge') const minecraftData = require('minecraft-data') const protocols = {} +// The generated code is fully determined by these versions plus the protocol +// key and customPackets, so together they make a stale cache file unreachable. +function protocolCacheFile (state, direction, version, customPackets) { + const cacheDir = process.env.NMP_PROTOCOL_CACHE_DIR ?? + require('path').join(require('os').tmpdir(), 'node-minecraft-protocol-cache') + if (cacheDir === '0') return null + const inputs = [ + require('../../package.json').version, + require('protodef/package.json').version, + require('minecraft-data/package.json').version, + JSON.stringify(customPackets ?? {}) + ].join(';') + const hash = require('crypto').createHash('sha1').update(inputs).digest('hex').slice(0, 12) + const name = `${version}-${state}-${direction}-${hash}.js`.replace(/[^a-zA-Z0-9.-]/g, '_') + return require('path').join(cacheDir, name) +} + function createProtocol (state, direction, version, customPackets, compiled = true) { const key = `${state};${direction};${version}${compiled ? ';c' : ''}` if (protocols[key]) { return protocols[key] } @@ -24,18 +41,31 @@ function createProtocol (state, direction, version, customPackets, compiled = tr throw new Error(`Unsupported protocol version '${versionInfo.version}' (attempted to use '${mcData.version.version}' data); try updating your packages with 'npm update'`) } - const mergedProtocol = merge(mcData.protocol, customPackets?.[mcData.version.majorVersion] ?? {}) - if (compiled) { const compiler = new ProtoDefCompiler() compiler.addTypes(require('../datatypes/compiler-minecraft')) - compiler.addProtocol(mergedProtocol, [state, direction]) nbt.addTypesToCompiler('big', compiler) - const proto = compiler.compileProtoDefSync() + const cacheFile = protocolCacheFile(state, direction, version, customPackets) + let proto + if (cacheFile) { + // A hit also skips loading mcData.protocol and walking it in + // addProtocol, which cost as much as the compile itself. + try { proto = compiler.loadCompiledProtoDefSync(cacheFile) } catch {} + } + if (!proto) { + const mergedProtocol = merge(mcData.protocol, customPackets?.[mcData.version.majorVersion] ?? {}) + compiler.addProtocol(mergedProtocol, [state, direction]) + // Registered a second time: the nbt schemas must override the types the + // protocol declares as native (the pre-cache call above only provides + // the natives needed to load a cached protocol). + nbt.addTypesToCompiler('big', compiler) + proto = compiler.compileProtoDefSync(cacheFile ? { cacheFile } : {}) + } protocols[key] = proto return proto } + const mergedProtocol = merge(mcData.protocol, customPackets?.[mcData.version.majorVersion] ?? {}) const proto = new ProtoDef(false) proto.addTypes(minecraft) proto.addProtocol(mergedProtocol, [state, direction])