This document describes a shared data store for game servers, orchestrated by a centralized synchronization service. It is used primarily to store user stats, but is extensible enough to share other data too.
To avoid the overhead of synchronizing all data to all servers, it works on a publish-subscribe model. Game servers will publish updates to the sync service, and subscribe to the subset of logged-in users that are currently playing on the server to receive updates on them from the sync service.
The sync service connects to game servers in airmash-refugees/airmash-games, using the same websocket endpoint that players connect to.
It sends a SYNC_START
message to indicate that this connection is from the sync service, and that any further messages on this connection will be related to data synchronization. This is similar in function to how player connections use an initial LOGIN
or BACKUP
message to denote the connection type.
When the sync service connects to the websocket endpoint of a game server, authentication of that server is implied by a successful TLS connection.
For example, if the sync service connects to https://eu.airmash.online/ffa without error, it accepts that it is communicating with game server eu-ffa1.
A game server first needs to obtain the login service key from https://login.airmash.online/key for authentication to proceed.
Upon receiving a sync connection from sync service (via SYNC_START
), the game server sends a nonce in a SYNC_AUTH
message. The sync service sends back its own SYNC_AUTH
message, with a signed token containing this nonce.
The game server checks that this token is signed with the login service key, and that the nonce matches the one it sent. This binds authentication just to this connection, and prevents replay of a stolen token elsewhere.
Example:
# | From | To | Message type | Data | ||
---|---|---|---|---|---|---|
1 | Sync service | Game server | SYNC_START |
|||
2 | Game server | Sync service | SYNC_AUTH |
|
||
3 | Sync service | Game server | SYNC_AUTH |
|
The token format is the same already used for logged-in players to authenticate to the game server and the settings service: two Base64-encoded strings separated by a dot (.
). The first part is the token data, the second part is the signature for that data.
$ echo 'eyJub25jZSI6Ijg0YmMwMWQ5ZjhlMDE4MmU4IiwiZm9yIjoic3luYyJ9' | base64 -d
{"nonce":"84bc01d9f8e0182e8","for":"sync"}
Once authentication has succeeded, the game server and sync service send each other metadata regarding their current state, in SYNC_INIT
messages.
Of most importance is the sequence
, a monotonically-increasing counter by which data update messages generated by the game server are numbered. The game server sends its next sequence
id, the sync service responds with its last known next sequence
id. The game server uses the larger of the two if there is a difference. These will usually be the same, but the latter may be greater if the game server was shut down without persisting its last known sequence id to disk, or if this is a fresh install where no data was kept. The sync service logs a warning if there are any gaps in the sequence, and will refuse to accept data updates that reuse a previously-seen sequence id.
Both sides send a timestamp
of the current time. This is intended to detect if there is any significant divergence by which any timestamps sent later on (such as the event timestamp in a data update message) may need to be corrected.
The sync service also sends details of the game server id and the websocket endpoint it has connected to, as serverId
and wsEndpoint
respectively. This is for informational purposes.
Example:
# | From | To | Message type | Data | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | Game server | Sync service | SYNC_INIT |
|
||||||||
2 | Sync service | Game server | SYNC_INIT |
|
The game server requests data from the sync service by subscribing to changes on specific objects, using SYNC_SUBSCRIBE
with the active
field set to true, specifying the object type
and id
of interest.
The sync service will send a SYNC_UPDATE
containing the current data for the object with that type
and id
. The complete
field is set to true to signal that data
holds the entire object.
This may be followed by a series of SYNC_UPDATE
with partial updates of the data. For these, the complete
field is set to false.
Example when a user logs in:
# | From | To | Message type | Data | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | Game server | Sync service | SYNC_SUBSCRIBE |
|
||||||||
2 | Sync service | Game server | SYNC_UPDATE |
|
The game server sends an SYNC_SUBSCRIBE
when the object is no longer of interest, but with the active
field set to false.
Example when a user logs out:
# | From | To | Message type | Data | ||||||
---|---|---|---|---|---|---|---|---|---|---|
1 | Game server | Sync service | SYNC_SUBSCRIBE |
|
Data updates from the game server to the sync service are sent using SYNC_UPDATE
. This includes the sequence
id, the object type
and id
, and the data
to update. Also included is the timestamp
when this was updated on the server, and game event
information on the cause - these two fields are informational only.
The sync service responds with a SYNC_ACK
on the outcome of the data update. This contains the sequence
id, and a result
code:
Result code | Description |
---|---|
0 | Successful |
positive | Transient failure, may be retried |
negative | Permanent failure, do not retry |
Unlike SYNC_UPDATE
messages from the sync service to the game server, there is no complete
field indicating if this is completely replaces the data or not. It is assumed that all updates in this direction are partial updates.
Example:
# | From | To | Message type | Data | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | Game server | Sync service | SYNC_UPDATE |
|
||||||||||||
2 | Sync service | Game server | SYNC_ACK |
|
||||||||||||
3 | Game server | Sync service | SYNC_UPDATE |
|
||||||||||||
4 | Sync service | Game server | SYNC_ACK |
|
Each logged in player has a user
object.
Field name | Data type | Update action | Initial value |
---|---|---|---|
id |
string | None | Randomly generated in login service |
earnings |
integer | Addition | 0 |
totalkills |
integer | Addition | 0 |
totaldeaths |
integer | Addition | 0 |