diff --git a/README.md b/README.md index f6d8d64..f57d75a 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,28 @@ python3 build.py python3 tools/serve.py # builds, serves at http://localhost:8000 and rebuilds when src/ changes ``` -Opening `dist/index.html` directly works too. The game is one self-contained page with no -downloads; sound and graphics are all generated in code. Saves stay in your browser. +The game must be served: use `python3 tools/serve.py` locally, or serve `dist/` with any web +server that sends these headers. Opening `dist/index.html` from disk is no longer supported. +Use HTTPS when hosting remotely (localhost works over HTTP). + +nginx, in the site's `server` or `location` block: + +```nginx +add_header Cross-Origin-Opener-Policy same-origin always; +add_header Cross-Origin-Embedder-Policy require-corp always; +``` + +Caddy, in the site block alongside `root * /path/to/dist` and `file_server`: + +```caddyfile +header { + Cross-Origin-Opener-Policy same-origin + Cross-Origin-Embedder-Policy require-corp +} +``` + +These are plain static files, so a small server is enough. The game is one self-contained page +with no downloads; sound and graphics are generated in code. Saves stay in your browser. - To keep or move a world, use Pause → Export world file. - To load one, use Settings → Load a world file on the title screen, or Pause → Import world file during a game. World files exported from the claude.ai version load here too. @@ -36,11 +56,11 @@ You need Node (tested with 22), Python 3, and Playwright for the browser tests a ```sh uv venv .venv uv pip install --python .venv/bin/python -r requirements.txt -.venv/bin/python -m playwright install chromium +.venv/bin/python -m playwright install chromium firefox ``` If uv is not installed, `python3 -m venv .venv` and -`.venv/bin/pip install -r requirements.txt` also work; then install Chromium as above. +`.venv/bin/pip install -r requirements.txt` also work; then install the browsers as above. After that, plain `python3` works for every command below; the browser scripts switch to `.venv` on their own. diff --git a/build.py b/build.py index 76ecbf1..8159aa1 100644 --- a/build.py +++ b/build.py @@ -6,7 +6,7 @@ code) into one classic script, so they share one global scope and 'use strict' a src/sim/base.js applies to all of it. No bundler, no dependencies. Writes: - dist/index.html the full page: open it, or serve with python3 tools/serve.py + dist/index.html the full page: serve with python3 tools/serve.py dist/deepgrain-foundry.html the same without /
/, for hosts (like a claude.ai artifact) that wrap the page themselves dist/bundle-lines.json where each source file starts in dist/index.html, so a line number diff --git a/docs/PROJECT_MAP.md b/docs/PROJECT_MAP.md index 24dafaa..e215161 100644 --- a/docs/PROJECT_MAP.md +++ b/docs/PROJECT_MAP.md @@ -19,8 +19,9 @@ src/render/ drawing: the pixel look (CPU) and the smooth look (WebGL2) src/ui/ the browser shell: input, HUD, build menu, notebook, settings, saves, audio, the frame loop test/ headless suites (t_*.js) and their harness; browser tests (play.py, - t_smooth.py, t_dev_console.py); run_all.py + t_smooth.py, t_dev_console.py, t_caps.py, firefox_smoke.py); run_all.py tools/ serve.py (build, serve, rebuild on change), server.py (isolated HTTP), shot.py (screenshots), + browser.py (shared launch/GPU choice), caps.py (capability report), where.py, map.py, perf.py, playthrough.py, use_venv.py (switches browser scripts to .venv), scenes/ (screenshot setups), experiments/ (measurements and reports) @@ -441,10 +442,10 @@ Game shell (src/ui/): browser only. This file: settings, the GAME state object, - functions: loadSettings, saveSettings, el, esc, rgbOf, fmtT, toast, setStatus - variables: SET_DEFAULT, SET, TOOLS, ICONS, BAG_ORDER, GAME, keys, hudDirty, $ -### src/ui/title.js (46 lines) -The title screen art. -- functions: buildTitleCells, drawTitle -- variables: FONT5, TITLE +### src/ui/title.js (74 lines) +Startup capabilities and title art. +- functions: checkCapabilities, renderCapabilityNotice, canStartGame, buildTitleCells, drawTitle +- variables: REQUIRE_GPU_AND_THREADS, CAPS, CAPS_CHECK, FONT5, TITLE ### src/ui/saves.js (224 lines) Saves in IndexedDB, world files (export / import) and the blueprint library. @@ -456,7 +457,7 @@ Shared player state; the world save remains a complete standalone fallback. - functions: careerState, validCareer, applyCareerState, loadCareer, claimName, openNewClaim, travel, renderClaimsTab - variables: career, claimBusy -### src/ui/session.js (158 lines) +### src/ui/session.js (160 lines) Starting, loading and quitting a game; modal dialogs; the pause menu. - functions: showLoading, enterGame, setupNewWorld, startNewWorld, startFromSave, loadSavedWorld, quitToTitle, refreshContinue, anyModal, openModal, closeModal, closeAllModals, topModal, dialog, openPause, renderWorlds @@ -564,6 +565,7 @@ The frame loop and boot. ## Tests and tools +- `test/firefox_smoke.py`: An isolated Firefox title screen and three seconds of a new pixel world. - `test/furnace_play.js`: Fires a starter furnace design repeatedly the way a player would, without cleaning it out between firings, to see whether it clogs. Usage: node test/furnace_play.js [design] [ore] [coal] [firings] [roll] [mode] mode: 'mix' (even ore/coal mix blown in), 'layers' (ore first, coal on top), 'vent' (blown in from above through the vent) - `test/harness.js`: Loads the simulation (the src/sim/ files listed in src/manifest.json, joined in order into one script, the same way build.py joins them) so scenarios can drive it headlessly in Node. Errors point at the real file and line (sim/pipes.js:42), not at the joined script. - `test/play.py`: Play-test in a real browser: mouse, keyboard and a mocked gamepad drive the game; save and continue. Screenshots go to shots/play/ (or the folder given as the first argument). Pixel look unless the second argument is 'smooth'. @@ -571,6 +573,7 @@ The frame loop and boot. - `test/t_bar_stacking.js`: Resting bars snap into pairs of grains and tip off narrow supports. - `test/t_basics.js`: Basics: drilling to rubble, the vacuum and blowing, walls, the ingot mold and bars, belts, blueprints, save/load. - `test/t_build.js`: Building: undo (Ctrl+Z), cut, replace a machine with Ctrl, pasting ghosts, and the fan heat warning. +- `test/t_caps.py`: Capability detection, title warnings and the future startup gate. - `test/t_conveyors.js`: Screw conveyors that never carry liquid (it flows through), screw and belt throughput, filters. - `test/t_corner_seal.js`: Corner-touching square walls seal loose grains without changing free piles or worldgen. - `test/t_cursor.js`: Ore identification, cached connected veins, ping timing, settings and label lifetime. @@ -601,6 +604,8 @@ The frame loop and boot. - `test/t_water.js`: Water conservation, settling, ledge pouring, boundaries, pipes and phase changes. Tall worlds keep geothermal heat out of the ponds. - `test/t_weather.js`: Weather: no more springs; steam rises into clouds and rains back; showers; snow when it is freezing. - `test/t_world.js`: Trees from acorns, charcoal, brick, density, water/lava/snow/ice, full-bag refunds, drill reach, ballistic blowing, snowy mountains. +- `tools/browser.py`: Shared isolated HTTP sessions; callers attach hooks before navigating to url. +- `tools/caps.py`: Print isolation, WebGPU adapter info and GL renderer for installed browsers. - `tools/map.py`: Regenerate the file and symbol index at the end of docs/PROJECT_MAP.md. - `tools/perf-measure.js`: Temporary wrappers live only in the profiling page, never in the simulation bundle. - `tools/perf-scenes.js`: Seeded worlds, no storage round-trip (saves quantize heat and land flying grains). diff --git a/docs/TESTING.md b/docs/TESTING.md index ad732c2..9421640 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -71,8 +71,30 @@ diff -r /tmp/before /tmp/after to pixels cleanly, and that the thermal lens works. - Run them one at a time. Two headless browsers at once can starve each other, and `t_smooth.py`'s half-resolution check then fails. `run_all.py` runs them one after the other. -- Rebuild first (`python3 build.py`). They load `dist/index.html`. `run_all.py --browser` - rebuilds for you. +- Rebuild first (`python3 build.py`); `run_all.py --browser` rebuilds for you. All browser tools + use `tools/browser.py` and `tools/server.py`: a free-port background HTTP server serves the + built page with COOP/COEP headers and shuts down after the browser. No `file://` loads. +- Choose Chromium graphics with `--gpu real|soft` or `DEEPGRAIN_GPU=real|soft` (the command line + wins). This works for the suite runner, individual browser tests, screenshots and profiler. + Real mode uses ANGLE Vulkan on the host GPU; soft uses SwiftShader and sets + `window.__allowSoftwareGL` before load. The explicit software-fallback test still uses soft + mode with that override off. Firefox uses its own default graphics backend. + The default is **real**, including the profiler and `caps.py`. No colour tolerances were + changed. The play test holds the slow-save fixture open during its screenshot, waits for + tool/selection UI updates, disables camera lead after testing it, and gives the gamepad drill + a known reachable target; these checks do not depend on GPU speed or earlier digging. +- `python3 tools/caps.py` prints isolation, actual WebGPU adapter info and the GL renderer for + Chromium and installed Firefox. Real mode requires a hardware Chromium adapter. +- `test/t_caps.py` checks missing, null and rejected WebGPU adapters, both title notices, warning + mode and the future `REQUIRE_GPU_AND_THREADS` startup gate. Its forced notice screenshot is + `shots/t88-p1-notice.png`. +- Install Firefox with `.venv/bin/python -m playwright install firefox`. + `python3 test/firefox_smoke.py` checks isolation and the WebGPU notice, starts a pixel world, + runs three seconds and checks page errors. `run_all.py --browser` includes it and prints a + skip note if Firefox is absent; an installed browser that fails to launch is a failure. +- Linux WebGPU currently needs Firefox Nightly; the notice points to `about:config` → + `dom.webgpu.enabled`. Sources: [Mozilla's experimental features](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Experimental_features#webgpu_api) + and [GPUWeb implementation status](https://github.com/gpuweb/gpuweb/wiki/Implementation-Status#firefox). ## Screenshots: tools/shot.py @@ -106,10 +128,9 @@ python3 tools/shot.py shots/silo.png --setup tools/scenes/clear-sky.js \ ## Pitfalls -- **WebGL in headless Chromium.** The browser tests use SwiftShader for portable graphics checks: - launch with `--use-gl=angle --use-angle=swiftshader --enable-unsafe-swiftshader`. Also set - `window.__allowSoftwareGL = true` before the page loads (`add_init_script`); otherwise the game - sees software rendering and switches to the pixel look. `tools/shot.py` does both. +- **GPU access in headless Chromium.** Real mode needs access to GPU devices and driver caches; + a filesystem sandbox can silently force software rendering. Use `tools/caps.py` to check. + Soft mode is available for machines without a GPU. Both modes load isolated HTTP pages. - **Top-level `const` and `let` are not `window` properties.** In the page, `window.SM` is `undefined` even though `SM` exists. Write `typeof SM !== 'undefined'`, or use the bare name. Functions declared with `function` are on `window`. @@ -176,12 +197,12 @@ python3 tools/shot.py shots/silo.png --setup tools/scenes/clear-sky.js \ tiny timings and tails can be noisy, and a zero baseline has no meaningful percent change. Performance runs launch Chromium with - `--use-angle=vulkan --enable-features=Vulkan --enable-gpu --ignore-gpu-blocklist --enable-gpu-rasterization`. + `--use-angle=vulkan --enable-features=Vulkan,VulkanFromANGLE --enable-gpu --ignore-gpu-blocklist --enable-gpu-rasterization --enable-unsafe-webgpu`. They record the unmasked GPU/renderer, cores, browser version, viewport/world size, - `crossOriginIsolated`, `navigator.gpu`, source hash and elapsed time. They refuse software - renderers or missing isolation. Chromium needs access to the host GPU devices; a filesystem + `crossOriginIsolated`, `navigator.gpu`, source hash and elapsed time. Real mode refuses software + renderers; both modes require isolation. Chromium needs access to the host GPU devices; a filesystem sandbox that hides `/dev/dri`/NVIDIA devices must be lifted for this measurement. The browser - tests' SwiftShader numbers are not a performance baseline. + tests' SwiftShader numbers (and explicit `--gpu soft` profiler runs) are not a hardware performance baseline. The existing in-game Performance stats toggle shows smoothed CPU world/light/overlay times and movement/heat/rest per simulation tick. Detailed renderer clocks are off by default diff --git a/docs/agent-log.md b/docs/agent-log.md index ce704a3..d565f51 100644 --- a/docs/agent-log.md +++ b/docs/agent-log.md @@ -4,6 +4,19 @@ What agents have done, newest first. Add an entry for every piece of work: the d note or request it was for, what changed, the files touched, the tests run, and anything left open or not verified. Keep each entry short; the git history has the details. +## 2026-10-27: t88 phase 1, serving and capability checks (Claude Code + Codex) + +- The game must be served with COOP/COEP (README has nginx and Caddy lines; opening from disk is + dropped). The title screen checks `crossOriginIsolated` and a WebGPU adapter (`CAPS`, + `checkCapabilities` in `src/ui/title.js`) and shows a notice; warnings only until + `REQUIRE_GPU_AND_THREADS` is flipped in Phases 2-3. +- Browser tests and tools load through `tools/browser.py` + `tools/server.py`, on the real GPU by + default (`--gpu soft` / `DEEPGRAIN_GPU=soft` for SwiftShader). `tools/caps.py` prints isolation + and WebGPU per browser; `test/firefox_smoke.py` runs in `run_all.py --browser` when Firefox is + installed. Chromium 156: isolated, WebGPU on the RTX 5070. Playwright Firefox 157: isolated, no + WebGPU (Linux). +- Tests: `python3 test/run_all.py --browser` all passed on the real GPU. + ## 2026-10-27: t88 phase 0, profiler and baseline (Claude Code + Codex) - `tools/perf.py` rewritten: real GPU (ANGLE Vulkan flags), served by the new `tools/server.py` diff --git a/src/shell.html b/src/shell.html index bfeef6c..04bd213 100644 --- a/src/shell.html +++ b/src/shell.html @@ -48,6 +48,7 @@Dig. Smelt. Cast your own tools. Every grain is simulated, and nothing turns into anything unless the physics says so.
+