Skip to main content

Crate signal_fish_client

Crate signal_fish_client 

Source
Expand description

§Signal Fish Client

Transport-agnostic Rust client for the Signal Fish multiplayer signaling protocol.

This crate provides a high-level async client that communicates with a Signal Fish signaling server using JSON text messages over any bidirectional transport.

§Features

  • Transport-agnostic — implement the Transport trait for any backend
  • Wire-compatible — all protocol types match the server’s v2 format exactly
  • Protocol v2 relay + v3 mesh — v3 is additive and opt-in; a default client stays byte-identical to v2 (see Protocol versions)
  • WebSocket built-in — default transport-websocket feature provides WebSocketTransport
  • Event-driven — receive typed SignalFishEvents via a channel
  • No silent loss — events are delivered with backpressure and sends are bounded with explicit congestion signals (see Delivery guarantees)

§Choosing a client

The crate ships two clients with identical protocol behavior; pick by how your application is driven:

  • SignalFishClient (async) — spawns a background transport loop with tokio::spawn. Use it when a tokio runtime is running (a #[tokio::main]/block_on application, multi-thread or current_thread). It only makes progress while the runtime is driven — manually “ticking” a runtime once per frame starves it (see the driving contract).
  • SignalFishPollingClient (sync, feature polling-client) — no background task, no runtime. You call poll() once per frame from a game loop. This is the right client for frame-driven engines (Godot, Bevy without tokio, Unity via FFI) and wasm32 targets.

§Protocol versions

The SDK speaks two protocol generations, and you choose which by how you build SignalFishConfig:

  • v2 — the relay floor (default). SignalFishConfig::new advertises no v3 capabilities, the server relays all traffic through itself, and the Authenticate bytes are byte-identical to the old v2 client. This is the relay-floor guarantee: opt into nothing and nothing changes.
  • v3 — additive mesh (opt-in). SignalFishConfig::enable_mesh advertises the WebRTC/relay transports and mesh/host/relay topologies, letting the server form a peer-to-peer session. v3 capabilities are additive to the v2 relay floor, and the server falls back to relay whenever it cannot form a session. On current servers, an eligible client explicitly calls SignalFishClient::start_game after readiness instead of relying on automatic start.

The negotiated version comes back in the server’s ProtocolInfo; check it via SignalFishClient::negotiated_protocol_version / SignalFishClient::supports_mesh. v3-only sends fail fast with SignalFishError::ProtocolUnsupported until v3 is negotiated. The SDK is signaling-only — it bundles no WebRTC stack; with the mesh feature you implement the webrtc::WebRtcDriver seam (or use webrtc::MeshController) against str0m / webrtc-rs / web-sys. The highest version this SDK speaks is PROTOCOL_VERSION.

§Quick Start

use signal_fish_client::{
    WebSocketTransport, SignalFishClient, SignalFishConfig,
    JoinRoomParams, SignalFishEvent,
};

#[tokio::main]
async fn main() -> Result<(), signal_fish_client::SignalFishError> {
    // 1. Connect a WebSocket transport to the signaling server.
    let transport = WebSocketTransport::connect("ws://localhost:3536/ws").await?;

    // 2. Build a client config with your application ID.
    let config = SignalFishConfig::new("mb_app_abc123");

    // 3. Start the client — returns a handle and an event receiver.
    //    The client automatically sends Authenticate on start.
    let (mut client, mut event_rx) = SignalFishClient::start(transport, config);

    // 4. Process events — wait for Authenticated before joining a room.
    while let Some(event) = event_rx.recv().await {
        match event {
            SignalFishEvent::Authenticated { app_name, .. } => {
                println!("Authenticated as {app_name}");
                // Now it's safe to join a room.
                client.join_room(JoinRoomParams::new("my-game", "Alice"))?;
            }
            SignalFishEvent::RoomJoined { room_code, .. } => {
                println!("Joined room {room_code}");
                client.set_ready()?;
            }
            // Protocol v2: the game starts explicitly, not on readiness.
            SignalFishEvent::LobbyStateChanged { all_ready: true, .. } => {
                client.start_game()?;
            }
            SignalFishEvent::Disconnected { .. } => break,
            _ => {}
        }
    }

    // 5. Shut down gracefully.
    client.shutdown().await;
    Ok(())
}

Re-exports§

pub use client::ClientSnapshot;
pub use client::ClientStats;
pub use client::GameDataDelivery;
pub use client::JoinRoomParams;
pub use client::ProtocolViolationPolicy;
pub use client::SignalFishClient;
pub use client::SignalFishConfig;
pub use client_api::SignalFishClientApi;
pub use error::SignalFishError;
pub use error_codes::ErrorCode;
pub use event::ProtocolViolationKind;
pub use event::ServerErrorInfo;
pub use event::SignalFishEvent;
pub use event::DECODE_FAILED_RAW_PREFIX_MAX;
pub use protocol::decode_v3_binary_game_data;
pub use protocol::ClientMessage;
pub use protocol::DeliveryClass;
pub use protocol::DeliveryCountersByClass;
pub use protocol::DeliveryGap;
pub use protocol::DeliveryGapReason;
pub use protocol::DeliveryReportPayload;
pub use protocol::IceServer;
pub use protocol::LatestDeliveryCounters;
pub use protocol::MessageTransport;
pub use protocol::ReliableDeliveryCounters;
pub use protocol::ReplayStatus;
pub use protocol::SenderWatermark;
pub use protocol::ServerMessage;
pub use protocol::SessionPeer;
pub use protocol::SessionPlanPayload;
pub use protocol::Topology;
pub use protocol::TransportKind;
pub use protocol::V3BinaryGameDataFrame;
pub use protocol::VolatileDeliveryCounters;
pub use signal::PeerSignal;
pub use transport::Transport;
pub use transport::TransportCloseInfo;
pub use transport::TransportDiagnostics;
pub use transport::TransportFrame;
pub use transports::WebSocketConnectOptions;
pub use transports::WebSocketTransport;
pub use polling_client::PollingClientOptions;
pub use polling_client::PollingClosePolicy;
pub use polling_client::PollingQueueAgeStats;
pub use polling_client::PollingStats;
pub use polling_client::PollingWorkBudget;
pub use polling_client::SignalFishPollingClient;
pub use mesh::MeshPeer;
pub use mesh::MeshSession;
pub use webrtc::DriverEvent;
pub use webrtc::MeshEvent;
pub use webrtc::WebRtcDriver;
pub use webrtc::MeshController;

Modules§

client
Async client for the Signal Fish signaling protocol.
client_api
Common synchronous API implemented by both Signal Fish client drivers.
error
Error types for the Signal Fish client.
error_codes
Error codes for structured error handling in the Signal Fish protocol.
event
High-level events emitted by the Signal Fish client.
mesh
Optional zero-dependency mesh session tracker (protocol v3).
polling_client
Synchronous, polling-based client for the Signal Fish signaling protocol.
protocol
Wire-compatible protocol types for the Signal Fish signaling protocol.
signal
Typed WebRTC signaling payloads (protocol v3).
transport
Frame-capable polling transport contract.
transports
Transport implementations for the Signal Fish signaling protocol.
webrtc
The pluggable WebRTC driver seam and the MeshController that drives it (protocol v3, mesh feature).

Constants§

PROTOCOL_VERSION
Highest signaling protocol version this SDK speaks.