Skip to main content

Crate tapaculo

Crate tapaculo 

Source
Expand description

Tapaculo - Lightweight Rust server for real-time and turn-based multiplayer communication.

§Overview

Tapaculo is a batteries-included framework for building multiplayer game servers and real-time applications. It handles WebSocket connections, authentication, room management, pub/sub messaging, rate limiting, and more - letting you focus on your game logic.

§Features

  • Pluggable PubSub: Swap between in-memory and Redis backends without code changes
  • JWT Authentication: Secure token-based auth with session tracking and reconnection support
  • Room Management: Capacity limits, player tracking, and lifecycle events
  • WebSocket Server: Bidirectional real-time communication with automatic cleanup
  • Rate Limiting: Built-in spam prevention and abuse protection
  • Message History: Optional replay for new joiners
  • User Metadata: Associate custom data with players
  • Typed Messages: Type-safe message handling with serde
  • Production Ready: Comprehensive error handling, logging, and reconnection logic

§Quick Start

use tapaculo::*;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let auth = JwtAuth::new("your-secret-key");
    let pubsub = InMemoryPubSub::new();

    Server::new()
        .with_auth(auth)
        .with_pubsub(pubsub)
        .on_message(|ctx, envelope| async move {
            // Echo messages to all other players
            ctx.broadcast_to_others(envelope.data).await.ok();
        })
        .listen("0.0.0.0:8080")
        .await
}

§Examples

§Chess Server (2 players, turn-based)

use tapaculo::*;
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
enum ChessMove {
    Move { from: String, to: String },
    Resign,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let room_settings = RoomSettings {
        max_players: Some(2),
        allow_spectators: false,
        store_message_history: true,
        ..Default::default()
    };

    Server::new()
        .with_auth(JwtAuth::new("secret"))
        .with_pubsub(InMemoryPubSub::new())
        .with_room_settings(room_settings)
        .on_message_typed::<ChessMove, _, _>(|ctx, envelope| async move {
            ctx.broadcast_to_others(envelope.data).await.ok();
        })
        .listen("0.0.0.0:8080")
        .await
}

§Chat Server (group messaging)

use tapaculo::*;
use std::time::Duration;

Server::new()
    .with_auth(JwtAuth::new("secret"))
    .with_pubsub(InMemoryPubSub::new())
    .with_room_settings(RoomSettings {
        max_players: Some(50),
        store_message_history: true,
        max_history_messages: 200,
        ..Default::default()
    })
    .with_limits(MessageLimits {
        max_messages_per_window: 10,
        window_duration: Duration::from_secs(1),
        ..Default::default()
    })
    .on_message(|ctx, envelope| async move {
        ctx.broadcast(envelope.data).await.ok();
    })
    .listen("0.0.0.0:8081")
    .await

§Core Concepts

§Rooms

Rooms are isolated multiplayer sessions. Each WebSocket connection joins a specific room identified by the JWT token’s room_id claim.

use tapaculo::*;
use std::time::Duration;

let room_settings = RoomSettings {
    max_players: Some(4),
    allow_spectators: true,
    store_message_history: true,
    max_history_messages: 100,
    empty_room_timeout: Some(Duration::from_secs(300)),
};

§Broadcasting

Send messages to specific subsets of players:

// To all players in room
ctx.broadcast(data.clone()).await.ok();

// To all OTHER players (exclude sender)
ctx.broadcast_to_others(data.clone()).await.ok();

// Direct message to one player
ctx.send_to("player456", data).await.ok();

§Authentication

Generate JWT tokens for client connections:

use tapaculo::JwtAuth;

let auth = JwtAuth::new("your-secret-key");

let access_token = auth.sign_access(
    "user_id".to_string(),
    "room_id".to_string(),
    "session_id".to_string(),
    3600  // 1 hour
)?;

Clients connect via WebSocket with the token as a query parameter:

ws://localhost:8080/ws?token=eyJ0eXAiOiJKV1QiLCJhbGc...

§PubSub Backends

§In-Memory (Development)

use tapaculo::InMemoryPubSub;

let pubsub = InMemoryPubSub::new();
// or with custom buffer size
let pubsub = InMemoryPubSub::with_buffer(128);

§Redis (Production)

Requires the redis-backend feature:

[dependencies]
tapaculo = { version = "0.2", features = ["redis-backend"] }
use tapaculo::RedisPubSub;

let pubsub = RedisPubSub::new("redis://localhost:6379")?;

Re-exports§

pub use auth::AccessClaims;
pub use auth::JwtAuth;
pub use auth::JwtAuthOptions;
pub use auth::RefreshClaims;
pub use error::PubSubError;
pub use pubsub::InMemoryPubSub;
pub use pubsub::PubSubBackend;
pub use pubsub::PubSubExt;
pub use pubsub::Subscription;
pub use rate_limit::MessageLimits;
pub use rate_limit::RateLimiter;
pub use room::PlayerMetadata;
pub use room::RoomInfo;
pub use room::RoomManager;
pub use room::RoomSettings;
pub use room::StoredMessage;
pub use server::Context;
pub use server::Envelope;
pub use server::RoomEventHandler;
pub use server::Server;

Modules§

auth
JWT authentication module for signing and verifying access and refresh tokens.
error
pubsub
Unified publish/subscribe abstraction with in-memory and Redis backends.
rate_limit
Rate limiting for message spam prevention.
room
Room management for multiplayer sessions.
server
WebSocket server for real-time multiplayer communication with room management.

Structs§

Router
The router type for composing handlers and services.