From abb62793316997b034c9e7dff78dc6655be8e6f5 Mon Sep 17 00:00:00 2001 From: MichaelFisher1997 Date: Wed, 17 Jun 2026 23:58:40 +0100 Subject: [PATCH 1/3] Add Railway infrastructure-as-code for game + website services --- .agents/skills/railway-config/SKILL.md | 214 +++++++++++++++++++++++++ .railway/README.md | 45 ++++++ .railway/railway.ts | 30 ++++ bun.lock | 63 ++++++++ package.json | 1 + 5 files changed, 353 insertions(+) create mode 100644 .agents/skills/railway-config/SKILL.md create mode 100644 .railway/README.md create mode 100644 .railway/railway.ts diff --git a/.agents/skills/railway-config/SKILL.md b/.agents/skills/railway-config/SKILL.md new file mode 100644 index 0000000..563b389 --- /dev/null +++ b/.agents/skills/railway-config/SKILL.md @@ -0,0 +1,214 @@ +--- +name: railway-config +description: Edit this project's Railway infrastructure-as-code configuration. Use this skill whenever the user asks to create, change, import, review, or troubleshoot Railway project infrastructure for the current repository, including services, databases, buckets, custom domains, replicas/regions, groups, environment variables, `railway config *`, or `.railway/railway.ts`. +--- + +# Railway configuration skill + +Use this skill when editing this repository's Railway configuration. + +The source of desired Railway project state is: + +```txt +.railway/railway.ts +``` + +## Core rules + +1. Express Railway product intent, not internal API details. +2. Do not write Railway UUIDs into `.railway/railway.ts`. +3. Do not write `EnvironmentConfigPatch`, `ServiceInstance`, Backboard internals, or generated Railway domains into source. +4. Prefer Railway configuration helpers like `service()`, `postgres()`, `redis()`, `mysql()`, `mongo()`, `bucket()`, `group()`, `github()`, and `image()`. +5. Use `service.env.VARIABLE` and `database.env.VARIABLE` for references. +6. Keep secrets out of source. Imported unknown secret values should use `preserve()` or be omitted when the user wants a smaller import. +7. Prefer product DSL names such as `domains`, `replicas`, and `group`; avoid internal names like `customDomains` and `multiRegionConfig`. +8. Do not add platform defaults unless the user explicitly wants them. +9. Do not manage a service from both `.railway/railway.ts` and `railway.json` / `railway.toml`; migrate the repo config first. +10. After editing `.railway/railway.ts`, run `railway config plan`. +11. Do not run `railway config apply` unless the user explicitly asks. +12. Never use `railway config apply --yes` or `railway config apply --confirm-destructive` from an agent session without explicit user approval for the exact plan. + +## Commands + +Initialize configuration files: + +```bash +railway config init +``` + +Import current Railway state: + +```bash +railway config pull +``` + +Import current Railway state with a smaller generated file that omits unknown variable values: + +```bash +railway config pull --omit-preserved-variables +``` + +Preview changes: + +```bash +railway config plan +``` + +Apply changes: + +```bash +railway config apply +``` + +Machine-readable preview: + +```bash +railway config plan --json +``` + +## Authoring + +Use Railway configuration helpers: + +```ts +import { + bucket, + defineRailway, + github, + group, + image, + mongo, + mysql, + postgres, + preserve, + project, + redis, + service, +} from "railway/iac"; +``` + +Minimal local service: + +```ts +const web = service("web", { + build: "bun run build", + start: "NODE_ENV=production bun src/index.ts", +}); +``` + +GitHub service: + +```ts +const web = service("web", { + source: github("owner/repo", { branch: "main" }), + build: "pnpm run build", + start: "pnpm start", +}); +``` + +Docker image service: + +```ts +const worker = service("worker", { + source: image("ghcr.io/acme/worker:latest"), +}); +``` + +Database reference: + +```ts +const db = postgres("postgres"); + +const web = service("web", { + env: { + DATABASE_URL: db.env.DATABASE_URL, + }, +}); +``` + +Service-to-service reference: + +```ts +const api = service("api", { + env: { + INTERNAL_TOKEN: preserve(), + }, +}); + +const web = service("web", { + env: { + API_TOKEN: api.env.INTERNAL_TOKEN, + API_HOST: api.env.RAILWAY_PRIVATE_DOMAIN, + }, +}); +``` + +Custom domains: + +```ts +const web = service("web", { + domains: ["app.example.com"], +}); +``` + +Replicas: + +```ts +const web = service("web", { + replicas: 3, +}); +``` + +Advanced placement: + +```ts +const web = service("web", { + replicas: { + "us-west2": 2, + "europe-west4": 1, + }, +}); +``` + +Groups: + +```ts +const api = service("api"); +const worker = service("worker"); +const backend = group("Backend", [api, worker]); +``` + +Bucket: + +```ts +const media = bucket("media", { region: "iad" }); +``` + +Project shape: + +```ts +export default defineRailway(() => { + const db = postgres("postgres"); + const web = service("web", { + env: { + DATABASE_URL: db.env.DATABASE_URL, + }, + }); + + return project("my-app", { + resources: [db, web], + }); +}); +``` + +## Review checklist + +Before applying changes, confirm: + +- The user has reviewed the latest `railway config plan` output. +- `railway config plan` shows only expected changes. +- Secrets are not replaced with literal placeholder values. +- Existing Railway-managed variables are omitted or use `preserve()` when the value should remain untouched. +- Custom domains are declared with `domains`, not networking internals. +- Scaling is declared with `replicas`, not `multiRegionConfig`. +- No generated Railway service domains are committed. diff --git a/.railway/README.md b/.railway/README.md new file mode 100644 index 0000000..dce1c51 --- /dev/null +++ b/.railway/README.md @@ -0,0 +1,45 @@ +# Railway configuration + +This project defines its Railway infrastructure in code. + +```txt +.railway/railway.ts +``` + +Use this file to describe the Railway project you want: services, databases, buckets, custom domains, replicas, groups, and environment variables. + +## Common commands + +Create the configuration files: + +```bash +railway config init +``` + +Import an existing Railway project into code: + +```bash +railway config pull +``` + +Preview what Railway would change: + +```bash +railway config plan +``` + +Apply the planned changes: + +```bash +railway config apply +``` + +## Notes + +- `railway config plan` is safe and does not change Railway. +- `railway config apply` previews changes and asks before applying unless you pass `--yes`. +- Destructive changes in non-interactive or agent sessions require `railway config apply --confirm-destructive` after reviewing the plan. +- Services already managed by `railway.json` / `railway.toml` must be migrated before `.railway/railway.ts` can manage them. +- Use `replicas` for scaling; advanced placement can still specify region names. +- Use `group("Name", [resources])` to keep large projects organized on the Railway canvas. +- Secrets imported from Railway are rendered as `preserve()` so existing values are retained without writing secret values to source. Use `railway config pull --omit-preserved-variables` for a smaller import. diff --git a/.railway/railway.ts b/.railway/railway.ts new file mode 100644 index 0000000..94a442d --- /dev/null +++ b/.railway/railway.ts @@ -0,0 +1,30 @@ +import { defineRailway, github, preserve, project, service } from "railway/iac"; + +export default defineRailway(() => { + const voxl = github("OpenStaticFish/voxl", { branch: "master" }); + + const game = service("game", { + source: voxl, + build: { buildEnvironment: "V3", builder: "DOCKERFILE", dockerfilePath: "Dockerfile" }, + healthcheck: "/", + healthcheckTimeout: 100, + replicas: 1, + env: { + PORT: preserve(), + }, + }); + const website = service("website", { + source: voxl, + build: { buildEnvironment: "V3", builder: "DOCKERFILE", dockerfilePath: "website/Dockerfile" }, + healthcheck: "/", + healthcheckTimeout: 100, + replicas: 1, + env: { + PORT: preserve(), + }, + }); + + return project("voxl", { + resources: [game, website], + }); +}); diff --git a/bun.lock b/bun.lock index f0dcd7e..338ef4b 100644 --- a/bun.lock +++ b/bun.lock @@ -8,6 +8,7 @@ "@babylonjs/core": "^7.40.0", }, "devDependencies": { + "railway": "^3.3.2", "typescript": "^5.6.0", "vite": "^5.4.0", }, @@ -50,10 +51,16 @@ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], @@ -62,6 +69,8 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], + "@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.62.0", "", { "os": "android", "cpu": "arm" }, "sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.62.0", "", { "os": "android", "cpu": "arm64" }, "sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA=="], @@ -118,18 +127,72 @@ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + "graphql": ["graphql@16.14.2", "", {}, "sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA=="], + "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], + "railway": ["railway@3.3.2", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.2.0", "graphql": "^16.10.0", "tsx": "^4.20.0" }, "bin": { "railway-iac-ts": "dist/iac/bin.js" } }, "sha512-gK9vYzyD/WizP7TyuPA9X9ojzgn/DcpIAI/OZjBZH7WT7tAWt8srmjnK6wMZ0mmakNpCrBYpt9r2bBmY3SB3Gw=="], + "rollup": ["rollup@4.62.0", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.62.0", "@rollup/rollup-android-arm64": "4.62.0", "@rollup/rollup-darwin-arm64": "4.62.0", "@rollup/rollup-darwin-x64": "4.62.0", "@rollup/rollup-freebsd-arm64": "4.62.0", "@rollup/rollup-freebsd-x64": "4.62.0", "@rollup/rollup-linux-arm-gnueabihf": "4.62.0", "@rollup/rollup-linux-arm-musleabihf": "4.62.0", "@rollup/rollup-linux-arm64-gnu": "4.62.0", "@rollup/rollup-linux-arm64-musl": "4.62.0", "@rollup/rollup-linux-loong64-gnu": "4.62.0", "@rollup/rollup-linux-loong64-musl": "4.62.0", "@rollup/rollup-linux-ppc64-gnu": "4.62.0", "@rollup/rollup-linux-ppc64-musl": "4.62.0", "@rollup/rollup-linux-riscv64-gnu": "4.62.0", "@rollup/rollup-linux-riscv64-musl": "4.62.0", "@rollup/rollup-linux-s390x-gnu": "4.62.0", "@rollup/rollup-linux-x64-gnu": "4.62.0", "@rollup/rollup-linux-x64-musl": "4.62.0", "@rollup/rollup-openbsd-x64": "4.62.0", "@rollup/rollup-openharmony-arm64": "4.62.0", "@rollup/rollup-win32-arm64-msvc": "4.62.0", "@rollup/rollup-win32-ia32-msvc": "4.62.0", "@rollup/rollup-win32-x64-gnu": "4.62.0", "@rollup/rollup-win32-x64-msvc": "4.62.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + "tsx": ["tsx@4.22.4", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], + + "tsx/esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], + + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], + + "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], + + "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], + + "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], + + "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], + + "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], + + "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], + + "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], + + "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], + + "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], + + "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], + + "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], + + "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], + + "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], + + "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], + + "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], + + "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], + + "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], + + "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], + + "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], + + "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], + + "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], + + "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], } } diff --git a/package.json b/package.json index 7ba2057..73a508f 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@babylonjs/core": "^7.40.0" }, "devDependencies": { + "railway": "^3.3.2", "typescript": "^5.6.0", "vite": "^5.4.0" } From a7e756b0daeb1d561716832e276edd8ae1c949dd Mon Sep 17 00:00:00 2001 From: MichaelFisher1997 Date: Thu, 18 Jun 2026 03:01:11 +0100 Subject: [PATCH 2/3] Improve view distance and water behavior --- index.html | 2 +- src/engine/Renderer.ts | 4 +- src/game/Game.ts | 7 ++-- src/game/World.ts | 6 ++- src/game/graphics/GraphicsController.ts | 6 +-- src/game/graphics/GraphicsSettings.ts | 14 ++++--- src/game/lighting/WaterMaterial.ts | 6 +-- src/game/liquid/LiquidSimulator.ts | 7 ++-- src/game/liquid/LiquidUpdateQueue.ts | 50 ++++++++++++++++++++----- src/state/Settings.ts | 10 ++++- tests/liquid.test.ts | 14 +++++++ 11 files changed, 91 insertions(+), 35 deletions(-) diff --git a/index.html b/index.html index 02f6812..bd3d8f6 100644 --- a/index.html +++ b/index.html @@ -67,7 +67,7 @@

Settings

- +
diff --git a/src/engine/Renderer.ts b/src/engine/Renderer.ts index 339ee60..de2bc97 100644 --- a/src/engine/Renderer.ts +++ b/src/engine/Renderer.ts @@ -16,11 +16,11 @@ export class Renderer { this.engine = new Engine( this.canvas, - true, // antialias + false, // antialias; FXAA is controlled by graphics settings when requested { preserveDrawingBuffer: true, // needed for canvas.toDataURL screenshots powerPreference: "high-performance", - stencil: true, + stencil: false, }, false, // adaptToDeviceRatio — we cap manually below ); diff --git a/src/game/Game.ts b/src/game/Game.ts index 107dac3..0db131a 100644 --- a/src/game/Game.ts +++ b/src/game/Game.ts @@ -11,6 +11,7 @@ import { Vector3, } from "@babylonjs/core"; import { + CHUNK_SIZE, PLAYER_HALF_WIDTH, PLAYER_HEIGHT, SEA_LEVEL, @@ -925,13 +926,13 @@ export class Game { this.scene.fogEnd = 1e6; return; } - const far = this.settings.viewDistance * 16 * 2.0; + const far = (this.settings.viewDistance + 1.25) * CHUNK_SIZE; // Higher presets get a later fog start → clearer mid-distance terrain and // less of the washed-out haze. Low keeps an earlier start to hide the // pop-in that comes with its shorter render distance. const startFrac = - this.settings.graphics.preset === "high" ? 0.6 : - this.settings.graphics.preset === "low" ? 0.42 : 0.5; + this.settings.graphics.preset === "high" ? 0.78 : + this.settings.graphics.preset === "low" ? 0.58 : 0.7; this.scene.fogEnd = far; this.scene.fogStart = far * startFrac; // Tell the underwater renderer the new above-water baseline so its blend diff --git a/src/game/World.ts b/src/game/World.ts index 8a630df..0a8ba3c 100644 --- a/src/game/World.ts +++ b/src/game/World.ts @@ -194,9 +194,11 @@ export class World { const cached = this.spiralCache.get(radius); if (cached) return cached; const list: Array<{ dx: number; dz: number; d: number }> = []; + const radiusSq = radius * radius; for (let dx = -radius; dx <= radius; dx++) { for (let dz = -radius; dz <= radius; dz++) { - list.push({ dx, dz, d: dx * dx + dz * dz }); + const d = dx * dx + dz * dz; + if (d <= radiusSq) list.push({ dx, dz, d }); } } list.sort((a, b) => a.d - b.d); @@ -514,7 +516,7 @@ export class World { /** Set the foliage (cutout) render-distance tier. Draw-time only. */ setFoliageDensity(density: FoliageDensity): void { - this.foliageCutoutDistance = density === "low" ? 48 : density === "medium" ? 96 : Infinity; + this.foliageCutoutDistance = density === "low" ? 48 : density === "medium" ? 96 : 160; } /** diff --git a/src/game/graphics/GraphicsController.ts b/src/game/graphics/GraphicsController.ts index bfda87e..65b60c0 100644 --- a/src/game/graphics/GraphicsController.ts +++ b/src/game/graphics/GraphicsController.ts @@ -41,7 +41,7 @@ export function shadowConfigForQuality(quality: ShadowQuality): { } /** Maximum supported render distance (chunk radius), capped for browser safety. */ -export const MAX_RENDER_DISTANCE = 12; +export const MAX_RENDER_DISTANCE = 20; /** Minimum render distance — below this the world feels empty. */ export const MIN_RENDER_DISTANCE = 2; @@ -179,9 +179,9 @@ export class GraphicsController { export function presetRenderDistance(preset: GraphicsSettings["preset"]): number { switch (preset) { case "low": return 4; - case "high": return 8; + case "high": return 16; case "medium": - default: return 6; + default: return 10; } } diff --git a/src/game/graphics/GraphicsSettings.ts b/src/game/graphics/GraphicsSettings.ts index 2625d5a..c52fadf 100644 --- a/src/game/graphics/GraphicsSettings.ts +++ b/src/game/graphics/GraphicsSettings.ts @@ -60,7 +60,7 @@ export const GRAPHICS_PRESETS: Record, Graphic renderScale: 1.0, dprCap: 2, antiAliasing: false, - shadows: "low", + shadows: "off", water: "medium", foliage: "medium", clouds: "fancy", @@ -71,7 +71,7 @@ export const GRAPHICS_PRESETS: Record, Graphic renderScale: 1.0, dprCap: 2, antiAliasing: true, - shadows: "medium", + shadows: "off", water: "high", foliage: "high", clouds: "fancy", @@ -88,7 +88,7 @@ export function defaultGraphicsSettings(): GraphicsSettings { /** A sensible default render distance (chunks) matching the device class. */ export function defaultRenderDistance(): number { - return detectLowEndDevice() ? 4 : 6; + return detectLowEndDevice() ? 4 : 10; } /** @@ -129,12 +129,16 @@ export function migrateGraphics(parsed: Partial | undefined): if (clouds === undefined && typeof legacy.clouds === "boolean") { clouds = legacy.clouds ? "fancy" : "off"; } + const preset = parsed.preset ?? base.preset; + const oldPresetShadow = + (preset === "medium" && parsed.shadows === "low") || + (preset === "high" && parsed.shadows === "medium"); return { - preset: parsed.preset ?? base.preset, + preset, renderScale: clamp(parsed.renderScale ?? base.renderScale, 0.5, 1), dprCap: clamp(parsed.dprCap ?? base.dprCap, 1.5, 2), antiAliasing: parsed.antiAliasing ?? base.antiAliasing, - shadows: parsed.shadows ?? base.shadows, + shadows: oldPresetShadow ? "off" : parsed.shadows ?? base.shadows, water: parsed.water ?? base.water, foliage: parsed.foliage ?? base.foliage, clouds: clouds ?? base.clouds, diff --git a/src/game/lighting/WaterMaterial.ts b/src/game/lighting/WaterMaterial.ts index b616931..8e86eee 100644 --- a/src/game/lighting/WaterMaterial.ts +++ b/src/game/lighting/WaterMaterial.ts @@ -43,7 +43,7 @@ export class WaterMaterial { private alpha = 0.82; /** Base diffuse/emissive colours (the subtle animation oscillates around these). */ private readonly baseDiffuse = Color3.FromHexString("#1f86d8"); - private readonly baseEmissive = Color3.FromHexString("#0b3a6b"); + private readonly baseEmissive = Color3.Black(); private readonly shimmerColor = Color3.FromHexString("#2aa0e8"); private animTime = 0; private animationEnabled = true; @@ -160,9 +160,7 @@ export class WaterMaterial { m.diffuseColor.r = this.baseDiffuse.r + (this.shimmerColor.r - this.baseDiffuse.r) * amp * w; m.diffuseColor.g = this.baseDiffuse.g + (this.shimmerColor.g - this.baseDiffuse.g) * amp * w; m.diffuseColor.b = this.baseDiffuse.b + (this.shimmerColor.b - this.baseDiffuse.b) * amp * w; - m.emissiveColor.r = this.baseEmissive.r * (1 + amp * 0.5 * w); - m.emissiveColor.g = this.baseEmissive.g * (1 + amp * 0.5 * w); - m.emissiveColor.b = this.baseEmissive.b * (1 + amp * 0.5 * w); + m.emissiveColor.copyFrom(this.baseEmissive); } /** Debug: enable/disable the surface scroll + shimmer (texture stays put). */ diff --git a/src/game/liquid/LiquidSimulator.ts b/src/game/liquid/LiquidSimulator.ts index b9a5a9e..bc5bd34 100644 --- a/src/game/liquid/LiquidSimulator.ts +++ b/src/game/liquid/LiquidSimulator.ts @@ -452,10 +452,9 @@ export class LiquidSimulator { /** * True if the liquid cell at (x,y,z) may spread SIDEWARDS — i.e. it is a - * source, or a flowing cell resting on SOLID terrain. A flowing cell with - * water/air below is part of a suspended/falling column and must NOT spread - * sideways (otherwise a waterfall grows into a diverging 3D plume). Only the - * cell that actually lands on the floor spreads, which keeps flow bounded. + * source, or a flowing cell resting on SOLID terrain. This mirrors Luanti's + * `LIQUID_FLOW_DOWN_MASK` behavior: source nodes still feed same-level + * neighbours, but same-level falling flowing nodes cannot feed sideways. */ private isSupported(access: LiquidAccess, x: number, y: number, z: number): boolean { const id = access.getBlock(x, y, z); diff --git a/src/game/liquid/LiquidUpdateQueue.ts b/src/game/liquid/LiquidUpdateQueue.ts index d651ce9..4505cbd 100644 --- a/src/game/liquid/LiquidUpdateQueue.ts +++ b/src/game/liquid/LiquidUpdateQueue.ts @@ -25,9 +25,11 @@ export class LiquidUpdateQueue { /** Normal lane (flow propagation + seeding). */ private readonly pending = new Set(); private fifo: QueuedCell[] = []; + private fifoHead = 0; /** Priority lane (player edits) — drained before the normal lane. */ private readonly priorityPending = new Set(); private priorityFifo: QueuedCell[] = []; + private priorityFifoHead = 0; /** High-water mark (both lanes) since creation, for the debug overlay. */ peakSize = 0; @@ -84,17 +86,25 @@ export class LiquidUpdateQueue { */ dequeue(): QueuedCell | null { // Priority first. - while (this.priorityFifo.length > 0) { - const cell = this.priorityFifo.shift()!; + while (this.priorityFifoHead < this.priorityFifo.length) { + const cell = this.priorityFifo[this.priorityFifoHead++]!; const k = this.key(cell.x, cell.y, cell.z); - if (this.priorityPending.delete(k)) return cell; + if (this.priorityPending.delete(k)) { + this.compactPriority(); + return cell; + } } + this.compactPriority(true); // Then the normal backlog. - while (this.fifo.length > 0) { - const cell = this.fifo.shift()!; + while (this.fifoHead < this.fifo.length) { + const cell = this.fifo[this.fifoHead++]!; const k = this.key(cell.x, cell.y, cell.z); - if (this.pending.delete(k)) return cell; + if (this.pending.delete(k)) { + this.compactNormal(); + return cell; + } } + this.compactNormal(true); return null; } @@ -104,11 +114,15 @@ export class LiquidUpdateQueue { * scheduled tick). Returns null when the priority lane is empty. */ pullPriority(): QueuedCell | null { - while (this.priorityFifo.length > 0) { - const cell = this.priorityFifo.shift()!; + while (this.priorityFifoHead < this.priorityFifo.length) { + const cell = this.priorityFifo[this.priorityFifoHead++]!; const k = this.key(cell.x, cell.y, cell.z); - if (this.priorityPending.delete(k)) return cell; + if (this.priorityPending.delete(k)) { + this.compactPriority(); + return cell; + } } + this.compactPriority(true); return null; } @@ -116,12 +130,28 @@ export class LiquidUpdateQueue { clear(): void { this.pending.clear(); this.fifo.length = 0; + this.fifoHead = 0; this.priorityPending.clear(); this.priorityFifo.length = 0; + this.priorityFifoHead = 0; } /** Debug snapshot of queued positions (read-only; does not mutate). */ snapshot(): readonly QueuedCell[] { - return this.fifo; + return this.fifo.slice(this.fifoHead); + } + + private compactNormal(force = false): void { + if (this.fifoHead === 0) return; + if (!force && this.fifoHead < 1024 && this.fifoHead * 2 < this.fifo.length) return; + this.fifo = this.fifo.slice(this.fifoHead); + this.fifoHead = 0; + } + + private compactPriority(force = false): void { + if (this.priorityFifoHead === 0) return; + if (!force && this.priorityFifoHead < 1024 && this.priorityFifoHead * 2 < this.priorityFifo.length) return; + this.priorityFifo = this.priorityFifo.slice(this.priorityFifoHead); + this.priorityFifoHead = 0; } } diff --git a/src/state/Settings.ts b/src/state/Settings.ts index e1c763b..000127b 100644 --- a/src/state/Settings.ts +++ b/src/state/Settings.ts @@ -36,7 +36,8 @@ export function loadSettings(): Settings { if (legacyClouds !== undefined && savedGraphics?.clouds === undefined) { graphics.clouds = legacyClouds ? "fancy" : "off"; } - return { ...DEFAULT_SETTINGS, ...rest, mode, graphics }; + const viewDistance = migratePresetViewDistance(rest.viewDistance, graphics.preset); + return { ...DEFAULT_SETTINGS, ...rest, viewDistance, mode, graphics }; } catch { return { ...DEFAULT_SETTINGS }; } @@ -50,4 +51,11 @@ export function saveSettings(settings: Settings): void { } } +function migratePresetViewDistance(viewDistance: number | undefined, preset: GraphicsSettings["preset"]): number { + if (viewDistance === undefined) return DEFAULT_SETTINGS.viewDistance; + if (preset === "medium" && (viewDistance === 6 || viewDistance === 8)) return 10; + if (preset === "high" && (viewDistance === 8 || viewDistance === 12)) return 16; + return viewDistance; +} + export type { GraphicsSettings }; diff --git a/tests/liquid.test.ts b/tests/liquid.test.ts index 778f730..be6c96a 100644 --- a/tests/liquid.test.ts +++ b/tests/liquid.test.ts @@ -66,10 +66,24 @@ console.log("\n[Test 1] Source above air pours straight down (waterfall), stops assert(w.getBlock(0, 4, 0) === WATER_FLOWING_BLOCK, "flowing at y=4"); assert(w.getBlock(0, 3, 0) === WATER_FLOWING_BLOCK, "flowing at y=3"); assert(w.getLevel(0, 4, 0) === 7 && w.getLevel(0, 1, 0) === 7, "falling column is full level (7)"); + assert(w.getBlock(1, 5, 0) === WATER_FLOWING_BLOCK && w.getBlock(-1, 5, 0) === WATER_FLOWING_BLOCK, "source feeds same-level neighbours like Luanti"); assert(w.getBlock(0, 0, 0) === 3, "floor intact"); assert(sim.queueSize === 0, "queue drained (no endless loop)"); } +console.log("\n[Test 1b] Falling flowing water does not feed sideways until it lands."); +{ + const w = new FakeWorld(); + w.floor(-14, 14, -14, 14, 0); + w.set(0, 5, 0, WATER_FLOWING_BLOCK, 7); + w.set(0, 4, 0, WATER_FLOWING_BLOCK, 7); + const sim = new LiquidSimulator(); + sim.enqueue(1, 4, 0); + settle(sim, w); + assert(w.getBlock(1, 4, 0) === AIR_BLOCK, "same-level falling flowing node does not feed sideways"); + assert(sim.queueSize === 0, "queue drained"); +} + console.log("\n[Test 2] Source on a solid floor spreads horizontally and stops at range."); { const w = new FakeWorld(); From dc3adfb1072eeff3f2fbb37a16b2dc2dd8adeb2a Mon Sep 17 00:00:00 2001 From: MichaelFisher1997 Date: Thu, 18 Jun 2026 03:14:20 +0100 Subject: [PATCH 3/3] Address PR review cleanup --- .agents/skills/railway-config/SKILL.md | 214 ------------------------ .railway/README.md | 45 ----- .railway/railway.ts | 30 ---- bun.lock | 63 ------- package.json | 1 - src/game/graphics/GraphicsController.ts | 6 +- 6 files changed, 3 insertions(+), 356 deletions(-) delete mode 100644 .agents/skills/railway-config/SKILL.md delete mode 100644 .railway/README.md delete mode 100644 .railway/railway.ts diff --git a/.agents/skills/railway-config/SKILL.md b/.agents/skills/railway-config/SKILL.md deleted file mode 100644 index 563b389..0000000 --- a/.agents/skills/railway-config/SKILL.md +++ /dev/null @@ -1,214 +0,0 @@ ---- -name: railway-config -description: Edit this project's Railway infrastructure-as-code configuration. Use this skill whenever the user asks to create, change, import, review, or troubleshoot Railway project infrastructure for the current repository, including services, databases, buckets, custom domains, replicas/regions, groups, environment variables, `railway config *`, or `.railway/railway.ts`. ---- - -# Railway configuration skill - -Use this skill when editing this repository's Railway configuration. - -The source of desired Railway project state is: - -```txt -.railway/railway.ts -``` - -## Core rules - -1. Express Railway product intent, not internal API details. -2. Do not write Railway UUIDs into `.railway/railway.ts`. -3. Do not write `EnvironmentConfigPatch`, `ServiceInstance`, Backboard internals, or generated Railway domains into source. -4. Prefer Railway configuration helpers like `service()`, `postgres()`, `redis()`, `mysql()`, `mongo()`, `bucket()`, `group()`, `github()`, and `image()`. -5. Use `service.env.VARIABLE` and `database.env.VARIABLE` for references. -6. Keep secrets out of source. Imported unknown secret values should use `preserve()` or be omitted when the user wants a smaller import. -7. Prefer product DSL names such as `domains`, `replicas`, and `group`; avoid internal names like `customDomains` and `multiRegionConfig`. -8. Do not add platform defaults unless the user explicitly wants them. -9. Do not manage a service from both `.railway/railway.ts` and `railway.json` / `railway.toml`; migrate the repo config first. -10. After editing `.railway/railway.ts`, run `railway config plan`. -11. Do not run `railway config apply` unless the user explicitly asks. -12. Never use `railway config apply --yes` or `railway config apply --confirm-destructive` from an agent session without explicit user approval for the exact plan. - -## Commands - -Initialize configuration files: - -```bash -railway config init -``` - -Import current Railway state: - -```bash -railway config pull -``` - -Import current Railway state with a smaller generated file that omits unknown variable values: - -```bash -railway config pull --omit-preserved-variables -``` - -Preview changes: - -```bash -railway config plan -``` - -Apply changes: - -```bash -railway config apply -``` - -Machine-readable preview: - -```bash -railway config plan --json -``` - -## Authoring - -Use Railway configuration helpers: - -```ts -import { - bucket, - defineRailway, - github, - group, - image, - mongo, - mysql, - postgres, - preserve, - project, - redis, - service, -} from "railway/iac"; -``` - -Minimal local service: - -```ts -const web = service("web", { - build: "bun run build", - start: "NODE_ENV=production bun src/index.ts", -}); -``` - -GitHub service: - -```ts -const web = service("web", { - source: github("owner/repo", { branch: "main" }), - build: "pnpm run build", - start: "pnpm start", -}); -``` - -Docker image service: - -```ts -const worker = service("worker", { - source: image("ghcr.io/acme/worker:latest"), -}); -``` - -Database reference: - -```ts -const db = postgres("postgres"); - -const web = service("web", { - env: { - DATABASE_URL: db.env.DATABASE_URL, - }, -}); -``` - -Service-to-service reference: - -```ts -const api = service("api", { - env: { - INTERNAL_TOKEN: preserve(), - }, -}); - -const web = service("web", { - env: { - API_TOKEN: api.env.INTERNAL_TOKEN, - API_HOST: api.env.RAILWAY_PRIVATE_DOMAIN, - }, -}); -``` - -Custom domains: - -```ts -const web = service("web", { - domains: ["app.example.com"], -}); -``` - -Replicas: - -```ts -const web = service("web", { - replicas: 3, -}); -``` - -Advanced placement: - -```ts -const web = service("web", { - replicas: { - "us-west2": 2, - "europe-west4": 1, - }, -}); -``` - -Groups: - -```ts -const api = service("api"); -const worker = service("worker"); -const backend = group("Backend", [api, worker]); -``` - -Bucket: - -```ts -const media = bucket("media", { region: "iad" }); -``` - -Project shape: - -```ts -export default defineRailway(() => { - const db = postgres("postgres"); - const web = service("web", { - env: { - DATABASE_URL: db.env.DATABASE_URL, - }, - }); - - return project("my-app", { - resources: [db, web], - }); -}); -``` - -## Review checklist - -Before applying changes, confirm: - -- The user has reviewed the latest `railway config plan` output. -- `railway config plan` shows only expected changes. -- Secrets are not replaced with literal placeholder values. -- Existing Railway-managed variables are omitted or use `preserve()` when the value should remain untouched. -- Custom domains are declared with `domains`, not networking internals. -- Scaling is declared with `replicas`, not `multiRegionConfig`. -- No generated Railway service domains are committed. diff --git a/.railway/README.md b/.railway/README.md deleted file mode 100644 index dce1c51..0000000 --- a/.railway/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Railway configuration - -This project defines its Railway infrastructure in code. - -```txt -.railway/railway.ts -``` - -Use this file to describe the Railway project you want: services, databases, buckets, custom domains, replicas, groups, and environment variables. - -## Common commands - -Create the configuration files: - -```bash -railway config init -``` - -Import an existing Railway project into code: - -```bash -railway config pull -``` - -Preview what Railway would change: - -```bash -railway config plan -``` - -Apply the planned changes: - -```bash -railway config apply -``` - -## Notes - -- `railway config plan` is safe and does not change Railway. -- `railway config apply` previews changes and asks before applying unless you pass `--yes`. -- Destructive changes in non-interactive or agent sessions require `railway config apply --confirm-destructive` after reviewing the plan. -- Services already managed by `railway.json` / `railway.toml` must be migrated before `.railway/railway.ts` can manage them. -- Use `replicas` for scaling; advanced placement can still specify region names. -- Use `group("Name", [resources])` to keep large projects organized on the Railway canvas. -- Secrets imported from Railway are rendered as `preserve()` so existing values are retained without writing secret values to source. Use `railway config pull --omit-preserved-variables` for a smaller import. diff --git a/.railway/railway.ts b/.railway/railway.ts deleted file mode 100644 index 94a442d..0000000 --- a/.railway/railway.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { defineRailway, github, preserve, project, service } from "railway/iac"; - -export default defineRailway(() => { - const voxl = github("OpenStaticFish/voxl", { branch: "master" }); - - const game = service("game", { - source: voxl, - build: { buildEnvironment: "V3", builder: "DOCKERFILE", dockerfilePath: "Dockerfile" }, - healthcheck: "/", - healthcheckTimeout: 100, - replicas: 1, - env: { - PORT: preserve(), - }, - }); - const website = service("website", { - source: voxl, - build: { buildEnvironment: "V3", builder: "DOCKERFILE", dockerfilePath: "website/Dockerfile" }, - healthcheck: "/", - healthcheckTimeout: 100, - replicas: 1, - env: { - PORT: preserve(), - }, - }); - - return project("voxl", { - resources: [game, website], - }); -}); diff --git a/bun.lock b/bun.lock index 338ef4b..f0dcd7e 100644 --- a/bun.lock +++ b/bun.lock @@ -8,7 +8,6 @@ "@babylonjs/core": "^7.40.0", }, "devDependencies": { - "railway": "^3.3.2", "typescript": "^5.6.0", "vite": "^5.4.0", }, @@ -51,16 +50,10 @@ "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], @@ -69,8 +62,6 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - "@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.62.0", "", { "os": "android", "cpu": "arm" }, "sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.62.0", "", { "os": "android", "cpu": "arm64" }, "sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA=="], @@ -127,72 +118,18 @@ "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - "graphql": ["graphql@16.14.2", "", {}, "sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA=="], - "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], - "railway": ["railway@3.3.2", "", { "dependencies": { "@graphql-typed-document-node/core": "^3.2.0", "graphql": "^16.10.0", "tsx": "^4.20.0" }, "bin": { "railway-iac-ts": "dist/iac/bin.js" } }, "sha512-gK9vYzyD/WizP7TyuPA9X9ojzgn/DcpIAI/OZjBZH7WT7tAWt8srmjnK6wMZ0mmakNpCrBYpt9r2bBmY3SB3Gw=="], - "rollup": ["rollup@4.62.0", "", { "dependencies": { "@types/estree": "1.0.9" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.62.0", "@rollup/rollup-android-arm64": "4.62.0", "@rollup/rollup-darwin-arm64": "4.62.0", "@rollup/rollup-darwin-x64": "4.62.0", "@rollup/rollup-freebsd-arm64": "4.62.0", "@rollup/rollup-freebsd-x64": "4.62.0", "@rollup/rollup-linux-arm-gnueabihf": "4.62.0", "@rollup/rollup-linux-arm-musleabihf": "4.62.0", "@rollup/rollup-linux-arm64-gnu": "4.62.0", "@rollup/rollup-linux-arm64-musl": "4.62.0", "@rollup/rollup-linux-loong64-gnu": "4.62.0", "@rollup/rollup-linux-loong64-musl": "4.62.0", "@rollup/rollup-linux-ppc64-gnu": "4.62.0", "@rollup/rollup-linux-ppc64-musl": "4.62.0", "@rollup/rollup-linux-riscv64-gnu": "4.62.0", "@rollup/rollup-linux-riscv64-musl": "4.62.0", "@rollup/rollup-linux-s390x-gnu": "4.62.0", "@rollup/rollup-linux-x64-gnu": "4.62.0", "@rollup/rollup-linux-x64-musl": "4.62.0", "@rollup/rollup-openbsd-x64": "4.62.0", "@rollup/rollup-openharmony-arm64": "4.62.0", "@rollup/rollup-win32-arm64-msvc": "4.62.0", "@rollup/rollup-win32-ia32-msvc": "4.62.0", "@rollup/rollup-win32-x64-gnu": "4.62.0", "@rollup/rollup-win32-x64-msvc": "4.62.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - "tsx": ["tsx@4.22.4", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg=="], - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], - - "tsx/esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], - - "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], - - "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], - - "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], - - "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], - - "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], - - "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], - - "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], - - "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], - - "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], - - "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], - - "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], - - "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], - - "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], - - "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], - - "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], - - "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], - - "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], - - "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], - - "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], - - "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], - - "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], - - "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], - - "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], } } diff --git a/package.json b/package.json index 73a508f..7ba2057 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "@babylonjs/core": "^7.40.0" }, "devDependencies": { - "railway": "^3.3.2", "typescript": "^5.6.0", "vite": "^5.4.0" } diff --git a/src/game/graphics/GraphicsController.ts b/src/game/graphics/GraphicsController.ts index 65b60c0..aa93fea 100644 --- a/src/game/graphics/GraphicsController.ts +++ b/src/game/graphics/GraphicsController.ts @@ -133,9 +133,9 @@ export class GraphicsController { } this.pipeline.fxaaEnabled = true; } else if (this.pipeline) { - // AA off → tear down the pipeline so the scene renders straight to the - // default framebuffer (no fullscreen pass). Engine-level MSAA (set at - // context creation) remains as the baseline edge AA. + // AA off: tear down the pipeline so the scene renders straight to the + // default framebuffer. Engine-level antialiasing is also disabled at + // context creation, so low/custom presets render with no AA. this.pipeline.dispose(); this.pipeline = null; }