#message-delivery #transport-layer #networking #connection #gamedev #channel #multiplayer

renet

Server/Client network library for multiplayer games with authentication and connection management

14 releases

0.0.15 Feb 21, 2024
0.0.14 Nov 12, 2023
0.0.13 Jul 20, 2023
0.0.11 Mar 12, 2023
0.0.4 Dec 27, 2021

#150 in Network programming

Download history 155/week @ 2023-12-22 182/week @ 2023-12-29 173/week @ 2024-01-05 176/week @ 2024-01-12 163/week @ 2024-01-19 323/week @ 2024-01-26 252/week @ 2024-02-02 223/week @ 2024-02-09 439/week @ 2024-02-16 503/week @ 2024-02-23 499/week @ 2024-03-01 353/week @ 2024-03-08 212/week @ 2024-03-15 270/week @ 2024-03-22 402/week @ 2024-03-29 163/week @ 2024-04-05

1,086 downloads per month
Used in 12 crates (5 directly)

MIT/Apache

155KB
3K SLoC

Renet

Latest version Documentation MIT Apache

Renet is a network library for Server/Client games written in rust. It is focused on fast-paced games such as FPS, and competitive games. Provides the following features:

  • Client/Server connection management
  • Message based communication using channels, they can have different garantees:
    • ReliableOrdered: garantee of message delivery and order
    • ReliableUnordered: garantee of message delivery but not order
    • Unreliable: no garantee of message delivery or order
  • Packet fragmention and reassembly
  • Authentication and encryption, using renetcode
    • The transport layer can be customizable. The default transport can be disabled and replaced with a custom one

Channels

Renet communication is message based, and channels describe how the messages should be delivered. Channels are unilateral, ConnectionConfig.client_channels_config describes the channels that the clients sends to the server, and ConnectionConfig.server_channels_config describes the channels that the server sends to the clients.

Each channel has its own configuration ChannelConfig:

// No garantee of message delivery or order
let send_type = SendType::Unreliable;
// Garantee of message delivery and order
let send_type = SendType::ReliableOrdered {
    // If a message is lost, it will be resent after this duration
    resend_time: Duration::from_millis(300)
};

// Guarantee of message delivery but not order
let send_type = SendType::ReliableUnordered {
    resend_time: Duration::from_millis(300)
};

let channel_config = ChannelConfig {
    // The id for the channel, must be unique within its own list,
    // but it can be repeated between the server and client lists.
    channel_id: 0,
    // Maximum number of bytes that the channel may hold without acknowledgement of messages before becoming full.
    max_memory_usage_bytes: 5 * 1024 * 1024, // 5 megabytes
    send_type
};

Usage

Renet aims to have a simple API that is easy to integrate with any code base. Pool for new messages at the start of a frame with update. Call send_packets from the transport layer to send packets to the client/server.

Server

let mut server = RenetServer::new(ConnectionConfig::default());

// Setup transport layer
const SERVER_ADDR: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 5000);
let socket: UdpSocket = UdpSocket::bind(SERVER_ADDR).unwrap();
let server_config = ServerConfig {
    current_time: SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(),
    max_clients: 64,
    protocol_id: 0,
    public_addresses: vec![SERVER_ADDR],
    authentication: ServerAuthentication::Unsecure
};
let mut transport = NetcodeServerTransport::new(server_config, socket).unwrap();

// Your gameplay loop
loop {
    let delta_time = Duration::from_millis(16);
    // Receive new messages and update clients
    server.update(delta_time);
    transport.update(delta_time, &mut server)?;
    
    // Check for client connections/disconnections
    while let Some(event) = server.get_event() {
        match event {
            ServerEvent::ClientConnected { client_id } => {
                println!("Client {client_id} connected");
            }
            ServerEvent::ClientDisconnected { client_id, reason } => {
                println!("Client {client_id} disconnected: {reason}");
            }
        }
    }

    // Receive message from channel
    for client_id in server.clients_id() {
        // The enum DefaultChannel describe the channels used by the default configuration
        while let Some(message) = server.receive_message(client_id, DefaultChannel::ReliableOrdered) {
            // Handle received message
        }
    }
    
    // Send a text message for all clients
    server.broadcast_message(DefaultChannel::ReliableOrdered, "server message");

    let client_id = ClientId::from_raw(0);
    // Send a text message for all clients except for Client 0
    server.broadcast_message_except(client_id, DefaultChannel::ReliableOrdered, "server message");
    
    // Send message to only one client
    server.send_message(client_id, DefaultChannel::ReliableOrdered, "server message");
 
    // Send packets to clients using the transport layer
    transport.send_packets(&mut server);

    std::thread::sleep(delta_time); // Running at 60hz
}

Client

let mut client = RenetClient::new(ConnectionConfig::default());

// Setup transport layer
const server_addr: SocketAddr = "127.0.0.1:5000".parse().unwrap();
let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
let authentication = ClientAuthentication::Unsecure {
    server_addr,
    client_id: 0,
    user_data: None,
    protocol_id: 0,
};

let mut transport = NetcodeClientTransport::new(current_time, authentication, socket).unwrap();

// Your gameplay loop
loop {
    let delta_time = Duration::from_millis(16);
    // Receive new messages and update client
    client.update(delta_time);
    transport.update(delta_time, &mut client).unwrap();
    
    if client.is_connected() {
        // Receive message from server
        while let Some(message) = client.receive_message(DefaultChannel::ReliableOrdered) {
            // Handle received message
        }
        
        // Send message
        client.send_message(DefaultChannel::ReliableOrdered, "client text");
    }
 
    // Send packets to server using the transport layer
    transport.send_packets(&mut client)?;
    
    std::thread::sleep(delta_time); // Running at 60hz
}

Demos

You can checkout the echo example for a simple usage of the library. Usage:

  • Server: cargo run --example echo -- server 5000
  • Client: cargo run --example echo -- client 127.0.0.1:5000 CoolNickName

Or you can look into the two demos that have more complex uses of renet:

Bevy Demo
Simple bevy application to demonstrate how you could replicate entities and send reliable messages as commands from the server/client using renet:

Bevy Demo.webm

Repository

Chat Demo
Simple chat application made with egui to demonstrate how you could handle errors, states transitions and client self hosting:

Chat Demo.webm

Repository

Plugins

Checkout bevy_renet if you want to use renet as a plugin with the Bevy engine.

Visualizer

Checkout renet_visualizer for a egui plugin to plot metrics data from renet clients and servers:

https://user-images.githubusercontent.com/35241085/175834010-b1eafd77-7ea2-47dc-a915-a399099c7a99.mp4

Dependencies

~0.2–3MB
~59K SLoC