UNPKG

@soapbox.pub/wasmboy

Version:

Soapbox fork of Wasmboy.

234 lines (191 loc) 6.29 kB
(function () { 'use strict'; function getEventData(event) { if (event.data) { return event.data; } return event; } const isInBrowser = typeof self !== 'undefined'; // Function to read a base64 string as a buffer // Isomorphic worker api to be imported by web workers let parentPort; if (!isInBrowser) { parentPort = require('worker_threads').parentPort; } // https://nodejs.org/api/worker_threads.html#worker_threads_worker_postmessage_value_transferlist // https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage function postMessage(message, transferArray) { // Can't bind parentPort.postMessage, so we need to kinda copy code here :p if (isInBrowser) { self.postMessage(message, transferArray); } else { parentPort.postMessage(message, transferArray); } } // https://nodejs.org/api/worker_threads.html#worker_threads_worker_parentport // https://developer.mozilla.org/en-US/docs/Web/API/Worker/onmessage function onMessage(callback, port) { if (!callback) { console.error('workerapi: No callback was provided to onMessage!'); } // If we passed a port, use that if (port) { if (isInBrowser) { // We are in the browser port.onmessage = callback; } else { // We are in Node port.on('message', callback); } return; } if (isInBrowser) { // We are in the browser self.onmessage = callback; } else { // We are in Node parentPort.on('message', callback); } } // Smarter workers. let idCounter = 0; const generateId = () => { const randomId = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(2, 10); idCounter++; const id = `${randomId}-${idCounter}`; if (idCounter > 100000) { idCounter = 0; } return id; }; function getSmartWorkerMessage(message, messageId, workerId) { if (!messageId) { messageId = generateId(); } return { workerId, messageId, message }; } const WORKER_MESSAGE_TYPE = { CONNECT: 'CONNECT', INSTANTIATE_WASM: 'INSTANTIATE_WASM', CLEAR_MEMORY: 'CLEAR_MEMORY', CLEAR_MEMORY_DONE: 'CLEAR_MEMORY_DONE', GET_MEMORY: 'GET_MEMORY', SET_MEMORY: 'SET_MEMORY', SET_MEMORY_DONE: 'SET_MEMORY_DONE', GET_CONSTANTS: 'GET_CONSTANTS', GET_CONSTANTS_DONE: 'GET_CONSTANTS_DONE', CONFIG: 'CONFIG', RESET_AUDIO_QUEUE: 'RESET_AUDIO_QUEUE', PLAY: 'PLAY', BREAKPOINT: 'BREAKPOINT', PAUSE: 'PAUSE', UPDATED: 'UPDATED', CRASHED: 'CRASHED', SET_JOYPAD_STATE: 'SET_JOYPAD_STATE', AUDIO_LATENCY: 'AUDIO_LATENCY', RUN_WASM_EXPORT: 'RUN_WASM_EXPORT', GET_WASM_MEMORY_SECTION: 'GET_WASM_MEMORY_SECTION', GET_WASM_CONSTANT: 'GET_WASM_CONSTANT', FORCE_OUTPUT_FRAME: 'FORCE_OUTPUT_FRAME', SET_SPEED: 'SET_SPEED', IS_GBC: 'IS_GBC' }; // Web worker for wasmboy lib let libWorkerPort; // Function to pass memory to parent thread as transferable const passMemoryToConnectedThread = (isForLib, eventData) => { const transferableMemory = []; Object.keys(eventData.message).forEach(key => { if (key !== 'type') { transferableMemory.push(eventData.message[key]); } }); // Forward to our lib worker const workerMessage = getSmartWorkerMessage(eventData.message, eventData.messageId); if (isForLib) { libWorkerPort.postMessage(workerMessage, transferableMemory); } else { postMessage(workerMessage, transferableMemory); } }; const libMessageHandler = event => { // Handle our messages from the lib thread const eventData = getEventData(event); // Handle update method transfrables if (!eventData.message) { return; } // Handle our messages from the lib thread switch (eventData.message.type) { case WORKER_MESSAGE_TYPE.CLEAR_MEMORY_DONE: { postMessage(getSmartWorkerMessage(eventData.message, eventData.messageId), [eventData.message.wasmByteMemory]); return; } case WORKER_MESSAGE_TYPE.GET_CONSTANTS_DONE: { postMessage(getSmartWorkerMessage(eventData.message, eventData.messageId)); return; } case WORKER_MESSAGE_TYPE.SET_MEMORY_DONE: { postMessage(getSmartWorkerMessage(eventData.message, eventData.messageId)); return; } case WORKER_MESSAGE_TYPE.GET_MEMORY: { passMemoryToConnectedThread(false, eventData); return; } case WORKER_MESSAGE_TYPE.UPDATED: { passMemoryToConnectedThread(false, eventData); return; } } }; const messageHandler = event => { // Handle our messages from the main thread const eventData = getEventData(event); switch (eventData.message.type) { case WORKER_MESSAGE_TYPE.CONNECT: { // Set our lib port libWorkerPort = eventData.message.ports[0]; onMessage(libMessageHandler, libWorkerPort); // Simply post back that we are ready postMessage(getSmartWorkerMessage(undefined, eventData.messageId)); return; } case WORKER_MESSAGE_TYPE.CLEAR_MEMORY: { // Forward to our lib worker libWorkerPort.postMessage(getSmartWorkerMessage({ type: WORKER_MESSAGE_TYPE.CLEAR_MEMORY }, eventData.messageId)); return; } case WORKER_MESSAGE_TYPE.GET_CONSTANTS: { // Forward to our lib worker libWorkerPort.postMessage(getSmartWorkerMessage({ type: WORKER_MESSAGE_TYPE.GET_CONSTANTS }, eventData.messageId)); return; } case WORKER_MESSAGE_TYPE.GET_MEMORY: { // Forward to our lib worker libWorkerPort.postMessage(getSmartWorkerMessage(eventData.message, eventData.messageId)); return; } case WORKER_MESSAGE_TYPE.SET_MEMORY: { passMemoryToConnectedThread(true, eventData); return; } default: { //handle other messages from main console.log(eventData); } } }; onMessage(messageHandler); }());