diff --git a/Tiltfile b/Tiltfile index b1733989a2..6e6181bf70 100644 --- a/Tiltfile +++ b/Tiltfile @@ -1,7 +1,7 @@ # phase.rs — local development orchestration # # Usage: -# tilt up core dev loop (wasm + frontend) +# tilt up core dev loop (wasm + frontend + lobby worker) # tilt up -- server also start the game server # tilt up -- test lint also start test runners and linters # tilt up -- server test lint full stack @@ -109,6 +109,27 @@ local_resource('frontend', labels = ['serve'], ) +# Deck-import + lobby broker Worker. vite.config.ts proxies /import-deck to +# :8787 unconditionally, so without this process the "Import from URL" deck +# flow fails with a Vite-generated 500: a connection refusal wearing the +# costume of a server bug, with nothing naming the missing service. It +# therefore starts with the core loop rather than sitting behind an opt-in +# group, because the feature it backs ships in the default frontend. +# +# No `deps`, for the same reason `frontend` above carries none: wrangler +# watches lobby-worker/src/ and reloads itself, so listing deps here would +# restart the process out from under its own hot reload. wrangler.toml's +# [build] runs scripts/build-broker-wasm.sh, which compiles +# lobby-worker/broker-wasm/ into that crate's own target dir, so it never takes +# the workspace cargo lock. +local_resource('lobby-worker', + serve_cmd = 'npm run dev', + serve_dir = 'lobby-worker', + allow_parallel = True, + links = ['http://localhost:8787'], + labels = ['serve'], +) + # HTTPS reverse proxy for LAN testing — required so WebRTC (PeerJS P2P # hosting) and crypto.randomUUID work for guest devices, which both refuse # to operate on insecure origins other than localhost. Bound to :443 via diff --git a/lobby-worker/.gitignore b/lobby-worker/.gitignore index ed63025b9e..c0a55ec0ef 100644 --- a/lobby-worker/.gitignore +++ b/lobby-worker/.gitignore @@ -4,6 +4,6 @@ dist/ .dev.vars # Generated wasm-bindgen glue (rebuilt by scripts/build-broker-wasm.sh) -src/broker-wasm-pkg/ +broker-wasm-pkg/ # Cargo build artifacts for the standalone wasm-bindgen crate broker-wasm/target/ diff --git a/lobby-worker/README.md b/lobby-worker/README.md index 425727bdf9..8cd1c37bab 100644 --- a/lobby-worker/README.md +++ b/lobby-worker/README.md @@ -3,7 +3,7 @@ The official phase.rs lobby broker, running as a single global Cloudflare Durable Object. The DO body (`src/lobby-do.ts`) is a thin imperative shell around the compiled Rust `lobby-broker` core (`broker-wasm` → -`src/broker-wasm-pkg`) — the SAME code `phase-server` runs natively, so the two +`broker-wasm-pkg`) — the SAME code `phase-server` runs natively, so the two deployments behave identically by construction. See `.planning/lobby-failover-federation-plan.md`. diff --git a/lobby-worker/src/lobby-do.ts b/lobby-worker/src/lobby-do.ts index 6a039ba2da..41f7fa01b4 100644 --- a/lobby-worker/src/lobby-do.ts +++ b/lobby-worker/src/lobby-do.ts @@ -17,8 +17,8 @@ // Mirrors the engine -> engine-wasm -> React-adapter pattern: the WASM owns the // logic, the host language is a serialization boundary with zero game logic. -import wasmModule from "./broker-wasm-pkg/broker_bg.wasm"; -import { initSync, protocol_version, WasmBroker } from "./broker-wasm-pkg/broker.js"; +import wasmModule from "../broker-wasm-pkg/broker_bg.wasm"; +import { initSync, protocol_version, WasmBroker } from "../broker-wasm-pkg/broker.js"; import { classifyHelloGate, helloGateErrorMessage, diff --git a/lobby-worker/wrangler.toml b/lobby-worker/wrangler.toml index f49cbca33f..432445a067 100644 --- a/lobby-worker/wrangler.toml +++ b/lobby-worker/wrangler.toml @@ -1,7 +1,7 @@ # Cloudflare Worker + Durable Object for the official phase.rs lobby broker. # # The DO body (src/lobby-do.ts) is a thin imperative shell around the compiled -# Rust `lobby-broker` core (lobby-worker/broker-wasm -> src/broker-wasm-pkg). +# Rust `lobby-broker` core (lobby-worker/broker-wasm -> broker-wasm-pkg). # All protocol parsing, dispatch, reservations, capacity caps, build-commit # gating, and the staleness reaper live in Rust — the SAME code phase-server # runs natively — so the two deployments behave identically by construction. @@ -12,10 +12,24 @@ compatibility_date = "2026-05-01" # Build the Rust broker to WASM (+ wasm-bindgen glue) before bundling. wrangler # runs this on `wrangler deploy` and `wrangler dev`; the generated glue under -# src/broker-wasm-pkg/ is gitignored and regenerated. Requires the wasm32 target, +# broker-wasm-pkg/ is gitignored and regenerated. Requires the wasm32 target, # wasm-bindgen-cli (0.2.121), and wasm-opt — the release workflow sets these up. +# +# Two rules keep `wrangler dev` from rebuilding itself in a loop. Both are +# load-bearing: +# +# 1. The glue lands in broker-wasm-pkg/ NEXT TO src/, never inside it. +# wrangler re-runs this command whenever watch_dir changes, so output +# written into a watched directory retriggers the watcher that produced +# it. That is an unbounded loop, not merely a slow start. Keep any future +# codegen out of the watched tree for the same reason. +# 2. watch_dir names the build's INPUT, the Rust crate this command compiles, +# rather than the default ./src. Worker TypeScript still hot-reloads: +# wrangler watches the bundle's module graph for that, independently. The +# only thing this suppresses is a pointless wasm rebuild on every .ts save. [build] command = "bash ../scripts/build-broker-wasm.sh release" +watch_dir = "broker-wasm/src" # Single global lobby = one DO instance addressed by a fixed name ("global"). [[durable_objects.bindings]] diff --git a/scripts/build-broker-wasm.sh b/scripts/build-broker-wasm.sh index 86bda5f88d..932acc6a94 100755 --- a/scripts/build-broker-wasm.sh +++ b/scripts/build-broker-wasm.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Build the Rust `lobby-broker` core to WASM for the Cloudflare Durable Object # shell, emitting wasm-bindgen "web"-target glue to -# lobby-worker/src/broker-wasm-pkg/. +# lobby-worker/broker-wasm-pkg/. # # Invoked by wrangler's [build] command (lobby-worker/wrangler.toml) on # `wrangler deploy` / `wrangler dev`, and directly in CI (release workflow). @@ -13,7 +13,7 @@ set -euo pipefail PROFILE="${1:-release}" ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" CRATE_DIR="$ROOT/lobby-worker/broker-wasm" -OUT_DIR="$ROOT/lobby-worker/src/broker-wasm-pkg" +OUT_DIR="$ROOT/lobby-worker/broker-wasm-pkg" # Build from the crate dir so its .cargo/config.toml — which sets # `getrandom_backend="wasm_js"` for wasm32 — is on cargo's config search path. diff --git a/scripts/setup.sh b/scripts/setup.sh index 16136e93de..b649bf01fd 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -149,6 +149,18 @@ echo "Step 2: Installing frontend dependencies..." (cd client && pnpm install) & PID_PNPM=$! +# The lobby worker is a separate npm project (its own package-lock.json), and +# Tilt's 'lobby-worker' resource runs `npm run dev` from it. Without this the +# resource comes up red on a fresh clone and deck URL import stays broken. +# Guarded on presence for the same reason release.yml guards its deploy job: +# commits older than the Worker have no lobby-worker/, and setup must not hard +# fail there. +PID_WORKER="" +if [ -d lobby-worker ]; then + (cd lobby-worker && npm install) & + PID_WORKER=$! +fi + # --- Card-data + WASM --- if [ "$USE_TILT" = 1 ]; then echo "" @@ -166,6 +178,9 @@ else fi wait $PID_PNPM || FAIL=1 +if [ -n "$PID_WORKER" ]; then + wait $PID_WORKER || FAIL=1 +fi if [ $FAIL -ne 0 ]; then echo "ERROR: setup step failed (see logs above)." >&2 exit 1