#!/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