Uniplay

Versioning and storage keys#

Versioning#

The v fields are 1, and so is this spec. A breaking change to the manifest, the achievements formats or the saves index bumps the version, and readers must ignore versions they don't understand. Adding optional fields is not a breaking change.

Keys games write#

Each game writes only its own keys, all under its game id:

Key Page
uniplay:achievements:<game id> Achievements
uniplay:saves:<game id> and uniplay:saves:<game id>:<slot id> Saves
uniplay:playtime:<game id> Time played

Keys the Hub owns#

The Hub page keeps the player's profile and its own preferences in the same localStorage. Games may read these keys, for example to greet the player by name, but never write them.

Key Value
uniplay:profile { "v": 1, "id": "3j6ahfn7jzp1mshl", "name": "Ruben", "room": "JBN69G", "avatar": "re-elect-me/marta-velin", "platinum": false, "updatedAt": 1790000000000 }. name is what the player chose on their first visit, at most 20 characters; without it, the Hub shows the intro and asks for a name. id (8–32 of a-z0-9) is random and never changes; room is the player's room code (Buds rooms), which they can replace from Settings. The Hub adds both to older profiles. avatar is optional: null or missing shows the player's initial. Avatars are premade images the Hub ships under /assets/avatars/<set>/<name>.svg (or .png, .webp), with id <set>/<name>; a set is usually named after a game id. Show an avatar only when you know its id, never as a URL from a peer. platinum is optional: true shows the platinum version of the avatar (a metallic ring and badge). The Hub offers it only while every achievement of the game named like the avatar's set is unlocked, and sets it back to false when that stops being true (the game added achievements).
uniplay:sound "on" or "off": menu and intro sound. Missing means on.
uniplay:language "en" or "es": the language of the Hub's own text, from Settings. Missing means the browser's language when the Hub has it, else English. Signed in, the account's language replaces it. Games' own names, descriptions and contents aren't translated.
uniplay:overlay { "v": 1, "windows": { "buds": true, "achievements": true, "notes": false, "feedback": false }, "tab": "buds" }: which windows the overlay shows, switched from its dock (missing means Buds and achievements on, notes and feedback off; feedback only exists where the Hub has accounts), and the one shown on phone-sized screens, which fit one at a time.
uniplay:notes { "v": 1, "notes": [{ "id": "k3v9q0ab", "text": "Code for the vault: 4721", "updatedAt": 1790000000000 }], "current": "k3v9q0ab" }: the player's plain-text notes from the overlay, shared by every game, at most 20 of 10,000 characters each. A note's tab shows its first line.
uniplay:cloud { "v": 1, "enabled": true, "account": { "id": "k3v9q0ab12cd34ef", "nickname": "ruben", "email": "[email protected]" }, "device": "q8w2e7r1t5y3u9i0", "synced": { "<game id>": { "<slot id>": { "rev": 3, "at": 1790000000000 } } }, "shelved": { "<game id>": { "<shelf id>": { "rev": 1, "at": 1790000000000 } } }, "playtime": { "<game id>": { "others": 5400, "sent": 1200 } }, "lastSync": 1790000000000 }: the signed-in account (null when signed out), whether cloud sync is on (missing means on), a random id for this browser, for each slot (and in shelved, each put-aside playthrough) the cloud revision and the updatedAt at its last sync, and for each game's time played the seconds merged in from other devices and the seconds this browser last uploaded. Only where the Hub has accounts. A different account signing in starts synced and shelved afresh.
uniplay:shelves:<game id> and uniplay:shelves:<game id>:<shelf id> Playthroughs put aside with Start a new game (playthroughs). The index is { "v": 1, "shelves": { "<shelf id>": { "label": "Mansion 3", "summary": "$182,000 stolen", "playtime": 4210, "updatedAt": 1790000000000, "slots": 2 } } }: the newest slot's label, summary, time played and last write, and how many slots the playthrough holds. Each bundle is { "v": 1, "shelvedAt": 1790000000000, "slots": { "<slot id>": { "entry": { …index entry… }, "data": "<the slot's data, as stored>" } } }. Shelf ids are random, 16 of a-z0-9. Games must not read or write these keys.
uniplay:view-as-user "on" or "off": whether an admin views the Hub as a user, without admin info (game ids, versions and dashboard links) or the games hidden from players. Missing means off; ignored for everyone else.
uniplay:publisher-dashboard The id of the publisher the publisher dashboard (/publisher/) last managed, for members of more than one. ?p=<id> picks another.
uniplay:library { "v": 1, "games": ["level-dice", "orbion"], "sort": "played", "view": "list", "favourites": ["level-dice"] }: the ids of the games in My Library (added from the Store, or by playing them; without games, the Hub starts it with the games this browser has played, has saves of or marked as favourites; signed in, the games and favourites follow the account, while sort and view stay per browser), whether the games show as a "list" (the default) or a "grid" of square covers, how they're sorted, "played" (most time played, time played; the default), "recent" (last played), "name" or "library" (the catalogue's order), and the game ids the player marked as favourites, which come first in any order. Unknown ids stay, so a game that's away for a while keeps its place.
uniplay:catalogue The community API's last GET /catalogue answer, as it came: the hosted games the Hub lists. The Hub opens with it, so it needn't wait for the API, then replaces it with the fresh answer. Only where the Hub has accounts.