run.sh:
- phase() wrapper logs elapsed seconds per build step.
- Tracks total build+startup at the end.
- Output is "==> phase / [Ns] phase" so the slow steps are obvious.
test/run.py:
- Per-step time.perf_counter() around each scenario step.
- "slowest steps" summary printed at the end so the worst
offenders are immediately visible.
- Total wall-clock time at scenario end.
src/render/mod.rs:
- browser_now() helper: web_sys::performance().now() on wasm,
Instant-based on native. Monotonic ms timestamps for tick/toc.
- Renderer::rebuild_chunk wraps build_chunk_mesh in a t0/t1
measurement and logs anything over 5ms with vertex/index counts.
Surfaces sky_visibility cost in the browser console.
web/main.js:
- Exposes window.voxel_game = wasm after init so the test
harness can drive scenarios declaratively (set_scene_time,
teleport, look_at, get_position, etc.).
src/shader.wgsl:
- Fix duplicate `let to_eye` declaration introduced in Round D
(specular's normalized to_eye conflicted with fog's raw version).
Renamed fog's local to_eye_raw. The test harness caught this
immediately — first WGSL compile error, first scenario run.
Findings from running scenarios/lighting-times-of-day.yaml:
- 289 chunks × ~100ms avg = ~29s mesh-build on main thread.
- Page-ready latency dominated by this. window.voxel_game appears
almost immediately (init resolves before chunks build), but
the world is invisible until meshes are uploaded.
- sky_visibility (8 cosine rays × HashMap voxel lookups) is the
hot path inside build_chunk_mesh.
Next: make chunk-mesh build progressive (one or two chunks per tick
instead of all up-front), so the world becomes visible immediately
and pops in over a few seconds.
114 lines
3.6 KiB
Bash
Executable file
114 lines
3.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Build the wasm bundle, build + run the multiplayer server, and open a public
|
|
# tunnel via localhost.run (free, ssh-based, no signup).
|
|
#
|
|
# Usage:
|
|
# ./run.sh # build everything then run server + tunnel
|
|
# ./run.sh --no-tunnel # local only (http://localhost:8080)
|
|
# ./run.sh --no-build # skip rebuild; just run
|
|
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")"
|
|
|
|
DO_BUILD=1
|
|
DO_TUNNEL=1
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--no-build) DO_BUILD=0 ;;
|
|
--no-tunnel) DO_TUNNEL=0 ;;
|
|
*) echo "unknown arg: $arg" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
# tick/toc — measure each build phase so we know where time goes.
|
|
# Bash arithmetic on $SECONDS gives whole-second resolution; cheap and
|
|
# portable. For sub-second jobs use `time` directly.
|
|
phase() {
|
|
local label="$1"; shift
|
|
local start=$SECONDS
|
|
echo "==> $label"
|
|
"$@"
|
|
local elapsed=$((SECONDS - start))
|
|
echo " [${elapsed}s] $label"
|
|
}
|
|
|
|
TOTAL_START=$SECONDS
|
|
|
|
if [[ $DO_BUILD -eq 1 ]]; then
|
|
phase "running unit tests (proves spawn / collision / mesh invariants)" \
|
|
cargo test --lib
|
|
|
|
phase "building wasm client (release)" \
|
|
cargo build --target wasm32-unknown-unknown --release --lib
|
|
|
|
phase "wasm-bindgen → web/" \
|
|
~/.cargo/bin/wasm-bindgen --target web --out-dir web --no-typescript \
|
|
target/wasm32-unknown-unknown/release/voxel_game.wasm
|
|
|
|
phase "building server (release)" \
|
|
bash -c "cd server && cargo build --release"
|
|
|
|
# Catch JS no-undef / parse errors before they hit the browser. eslint
|
|
# is optional: warns if absent so this step never blocks an emergency
|
|
# deploy, but normally you should `npm i -g eslint` to get it.
|
|
if command -v npx >/dev/null 2>&1; then
|
|
if [[ -f web/.eslintrc.json ]]; then
|
|
echo "==> linting web/main.js"
|
|
(cd web && npx --no-install eslint main.js) \
|
|
|| echo " (eslint reported issues; not fatal)"
|
|
fi
|
|
else
|
|
echo " (skipping JS lint: npx not installed)"
|
|
fi
|
|
fi
|
|
|
|
SERVER_BIN="./target/release/voxel-server"
|
|
if [[ ! -x "$SERVER_BIN" ]]; then
|
|
# workspace fallback: server has its own target
|
|
SERVER_BIN="./server/target/release/voxel-server"
|
|
fi
|
|
|
|
# Kill any previous instance.
|
|
pkill -f 'voxel-server' 2>/dev/null || true
|
|
pkill -f 'http.server --directory web' 2>/dev/null || true
|
|
sleep 0.3
|
|
|
|
echo "==> starting server on :8080"
|
|
STATIC_DIR="$(pwd)/web" "$SERVER_BIN" &
|
|
SERVER_PID=$!
|
|
trap 'echo; echo "stopping..."; kill $SERVER_PID 2>/dev/null || true; kill $TUNNEL_PID 2>/dev/null || true; exit' INT TERM
|
|
|
|
# Wait for server to come up.
|
|
for i in {1..20}; do
|
|
if curl -sf -o /dev/null http://localhost:8080/; then break; fi
|
|
sleep 0.2
|
|
done
|
|
|
|
echo "==> total build+startup: $((SECONDS - TOTAL_START))s"
|
|
|
|
if [[ $DO_TUNNEL -eq 0 ]]; then
|
|
echo "==> server running at http://localhost:8080 (no tunnel)"
|
|
wait $SERVER_PID
|
|
exit
|
|
fi
|
|
|
|
echo "==> opening Cloudflare quick tunnel (free, no signup)"
|
|
echo " look for the trycloudflare.com URL below; share that link."
|
|
echo
|
|
CLOUDFLARED="${CLOUDFLARED:-$HOME/.local/bin/cloudflared}"
|
|
if [[ ! -x "$CLOUDFLARED" ]] && command -v cloudflared >/dev/null 2>&1; then
|
|
CLOUDFLARED="$(command -v cloudflared)"
|
|
fi
|
|
if [[ ! -x "$CLOUDFLARED" ]]; then
|
|
echo "cloudflared not found. Install it with:"
|
|
echo " mkdir -p ~/.local/bin && curl -sL -o ~/.local/bin/cloudflared \\"
|
|
echo " https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 \\"
|
|
echo " && chmod +x ~/.local/bin/cloudflared"
|
|
kill $SERVER_PID 2>/dev/null || true
|
|
exit 1
|
|
fi
|
|
"$CLOUDFLARED" tunnel --url http://localhost:8080 --no-autoupdate &
|
|
TUNNEL_PID=$!
|
|
|
|
wait $SERVER_PID
|
|
kill $TUNNEL_PID 2>/dev/null || true
|