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, sodata.setgives 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 prefixedgml: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,removeorclearthe 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 (setresolvesfalse). - 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
| Call | Result |
|---|---|
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 Godotdata_*wrap the same store (Unity, Godot).