Skip to content

Commit

Permalink
Move progress tracking to runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
GarboMuffin committed Jan 3, 2024
1 parent b27142a commit a8639af
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 5 deletions.
48 changes: 48 additions & 0 deletions src/engine/runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,16 @@ class Runtime extends EventEmitter {
* Responsible for managing custom fonts.
*/
this.fontManager = new FontManager(this);

/**
* Total number of scratch-storage load() requests since the runtime was created or cleared.
*/
this.totalStorageRequests = 0;

/**
* Total number of finished or errored scratch-storage load() requests since the runtime was created or cleared.
*/
this.finishedStorageRequests = 0;
}

/**
Expand Down Expand Up @@ -651,6 +661,15 @@ class Runtime extends EventEmitter {
return 'AFTER_EXECUTE';
}

/**
* Event name for reporting asset download progress. Fired with finished, total
* @const {string}
*/
static get ASSET_PROGRESS () {
return 'ASSET_PROGRESS';
}


/**
* Event name when the project is started (threads may not necessarily be
* running).
Expand Down Expand Up @@ -2247,6 +2266,10 @@ class Runtime extends EventEmitter {
this.getNumberOfCloudVariables = newCloudDataManager.getNumberOfCloudVariables;
this.addCloudVariable = this._initializeAddCloudVariable(newCloudDataManager);
this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager);

this.totalStorageRequests = 0;
this.finishedStorageRequests = 0;
this.emitAssetProgress();
}

/**
Expand Down Expand Up @@ -3384,6 +3407,31 @@ class Runtime extends EventEmitter {
this.externalCommunicationMethods[method] = enabled;
this.updatePrivacy();
}

emitAssetProgress () {
this.emit(Runtime.ASSET_PROGRESS, this.finishedStorageRequests, this.totalStorageRequests);
}

loadFromStorage (assetType, assetId, dataFormat) {
if (!this.storage) {
return Promise.reject(new Error('No storage attached'));
}

this.totalStorageRequests++;
this.emitAssetProgress();

return this.storage.load(assetType, assetId, dataFormat)
.then(asset => {
this.finishedStorageRequests++;
this.emitAssetProgress();
return asset;
})
.catch(error => {
this.finishedStorageRequests++;
this.emitAssetProgress();
throw error;
});
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/import/load-costume.js
Original file line number Diff line number Diff line change
Expand Up @@ -465,11 +465,11 @@ const loadCostume = function (md5ext, costume, runtime, optVersion) {
const AssetType = runtime.storage.AssetType;
const assetType = (ext === 'svg') ? AssetType.ImageVector : AssetType.ImageBitmap;

const costumePromise = runtime.storage.load(assetType, md5, ext);
const costumePromise = runtime.loadFromStorage(assetType, md5, ext);

let textLayerPromise;
if (costume.textLayerMD5) {
textLayerPromise = runtime.storage.load(AssetType.ImageBitmap, costume.textLayerMD5, 'png');
textLayerPromise = runtime.loadFromStorage(AssetType.ImageBitmap, costume.textLayerMD5, 'png');
} else {
textLayerPromise = Promise.resolve(null);
}
Expand Down
2 changes: 1 addition & 1 deletion src/import/load-sound.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const loadSound = function (sound, runtime, soundBank) {
sound.dataFormat = ext;
return (
(sound.asset && Promise.resolve(sound.asset)) ||
runtime.storage.load(runtime.storage.AssetType.Sound, md5, ext)
runtime.loadFromStorage(runtime.storage.AssetType.Sound, md5, ext)
)
.then(soundAsset => {
sound.asset = soundAsset;
Expand Down
3 changes: 1 addition & 2 deletions src/util/tw-asset-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ class AssetUtil {
* @returns {Promise<Storage.Asset>} scratch-storage asset object
*/
static getByMd5ext (runtime, zip, assetType, md5ext) {
const storage = runtime.storage;
const idParts = StringUtil.splitFirst(md5ext, '.');
const md5 = idParts[0];
const ext = idParts[1].toLowerCase();
Expand All @@ -36,7 +35,7 @@ class AssetUtil {
}
}

return storage.load(assetType, md5, ext);
return runtime.loadFromStorage(assetType, md5, ext);
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/virtual-machine.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ class VirtualMachine extends EventEmitter {
this.runtime.on(Runtime.COMPILE_ERROR, (target, error) => {
this.emit(Runtime.COMPILE_ERROR, target, error);
});
this.runtime.on(Runtime.ASSET_PROGRESS, (finished, total) => {
this.emit(Runtime.ASSET_PROGRESS, finished, total);
});
this.runtime.on(Runtime.TURBO_MODE_OFF, () => {
this.emit(Runtime.TURBO_MODE_OFF);
});
Expand Down
140 changes: 140 additions & 0 deletions test/integration/tw_asset_progress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
const {test} = require('tap');
const Runtime = require('../../src/engine/runtime');
const VirtualMachine = require('../../src/virtual-machine');
const makeTestStorage = require('../fixtures/make-test-storage');
const {loadCostume} = require('../../src/import/load-costume');
const {loadSound} = require('../../src/import/load-sound');

test('emitAssetProgress', t => {
const vm = new VirtualMachine();

let runtimeOK = false;
let vmOK = false;
vm.runtime.on('ASSET_PROGRESS', (finished, total) => {
t.equal(finished, 1, 'runtime finished');
t.equal(total, 2, 'runtime total');
runtimeOK = true;
});
vm.on('ASSET_PROGRESS', (finished, total) => {
t.equal(finished, 1, 'vm finished');
t.equal(total, 2, 'vm total');
vmOK = true;
});

vm.runtime.totalStorageRequests = 2;
vm.runtime.finishedStorageRequests = 1;
vm.runtime.emitAssetProgress();

t.ok(runtimeOK, 'runtime');
t.ok(vmOK, 'vm');
t.end();
});

test('dispose', t => {
t.plan(4);

const runtime = new Runtime();
runtime.finishedStorageRequests = 10;
runtime.totalStorageRequests = 10;

runtime.on('ASSET_PROGRESS', (finished, total) => {
t.equal(finished, 0, 'event finished');
t.equal(total, 0, 'event total');
});

runtime.dispose();

t.equal(runtime.finishedStorageRequests, 0, 'property finishedStorageRequests');
t.equal(runtime.totalStorageRequests, 0, 'property totalStorageRequests');
t.end();
});

test('loadFromStorage', t => {
const runtime = new Runtime();

const storage = makeTestStorage();
storage.load = (assetType, assetId) => {
if (assetId === 'bad') {
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('Bad :(');
}
return Promise.resolve({
assetId
});
};
runtime.attachStorage(storage);

const log = [];
runtime.on('ASSET_PROGRESS', (finished, total) => {
log.push([finished, total]);
});

Promise.all([
runtime.loadFromStorage(storage.AssetType.ImageBitmap, '1234', 'png'),
runtime.loadFromStorage(storage.AssetType.ImageBitmap, '5678', 'png')
]).then(assets => {
t.same(assets.map(i => i.assetId), [
'1234',
'5678'
]);

runtime.loadFromStorage(storage.AssetType.ImageBitmap, 'bad', 'png').catch(error => {
t.equal(error, 'Bad :(');
t.same(log, [
[0, 1],
[0, 2],
[1, 2],
[2, 2],
[2, 3],
[3, 3]
]);
t.end();
});
});
});

test('load costume', t => {
const runtime = new Runtime();

const storage = makeTestStorage();
storage.load = (assetType, assetId) => Promise.resolve({
assetId
});
runtime.attachStorage(storage);

const log = [];
runtime.on('ASSET_PROGRESS', (finished, total) => {
log.push([finished, total]);
});

loadCostume('1234.png', {}, runtime).then(() => {
t.same(log, [
[0, 1],
[1, 1]
]);
t.end();
});
});

test('load sound', t => {
const runtime = new Runtime();

const storage = makeTestStorage();
storage.load = (assetType, assetId) => Promise.resolve({
assetId
});
runtime.attachStorage(storage);

const log = [];
runtime.on('ASSET_PROGRESS', (finished, total) => {
log.push([finished, total]);
});

loadSound({md5: '1234.wav'}, runtime).then(() => {
t.same(log, [
[0, 1],
[1, 1]
]);
t.end();
});
});

0 comments on commit a8639af

Please sign in to comment.