import {p5} from "./p5.js" import {svg as svg_2} from "./svg.js" import {get_code} from "./keys.js" if (window) window.ludus = {run, kill, flush_stdout, stdout, p5, flush_commands, commands, result, flush_result, input, is_running, key_down, key_up, is_starting_up, p5, svg} const worker_url = new URL("worker.js", import.meta.url) const worker = new Worker(worker_url, {type: "module"}) let outbox = [] let ludus_console = "" let ludus_commands = [] let ludus_result = null let code = null let running = false let ready = false let io_interval_id = null let keys_down = new Set(); worker.onmessage = handle_messages async function handle_messages (e) { let msgs try { msgs = JSON.parse(e.data) } catch { console.log(e.data) throw Error("Main: bad json from Ludus") } for (const msg of msgs) { switch (msg.verb) { case "Complete": { console.log("Main: ludus completed with => ", msg.data) ludus_result = msg.data running = false ready = false outbox = [] break } case "Error": { console.log("Main: ludus errored with => ", msg.data) ludus_result = msg.data running = false ready = false outbox = [] break } // TODO: do more than report these case "Console": { let new_lines = msg.data.join("\n"); ludus_console = ludus_console + new_lines console.log("Main: ludus says => ", new_lines) break } case "Commands": { console.log("Main: ludus commands => ", msg.data) for (const command of msg.data) { // commands will arrive asynchronously; ensure correct ordering ludus_commands[command[1]] = command } break } case "Fetch": { console.log("Main: ludus requests => ", msg.data) const res = await fetch(msg.data, {mode: "cors"}) const text = await res.text() console.log("Main: js responds => ", text) outbox.push({verb: "Fetch", data: [msg.data, res.status, text]}) } case "Ready": { console.log("Main: ludus is ready") ready = true } } } } function io_poller () { if (io_interval_id && !running) { // flush the outbox one last time // (presumably, with the kill message) worker.postMessage(outbox) // cancel the poller clearInterval(io_interval_id) outbox = [] } if (ready && running) { if (keys_down.size > 0) bundle_keys() worker.postMessage(outbox) outbox = [] } } function bundle_keys () { outbox.push({verb: "Keys", data: Array.from(keys_down)}) } function start_io_polling () { io_interval_id = setInterval(io_poller, 100) } // runs a ludus script; does not return the result // the result must be explicitly polled with `result` export function run (source) { if (running || ready) { console.log("Main: received bouncy `run` call"); return "TODO: handle this? should not be running" } // start the vm // wrapping the Run message in an array for the worker worker.postMessage([{verb: "Run", data: source}]) // update state for this run code = source running = true // reset the rest of my state outbox = [] ludus_console = "" ludus_commands = [] ludus_result = null ready = false keys_down = new Set(); // start the polling loop start_io_polling() } export function is_starting_up() { return running && !ready } // tells if the ludus script is still running export function is_running() { return running && ready } // kills a ludus script export function kill () { running = false outbox.push({verb: "Kill"}) console.log("Main: Killed Ludus") } // sends text into ludus (status: not working) export function input (text) { console.log("Main: calling `input` with ", text) outbox.push({verb: "Input", data: text}) } // returns the contents of the ludus console and resets the console export function flush_stdout () { let out = ludus_console ludus_console = "" return out } // returns the contents of the ludus console, retaining them export function stdout () { return ludus_console } // returns the array of turtle commands export function commands () { return ludus_commands } // returns the array of turtle commands and clears it export function flush_commands () { let out = ludus_commands ludus_commands = [] return out } // returns the ludus result // this is effectively Option: // null if no result has been returned, or // a string representation of the result export function result () { return ludus_result } export function flush_result () { let out = ludus_result ludus_result = null return out } export function key_down (key) { if (is_running()) keys_down.add(get_code(key)) } export function key_up (key) { if (is_running()) keys_down.delete(get_code(key)) } export {p5} from "./p5.js" export function svg (commands) { console.log("generating svg for ${code}") return svg_2(commands, code) }