diff --git a/lib/index.js b/lib/index.js index 4ae18d0..bad6a1c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -26,7 +26,8 @@ _.extend(config.engine, { customIntentTypes: {}, historyChunkSize: 20, useSigintTimeout: false, - reportMemoryUsageInterval: 0 + reportMemoryUsageInterval: 0, + enableInspector: false, }); config.engine.on('playerSandbox', (sandbox) => { @@ -508,4 +509,4 @@ exports.queue = queue; exports.constants = config.common.constants; -process.on('disconnect', () => process.exit()); \ No newline at end of file +process.on('disconnect', () => process.exit()); diff --git a/lib/runtime/make.js b/lib/runtime/make.js index 1a2739a..ef407be 100644 --- a/lib/runtime/make.js +++ b/lib/runtime/make.js @@ -315,7 +315,7 @@ module.exports = function(userId, onlyInRoom) { runtimeUserVm.clear(userId); console.error('isolated-vm timeout', userId); pubsub.publish(`user:${userId}/cpu`, JSON.stringify({cpu: 'error'})); - }, 5000); + }, Math.max(5000, config.engine.mainLoopResetInterval)); make(scope, userId, onlyInRoom).then(resolve).catch(reject); }) .then(result => { diff --git a/lib/runtime/user-vm.js b/lib/runtime/user-vm.js index 0659112..2860e17 100644 --- a/lib/runtime/user-vm.js +++ b/lib/runtime/user-vm.js @@ -18,60 +18,67 @@ exports.create = async function({userId, staticTerrainData, staticTerrainDataSiz exports.clear(userId); throw 'Script execution has been terminated: your isolate disposed unexpectedly, restarting virtual machine'; } - if(codeTimestamp > vms[userId].codeTimestamp) { + if(!vms[userId].ready) { + return vms[userId].promise; + } else if(codeTimestamp > vms[userId].codeTimestamp) { exports.clear(userId); } } if(!vms[userId]) { - let isolate = new ivm.Isolate({snapshot, memoryLimit: 256 + staticTerrainDataSize/1024/1024}); - let context = await isolate.createContext(); - let [ nativeModInstance, initScript, cleanupScript ] = await Promise.all([ - nativeMod.create(context), - isolate.compileScript('_init();'), - isolate.compileScript('new ' + function () { - delete global._ivm; - delete global._isolate; - delete global._context; - delete global._init; - delete global._evalFn; - delete global._start; - delete global._setStaticTerrainData; - delete global._worldSize; - delete global._nativeMod; - }), - ]); - await Promise.all([ - context.global.set('global', context.global.derefInto()), - context.global.set('_ivm', ivm), - context.global.set('_isolate', isolate), - context.global.set('_context', context), - context.global.set('_worldSize', index.getWorldSize()), - context.global.set('_nativeMod', nativeModInstance.derefInto()), - initScript.run(context), - ]); - let [ evalFn, start, setStaticTerrainData ] = await Promise.all([ - context.global.get('_evalFn'), - context.global.get('_start'), - context.global.get('_setStaticTerrainData'), - ]); + let inspector = config.engine.enableInspector; + let isolate = new ivm.Isolate({inspector, snapshot, memoryLimit: 256 + staticTerrainDataSize/1024/1024}); + let vm = vms[userId] = {isolate, ready: false}; + vm.promise = async function() { + let context = await isolate.createContext({inspector}); + let [ nativeModInstance, initScript, cleanupScript ] = await Promise.all([ + nativeMod.create(context), + isolate.compileScript('_init();'), + isolate.compileScript('new ' + function () { + delete global._ivm; + delete global._isolate; + delete global._context; + delete global._init; + delete global._evalFn; + delete global._start; + delete global._setStaticTerrainData; + delete global._worldSize; + delete global._nativeMod; + }), + ]); + await Promise.all([ + context.global.set('global', context.global.derefInto()), + context.global.set('_ivm', ivm), + context.global.set('_isolate', isolate), + context.global.set('_context', context), + context.global.set('_worldSize', index.getWorldSize()), + context.global.set('_nativeMod', nativeModInstance.derefInto()), + initScript.run(context), + ]); + let [ evalFn, start, setStaticTerrainData ] = await Promise.all([ + context.global.get('_evalFn'), + context.global.get('_start'), + context.global.get('_setStaticTerrainData'), + ]); - await Promise.all([ - setStaticTerrainData.apply(undefined, [ - new ivm.ExternalCopy(staticTerrainData.buffer).copyInto({ release: true }), - new ivm.ExternalCopy(staticTerrainData.roomOffsets).copyInto({ release: true }), - ]), - cleanupScript.run(context), - ]); + await Promise.all([ + setStaticTerrainData.apply(undefined, [ + new ivm.ExternalCopy(staticTerrainData.buffer).copyInto({ release: true }), + new ivm.ExternalCopy(staticTerrainData.roomOffsets).copyInto({ release: true }), + ]), + cleanupScript.run(context), + ]); - vms[userId] = { - isolate, - context, - start, - evalFn, - nativeModInstance, - codeTimestamp - }; + Object.assign(vm, { + ready: true, + context, + start, + evalFn, + nativeModInstance, + codeTimestamp + }); + }(); + await vm.promise; } vms[userId].lastUsed = Date.now(); @@ -79,16 +86,15 @@ exports.create = async function({userId, staticTerrainData, staticTerrainDataSiz exports.get = function(userId) { userId = ""+userId; - return vms[userId]; + let vm = vms[userId]; + if (vm && vm.ready) { + return vm; + } }; exports.clear = function(userId) { userId = ""+userId; if(vms[userId]) { try { - vms[userId].start.dispose(); - vms[userId].evalFn.dispose(); - vms[userId].nativeModInstance.dispose(); - vms[userId].context.release(); if(!vms[userId].isolate.isDisposed) { vms[userId].isolate.dispose(); }