mirror of
https://github.com/scratchfoundation/scratch-vm.git
synced 2024-12-23 14:32:59 -05:00
Merge pull request #1947 from mzgoddard/sb2-load-assets-first
Deserialize sb2 assets before other sprite data and blocks
This commit is contained in:
commit
9350c2a9c2
1 changed files with 86 additions and 37 deletions
|
@ -391,47 +391,28 @@ const parseMonitorObject = (object, runtime, targets, extensions) => {
|
|||
};
|
||||
|
||||
/**
|
||||
* Parse a single "Scratch object" and create all its in-memory VM objects.
|
||||
* TODO: parse the "info" section, especially "savedExtensions"
|
||||
* Parse the assets of a single "Scratch object" and load them. This
|
||||
* preprocesses objects to support loading the data for those assets over a
|
||||
* network while the objects are further processed into Blocks, Sprites, and a
|
||||
* list of needed Extensions.
|
||||
* @param {!object} object - From-JSON "Scratch object:" sprite, stage, watcher.
|
||||
* @param {!Runtime} runtime - Runtime object to load all structures into.
|
||||
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
||||
* @param {boolean} topLevel - Whether this is the top-level object (stage).
|
||||
* @param {?object} zip - Optional zipped assets for local file import
|
||||
* @return {!Promise.<Array.<Target>>} Promise for the loaded targets when ready, or null for unsupported objects.
|
||||
* @return {?{costumePromises:Array.<Promise>,soundPromises:Array.<Promise>,children:object}}
|
||||
* Object of arrays of promises and child objects for asset objects used in
|
||||
* Sprites. null for unsupported objects.
|
||||
*/
|
||||
const parseScratchObject = function (object, runtime, extensions, topLevel, zip) {
|
||||
const parseScratchAssets = function (object, runtime, topLevel, zip) {
|
||||
if (!object.hasOwnProperty('objName')) {
|
||||
if (object.hasOwnProperty('listName')) {
|
||||
// Shim these objects so they can be processed as monitors
|
||||
object.cmd = 'contentsOfList:';
|
||||
object.param = object.listName;
|
||||
object.mode = 'list';
|
||||
}
|
||||
// Defer parsing monitors until targets are all parsed
|
||||
object.deferredMonitor = true;
|
||||
return Promise.resolve(object);
|
||||
// Skip parsing monitors. Or any other objects missing objName.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Blocks container for this object.
|
||||
const blocks = new Blocks(runtime);
|
||||
// @todo: For now, load all Scratch objects (stage/sprites) as a Sprite.
|
||||
const sprite = new Sprite(blocks, runtime);
|
||||
// Sprite/stage name from JSON.
|
||||
if (object.hasOwnProperty('objName')) {
|
||||
if (topLevel && object.objName !== 'Stage') {
|
||||
for (const child of object.children) {
|
||||
if (!child.hasOwnProperty('objName') && child.target === object.objName) {
|
||||
child.target = 'Stage';
|
||||
}
|
||||
}
|
||||
object.objName = 'Stage';
|
||||
}
|
||||
const assets = {costumePromises: [], soundPromises: [], children: []};
|
||||
|
||||
sprite.name = object.objName;
|
||||
}
|
||||
// Costumes from JSON.
|
||||
const costumePromises = [];
|
||||
const costumePromises = assets.costumePromises;
|
||||
if (object.hasOwnProperty('costumes')) {
|
||||
for (let i = 0; i < object.costumes.length; i++) {
|
||||
const costumeSource = object.costumes[i];
|
||||
|
@ -476,7 +457,7 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip)
|
|||
}
|
||||
}
|
||||
// Sounds from JSON
|
||||
const soundPromises = [];
|
||||
const soundPromises = assets.soundPromises;
|
||||
if (object.hasOwnProperty('sounds')) {
|
||||
for (let s = 0; s < object.sounds.length; s++) {
|
||||
const soundSource = object.sounds[s];
|
||||
|
@ -505,12 +486,72 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip)
|
|||
// the file name of the sound should be the soundID (provided from the project.json)
|
||||
// followed by the file ext
|
||||
const assetFileName = `${soundSource.soundID}.${ext}`;
|
||||
soundPromises.push(deserializeSound(sound, runtime, zip, assetFileName)
|
||||
.then(() => loadSound(sound, runtime, sprite))
|
||||
);
|
||||
soundPromises.push(deserializeSound(sound, runtime, zip, assetFileName).then(() => sound));
|
||||
}
|
||||
}
|
||||
|
||||
// The stage will have child objects; recursively process them.
|
||||
const childrenAssets = assets.children;
|
||||
if (object.children) {
|
||||
for (let m = 0; m < object.children.length; m++) {
|
||||
childrenAssets.push(parseScratchAssets(object.children[m], runtime, false, zip));
|
||||
}
|
||||
}
|
||||
|
||||
return assets;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a single "Scratch object" and create all its in-memory VM objects.
|
||||
* TODO: parse the "info" section, especially "savedExtensions"
|
||||
* @param {!object} object - From-JSON "Scratch object:" sprite, stage, watcher.
|
||||
* @param {!Runtime} runtime - Runtime object to load all structures into.
|
||||
* @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
|
||||
* @param {boolean} topLevel - Whether this is the top-level object (stage).
|
||||
* @param {?object} zip - Optional zipped assets for local file import
|
||||
* @param {object} assets - Promises for assets of this scratch object grouped
|
||||
* into costumes and sounds
|
||||
* @return {!Promise.<Array.<Target>>} Promise for the loaded targets when ready, or null for unsupported objects.
|
||||
*/
|
||||
const parseScratchObject = function (object, runtime, extensions, topLevel, zip, assets) {
|
||||
if (!object.hasOwnProperty('objName')) {
|
||||
if (object.hasOwnProperty('listName')) {
|
||||
// Shim these objects so they can be processed as monitors
|
||||
object.cmd = 'contentsOfList:';
|
||||
object.param = object.listName;
|
||||
object.mode = 'list';
|
||||
}
|
||||
// Defer parsing monitors until targets are all parsed
|
||||
object.deferredMonitor = true;
|
||||
return Promise.resolve(object);
|
||||
}
|
||||
|
||||
// Blocks container for this object.
|
||||
const blocks = new Blocks(runtime);
|
||||
// @todo: For now, load all Scratch objects (stage/sprites) as a Sprite.
|
||||
const sprite = new Sprite(blocks, runtime);
|
||||
// Sprite/stage name from JSON.
|
||||
if (object.hasOwnProperty('objName')) {
|
||||
if (topLevel && object.objName !== 'Stage') {
|
||||
for (const child of object.children) {
|
||||
if (!child.hasOwnProperty('objName') && child.target === object.objName) {
|
||||
child.target = 'Stage';
|
||||
}
|
||||
}
|
||||
object.objName = 'Stage';
|
||||
}
|
||||
|
||||
sprite.name = object.objName;
|
||||
}
|
||||
// Costumes from JSON.
|
||||
const costumePromises = assets.costumePromises;
|
||||
// Sounds from JSON
|
||||
const soundPromises = assets.soundPromises;
|
||||
for (let s = 0; s < soundPromises.length; s++) {
|
||||
soundPromises[s] = soundPromises[s]
|
||||
.then(sound => loadSound(sound, runtime, sprite));
|
||||
}
|
||||
|
||||
// Create the first clone, and load its run-state from JSON.
|
||||
const target = sprite.createClone(topLevel ? StageLayering.BACKGROUND_LAYER : StageLayering.SPRITE_LAYER);
|
||||
|
||||
|
@ -703,7 +744,9 @@ const parseScratchObject = function (object, runtime, extensions, topLevel, zip)
|
|||
const childrenPromises = [];
|
||||
if (object.children) {
|
||||
for (let m = 0; m < object.children.length; m++) {
|
||||
childrenPromises.push(parseScratchObject(object.children[m], runtime, extensions, false, zip));
|
||||
childrenPromises.push(
|
||||
parseScratchObject(object.children[m], runtime, extensions, false, zip, assets.children[m])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -810,7 +853,13 @@ const sb2import = function (json, runtime, optForceSprite, zip) {
|
|||
extensionIDs: new Set(),
|
||||
extensionURLs: new Map()
|
||||
};
|
||||
return parseScratchObject(json, runtime, extensions, !optForceSprite, zip)
|
||||
return Promise.resolve(parseScratchAssets(json, runtime, !optForceSprite, zip))
|
||||
// Force this promise to wait for the next loop in the js tick. Let
|
||||
// storage have some time to send off asset requests.
|
||||
.then(assets => Promise.resolve(assets))
|
||||
.then(assets => (
|
||||
parseScratchObject(json, runtime, extensions, !optForceSprite, zip, assets)
|
||||
))
|
||||
.then(reorderParsedTargets)
|
||||
.then(targets => ({
|
||||
targets,
|
||||
|
|
Loading…
Reference in a new issue