Skip to content

Latest commit

 

History

History
118 lines (74 loc) · 8.97 KB

sync.md

File metadata and controls

118 lines (74 loc) · 8.97 KB

Synchronization of shared data between game servers

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.

Connection

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.

Authentication

Game server to sync service

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.

Sync service to game server

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
challenge84bc01d9f8e0182e8
3 Sync service Game server SYNC_AUTH
responseeyJub25jZSI6Ijg0YmMwMWQ5ZjhlMDE4MmU4IiwiZm9yIjoic3luYyJ9.ob5rzI02ehC1MqBeiYVE3EG/GdtejMO46IVLwDPPO8tNv5MaRHAy8+F+9COkks+NFrU8pp/nL1GaD8FYylaYCQ

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"}

Initialization

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
sequence1
timestamp1598733150123
2 Sync service Game server SYNC_INIT
sequence1234
timestamp1598733151234
serverIdeu-ffa1
wsEndpointeu.airmash.online/ffa

Data update from sync service to game server

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
activetrue
typeuser
idf954d1bba02ad02d
2 Sync service Game server SYNC_UPDATE
completetrue
typeuser
idf954d1bba02ad02d
data{"earnings":78854,"totalkills":859,"totaldeaths":607}

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
activefalse
typeuser
idf954d1bba02ad02d

Data update from game server to sync service

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
sequence1234
typeuser
idf954d1bba02ad02d
data{"totaldeaths":1}
timestamp1598167495623
event["victim",{"killer":{"name":"Deceitful Zoilist","flag":"jolly","bot":true}}]
2 Sync service Game server SYNC_ACK
sequence1234
result0
3 Game server Sync service SYNC_UPDATE
sequence1235
typeuser
idf954d1bba02ad02d
data{"score":1234,"totalkills":1}
timestamp1598167517551
event["killer",{"victim":{"name":"jam 🥑","flag":"rainbow","user":"d20da20abb1d459f"}}]
4 Sync service Game server SYNC_ACK
sequence1235
result0

Object types

Users

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