WIP for demo

This commit is contained in:
Christopher Willis-Ford 2017-08-29 11:43:09 -07:00
parent aaa7784d39
commit d43749d675
6 changed files with 78 additions and 13 deletions

View file

@ -115,6 +115,12 @@ class SharedDispatch {
_remoteTransferCall (provider, service, method, transfer, ...args) {
return new Promise((resolve, reject) => {
const responseId = this._storeCallbacks(resolve, reject);
/** @TODO: remove this hack! this is just here so we don't try to send `util` to a worker */
if ((args.length > 0) && (typeof args[args.length - 1].yield === 'function')) {
args.pop();
}
if (transfer) {
provider.postMessage({service, method, responseId, args}, transfer);
} else {

View file

@ -294,6 +294,14 @@ class Runtime extends EventEmitter {
return 'MONITORS_UPDATE';
}
/**
* Event name for reporting that an extension was added.
* @const {string}
*/
static get EXTENSION_WAS_ADDED () {
return 'EXTENSION_WAS_ADDED';
}
/**
* How rapidly we try to step threads by default, in ms.
*/
@ -371,10 +379,12 @@ class Runtime extends EventEmitter {
for (const blockInfo of extensionInfo.blocks) {
const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo);
const opcode = convertedBlock.json.id;
const opcode = convertedBlock.json.type;
categoryInfo.blocks.push(convertedBlock);
this._primitives[opcode] = convertedBlock.info.func;
}
this.emit(Runtime.EXTENSION_WAS_ADDED, categoryInfo.blocks);
}
/**
@ -387,10 +397,8 @@ class Runtime extends EventEmitter {
_convertForScratchBlocks (blockInfo, categoryInfo) {
const extendedOpcode = `${categoryInfo.id}.${blockInfo.opcode}`;
const blockJSON = {
id: extendedOpcode,
type: extendedOpcode,
inputsInline: true,
previousStatement: null, // null = available connection; undefined = hat block
nextStatement: null, // null = available connection; undefined = terminal
category: categoryInfo.name,
colour: categoryInfo.color1,
colourSecondary: categoryInfo.color2,
@ -436,6 +444,10 @@ class Runtime extends EventEmitter {
switch (blockInfo.blockType) {
case BlockType.COMMAND:
blockJSON.outputShape = ScratchBlocks.OUTPUT_SHAPE_SQUARE;
blockJSON.previousStatement = null; // null = available connection; undefined = hat
if (!blockInfo.isTerminal) {
blockJSON.nextStatement = null; // null = available connection; undefined = terminal
}
break;
case BlockType.REPORTER:
blockJSON.output = 'String'; // TODO: distinguish number & string here?
@ -447,7 +459,7 @@ class Runtime extends EventEmitter {
break;
case BlockType.HAT:
blockJSON.outputShape = ScratchBlocks.OUTPUT_SHAPE_SQUARE;
delete blockJSON.previousStatement;
blockJSON.nextStatement = null; // null = available connection; undefined = terminal
break;
case BlockType.CONDITIONAL:
// Statement inputs get names like 'SUBSTACK', 'SUBSTACK2', 'SUBSTACK3', ...
@ -458,6 +470,8 @@ class Runtime extends EventEmitter {
};
}
blockJSON.outputShape = ScratchBlocks.OUTPUT_SHAPE_SQUARE;
blockJSON.previousStatement = null; // null = available connection; undefined = hat
blockJSON.nextStatement = null; // null = available connection; undefined = terminal
break;
}

View file

@ -65,10 +65,6 @@ class ExtensionManager {
});
}
foo () {
return this.loadExtensionURL('extensions/example-extension.js');
}
/**
* Load an extension by URL
* @param {string} extensionURL - the URL for the extension to load
@ -77,7 +73,7 @@ class ExtensionManager {
loadExtensionURL (extensionURL) {
return new Promise((resolve, reject) => {
// If we `require` this at the global level it breaks non-webpack targets, including tests
const ExtensionWorker = require('worker-loader!./extension-worker');
const ExtensionWorker = require('worker-loader?name=extension-worker.js!./extension-worker');
this.pendingExtensions.push({extensionURL, resolve, reject});
dispatch.addWorker(new ExtensionWorker());

View file

@ -33,6 +33,28 @@ class ExampleExtension {
// Required: the list of blocks implemented by this extension,
// in the order intended for display.
blocks: [
{
opcode: 'example-noop',
blockType: Scratch.BlockType.COMMAND,
blockAllThreads: false,
text: 'do nothing',
func: 'noop'
},
{
opcode: 'example-conditional',
blockType: Scratch.BlockType.CONDITIONAL,
branchCount: 4,
isTerminal: true,
blockAllThreads: false,
text: 'choose [BRANCH]',
arguments: {
BRANCH: {
type: Scratch.ArgumentType.NUMBER,
defaultValue: 1
}
},
func: 'noop'
},
{
// Required: the machine-readable name of this operation.
// This will appear in project JSON. Must not contain a '.' character.
@ -110,6 +132,18 @@ class ExampleExtension {
// ['sprite', 'stage']
filter: ['someBlocks.wedo2', 'sprite', 'stage']
},
{
opcode: 'example-Boolean',
blockType: Scratch.BlockType.BOOLEAN,
text: 'return true',
func: 'returnTrue'
},
{
opcode: 'example-hat',
blockType: Scratch.BlockType.HAT,
text: 'after forever',
func: 'returnFalse'
},
{
// Another block...
}
@ -179,6 +213,17 @@ class ExampleExtension {
return ['Letter ', args.LETTER_NUM, ' of ', args.TEXT, ' is ', result, '.'].join('');
}
noop () {
}
returnTrue () {
return true;
}
returnFalse () {
return false;
}
}
Scratch.extensions.register(new ExampleExtension());

View file

@ -1,10 +1,8 @@
const VirtualMachine = require('./virtual-machine');
const CentralDispatch = require('./dispatch/central-dispatch');
const ExtensionManager = require('./extension-support/extension-manager');
global.Scratch = global.Scratch || {};
global.Scratch.dispatch = CentralDispatch;
global.Scratch.extensions = new ExtensionManager();
module.exports = VirtualMachine;

View file

@ -1,6 +1,7 @@
const EventEmitter = require('events');
const centralDispatch = require('./dispatch/central-dispatch');
const ExtensionManager = require('./extension-support/extension-manager');
const log = require('./util/log');
const Runtime = require('./engine/runtime');
const sb2 = require('./serialization/sb2');
@ -63,6 +64,11 @@ class VirtualMachine extends EventEmitter {
this.runtime.on(Runtime.MONITORS_UPDATE, monitorList => {
this.emit(Runtime.MONITORS_UPDATE, monitorList);
});
this.runtime.on(Runtime.EXTENSION_WAS_ADDED, blocksInfo => {
this.emit(Runtime.EXTENSION_WAS_ADDED, blocksInfo);
});
this.extensionManager = new ExtensionManager();
this.blockListener = this.blockListener.bind(this);
this.flyoutBlockListener = this.flyoutBlockListener.bind(this);