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.
|
||||
* @param {object} e Blockly "block" or "variable" event
|
||||
* @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
|
||||
if (typeof e !== 'object') return;
|
||||
if (typeof e.blockId !== 'string' && typeof e.varId !== 'string' &&
|
||||
|
@ -305,6 +307,21 @@ class Blocks {
|
|||
switch (e.type) {
|
||||
case 'create': {
|
||||
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.
|
||||
for (let i = 0; i < newBlocks.length; i++) {
|
||||
this.createBlock(newBlocks[i]);
|
||||
|
|
|
@ -659,6 +659,10 @@ class Runtime extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
getMonitorState () {
|
||||
return this._monitorState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an extension-specific menu ID.
|
||||
* @param {string} menuName - the name of the menu.
|
||||
|
|
|
@ -9,6 +9,7 @@ const Blocks = require('../engine/blocks');
|
|||
const Sprite = require('../sprites/sprite');
|
||||
const Variable = require('../engine/variable');
|
||||
const Comment = require('../engine/comment');
|
||||
const MonitorRecord = require('../engine/monitor-record');
|
||||
const StageLayering = require('../engine/stage-layering');
|
||||
const log = require('../util/log');
|
||||
const uid = require('../util/uid');
|
||||
|
@ -480,6 +481,29 @@ const getSimplifiedLayerOrdering = function (targets) {
|
|||
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.
|
||||
* @param {!Runtime} runtime VM runtime instance to be serialized.
|
||||
|
@ -516,8 +540,7 @@ const serialize = function (runtime, targetId) {
|
|||
|
||||
obj.targets = serializedTargets;
|
||||
|
||||
|
||||
// TODO Serialize monitors
|
||||
obj.monitors = serializeMonitors(runtime.getMonitorState());
|
||||
|
||||
// Assemble extension list
|
||||
obj.extensions = Array.from(extensions);
|
||||
|
@ -1015,6 +1038,39 @@ const parseScratchObject = function (object, runtime, extensions, zip) {
|
|||
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.
|
||||
* @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}))
|
||||
.sort((a, b) => a.layerOrder - b.layerOrder);
|
||||
|
||||
const monitorObjects = json.monitors || [];
|
||||
|
||||
return Promise.all(
|
||||
targetObjects.map(target =>
|
||||
parseScratchObject(target, runtime, extensions, zip))
|
||||
|
@ -1050,6 +1108,10 @@ const deserialize = function (json, runtime, zip, isSingleSprite) {
|
|||
delete t.layerOrder;
|
||||
return t;
|
||||
}))
|
||||
.then(targets => {
|
||||
monitorObjects.map(monitorDesc => deserializeMonitor(monitorDesc, runtime, targets));
|
||||
return targets;
|
||||
})
|
||||
.then(targets => ({
|
||||
targets,
|
||||
extensions
|
||||
|
|
|
@ -1060,7 +1060,7 @@ class VirtualMachine extends EventEmitter {
|
|||
// 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.
|
||||
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