PlayerServerPresence¶
The PlayerServerPresence table tracks which server each player is currently connected to across the entire QS-Bridge cluster. It serves as the single source of truth for player location, enabling dual-login prevention, population counting, and cross-server coordination.
Scope¶
🌍 Global — All servers and the admin panel can observe player presence across the cluster. This global visibility is essential for preventing a player from being connected to multiple servers simultaneously.
Schema¶
| Column | Type | Constraints | Description |
|---|---|---|---|
player_id |
String |
Primary Key | Platform-wide player identifier |
server_id |
String |
Indexed | The server_id of the server the player is connected to |
connected_at |
Timestamp |
— | When the player connected to the current server |
last_heartbeat |
Timestamp |
— | Most recent activity timestamp for staleness detection |
Rust Definition¶
#[spacetimedb::table(public, name = player_server_presence)]
pub struct PlayerServerPresence {
#[primary_key]
pub player_id: String,
pub server_id: String,
#[index(btree)]
pub connected_at: Timestamp,
pub last_heartbeat: Timestamp,
}
Usage Patterns¶
Dual-Login Prevention¶
The primary purpose of this table is to prevent a player from connecting to two servers at the same time. Because player_id is the primary key, each player can have at most one row in the table.
When a player connects to a server, the game hook checks for an existing PlayerServerPresence row:
// Pseudocode — game hook connection flow
fn on_player_connect(player_id: &str, this_server_id: &str) {
if let Some(existing) = PlayerServerPresence::filter_by_player_id(player_id) {
if existing.server_id != this_server_id {
// Player is connected elsewhere — reject or handle transfer
reject_connection("Already connected to another server");
return;
}
}
// Register presence
PlayerServerPresence::insert(PlayerServerPresence {
player_id: player_id.to_string(),
server_id: this_server_id.to_string(),
connected_at: Timestamp::now(),
last_heartbeat: Timestamp::now(),
});
}
Player Disconnect¶
When a player disconnects, the game hook deletes their PlayerServerPresence row:
Staleness Detection¶
If a server crashes without gracefully disconnecting its players, their PlayerServerPresence rows will remain — creating "ghost" entries that block the players from connecting elsewhere. The last_heartbeat column enables the gateway or a cleanup process to identify and remove stale entries:
-- Find presence entries with no heartbeat in the last 2 minutes
SELECT * FROM player_server_presence
WHERE last_heartbeat < NOW() - INTERVAL '2 minutes'
Population by Server¶
The server_id index allows efficient queries to count players per server:
-- Count players on each server
SELECT server_id, COUNT(*) as player_count
FROM player_server_presence
GROUP BY server_id
This data is used by the admin panel's population dashboard and can feed into matchmaking or load-balancing logic.
Transfer Coordination¶
During a server transfer (see ServerTransferQueue), the player's PlayerServerPresence row is updated to reflect the destination server once the transfer completes. The presence row acts as a lock — preventing the player from connecting to a third server during an in-flight transfer.
Related Reducers¶
Player presence is typically managed by game hook logic rather than dedicated platform reducers. The server_heartbeat reducer may update aggregate counts in ServerRegistry based on presence data.
| Reducer | Relationship |
|---|---|
server_heartbeat |
Reports online_count derived from presence data |
Related Subscriptions¶
| Subscriber | Query | Purpose |
|---|---|---|
| Admin Panel | SELECT * FROM player_server_presence |
Player location tracking, population dashboard |
| Gateway | SELECT * FROM player_server_presence |
Dual-login detection, staleness cleanup |
| Game Hook | SELECT * FROM player_server_presence WHERE server_id = '{self}' |
Local player roster (optional) |
For full subscription architecture, see Subscriptions.
Related Pages¶
- ServerRegistry — Server directory (provides
server_idvalues) - ServerTransferQueue — Character transfer pipeline (coordinates with presence)
- Multi-Server Architecture — Player presence as a cross-server capability
- Platform Overview — How presence fits in the 12-table platform model