mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-23 14:32:59 -05:00
Merge pull request #829 from kchadha/broadcast-message-typed-variable
Broadcast message functionality
This commit is contained in:
commit
a9e95f3b01
10 changed files with 164 additions and 39 deletions
|
@ -30,24 +30,28 @@ class Scratch3DataBlocks {
|
||||||
}
|
}
|
||||||
|
|
||||||
getVariable (args, util) {
|
getVariable (args, util) {
|
||||||
const variable = util.target.lookupOrCreateVariable(args.VARIABLE);
|
const variable = util.target.lookupOrCreateVariable(
|
||||||
|
args.VARIABLE.id, args.VARIABLE.name);
|
||||||
return variable.value;
|
return variable.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
setVariableTo (args, util) {
|
setVariableTo (args, util) {
|
||||||
const variable = util.target.lookupOrCreateVariable(args.VARIABLE);
|
const variable = util.target.lookupOrCreateVariable(
|
||||||
|
args.VARIABLE.id, args.VARIABLE.name);
|
||||||
variable.value = args.VALUE;
|
variable.value = args.VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
changeVariableBy (args, util) {
|
changeVariableBy (args, util) {
|
||||||
const variable = util.target.lookupOrCreateVariable(args.VARIABLE);
|
const variable = util.target.lookupOrCreateVariable(
|
||||||
|
args.VARIABLE.id, args.VARIABLE.name);
|
||||||
const castedValue = Cast.toNumber(variable.value);
|
const castedValue = Cast.toNumber(variable.value);
|
||||||
const dValue = Cast.toNumber(args.VALUE);
|
const dValue = Cast.toNumber(args.VALUE);
|
||||||
variable.value = castedValue + dValue;
|
variable.value = castedValue + dValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
getListContents (args, util) {
|
getListContents (args, util) {
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(
|
||||||
|
args.LIST.id, args.LIST.name);
|
||||||
// Determine if the list is all single letters.
|
// Determine if the list is all single letters.
|
||||||
// If it is, report contents joined together with no separator.
|
// If it is, report contents joined together with no separator.
|
||||||
// If it's not, report contents joined together with a space.
|
// If it's not, report contents joined together with a space.
|
||||||
|
@ -68,12 +72,14 @@ class Scratch3DataBlocks {
|
||||||
}
|
}
|
||||||
|
|
||||||
addToList (args, util) {
|
addToList (args, util) {
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(
|
||||||
|
args.LIST.id, args.LIST.name);
|
||||||
list.value.push(args.ITEM);
|
list.value.push(args.ITEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteOfList (args, util) {
|
deleteOfList (args, util) {
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(
|
||||||
|
args.LIST.id, args.LIST.name);
|
||||||
const index = Cast.toListIndex(args.INDEX, list.value.length);
|
const index = Cast.toListIndex(args.INDEX, list.value.length);
|
||||||
if (index === Cast.LIST_INVALID) {
|
if (index === Cast.LIST_INVALID) {
|
||||||
return;
|
return;
|
||||||
|
@ -86,7 +92,8 @@ class Scratch3DataBlocks {
|
||||||
|
|
||||||
insertAtList (args, util) {
|
insertAtList (args, util) {
|
||||||
const item = args.ITEM;
|
const item = args.ITEM;
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(
|
||||||
|
args.LIST.id, args.LIST.name);
|
||||||
const index = Cast.toListIndex(args.INDEX, list.value.length + 1);
|
const index = Cast.toListIndex(args.INDEX, list.value.length + 1);
|
||||||
if (index === Cast.LIST_INVALID) {
|
if (index === Cast.LIST_INVALID) {
|
||||||
return;
|
return;
|
||||||
|
@ -96,7 +103,8 @@ class Scratch3DataBlocks {
|
||||||
|
|
||||||
replaceItemOfList (args, util) {
|
replaceItemOfList (args, util) {
|
||||||
const item = args.ITEM;
|
const item = args.ITEM;
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(
|
||||||
|
args.LIST.id, args.LIST.name);
|
||||||
const index = Cast.toListIndex(args.INDEX, list.value.length);
|
const index = Cast.toListIndex(args.INDEX, list.value.length);
|
||||||
if (index === Cast.LIST_INVALID) {
|
if (index === Cast.LIST_INVALID) {
|
||||||
return;
|
return;
|
||||||
|
@ -105,7 +113,8 @@ class Scratch3DataBlocks {
|
||||||
}
|
}
|
||||||
|
|
||||||
getItemOfList (args, util) {
|
getItemOfList (args, util) {
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(
|
||||||
|
args.LIST.id, args.LIST.name);
|
||||||
const index = Cast.toListIndex(args.INDEX, list.value.length);
|
const index = Cast.toListIndex(args.INDEX, list.value.length);
|
||||||
if (index === Cast.LIST_INVALID) {
|
if (index === Cast.LIST_INVALID) {
|
||||||
return '';
|
return '';
|
||||||
|
@ -114,13 +123,15 @@ class Scratch3DataBlocks {
|
||||||
}
|
}
|
||||||
|
|
||||||
lengthOfList (args, util) {
|
lengthOfList (args, util) {
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(
|
||||||
|
args.LIST.id, args.LIST.name);
|
||||||
return list.value.length;
|
return list.value.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
listContainsItem (args, util) {
|
listContainsItem (args, util) {
|
||||||
const item = args.ITEM;
|
const item = args.ITEM;
|
||||||
const list = util.target.lookupOrCreateList(args.LIST);
|
const list = util.target.lookupOrCreateList(
|
||||||
|
args.LIST.id, args.LIST.name);
|
||||||
if (list.value.indexOf(item) >= 0) {
|
if (list.value.indexOf(item) >= 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,14 +56,18 @@ class Scratch3EventBlocks {
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast (args, util) {
|
broadcast (args, util) {
|
||||||
const broadcastOption = Cast.toString(args.BROADCAST_OPTION);
|
const broadcastVar = util.runtime.getTargetForStage().lookupOrCreateBroadcastMsg(
|
||||||
|
args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name);
|
||||||
|
const broadcastOption = broadcastVar.name;
|
||||||
util.startHats('event_whenbroadcastreceived', {
|
util.startHats('event_whenbroadcastreceived', {
|
||||||
BROADCAST_OPTION: broadcastOption
|
BROADCAST_OPTION: broadcastOption
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastAndWait (args, util) {
|
broadcastAndWait (args, util) {
|
||||||
const broadcastOption = Cast.toString(args.BROADCAST_OPTION);
|
const broadcastVar = util.runtime.getTargetForStage().lookupOrCreateBroadcastMsg(
|
||||||
|
args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name);
|
||||||
|
const broadcastOption = broadcastVar.name;
|
||||||
// Have we run before, starting threads?
|
// Have we run before, starting threads?
|
||||||
if (!util.stackFrame.startedThreads) {
|
if (!util.stackFrame.startedThreads) {
|
||||||
// No - start hats for this broadcast.
|
// No - start hats for this broadcast.
|
||||||
|
|
|
@ -304,7 +304,14 @@ class Blocks {
|
||||||
// Check if this variable exists on the current target or stage.
|
// Check if this variable exists on the current target or stage.
|
||||||
// If not, create it on the stage.
|
// If not, create it on the stage.
|
||||||
// TODO create global and local variables when UI provides a way.
|
// TODO create global and local variables when UI provides a way.
|
||||||
if (!optRuntime.getEditingTarget().lookupVariableById(e.varId)) {
|
if (optRuntime.getEditingTarget()) {
|
||||||
|
if (!optRuntime.getEditingTarget().lookupVariableById(e.varId)) {
|
||||||
|
stage.createVariable(e.varId, e.varName, e.varType);
|
||||||
|
}
|
||||||
|
} else if (!stage.lookupVariableById(e.varId)) {
|
||||||
|
// Since getEditingTarget returned null, we now need to
|
||||||
|
// explicitly check if the stage has the variable, and
|
||||||
|
// create one if not.
|
||||||
stage.createVariable(e.varId, e.varName, e.varType);
|
stage.createVariable(e.varId, e.varName, e.varType);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -365,7 +372,8 @@ class Blocks {
|
||||||
case 'field':
|
case 'field':
|
||||||
// Update block value
|
// Update block value
|
||||||
if (!block.fields[args.name]) return;
|
if (!block.fields[args.name]) return;
|
||||||
if (args.name === 'VARIABLE' || args.name === 'LIST') {
|
if (args.name === 'VARIABLE' || args.name === 'LIST' ||
|
||||||
|
args.name === 'BROADCAST_OPTION') {
|
||||||
// Get variable name using the id in args.value.
|
// Get variable name using the id in args.value.
|
||||||
const variable = optRuntime.getEditingTarget().lookupVariableById(args.value);
|
const variable = optRuntime.getEditingTarget().lookupVariableById(args.value);
|
||||||
if (variable) {
|
if (variable) {
|
||||||
|
|
|
@ -175,8 +175,12 @@ const execute = function (sequencer, thread) {
|
||||||
// Add all fields on this block to the argValues.
|
// Add all fields on this block to the argValues.
|
||||||
for (const fieldName in fields) {
|
for (const fieldName in fields) {
|
||||||
if (!fields.hasOwnProperty(fieldName)) continue;
|
if (!fields.hasOwnProperty(fieldName)) continue;
|
||||||
if (fieldName === 'VARIABLE' || fieldName === 'LIST') {
|
if (fieldName === 'VARIABLE' || fieldName === 'LIST' ||
|
||||||
argValues[fieldName] = fields[fieldName].id;
|
fieldName === 'BROADCAST_OPTION') {
|
||||||
|
argValues[fieldName] = {
|
||||||
|
id: fields[fieldName].id,
|
||||||
|
name: fields[fieldName].value
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
argValues[fieldName] = fields[fieldName].value;
|
argValues[fieldName] = fields[fieldName].value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,22 @@ class Target extends EventEmitter {
|
||||||
return newVariable;
|
return newVariable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up a broadcast message object, and create it if one doesn't exist.
|
||||||
|
* @param {string} id Id of the variable.
|
||||||
|
* @param {string} name Name of the variable.
|
||||||
|
* @return {!Variable} Variable object.
|
||||||
|
*/
|
||||||
|
lookupOrCreateBroadcastMsg (id, name) {
|
||||||
|
const broadcastMsg = this.lookupVariableById(id);
|
||||||
|
if (broadcastMsg) return broadcastMsg;
|
||||||
|
// No variable with this name exists - create it locally.
|
||||||
|
const newBroadcastMsg = new Variable(id, name,
|
||||||
|
Variable.BROADCAST_MESSAGE_TYPE, false);
|
||||||
|
this.variables[id] = newBroadcastMsg;
|
||||||
|
return newBroadcastMsg;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Look up a variable object.
|
* Look up a variable object.
|
||||||
* Search begins for local variables; then look for globals.
|
* Search begins for local variables; then look for globals.
|
||||||
|
@ -134,7 +150,7 @@ class Target extends EventEmitter {
|
||||||
* dictionary of variables.
|
* dictionary of variables.
|
||||||
* @param {string} id Id of variable
|
* @param {string} id Id of variable
|
||||||
* @param {string} name Name of variable.
|
* @param {string} name Name of variable.
|
||||||
* @param {string} type Type of variable, '' or 'list'
|
* @param {string} type Type of variable, '', 'broadcast_msg', or 'list'
|
||||||
*/
|
*/
|
||||||
createVariable (id, name, type) {
|
createVariable (id, name, type) {
|
||||||
if (!this.variables.hasOwnProperty(id)) {
|
if (!this.variables.hasOwnProperty(id)) {
|
||||||
|
|
|
@ -25,6 +25,9 @@ class Variable {
|
||||||
case Variable.LIST_TYPE:
|
case Variable.LIST_TYPE:
|
||||||
this.value = [];
|
this.value = [];
|
||||||
break;
|
break;
|
||||||
|
case Variable.BROADCAST_MESSAGE_TYPE:
|
||||||
|
this.value = this.name;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Invalid variable type: ${this.type}`);
|
throw new Error(`Invalid variable type: ${this.type}`);
|
||||||
}
|
}
|
||||||
|
@ -51,6 +54,14 @@ class Variable {
|
||||||
static get LIST_TYPE () {
|
static get LIST_TYPE () {
|
||||||
return 'list';
|
return 'list';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type representation for list variables.
|
||||||
|
* @const {string}
|
||||||
|
*/
|
||||||
|
static get BROADCAST_MESSAGE_TYPE () {
|
||||||
|
return 'broadcast_msg';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Variable;
|
module.exports = Variable;
|
||||||
|
|
|
@ -88,17 +88,18 @@ const flatten = function (blocks) {
|
||||||
* a list of blocks in a branch (e.g., in forever),
|
* a list of blocks in a branch (e.g., in forever),
|
||||||
* or a list of blocks in an argument (e.g., move [pick random...]).
|
* or a list of blocks in an argument (e.g., move [pick random...]).
|
||||||
* @param {Array.<object>} blockList SB2 JSON-format block list.
|
* @param {Array.<object>} blockList SB2 JSON-format block list.
|
||||||
|
* @param {Function} addBroadcastMsg function to update broadcast message name map
|
||||||
* @param {Function} getVariableId function to retreive a variable's ID based on name
|
* @param {Function} getVariableId function to retreive a variable's ID based on name
|
||||||
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
||||||
* @return {Array.<object>} Scratch VM-format block list.
|
* @return {Array.<object>} Scratch VM-format block list.
|
||||||
*/
|
*/
|
||||||
const parseBlockList = function (blockList, getVariableId, extensions) {
|
const parseBlockList = function (blockList, addBroadcastMsg, getVariableId, extensions) {
|
||||||
const resultingList = [];
|
const resultingList = [];
|
||||||
let previousBlock = null; // For setting next.
|
let previousBlock = null; // For setting next.
|
||||||
for (let i = 0; i < blockList.length; i++) {
|
for (let i = 0; i < blockList.length; i++) {
|
||||||
const block = blockList[i];
|
const block = blockList[i];
|
||||||
// eslint-disable-next-line no-use-before-define
|
// eslint-disable-next-line no-use-before-define
|
||||||
const parsedBlock = parseBlock(block, getVariableId, extensions);
|
const parsedBlock = parseBlock(block, addBroadcastMsg, getVariableId, extensions);
|
||||||
if (typeof parsedBlock === 'undefined') continue;
|
if (typeof parsedBlock === 'undefined') continue;
|
||||||
if (previousBlock) {
|
if (previousBlock) {
|
||||||
parsedBlock.parent = previousBlock.id;
|
parsedBlock.parent = previousBlock.id;
|
||||||
|
@ -115,16 +116,17 @@ const parseBlockList = function (blockList, getVariableId, extensions) {
|
||||||
* This should only handle top-level scripts that include X, Y coordinates.
|
* This should only handle top-level scripts that include X, Y coordinates.
|
||||||
* @param {!object} scripts Scripts object from SB2 JSON.
|
* @param {!object} scripts Scripts object from SB2 JSON.
|
||||||
* @param {!Blocks} blocks Blocks object to load parsed blocks into.
|
* @param {!Blocks} blocks Blocks object to load parsed blocks into.
|
||||||
|
* @param {Function} addBroadcastMsg function to update broadcast message name map
|
||||||
* @param {Function} getVariableId function to retreive a variable's ID based on name
|
* @param {Function} getVariableId function to retreive a variable's ID based on name
|
||||||
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
||||||
*/
|
*/
|
||||||
const parseScripts = function (scripts, blocks, getVariableId, extensions) {
|
const parseScripts = function (scripts, blocks, addBroadcastMsg, getVariableId, extensions) {
|
||||||
for (let i = 0; i < scripts.length; i++) {
|
for (let i = 0; i < scripts.length; i++) {
|
||||||
const script = scripts[i];
|
const script = scripts[i];
|
||||||
const scriptX = script[0];
|
const scriptX = script[0];
|
||||||
const scriptY = script[1];
|
const scriptY = script[1];
|
||||||
const blockList = script[2];
|
const blockList = script[2];
|
||||||
const parsedBlockList = parseBlockList(blockList, getVariableId, extensions);
|
const parsedBlockList = parseBlockList(blockList, addBroadcastMsg, getVariableId, extensions);
|
||||||
if (parsedBlockList[0]) {
|
if (parsedBlockList[0]) {
|
||||||
// Adjust script coordinates to account for
|
// Adjust script coordinates to account for
|
||||||
// larger block size in scratch-blocks.
|
// larger block size in scratch-blocks.
|
||||||
|
@ -166,6 +168,20 @@ const generateVariableIdGetter = (function () {
|
||||||
};
|
};
|
||||||
}());
|
}());
|
||||||
|
|
||||||
|
const globalBroadcastMsgStateGenerator = (function () {
|
||||||
|
let broadcastMsgNameMap = {};
|
||||||
|
return function (topLevel) {
|
||||||
|
if (topLevel) broadcastMsgNameMap = {};
|
||||||
|
return {
|
||||||
|
broadcastMsgMapUpdater: function (name) {
|
||||||
|
broadcastMsgNameMap[name] = `broadcastMsgId-${name}`;
|
||||||
|
return broadcastMsgNameMap[name];
|
||||||
|
},
|
||||||
|
globalBroadcastMsgs: broadcastMsgNameMap
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a single "Scratch object" and create all its in-memory VM objects.
|
* Parse a single "Scratch object" and create all its in-memory VM objects.
|
||||||
* TODO: parse the "info" section, especially "savedExtensions"
|
* TODO: parse the "info" section, especially "savedExtensions"
|
||||||
|
@ -227,6 +243,9 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) {
|
||||||
|
|
||||||
const getVariableId = generateVariableIdGetter(target.id, topLevel);
|
const getVariableId = generateVariableIdGetter(target.id, topLevel);
|
||||||
|
|
||||||
|
const globalBroadcastMsgObj = globalBroadcastMsgStateGenerator(topLevel);
|
||||||
|
const addBroadcastMsg = globalBroadcastMsgObj.broadcastMsgMapUpdater;
|
||||||
|
|
||||||
// Load target properties from JSON.
|
// Load target properties from JSON.
|
||||||
if (object.hasOwnProperty('variables')) {
|
if (object.hasOwnProperty('variables')) {
|
||||||
for (let j = 0; j < object.variables.length; j++) {
|
for (let j = 0; j < object.variables.length; j++) {
|
||||||
|
@ -244,7 +263,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) {
|
||||||
|
|
||||||
// If included, parse any and all scripts/blocks on the object.
|
// If included, parse any and all scripts/blocks on the object.
|
||||||
if (object.hasOwnProperty('scripts')) {
|
if (object.hasOwnProperty('scripts')) {
|
||||||
parseScripts(object.scripts, blocks, getVariableId, extensions);
|
parseScripts(object.scripts, blocks, addBroadcastMsg, getVariableId, extensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object.hasOwnProperty('lists')) {
|
if (object.hasOwnProperty('lists')) {
|
||||||
|
@ -317,6 +336,21 @@ const parseScratchObject = function (object, runtime, extensions, topLevel) {
|
||||||
Promise.all(
|
Promise.all(
|
||||||
childrenPromises
|
childrenPromises
|
||||||
).then(children => {
|
).then(children => {
|
||||||
|
// Need create broadcast msgs as variables after
|
||||||
|
// all other targets have finished processing.
|
||||||
|
if (target.isStage) {
|
||||||
|
const allBroadcastMsgs = globalBroadcastMsgObj.globalBroadcastMsgs;
|
||||||
|
for (const msgName in allBroadcastMsgs) {
|
||||||
|
const msgId = allBroadcastMsgs[msgName];
|
||||||
|
const newMsg = new Variable(
|
||||||
|
msgId,
|
||||||
|
msgName,
|
||||||
|
Variable.BROADCAST_MESSAGE_TYPE,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
target.variables[newMsg.id] = newMsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
let targets = [target];
|
let targets = [target];
|
||||||
for (let n = 0; n < children.length; n++) {
|
for (let n = 0; n < children.length; n++) {
|
||||||
targets = targets.concat(children[n]);
|
targets = targets.concat(children[n]);
|
||||||
|
@ -349,11 +383,12 @@ const sb2import = function (json, runtime, optForceSprite) {
|
||||||
/**
|
/**
|
||||||
* Parse a single SB2 JSON-formatted block and its children.
|
* Parse a single SB2 JSON-formatted block and its children.
|
||||||
* @param {!object} sb2block SB2 JSON-formatted block.
|
* @param {!object} sb2block SB2 JSON-formatted block.
|
||||||
|
* @param {Function} addBroadcastMsg function to update broadcast message name map
|
||||||
* @param {Function} getVariableId function to retrieve a variable's ID based on name
|
* @param {Function} getVariableId function to retrieve a variable's ID based on name
|
||||||
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
||||||
* @return {object} Scratch VM format block, or null if unsupported object.
|
* @return {object} Scratch VM format block, or null if unsupported object.
|
||||||
*/
|
*/
|
||||||
const parseBlock = function (sb2block, getVariableId, extensions) {
|
const parseBlock = function (sb2block, addBroadcastMsg, getVariableId, extensions) {
|
||||||
// First item in block object is the old opcode (e.g., 'forward:').
|
// First item in block object is the old opcode (e.g., 'forward:').
|
||||||
const oldOpcode = sb2block[0];
|
const oldOpcode = sb2block[0];
|
||||||
// Convert the block using the specMap. See sb2specmap.js.
|
// Convert the block using the specMap. See sb2specmap.js.
|
||||||
|
@ -404,10 +439,10 @@ const parseBlock = function (sb2block, getVariableId, extensions) {
|
||||||
let innerBlocks;
|
let innerBlocks;
|
||||||
if (typeof providedArg[0] === 'object' && providedArg[0]) {
|
if (typeof providedArg[0] === 'object' && providedArg[0]) {
|
||||||
// Block list occupies the input.
|
// Block list occupies the input.
|
||||||
innerBlocks = parseBlockList(providedArg, getVariableId, extensions);
|
innerBlocks = parseBlockList(providedArg, addBroadcastMsg, getVariableId, extensions);
|
||||||
} else {
|
} else {
|
||||||
// Single block occupies the input.
|
// Single block occupies the input.
|
||||||
innerBlocks = [parseBlock(providedArg, getVariableId, extensions)];
|
innerBlocks = [parseBlock(providedArg, addBroadcastMsg, getVariableId, extensions)];
|
||||||
}
|
}
|
||||||
let previousBlock = null;
|
let previousBlock = null;
|
||||||
for (let j = 0; j < innerBlocks.length; j++) {
|
for (let j = 0; j < innerBlocks.length; j++) {
|
||||||
|
@ -493,6 +528,10 @@ const parseBlock = function (sb2block, getVariableId, extensions) {
|
||||||
if (expectedArg.fieldName === 'VARIABLE' || expectedArg.fieldName === 'LIST') {
|
if (expectedArg.fieldName === 'VARIABLE' || expectedArg.fieldName === 'LIST') {
|
||||||
// Add `id` property to variable fields
|
// Add `id` property to variable fields
|
||||||
activeBlock.fields[expectedArg.fieldName].id = getVariableId(providedArg);
|
activeBlock.fields[expectedArg.fieldName].id = getVariableId(providedArg);
|
||||||
|
} else if (expectedArg.fieldName === 'BROADCAST_OPTION') {
|
||||||
|
// add the name in this field to the broadcast msg name map
|
||||||
|
const broadcastId = addBroadcastMsg(providedArg);
|
||||||
|
activeBlock.fields[expectedArg.fieldName].id = broadcastId;
|
||||||
}
|
}
|
||||||
const varType = expectedArg.variableType;
|
const varType = expectedArg.variableType;
|
||||||
if (typeof varType === 'string') {
|
if (typeof varType === 'string') {
|
||||||
|
|
|
@ -656,7 +656,8 @@ const specMap = {
|
||||||
argMap: [
|
argMap: [
|
||||||
{
|
{
|
||||||
type: 'field',
|
type: 'field',
|
||||||
fieldName: 'BROADCAST_OPTION'
|
fieldName: 'BROADCAST_OPTION',
|
||||||
|
variableType: Variable.BROADCAST_MESSAGE_TYPE
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -664,9 +665,9 @@ const specMap = {
|
||||||
opcode: 'event_broadcast',
|
opcode: 'event_broadcast',
|
||||||
argMap: [
|
argMap: [
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'field',
|
||||||
inputOp: 'event_broadcast_menu',
|
fieldName: 'BROADCAST_OPTION',
|
||||||
inputName: 'BROADCAST_OPTION'
|
variableType: Variable.BROADCAST_MESSAGE_TYPE
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -674,9 +675,9 @@ const specMap = {
|
||||||
opcode: 'event_broadcastandwait',
|
opcode: 'event_broadcastandwait',
|
||||||
argMap: [
|
argMap: [
|
||||||
{
|
{
|
||||||
type: 'input',
|
type: 'field',
|
||||||
inputOp: 'event_broadcast_menu',
|
fieldName: 'BROADCAST_OPTION',
|
||||||
inputName: 'BROADCAST_OPTION'
|
variableType: Variable.BROADCAST_MESSAGE_TYPE
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,7 +12,7 @@ test('#760 - broadcastAndWait', t => {
|
||||||
id: 'broadcastAndWaitBlock',
|
id: 'broadcastAndWaitBlock',
|
||||||
fields: {
|
fields: {
|
||||||
BROADCAST_OPTION: {
|
BROADCAST_OPTION: {
|
||||||
id: 'BROADCAST_OPTION',
|
id: 'testBroadcastID',
|
||||||
value: 'message'
|
value: 'message'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -30,7 +30,7 @@ test('#760 - broadcastAndWait', t => {
|
||||||
id: 'receiveMessageBlock',
|
id: 'receiveMessageBlock',
|
||||||
fields: {
|
fields: {
|
||||||
BROADCAST_OPTION: {
|
BROADCAST_OPTION: {
|
||||||
id: 'BROADCAST_OPTION',
|
id: 'testBroadcastID',
|
||||||
value: 'message'
|
value: 'message'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -51,15 +51,17 @@ test('#760 - broadcastAndWait', t => {
|
||||||
b.createBlock(broadcastAndWaitBlock);
|
b.createBlock(broadcastAndWaitBlock);
|
||||||
b.createBlock(receiveMessageBlock);
|
b.createBlock(receiveMessageBlock);
|
||||||
const tgt = new Target(rt, b);
|
const tgt = new Target(rt, b);
|
||||||
|
tgt.isStage = true;
|
||||||
rt.targets.push(tgt);
|
rt.targets.push(tgt);
|
||||||
|
|
||||||
let th = rt._pushThread('broadcastAndWaitBlock', t);
|
let th = rt._pushThread('broadcastAndWaitBlock', t);
|
||||||
const util = new BlockUtility();
|
const util = new BlockUtility();
|
||||||
util.sequencer = rt.sequencer;
|
util.sequencer = rt.sequencer;
|
||||||
util.thread = th;
|
util.thread = th;
|
||||||
|
util.runtime = rt;
|
||||||
|
|
||||||
// creates threads
|
// creates threads
|
||||||
e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util);
|
e.broadcastAndWait({BROADCAST_OPTION: {id: 'testBroadcastID', name: 'message'}}, util);
|
||||||
t.strictEqual(rt.threads.length, 2);
|
t.strictEqual(rt.threads.length, 2);
|
||||||
t.strictEqual(rt.threads[1].topBlock, 'receiveMessageBlock');
|
t.strictEqual(rt.threads[1].topBlock, 'receiveMessageBlock');
|
||||||
// yields when some thread is active
|
// yields when some thread is active
|
||||||
|
@ -76,18 +78,18 @@ test('#760 - broadcastAndWait', t => {
|
||||||
// restarts done threads that are in runtime threads
|
// restarts done threads that are in runtime threads
|
||||||
th = rt._pushThread('broadcastAndWaitBlock', tgt);
|
th = rt._pushThread('broadcastAndWaitBlock', tgt);
|
||||||
util.thread = th;
|
util.thread = th;
|
||||||
e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util);
|
e.broadcastAndWait({BROADCAST_OPTION: {id: 'testBroadcastID', name: 'message'}}, util);
|
||||||
t.strictEqual(rt.threads.length, 3);
|
t.strictEqual(rt.threads.length, 3);
|
||||||
t.strictEqual(rt.threads[1].status, Thread.STATUS_RUNNING);
|
t.strictEqual(rt.threads[1].status, Thread.STATUS_RUNNING);
|
||||||
t.strictEqual(th.status, Thread.STATUS_YIELD);
|
t.strictEqual(th.status, Thread.STATUS_YIELD);
|
||||||
// yields when some restarted thread is active
|
// yields when some restarted thread is active
|
||||||
th.status = Thread.STATUS_RUNNING;
|
th.status = Thread.STATUS_RUNNING;
|
||||||
e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util);
|
e.broadcastAndWait({BROADCAST_OPTION: {id: 'testBroadcastID', name: 'message'}}, util);
|
||||||
t.strictEqual(th.status, Thread.STATUS_YIELD);
|
t.strictEqual(th.status, Thread.STATUS_YIELD);
|
||||||
// does not yield once all threads are done
|
// does not yield once all threads are done
|
||||||
th.status = Thread.STATUS_RUNNING;
|
th.status = Thread.STATUS_RUNNING;
|
||||||
rt.threads[1].status = Thread.STATUS_DONE;
|
rt.threads[1].status = Thread.STATUS_DONE;
|
||||||
e.broadcastAndWait({BROADCAST_OPTION: 'message'}, util);
|
e.broadcastAndWait({BROADCAST_OPTION: {id: 'testBroadcastID', name: 'message'}}, util);
|
||||||
t.strictEqual(th.status, Thread.STATUS_RUNNING);
|
t.strictEqual(th.status, Thread.STATUS_RUNNING);
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
|
|
|
@ -173,3 +173,32 @@ test('lookupOrCreateList returns list if one with given id exists', t => {
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('lookupOrCreateBroadcastMsg creates a var if one does not exist', t => {
|
||||||
|
const target = new Target();
|
||||||
|
const variables = target.variables;
|
||||||
|
|
||||||
|
t.equal(Object.keys(variables).length, 0);
|
||||||
|
const broadcastVar = target.lookupOrCreateBroadcastMsg('foo', 'bar');
|
||||||
|
t.equal(Object.keys(variables).length, 1);
|
||||||
|
t.equal(broadcastVar.id, 'foo');
|
||||||
|
t.equal(broadcastVar.name, 'bar');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('lookupOrCreateBroadcastMsg returns the var with given id if exists', t => {
|
||||||
|
const target = new Target();
|
||||||
|
const variables = target.variables;
|
||||||
|
|
||||||
|
t.equal(Object.keys(variables).length, 0);
|
||||||
|
target.createVariable('foo', 'bar', Variable.BROADCAST_MESSAGE_TYPE);
|
||||||
|
t.equal(Object.keys(variables).length, 1);
|
||||||
|
|
||||||
|
const broadcastMsg = target.lookupOrCreateBroadcastMsg('foo', 'bar');
|
||||||
|
t.equal(Object.keys(variables).length, 1);
|
||||||
|
t.equal(broadcastMsg.id, 'foo');
|
||||||
|
t.equal(broadcastMsg.name, 'bar');
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue