mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-25 07:22:33 -05:00
Deserialize compressed primitives.
This commit is contained in:
parent
21d60604ac
commit
d61ea23e1e
1 changed files with 288 additions and 59 deletions
|
@ -9,6 +9,8 @@ const Blocks = require('../engine/blocks');
|
||||||
const Sprite = require('../sprites/sprite');
|
const Sprite = require('../sprites/sprite');
|
||||||
const Variable = require('../engine/variable');
|
const Variable = require('../engine/variable');
|
||||||
const log = require('../util/log');
|
const log = require('../util/log');
|
||||||
|
const uid = require('../util/uid');
|
||||||
|
// const Cast = require('../util/Cast');
|
||||||
|
|
||||||
const {loadCostume} = require('../import/load-costume.js');
|
const {loadCostume} = require('../import/load-costume.js');
|
||||||
const {loadSound} = require('../import/load-sound.js');
|
const {loadSound} = require('../import/load-sound.js');
|
||||||
|
@ -26,56 +28,133 @@ const {deserializeCostume, deserializeSound} = require('./deserialize-assets.js'
|
||||||
* @property {Map.<string, string>} extensionURLs - map of ID => URL from project metadata. May not match extensionIDs.
|
* @property {Map.<string, string>} extensionURLs - map of ID => URL from project metadata. May not match extensionIDs.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const INPUT_SAME_BLOCK_SHADOW = 1;
|
const INPUT_SAME_BLOCK_SHADOW = 1; // unobscured shadow
|
||||||
const INPUT_BLOCK_NO_SHADOW = 2;
|
const INPUT_BLOCK_NO_SHADOW = 2; // no shadow
|
||||||
const INPUT_DIFF_BLOCK_SHADOW = 3;
|
const INPUT_DIFF_BLOCK_SHADOW = 3; // obscured shadow
|
||||||
// haven't found a case where block = null, but shadow is present...
|
// haven't found a case where block = null, but shadow is present...
|
||||||
|
|
||||||
// Constants referring to 'primitive' types, e.g.
|
// Constants referring to 'primitive' blocks that are usually shadows,
|
||||||
|
// or in the case of variables and lists, appear quite often in projects
|
||||||
// math_number
|
// math_number
|
||||||
|
const MATH_NUM_PRIMITIVE = 4; // there's no reason these constants can't collide
|
||||||
|
// math_positive_number
|
||||||
|
const POSITIVE_NUM_PRIMITIVE = 5; // with the above, but removing duplication for clarity
|
||||||
|
// math_whole_number
|
||||||
|
const WHOLE_NUM_PRIMITIVE = 6;
|
||||||
|
// math_integer
|
||||||
|
const INTEGER_NUM_PRIMITIVE = 7;
|
||||||
|
// math_angle
|
||||||
|
const ANGLE_NUM_PRIMITIVE = 8;
|
||||||
|
// colour_picker
|
||||||
|
const COLOR_PICKER_PRIMITIVE = 9;
|
||||||
// text
|
// text
|
||||||
|
const TEXT_PRIMITIVE = 10;
|
||||||
// event_broadcast_menu
|
// event_broadcast_menu
|
||||||
|
const BROADCAST_PRIMITIVE = 11;
|
||||||
// data_variable
|
// data_variable
|
||||||
|
const VAR_PRIMITIVE = 12;
|
||||||
// data_listcontents
|
// data_listcontents
|
||||||
const MATH_PRIMITIVE = 4; // there's no reason these constants can't collide
|
const LIST_PRIMITIVE = 13;
|
||||||
const TEXT_PRIMITIVE = 5; // with the above, but removing duplication for clarity
|
|
||||||
const BROADCAST_PRIMITIVE = 6;
|
const primitiveOpcodeInfoMap = {
|
||||||
const VAR_PRIMITIVE = 7;
|
math_number: [MATH_NUM_PRIMITIVE, 'NUM'],
|
||||||
const LIST_PRIMITIVE = 8;
|
math_positive_number: [POSITIVE_NUM_PRIMITIVE, 'NUM'],
|
||||||
|
math_whole_number: [WHOLE_NUM_PRIMITIVE, 'NUM'],
|
||||||
|
math_integer: [INTEGER_NUM_PRIMITIVE, 'NUM'],
|
||||||
|
math_angle: [ANGLE_NUM_PRIMITIVE, 'NUM'],
|
||||||
|
colour_picker: [COLOR_PICKER_PRIMITIVE, 'COLOUR'],
|
||||||
|
text: [TEXT_PRIMITIVE, 'TEXT'],
|
||||||
|
event_broadcast_menu: [BROADCAST_PRIMITIVE, 'BROADCAST_OPTION'],
|
||||||
|
data_variable: [VAR_PRIMITIVE, 'VARIABLE'],
|
||||||
|
data_listcontents: [LIST_PRIMITIVE, 'LIST']
|
||||||
|
};
|
||||||
|
|
||||||
const serializePrimitiveBlock = function (block) {
|
const serializePrimitiveBlock = function (block) {
|
||||||
// Returns an array represeting a primitive block or null if not one of
|
// Returns an array represeting a primitive block or null if not one of
|
||||||
// the primitive types above
|
// the primitive types above
|
||||||
if (block.opcode === 'math_number') {
|
if (primitiveOpcodeInfoMap.hasOwnProperty(block.opcode)) {
|
||||||
const numField = block.fields.NUM;
|
const primitiveInfo = primitiveOpcodeInfoMap[block.opcode];
|
||||||
// If the primitive block has already been serialized, e.g. serializeFields has run on it
|
const primitiveConstant = primitiveInfo[0];
|
||||||
// then the value of its NUM field will be an array with the value we want
|
const fieldName = primitiveInfo[1];
|
||||||
// if (Array.isArray(numField)) return [MATH_PRIMITIVE, numField[0]];
|
const field = block.fields[fieldName];
|
||||||
// otherwise get the num out of the unserialized field
|
const primitiveDesc = [primitiveConstant, field.value];
|
||||||
return [MATH_PRIMITIVE, numField.value];
|
if (block.opcode === 'event_broadcast_menu') {
|
||||||
|
primitiveDesc.push(field.id);
|
||||||
|
} else if (block.opcode === 'data_variable' || block.opcode === 'data_listcontents') {
|
||||||
|
primitiveDesc.push(field.id);
|
||||||
|
if (block.topLevel) {
|
||||||
|
primitiveDesc.push(block.x ? Math.round(block.x) : 0);
|
||||||
|
primitiveDesc.push(block.y ? Math.round(block.y) : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return primitiveDesc;
|
||||||
}
|
}
|
||||||
if (block.opcode === 'text') {
|
|
||||||
const textField = block.fields.TEXT;
|
|
||||||
// if (Array.isArray(textField)) return [TEXT_PRIMITIVE, textField[0]];
|
|
||||||
return [TEXT_PRIMITIVE, textField.value];
|
|
||||||
}
|
|
||||||
if (block.opcode === 'event_broadcast_menu') {
|
|
||||||
const broadcastField = block.fields.BROADCAST_OPTION;
|
|
||||||
// if (Array.isArray(broadcastField)) return [BROADCAST_PRIMITIVE, broadcastField[0], broadcastField[1]];
|
|
||||||
return [BROADCAST_PRIMITIVE, broadcastField.value, broadcastField.id];
|
|
||||||
}
|
|
||||||
if (block.opcode === 'data_variable') {
|
|
||||||
const variableField = block.fields.VARIABLE;
|
|
||||||
// if (Array.isArray(variableField)) return [VAR_PRIMITIVE, variableField[0], variableField[1]];
|
|
||||||
return [VAR_PRIMITIVE, variableField.value, variableField.id];
|
|
||||||
}
|
|
||||||
if (block.opcode === 'data_listcontents') {
|
|
||||||
const listField = block.fields.LIST;
|
|
||||||
// if (Array.isArray(listField)) return [LIST_PRIMITIVE, listField[0], listField[1]];
|
|
||||||
return [LIST_PRIMITIVE, listField.value, listField.id];
|
|
||||||
}
|
|
||||||
// If none of the above, return null
|
|
||||||
return null;
|
return null;
|
||||||
|
// if (block.opcode === 'math_number') {
|
||||||
|
// const numField = block.fields.NUM;
|
||||||
|
// // const numValue = (typeof numField.value === 'number') ?
|
||||||
|
// // numField.value : Cast.toNumber(numField.value);
|
||||||
|
// return [MATH_NUM_PRIMITIVE, numField.value];
|
||||||
|
// }
|
||||||
|
// if (block.opcode === 'math_positive_number') {
|
||||||
|
// const positiveNumField = block.fields.NUM;
|
||||||
|
// // TODO should I actually be providing more validation here and ensure that the number is positive?
|
||||||
|
// // const numValue = (typeof positiveNumField.value === 'number') ?
|
||||||
|
// // positiveNumField.value : Cast.toNumber(positiveNumField.value);
|
||||||
|
// return [POSITIVE_NUM_PRIMITIVE, positiveNumField.Value];
|
||||||
|
// }
|
||||||
|
// if (block.opcode === 'math_whole_number') {
|
||||||
|
// const wholeNumField = block.fields.NUM;
|
||||||
|
// const numValue = (typeof wholeNumField.value === 'number') ?
|
||||||
|
// wholeNumField.value : JSON.parse(wholeNumField.value);
|
||||||
|
// return [WHOLE_NUM_PRIMITIVE, numValue];
|
||||||
|
// }
|
||||||
|
// if (block.opcode === 'math_integer') {
|
||||||
|
// const integerNumField = block.fields.NUM;
|
||||||
|
// const numValue = (typeof integerNumField.value === 'number') ?
|
||||||
|
// integerNumField.value : JSON.parse(integerNumField.value);
|
||||||
|
// return [INTEGER_NUM_PRIMITIVE, numValue];
|
||||||
|
// }
|
||||||
|
// if (block.opcode === 'math_angle') {
|
||||||
|
// const angleNumField = block.fields.NUM;
|
||||||
|
// const numValue = (typeof angleNumField.value === 'number') ?
|
||||||
|
// angleNumField.value : JSON.parse(angleNumField.value);
|
||||||
|
// return [ANGLE_NUM_PRIMITIVE, numValue];
|
||||||
|
// }
|
||||||
|
// if (block.opcode === 'colour_picker') {
|
||||||
|
// const colorField = block.fields.COLOUR; // field uses this spelling
|
||||||
|
// return [COLOR_PICKER_PRIMITIVE, colorField.value];
|
||||||
|
// }
|
||||||
|
// if (block.opcode === 'text') {
|
||||||
|
// const textField = block.fields.TEXT;
|
||||||
|
// return [TEXT_PRIMITIVE, textField.value];
|
||||||
|
// }
|
||||||
|
// if (block.opcode === 'event_broadcast_menu') {
|
||||||
|
// const broadcastField = block.fields.BROADCAST_OPTION;
|
||||||
|
// return [BROADCAST_PRIMITIVE, broadcastField.value, broadcastField.id];
|
||||||
|
// }
|
||||||
|
// if (block.opcode === 'data_variable') {
|
||||||
|
// const variableField = block.fields.VARIABLE;
|
||||||
|
// const varArray = [VAR_PRIMITIVE, variableField.value, variableField.id];
|
||||||
|
// if (block.topLevel) {
|
||||||
|
// varArray.push(block.x ? Math.round(block.x) : 0);
|
||||||
|
// varArray.push(block.y ? Math.round(block.y) : 0);
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// return varArray;
|
||||||
|
// }
|
||||||
|
// if (block.opcode === 'data_listcontents') {
|
||||||
|
// const listField = block.fields.LIST;
|
||||||
|
// const listArray = [LIST_PRIMITIVE, listField.value, listField.id];
|
||||||
|
// if (block.topLevel) {
|
||||||
|
// listArray.push(block.x ? Math.round(block.x) : 0);
|
||||||
|
// listArray.push(block.y ? Math.round(block.y) : 0);
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// return listArray;
|
||||||
|
// }
|
||||||
|
// // If none of the above, return null
|
||||||
|
// return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const serializeInputs = function (inputs) {
|
const serializeInputs = function (inputs) {
|
||||||
|
@ -124,10 +203,10 @@ const serializeBlock = function (block) {
|
||||||
if (serializedPrimitive) return serializedPrimitive;
|
if (serializedPrimitive) return serializedPrimitive;
|
||||||
// If serializedPrimitive is null, proceed with serializing a non-primitive block
|
// If serializedPrimitive is null, proceed with serializing a non-primitive block
|
||||||
const obj = Object.create(null);
|
const obj = Object.create(null);
|
||||||
// obj.id = block.id; // don't need this, it's the index of this block in its containing object
|
|
||||||
obj.opcode = block.opcode;
|
obj.opcode = block.opcode;
|
||||||
if (block.next) obj.next = block.next; // don't serialize next if null
|
// NOTE: this is extremely important to serialize even if null;
|
||||||
// obj.next = if (block.next;
|
// not serializing `next: null` results in strange behavior
|
||||||
|
obj.next = block.next;
|
||||||
obj.parent = block.parent;
|
obj.parent = block.parent;
|
||||||
obj.inputs = serializeInputs(block.inputs);
|
obj.inputs = serializeInputs(block.inputs);
|
||||||
obj.fields = serializeFields(block.fields);
|
obj.fields = serializeFields(block.fields);
|
||||||
|
@ -172,7 +251,6 @@ const compressInputTree = function (block, blocks) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// block.inputs = newInputs;
|
|
||||||
return block;
|
return block;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -247,17 +325,9 @@ const serializeVariables = function (variables) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// should be a scalar type
|
// otherwise should be a scalar type
|
||||||
obj.variables[varId] = [v.name];
|
obj.variables[varId] = [v.name, v.value];
|
||||||
let val = v.value;
|
// only scalar vars have the potential to be cloud vars
|
||||||
if ((typeof val !== 'string') && (typeof val !== 'number')) {
|
|
||||||
log.info(`Variable: ${v.name} had value ${val} of type: ${typeof val} converting to string`);
|
|
||||||
val = JSON.stringify(val);
|
|
||||||
}
|
|
||||||
obj.variables[varId].push(val);
|
|
||||||
// Some hacked blocks have booleans as variable values
|
|
||||||
// (typeof v.value === 'string') || (typeof v.value === 'number') ?
|
|
||||||
// v.value : JSON.stringify(v.value)];
|
|
||||||
if (v.isPersistent) obj.variables[varId].push(true);
|
if (v.isPersistent) obj.variables[varId].push(true);
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -316,7 +386,154 @@ const serialize = function (runtime) {
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
|
|
||||||
const deserializeInputs = function (inputs) {
|
// Deserializes input descriptors, which is either a block id or a serialized primitive
|
||||||
|
// (see serializePrimitiveBlock function).
|
||||||
|
const deserializeInputDesc = function (inputDescOrId, parentId, isShadow, blocks) {
|
||||||
|
if (!Array.isArray(inputDescOrId)) return inputDescOrId;
|
||||||
|
const primitiveObj = Object.create(null);
|
||||||
|
const newId = uid();
|
||||||
|
primitiveObj.id = newId;
|
||||||
|
primitiveObj.next = null;
|
||||||
|
primitiveObj.parent = parentId;
|
||||||
|
primitiveObj.shadow = isShadow;
|
||||||
|
primitiveObj.inputs = Object.create(null);
|
||||||
|
// need a reference to parent id
|
||||||
|
switch (inputDescOrId[0]) {
|
||||||
|
case MATH_NUM_PRIMITIVE: {
|
||||||
|
primitiveObj.opcode = 'math_number';
|
||||||
|
primitiveObj.fields = {
|
||||||
|
NUM: {
|
||||||
|
name: 'NUM',
|
||||||
|
value: inputDescOrId[1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
primitiveObj.topLevel = false;
|
||||||
|
// what should we do about shadows
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case POSITIVE_NUM_PRIMITIVE: {
|
||||||
|
primitiveObj.opcode = 'math_positive_number';
|
||||||
|
primitiveObj.fields = {
|
||||||
|
NUM: {
|
||||||
|
name: 'NUM',
|
||||||
|
value: inputDescOrId[1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
primitiveObj.topLevel = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WHOLE_NUM_PRIMITIVE: {
|
||||||
|
primitiveObj.opcode = 'math_whole_number';
|
||||||
|
primitiveObj.fields = {
|
||||||
|
NUM: {
|
||||||
|
name: 'NUM',
|
||||||
|
value: inputDescOrId[1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
primitiveObj.topLevel = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case INTEGER_NUM_PRIMITIVE: {
|
||||||
|
primitiveObj.opcode = 'math_integer';
|
||||||
|
primitiveObj.fields = {
|
||||||
|
NUM: {
|
||||||
|
name: 'NUM',
|
||||||
|
value: inputDescOrId[1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
primitiveObj.topLevel = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ANGLE_NUM_PRIMITIVE: {
|
||||||
|
primitiveObj.opcode = 'math_angle';
|
||||||
|
primitiveObj.fields = {
|
||||||
|
NUM: {
|
||||||
|
name: 'NUM',
|
||||||
|
value: inputDescOrId[1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
primitiveObj.topLevel = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case COLOR_PICKER_PRIMITIVE: {
|
||||||
|
primitiveObj.opcode = 'colour_picker';
|
||||||
|
primitiveObj.fields = {
|
||||||
|
COLOUR: {
|
||||||
|
name: 'COLOUR',
|
||||||
|
value: inputDescOrId[1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
primitiveObj.topLevel = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TEXT_PRIMITIVE: {
|
||||||
|
primitiveObj.opcode = 'text';
|
||||||
|
primitiveObj.fields = {
|
||||||
|
TEXT: {
|
||||||
|
name: 'TEXT',
|
||||||
|
value: inputDescOrId[1]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
primitiveObj.topLevel = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BROADCAST_PRIMITIVE: {
|
||||||
|
primitiveObj.opcode = 'event_broadcast_menu';
|
||||||
|
primitiveObj.fields = {
|
||||||
|
BROADCAST_OPTION: {
|
||||||
|
name: 'BROADCAST_OPTION',
|
||||||
|
value: inputDescOrId[1],
|
||||||
|
id: inputDescOrId[2],
|
||||||
|
variableType: Variable.BROADCAST_MESSAGE_TYPE
|
||||||
|
}
|
||||||
|
};
|
||||||
|
primitiveObj.topLevel = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VAR_PRIMITIVE: {
|
||||||
|
primitiveObj.opcode = 'data_variable';
|
||||||
|
primitiveObj.fields = {
|
||||||
|
VARIABLE: {
|
||||||
|
name: 'VARIABLE',
|
||||||
|
value: inputDescOrId[1],
|
||||||
|
id: inputDescOrId[2],
|
||||||
|
variableType: Variable.SCALAR_TYPE
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (inputDescOrId.length > 3) {
|
||||||
|
primitiveObj.topLevel = true;
|
||||||
|
primitiveObj.x = inputDescOrId[3];
|
||||||
|
primitiveObj.y = inputDescOrId[4];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LIST_PRIMITIVE: {
|
||||||
|
primitiveObj.opcode = 'data_listcontents';
|
||||||
|
primitiveObj.fields = {
|
||||||
|
LIST: {
|
||||||
|
name: 'LIST',
|
||||||
|
value: inputDescOrId[1],
|
||||||
|
id: inputDescOrId[2],
|
||||||
|
variableType: Variable.LIST_TYPE
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (inputDescOrId.length > 3) {
|
||||||
|
primitiveObj.topLevel = true;
|
||||||
|
primitiveObj.x = inputDescOrId[3];
|
||||||
|
primitiveObj.y = inputDescOrId[4];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
log.error(`Found unknown primitive type during deserialization: ${JSON.stringify(inputDescOrId)}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blocks[newId] = primitiveObj;
|
||||||
|
return newId;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deserializeInputs = function (inputs, parentId, blocks) {
|
||||||
// Explicitly not using Object.create(null) here
|
// Explicitly not using Object.create(null) here
|
||||||
// because we call prototype functions later in the vm
|
// because we call prototype functions later in the vm
|
||||||
const obj = {};
|
const obj = {};
|
||||||
|
@ -328,12 +545,12 @@ const deserializeInputs = function (inputs) {
|
||||||
const blockShadowInfo = inputDescArr[0];
|
const blockShadowInfo = inputDescArr[0];
|
||||||
if (blockShadowInfo === INPUT_SAME_BLOCK_SHADOW) {
|
if (blockShadowInfo === INPUT_SAME_BLOCK_SHADOW) {
|
||||||
// block and shadow are the same id, and only one is provided
|
// block and shadow are the same id, and only one is provided
|
||||||
block = shadow = inputDescArr[1];
|
block = shadow = deserializeInputDesc(inputDescArr[1], parentId, true, blocks);
|
||||||
} else if (blockShadowInfo === INPUT_BLOCK_NO_SHADOW) {
|
} else if (blockShadowInfo === INPUT_BLOCK_NO_SHADOW) {
|
||||||
block = inputDescArr[1];
|
block = deserializeInputDesc(inputDescArr[1], parentId, false, blocks);
|
||||||
} else { // assume INPUT_DIFF_BLOCK_SHADOW
|
} else { // assume INPUT_DIFF_BLOCK_SHADOW
|
||||||
block = inputDescArr[1];
|
block = deserializeInputDesc(inputDescArr[1], parentId, false, blocks);
|
||||||
shadow = inputDescArr[2];
|
shadow = deserializeInputDesc(inputDescArr[2], parentId, true, blocks);
|
||||||
}
|
}
|
||||||
obj[inputName] = {
|
obj[inputName] = {
|
||||||
name: inputName,
|
name: inputName,
|
||||||
|
@ -397,13 +614,26 @@ const parseScratchObject = function (object, runtime, extensions, zip) {
|
||||||
for (const blockId in object.blocks) {
|
for (const blockId in object.blocks) {
|
||||||
if (!object.blocks.hasOwnProperty(blockId)) continue;
|
if (!object.blocks.hasOwnProperty(blockId)) continue;
|
||||||
const blockJSON = object.blocks[blockId];
|
const blockJSON = object.blocks[blockId];
|
||||||
|
if (Array.isArray(blockJSON)) {
|
||||||
|
// this is one of the primitives
|
||||||
|
// delete the old entry in object.blocks and replace it w/the
|
||||||
|
// deserialized object
|
||||||
|
delete object.blocks[blockId];
|
||||||
|
deserializeInputDesc(blockJSON, null, false, object.blocks);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
blockJSON.id = blockId; // add id back to block since it wasn't serialized
|
blockJSON.id = blockId; // add id back to block since it wasn't serialized
|
||||||
const serializedInputs = blockJSON.inputs;
|
const serializedInputs = blockJSON.inputs;
|
||||||
const deserializedInputs = deserializeInputs(serializedInputs);
|
const deserializedInputs = deserializeInputs(serializedInputs, blockId, object.blocks);
|
||||||
blockJSON.inputs = deserializedInputs;
|
blockJSON.inputs = deserializedInputs;
|
||||||
const serializedFields = blockJSON.fields;
|
const serializedFields = blockJSON.fields;
|
||||||
const deserializedFields = deserializeFields(serializedFields);
|
const deserializedFields = deserializeFields(serializedFields);
|
||||||
blockJSON.fields = deserializedFields;
|
blockJSON.fields = deserializedFields;
|
||||||
|
}
|
||||||
|
// Take a second pass to create objects and add extensions
|
||||||
|
for (const blockId in object.blocks) {
|
||||||
|
if (!object.blocks.hasOwnProperty(blockId)) continue;
|
||||||
|
const blockJSON = object.blocks[blockId];
|
||||||
blocks.createBlock(blockJSON);
|
blocks.createBlock(blockJSON);
|
||||||
|
|
||||||
const dotIndex = blockJSON.opcode.indexOf('.');
|
const dotIndex = blockJSON.opcode.indexOf('.');
|
||||||
|
@ -412,7 +642,6 @@ const parseScratchObject = function (object, runtime, extensions, zip) {
|
||||||
extensions.extensionIDs.add(extensionId);
|
extensions.extensionIDs.add(extensionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// console.log(blocks);
|
|
||||||
}
|
}
|
||||||
// Costumes from JSON.
|
// Costumes from JSON.
|
||||||
const costumePromises = (object.costumes || []).map(costumeSource => {
|
const costumePromises = (object.costumes || []).map(costumeSource => {
|
||||||
|
|
Loading…
Reference in a new issue