# Architecture

How your game runs on BGS.

![Architecture](/guide/architecture.png)

The preferred way to publish a game is to upload its built engine package (`.tgz`)
and viewer bundle directly to BGS. BGS installs the engine and hosts the viewer,
which runs inside a sandboxed iframe. npm registry packages and external viewer
URLs remain available as alternatives. See [Adding a game](./adding-a-game.md).

Keep the engine usable independently of the viewer, even if they share a package.

## Your game and BGS

Your game has two parts:

- **Engine:** Runs on BGS. It validates moves, applies the rules, and returns the updated game state.
- **Viewer:** Runs in the player's browser. It displays the state and sends player actions through the
  [viewer API](./viewer-api.md).

BGS handles accounts, saving game state, [clocks](./timing.md), and sending updates to players.

## How engines are loaded and run

Engines use the npm package format but do not need to be published on npm. Upload
a built `npm pack` tarball through BGS; the game-server installs it from BGS storage
into an isolated `games/` directory. Installing `name@version` from the npm registry
is also supported. Runtime dependencies still resolve through their configured sources.

Engine changes load without restarting the server. Creating a new game version
affects new games; replacing the engine package on an existing version also updates
ongoing games on that version. See [updating ongoing games](./adding-a-game.md#hot-swapping-ongoing-games).

Engine requirements:

- **Execution time:** Keep engine calls fast, including move validation and available-move generation.
  `move` and `moveAI` have a 10-second time limit by default. A timeout fails the call without applying the move.
- **Game data:** Return JSON-serializable `GameData`: plain objects, arrays, strings, finite numbers, booleans,
  and `null`. Convert values such as `Map`, `Set`, or class instances to plain data before returning them.

## How the viewer is served

Upload the pre-built viewer bundle and any CSS or other supporting files to BGS,
which serves them from its storage. BGS does not build game source. An external
bundle URL, such as a CDN URL, is also supported. The api wraps the viewer in a small
page (handling script/style dependencies and [dark mode](./viewer-api.md#dark-mode)) that the web app iframes
with a restrictive `sandbox` attribute. All viewer ↔ platform communication goes through the
[viewer API](./viewer-api.md) event bridge — the viewer never talks to the api or game-server directly.

## How a move flows

1. The viewer calls `move(payload)`; the library sends the protocol event and the web app forwards it to the game-server.
2. The game-server loads the game's engine version, calls `move(data, move, player)`, then
   `toSave`, `logSlice`, `scores`, `currentPlayer`… and stores the new JSON state only if `toSave` returns a state.
3. Clocks are updated, notifications (and [bot](./bots.md) auto-play) are scheduled, and the api pushes the
   update to every connected client over websocket.
4. The library fetches updates and calls `onState` by default. For incremental updates,
   use its [`onUpdate` and `onLog` callbacks](./viewer-api.md#incremental-logs) with `fetchLog`.
