Persisting players
Who a player is, and what is kept for them between visits.
Who a player is comes from the accounts addon, and what is kept for them from persist, which loads records into a truth as rows and writes them back as they change.
The manager scene
- In
games/<game>/<game>.scene.json, giveworld.accounts:name{ min, max, pattern },secret{ min, max },scrypt{ N, r, p, keylen, salt },token{ bytes, ttl }andlimit{ connection, name, window }. Every child scene reads the same rules, so a login screen can say a name is too short before it asks. - Give
world.persist:kinds, each kind's{ key, load };every, the fewest steps between two writes of one row;file, the store's name; andbusy, the milliseconds a write on node waits for another.keyisuid.loadisbootfor records loaded when the scene starts, orclaimfor records loaded when a rule asks. - Add the
persistprefab,{ "claims": {} }, and add the login scene toscenemanager.scenesas itsentry. - Name
fileafter the game: two games on one origin with one name share one database. On node"file": "<game>.db"lands at the repository root with<game>.db-waland<game>.db-shmbeside it; add all three to.gitignore.
The login screen
- Make the entry scene's login kind:
<login>.scene.json,<login>.sim.ts, and the kind'slogin.sim.tsandlogin.client.ts, with its words in its definition inassets/<game>.json, drawn on a ui panel. login.client.tsasksrelogin(link)once with the kept token. With no token, or one refused, it shows a form that callsregister(link, name, secret)orlogin(link, name, secret).- Each ask returns
{ account }or{ why, wait? }. Give the screen words for eachwhy:wrong,taken,name,secret,token,slow(waitis the milliseconds until one more ask),busy,signed,down,elsewhereandclosed. login.sim.tssends the seat on with the scenemanager'sgo, to the scene its definition names asnext.- When the account logs in on another connection, this one is told
elsewhereand logged out. Every scene the seat can reach shows that and offers to log in again; it asks only when the player says so.
A connection is seated on the entry scene and sent its packets from the first step, logged in or not. Anything else it sends before it logs in is dropped. In index.html every connection is logged in at once as local, and its records are kept in IndexedDB.
The scene that keeps records
- Add the actor
{ "is": "persist" }to the scene file. Its sim imports the kind from#plugins/persist/persist.sim.tsand names it with each record kind. - Give the
playerprefab a field that says it has asked, and every field of the record it plays with (a level, gold, an inventory) at the values a new account starts with. - The
playerrow'sbeforecallsclaim(w, kind, match)and marks that it asked. On a later step it finds the record withw.all(kind)and copies its fields onto itself, or spawns the record from its own fields when there is none. - From then the
playerrow plays on its own fields. Each change to a kept field is written back through an act of the record's kind that lists noopen, so no seat can send it. - List every field only the owner may see in the scene's
securityblock underme, on the record and on theplayerrow.
When records are written
| Row | Written |
|---|---|
| Changed since its last write | At most once every every steps |
| Spawned by this truth | In the step it was spawned |
| Loaded, then changed | At once |
urgent: true in its data | That step, and the flag is deleted |
| Ended | A last time, or dropped when its data has forget: true |
| Any changed row after a seat closed | The next step |
One step's writes go in one transaction. A rule sets urgent on every row a transfer touches. Records name each other and accounts by uid, never by row id or name.
The store and local keep nothing for a player: the store holds page-only values such as volume and rebound keys, and starts again on a reload.
Checking it
games/<game>/<game>.sim.test.tsboots the manager oversqlitein a temporary file and checks that a logged-in seat moves on, that itsplayerrow holds the record's fields, that a visit is on disk and counts again on the next visit, and that a seat sending the write-back act changes nothing.games/<game>/<game>.server.test.tsruns the server twice with a stop between, and checks that a reloaded page logs back in with its token and the count goes on after the restart.- Play
index.htmland see the count go up by one on each reload. Playclient.htmlagainst the server: register, reload, restart the server, and log the account in from a second window to see the first told it was logged in elsewhere.