Protocol Reference¶
Signal Fish Server uses a JSON-based WebSocket protocol. All messages are JSON objects with a type field and
optional data field.
MessagePack encoding is also supported for game data when enable_message_pack_game_data is enabled.
Client Messages¶
Authenticate¶
Authenticate with app credentials (required when auth is enabled). App ID is a public identifier that identifies the game application.
Optional fields:
sdk_version- SDK version for debugging and analyticsplatform- Platform information (e.g., "unity", "godot", "unreal")game_data_format- Preferred game data encoding (defaults to JSON text frames)
JoinRoom¶
Join or create a room for a specific game. There is no separate room-creation
message. JoinRoom behavior depends on room_code:
- Omit
room_code: create a new room with a generated room code. - Provide
room_codeand room exists for thatgame_name: join that room. - Provide
room_codeand no room exists for thatgame_name: create a new room with that room code.
Required fields:
game_name- Name of the gameplayer_name- Name for the player
Optional fields:
room_code- Code used to join/create a room for thisgame_namemax_players- Maximum players (applied only when a new room is created)supports_authority- Authority support (applied only when a new room is created)relay_transport- Preferred relay transport protocol (TCP, UDP, or Auto)
GameData¶
Send arbitrary game data to other players in the room.
The outer data is the serde content tag. The inner data is the variant
field and can be any JSON-serializable object.
PlayerReady¶
Toggle your own ready state in the lobby. This message has no payload.
Behavior:
- First send in
lobbystate marks the player ready. - Sending again in
lobbystate marks the player unready. - The server broadcasts
LobbyStateChangedafter each toggle. - When all players are ready, the server sends
GameStarting.
This message has no data payload.
If sent while not in a joinable lobby state, the server returns an Error
with INVALID_ROOM_STATE.
AuthorityRequest¶
Request or release game authority.
LeaveRoom¶
Leave the current room.
Ping¶
Heartbeat ping. Server responds with Pong.
Reconnect¶
Reconnect to a room after disconnection using authentication token.
{
"type": "Reconnect",
"data": {
"player_id": "player-id",
"room_id": "room-id",
"auth_token": "token-string"
}
}
The auth_token is generated by the server when a player disconnects and is
provided to the client for reconnection purposes.
ProvideConnectionInfo¶
Provide connection info for P2P establishment.
{
"type": "ProvideConnectionInfo",
"data": {
"connection_info": {
"type": "direct",
"host": "192.168.1.10",
"port": 7777
}
}
}
JoinAsSpectator¶
Join a room as a spectator (read-only observer).
{
"type": "JoinAsSpectator",
"data": {
"game_name": "my-game",
"room_code": "ABC123",
"spectator_name": "Observer1"
}
}
Required fields:
game_name- Name of the gameroom_code- Code of the room to spectatespectator_name- Name for the spectator
LeaveSpectator¶
Leave spectator mode.
This message has no data payload.
Server Messages¶
Authenticated¶
Authentication successful. Includes app information and rate limits.
{
"type": "Authenticated",
"data": {
"app_name": "my-game",
"organization": "My Organization",
"rate_limits": {
"per_minute": 60,
"per_hour": 3600,
"per_day": 86400
}
}
}
Optional fields:
organization- Organization name (if any)
ProtocolInfo¶
SDK/protocol compatibility details advertised after authentication.
{
"type": "ProtocolInfo",
"data": {
"capabilities": ["reconnection", "spectators", "authority"],
"game_data_formats": ["json", "message_pack"]
}
}
AuthenticationError¶
Authentication failed.
{
"type": "AuthenticationError",
"data": {
"error": "Invalid app_id",
"error_code": "INVALID_APP_ID"
}
}
RoomJoined¶
Successfully joined or created a room. This message is sent both when creating a new room and when joining an existing room. There is no separate room-created response type.
{
"type": "RoomJoined",
"data": {
"room_id": "uuid-string",
"room_code": "ABC123",
"player_id": "your-player-id",
"game_name": "my-game",
"max_players": 8,
"supports_authority": true,
"current_players": [
{
"id": "player-id",
"name": "Player 1",
"is_authority": false,
"is_ready": false,
"connected_at": "2024-01-01T00:00:00Z"
}
],
"is_authority": false,
"lobby_state": "waiting",
"ready_players": [],
"relay_type": "WebRTC",
"current_spectators": []
}
}
PlayerJoined¶
Another player joined the room.
{
"type": "PlayerJoined",
"data": {
"player": {
"id": "player-id",
"name": "Player 2",
"is_authority": false,
"is_ready": false,
"connected_at": "2024-01-01T00:00:00Z"
}
}
}
PlayerLeft¶
A player left the room.
RoomJoinFailed¶
Failed to join room.
Note: The error_code field is optional.
RoomLeft¶
Successfully left room.
This message has no data payload.
GameData¶
Game data relayed from another player.
{
"type": "GameData",
"data": {
"from_player": "player-id",
"data": {
"action": "move",
"x": 100,
"y": 200
}
}
}
GameDataBinary¶
Binary game data payload from another player. Uses bytes for zero-copy cloning during broadcast.
{
"type": "GameDataBinary",
"data": {
"from_player": "player-id",
"encoding": "message_pack",
"payload": "<base64-encoded-bytes>"
}
}
LobbyStateChanged¶
Lobby state transitioned.
{
"type": "LobbyStateChanged",
"data": {
"lobby_state": "finalized",
"ready_players": ["player-id-1", "player-id-2"],
"all_ready": true
}
}
Possible states:
waiting- Waiting for players to joinlobby- Room is full, players coordinating readinessfinalized- All players ready, game starting
AuthorityChanged¶
Authority status changed in the room.
{
"type": "AuthorityChanged",
"data": {
"authority_player": "player-id",
"you_are_authority": false
}
}
The authority_player field can be null if no player currently has authority.
AuthorityResponse¶
Authority request response.
Note: The reason and error_code fields are optional.
GameStarting¶
Game is starting with peer connection information.
{
"type": "GameStarting",
"data": {
"peer_connections": [
{
"player_id": "player-id-1",
"player_name": "Player 1",
"is_authority": false,
"relay_type": "WebRTC"
}
]
}
}
Error¶
An error occurred.
Note: The error_code field is optional.
Common error codes:
ROOM_FULL- Room has reached max playersROOM_NOT_FOUND- Room code does not existINVALID_GAME_NAME- Game name validation failedRATE_LIMIT_EXCEEDED- Too many requestsAUTHENTICATION_REQUIRED- Authentication requiredINVALID_APP_ID- Invalid app ID
Pong¶
Response to client Ping.
Reconnected¶
Reconnection successful. Includes current room state and missed events.
{
"type": "Reconnected",
"data": {
"room_id": "uuid-string",
"room_code": "ABC123",
"player_id": "your-player-id",
"game_name": "my-game",
"max_players": 8,
"supports_authority": true,
"current_players": [
{
"id": "player-id",
"name": "Player 1",
"is_authority": false,
"is_ready": false,
"connected_at": "2024-01-01T00:00:00Z"
}
],
"is_authority": false,
"lobby_state": "lobby",
"ready_players": ["player-id-1"],
"relay_type": "WebRTC",
"current_spectators": [],
"missed_events": [
{
"type": "GameData",
"data": {
"from_player": "player-id",
"data": {"action": "move"}
}
}
]
}
}
ReconnectionFailed¶
Reconnection failed.
{
"type": "ReconnectionFailed",
"data": {
"reason": "The reconnection token is invalid or malformed.",
"error_code": "RECONNECTION_TOKEN_INVALID"
}
}
PlayerReconnected¶
Another player reconnected to the room.
SpectatorJoined¶
Successfully joined a room as spectator.
{
"type": "SpectatorJoined",
"data": {
"room_id": "uuid-string",
"room_code": "ABC123",
"spectator_id": "your-spectator-id",
"game_name": "my-game",
"current_players": [
{
"id": "player-id",
"name": "Player 1",
"is_authority": false,
"is_ready": false,
"connected_at": "2024-01-01T00:00:00Z"
}
],
"current_spectators": [
{
"id": "spectator-id",
"name": "Observer1",
"connected_at": "2025-01-15T10:35:00Z"
}
],
"lobby_state": "lobby",
"reason": "joined"
}
}
Note: The reason field is optional.
SpectatorJoinFailed¶
Failed to join as spectator.
{
"type": "SpectatorJoinFailed",
"data": {
"reason": "Room not found",
"error_code": "ROOM_NOT_FOUND"
}
}
Note: The error_code field is optional.
SpectatorLeft¶
Successfully left spectator mode.
{
"type": "SpectatorLeft",
"data": {
"room_id": "uuid-string",
"room_code": "ABC123",
"reason": "voluntary_leave",
"current_spectators": []
}
}
Note: All fields are optional.
NewSpectatorJoined¶
Another spectator joined the room.
{
"type": "NewSpectatorJoined",
"data": {
"spectator": {
"id": "spectator-id",
"name": "Observer2",
"connected_at": "2025-01-15T10:36:00Z"
},
"current_spectators": [
{
"id": "spectator-id-1",
"name": "Observer1",
"connected_at": "2025-01-15T10:35:00Z"
},
{
"id": "spectator-id-2",
"name": "Observer2",
"connected_at": "2025-01-15T10:36:00Z"
}
],
"reason": "joined"
}
}
Note: The reason field is optional.
SpectatorDisconnected¶
Another spectator left the room.
{
"type": "SpectatorDisconnected",
"data": {
"spectator_id": "spectator-id",
"reason": "disconnected",
"current_spectators": []
}
}
Note: The reason field is optional.
Session Flow¶
Client Server
| |
|--- Authenticate ------------------>|
|<-- Authenticated ------------------|
| |
|--- JoinRoom (no room_code) ------->|
|<-- RoomJoined ---------------------|
| |
| (other client joins) |
|<-- PlayerJoined -------------------|
| |
|--- PlayerReady ------------------->|
|<-- LobbyStateChanged (lobby) ------|
| |
|--- GameData ---------------------->|
|<-- GameData (from other player) ---|
| |
|--- LeaveRoom --------------------->|
|<-- RoomLeft -----------------------|
Reconnection Flow¶
When a client disconnects, the server generates a reconnection token bound to
the player's ID and room. The client uses this token along with the player_id
and room_id (from the original RoomJoined response) to reconnect:
{
"type": "Reconnect",
"data": {
"player_id": "your-player-id",
"room_id": "your-room-id",
"auth_token": "stored-token"
}
}
On successful reconnection, the server sends a Reconnected message with the current room state and any
missed events that occurred during the disconnection.
Next Steps¶
- Getting Started - Basic usage examples
- Features - Complete feature overview