mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-23 06:23:37 -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(
|
||||
args.VARIABLE.id, args.VARIABLE.name);
|
||||
variable.value = args.VALUE;
|
||||
|
||||
if (variable.isCloud) {
|
||||
util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, args.VALUE]);
|
||||
}
|
||||
}
|
||||
|
||||
changeVariableBy (args, util) {
|
||||
|
@ -52,7 +56,12 @@ class Scratch3DataBlocks {
|
|||
args.VARIABLE.id, args.VARIABLE.name);
|
||||
const castedValue = Cast.toNumber(variable.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) {
|
||||
|
|
|
@ -17,6 +17,7 @@ const Variable = require('./variable');
|
|||
|
||||
// Virtual I/O devices.
|
||||
const Clock = require('../io/clock');
|
||||
const Cloud = require('../io/cloud');
|
||||
const DeviceManager = require('../io/deviceManager');
|
||||
const Keyboard = require('../io/keyboard');
|
||||
const Mouse = require('../io/mouse');
|
||||
|
@ -262,6 +263,7 @@ class Runtime extends EventEmitter {
|
|||
/** @type {Object.<string, Object>} */
|
||||
this.ioDevices = {
|
||||
clock: new Clock(),
|
||||
cloud: new Cloud(),
|
||||
deviceManager: new DeviceManager(),
|
||||
keyboard: new Keyboard(this),
|
||||
mouse: new Mouse(this),
|
||||
|
@ -1383,6 +1385,7 @@ class Runtime extends EventEmitter {
|
|||
this.targets.map(this.disposeTarget, this);
|
||||
this._monitorState = OrderedMap({});
|
||||
// @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);
|
||||
}
|
||||
|
||||
setCloudProvider (cloudProvider) {
|
||||
this.runtime.ioDevices.cloud.setProvider(cloudProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the specified extension to scan for a peripheral.
|
||||
* @param {string} extensionId - the id of the extension.
|
||||
|
@ -463,6 +467,7 @@ class VirtualMachine extends EventEmitter {
|
|||
this.emitTargetsUpdate();
|
||||
this.emitWorkspaceUpdate();
|
||||
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