mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-23 14:32:59 -05:00
Serialize and deserialize monitors. Obtain monitor block information from runtime. Fix issue where stage monitors weren't getting loaded correctly.
This commit is contained in:
parent
3050f496b2
commit
1a4f0a75f2
4 changed files with 87 additions and 4 deletions
|
@ -282,8 +282,10 @@ class Blocks {
|
||||||
* runtime interface.
|
* runtime interface.
|
||||||
* @param {object} e Blockly "block" or "variable" event
|
* @param {object} e Blockly "block" or "variable" event
|
||||||
* @param {?Runtime} optRuntime Optional runtime to forward click events to.
|
* @param {?Runtime} optRuntime Optional runtime to forward click events to.
|
||||||
|
* @param {boolean=} forMonitors Whether the event being listened to pertains to a
|
||||||
|
* monitor block.
|
||||||
*/
|
*/
|
||||||
blocklyListen (e, optRuntime) {
|
blocklyListen (e, optRuntime, forMonitors) {
|
||||||
// Validate event
|
// Validate event
|
||||||
if (typeof e !== 'object') return;
|
if (typeof e !== 'object') return;
|
||||||
if (typeof e.blockId !== 'string' && typeof e.varId !== 'string' &&
|
if (typeof e.blockId !== 'string' && typeof e.varId !== 'string' &&
|
||||||
|
@ -305,6 +307,21 @@ class Blocks {
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case 'create': {
|
case 'create': {
|
||||||
const newBlocks = adapter(e);
|
const newBlocks = adapter(e);
|
||||||
|
|
||||||
|
if (forMonitors) {
|
||||||
|
// If this is the monitor block container,
|
||||||
|
// add the appropriate info to the monitorBlock looking it up
|
||||||
|
// in the runtime monitorState
|
||||||
|
const topLevelBlock = newBlocks[0];
|
||||||
|
if (optRuntime && optRuntime.getMonitorState().has(topLevelBlock.id)) {
|
||||||
|
|
||||||
|
const monitorData = optRuntime.getMonitorState().get(topLevelBlock.id);
|
||||||
|
|
||||||
|
topLevelBlock.isMonitored = monitorData.visible;
|
||||||
|
topLevelBlock.targetId = monitorData.targetId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A create event can create many blocks. Add them all.
|
// A create event can create many blocks. Add them all.
|
||||||
for (let i = 0; i < newBlocks.length; i++) {
|
for (let i = 0; i < newBlocks.length; i++) {
|
||||||
this.createBlock(newBlocks[i]);
|
this.createBlock(newBlocks[i]);
|
||||||
|
|
|
@ -659,6 +659,10 @@ class Runtime extends EventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getMonitorState () {
|
||||||
|
return this._monitorState;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate an extension-specific menu ID.
|
* Generate an extension-specific menu ID.
|
||||||
* @param {string} menuName - the name of the menu.
|
* @param {string} menuName - the name of the menu.
|
||||||
|
|
|
@ -9,6 +9,7 @@ const Blocks = require('../engine/blocks');
|
||||||
const Sprite = require('../sprites/sprite');
|
const Sprite = require('../sprites/sprite');
|
||||||
const Variable = require('../engine/variable');
|
const Variable = require('../engine/variable');
|
||||||
const Comment = require('../engine/comment');
|
const Comment = require('../engine/comment');
|
||||||
|
const MonitorRecord = require('../engine/monitor-record');
|
||||||
const StageLayering = require('../engine/stage-layering');
|
const StageLayering = require('../engine/stage-layering');
|
||||||
const log = require('../util/log');
|
const log = require('../util/log');
|
||||||
const uid = require('../util/uid');
|
const uid = require('../util/uid');
|
||||||
|
@ -480,6 +481,29 @@ const getSimplifiedLayerOrdering = function (targets) {
|
||||||
return MathUtil.reducedSortOrdering(layerOrders);
|
return MathUtil.reducedSortOrdering(layerOrders);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const serializeMonitors = function (monitors) {
|
||||||
|
return monitors.valueSeq().map(monitorData => {
|
||||||
|
const serializedMonitor = {
|
||||||
|
id: monitorData.id,
|
||||||
|
mode: monitorData.mode,
|
||||||
|
opcode: monitorData.opcode,
|
||||||
|
params: monitorData.params,
|
||||||
|
spriteName: monitorData.spriteName,
|
||||||
|
value: monitorData.value,
|
||||||
|
width: monitorData.width,
|
||||||
|
height: monitorData.height,
|
||||||
|
x: monitorData.x,
|
||||||
|
y: monitorData.y,
|
||||||
|
visible: monitorData.visible
|
||||||
|
};
|
||||||
|
if (monitorData.mode !== 'list') {
|
||||||
|
serializedMonitor.min = monitorData.sliderMin;
|
||||||
|
serializedMonitor.max = monitorData.sliderMax;
|
||||||
|
}
|
||||||
|
return serializedMonitor;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializes the specified VM runtime.
|
* Serializes the specified VM runtime.
|
||||||
* @param {!Runtime} runtime VM runtime instance to be serialized.
|
* @param {!Runtime} runtime VM runtime instance to be serialized.
|
||||||
|
@ -516,8 +540,7 @@ const serialize = function (runtime, targetId) {
|
||||||
|
|
||||||
obj.targets = serializedTargets;
|
obj.targets = serializedTargets;
|
||||||
|
|
||||||
|
obj.monitors = serializeMonitors(runtime.getMonitorState());
|
||||||
// TODO Serialize monitors
|
|
||||||
|
|
||||||
// Assemble extension list
|
// Assemble extension list
|
||||||
obj.extensions = Array.from(extensions);
|
obj.extensions = Array.from(extensions);
|
||||||
|
@ -1015,6 +1038,39 @@ const parseScratchObject = function (object, runtime, extensions, zip) {
|
||||||
return Promise.all(costumePromises.concat(soundPromises)).then(() => target);
|
return Promise.all(costumePromises.concat(soundPromises)).then(() => target);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deserializeMonitor = function (monitorData, runtime, targets) {
|
||||||
|
const monitorBlockInfo = runtime.monitorBlockInfo[monitorData.opcode];
|
||||||
|
if (monitorData.spriteName) {
|
||||||
|
const filteredTargets = targets.filter(t => t.sprite.name === monitorData.spriteName);
|
||||||
|
if (!filteredTargets || filteredTargets.length !== 1) {
|
||||||
|
log.error(`Could not deserialize monitor ${monitorData.opcode} for sprite ${
|
||||||
|
monitorData.spriteName} because no such sprite could be found.`);
|
||||||
|
}
|
||||||
|
monitorData.targetId = filteredTargets[0].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitorData.opcode !== 'data_variable' && monitorData.opcode !== 'data_listcontents') {
|
||||||
|
// Variables and lists already have their ID serialized in the monitorData,
|
||||||
|
// find the correct id for all other monitors. monitorBlockInfo.getId should
|
||||||
|
// ignore the given parameters if the monitor in question is not target specific.
|
||||||
|
monitorData.id = monitorBlockInfo.getId(
|
||||||
|
monitorData.targetId, Object.values(monitorData.params)[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingMonitorBlock = runtime.monitorBlocks._blocks[monitorData.id];
|
||||||
|
if (existingMonitorBlock) {
|
||||||
|
// A monitor block already exists if the toolbox has been loaded and
|
||||||
|
// the monitor block is not target specific (because the block gets recycled).
|
||||||
|
// Update the existing block with the relevant monitor information.
|
||||||
|
existingMonitorBlock.isMonitored = monitorData.visible;
|
||||||
|
existingMonitorBlock.targetId = monitorData.targetId;
|
||||||
|
}
|
||||||
|
// Otherwise, the monitor block will get created when the toolbox updates
|
||||||
|
// after the target has been installed.
|
||||||
|
|
||||||
|
runtime.requestAddMonitor(MonitorRecord(monitorData));
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserialize the specified representation of a VM runtime and loads it into the provided runtime instance.
|
* Deserialize the specified representation of a VM runtime and loads it into the provided runtime instance.
|
||||||
* @param {object} json - JSON representation of a VM runtime.
|
* @param {object} json - JSON representation of a VM runtime.
|
||||||
|
@ -1037,6 +1093,8 @@ const deserialize = function (json, runtime, zip, isSingleSprite) {
|
||||||
.map((t, i) => Object.assign(t, {targetPaneOrder: i}))
|
.map((t, i) => Object.assign(t, {targetPaneOrder: i}))
|
||||||
.sort((a, b) => a.layerOrder - b.layerOrder);
|
.sort((a, b) => a.layerOrder - b.layerOrder);
|
||||||
|
|
||||||
|
const monitorObjects = json.monitors || [];
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
targetObjects.map(target =>
|
targetObjects.map(target =>
|
||||||
parseScratchObject(target, runtime, extensions, zip))
|
parseScratchObject(target, runtime, extensions, zip))
|
||||||
|
@ -1050,6 +1108,10 @@ const deserialize = function (json, runtime, zip, isSingleSprite) {
|
||||||
delete t.layerOrder;
|
delete t.layerOrder;
|
||||||
return t;
|
return t;
|
||||||
}))
|
}))
|
||||||
|
.then(targets => {
|
||||||
|
monitorObjects.map(monitorDesc => deserializeMonitor(monitorDesc, runtime, targets));
|
||||||
|
return targets;
|
||||||
|
})
|
||||||
.then(targets => ({
|
.then(targets => ({
|
||||||
targets,
|
targets,
|
||||||
extensions
|
extensions
|
||||||
|
|
|
@ -1060,7 +1060,7 @@ class VirtualMachine extends EventEmitter {
|
||||||
// Filter events by type, since monitor blocks only need to listen to these events.
|
// Filter events by type, since monitor blocks only need to listen to these events.
|
||||||
// Monitor blocks shouldn't be destroyed when flyout blocks are deleted.
|
// Monitor blocks shouldn't be destroyed when flyout blocks are deleted.
|
||||||
if (['create', 'change'].indexOf(e.type) !== -1) {
|
if (['create', 'change'].indexOf(e.type) !== -1) {
|
||||||
this.runtime.monitorBlocks.blocklyListen(e, this.runtime);
|
this.runtime.monitorBlocks.blocklyListen(e, this.runtime, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue