2016-04-18 17:20:30 -04:00
|
|
|
var EventEmitter = require('events');
|
|
|
|
var util = require('util');
|
|
|
|
|
2016-06-29 13:48:30 -04:00
|
|
|
var Sprite = require('./sprites/sprite');
|
2016-04-18 17:20:30 -04:00
|
|
|
var Runtime = require('./engine/runtime');
|
|
|
|
|
2016-06-21 14:48:54 -04:00
|
|
|
/**
|
|
|
|
* Whether the environment is a WebWorker.
|
|
|
|
* @const{boolean}
|
|
|
|
*/
|
|
|
|
var ENV_WORKER = typeof importScripts === 'function';
|
|
|
|
|
2016-04-18 17:20:30 -04:00
|
|
|
/**
|
|
|
|
* Handles connections between blocks, stage, and extensions.
|
|
|
|
*
|
|
|
|
* @author Andrew Sliwinski <ascii@media.mit.edu>
|
|
|
|
*/
|
|
|
|
function VirtualMachine () {
|
|
|
|
var instance = this;
|
|
|
|
|
|
|
|
// Bind event emitter and runtime to VM instance
|
2016-04-18 18:03:18 -04:00
|
|
|
// @todo Post message (Web Worker) polyfill
|
2016-04-18 17:20:30 -04:00
|
|
|
EventEmitter.call(instance);
|
2016-06-29 13:48:30 -04:00
|
|
|
// @todo support multiple targets/sprites.
|
|
|
|
// This is just a demo/example.
|
|
|
|
var exampleSprite = new Sprite();
|
2016-06-29 20:56:55 -04:00
|
|
|
exampleSprite.createClone();
|
2016-06-29 13:48:30 -04:00
|
|
|
var exampleTargets = [exampleSprite.clones[0]];
|
|
|
|
instance.exampleSprite = exampleSprite;
|
|
|
|
instance.runtime = new Runtime(exampleTargets);
|
2016-04-18 17:20:30 -04:00
|
|
|
|
|
|
|
/**
|
2016-06-08 13:44:09 -04:00
|
|
|
* Event listeners for scratch-blocks.
|
2016-04-18 17:20:30 -04:00
|
|
|
*/
|
2016-06-08 13:44:09 -04:00
|
|
|
instance.blockListener = (
|
2016-06-29 13:48:30 -04:00
|
|
|
exampleSprite.blocks.generateBlockListener(false, instance.runtime)
|
2016-06-08 13:44:09 -04:00
|
|
|
);
|
2016-04-18 17:20:30 -04:00
|
|
|
|
2016-06-08 13:44:09 -04:00
|
|
|
instance.flyoutBlockListener = (
|
2016-06-29 13:48:30 -04:00
|
|
|
exampleSprite.blocks.generateBlockListener(true, instance.runtime)
|
2016-06-08 13:44:09 -04:00
|
|
|
);
|
2016-06-21 15:30:27 -04:00
|
|
|
|
|
|
|
// Runtime emits are passed along as VM emits.
|
|
|
|
instance.runtime.on(Runtime.STACK_GLOW_ON, function (id) {
|
|
|
|
instance.emit(Runtime.STACK_GLOW_ON, {id: id});
|
|
|
|
});
|
|
|
|
instance.runtime.on(Runtime.STACK_GLOW_OFF, function (id) {
|
|
|
|
instance.emit(Runtime.STACK_GLOW_OFF, {id: id});
|
|
|
|
});
|
|
|
|
instance.runtime.on(Runtime.BLOCK_GLOW_ON, function (id) {
|
|
|
|
instance.emit(Runtime.BLOCK_GLOW_ON, {id: id});
|
|
|
|
});
|
|
|
|
instance.runtime.on(Runtime.BLOCK_GLOW_OFF, function (id) {
|
|
|
|
instance.emit(Runtime.BLOCK_GLOW_OFF, {id: id});
|
|
|
|
});
|
2016-07-07 19:42:38 -04:00
|
|
|
instance.runtime.on(Runtime.VISUAL_REPORT, function (id, value) {
|
|
|
|
instance.emit(Runtime.VISUAL_REPORT, {id: id, value: value});
|
|
|
|
});
|
2016-04-18 17:20:30 -04:00
|
|
|
}
|
|
|
|
|
2016-06-21 15:30:27 -04:00
|
|
|
/**
|
|
|
|
* Inherit from EventEmitter
|
|
|
|
*/
|
|
|
|
util.inherits(VirtualMachine, EventEmitter);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start running the VM - do this before anything else.
|
|
|
|
*/
|
|
|
|
VirtualMachine.prototype.start = function () {
|
|
|
|
this.runtime.start();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* "Green flag" handler - start all threads starting with a green flag.
|
|
|
|
*/
|
|
|
|
VirtualMachine.prototype.greenFlag = function () {
|
|
|
|
this.runtime.greenFlag();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stop all threads and running activities.
|
|
|
|
*/
|
|
|
|
VirtualMachine.prototype.stopAll = function () {
|
|
|
|
this.runtime.stopAll();
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get data for playground. Data comes back in an emitted event.
|
|
|
|
*/
|
|
|
|
VirtualMachine.prototype.getPlaygroundData = function () {
|
|
|
|
this.emit('playgroundData', {
|
2016-06-29 13:48:30 -04:00
|
|
|
blocks: this.exampleSprite.blocks,
|
2016-06-21 15:30:27 -04:00
|
|
|
threads: this.runtime.threads
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2016-07-01 11:52:43 -04:00
|
|
|
/**
|
|
|
|
* Handle an animation frame.
|
|
|
|
*/
|
|
|
|
VirtualMachine.prototype.animationFrame = function () {
|
|
|
|
this.runtime.animationFrame();
|
|
|
|
};
|
|
|
|
|
2016-08-15 21:37:36 -04:00
|
|
|
/**
|
|
|
|
* Post I/O data to the virtual devices.
|
|
|
|
* @param {?string} device Name of virtual I/O device.
|
|
|
|
* @param {Object} data Any data object to post to the I/O device.
|
|
|
|
*/
|
|
|
|
VirtualMachine.prototype.postIOData = function (device, data) {
|
|
|
|
if (this.runtime.ioDevices[device]) {
|
|
|
|
this.runtime.ioDevices[device].postData(data);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-06-21 14:48:54 -04:00
|
|
|
/*
|
2016-06-21 15:30:27 -04:00
|
|
|
* Worker handlers: for all public methods available above,
|
|
|
|
* we must also provide a message handler in case the VM is run
|
|
|
|
* from a worker environment.
|
2016-06-21 14:48:54 -04:00
|
|
|
*/
|
|
|
|
if (ENV_WORKER) {
|
2016-06-29 20:56:55 -04:00
|
|
|
self.importScripts(
|
2016-08-08 15:43:52 -04:00
|
|
|
'./node_modules/scratch-render/render-worker.js'
|
2016-06-29 20:56:55 -04:00
|
|
|
);
|
|
|
|
self.renderer = new self.RenderWebGLWorker();
|
2016-06-21 14:48:54 -04:00
|
|
|
self.vmInstance = new VirtualMachine();
|
|
|
|
self.onmessage = function (e) {
|
|
|
|
var messageData = e.data;
|
|
|
|
switch (messageData.method) {
|
|
|
|
case 'start':
|
|
|
|
self.vmInstance.runtime.start();
|
|
|
|
break;
|
|
|
|
case 'greenFlag':
|
|
|
|
self.vmInstance.runtime.greenFlag();
|
|
|
|
break;
|
|
|
|
case 'stopAll':
|
|
|
|
self.vmInstance.runtime.stopAll();
|
|
|
|
break;
|
|
|
|
case 'blockListener':
|
|
|
|
self.vmInstance.blockListener(messageData.args);
|
|
|
|
break;
|
|
|
|
case 'flyoutBlockListener':
|
|
|
|
self.vmInstance.flyoutBlockListener(messageData.args);
|
|
|
|
break;
|
|
|
|
case 'getPlaygroundData':
|
|
|
|
self.postMessage({
|
|
|
|
method: 'playgroundData',
|
2016-06-29 13:48:30 -04:00
|
|
|
blocks: self.vmInstance.exampleSprite.blocks,
|
2016-06-21 14:48:54 -04:00
|
|
|
threads: self.vmInstance.runtime.threads
|
|
|
|
});
|
|
|
|
break;
|
2016-07-01 11:52:43 -04:00
|
|
|
case 'animationFrame':
|
|
|
|
self.vmInstance.animationFrame();
|
|
|
|
break;
|
2016-08-15 21:37:36 -04:00
|
|
|
case 'postIOData':
|
|
|
|
self.vmInstance.postIOData(messageData.device, messageData.data);
|
|
|
|
break;
|
2016-06-21 14:48:54 -04:00
|
|
|
default:
|
2016-06-29 20:56:55 -04:00
|
|
|
if (e.data.id == 'RendererConnected') {
|
|
|
|
//initRenderWorker();
|
|
|
|
}
|
|
|
|
self.renderer.onmessage(e);
|
|
|
|
break;
|
2016-06-21 14:48:54 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
// Bind runtime's emitted events to postmessages.
|
|
|
|
self.vmInstance.runtime.on(Runtime.STACK_GLOW_ON, function (id) {
|
|
|
|
self.postMessage({method: Runtime.STACK_GLOW_ON, id: id});
|
|
|
|
});
|
|
|
|
self.vmInstance.runtime.on(Runtime.STACK_GLOW_OFF, function (id) {
|
|
|
|
self.postMessage({method: Runtime.STACK_GLOW_OFF, id: id});
|
|
|
|
});
|
|
|
|
self.vmInstance.runtime.on(Runtime.BLOCK_GLOW_ON, function (id) {
|
|
|
|
self.postMessage({method: Runtime.BLOCK_GLOW_ON, id: id});
|
|
|
|
});
|
|
|
|
self.vmInstance.runtime.on(Runtime.BLOCK_GLOW_OFF, function (id) {
|
|
|
|
self.postMessage({method: Runtime.BLOCK_GLOW_OFF, id: id});
|
|
|
|
});
|
2016-07-07 19:42:38 -04:00
|
|
|
self.vmInstance.runtime.on(Runtime.VISUAL_REPORT, function (id, value) {
|
|
|
|
self.postMessage({method: Runtime.VISUAL_REPORT, id: id, value: value});
|
|
|
|
});
|
2016-06-21 14:48:54 -04:00
|
|
|
}
|
|
|
|
|
2016-04-18 17:20:30 -04:00
|
|
|
/**
|
|
|
|
* Export and bind to `window`
|
|
|
|
*/
|
|
|
|
module.exports = VirtualMachine;
|
|
|
|
if (typeof window !== 'undefined') window.VirtualMachine = module.exports;
|