mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2025-01-10 15:02:06 -05:00
Populate executable targets when runtime.targets is populated so that scripts don't run before they are supposed to (e.g. before targets are done getting installed).
This commit is contained in:
parent
522b5e1a8e
commit
fbee37e915
9 changed files with 58 additions and 16 deletions
|
@ -166,6 +166,10 @@ class Scratch3ControlBlocks {
|
||||||
const newClone = cloneTarget.makeClone();
|
const newClone = cloneTarget.makeClone();
|
||||||
if (newClone) {
|
if (newClone) {
|
||||||
this.runtime.targets.push(newClone);
|
this.runtime.targets.push(newClone);
|
||||||
|
this.runtime.addExecutable(newClone);
|
||||||
|
|
||||||
|
// Place behind the original target.
|
||||||
|
newClone.goBehindOther(cloneTarget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,10 +69,6 @@ class Target extends EventEmitter {
|
||||||
* @type {Object.<string, *>}
|
* @type {Object.<string, *>}
|
||||||
*/
|
*/
|
||||||
this._edgeActivatedHatValues = {};
|
this._edgeActivatedHatValues = {};
|
||||||
|
|
||||||
if (this.runtime) {
|
|
||||||
this.runtime.addExecutable(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -781,14 +781,17 @@ const reorderParsedTargets = function (targets) {
|
||||||
// Reorder parsed targets based on the temporary targetPaneOrder property
|
// Reorder parsed targets based on the temporary targetPaneOrder property
|
||||||
// and then delete it.
|
// and then delete it.
|
||||||
|
|
||||||
targets.sort((a, b) => a.targetPaneOrder - b.targetPaneOrder);
|
const reordered = targets.map((t, index) => {
|
||||||
|
t.layerOrder = index;
|
||||||
|
return t;
|
||||||
|
}).sort((a, b) => a.targetPaneOrder - b.targetPaneOrder);
|
||||||
|
|
||||||
// Delete the temporary target pane ordering since we shouldn't need it anymore.
|
// Delete the temporary target pane ordering since we shouldn't need it anymore.
|
||||||
targets.forEach(t => {
|
reordered.forEach(t => {
|
||||||
delete t.targetPaneOrder;
|
delete t.targetPaneOrder;
|
||||||
});
|
});
|
||||||
|
|
||||||
return targets;
|
return reordered;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1157,12 +1157,18 @@ const deserialize = function (json, runtime, zip, isSingleSprite) {
|
||||||
parseScratchObject(target, runtime, extensions, zip))
|
parseScratchObject(target, runtime, extensions, zip))
|
||||||
)
|
)
|
||||||
.then(targets => targets // Re-sort targets back into original sprite-pane ordering
|
.then(targets => targets // Re-sort targets back into original sprite-pane ordering
|
||||||
|
.map((t, i) => {
|
||||||
|
// Add layer order property to deserialized targets.
|
||||||
|
// This property is used to initialize executable targets in
|
||||||
|
// the correct order and is deleted in VM's installTargets function
|
||||||
|
t.layerOrder = i;
|
||||||
|
return t;
|
||||||
|
})
|
||||||
.sort((a, b) => a.targetPaneOrder - b.targetPaneOrder)
|
.sort((a, b) => a.targetPaneOrder - b.targetPaneOrder)
|
||||||
.map(t => {
|
.map(t => {
|
||||||
// Delete the temporary properties used for
|
// Delete the temporary properties used for
|
||||||
// sprite pane ordering and stage layer ordering
|
// sprite pane ordering and stage layer ordering
|
||||||
delete t.targetPaneOrder;
|
delete t.targetPaneOrder;
|
||||||
delete t.layerOrder;
|
|
||||||
return t;
|
return t;
|
||||||
}))
|
}))
|
||||||
.then(targets => {
|
.then(targets => {
|
||||||
|
|
|
@ -1021,8 +1021,6 @@ class RenderedTarget extends Target {
|
||||||
newClone._edgeActivatedHatValues = Clone.simple(this._edgeActivatedHatValues);
|
newClone._edgeActivatedHatValues = Clone.simple(this._edgeActivatedHatValues);
|
||||||
newClone.initDrawable(StageLayering.SPRITE_LAYER);
|
newClone.initDrawable(StageLayering.SPRITE_LAYER);
|
||||||
newClone.updateAllDrawableProperties();
|
newClone.updateAllDrawableProperties();
|
||||||
// Place behind the current target.
|
|
||||||
newClone.goBehindOther(this);
|
|
||||||
return newClone;
|
return newClone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1046,7 +1044,6 @@ class RenderedTarget extends Target {
|
||||||
newTarget.effects = JSON.parse(JSON.stringify(this.effects));
|
newTarget.effects = JSON.parse(JSON.stringify(this.effects));
|
||||||
newTarget.variables = this.duplicateVariables(newTarget.blocks);
|
newTarget.variables = this.duplicateVariables(newTarget.blocks);
|
||||||
newTarget.updateAllDrawableProperties();
|
newTarget.updateAllDrawableProperties();
|
||||||
newTarget.goBehindOther(this);
|
|
||||||
return newTarget;
|
return newTarget;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -500,6 +500,15 @@ class VirtualMachine extends EventEmitter {
|
||||||
// Ensure unique sprite name
|
// Ensure unique sprite name
|
||||||
if (target.isSprite()) this.renameSprite(target.id, target.getName());
|
if (target.isSprite()) this.renameSprite(target.id, target.getName());
|
||||||
});
|
});
|
||||||
|
// Initialize executable targets sorted by the layerOrder.
|
||||||
|
// Remove layerOrder property after use.
|
||||||
|
const executableTargets = targets.slice()
|
||||||
|
.sort((a, b) => a.layerOrder - b.layerOrder);
|
||||||
|
executableTargets.forEach(target => {
|
||||||
|
this.runtime.addExecutable(target);
|
||||||
|
delete target.layerOrder;
|
||||||
|
});
|
||||||
|
|
||||||
// Select the first target for editing, e.g., the first sprite.
|
// Select the first target for editing, e.g., the first sprite.
|
||||||
if (wholeProject && (targets.length > 1)) {
|
if (wholeProject && (targets.length > 1)) {
|
||||||
this.editingTarget = targets[1];
|
this.editingTarget = targets[1];
|
||||||
|
@ -1017,6 +1026,8 @@ class VirtualMachine extends EventEmitter {
|
||||||
}
|
}
|
||||||
return target.duplicate().then(newTarget => {
|
return target.duplicate().then(newTarget => {
|
||||||
this.runtime.targets.push(newTarget);
|
this.runtime.targets.push(newTarget);
|
||||||
|
this.runtime.addExecutable(newTarget);
|
||||||
|
newTarget.goBehindOther(target);
|
||||||
this.setEditingTarget(newTarget.id);
|
this.setEditingTarget(newTarget.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,7 +192,9 @@ test('edge activated hat should trigger for both sprites when sprite is cloned',
|
||||||
val + Object.keys(target._edgeActivatedHatValues).length, 0);
|
val + Object.keys(target._edgeActivatedHatValues).length, 0);
|
||||||
t.equal(numTargetEdgeHats, 1);
|
t.equal(numTargetEdgeHats, 1);
|
||||||
|
|
||||||
vm.runtime.targets.push(vm.runtime.targets[1].makeClone());
|
const cloneTarget = vm.runtime.targets[1].makeClone();
|
||||||
|
vm.runtime.targets.push(cloneTarget);
|
||||||
|
vm.runtime.addExecutable(cloneTarget);
|
||||||
|
|
||||||
vm.runtime._step();
|
vm.runtime._step();
|
||||||
// Check that the runtime's _edgeActivatedHatValues object has two separate keys
|
// Check that the runtime's _edgeActivatedHatValues object has two separate keys
|
||||||
|
|
|
@ -54,7 +54,9 @@ test('#760 - broadcastAndWait', t => {
|
||||||
const tgt = new Target(rt, b);
|
const tgt = new Target(rt, b);
|
||||||
tgt.isStage = true;
|
tgt.isStage = true;
|
||||||
tgt.createVariable('testBroadcastID', 'message', Variable.BROADCAST_MESSAGE_TYPE);
|
tgt.createVariable('testBroadcastID', 'message', Variable.BROADCAST_MESSAGE_TYPE);
|
||||||
|
// Need to add to both runtime.targets as well as runtime.executableTargets here
|
||||||
rt.targets.push(tgt);
|
rt.targets.push(tgt);
|
||||||
|
rt.executableTargets.push(tgt);
|
||||||
|
|
||||||
let th = rt._pushThread('broadcastAndWaitBlock', t);
|
let th = rt._pushThread('broadcastAndWaitBlock', t);
|
||||||
const util = new BlockUtility();
|
const util = new BlockUtility();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const test = require('tap').test;
|
const test = require('tap').test;
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const VirtualMachine = require('../../src/index');
|
const VirtualMachine = require('../../src/index');
|
||||||
|
const Runtime = require('../../src/engine/runtime');
|
||||||
const sb3 = require('../../src/serialization/sb3');
|
const sb3 = require('../../src/serialization/sb3');
|
||||||
const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer;
|
const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer;
|
||||||
const exampleProjectPath = path.resolve(__dirname, '../fixtures/clone-cleanup.sb2');
|
const exampleProjectPath = path.resolve(__dirname, '../fixtures/clone-cleanup.sb2');
|
||||||
|
@ -147,10 +148,10 @@ test('deserialize sb3 project with comments - no duplicate id serialization', t
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('serialize sb3 preserves sprite layer order', t => {
|
test('serializing and deserializing sb3 preserves sprite layer order', t => {
|
||||||
const vm = new VirtualMachine();
|
const vm = new VirtualMachine();
|
||||||
vm.attachRenderer(new FakeRenderer());
|
vm.attachRenderer(new FakeRenderer());
|
||||||
vm.loadProject(readFileToBuffer(path.resolve(__dirname, '../fixtures/ordering.sb2')))
|
return vm.loadProject(readFileToBuffer(path.resolve(__dirname, '../fixtures/ordering.sb2')))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// Target get layer order needs a renderer,
|
// Target get layer order needs a renderer,
|
||||||
// fake the numbers we would get back from the
|
// fake the numbers we would get back from the
|
||||||
|
@ -182,8 +183,28 @@ test('serialize sb3 preserves sprite layer order', t => {
|
||||||
t.equal(result.targets[2].layerOrder, 1);
|
t.equal(result.targets[2].layerOrder, 1);
|
||||||
t.equal(result.targets[3].layerOrder, 3);
|
t.equal(result.targets[3].layerOrder, 3);
|
||||||
|
|
||||||
t.end();
|
return result;
|
||||||
});
|
})
|
||||||
|
.then(serializedObject =>
|
||||||
|
sb3.deserialize(
|
||||||
|
JSON.parse(JSON.stringify(serializedObject)), new Runtime(), null, false)
|
||||||
|
.then(({targets}) => {
|
||||||
|
// First check that the sprites are ordered correctly (as they would
|
||||||
|
// appear in the target pane)
|
||||||
|
t.equal(targets[0].sprite.name, 'Stage');
|
||||||
|
t.equal(targets[1].sprite.name, 'First');
|
||||||
|
t.equal(targets[2].sprite.name, 'Second');
|
||||||
|
t.equal(targets[3].sprite.name, 'Third');
|
||||||
|
|
||||||
|
// Check that they are in the correct layer order (as they would render
|
||||||
|
// back to front on the stage)
|
||||||
|
t.equal(targets[0].layerOrder, 0);
|
||||||
|
t.equal(targets[1].layerOrder, 2);
|
||||||
|
t.equal(targets[2].layerOrder, 1);
|
||||||
|
t.equal(targets[3].layerOrder, 3);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('serializeBlocks', t => {
|
test('serializeBlocks', t => {
|
||||||
|
|
Loading…
Reference in a new issue