The server page (server.html) is a browser-based interface that acts as a proxy controller for a dedicated Quake server process.
It does not run the Quake server directly in the browser, but rather communicates with a remote dedicated server through the master
server using WebSocket connections.
- Browser → Master Server: WebSocket connection for control and signaling
- Master Server → Dedicated Server: Process management and RCON forwarding
- Dedicated Server → Master Server → Browser: Status updates and RCON responses
- server.html: Browser interface with embedded JavaScript
- Master Server: WebSocket server that manages dedicated server processes
- Dedicated Server: Node.js Quake server process (ioq3ded.js)
window.addEventListener('load', async () => {
setupUIHandlers();
await connectToMasterServer();
initQuakeGame();
});- Connects to WebSocket server at configured master server URL
- Default:
ws://localhost:27950(can be overridden via URL parameter) - Handles connection lifecycle with automatic reconnection
- Stores connection in global
serverConnectionvariable
The server page acts as a "browser server" that registers with the master:
- Sends
register_servermessage with server info - Receives
server_registeredconfirmation with peer ID - Starts sending heartbeats every 30 seconds
- Updates UI to show "Registered" status
Instead of running Quake in an iframe, it requests a remote server:
startRemoteServer() {
// Sends start_game_server message to master
// Master spawns dedicated server process
// Returns gameId for tracking
}-
Server Management
register_server: Register as a game serverunregister_server: Unregister from masterheartbeat: Keep-alive signal with server infoupdate_server: Update server name/info
-
Dedicated Server Control
start_game_server: Request to spawn dedicated serverstop_game_server: Stop dedicated server processrcon_command: Execute RCON command on dedicated server
-
Client Management
proxy_connection: Accept client connectionproxy_data: Forward data to/from clientskick_client: Disconnect a client
-
Token/Game State
game:state:token: Broadcast game state tokensrequest_scores: Get current match scores
-
Connection Status
connected: Initial connection confirmationserver_registered: Registration successfulheartbeat_ack: Heartbeat acknowledged
-
Client Events
connection_request: New client wants to connectproxy_data: Data from connected clientclient_disconnected: Client left
-
Server Events
game_server_started: Dedicated server is runninggame_server_stopped: Dedicated server stoppedrcon_response: RCON command outputserver_list: List of active servers
- Polls dedicated server via RCON every 5 seconds
- Executes
statuscommand for player list and scores - Executes
serverinfofor server configuration - Parses fixed-width RCON output format
- Stores in
latestPlayerScoresfor distribution
- Monitors match progress via RCON polling
- Detects match end conditions:
- Time limit reached
- Frag limit reached
- Manual "End Match" button
- Initiates 30-second restart countdown // Currently disabled
- Handles automatic server restart cycle // Currently disabled
- Creates blockchain-verifiable state tokens
- Token creation process:
createGameStateToken() { // Gather current game state // Create token with Unicity service // Broadcast to all clients }
- Broadcasts tokens every 10 seconds
- Resets tokens every 10 frames for performance
- Includes: gameId, frame, timestamp, player states
- Requires entry fee tokens from connecting clients
- Validates tokens via Unicity service
- Stores collected fees for winner distribution
- Prevents double-spending with transaction tracking
sendRCONCommand(command) {
// Generate unique request ID
// Send via master server proxy
// Wait for response with timeout
}status: Get player list with scoresserverinfo: Get server configurationkick <slot>: Kick player by slot numbersay <message>: Server-wide messagemap <mapname>: Change mapfraglimit <n>: Set frag limittimelimit <n>: Set time limit
- Handles multi-line RCON output
- Parses player status lines (fixed-width format)
- Extracts: slot, score, ping, name, IP info
- Updates UI with parsed data
- Client requests connection via master server
- Server receives
connection_requestwith client ID - Server accepts via
proxy_connectionresponse - All client data flows through master server proxy
serverState.clients = new Map() // clientId -> client info
// Tracks: id, username, pubkey, entryTokenReceived, scores- Chat messages forwarded via proxy
- Game state tokens broadcast to all
- Individual messages via
proxy_data - Kick functionality with reason
- Automatic: Time/frag limit via RCON polling
- Manual: "End Match and Pay Rewards" button
- Triggers reward distribution process
handleGameOver() {
// Get fresh scores from RCON
// Stop match control
// Distribute rewards
// Start restart countdown
}- Uses latest RCON scores for accuracy
- Determines winners by highest score
- Distributes collected entry fees
- Records distribution to prevent duplicates
- Shows results overlay on UI
- 30-second countdown after match end
- Automatic server restart
- Clients auto-reconnect
- New match begins with fresh state
- Server status (registered/unregistered)
- Dedicated server state (running/stopped)
- Connected clients list
- Player statistics table
- Match timer and limits
- Server name input with update button
- "End Match and Pay Rewards" button
- Client action buttons (kick, message)
- Token information display
- WebSocket message handlers update UI
- Periodic RCON polls refresh stats
- Client connect/disconnect events
- Match progress and countdown timers
- Automatic reconnection to master server
- Heartbeat monitoring for liveness
- Graceful handling of disconnections
- 10-second timeout on commands
- Retry logic for critical commands
- Error reporting in UI logs
- Verifies entry tokens before acceptance
- Handles invalid token rejections
- Tracks spending to prevent reuse
- Server identity via Unicity tokens
- Client identity verification
- Entry fee validation
- RCON commands restricted to server page
- Client actions validated server-side
- Token operations cryptographically secured
- Input sanitization for server names
- RCON output parsing safety
- Message size limits
// WebSocket connection to master server
let serverConnection = null;
// Current server configuration
let currentServerName = "";
let dedicatedServerInfo = null;
// Server state tracking
const serverState = {
registered: false,
clients: new Map(),
collectedFees: [],
gameStateInterval: null,
restartCycle: null
};
// Latest player scores from RCON
let latestPlayerScores = {
players: [],
lastUpdate: null
};
// Match control state
let matchEndDetection = null;
let gameEnded = false;The server page provides a complete browser-based interface for managing a dedicated Quake server. It handles:
- Server Lifecycle: Start, stop, register, heartbeat
- Player Management: Connect, disconnect, kick, track
- Match Control: Limits, timing, restart cycles
- Token System: Entry fees, game state, rewards
- RCON Integration: Commands, status, statistics
- UI Updates: Real-time status, controls, displays
All functionality operates through WebSocket proxy to the master server, which manages the actual dedicated server process. This architecture allows browser-based server management while maintaining full game server capabilities.