Merge branch 'develop' into sensor-error

This commit is contained in:
Eric Rosenbaum 2019-01-15 10:38:51 -05:00 committed by GitHub
commit 2856b32dd5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 74 additions and 40 deletions

View file

@ -165,7 +165,10 @@ class Scratch3ControlBlocks {
// Create clone
const newClone = cloneTarget.makeClone();
if (newClone) {
this.runtime.targets.push(newClone);
this.runtime.addTarget(newClone);
// Place behind the original target.
newClone.goBehindOther(cloneTarget);
}
}

View file

@ -1560,11 +1560,14 @@ class Runtime extends EventEmitter {
}
/**
* Add a target to the execution order.
* @param {Target} executableTarget target to add
* Add a target to the runtime. This tracks the sprite pane
* ordering of the target. The target still needs to be put
* into the correct execution order after calling this function.
* @param {Target} target target to add
*/
addExecutable (executableTarget) {
this.executableTargets.push(executableTarget);
addTarget (target) {
this.targets.push(target);
this.executableTargets.push(target);
}
/**

View file

@ -69,10 +69,6 @@ class Target extends EventEmitter {
* @type {Object.<string, *>}
*/
this._edgeActivatedHatValues = {};
if (this.runtime) {
this.runtime.addExecutable(this);
}
}
/**

View file

@ -13,7 +13,7 @@ const ScratchLinkDeviceAdapter = require('./scratch-link-device-adapter');
* @type {string}
*/
// eslint-disable-next-line max-len
const blockIconURI = 'data:image/png;base64,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';
const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOCAuNSkiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PHBhdGggZD0iTTEyIDM5LjVBMi41IDIuNSAwIDAgMSA5LjUgMzdjMC0uMy4yLS41LjUtLjVzLjUuMi41LjVhMS41IDEuNSAwIDEgMCAzIDB2LS4yYzAtLjQtLjItLjgtLjUtMWwtLjgtLjljLS41LS40LS43LTEtLjctMS43VjMxYzAtLjMuMi0uNS41LS41cy41LjIuNS41djIuMmMwIC40LjEuOC40IDFsLjguOWMuNS40LjggMSAuOCAxLjd2LjJjMCAxLjQtMS4xIDIuNS0yLjUgMi41eiIgZmlsbD0iI0U2RTdFOCIvPjxwYXRoIGQ9Ik0yMy43LjNBMSAxIDAgMCAwIDIzIDBIMWExIDEgMCAwIDAtLjcuM0ExIDEgMCAwIDAgMCAxdjI2YzAgLjMuMS41LjMuNy4yLjIuNC4zLjcuM2gyMmMuMyAwIC41LS4xLjctLjMuMi0uMi4zLS40LjMtLjdWMWExIDEgMCAwIDAtLjMtLjd6TTEyIDRjMiAwIDMuMyAyIDIuNiAzLjhMMTMuMyAxMWExLjQgMS40IDAgMCAxLTIuNyAwTDkuNSA3LjdsLS4yLTFDOS4yIDUuNCAxMC40IDQgMTIgNHoiIHN0cm9rZT0iIzdDODdBNSIgZmlsbD0iIzg1OTJBRiIgZmlsbC1ydWxlPSJub256ZXJvIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMiAydjI0aDIwVjJIMnptMTAgMmMyIDAgMy4zIDIgMi42IDMuOEwxMy4zIDExYTEuNCAxLjQgMCAwIDEtMi43IDBMOS41IDcuN2wtLjItMUM5LjIgNS40IDEwLjQgNCAxMiA0eiIgc3Ryb2tlPSIjN0M4N0E1IiBmaWxsPSIjNUNCMUQ2IiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIHN0cm9rZT0iIzdDODdBNSIgZmlsbD0iIzg1OTJBRiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBkPSJNMjIgMjZIMnYtNmwyMC00eiIvPjxwYXRoIGQ9Ik0uMyAyNy43TDIgMjZNLjMuM0wyIDJNMjIgMkwyMy43LjNNMjMuNyAyNy43TDIyIDI2IiBzdHJva2U9IiM3Qzg3QTUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxjaXJjbGUgZmlsbD0iI0ZGQkYwMCIgY3g9IjEyIiBjeT0iMTQuOCIgcj0iMS4yIi8+PHBhdGggc3Ryb2tlPSIjN0M4N0E1IiBmaWxsPSIjRTZFN0U4IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGQ9Ik0xMCAyOGg0djRoLTR6Ii8+PHBhdGggZD0iTTE1LjUgMjJoLTdhLjUuNSAwIDAgMS0uNS0uNWMwLS4zLjItLjUuNS0uNWg3Yy4zIDAgLjUuMi41LjVzLS4yLjUtLjUuNXpNMTcuNSAyNGgtMTFhLjUuNSAwIDAgMS0uNS0uNWMwLS4zLjItLjUuNS0uNWgxMWMuMyAwIC41LjIuNS41cy0uMi41LS41LjV6IiBmaWxsPSIjRkZCRjAwIi8+PC9nPjwvc3ZnPg==';
/**
* Enum for Vernier godirect protocol.
@ -76,8 +76,8 @@ class GdxFor {
* Called by the runtime when user wants to scan for a peripheral.
*/
scan () {
if (this._device) {
this._device.close();
if (this._scratchLinkSocket) {
this._scratchLinkSocket.disconnect();
}
this._scratchLinkSocket = new BLE(this._runtime, this._extensionId, {
@ -105,10 +105,9 @@ class GdxFor {
* Disconnect from the GDX FOR.
*/
disconnect () {
if (this._device) {
this._device.close();
this._device = null;
this._sensorsEnabled = false;
this._sensorsEnabled = false;
if (this._scratchLinkSocket) {
this._scratchLinkSocket.disconnect();
}
}

View file

@ -39,10 +39,6 @@ class ScratchLinkDeviceAdapter {
const response = new DataView(array.buffer);
return this._deviceOnResponse(response);
}
close () {
return this.scratchLinkSocket.disconnect();
}
}
module.exports = ScratchLinkDeviceAdapter;

View file

@ -781,14 +781,17 @@ const reorderParsedTargets = function (targets) {
// Reorder parsed targets based on the temporary targetPaneOrder property
// 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.
targets.forEach(t => {
reordered.forEach(t => {
delete t.targetPaneOrder;
});
return targets;
return reordered;
};

View file

@ -1157,12 +1157,18 @@ const deserialize = function (json, runtime, zip, isSingleSprite) {
parseScratchObject(target, runtime, extensions, zip))
)
.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)
.map(t => {
// Delete the temporary properties used for
// sprite pane ordering and stage layer ordering
delete t.targetPaneOrder;
delete t.layerOrder;
return t;
}))
.then(targets => {

View file

@ -1021,8 +1021,6 @@ class RenderedTarget extends Target {
newClone._edgeActivatedHatValues = Clone.simple(this._edgeActivatedHatValues);
newClone.initDrawable(StageLayering.SPRITE_LAYER);
newClone.updateAllDrawableProperties();
// Place behind the current target.
newClone.goBehindOther(this);
return newClone;
}
@ -1046,7 +1044,6 @@ class RenderedTarget extends Target {
newTarget.effects = JSON.parse(JSON.stringify(this.effects));
newTarget.variables = this.duplicateVariables(newTarget.blocks);
newTarget.updateAllDrawableProperties();
newTarget.goBehindOther(this);
return newTarget;
});
}

View file

@ -495,11 +495,18 @@ class VirtualMachine extends EventEmitter {
return Promise.all(extensionPromises).then(() => {
targets.forEach(target => {
this.runtime.targets.push(target);
this.runtime.addTarget(target);
(/** @type RenderedTarget */ target).updateAllDrawableProperties();
// Ensure unique sprite name
if (target.isSprite()) this.renameSprite(target.id, target.getName());
});
// Sort the executable targets by layerOrder.
// Remove layerOrder property after use.
this.runtime.executableTargets.sort((a, b) => a.layerOrder - b.layerOrder);
targets.forEach(target => {
delete target.layerOrder;
});
// Select the first target for editing, e.g., the first sprite.
if (wholeProject && (targets.length > 1)) {
this.editingTarget = targets[1];
@ -1016,7 +1023,8 @@ class VirtualMachine extends EventEmitter {
throw new Error('No sprite associated with this target.');
}
return target.duplicate().then(newTarget => {
this.runtime.targets.push(newTarget);
this.runtime.addTarget(newTarget);
newTarget.goBehindOther(target);
this.setEditingTarget(newTarget.id);
});
}

View file

@ -192,7 +192,8 @@ test('edge activated hat should trigger for both sprites when sprite is cloned',
val + Object.keys(target._edgeActivatedHatValues).length, 0);
t.equal(numTargetEdgeHats, 1);
vm.runtime.targets.push(vm.runtime.targets[1].makeClone());
const cloneTarget = vm.runtime.targets[1].makeClone();
vm.runtime.addTarget(cloneTarget);
vm.runtime._step();
// Check that the runtime's _edgeActivatedHatValues object has two separate keys

View file

@ -54,7 +54,8 @@ test('#760 - broadcastAndWait', t => {
const tgt = new Target(rt, b);
tgt.isStage = true;
tgt.createVariable('testBroadcastID', 'message', Variable.BROADCAST_MESSAGE_TYPE);
rt.targets.push(tgt);
rt.addTarget(tgt);
let th = rt._pushThread('broadcastAndWaitBlock', t);
const util = new BlockUtility();

View file

@ -48,7 +48,7 @@ const testCostume = (costumes, arg, currentCostume = 1, isStage = false) => {
if (isStage) {
target.isStage = true;
rt.targets.push(target);
rt.addTarget(target);
looks.switchBackdrop({BACKDROP: arg}, {target});
} else {
looks.switchCostume({COSTUME: arg}, {target});

View file

@ -188,7 +188,7 @@ test('renameVariable calls cloud io device\'s requestRenameVariable function', t
target.isStage = true;
const mockCloudVar = new Variable('foo', 'bar', Variable.SCALAR_TYPE, true);
target.variables[mockCloudVar.id] = mockCloudVar;
runtime.targets.push(target);
runtime.addTarget(target);
target.renameVariable('foo', 'bar2');
@ -215,7 +215,7 @@ test('renameVariable does not call cloud io device\'s requestRenameVariable func
const target = new Target(runtime);
const mockCloudVar = new Variable('foo', 'bar', Variable.SCALAR_TYPE, true);
target.variables[mockCloudVar.id] = mockCloudVar;
runtime.targets.push(target);
runtime.addTarget(target);
target.renameVariable('foo', 'bar2');
@ -266,7 +266,7 @@ test('deleteVariable calls cloud io device\'s requestRenameVariable function', t
target.isStage = true;
const mockCloudVar = new Variable('foo', 'bar', Variable.SCALAR_TYPE, true);
target.variables[mockCloudVar.id] = mockCloudVar;
runtime.targets.push(target);
runtime.addTarget(target);
target.deleteVariable('foo');
@ -288,7 +288,7 @@ test('deleteVariable calls cloud io device\'s requestRenameVariable function', t
const target = new Target(runtime);
const mockCloudVar = new Variable('foo', 'bar', Variable.SCALAR_TYPE, true);
target.variables[mockCloudVar.id] = mockCloudVar;
runtime.targets.push(target);
runtime.addTarget(target);
target.deleteVariable('foo');

View file

@ -1,6 +1,7 @@
const test = require('tap').test;
const path = require('path');
const VirtualMachine = require('../../src/index');
const Runtime = require('../../src/engine/runtime');
const sb3 = require('../../src/serialization/sb3');
const readFileToBuffer = require('../fixtures/readProjectFile').readFileToBuffer;
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();
vm.attachRenderer(new FakeRenderer());
vm.loadProject(readFileToBuffer(path.resolve(__dirname, '../fixtures/ordering.sb2')))
return vm.loadProject(readFileToBuffer(path.resolve(__dirname, '../fixtures/ordering.sb2')))
.then(() => {
// Target get layer order needs a renderer,
// 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[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 => {