Prepare Cloudflare Workers/Pages deploy for the static front-end.
Add wrangler assets config, multi-page Vite build (game + studio), cache/WASM headers, and deploy scripts so `npm run deploy` publishes dist.
This commit is contained in:
@@ -2,3 +2,7 @@ node_modules/
|
|||||||
dist/
|
dist/
|
||||||
shots/
|
shots/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.wrangler/
|
||||||
|
.dev.vars
|
||||||
|
.dev.vars.*
|
||||||
|
!.dev.vars.example
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
# Deploying tilt
|
||||||
|
|
||||||
|
tilt is a **static Vite front-end** (Three.js + Box3D WASM). There is no API
|
||||||
|
server yet. Cloudflare **Workers Static Assets** is the primary target;
|
||||||
|
**Pages** is a one-liner alternative if you prefer Git/dashboard deploys.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. Cloudflare account
|
||||||
|
2. Auth once on this machine:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx wrangler login
|
||||||
|
# or: npm run cf:whoami
|
||||||
|
```
|
||||||
|
|
||||||
|
## Preferred: Workers (static assets)
|
||||||
|
|
||||||
|
Config lives in [`wrangler.jsonc`](wrangler.jsonc). Assets come from `dist/`
|
||||||
|
after a Vite production build.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run deploy:dry # validate upload without publishing
|
||||||
|
npm run deploy # build + wrangler deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
That publishes a Worker named **`tilt`** serving:
|
||||||
|
|
||||||
|
| Path | App |
|
||||||
|
|------|-----|
|
||||||
|
| `/` | Game (menu → 1v1 / 3v3) |
|
||||||
|
| `/character.html` | Gear / animation studio |
|
||||||
|
|
||||||
|
URL shape after first deploy: `https://tilt.<your-subdomain>.workers.dev`
|
||||||
|
|
||||||
|
### Custom domain
|
||||||
|
|
||||||
|
Dashboard → Workers & Pages → **tilt** → Settings → Domains, or:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx wrangler domains add tilt.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
(Exact CLI may vary by Wrangler version; dashboard is fine.)
|
||||||
|
|
||||||
|
## Alternative: Cloudflare Pages
|
||||||
|
|
||||||
|
Same build output, Pages project instead of a Worker:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run pages:deploy
|
||||||
|
# → wrangler pages deploy dist --project-name=tilt
|
||||||
|
```
|
||||||
|
|
||||||
|
Or connect the Git remote in the dashboard:
|
||||||
|
|
||||||
|
| Setting | Value |
|
||||||
|
|---------|--------|
|
||||||
|
| Production branch | `main` |
|
||||||
|
| Build command | `npm run build` |
|
||||||
|
| Build output directory | `dist` |
|
||||||
|
| Root directory | `/` (repo root) |
|
||||||
|
| Node version | 20+ |
|
||||||
|
|
||||||
|
`public/_headers` is copied into `dist/` by Vite and applies on both Workers
|
||||||
|
assets and Pages.
|
||||||
|
|
||||||
|
## Local production check
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
npm run preview # Vite static server on dist/
|
||||||
|
# or, after wrangler is installed:
|
||||||
|
npx wrangler dev # serves assets via Workers runtime locally
|
||||||
|
```
|
||||||
|
|
||||||
|
## What is *not* deployed
|
||||||
|
|
||||||
|
- `test/`, `tools/` (Node harnesses, Puppeteer captures)
|
||||||
|
- `shots/`, `node_modules/`, source maps (unless you opt in)
|
||||||
|
- Server/netcode — still pure client sim
|
||||||
|
|
||||||
|
When you add an API later, keep this assets config and introduce a Worker
|
||||||
|
`main` (or Pages Functions) beside it; SPA/static hosting stays the same.
|
||||||
|
|
||||||
|
## CI sketch
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# e.g. Gitea Actions / GitHub Actions
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm test
|
||||||
|
- run: npm run build
|
||||||
|
- run: npx wrangler deploy
|
||||||
|
env:
|
||||||
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
```
|
||||||
|
|
||||||
|
Create an API token with **Workers Scripts:Edit** (and **Account:Read**).
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Symptom | Fix |
|
||||||
|
|---------|-----|
|
||||||
|
| `wrangler login` / auth errors | `npx wrangler login` or set `CLOUDFLARE_API_TOKEN` |
|
||||||
|
| Blank page, missing WASM | Confirm `dist/assets/*.wasm` exists after build; hard-refresh |
|
||||||
|
| 404 on `/character` | Use `/character.html` (multi-page, not SPA rewrite) |
|
||||||
|
| Huge first load | Expected (~0.7 MB JS + ~0.8 MB WASM); cached after first visit |
|
||||||
|
| `node:module` warning in build | Harmless browser stub for box3d’s Node path; WASM path is used |
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
| File | Role |
|
||||||
|
|------|------|
|
||||||
|
| `wrangler.jsonc` | Workers name, assets directory, observability |
|
||||||
|
| `public/_headers` | Cache + WASM content-type hints |
|
||||||
|
| `vite.config.js` | Multi-page entries (`index` + `character`) |
|
||||||
|
| `package.json` | `deploy`, `deploy:dry`, `pages:deploy` scripts |
|
||||||
@@ -24,8 +24,11 @@ npm install
|
|||||||
npm run dev # http://localhost:5174
|
npm run dev # http://localhost:5174
|
||||||
npm test # headless: sim, rink, AI, pose, input, physics, hits
|
npm test # headless: sim, rink, AI, pose, input, physics, hits
|
||||||
npm run capture # boots the app headless and screenshots it into shots/
|
npm run capture # boots the app headless and screenshots it into shots/
|
||||||
|
npm run deploy # vite build + Cloudflare Workers static assets
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Cloudflare setup, Pages alternative, and CI notes: **[DEPLOY.md](DEPLOY.md)**.
|
||||||
|
|
||||||
In the browser:
|
In the browser:
|
||||||
|
|
||||||
| Xbox | keyboard | |
|
| Xbox | keyboard | |
|
||||||
|
|||||||
Generated
+1541
-1
File diff suppressed because it is too large
Load Diff
+7
-2
@@ -12,7 +12,11 @@
|
|||||||
"capture": "node tools/capture.mjs",
|
"capture": "node tools/capture.mjs",
|
||||||
"img2mesh": "node tools/img2mesh.mjs",
|
"img2mesh": "node tools/img2mesh.mjs",
|
||||||
"img2mesh:player": "node tools/img2mesh.mjs --subject player",
|
"img2mesh:player": "node tools/img2mesh.mjs --subject player",
|
||||||
"img2mesh:goalie": "node tools/img2mesh.mjs --subject goalie"
|
"img2mesh:goalie": "node tools/img2mesh.mjs --subject goalie",
|
||||||
|
"deploy": "npm run build && wrangler deploy",
|
||||||
|
"deploy:dry": "npm run build && wrangler deploy --dry-run",
|
||||||
|
"pages:deploy": "npm run build && wrangler pages deploy dist --project-name=tilt",
|
||||||
|
"cf:whoami": "wrangler whoami"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"box3d.js": "^0.0.2",
|
"box3d.js": "^0.0.2",
|
||||||
@@ -20,6 +24,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"puppeteer-core": "^25.4.0",
|
"puppeteer-core": "^25.4.0",
|
||||||
"vite": "^8.1.5"
|
"vite": "^8.1.5",
|
||||||
|
"wrangler": "^4.118.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# Cloudflare Pages / Workers static asset headers.
|
||||||
|
# Vite copies everything under public/ into dist/ on build.
|
||||||
|
|
||||||
|
/*
|
||||||
|
X-Content-Type-Options: nosniff
|
||||||
|
Referrer-Policy: strict-origin-when-cross-origin
|
||||||
|
Permissions-Policy: interest-cohort=()
|
||||||
|
|
||||||
|
# Hashed Vite assets: long cache. WASM must load as application/wasm.
|
||||||
|
/assets/*
|
||||||
|
Cache-Control: public, max-age=31536000, immutable
|
||||||
|
|
||||||
|
/*.wasm
|
||||||
|
Content-Type: application/wasm
|
||||||
|
Cache-Control: public, max-age=31536000, immutable
|
||||||
|
|
||||||
|
# HTML entry points stay fresh so deploys pick up new asset hashes quickly.
|
||||||
|
/
|
||||||
|
Cache-Control: public, max-age=0, must-revalidate
|
||||||
|
|
||||||
|
/index.html
|
||||||
|
Cache-Control: public, max-age=0, must-revalidate
|
||||||
|
|
||||||
|
/character.html
|
||||||
|
Cache-Control: public, max-age=0, must-revalidate
|
||||||
@@ -1,12 +1,20 @@
|
|||||||
|
import { resolve } from 'node:path';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
server: { port: 5174, open: true },
|
server: { port: 5174, open: true },
|
||||||
|
// Multi-page: main game + character studio both ship as real HTML entries.
|
||||||
build: {
|
build: {
|
||||||
target: 'es2022',
|
target: 'es2022',
|
||||||
// The body/skin generators are one deterministic chunk; splitting them buys
|
// The body/skin generators are one deterministic chunk; splitting them buys
|
||||||
// nothing and costs a round trip before anything can render.
|
// nothing and costs a round trip before anything can render.
|
||||||
chunkSizeWarningLimit: 2500,
|
chunkSizeWarningLimit: 2500,
|
||||||
|
rollupOptions: {
|
||||||
|
input: {
|
||||||
|
main: resolve(import.meta.dirname, 'index.html'),
|
||||||
|
character: resolve(import.meta.dirname, 'character.html'),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// box3d.js ships an Emscripten bundle that resolves its .wasm via
|
// box3d.js ships an Emscripten bundle that resolves its .wasm via
|
||||||
// import.meta.url. Pre-bundling rewrites that URL and breaks the lookup, so
|
// import.meta.url. Pre-bundling rewrites that URL and breaks the lookup, so
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./node_modules/wrangler/config-schema.json",
|
||||||
|
// tilt — static front-end on Workers (assets only, no server Worker code).
|
||||||
|
// Build first: `npm run build` → uploads `dist/`.
|
||||||
|
"name": "tilt",
|
||||||
|
"compatibility_date": "2026-08-03",
|
||||||
|
"assets": {
|
||||||
|
"directory": "./dist",
|
||||||
|
// Multi-page site (index.html + character.html), not a client-side router SPA.
|
||||||
|
// Missing paths return 404 rather than rewriting everything to index.html.
|
||||||
|
"not_found_handling": "404-page",
|
||||||
|
"html_handling": "auto-trailing-slash"
|
||||||
|
},
|
||||||
|
"observability": {
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user