- TypeScript 82.7%
- Svelte 16.2%
- CSS 0.8%
- HTML 0.3%
| src | ||
| tests | ||
| .gitignore | ||
| .oxfmtrc.json | ||
| .oxlintrc.json | ||
| AGENTS.md | ||
| deno.json | ||
| deno.lock | ||
| index.html | ||
| README.md | ||
| svelte.config.js | ||
| tsconfig.json | ||
| vite.config.ts | ||
Path of Steel
Path of Steel is a web based, text-based game inspired by Oregon Trail.
In a not too distant future, society is being rebuilt by a small group of skilled people with access to an battle mech which they now use for scavenging and construction.
The player sets up the party of skilled people and sends them and/or the mech on missions to scavenge resources or build up the community. During those missions, random events can happen that can be beneficial or detrimental to the mission. They also pick up survivors and bring them in, as well as sometimes meet new people to add to the team.
Implementation
The game is implemented as a web-based Svelte application that can be played entirely played in the browser. The game is running using deno. The user's game save is in localStorage. The player can have many save slots for ongoing games. The user is presented with a console-style interface with the choices as buttons to click on.
Events are fully configurable in JSON files that can be edited before launching the game. Resources, skills and starting amounts are also configured in JSON files. Names, gender and skills of the starting team are random.
Deploying on a server with nginx
The game is a static single-page app once built: deno task build produces a dist/ folder of plain
HTML/CSS/JS. You can build on your local machine and just upload that folder — the build output has no
platform-specific parts, so it works anywhere nginx can serve static files. No Deno/Node process needs
to run on the server, and Deno does not need to be installed there.
-
On your local machine, install Deno (skip if already installed):
curl -fsSL https://deno.land/install.sh | shMake sure
~/.deno/binis on yourPATHafterwards. -
From the project directory, build the static site:
deno task buildThis writes the deployable files to
dist/. All game content lives insrc/data/*.json, so make any content edits before this step; dependencies are fetched automatically on the first run.The build adds Subresource Integrity hashes (
integrity="sha384-...") to the<script>and<link>tags indist/index.html(viavite-plugin-sri3), so browsers reject any tampered JS/CSS files. -
Upload
dist/to the server, for example into/var/www/path-of-steel(adjustuser@server):rsync -av --delete dist/ user@server:/var/www/path-of-steel/On the server, make sure the directory is writable by you (e.g.
sudo mkdir -p /var/www/path-of-steel && sudo chown $USER /var/www/path-of-steel). Plainscpworks too:scp -r dist/. user@server:/var/www/path-of-steel/. -
Create an nginx server block, e.g.
/etc/nginx/sites-available/path-of-steel:server { listen 80; server_name example.com; root /var/www/path-of-steel; index index.html; # Security headers. nginx's add_header is only inherited by a location # if that location defines no add_header of its own, so the shared set # lives in one snippet that every location includes (see below). include snippets/path-of-steel-headers.conf; # SPA fallback: unknown paths are handled by the app's entry point. location / { try_files $uri $uri/ /index.html; include snippets/path-of-steel-headers.conf; } # Long-lived caching for hashed build assets. location /assets/ { expires 1y; add_header Cache-Control "public, immutable"; include snippets/path-of-steel-headers.conf; } }Replace
example.comwith your domain (or the server's IP).Then create the headers snippet, e.g.
/etc/nginx/snippets/path-of-steel-headers.conf:add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "DENY" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "geolocation=(), camera=(), microphone=()" always; add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'" always;The CSP is minimal on purpose: the app is fully self-contained (no CDN, no API calls, no images, no inline scripts or styles), so
default-src 'self'covers scripts, styles, fonts, images, and connections in one stroke. The four extra directives exist because they do not fall back todefault-src:object-src 'none'bans plugins,base-uri 'self'blocks<base>hijacking,form-action 'self'contains any injected form, andframe-ancestors 'none'(with the belt-and-bracesX-Frame-Options) forbids framing entirely. Script and stylesheet tags carry SRI hashes (see step 2), so a tampered asset is rejected even though it is same-origin. -
Enable the site and reload nginx:
sudo ln -s /etc/nginx/sites-available/path-of-steel /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl reload nginx -
Open the site in a browser. To add HTTPS, run
sudo certbot --nginx -d example.com(Certbot rewrites the server block for you).
To ship an update later, re-run deno task build locally and repeat the upload from step 3.