mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-06-09 12:01:21 -04:00
Import monitors from sb2 files.
Paired with @kchadha on all of this.
This commit is contained in:
parent
784705d46e
commit
4713f47fb7
8 changed files with 196 additions and 22 deletions
src/serialization
|
@ -14,6 +14,7 @@ const uid = require('../util/uid');
|
|||
const StringUtil = require('../util/string-util');
|
||||
const specMap = require('./sb2_specmap');
|
||||
const Variable = require('../engine/variable');
|
||||
const MonitorRecord = require('../engine/monitor-record');
|
||||
|
||||
const {loadCostume} = require('../import/load-costume.js');
|
||||
const {loadSound} = require('../import/load-sound.js');
|
||||
|
@ -208,6 +209,104 @@ const globalBroadcastMsgStateGenerator = (function () {
|
|||
};
|
||||
}());
|
||||
|
||||
/**
|
||||
* Parse a single monitor object and create all its in-memory VM objects.
|
||||
* @param {!object} object - From-JSON "Scratch object"
|
||||
* @param {!Runtime} runtime - (in/out) Runtime object to load monitor info into.
|
||||
* @param {!Array.<Target>} targets - Targets have already been parsed.
|
||||
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
||||
*/
|
||||
const parseMonitorObject = (object, runtime, targets, extensions) => {
|
||||
let target = null;
|
||||
// List blocks don't come in with their target name set.
|
||||
// Find the target by searching for a target with matching variable name/type.
|
||||
if (!object.hasOwnProperty('target')) {
|
||||
for (let i = 0; i < targets.length; i++) {
|
||||
const currTarget = targets[i];
|
||||
const listVariables = Object.keys(currTarget.variables).filter(key => {
|
||||
const variable = currTarget.variables[key];
|
||||
return variable.type === Variable.LIST_TYPE && variable.name === object.listName;
|
||||
});
|
||||
if (listVariables.length > 0) {
|
||||
target = currTarget; // Keep this target for later use
|
||||
object.target = currTarget.getName(); // Set target name to normalize with other monitors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a block for the monitor blocks container
|
||||
target = target || targets.filter(t => t.getName() === object.target)[0];
|
||||
if (!target) throw new Error('Cannot create monitor for target that cannot be found by name');
|
||||
|
||||
// Create var id getter to make block naming/parsing easier, variables already created.
|
||||
const getVariableId = generateVariableIdGetter(target.id, false);
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
const block = parseBlock(
|
||||
[object.cmd, object.param], // Scratch 2 monitor blocks only have one param.
|
||||
null, // `addBroadcastMsg`, not needed for monitor blocks.
|
||||
getVariableId,
|
||||
extensions
|
||||
);
|
||||
|
||||
let isSpriteLocalVariable;
|
||||
if (object.cmd === 'getVar:' || object.cmd === 'contentsOfList:') {
|
||||
// These monitors are sprite-specific if they are not targetting the stage.
|
||||
isSpriteLocalVariable = object.target.isStage;
|
||||
// Variable getters have special block IDs for the toolbox that match the variable ID.
|
||||
block.id = getVariableId(object.param);
|
||||
}
|
||||
|
||||
block.id = runtime.monitorBlockInfo.hasOwnProperty(block.opcode) ?
|
||||
runtime.monitorBlockInfo[block.opcode].getId(target.id, object.param) : block.id;
|
||||
|
||||
// Block needs a targetId if it is sprite specific or a local variable.
|
||||
// Consult the monitorBlockInfo in the runtime for sprite-specificity.
|
||||
const isSpriteSpecific = isSpriteLocalVariable ||
|
||||
(runtime.monitorBlockInfo.hasOwnProperty(block.opcode) &&
|
||||
runtime.monitorBlockInfo[block.opcode].isSpriteSpecific);
|
||||
block.targetId = isSpriteSpecific ? target.id : null;
|
||||
|
||||
// Property required for running monitored blocks.
|
||||
block.isMonitored = object.visible;
|
||||
|
||||
// Blocks can be created with children, flatten and add to monitorBlocks.
|
||||
const newBlocks = flatten([block]);
|
||||
for (let i = 0; i < newBlocks.length; i++) {
|
||||
runtime.monitorBlocks.createBlock(newBlocks[i]);
|
||||
}
|
||||
|
||||
// Convert numbered mode into strings for better understandability.
|
||||
switch (object.mode) {
|
||||
case 1:
|
||||
object.mode = 'default';
|
||||
break;
|
||||
case 2:
|
||||
object.mode = 'large';
|
||||
break;
|
||||
case 3:
|
||||
object.mode = 'slider';
|
||||
break;
|
||||
}
|
||||
|
||||
// Create a monitor record for the runtime's monitorState
|
||||
runtime.requestAddMonitor(MonitorRecord({
|
||||
id: block.id,
|
||||
targetId: block.targetId,
|
||||
spriteName: block.targetId ? object.target : null,
|
||||
opcode: block.opcode,
|
||||
params: runtime.monitorBlocks._getBlockParams(block),
|
||||
value: '',
|
||||
mode: object.mode,
|
||||
sliderMin: object.sliderMin,
|
||||
sliderMax: object.sliderMax,
|
||||
x: object.x,
|
||||
y: object.y,
|
||||
width: object.width,
|
||||
height: object.height,
|
||||
visible: object.visible
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a single "Scratch object" and create all its in-memory VM objects.
|
||||
* TODO: parse the "info" section, especially "savedExtensions"
|
||||
|
@ -220,10 +319,17 @@ const globalBroadcastMsgStateGenerator = (function () {
|
|||
*/
|
||||
const parseScratchObject = function (object, runtime, extensions, topLevel, zip) {
|
||||
if (!object.hasOwnProperty('objName')) {
|
||||
// Watcher/monitor - skip this object until those are implemented in VM.
|
||||
// @todo
|
||||
return Promise.resolve(null);
|
||||
if (object.hasOwnProperty('listName')) {
|
||||
// Shim these objects so they can be processed as monitors
|
||||
object.cmd = 'contentsOfList:';
|
||||
object.param = object.listName;
|
||||
object.mode = 'list';
|
||||
}
|
||||
// Defer parsing monitors until targets are all parsed
|
||||
object.deferredMonitor = true;
|
||||
return Promise.resolve(object);
|
||||
}
|
||||
|
||||
// Blocks container for this object.
|
||||
const blocks = new Blocks();
|
||||
// @todo: For now, load all Scratch objects (stage/sprites) as a Sprite.
|
||||
|
@ -332,7 +438,6 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip)
|
|||
if (object.hasOwnProperty('lists')) {
|
||||
for (let k = 0; k < object.lists.length; k++) {
|
||||
const list = object.lists[k];
|
||||
// @todo: monitor properties.
|
||||
const newVariable = new Variable(
|
||||
getVariableId(list.listName),
|
||||
list.listName,
|
||||
|
@ -455,8 +560,18 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip)
|
|||
}
|
||||
}
|
||||
let targets = [target];
|
||||
const deferredMonitors = [];
|
||||
for (let n = 0; n < children.length; n++) {
|
||||
targets = targets.concat(children[n]);
|
||||
if (children[n]) {
|
||||
if (children[n].deferredMonitor) {
|
||||
deferredMonitors.push(children[n]);
|
||||
} else {
|
||||
targets = targets.concat(children[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let n = 0; n < deferredMonitors.length; n++) {
|
||||
parseMonitorObject(deferredMonitors[n], runtime, targets, extensions);
|
||||
}
|
||||
return targets;
|
||||
})
|
||||
|
|
|
@ -1370,6 +1370,18 @@ const specMap = {
|
|||
}
|
||||
]
|
||||
},
|
||||
// Scratch 2 uses this alternative variable getter opcode only in monitors,
|
||||
// blocks use the `readVariable` opcode above.
|
||||
'getVar:': {
|
||||
opcode: 'data_variable',
|
||||
argMap: [
|
||||
{
|
||||
type: 'field',
|
||||
fieldName: 'VARIABLE',
|
||||
variableType: Variable.SCALAR_TYPE
|
||||
}
|
||||
]
|
||||
},
|
||||
'setVar:to:': {
|
||||
opcode: 'data_setvariableto',
|
||||
argMap: [
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue