Spaces:
Running
Running
Download play.html from RSIGame/rsigame-page: direct link, hf CLI and curl.
- Browser
- Download file 5.16 kB
-
https://huggingface.co/spaces/RSIGame/rsigame-page/resolve/e39b7b33a153bfdea68739ffd6d18ed1f1cbd7be/play.html
- Command line
-
hf download hf://spaces/RSIGame/rsigame-page@e39b7b33a153bfdea68739ffd6d18ed1f1cbd7be/play.html
-
curl -L -o play.html https://huggingface.co/spaces/RSIGame/rsigame-page/resolve/e39b7b33a153bfdea68739ffd6d18ed1f1cbd7be/play.html
5.16 kB
| <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> | |