floppy-diskSave System

The save system provides a key-value data storage system for persisting player progress and preferences across sessions.

The Save system is a simple key-value store. You give it a string key (like "xp"), and it stores a value that will still be there the next time the player joins.

There are two kinds of save data:

  • Player save: one player's personal data (XP, upgrades, settings, inventory, etc)

  • Game-wide save: shared by everyone in the game (world records, server settings, global counters, etc)

circle-exclamation

Quick start (save + load a stat)

The most common pattern is:

  • Load saved values in ao_start

  • Save again whenever the value changes

Player :: class : Player_Base {
    xp: s64;
    level: s64;

    ao_start :: method() {
        // Defaults are used for brand new players
        xp    = Save.get_int(this, "xp", 0);
        level = Save.get_int(this, "level", 1);
    }
}

add_xp :: proc(player: Player, amount: s64) {
    player.xp += amount;

    // ... your level-up logic here ...

    // Save immediately when it changes
    Save.set_int(player, "xp", player.xp);
    Save.set_int(player, "level", player.level);
}

Player save (per-player data)

Player save is scoped to a single player. Every player has their own isolated key/value data.

Supported types:

  • String: Save.set_string / Save.get_string

  • Integer (s64): Save.set_int / Save.get_int

  • Float (f64): Save.set_f64 / Save.get_f64

  • JSON (advanced): Save.set_json / Save.try_get_json

circle-info

Always provide a sensible default when reading. New players won't have keys yet, and get_* will return your default.

Saving “bigger” data (JSON)

If you have a little bundle of fields (like progress + unlocked things), it's often nicer to save it as one JSON blob.

Only fields marked with @ao_serialize are saved.

circle-info

Save.try_get_json returns false if the key doesn't exist or the saved JSON no longer matches your structure. Treat that as “start fresh with defaults”.

Save versioning (migrating old data safely)

If you ever change your save format, keep a version key and migrate older saves forward.

Game-wide save (shared by everyone)

Game-wide save is shared across the whole game, not per-player.

circle-info

Use Save.increment_game_int for counters that multiple players might update at the same time (kills, joins, rounds played, etc).

Common patterns

Booleans

Save booleans as 0/1:

Key naming

Keys are just strings, so pick names that won't collide later:

  • "xp", "level", "selected_skin"

  • "tycoon.cash", "tycoon.upgrades.mouth_level"

If you have multiple games connected via game parenting (hub + minigames), see Cross-Game Products/Data for how save data can be shared.

Last updated