dev21w45a

This commit is contained in:
Victor Westerlund 2021-11-08 16:38:56 +01:00
parent ff0ed25a3b
commit 86ea8cd031
3 changed files with 129 additions and 26 deletions

View file

@ -10,16 +10,32 @@ export default class Monkeydo extends MonkeyMaster {
Object.assign(this.methods,methods); Object.assign(this.methods,methods);
} }
// Execute a task
do(task) { do(task) {
console.log("TASK",task); if(!task[1] in this.methods) return;
const args = task.splice(0,2);
this.methods[task[1]](...args);
} }
async loop(times = null) { async debug(state = true) {
times = times < 0 ? null : times; return await this.setFlag("debug",state);
return await this.setFlag("loop",times);
} }
// Loop playback X times or negative number for infinite
async loop(times = 255) {
if(typeof times !== "number") {
times = parseInt(times);
}
// Clamp number to 8 bit max
times = Math.min(Math.max(times,0),255);
return await this.setFlag("playing",times);
}
// Load Monkeydo manifest
async load(manifest) { async load(manifest) {
if(typeof manifest === "object") {
manifest = JSON.stringify(manifest);
}
return await this.loadManifest(manifest);
} }
} }

View file

@ -4,10 +4,44 @@ importScripts("https://unpkg.com/comlink/dist/umd/comlink.js");
class Monkey { class Monkey {
constructor() { constructor() {
this.manifest = {};
// Runtime flags
this.flags = new Uint8ClampedArray(2); this.flags = new Uint8ClampedArray(2);
this.tasks = [];
// Runtime task queue
this.queue = {
thisTask: null,
nextTask: null
}
}
// Task scheduler
next() {
if(this.flags[0] === 0) return;
const task = this.tasks[this.i];
// Run task after delay
this.queue.thisTask = setTimeout(() => {
// Dispatch task to main thread
this.postMessage(["TASK",task]);
this.i++;
},task[0]);
// Loop until flag is 0 or infinite if 255
if(this.i === this.tasks.length) {
this.i = 0;
if(flags[1] === 255) return;
flags[1]--;
}
// Queue the next task
this.queue.nextTask = setTimeout(() => this.next(),task[0]);
}
abort() {
clearTimeout(this.queue.thisTask);
clearTimeout(this.queue.nextTask);
this.queue.thisTask = null;
this.queue.nextTask = null;
this.flags[1] = 0; // Playing: false
} }
// Set or get a runtime flag // Set or get a runtime flag
@ -15,15 +49,28 @@ class Monkey {
return value ? this.flags[index] = value : this.flags[index]; return value ? this.flags[index] = value : this.flags[index];
} }
// Fetch and install manifest from URL
async fetchManifest(url) {
const manifest = await fetch(url);
const json = await manifest.json();
return await this.loadManifest(json);
}
// Install a Monkeydo manifest
async loadManifest(manifest) { async loadManifest(manifest) {
try { return await new Promise((resolve,reject) => {
const data = JSON.parse(manifest); if(typeof manifest !== "object") {
this.manifest = data; try {
this.flags[0] = 1; manifest = JSON.parse(manifest);
} }
catch { catch {
const url = new URL(manifest); reject("Failed to load manifest");
} }
}
this.tasks = manifest.tasks;
this.flags[0] = 1; // Manifest loaded: true
resolve();
});
} }
} }

View file

@ -7,8 +7,21 @@ export default class MonkeyMaster {
this.comlink = null; this.comlink = null;
this.ready = false; this.ready = false;
this.flagQueue = []; // Tasks will be queued here on runtime if the worker isn't ready
this.init(); this.queue = {
_flags: [],
set flag(flag) {
this._flags.push(flag);
},
// Attempt to send all queued flags
sendAllFlags: () => {
// Copy flags and clear queue
const flags = [...this.queue._flags];
this.queue._flags = [];
flags.forEach(flag => this.setFlag(flag));
}
};
} }
// Import worker relative to this module // Import worker relative to this module
@ -16,6 +29,7 @@ export default class MonkeyMaster {
const name = "Monkey.js"; const name = "Monkey.js";
const url = new URL(import.meta.url); const url = new URL(import.meta.url);
// Replace pathname of this file with worker
const path = url.pathname.split("/"); const path = url.pathname.split("/");
path[path.length - 1] = name; path[path.length - 1] = name;
@ -27,24 +41,31 @@ export default class MonkeyMaster {
// Spawn and wrap dedicated worker with Comlink // Spawn and wrap dedicated worker with Comlink
const worker = new Worker(this.getWorkerPath()); const worker = new Worker(this.getWorkerPath());
const Monkey = Comlink.wrap(worker); const Monkey = Comlink.wrap(worker);
this.comlink = await new Monkey(); this.comlink = await new Monkey();
// Wait for comlink to initialize proxy and send queued flags
return await new Promise((resolve,reject) => {
if(!this.comlink) reject("Failed to open proxy to worker");
this.ready = true;
this.queue.sendAllFlags();
resolve();
});
} }
// Return a flag array index by name // Return a flag array index by name
flagStringToIndex(flag) { flagStringToIndex(flag) {
const flags = [ const flags = [
"MANIFEST_LOADED", "MANIFEST_LOADED",
"PLAYING", "PLAYING"
"LOOP"
]; ];
// Translate string to index // Translate string to index
if(typeof flag === "string" || flag < 0) { if(typeof flag === "string" || flag < 0) {
const key = flags.indexOf(flag.toUpperCase()); const key = flags.indexOf(flag.toUpperCase());
if(key < 0) { if(key < 0) return;
return false;
}
} }
// Check key is in bounds // Check key is in bounds
if(flag < 0 || flags > flags.length - 1) { if(flag < 0 || flags > flags.length - 1) {
throw new Error(`Array key '${flag}' out of range`); throw new Error(`Array key '${flag}' out of range`);
@ -57,11 +78,30 @@ export default class MonkeyMaster {
return await this.comlink.flag(key); return await this.comlink.flag(key);
} }
// Set or queue worker runtime flag
async setFlag(flag,value) { async setFlag(flag,value) {
const key = this.flagStringToIndex(flag); const key = this.flagStringToIndex(flag);
const update = await this.comlink.flag(0,12); if(!this.ready) {
this.queue.flag = [key,value];
return;
}
// Tell worker to update flag by key
const update = await this.comlink.flag(key,value);
if(!update) { if(!update) {
this.flagQueue.push([key,value]); this.queue.flag = [key,value];
}
return update;
}
async loadManifest(manifest) {
if(!this.ready) await this.init();
try {
const url = new URL(manifest);
this.comlink.fetchManifest(url.toString());
}
catch {
this.comlink.loadManifest(manifest);
} }
return true; return true;
} }