Player profiles
Every player has one profile in PostgreSQL (the players table): their name, their permissions, their settings and the
data the modules store for them. The game server they are on loads it when they join and saves it while they play.
One owner at a time
A profile is owned by one server at a time. Loading it takes a lock (lock, the owner's SERVER_ID) with a lease of
60 seconds (lock_expires_at) that the owner renews every 10 seconds. Another server cannot load the profile while the lock
is held. If the owner crashes, the lease expires and any server can take the profile over.
When a player switches servers, the proxy connects them to the new server before disconnecting them from the old one. The new server therefore lets the player in first and loads the profile a fraction of a second later, once the old one has saved and released it. Meanwhile the player is held still: they cannot move, chat or interact with a profile that is not there yet.
Saving
- Autosave every minute, and a final save when the player leaves, which releases the lock.
- The profile is serialized on the main thread, so the copy is consistent, and written by background workers.
- Saves of one player are written in order; a burst of autosaves cannot delay the final save of another player.
- A profile that did not change since its last write is not written again.
- Transient database errors are retried. A save that finds the lock taken by another server is reported and not retried.
- On shutdown, the server waits for every queued save.
What a profile holds
- Identity: id and name.
- Permissions: the groups and permissions of the player, see Permissions.
- Settings: personal options that follow the player everywhere, such as the debug mode.
- Module data: whatever the modules store, each under its own key (for example
kits.usagesfor the kit cooldowns). A module that is switched off leaves its data untouched.
Numeric counters that change often (currencies, statistics) are not in the profile but in the vault (player_vault), one
row per player and key, changed atomically in the database.
Seeing it
/debug on shows, on each join and server switch, how long the profile took to load (including the wait for the previous
server) and how long each save took. In the database, select name, lock, lock_expires_at from players; shows which server
owns which player.