Files

117 lines
4.0 KiB
JavaScript

"use strict";
(function () {
const build = document.documentElement.dataset.build || "";
const canvas = document.getElementById("game");
const loader = document.getElementById("loader");
const bar = document.getElementById("bar");
const status = document.getElementById("status");
const errorBox = document.getElementById("error");
const full = document.getElementById("fullscreen");
const PARALLEL = 6;
const started = performance.now();
let total = 0;
let got = 0;
function mb(n) {
return (n / 1048576).toFixed(1) + " MB";
}
function fail(msg) {
loader.classList.remove("done");
loader.classList.add("failed");
status.textContent = "The game cannot start";
errorBox.textContent = String(msg);
errorBox.hidden = false;
if (!loader.isConnected) document.body.appendChild(loader);
}
globalThis.aegoFail = fail;
globalThis.aegoStarted = function () {
window.aegoBoot = Math.round(performance.now() - started);
loader.classList.add("done");
setTimeout(() => {
if (errorBox.hidden) loader.remove();
}, 300);
full.hidden = !canvas.requestFullscreen;
canvas.focus();
};
full.addEventListener("click", () => {
const done = canvas.requestFullscreen();
if (done && done.catch) done.catch(() => {});
canvas.focus();
});
document.addEventListener("fullscreenchange", () => {
full.hidden = !!document.fullscreenElement;
});
function progress(n) {
got += n;
if (!total) return;
bar.style.width = Math.min(100, (got * 100) / total) + "%";
status.textContent = `${mb(got)} of ${mb(total)}`;
}
async function read(url) {
const resp = await fetch(url);
if (!resp.ok) throw new Error(`${url}: HTTP ${resp.status}`);
if (!resp.body) {
const all = new Uint8Array(await resp.arrayBuffer());
progress(all.byteLength);
return all;
}
const reader = resp.body.getReader();
const parts = [];
let size = 0;
for (;;) {
const { done, value } = await reader.read();
if (done) break;
parts.push(value);
size += value.byteLength;
progress(value.byteLength);
}
const out = new Uint8Array(size);
let at = 0;
for (const p of parts) {
out.set(p, at);
at += p.byteLength;
}
return out;
}
async function each(items, fn) {
let next = 0;
const worker = async () => {
while (next < items.length) await fn(items[next++]);
};
await Promise.all(Array.from({ length: Math.min(PARALLEL, items.length) }, worker));
}
async function start() {
const resp = await fetch(`data/files.json?v=${build}`);
if (!resp.ok) throw new Error(`data/files.json: HTTP ${resp.status}`);
const list = await resp.json();
total = list.wasm + list.manifest.size + list.files.reduce((s, f) => s + f.size, 0);
const wasm = read(`game.wasm?v=${build}`);
const manifest = read(`data/${list.manifest.path}?v=${build}`);
const files = {};
await each(list.files, async (f) => {
files[f.path] = await read(`data/${f.path}`);
});
globalThis.aegoData = { manifest: await manifest, files };
const bytes = await wasm;
status.textContent = "Starting…";
bar.style.width = "";
bar.classList.add("indeterminate");
const go = new Go();
const { instance } = await WebAssembly.instantiate(bytes, go.importObject);
go.run(instance).then(() => {
if (errorBox.hidden && !loader.classList.contains("done")) fail("the game exited during start-up");
});
}
if (typeof WebAssembly !== "object") fail("this browser has no WebAssembly");
else start().catch((e) => fail(e && e.message ? e.message : e));
})();