'use strict'; // OpenRA3 WebAssembly worker bootstrap. // // The whole Emscripten runtime runs here (not on the page main thread), so the // runtime's filesystem is local to this worker: `FS.createLazyFile` can use // synchronous XHR to fetch assets on demand without blocking the page. // // Protocol with index.html: // page -> worker { canvas, width, height } (once, transfers the OffscreenCanvas) // page -> worker { type: 'key' | 'text' | 'mousebutton' | 'mousemove' | 'wheel' | 'resize' | 'quit', ... } // worker -> page { log } | { ready } | { openra3Frames } // // The Emscripten runtime is started by importScripts('openra3.js') once the // canvas has arrived; main() then runs here and yields via Asyncify. // Forward worker warnings/errors (including WebGPU validation messages from the // runtime) to the page, which logs them to the main console. (function () { var origError = console.error.bind(console); var origWarn = console.warn.bind(console); console.error = function () { origError.apply(null, arguments); try { self.postMessage({ log: '[worker error] ' + Array.prototype.join.call(arguments, ' ') }); } catch (e) {} }; console.warn = function () { origWarn.apply(null, arguments); try { self.postMessage({ log: '[worker warn] ' + Array.prototype.join.call(arguments, ' ') }); } catch (e) {} }; })(); self.Module = { print: function (text) { self.postMessage({ log: String(text) }); }, printErr: function (text) { self.postMessage({ log: String(text) }); }, onRuntimeInitialized: function () { self.postMessage({ ready: true }); }, }; var openra3Started = false; self.onmessage = function (event) { var data = event.data; if (!data) return; if (!openra3Started) { if (!data.canvas) return; openra3Started = true; var canvas = data.canvas; if (data.width && data.height) { canvas.width = data.width; canvas.height = data.height; } self.Module.canvas = canvas; var start = function () { // `?nocache` on the page forces a fresh module + wasm fetch, so a // rebuild is picked up without clearing the browser cache. var bust = data.bust ? ('?v=' + data.bust) : ''; if (bust) self.Module.locateFile = function (path) { return path + bust; }; try { importScripts('openra3.js' + bust); } catch (error) { self.postMessage({ log: 'OpenRA3 worker failed to start: ' + error }); } }; // The WebGPU backend starts from a device created here (`Module. // preinitializedWebGPUDevice`); the WebGL backend ignores it. If WebGPU // is unavailable the runtime falls back to WebGL. if (typeof navigator !== 'undefined' && navigator.gpu) { navigator.gpu.requestAdapter().then(function (adapter) { if (!adapter) { start(); return; } adapter.requestDevice().then(function (device) { self.Module.preinitializedWebGPUDevice = device; start(); }).catch(start); }).catch(start); } else { start(); } return; } // Input is only meaningful once the runtime has initialized its exports. switch (data.type) { case 'key': if (self.Module.ccall) self.Module.ccall('openra3_key', null, ['string', 'number'], [data.code, data.down ? 1 : 0]); break; case 'text': if (self.Module._openra3_text) self.Module._openra3_text(data.char | 0); break; case 'mousebutton': if (self.Module._openra3_mouse_button) self.Module._openra3_mouse_button(data.x, data.y, data.left ? 1 : 0); break; case 'mousemove': if (self.Module._openra3_mouse_move) self.Module._openra3_mouse_move(data.x, data.y, data.dx, data.dy, data.left ? 1 : 0); break; case 'wheel': if (self.Module._openra3_wheel) self.Module._openra3_wheel(data.deltaY); break; case 'resize': if (self.Module._openra3_resize) self.Module._openra3_resize(data.width | 0, data.height | 0); break; case 'quit': if (self.Module._openra3_quit) self.Module._openra3_quit(); break; default: break; } };