Crate cobalt [] [src]

cobalt is a networking library which provides virtual connections over UDP along with a messaging layer for sending both unreliable, reliable as well as ordered messages.

It is primarily designed to be used as the basis for real-time, latency bound, multi client systems.

The library provides the underlying architecture required for handling and maintaining virtual connections over UDP sockets and takes care of sending reliable, raw messages over the established client-server connections with minimal overhead.

cobalt is also fully configurable and can be plugged in a variety of ways, so that library users have the largest possible degree of control over the behavior of their connections.

Getting started

When working with cobalt the most important part is implementing the so called handlers. A Handler is a trait which acts as a event proxy for both server and client events that are emitted from the underlying tick loop.

Below follows a very basic example implementation of a custom game server.

use std::collections::HashMap;
use cobalt::{Config, Connection, ConnectionID, Handler, Server};

struct GameServer;
impl Handler<Server> for GameServer {

    fn bind(&mut self, server: &mut Server) {
        // Since this is a runnable doc, we'll just exit right away
        server.shutdown();
    }

    fn tick_connections(
        &mut self, _: &mut Server,
        connections: &mut HashMap<ConnectionID, Connection>
    ) {

        for (_, conn) in connections.iter_mut() {
            // Receive player input
        }

        // Advance game state

        for (_, conn) in connections.iter_mut() {
            // Send state to players
        }

    }

    fn shutdown(&mut self, _: &mut Server) {
        // Logging and things
    }

    fn connection(&mut self, _: &mut Server, _: &mut Connection) {
        // Create Player, send MOTD etc.
    }

    fn connection_lost(&mut self, _: &mut Server, _: &mut Connection) {
        // Remove Player
    }

}

let mut handler = GameServer;
let mut server = Server::new(Config::default());
server.bind(&mut handler, "127.0.0.1:7156").expect("Failed to bind server.");

And the client version would look almost identical except for a few methods having different names.

use std::collections::HashMap;
use cobalt::{Config, Connection, Handler, Client};

struct GameClient;
impl Handler<Client> for GameClient {

    fn connect(&mut self, client: &mut Client) {
        // Since this is a runnable doc, we'll just exit right away
        client.close();
    }

    fn tick_connection(&mut self, _: &mut Client, conn: &mut Connection) {

        for msg in conn.received() {
            // Receive state from server
        }

        // Advance game state

        // Send input to server

    }

    fn close(&mut self, _: &mut Client) {
        // Exit game
    }

    fn connection(&mut self, _: &mut Client, _: &mut Connection) {
        // Request map list and settings from server
    }

    fn connection_failed(&mut self, client: &mut Client, _: &mut Connection) {
        // Failed to connect to the server
    }

    fn connection_lost(&mut self, client: &mut Client, _: &mut Connection) {
        // Inform the user
    }

}

let mut handler = GameClient;
let mut client = Client::new(Config::default());
client.connect(&mut handler, "127.0.0.1:7156").expect("Failed to connect.");

Integration with existing event loops

When a client already provides its own event loop via a rendering framework or game engine, a synchronous version of the Client interface is also available in order to ease integration in such cases.

In these cases it can also make sense to use a EventQueue like implementation of the Handler trait.

use cobalt::{Client, Config, Connection, ConnectionID, Handler};

struct SyncHandler;
impl Handler<Client> for SyncHandler {}

let mut handler = SyncHandler;
let mut client = Client::new(Config::default());
let mut state = client.connect_sync(
    &mut handler,
    "127.0.0.1:7156"

).expect("Failed to connect.");

// Receive from and tick the client connection
client.receive_sync(&mut handler, &mut state);
client.tick_sync(&mut handler, &mut state);

// Handle received message and send new ones here

// Send any pending messages via the connection
client.send_sync(&mut handler, &mut state);

Structs

BinaryRateLimiter

Implementation of a binary state rate limiter for congestion avoidance.

Client

Implementation of a single-server client with handler based event dispatch.

ClientState

A structure used for synchronous calls on a Client instance.

Config

Structure defining connection and message configuration options.

Connection

Implementation of a reliable, virtual connection logic.

ConnectionID

Representation of a random id for connection identification.

Server

Implementation of a multi-client server with handler based event dispatch.

Stats

A structure containing stats data average of the course of one second.

UdpSocket

Non-blocking abstraction over a UDP socket.

Enums

ConnectionState

Enum indicating the state of a connection.

MessageKind

Enum for specification of a message handling algorithm.

Traits

Handler

Trait for implementation of a client / server event proxy.

RateLimiter

Trait for implementation of a network congestion avoidance algorithm.

Socket

Trait for implementation of a non-blocking UDP socket.