mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-23 14:32:59 -05:00
commit
fd5e178d3b
6 changed files with 2746 additions and 2698 deletions
5212
package-lock.json
generated
5212
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -45,6 +45,10 @@ class Scratch3DataBlocks {
|
||||||
const variable = util.target.lookupOrCreateVariable(
|
const variable = util.target.lookupOrCreateVariable(
|
||||||
args.VARIABLE.id, args.VARIABLE.name);
|
args.VARIABLE.id, args.VARIABLE.name);
|
||||||
variable.value = args.VALUE;
|
variable.value = args.VALUE;
|
||||||
|
|
||||||
|
if (variable.isCloud) {
|
||||||
|
util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, args.VALUE]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changeVariableBy (args, util) {
|
changeVariableBy (args, util) {
|
||||||
|
@ -52,7 +56,12 @@ class Scratch3DataBlocks {
|
||||||
args.VARIABLE.id, args.VARIABLE.name);
|
args.VARIABLE.id, args.VARIABLE.name);
|
||||||
const castedValue = Cast.toNumber(variable.value);
|
const castedValue = Cast.toNumber(variable.value);
|
||||||
const dValue = Cast.toNumber(args.VALUE);
|
const dValue = Cast.toNumber(args.VALUE);
|
||||||
variable.value = castedValue + dValue;
|
const newValue = castedValue + dValue;
|
||||||
|
variable.value = newValue;
|
||||||
|
|
||||||
|
if (variable.isCloud) {
|
||||||
|
util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, newValue]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changeMonitorVisibility (id, visible) {
|
changeMonitorVisibility (id, visible) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ const Variable = require('./variable');
|
||||||
|
|
||||||
// Virtual I/O devices.
|
// Virtual I/O devices.
|
||||||
const Clock = require('../io/clock');
|
const Clock = require('../io/clock');
|
||||||
|
const Cloud = require('../io/cloud');
|
||||||
const DeviceManager = require('../io/deviceManager');
|
const DeviceManager = require('../io/deviceManager');
|
||||||
const Keyboard = require('../io/keyboard');
|
const Keyboard = require('../io/keyboard');
|
||||||
const Mouse = require('../io/mouse');
|
const Mouse = require('../io/mouse');
|
||||||
|
@ -262,6 +263,7 @@ class Runtime extends EventEmitter {
|
||||||
/** @type {Object.<string, Object>} */
|
/** @type {Object.<string, Object>} */
|
||||||
this.ioDevices = {
|
this.ioDevices = {
|
||||||
clock: new Clock(),
|
clock: new Clock(),
|
||||||
|
cloud: new Cloud(),
|
||||||
deviceManager: new DeviceManager(),
|
deviceManager: new DeviceManager(),
|
||||||
keyboard: new Keyboard(this),
|
keyboard: new Keyboard(this),
|
||||||
mouse: new Mouse(this),
|
mouse: new Mouse(this),
|
||||||
|
@ -1383,6 +1385,7 @@ class Runtime extends EventEmitter {
|
||||||
this.targets.map(this.disposeTarget, this);
|
this.targets.map(this.disposeTarget, this);
|
||||||
this._monitorState = OrderedMap({});
|
this._monitorState = OrderedMap({});
|
||||||
// @todo clear out extensions? turboMode? etc.
|
// @todo clear out extensions? turboMode? etc.
|
||||||
|
this.ioDevices.cloud.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
129
src/io/cloud.js
Normal file
129
src/io/cloud.js
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
const Variable = require('../engine/variable');
|
||||||
|
const log = require('../util/log');
|
||||||
|
|
||||||
|
class Cloud {
|
||||||
|
/**
|
||||||
|
* @typedef updateVariable
|
||||||
|
* @param {string} name The name of the cloud variable to update on the server
|
||||||
|
* @param {(string | number)} value The value to update the cloud variable with.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cloud data provider, responsible for managing the connection to the
|
||||||
|
* cloud data server and for posting data about cloud data activity to
|
||||||
|
* this IO device.
|
||||||
|
* @typedef {object} CloudProvider
|
||||||
|
* @property {updateVariable} updateVariable A function which sends a cloud variable
|
||||||
|
* update to the cloud data server.
|
||||||
|
* @property {Function} requestCloseConnection A function which closes
|
||||||
|
* the connection to the cloud data server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of a cloud io data post indicating a cloud variable update.
|
||||||
|
* @typedef {object} VarUpdateData
|
||||||
|
* @property {string} name The name of the variable to update
|
||||||
|
* @property {(number | string)} value The scalar value to update the variable with
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cloud io data post message.
|
||||||
|
* @typedef {object} CloudIOData
|
||||||
|
* @property {VarUpdateData} varUpdate A {@link VarUpdateData} message indicating
|
||||||
|
* a cloud variable update
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cloud IO Device responsible for sending and receiving messages from
|
||||||
|
* cloud provider (mananging the cloud server connection) and interacting
|
||||||
|
* with cloud variables in the current project.
|
||||||
|
*/
|
||||||
|
constructor () {
|
||||||
|
/**
|
||||||
|
* Reference to the cloud data provider, responsible for mananging
|
||||||
|
* the web socket connection to the cloud data server.
|
||||||
|
* @type {?CloudProvider}
|
||||||
|
*/
|
||||||
|
this.provider = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to the stage target which owns the cloud variables
|
||||||
|
* in the project.
|
||||||
|
* @type {?Target}
|
||||||
|
*/
|
||||||
|
this.stage = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a reference to the cloud data provider.
|
||||||
|
* @param {CloudProvider} provider The cloud data provider
|
||||||
|
*/
|
||||||
|
setProvider (provider) {
|
||||||
|
this.provider = provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a reference to the stage target which owns the
|
||||||
|
* cloud variables in the project.
|
||||||
|
* @param {Target} stage The stage target
|
||||||
|
*/
|
||||||
|
setStage (stage) {
|
||||||
|
this.stage = stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle incoming data to this io device.
|
||||||
|
* @param {CloudIOData} data The {@link CloudIOData} object to process
|
||||||
|
*/
|
||||||
|
postData (data) {
|
||||||
|
if (data.varUpdate) {
|
||||||
|
this.updateCloudVariable(data.varUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the cloud data provider to update the given variable with
|
||||||
|
* the given value. Does nothing if this io device does not have a provider set.
|
||||||
|
* @param {string} name The name of the variable to update
|
||||||
|
* @param {string | number} value The value to update the variable with
|
||||||
|
*/
|
||||||
|
requestUpdateVariable (name, value) {
|
||||||
|
if (this.provider) {
|
||||||
|
this.provider.updateVariable(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a cloud variable in the runtime based on the message received
|
||||||
|
* from the cloud provider.
|
||||||
|
* @param {VarUpdateData} varUpdate A {@link VarUpdateData} object describing
|
||||||
|
* a cloud variable update received from the cloud data provider.
|
||||||
|
*/
|
||||||
|
updateCloudVariable (varUpdate) {
|
||||||
|
const varName = varUpdate.name;
|
||||||
|
|
||||||
|
const variable = this.stage.lookupVariableByNameAndType(varName, Variable.SCALAR_TYPE);
|
||||||
|
if (!variable || !variable.isCloud) {
|
||||||
|
log.warn(`Received an update for a cloud variable that does not exist: ${varName}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
variable.value = varUpdate.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request the cloud data provider to close the web socket connection and
|
||||||
|
* clear this io device of references to the cloud data provider and the
|
||||||
|
* stage.
|
||||||
|
*/
|
||||||
|
clear () {
|
||||||
|
if (!this.provider) return;
|
||||||
|
|
||||||
|
this.provider.requestCloseConnection();
|
||||||
|
this.provider = null;
|
||||||
|
this.stage = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Cloud;
|
|
@ -218,6 +218,10 @@ class VirtualMachine extends EventEmitter {
|
||||||
this.runtime.ioDevices.video.setProvider(videoProvider);
|
this.runtime.ioDevices.video.setProvider(videoProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCloudProvider (cloudProvider) {
|
||||||
|
this.runtime.ioDevices.cloud.setProvider(cloudProvider);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell the specified extension to scan for a peripheral.
|
* Tell the specified extension to scan for a peripheral.
|
||||||
* @param {string} extensionId - the id of the extension.
|
* @param {string} extensionId - the id of the extension.
|
||||||
|
@ -463,6 +467,7 @@ class VirtualMachine extends EventEmitter {
|
||||||
this.emitTargetsUpdate();
|
this.emitTargetsUpdate();
|
||||||
this.emitWorkspaceUpdate();
|
this.emitWorkspaceUpdate();
|
||||||
this.runtime.setEditingTarget(this.editingTarget);
|
this.runtime.setEditingTarget(this.editingTarget);
|
||||||
|
this.runtime.ioDevices.cloud.setStage(this.runtime.getTargetForStage());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
84
test/unit/io_cloud.js
Normal file
84
test/unit/io_cloud.js
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
const test = require('tap').test;
|
||||||
|
const Cloud = require('../../src/io/cloud');
|
||||||
|
const Target = require('../../src/engine/target');
|
||||||
|
const Variable = require('../../src/engine/variable');
|
||||||
|
|
||||||
|
test('spec', t => {
|
||||||
|
const cloud = new Cloud();
|
||||||
|
|
||||||
|
t.type(cloud, 'object');
|
||||||
|
t.type(cloud.postData, 'function');
|
||||||
|
t.type(cloud.requestUpdateVariable, 'function');
|
||||||
|
t.type(cloud.updateCloudVariable, 'function');
|
||||||
|
t.type(cloud.setProvider, 'function');
|
||||||
|
t.type(cloud.setStage, 'function');
|
||||||
|
t.type(cloud.clear, 'function');
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('stage and provider are null initially', t => {
|
||||||
|
const cloud = new Cloud();
|
||||||
|
|
||||||
|
t.strictEquals(cloud.provider, null);
|
||||||
|
t.strictEquals(cloud.stage, null);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setProvider sets the provider', t => {
|
||||||
|
const cloud = new Cloud();
|
||||||
|
|
||||||
|
const provider = {
|
||||||
|
foo: 'a fake provider'
|
||||||
|
};
|
||||||
|
|
||||||
|
cloud.setProvider(provider);
|
||||||
|
t.strictEquals(cloud.provider, provider);
|
||||||
|
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('postData updates the variable', t => {
|
||||||
|
const stage = new Target();
|
||||||
|
const fooVar = new Variable(
|
||||||
|
'a fake var id',
|
||||||
|
'foo',
|
||||||
|
Variable.SCALAR_TYPE,
|
||||||
|
true /* isCloud */
|
||||||
|
);
|
||||||
|
stage.variables[fooVar.id] = fooVar;
|
||||||
|
|
||||||
|
t.strictEquals(fooVar.value, 0);
|
||||||
|
|
||||||
|
const cloud = new Cloud();
|
||||||
|
cloud.setStage(stage);
|
||||||
|
cloud.postData({varUpdate: {
|
||||||
|
name: 'foo',
|
||||||
|
value: 3
|
||||||
|
}});
|
||||||
|
t.strictEquals(fooVar.value, 3);
|
||||||
|
t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('requestUpdateVariable calls provider\'s updateVariable function', t => {
|
||||||
|
let updateVariableCalled = false;
|
||||||
|
let mockVarName = '';
|
||||||
|
let mockVarValue = '';
|
||||||
|
const mockUpdateVariable = (name, value) => {
|
||||||
|
updateVariableCalled = true;
|
||||||
|
mockVarName = name;
|
||||||
|
mockVarValue = value;
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const provider = {
|
||||||
|
updateVariable: mockUpdateVariable
|
||||||
|
};
|
||||||
|
|
||||||
|
const cloud = new Cloud();
|
||||||
|
cloud.setProvider(provider);
|
||||||
|
cloud.requestUpdateVariable('foo', 3);
|
||||||
|
t.equals(updateVariableCalled, true);
|
||||||
|
t.strictEquals(mockVarName, 'foo');
|
||||||
|
t.strictEquals(mockVarValue, 3);
|
||||||
|
t.end();
|
||||||
|
});
|
Loading…
Reference in a new issue