Chat
In-game text chat for player communication.
Channels
| Channel | Scope | Prefix | Color |
|---|---|---|---|
| Global | All connected players | [G] |
white (default) |
| Local | Players orbiting the same reference body | [L] |
cyan |
| Direct | Player-to-player private messages | [D] |
yellow |
Channel Behavior
Global: Messages sent to all connected players. Default channel.
Local: Messages sent to players whose ship orbits the same reference body as the sender. The api-gateway looks up each player’s ship reference body from the latest tick state. If the sender’s reference body is unknown, the message falls back to global.
Direct: Messages sent to a specific player by player_id. Both sender and recipient see the message. If the target player is offline, the server returns error E030.
Channel Selection
The chat window includes a channel selector dropdown (Global / Local / Direct). When Direct is selected, a recipient input appears for entering the target player name. The client resolves the player name to ID using the players list from the welcome message.
Message Format
Client → Server (chat_send)
| Field | Type | Required | Description |
|---|---|---|---|
| type | string | yes | "chat_send" |
| message | string | yes | Message text (1–256 chars) |
| channel | string | no | "global" (default), "local", or "direct" |
| ref_body | string | no | Sender’s reference body name (required when channel is "local") |
| target_id | string | no | Recipient player_id (required when channel is "direct") |
Server → Client (chat_message)
| Field | Type | Description |
|---|---|---|
| type | string | "chat_message" |
| player_id | uuid | Sender’s player ID |
| username | string | Sender’s display name |
| message | string | Message text (1–256 chars) |
| channel | string | "global", "local", or "direct" |
| timestamp | number | Server Unix time in seconds (epoch) |
Constraints
| Parameter | Value |
|---|---|
| Max message length | 256 characters |
| Rate limit | 5 messages per second per player (shared across all channels) |
| Rate limit window | 1.0 second (sliding) |
| Max displayed messages | 200 (oldest removed, per window — not per channel) |
XSS Prevention
All user-supplied content (username, message) is rendered via textContent — never innerHTML. This prevents script injection without requiring sanitization libraries.
Unread Indicator
When the chat window is closed and a new message arrives, a pulsing red badge appears on the chat window’s position in the View menu. The badge clears when the window is opened. All channels share a single unread counter.
Keyboard Shortcut
Shift+C toggles the chat window in both cockpit and map views.
Chat Window
- Floating/draggable window (same pattern as Ship Status, Orbit Diagram, etc.)
- Default position: bottom-right (
bottom: 60px; right: 20px) - Dimensions: 350px wide, 300px tall
- Message list scrolls; auto-scroll when near bottom (within 50px)
- Channel selector dropdown above the input row
- Input row: text input + Send button
- When Direct channel is selected, a recipient text input appears between the channel selector and message input
Entersends the messagestopPropagation()on input keydown/keyup prevents ship controls while typing- Messages are color-coded by channel (see Channels table above)
- Channel prefix (
[G],[L],[D]) appears before the sender name
Error Handling
| Condition | Error Code | User Feedback |
|---|---|---|
| Rate limited | E018 | Status bar message “Rate limit exceeded” |
| Message too long | E019 | Status bar message “Message too long” |
| Empty message | E020 | Blocked client-side (no send) |
| Invalid channel | E026 | Status bar message “Invalid channel” |
| Direct target not found | E030 | Status bar message “Player not found” |
| Direct target offline | E030 | Status bar message “Player not online” |
Server-Side Routing
Local Channel Routing
When a chat_send with channel: "local" arrives:
- Read
ref_bodyfrom the message (client sends its current reference body name) - Store
ref_bodyin the connection’s cached state (_player_ref_bodies[player_id]) - For each connected player, check if their cached ref_body matches the sender’s
- Send the
chat_messageonly to matching players (including sender) - If ref_body is missing, fall back to global broadcast
Performance: No Redis queries or SOI computation needed. The client already computes its reference body for the orbital elements display. The server caches each player’s last-known reference body from local chat messages (updated whenever they send a local message).
Direct Channel Routing
When a chat_send with channel: "direct" arrives:
- Look up target_id in
_connectionsdict - If not found (offline), return error E030
- Send the
chat_messageto both sender and recipient