# Saves and settings

> How games save progress into slots, how autosaves and backups work, and where per-player settings are stored.

Source: https://feather-engine.com/docs/shipping/saves-and-settings/

Feather 2 games have built-in save slots and a per-player settings menu. A save records everything a
running game changed, and settings record each player's preferences. Both live in the player's
Feather data folder, separate from the game's read-only files.

## What a save contains

| Part | Contents |
| --- | --- |
| Metadata | Slot, label, real save time, in-game day and minute, play time, and whether a thumbnail exists |
| Level and clock | The current level and game clock |
| Script state | World-script state |
| Objects | Objects spawned at runtime, authored objects that scripts moved or hid, and broken destructibles |
| Terrain | Runtime terrain edits such as craters and digging |
| Player | Position and facing |
| Story | Quest progress and dialogue memory |
| Sections | State that individual systems register, such as inventory, interaction, felled trees, fire, grass, season, fluids and player health |

When a save loads, each system restores its own section. A section missing from an older save resets
that system, and sections the game no longer uses are ignored, so saves stay loadable as a project
grows.

## Reliability

- Saves are written atomically, so a crash never leaves a half-written file.
- Each save carries a SHA-256 checksum, so damage is detected on load.
- The previous save of a slot is kept as `<slot>.bak.json` and loaded automatically if the current
  one is damaged.
- Autosaves rotate through `auto`, `auto-2`, `auto-3` and so on, so one bad autosave never loses the
  whole history.
- The save format is versioned; older saves are migrated on load.

## Configure saving

Saving is configured per project in **Project settings → Saves & autosave**, or with the `saves` object of
`project.game`.

| Setting | Default | Range |
| --- | --- | --- |
| `autosave_minutes` | 5 | 0 to 240 real minutes between autosaves; 0 turns timed autosave off |
| `autosave_each_day` | Off | Autosave at every in-game dawn |
| `autosave_slots` | 3 | 1 to 10 rotating autosaves |
| `manual_slots` | 6 | 1 to 50 slots in the save menu |
| `quicksave_key`, `quickload_key` | `F5`, `F9` | Empty disables |
| `thumbnails` | On | Capture a small screenshot with each save |
| `pause_menu` | On | Offer **Save game** and **Load game** in the pause menu |

Slot names are 1 to 32 letters, digits, `_` or `-`. The name `profile` is reserved for permanent
player progress.

## Save from scripts

| Function | Description |
| --- | --- |
| `save_game(slot)`, `save_game(slot, label)` | Save to a slot |
| `load_game(slot)`, `has_save(slot)` | Load a slot, or check that it exists |
| `autosave()`, `delete_save(slot)`, `latest_save()`, `save_slots()` | Manage slots |
| `open_save_menu(mode)`, `close_save_menu()` | Show or hide the built-in save menu |
| `profile_get(key, default)`, `profile_set(key, value)`, `profile_has(key)` | Progress that persists across runs and saves |
| `on_load()` | A hook that runs after a save has loaded |

See [Scripting](/docs/logic/scripting/).

## Player settings

Players change settings from the pause menu (**Escape**, or the gamepad's pause button) under
**Settings**:

| Setting | Values |
| --- | --- |
| Graphics quality | Low, Medium, High (default). Controls terrain view distance, shadow map size (1024, 2048, 4096), ambient occlusion (High only), bloom (Medium and High) and foliage distance |
| Fullscreen | Borderless fullscreen or windowed |
| Vertical sync | On by default |
| Volume, Music, Effects, Voices | 0 to 2; 1 is the default |
| Mouse sensitivity | 0.1 to 4 |
| Camera shake | 0 to 1 |
| Motion blur | On or off, where the project uses it |
| Language | One of the project's languages |
| Controls | Rebind any input action to a key or gamepad button |

Out-of-range values in a settings file are clamped when it loads.

## Your own menus and options screen

A game can replace the engine's pause menu with its own title and pause screens, and build its own
options screen on the same player settings.

| Function | Description |
| --- | --- |
| `use_game_menu(on)` | The game draws its own title and pause menus: **Escape** reaches `on_key` instead of opening the engine's menu |
| `open_system_menu()` | Opens the engine's menu, with its settings and key bindings |
| `pause_game(on)`, `game_paused()` | Freeze or resume game time: physics, timers and the player stop, while hooks keep running with `dt` 0 |
| `user_settings()` | The player's settings as a map |
| `set_user_setting(key, value)` | Changes one setting. It is saved like a change in the pause menu's **Settings** |
| `quit_game()` | Quits to the desktop. In the editor it stops Play |

`user_settings()` and `set_user_setting` use these keys:

| Key | Value |
| --- | --- |
| `quality` | `"Low"`, `"Medium"` or `"High"` |
| `fullscreen`, `vsync`, `motion_blur` | `true` or `false` |
| `master_volume`, `music_volume`, `sfx_volume`, `voice_volume` | 0 to 2 |
| `mouse_sensitivity` | 0.1 to 4 |
| `camera_shake` | 0 to 1 |

Numbers outside the range are clamped. An unknown key, or a value of the wrong type, is a script
error, so a typo shows up at once.

```rust
// A world script that runs its own pause menu.
fn start() {
    use_game_menu(true);
}

fn on_key(key) {
    if key == "ESCAPE" {
        pause_game(!game_paused());   // show or hide your pause screen here too
    }
    if game_paused() && key == "M" {
        let music = user_settings().music_volume;
        set_user_setting("music_volume", if music > 0.0 { 0.0 } else { 1.0 });
    }
    if game_paused() && key == "Q" {
        quit_game();
    }
}
```

The engine's own menu opens only in a standalone game, not during Play in the editor's viewport.

## Where data is stored

| File | Contents |
| --- | --- |
| `settings.json` | Player settings |
| `bindings.json` | Rebound controls |
| `language.json` | The chosen language |
| `saves/<game>/<slot>.json` | Save slots, with `.bak.json` backups and `.png` thumbnails |

While you play in the editor, these files are written to the Feather data folder itself. An exported
or packaged game writes them to its own folder inside the Feather data folder, named
after the project: `games/<project-slug>-<hash>/`. On macOS that is under
`~/Library/Application Support/Feather v2/`. Moving or replacing the game files does not touch this
data. Set `FEATHER_DATA_ROOT` to an absolute path to redirect it, for example for testing.

## Automate it

Agents can inspect saves made while playing in the editor, which are stored under
`saves/<project-slug>/` in the editor's data folder:

```bash
feather-cli --workspace ./my-game --project project.feather.json command save.list
feather-cli --workspace ./my-game --project project.feather.json command save.inspect \
  --json '{"slot":"auto"}'
```

`save.delete` removes a slot together with its backup and thumbnail. Save behaviour is set with
`project.game`:

```bash
feather-cli --workspace ./my-game --project project.feather.json command project.game \
  --json '{"saves":{"autosave_minutes":10,"manual_slots":12}}' --save project.feather.json
```
