rsigame-page / play.html
WenyiWU0111's picture
统一命名为 RSIGame;标题与引用对齐论文
1a69b77 verified
Raw History Blame
5.16 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>RSIGame player</title>
<!--
One shared Godot 4.6.2 web engine for every exported game.
play.html?engine=<base url of godot.js/.wasm.gz>&pck=<game pck url>&pck_size=<bytes>&wasm_gz=<bytes>
The engine's .wasm is stored gzipped; fetch() for it is answered from the .gz and inflated here.
-->
<style>
html, body { margin: 0; height: 100%; background: #05060a; overflow: hidden; }
#canvas { display: block; width: 100%; height: 100%; outline: none; }
#boot { position: absolute; inset: 0; display: grid; place-items: center; color: #b4bccd; font: 500 13px/1.5 ui-monospace, "JetBrains Mono", Menlo, monospace; text-align: center; padding: 16px; background: #05060a center / cover no-repeat; }
#boot::before { content: ""; position: absolute; inset: 0; background: rgba(5, 6, 10, .6); backdrop-filter: blur(3px); }
#boot > div { position: relative; }
#boot h1 { margin: 0 0 6px; font: 700 clamp(22px, 4vw, 40px)/1.1 system-ui, sans-serif; color: #eef1f7; }
#boot[hidden] { display: none; }
#bar { width: min(320px, 70vw); height: 4px; margin: 10px auto 0; border-radius: 2px; background: #1c2130; overflow: hidden; }
#bar i { display: block; height: 100%; width: 0; background: #c6f432; transition: width .15s; }
</style>
</head>
<body>
<canvas id="canvas" tabindex="0">Your browser does not support the canvas element.</canvas>
<div id="boot"><div><h1 id="boot-title"></h1><div id="msg">Loading engine…</div><div id="bar"><i></i></div></div></div>
<script>
(() => {
const q = new URLSearchParams(location.search);
const engineBase = q.get("engine");
const pck = q.get("pck");
const pckSize = +q.get("pck_size") || 0;
const wasmGz = +q.get("wasm_gz") || 0;
const msg = document.getElementById("msg"), bar = document.querySelector("#bar i");
document.getElementById("boot-title").textContent = q.get("title") || "";
if (/^(https?:|\/|\.)/.test(q.get("poster") || "") || /^[\w/.-]+$/.test(q.get("poster") || ""))
document.getElementById("boot").style.backgroundImage = `url("${encodeURI(q.get("poster"))}")`;
const mb = (n) => (n / 1048576).toFixed(1);
const show = (text, frac) => { msg.textContent = text; if (frac != null) bar.style.width = `${Math.min(100, frac * 100)}%`; };
const fail = (e) => { console.error(e); show(`Could not start the game: ${e && e.message ? e.message : e}`); };
// Answer the engine's request for godot.wasm with the gzipped copy, inflated in the browser.
const realFetch = window.fetch.bind(window);
window.fetch = async (input, init) => {
const url = typeof input === "string" ? input : input.url;
if (!/\.wasm(\?|$)/.test(url)) return realFetch(input, init);
const r = await realFetch(url.replace(/\.wasm(\?|$)/, ".wasm.gz$1"), init);
if (!r.ok) return realFetch(input, init);
const reader = r.body.getReader(), chunks = [];
let got = 0;
for (;;) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
got += value.length;
if (wasmGz) show(`Loading engine ${mb(got)} / ${mb(wasmGz)} MB`, (got / wasmGz) * 0.5);
}
let blob = new Blob(chunks);
const head = new Uint8Array(await blob.slice(0, 2).arrayBuffer());
if (head[0] === 0x1f && head[1] === 0x8b) { // not already inflated by the server
show("Unpacking engine…", 0.5);
blob = await new Response(blob.stream().pipeThrough(new DecompressionStream("gzip"))).blob();
}
return new Response(blob, { headers: { "Content-Type": "application/wasm", "Content-Length": String(blob.size) } });
};
if (!engineBase || !pck) return fail("missing engine or pck");
const script = document.createElement("script");
script.src = `${engineBase}.js`;
script.onerror = () => fail("engine script did not load");
script.onload = () => {
const missing = Engine.getMissingFeatures({ threads: false });
if (missing.length) return fail(`this browser lacks ${missing.join(", ")}`);
const engine = new Engine({
executable: engineBase,
mainPack: pck,
args: [],
canvasResizePolicy: 2,
focusCanvas: true,
experimentalVK: false,
gdextensionLibs: [],
ensureCrossOriginIsolationHeaders: false,
fileSizes: { [pck]: pckSize },
});
engine.startGame({
onProgress: (cur, total) => {
if (total > 0) show(`Loading game ${mb(cur)} / ${mb(total)} MB`, 0.5 + (cur / total) * 0.5);
},
}).then(() => {
document.getElementById("boot").hidden = true;
document.getElementById("canvas").focus();
parent.postMessage({ evoPlay: "started" }, "*");
}, fail);
};
document.head.append(script);
addEventListener("pointerdown", () => document.getElementById("canvas").focus());
// the game fills the embedding page's first screen: let the mouse wheel keep scrolling that page
if (parent !== window) addEventListener("wheel", (e) => { try { parent.scrollBy(0, e.deltaY); } catch { /* cross-origin parent */ } }, { passive: true, capture: true });
})();
</script>
</body>
</html>