Overview
Spectra Server acts as a real-time data pipeline, receiving game state from observer clients, processing it through the match orchestration layer, and broadcasting it to connected frontends. This page details the complete data journey.Observer Client Connection Flow
The primary data flow begins when an observer client connects to port 5100.Authentication Sequence
WebSocket Connection
Client establishes WebSocket connection to
wss://server:5100 (or ws:// in insecure mode)Server Validation
Server validates the request (see src/connector/websocketIncoming.ts:66):
- Packet validation: Ensures type is
DataTypes.AUTH - Version check: Calls
isCompatibleVersion()to verify client compatibility - Key validation: Checks access key via
isValidKey()method:- If
REQUIRE_AUTH_KEY=false, automatically valid - If matches
AUTH_KEYenv var, valid - If
USE_BACKEND=true, validates against backend API
- If
- Match creation: Calls
MatchController.createMatch()
Reconnection Support: If a client disconnects and reconnects with the same group code and group secret, the server reuses the existing match instance rather than creating a new one (see src/controller/MatchController.ts:46).
Game State Ingestion
Once authenticated, the observer client begins sending game data through theobs_data event.
Data Processing Pipeline
Match Routing
MatchController.receiveMatchData() routes data to the correct match instance (src/controller/MatchController.ts:96):- Adds timestamp to data
- Finds match by
groupCode - Validates match exists
- Forwards to
Match.receiveMatchSpecificData()
Event Type Handling
The
Match class processes different event types (src/model/Match.ts:102):Scoreboard Events (DataTypes.SCOREBOARD):- Routes to correct team by
startTeamID - Detects spike plants (300 credit increase)
- Updates player stats (K/D/A, money, ult points)
DataTypes.ROSTER):- Updates agent selections
- Tracks agent select start time
- Updates player positions
DataTypes.KILLFEED):- Broadcasts to both teams
- Tracks kills, assists, weapons used
DataTypes.ROUND_INFO):- Updates round number and phase
- Handles side switches at round 13 and OT rounds
- Triggers backend updates on round start
- Manages timeout grants in overtime
DataTypes.MATCH_START):- Stores match ID from game
- Registers match with backend (if enabled)
- Sets
isRunning = true
DataTypes.SCORE):- Determines round winner
- Calculates round end reason (kills/defuse/detonation/timeout)
- Updates team scores
Event Type Reference
The server handles these primary event types (defined in src/model/eventData.ts:155):| Event Type | Purpose | Frequency |
|---|---|---|
SCOREBOARD | Player stats, money, ult, armor | ~Every tick |
ROSTER | Agent selection, player order | Agent select phase |
KILLFEED | Kill/death notifications | Per elimination |
ROUND_INFO | Round phase, round number | Phase changes |
SCORE | Team scores after round end | Per round |
MATCH_START | Match UUID from game | Once per match |
MAP | Map being played | Once per match |
GAME_MODE | Bomb/Swift play | Once per match |
OBSERVING | Currently observed player | When observer switches |
SPIKE_PLANTED | Spike plant notification | Per spike plant |
SPIKE_DEFUSED | Spike defusal | Per defusal |
SPIKE_DETONATED | Spike explosion | Per detonation |
Spike plant detection has two mechanisms:
- Direct event: Client sends explicit
SPIKE_PLANTEDevent via hotkey - Automatic detection: Server detects 300 credit increase in scoreboard data (src/model/Match.ts:443)
Match State Management
TheMatch class maintains comprehensive game state:
Core State
State Updates Flow
Broadcasting to Frontends
Frontend Connection
Initial State
Server sends current match state via
sendMatchDataForLogon() (src/controller/MatchController.ts:134)Broadcast Loop
TheMatchController runs a continuous send loop at 100ms intervals (src/controller/MatchController.ts:160):
Efficient Broadcasting: The server only sends data when
eventNumber has increased, preventing redundant broadcasts and reducing bandwidth.Data Filtering
Before broadcasting, sensitive data is removed (src/connector/websocketOutgoing.ts:88):Auxiliary Client Data Flow
Auxiliary clients provide additional game data (player abilities, health, etc.) using a separate authentication flow.Auxiliary Connection
Aux client connects to port 5100 and sends
aux_logon event (src/connector/websocketIncoming.ts:158):Auxiliary Authentication
Server authenticates and registers client, then listens for
aux_data eventsAuxiliary Data Processing
Aux data types include:
AUX_SCOREBOARD: Player scoreboard from in-gameAUX_SCOREBOARD_TEAM: Full team scoreboardAUX_ABILITIES: Ability charge countsAUX_HEALTH: Player health/armorAUX_ASTRA_TARGETING: Astra star targeting modeAUX_CYPHER_CAM: Cypher camera active state
Disconnection Handling: When an auxiliary client disconnects, the server marks their player as disconnected in the match state (src/connector/websocketIncoming.ts:233), allowing frontends to show disconnected status.
Match Lifecycle
A complete match flow from creation to cleanup:Round Loop
For each round:
- Shopping phase → Side switches (if applicable)
- Combat phase → Scoreboard/killfeed/spike events
- End phase → Score calculation → Round reasons determined
Game End
Round phase becomes
game_end → Backend completion → Stats fetch (supporters) → Match removed from registry after 5 secondsPerformance Characteristics
Broadcast Efficiency
- 100ms send interval: Maximum 10 updates/second
- Room-based broadcasting: Only sends to subscribed frontends
- Event-driven: Only broadcasts when state changes
- Compression: perMessageDeflate reduces bandwidth ~60-70%
Scalability
- Multiple matches: Single server handles many concurrent matches
- Multiple frontends: Unlimited viewers per match (room-based)
- Singleton controller:
MatchControllerandWebsocketOutgoinguse singleton pattern - Automatic cleanup: Inactive matches removed to prevent memory leaks
Production Tip: For high-load scenarios with many concurrent matches, monitor the send loop performance. The 100ms interval processes all active matches sequentially.
Next Steps
System Components
Explore detailed implementation of each component
Architecture Overview
Return to high-level architecture overview