# Build a game

> Build a project as a standalone game for Windows, macOS or Linux from the editor, the CLI or GitHub Actions, and upload it to Steam.

Source: https://feather-engine.com/docs/shipping/export/

A build turns the open project into a game that players run without the editor. It contains the
native Feather player, a copy of the project and the assets it uses. The player runs the same
scripts, Blueprints and physics as Play in the editor.

The player is the same program for every game, so a build compiles no game code. Building is mostly
copying and checking files, and takes seconds once a player is available. The editor, `feather-cli`
and GitHub Actions all use the same build pipeline.

## Build from the editor

1. Save any pending edits. A build validates the whole project and its scripts first, and stops on
   the first error.
2. Open the **Build** window: choose **File → Build game…**, or **Window → Build · Windows, macOS,
   Linux**.
3. Pick a platform. Each button shows **This computer**, **Runtime ready** or **Needs a runtime**;
   hover over it to see where its player comes from.
4. Adjust the **Settings** section if you need to (see below). The window shows the output folder.
5. Click **Build \<name\> for \<platform\>**. The **Build log** shows progress.
6. When it finishes, the window shows the time, file count and size, with **Show in folder**,
   **Run game** (for this computer's platform) and **Copy path**.

The **Build** button in the toolbar, and **File → Build for this computer**, build straight away for
the platform selected in the Build window (this computer by default). Only one build runs at a time.

## Build settings

Settings are stored in the project and are the same in the Build window and the `build.get` /
`build.set` commands.

| Setting | Default | Notes |
| --- | --- | --- |
| **Game name** (`game_name`) | The project name | Name of the app and its executable. 1–64 characters, without `/ \ : * ? " < > \|`, not starting with a dot and not a reserved Windows name |
| `version` | `0.1.0` | Three numbers, such as `1.2.0` |
| `build_number` | `1` | 1 to 1,000,000. Increase it with every release (macOS `CFBundleVersion`) |
| **Bundle id** (`bundle_id`) | `com.feather-engine.<game>` | Reverse-DNS app id, such as `com.studio.game` |
| `profile` | `release` | `release` is optimised for players. `development` compiles faster and runs slower |
| **Output folder** (`output_dir`) | `builds` | A folder inside the workspace. In the editor, it is relative to the project's folder |
| **Make a zip** (`archive`) | Off | Also write a `.zip` (`.tar.gz` for Linux) beside the build |

## What a build contains

Each build is written to `<output folder>/<game>-<platform>`, for example `builds/my-game-windows`.
Development builds add `-dev`.

| Platform | Layout |
| --- | --- |
| macOS | `<Name>.app`, with the project and `assets/` inside `Contents/Resources/game/` |
| Windows | `<Name>.exe` with `project.feather.json` and `assets/` beside it. Release builds open no console window |
| Linux | `<Name>` with `project.feather.json` and `assets/` beside it |

Every build also contains `README.txt` (how to launch it), `licenses/`, `release-manifest.json`
(every file with its size and SHA-256, plus the platform, profile and version) and `SHA256SUMS`. To
check a build, run `shasum -a 256 -c SHA256SUMS` inside its folder.

On a Mac, the macOS app is code-signed ad hoc. Pass a signing identity with `sign_identity` (or the
`FEATHER_SIGN_IDENTITY` environment variable) to sign with it; a named identity also turns on the
hardened runtime. A macOS app built on another platform is not signed.

How a build stays reliable:

- Every asset is hash-checked as it is copied. A standalone texture missing from the asset cache is
  left out with a warning instead of failing the build; import it again to include it.
- The player's executable must match the target platform, so a Windows player can never end up in
  the Linux build.
- The build is assembled in a hidden folder and swapped in at the end. A failed build leaves the
  previous one untouched. Only a previous Feather build (a folder with `release-manifest.json`) is
  ever replaced; any other folder in the way is refused.

## Where the player comes from

The first source that applies is used. `game.platforms` and the Build window's platform buttons show
which one each platform would use.

| Source | When |
| --- | --- |
| A player you supply | `player` (`--player` on the CLI). It must be inside the workspace |
| Built with Cargo | This computer's platform, from a source checkout (`cargo build --locked --no-default-features --bin feather-player`, with `--release` for release builds). Only the first build takes minutes |
| The bundled player | This computer's platform, with the editor app's own player |
| A player runtime | Any platform: `feather-player` (or `feather-player.exe`) in `runtimes/<platform>/` in the Feather data folder, or in `$FEATHER_RUNTIMES/<platform>/` |

There is no cross-compiling. To build for another platform, put that platform's player in the
runtimes folder; a `feather-player-<platform>` file from a Feather release works too. The Build
window's **Build on GitHub** section shows the folder's path.

## Run the game

Players double-click `<Name>.app`, `<Name>.exe` or `<Name>`, and keep the whole folder together.
From a terminal you can also pass a project path to the player:

```bash
feather-player path/to/project.feather.json
```

The game starts in the project's start level. **Escape** opens the pause menu, with **Resume**,
**Save game**, **Load game**, **Settings** and **Quit to desktop**, unless the game draws its own
menu. Settings, saves and crash reports are written to the Feather data folder, never into the game
folder, so replacing a build keeps them. See [Saves and settings](/docs/shipping/saves-and-settings/).

### Player options

| Option | Effect |
| --- | --- |
| `PROJECT` | Run this project instead of the one beside the player |
| `--verify-run JSON` | Run, write a verification report and exit; see [Profiler](/docs/shipping/profiler/) |
| `--screenshot PNG` | With `--verify-run`, also save a screenshot |
| `--record DIR` | Save every frame as a numbered JPEG in `DIR` |
| `--version` | Print the player version |

From a source checkout you can run any saved project with the player directly:

```bash
cargo run --no-default-features --bin feather-player -- examples/paper-shredder/project.feather.json
```

## Build on GitHub

A game can live in its own GitHub repository, without the engine source. GitHub downloads the
prebuilt Feather tools and builds Windows, macOS and Linux versions; nothing is compiled.

1. Save the project, then in the Build window's **Build on GitHub** section click **Set up GitHub
   builds** (or run `game.setup_github`). It writes `.github/workflows/feather-build.yml` at the
   repository root, the nearest folder with `.git` (otherwise the project's folder: run `git init`
   there), and adds the build folders to `.gitignore`.
2. Commit the workflow with the project, including its `assets/` folder, and push. The workflow
   checks out Git LFS files, so large models, textures and sounds can be stored in LFS.
3. While the engine repository is private, add a repository secret `FEATHER_ENGINE_TOKEN`: a
   fine-grained token with **Contents: read** on the engine repository.
4. In GitHub, open **Actions → Build game → Run workflow**. You can choose the platforms, a version
   and whether to upload to Steam. Each platform's build is attached to the run as a `game-<platform>`
   download.
5. Push a tag such as `v1.0.0` to build that version and publish it as a GitHub release (and upload it
   to Steam, when Steam is set up).

The workflow is pinned to the Feather version of the editor that wrote it, so CI builds with what you
tested. Run **Set up GitHub builds** again after updating Feather; it refreshes a workflow Feather
wrote, and refuses to replace any other file unless you pass `overwrite`. Windows and Linux are
packaged on a Linux runner and macOS on a Mac runner, so the app is signed. Each runner checks the
downloaded tools against the release's `SHA256SUMS`.

Any other CI can run the same build with `feather-cli build`; the Build window shows the exact
command.

## Upload to Steam

Set the ids from Steamworks (**SteamPipe → Depots**) in the Build window's **Steam** section or with
`steam.set`. The GitHub workflow does the upload.

| Setting | Range | Default | Notes |
| --- | --- | --- | --- |
| **App ID** (`app_id`) | 0 to 4,294,967,295 | 0 | 0 turns Steam uploads off |
| **Windows depot**, **macOS depot**, **Linux depot** (`depot_windows`, `depot_macos`, `depot_linux`) | 0 to 4,294,967,295 | 0 | One depot per platform; 0 leaves that platform out. Each platform needs its own depot, and a depot id cannot be the app id |
| **Live branch** (`branch`) | Letters, digits, `-`, `_`; up to 64 | `beta` | Branch the upload goes live on. Empty only uploads. `default` and `public` are refused: set the default branch live in Steamworks yourself |

With an App ID set, at least one depot is required. The workflow also needs two repository secrets:
`STEAM_USERNAME`, a Steam account for builds with permission to edit and publish the app, and
`STEAM_CONFIG_VDF`, the base64 of an authorised steamcmd `config/config.vdf`, so CI never needs a
Steam Guard code. Tagged builds upload automatically when the App ID and both secrets are set;
without the secrets the upload is skipped with a warning. **Run workflow** with **Upload to Steam**
ticked uploads any build.

:::caution
The workflow reads the Steam settings directly from the project file. Keep a game you upload to Steam
as a single-file project (`project.feather.json`): in a `.feather` project folder, the settings are
in a separate file and the upload is skipped.
:::

## Limits

- One build at a time in the editor.
- No cross-compiling: other platforms need a player runtime or a GitHub build.
- Builds have no app icons yet, and are not notarised (macOS) or Authenticode-signed (Windows).
- Steam support covers uploads only.

## Automate it

```bash
# Which platforms can this machine build for, and where would each player come from?
feather-cli --workspace ./my-game --project project.feather.json command game.platforms

# Name and version the game, then build it for this computer with a zip.
feather-cli --workspace ./my-game --project project.feather.json command build.set \
  --json '{"patch":{"game_name":"My Game","version":"1.0.0"}}' --save project.feather.json
feather-cli --workspace ./my-game --project project.feather.json build --archive

# Build for Windows with a Windows player you put inside the workspace.
feather-cli --workspace ./my-game --project project.feather.json build \
  --platform windows --player tools/feather-player-windows.exe --version 1.0.1

# Write the GitHub workflow and set up Steam.
feather-cli --workspace ./my-game --project project.feather.json command steam.set \
  --json '{"patch":{"app_id":480,"depot_windows":481,"depot_macos":482,"depot_linux":483}}' \
  --save project.feather.json
feather-cli --workspace ./my-game --project project.feather.json command game.setup_github
```

`build` runs `game.export`, with `--output`, `--profile` and `--sign-identity` as its other options.
In a headless run, `--version`, `--build-number` and `--name` override the build settings for that
build only; they are not saved. Headless, `game.export` returns the report (`path`, `player`,
`files`, `bytes`, `seconds`, `archive`, `warnings`); in a live editor it returns a `job_id` to poll
with `job.inspect`, and `build` waits for it. To build from the editor binary without opening a
window, use `--export`; see [Install](/docs/getting-started/install/).
