Cloud save

Gamealia.data is a small key-value store per player and per game. Use it instead of localStorage: it follows signed-in players to every device. Your game plays from its own origin (https://<your-slug>.gamealia.app), but previews, the review and older links run it from the shared CDN origin, where localStorage is shared by every game: a game that reads other games' keys fails the autotest.

Limits and where the data lives

  • Values are JSON. The whole save of a game can be at most 1 MB per player, measured as the UTF-8 bytes of all keys together serialized as one JSON object ({"key":value,...}). The SDK, the portal and the API measure it the same way, so data.set gives the same answer in the mock as on the portal.
  • Signed-in players: the save is in their Gamealia account, on every device.
  • Guests: the save stays in that browser, on the portal (not in your game's storage).
  • In the mock (local development): this browser's localStorage, keys prefixed gml:local:.

When the player signs in or out

  • Sign-in: if the account has no save for this game yet, the guest save from this browser moves to the account: it is written there and removed from the browser, so a later account that signs in on the same browser does not get the same progress. The write only happens while the account is still empty (checked by the server), so a second tab or a failed read never overwrites a real save. If the account already has a save, that save is loaded and the guest save is left alone (it never overwrites the account).
  • Sign-out: the game goes back to the guest save in this browser (empty if it moved into an account).
  • A data.set, remove or clear the game sent before the player signed in, out or switched accounts is never applied to the new player's save: if it had not run yet, it is dropped (set resolves false).
  • Either way the SDK drops what it had cached and fires userChange. Load the save again there, so you never write the previous player's progress into the new account:
Gamealia.user.onChange(async () => {
  const r = await Gamealia.data.load();
  if (r.ok) state = r.data.progress ?? newGame(); // same rule as at startup: on !r.ok, do not save
});

The CrazyGames shim reloads SDK.data before it calls your addAuthListener callbacks.

Load first, then save

await Gamealia.init();
const r = await Gamealia.data.load();
if (!r.ok) {
  // Could not read the save (offline, timeout): do NOT overwrite it with a new game.
  showRetry();
} else {
  state = r.data.progress ?? newGame();
}

async function save() {
  const ok = await Gamealia.data.set("progress", state); // false when not saved
  if (!ok) showToast("Could not save");
}

data.get(key) returns null both when there is no value and when it could not be read; prefer data.load() at startup for that reason.

API

CallResult
data.load(){ ok: true, data } or { ok: false, error }
data.get(key)the value or null
data.set(key, value)true when saved, false otherwise
data.getAll()every key and value ({} on failure)
data.remove(key)deletes one key
data.clear()deletes the whole save

Tips

  • Save at checkpoints (level end, settings change), not every frame.
  • Keep it small: store progress and settings, not caches or big blobs.
  • With your own backend, keep server-side data on your server and identify the player with a token (see Player accounts).
  • Engines: Unity Gamealia.Data.* and Godot data_* wrap the same store (Unity, Godot).