Library Usage¶
Signal Fish Server is published as both a binary and a library crate. Embed the signaling server into your own Rust application.
Add Dependency¶
[dependencies]
signal-fish-server = "0.2.0"
tokio = { version = "1", features = ["full"] }
anyhow = "1"
Basic Embedded Server¶
use signal_fish_server::{
config,
database::DatabaseConfig,
server::{EnhancedGameServer, ServerConfig},
websocket,
};
use std::{net::SocketAddr, sync::Arc, time::Duration};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Load configuration
let cfg = config::load();
// Build server configuration
// Note: Only key fields shown for brevity - see src/server.rs ServerConfig for all required fields
let server_config = ServerConfig {
default_max_players: cfg.server.default_max_players,
ping_timeout: Duration::from_secs(cfg.server.ping_timeout),
room_cleanup_interval: Duration::from_secs(cfg.server.room_cleanup_interval),
max_rooms_per_game: cfg.server.max_rooms_per_game,
rate_limit_config: cfg.server.rate_limit_config.clone(),
empty_room_timeout: Duration::from_secs(cfg.server.empty_room_timeout),
inactive_room_timeout: Duration::from_secs(cfg.server.inactive_room_timeout),
max_message_size: cfg.server.max_message_size,
max_connections_per_ip: cfg.server.max_connections_per_ip,
require_metrics_auth: cfg.server.require_metrics_auth,
metrics_auth_token: cfg.server.metrics_auth_token.clone(),
reconnection_window: Duration::from_secs(cfg.server.reconnection_window),
event_buffer_size: cfg.server.event_buffer_size,
enable_reconnection: cfg.server.enable_reconnection,
websocket_config: cfg.server.websocket_config.clone(),
auth_enabled: cfg.server.auth_enabled,
heartbeat_throttle: Duration::from_secs(cfg.server.heartbeat_throttle_secs),
region_id: cfg.server.region_id.clone(),
room_code_prefix: cfg.server.room_code_prefix.clone(),
};
// Create the game server
let game_server = Arc::new(
EnhancedGameServer::new(
server_config,
cfg.protocol.clone(),
cfg.relay_types.clone(),
DatabaseConfig::InMemory,
cfg.metrics.clone(),
cfg.auth.clone(),
cfg.coordination.clone(),
cfg.security.transport.clone(),
cfg.security.authorized_apps.clone(),
)
.await?
);
// Start background cleanup task
let cleanup = game_server.clone();
tokio::spawn(async move {
cleanup.cleanup_task().await
});
// Build the Axum router
let router = websocket::create_router(&cfg.security.cors_origins)
.with_state(game_server);
// Start listening
let addr = SocketAddr::from(([0, 0, 0, 0], cfg.port));
let listener = tokio::net::TcpListener::bind(addr).await?;
println!("Server listening on {}", addr);
axum::serve(
listener,
router.into_make_service_with_connect_info::<SocketAddr>(),
)
.await?;
Ok(())
}
Custom Storage Backend¶
Implement the GameDatabase trait for custom persistence:
use signal_fish_server::database::GameDatabase;
use signal_fish_server::protocol::{Room, RoomId, PlayerId, PlayerInfo, SpectatorInfo, ConnectionInfo, LobbyState};
use async_trait::async_trait;
use anyhow::Result;
use std::collections::HashMap;
use std::any::Any;
use uuid::Uuid;
pub struct MyCustomDatabase {
// Your storage implementation (e.g., Redis client, PostgreSQL pool, etc.)
}
#[async_trait]
impl GameDatabase for MyCustomDatabase {
async fn initialize(&self) -> Result<()> {
// Initialize database connection and run migrations
Ok(())
}
async fn create_room(
&self,
game_name: String,
room_code: Option<String>,
max_players: u8,
supports_authority: bool,
creator_id: PlayerId,
relay_type: String,
region_id: String,
application_id: Option<Uuid>,
) -> Result<Room> {
// Create and store room in your database
todo!("Implement room creation")
}
async fn get_room(&self, game_name: &str, room_code: &str) -> Result<Option<Room>> {
// Retrieve room from your database by game name and room code
Ok(None)
}
async fn get_room_by_id(&self, room_id: &RoomId) -> Result<Option<Room>> {
// Retrieve room from your database by ID
Ok(None)
}
async fn add_player_to_room(&self, room_id: &RoomId, player: PlayerInfo) -> Result<bool> {
// Add player to room (returns false if room is full)
Ok(false)
}
async fn remove_player_from_room(
&self,
room_id: &RoomId,
player_id: &PlayerId,
) -> Result<Option<PlayerInfo>> {
// Remove player from room
Ok(None)
}
async fn get_room_players(&self, room_id: &RoomId) -> Result<Vec<PlayerInfo>> {
// Get all players in a room
Ok(Vec::new())
}
async fn delete_room(&self, room_id: &RoomId) -> Result<bool> {
// Delete a specific room by ID
Ok(false)
}
async fn health_check(&self) -> bool {
// Health check for your database
true
}
fn as_any(&self) -> &(dyn Any + Send + Sync) {
self
}
// ... implement all other required methods from the GameDatabase trait
// See src/database/mod.rs for the complete trait definition
}
Use your custom database:
let game_server = EnhancedGameServer::new(
server_config,
protocol_config,
relay_types_config,
DatabaseConfig::Custom(Arc::new(MyCustomDatabase::new())),
metrics_config,
auth_config,
coordination_config,
transport_config,
authorized_apps,
)
.await?;
Custom Router Integration¶
Integrate Signal Fish into an existing Axum application:
use axum::{Router, routing::get};
use signal_fish_server::websocket;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Your existing routes
let app = Router::new()
.route("/", get(|| async { "Hello, world!" }))
.route("/api/status", get(api_status));
// Add Signal Fish routes
let signal_fish_routes = websocket::create_router("*")
.with_state(game_server);
// Merge routers
let app = app.merge(signal_fish_routes);
// Start server
let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
let listener = tokio::net::TcpListener::bind(addr).await?;
axum::serve(listener, app).await?;
Ok(())
}
Programmatic Configuration¶
Build configuration programmatically instead of from files:
use signal_fish_server::config::{
Config, ServerConfig, ProtocolConfig, SecurityConfig,
};
let config = Config {
port: 3536,
server: ServerConfig {
default_max_players: 8,
ping_timeout: 30,
room_cleanup_interval: 60,
..Default::default()
},
protocol: ProtocolConfig {
max_game_name_length: 64,
room_code_length: 6,
..Default::default()
},
security: SecurityConfig {
cors_origins: "*".to_string(),
require_websocket_auth: false,
..Default::default()
},
..Default::default()
};
Message Handling¶
Note: The
EnhancedGameServerdoes not currently expose public APIs for directly sending messages to players. The server automatically handles message routing based on client connections and room membership. This is an internal implementation detail.If you need to send custom server-initiated messages, you would need to extend the server implementation or use the internal message coordinator interfaces.
Event Hooks¶
Note: The
EnhancedGameServerdoes not currently expose a public event subscription API. Server events are handled internally for metrics, logging, and coordination.If you need to monitor server events, consider:
- Using the metrics endpoints (
/metricsor/metrics/prom) to track room and player counts- Implementing custom logging by extending the server modules
- Monitoring structured logs with a log aggregation system (all events are logged with
tracing)
Feature Flags¶
Enable optional features:
[dependencies]
signal-fish-server = { version = "0.2.0", features = ["tls", "legacy-fullmesh"] }
Available features:
tls- Built-in TLS/mTLS supportlegacy-fullmesh- Upstream matchbox full-mesh signaling mode
Testing¶
Use the library in your tests:
#[cfg(test)]
mod tests {
use signal_fish_server::{
server::EnhancedGameServer,
protocol::{ClientMessage, ServerMessage},
};
#[tokio::test]
async fn test_room_creation() {
// Note: This is a conceptual example. The actual server does not expose
// a public handle_message API. Message handling is internal to the WebSocket
// connection lifecycle. For testing, see tests/integration_tests.rs for
// examples of how to test via the WebSocket interface.
let server = EnhancedGameServer::new(
/* config */
).await.unwrap();
// Example: Test via database state instead
let room = server.database().get_room("test-game", "ABC123").await.unwrap();
assert!(room.is_some());
}
}
API Documentation¶
Generate full API docs:
Next Steps¶
- Protocol Reference - Message types and flow
- Development - Building and testing