diff --git a/docs/docs/client.mdx b/docs/docs/client.mdx index 1dca4f6..ebe6447 100644 --- a/docs/docs/client.mdx +++ b/docs/docs/client.mdx @@ -1,80 +1,59 @@ --- -title: Client -description: Bun HTTP server that serves the browser-side of a NanoForge project +title: Client loader +description: The web server that serves the loading page and the client build --- -## Overview +## What it does -`loader-client` is a Bun HTTP server that bridges the browser and the compiled game client. It serves the `loader-website` frontend, exposes the game file manifest, delivers the compiled game assets, and forwards environment variables to the browser. It is the single entry point for everything the player's browser contacts. +The client loader (`@nanoforge-dev/loader-client`) is a Bun web server. [`nf start`](/docs/cli/commands/start) +starts one for each client app, pointed at the app's build folder. It serves: -## Usage +- the [loading page](./website), at `/`; +- the list of game files, at `/manifest`; +- the client's environment variables, at `/env`; +- the game files themselves, at `/game/`. -```bash -nf start -``` +It prints `Client started on url http://localhost:3000/` when it is ready. -Or invoke directly after building the loader packages: +## What nf start passes to it -```bash -npx loader-client --dir .nanoforge/client --port 3000 -``` +| `nf start` option or config field | Client loader option | +| ---------------------------------------------------------- | ------------------------------- | +| the client build folder (`out.dir`) | `-d, --dir ` (required) | +| `--port`, or `port` (default `3000`) | `-p, --port ` | +| `--watch` (set by `nf dev`) | `--watch` | +| the server build folder, with `--watch` and one server app | `--watch-server-dir ` | +| `--cert` and `--key`, or `tls` | `--cert `, `--key ` | -## Routes +The client loader also has `--watch-port ` for the live-reload WebSocket. `nf start` doesn't +set it, so the WebSocket takes a free port. -| Route | Description | -| --------------- | --------------------------------------------------------------------- | -| `GET /` | Serves the `loader-website` HTML application | -| `GET /*` | Serves static assets bundled with `loader-website` | -| `GET /manifest` | Returns the game files (path, SHA-256 hash, size) and version as JSON | -| `GET /env` | Returns `NANOFORGE_*` environment variables (prefix stripped) as JSON | -| `GET /game/*` | Serves compiled game client files from the configured game directory | +## Routes worth knowing when you debug -## Options +Open them in the browser next to the game: -| Option | Type | Default | Description | -| -------------------------- | --------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `-p, --port ` | `string` | `3000` | Port the HTTP server listens on | -| `-d, --dir ` | `string` | `.nanoforge/client` | Directory of compiled client game files | -| `--watch` | `boolean` | `false` | Enable file watcher and browser hot-reload | -| `--watch-port ` | `string` | auto | Port for the WebSocket watch server | -| `--watch-server-dir ` | `string` | — | Also watch a server game directory for changes | -| `--cert ` | `string` | — | TLS certificate file (enables HTTPS) | -| `--key ` | `string` | — | TLS private key file (enables HTTPS) | +| Route | Returns | +| -------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `/manifest` | JSON: each file of the build with its path, SHA-256 hash and size, a version, and the live-reload WebSocket URL when `--watch` is on. | +| `/env` | JSON: the variables the client gets, without the `NANOFORGE_` prefix, plus `tlsEnabled`. | +| `/game/` | One file of the build, for example `/game/main.js`. | -## Watch mode +`/manifest` lists the build folder each time it is requested, so files added by a rebuild show up +on the next page load. A hash is computed again only when a file's size or modification time +changes. -When `--watch` is enabled, `loader-client` starts a WebSocket server on `--watch-port` (or a random free port) and sets the `watch.url` field in the manifest response. Any file change in the game directory causes the server to broadcast an `update` message. The `loader-website` frontend connects to that WebSocket and reloads the page on receipt. +## Live reload -Pass `--watch-server-dir` to also reload when the server game directory changes — useful when client and server are compiled in one step. +With `--watch`, the client loader watches the build folder and sends `update` on its WebSocket +when a file changes. The loading page then reloads. Changes closer than 100 ms count as one. When +`nf start` passes `--watch-server-dir`, a change in the server build reloads the page too, 100 ms +later, to give the server time to restart. -## HTTPS / TLS +## HTTPS -Pass `--cert` and `--key` to enable HTTPS. The server reads both files with Bun's native TLS support. The `/env` response will include `tlsEnabled: true` so the game can detect the protocol at runtime. +With `--cert` and `--key`, the client loader serves HTTPS, and `/env` returns `tlsEnabled: true`. +You need HTTPS as soon as players open the game on an address other than `localhost`: see +[secure context](./website#secure-context). -HTTPS is required when the browser enforces a secure context — for example when using the Origin Private File System API used by `loader-website` to cache game files. - -## Examples - -**Start on port 8080:** - -```bash -npx loader-client --port 8080 -``` - -**Start with watch mode:** - -```bash -npx loader-client --watch --watch-server-dir .nanoforge/server -``` - -**Start with HTTPS:** - -```bash -npx loader-client --cert ./certs/server.crt --key ./certs/server.key -``` - -**Point to a custom game directory:** - -```bash -npx loader-client --dir ./dist/client -``` +The live-reload WebSocket stays plain `ws://`, on `localhost`. Live reload works only in a browser +on the machine that runs the loader. diff --git a/docs/docs/overview.mdx b/docs/docs/overview.mdx index bbe2794..3e22dc4 100644 --- a/docs/docs/overview.mdx +++ b/docs/docs/overview.mdx @@ -1,55 +1,48 @@ --- title: Loader -description: Overview of the @nanoforge-dev/loader packages +description: What runs in the browser and on the server when you run nf start or nf dev --- -## What is the Loader? +## What is the loader? -The NanoForge Loader is the runtime layer that starts a NanoForge project. It is invoked by the [NanoForge CLI](https://github.com/NanoForge-dev/CLI) when you run `nf start`, and handles both sides of a game simultaneously: the **client side** delivered to the browser and the **server side** running in Bun. +The loader is the runtime behind [`nf start`](/docs/cli/commands/start) and +[`nf dev`](/docs/cli/commands/dev). You don't install or run it yourself: the CLI includes it and +starts one loader per app of your project. -The Loader is a monorepo of three packages, each with a distinct responsibility at runtime: +| Part | Runs | Does | +| ------------------------- | ----------------------- | ---------------------------------------------------------------------------------------- | +| [Client loader](./client) | on your machine, in Bun | Serves the loading page and the client build to the browser, and the client's variables. | +| [Loading page](./website) | in the player's browser | Downloads the game files into a browser cache, then starts the client's `main.js`. | +| [Server loader](./server) | on your machine, in Bun | Runs the server build's `main.js` in a child process, and restarts it on change. | -- **`loader-client`** — the Bun HTTP server that serves the browser with the loader UI and the compiled game files. -- **`loader-server`** — the Bun process that runs the compiled server game code in an isolated worker. -- **`loader-website`** — the browser application that displays the loading screen, caches game files locally, and bootstraps the game. +A game without a server has only the client loader and the loading page. -The NanoForge CLI orchestrates all three; as a game developer you never interact with these packages directly. +## Ports -## Installation +| Port | Used by | Set with | +| ---------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| 3000 | the client loader (the page players open) | `nf start --port`, or `port` in the client's `nanoforge.config.ts` | +| a free port, picked at start | the live-reload WebSocket of `nf dev` | nothing: the loading page gets it from the client loader | +| 4444 (TCP), 4445 (UDP) | your game's network, in a project made by `nf new --server` | the `.env` file: see [environment variables](/docs/cli/configuration/environment-variables) | -```bash -npm install -g @nanoforge-dev/cli -``` +The server loader opens no port of its own: your server code opens the ones it needs. -The loader packages are dependencies of the CLI and do not need to be installed separately. If you want to invoke the loaders directly outside of the CLI: +## Environment variables -```bash -npm install @nanoforge-dev/loader-client -npm install @nanoforge-dev/loader-server -``` +Each loader passes the `NANOFORGE_*` variables that `nf start` gives it to your game, with the +prefix removed: `NANOFORGE_CLIENT_SERVER_ADDRESS` in `.env` +reaches the client game as `SERVER_ADDRESS`. The client's +variables are sent to the browser, so anyone can read them. The rules are on the +[environment variables](/docs/cli/configuration/environment-variables) page. -Then run them with: +## Troubleshooting -```bash -npx loader-client --dir .nanoforge/client -npx loader-server --dir .nanoforge/server -``` - -## Packages - - - - Bun HTTP server that serves the loader website and the compiled client game files. Exposes the - manifest, environment variables, and game assets. Supports HTTPS and hot-reload. - - - Browser application bundled as static assets. Displays the loading screen, downloads and caches - game files via the browser Origin Private File System, then bootstraps the game. - - - Bun process that runs the server-side game code in an isolated worker process. Supports - live-reload by restarting the worker on file changes. - - - -see the [changelog](https://github.com/NanoForge-dev/Loader/blob/main/CHANGELOG.md) for the full release history. +| What you see | Why, and what to do | +| ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Storage issue: The game must be hosted with SSL (HTTPS) to enable local storage and load properly.` | The page is not in a secure context: it was opened over HTTP on an address other than `localhost`. Use `localhost`, or [serve over HTTPS with nf start](/docs/cli/commands/start#https). | +| `Could not find main function` | The client build has no `main.js` exporting `main`. Run `nf build`, and keep the entry file named `main.ts`. | +| The server log shows `No main.js found` | Same, for the server build. | +| `Game file not found : ` | A file listed when the page loaded was removed before it was downloaded, usually by a rebuild. Reload the page. | +| The page shows old code after a rebuild | Reload the page. If it persists, clear the site's data in the browser (storage and cache) to empty the loader's cache. | +| The page doesn't reload with `nf dev` | The live-reload WebSocket address is `localhost` on a free port. Live reload works only in a browser on the machine that runs `nf dev`: reload by hand elsewhere. | +| An error message on the loading screen | The loading page shows errors thrown while loading or running the game. Open the browser console for the stack trace. **Back home** reloads the page. | diff --git a/docs/docs/server.mdx b/docs/docs/server.mdx index 55320f1..b91be7a 100644 --- a/docs/docs/server.mdx +++ b/docs/docs/server.mdx @@ -1,91 +1,44 @@ --- -title: Server -description: Bun process that runs the server-side game code of a NanoForge project +title: Server loader +description: The Bun process that runs the server build of your game --- -## Overview +## What it does -`loader-server` is a Bun process that runs the compiled server-side game code. It scans a built game directory, locates `/main.js`, then forks an isolated child process — the worker — that loads the game and calls its exported `main()` function. The parent process manages the worker lifecycle and restarts it when files change in watch mode. +The server loader (`@nanoforge-dev/loader-server`) runs the server app of your game. +[`nf start`](/docs/cli/commands/start) starts one for each server app, pointed at the app's build +folder (`-d, --dir `, required). -## Usage +It lists the files of the build folder, finds `main.js` at its root, and starts a child process +(the worker) that loads `main.js` and calls its `main` function. When the worker exits (a crash, +or a `process.exit()` in your game), the server loader stops too, even with `--watch`. -```bash -nf start -``` +## What your server's main receives -Or invoke directly after building the loader packages: +| Field | Type | Description | +| -------- | ------------------------------------- | -------------------------------------------------------------------------------------- | +| `files` | `Map` | Each file of the build (`/map.json`) mapped to its full path on disk. | +| `env` | `Record` | The server's environment variables, without the `NANOFORGE_` prefix. | +| `editor` | object, optional | Set only when the NanoForge editor runs the game: the channel between game and editor. | -```bash -npx loader-server --dir .nanoforge/server -``` +The `main.ts` that `nf new` generates passes this object to `app.init`. -## How it works +`files` is built once, when the loader starts. A file added to the build later is not in it until +the loader restarts (stop and start `nf dev` or `nf start`). -The server loader runs as two processes: +## Restart on change -1. **Server** (`server.js`) — the entry point. Scans the game directory, finds `/main.js`, and forks the worker. In `--watch` mode it restarts the worker on any file change. -2. **Worker** (`worker.js`) — an isolated child process forked by the server. It requires the game's `main.js` via `createRequire` and calls `main({ files, env })` to start the game. +With `--watch` (set by [`nf dev`](/docs/cli/commands/dev)), the loader watches the build folder. +When a file changes, it stops the worker and starts a new one, with a clean state. Changes closer +than 100 ms count as one restart. -This two-process split ensures that a game crash or `process.exit()` call in the game code does not terminate the loader itself. +Players connected to the old worker lose their connection when it restarts. -## Options +## Common errors -| Option | Type | Default | Description | -| ----------------- | --------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -| `-d, --dir ` | `string` | `.nanoforge/server` | Directory of compiled server game files | -| `--watch` | `boolean` | `false` | Enable file watcher and worker restart on change | - -## Watch mode - -When `--watch` is enabled, the loader watches the game directory recursively. On any file change the running worker is killed and a new worker is immediately forked with a clean state. Changes are debounced to 100 ms to avoid redundant restarts during bulk builds. - -## Environment variables - -The server loader collects all environment variables whose name starts with `NANOFORGE_`, strips the prefix, and passes the resulting object to the game via the `env` field of `main()`: - -``` -NANOFORGE_MY_VAR=hello → { MY_VAR: "hello" } -``` - -The game receives these inside the worker: - -```ts -export const main = async ({ - files, - env, -}: { - files: Map; - env: Record; -}) => { - console.log(env.MY_VAR); // "hello" -}; -``` - -## Game interface - -The worker calls the game's `main` export with the following shape: - -| Field | Type | Description | -| ------- | ------------------------------------- | --------------------------------------------------------------------------- | -| `files` | `Map` | Map of logical game paths (`/main.js`, etc.) to their full filesystem paths | -| `env` | `Record` | Environment variables forwarded from the host (prefix stripped) | - -## Examples - -**Start with a custom game directory:** - -```bash -npx loader-server --dir ./dist/server -``` - -**Start in watch mode:** - -```bash -npx loader-server --watch -``` - -**Pass environment variables to the game:** - -```bash -NANOFORGE_PORT=4000 NANOFORGE_DEBUG=true npx loader-server -``` +- **`No game dir specified`**: the loader was started without `-d`. `nf start` always passes it; + check the server's `out.dir`. +- **`No main.js found`**: the build folder has no `main.js` at its root. Run `nf build`, and keep + the server entry file named `main.ts`. +- **An error thrown by your game**: the worker prints it and exits, and the server loader stops + with it. Fix the error, then restart `nf dev` (or `nf start`). diff --git a/docs/docs/website.mdx b/docs/docs/website.mdx index 8c66062..bfdb8b4 100644 --- a/docs/docs/website.mdx +++ b/docs/docs/website.mdx @@ -1,62 +1,69 @@ --- -title: Website -description: Browser application that loads and bootstraps a NanoForge game +title: Loading page +description: The page that downloads, caches and starts your game in the browser --- -## Overview +## What it does -`loader-website` is a browser application (HTML + TypeScript) bundled as static assets and served by `loader-client`. It is the loading screen the player sees before the game starts. It fetches the game's file list from the server, downloads and caches each file locally in the browser, then dynamically imports the game entry point and starts it. +The loading page (`@nanoforge-dev/loader-website`) is what players see while the game loads. The +[client loader](./client) serves it at `/`. It downloads the game files into a cache in the +browser, so the next visit downloads only what changed, then starts the game. -This package is a dependency of `loader-client` and is not consumed directly. +## Loading steps -## Loading sequence +The text under the logo shows the current step: -When the browser opens the loader URL, the following steps happen in order: +1. **Fetching manifest**: it asks `/manifest` for the list of files, with their hash and size. +2. **Cleaning cache game files**: on the first visit (or after you clear the site's data), it + empties the cache. +3. **Verifying application integrity**: it compares each file with the cached copy. Files no + longer in the list are removed from the cache. +4. **Download n / total: [file]**: it downloads only the new or changed files, with a progress + bar. +5. It imports `main.js` from the cache and fetches `/env`. +6. **Starting game**: it calls the `main` function of `main.js`, then fades the loading screen + out. -1. **Fetch manifest** — requests `/manifest` to get the game version and the list of files to download. -2. **Verify cache** — compares each file's hash and size with the copy cached in the browser's Origin Private File System (OPFS), and removes cached files no longer in the manifest. The loading screen shows _"Verifying application integrity"_. -3. **Download files** — downloads only new or changed files from `/game/*` and writes them into OPFS. The loading screen shows a progress bar and the name of the current file. -4. **Fetch environment** — requests `/env` to get the `NANOFORGE_*` variables forwarded by `loader-client`. -5. **Bootstrap game** — dynamically imports `/main.js` from the local OPFS cache, calls its exported `main({ files, env, container })`, and fades out the loading screen. +If a step fails, or the game throws an error later, the loading screen comes back with the error +message and a **Back home** link that reloads the page. -If any step fails, the error message is displayed on screen instead of a blank page or an unhandled rejection. +## What your client's main receives -## Game interface +The loading page calls the `main` export of the client's `main.js` with one object: -`loader-website` calls the game's `main` export with the following shape: +| Field | Type | Description | +| ----------- | ------------------------------------- | ------------------------------------------------------------------ | +| `files` | `Map` | Each game file (`/player.png`) mapped to a URL of its cached copy. | +| `env` | `Record` | The client's environment variables, from `/env`. | +| `container` | `HTMLDivElement` | The element the game renders into. | -| Field | Type | Description | -| ----------- | ------------------------------------- | ------------------------------------------------------------------------------ | -| `files` | `Map` | Map of logical game paths (`/assets/sprite.png`, etc.) to their OPFS blob URLs | -| `env` | `Record` | Environment variables fetched from `/env` (prefix stripped) | -| `container` | `HTMLDivElement` | The DOM element the game should render into | +The `main.ts` that `nf new` generates passes this object to `app.init`, so you rarely read it +yourself. -## Watch mode +## Secure context -When `loader-client` starts with `--watch`, the manifest response includes a `watch.url` WebSocket URL. `loader-website` connects to that URL and listens for messages. When it receives an `update` message it reloads the page, giving the developer an automatic live-reload experience. +The cache uses the browser's +[Origin Private File System](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system), +which only works in a +[secure context](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts): -## Error display +- `http://localhost:3000` works; +- `http://192.168.1.20:3000` or `http://my-server:3000` does not; +- any `https://` address works. -`loader-website` installs global handlers for `error` and `unhandledrejection` window events. If the game throws or rejects after starting, the error message is surfaced on the loading screen instead of failing silently. +Outside a secure context, the page stops at once with: -## Requirements - -`loader-website` uses the browser's [Origin Private File System](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system) (OPFS) API to cache game files locally. This API is only available in a **secure context** — either `localhost` or a page served over HTTPS. - -Start `loader-client` with `--cert` and `--key` to enable HTTPS when running on a non-localhost address. - -## Examples - -The website loader is not invoked directly. Start it via the CLI or `loader-client`: - -```bash -nf start +```text +Storage issue: The game must be hosted with SSL (HTTPS) to enable local storage and load properly. ``` -Or: +Serve the game over HTTPS to open it from another machine: see +[HTTPS with nf start](/docs/cli/commands/start#https). -```bash -npx loader-client --port 3000 --cert ./certs/server.crt --key ./certs/server.key -``` +## Cache + +The cache belongs to the site's address (the origin). Two games served on the same address and +port share it: the second one replaces the files of the first. -Then open the printed URL in a browser. The loader screen will appear, followed by the game. +To empty it, clear the site's data in the browser (storage and cache). The next load downloads +every file again.