#multiplayer-game #networking #bevy-plugin #renet #connection #game-networking #gamedev

bevy_renet

Bevy plugin for the renet crate: Server/Client network library for multiplayer games with authentication and connection management

13 releases (1 stable)

1.0.0 Dec 24, 2024
0.0.12 Jul 20, 2024
0.0.11 Feb 21, 2024
0.0.10 Nov 12, 2023
0.0.4 Jul 25, 2022

#986 in Network programming

Download history 305/week @ 2024-09-23 276/week @ 2024-09-30 79/week @ 2024-10-07 294/week @ 2024-10-14 201/week @ 2024-10-21 164/week @ 2024-10-28 212/week @ 2024-11-04 108/week @ 2024-11-11 342/week @ 2024-11-18 564/week @ 2024-11-25 468/week @ 2024-12-02 623/week @ 2024-12-09 328/week @ 2024-12-16 294/week @ 2024-12-23 144/week @ 2024-12-30 302/week @ 2025-01-06

1,157 downloads per month
Used in 4 crates

MIT/Apache

160KB
2.5K SLoC

Bevy Renet

Latest version Documentation MIT Apache

A Bevy Plugin for the renet crate. A network crate for Server/Client with cryptographically secure authentication and encrypted packets. Designed for fast-paced competitive multiplayer games.

Usage

Bevy renet is a small layer over the renet crate, it adds systems to call the update function from the client/server. RenetClient, RenetServer, NetcodeClientTransport and NetcodeServerTransport need to be added as a resource, so the setup is similar to renet itself:

Server

fn main() {
    let mut app = App::new();
    app.add_plugin(RenetServerPlugin);

    let server = RenetServer::new(ConnectionConfig::default());
    app.insert_resource(server);

    // Transport layer setup
    app.add_plugin(NetcodeServerPlugin);
    let server_addr = "127.0.0.1:5000".parse().unwrap();
    let socket = 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 transport = NetcodeServerTransport::new(server_config, socket).unwrap();
    app.insert_resource(transport);

    app.add_system(send_message_system);
    app.add_system(receive_message_system);
    app.add_system(handle_events_system);
}

// Systems

fn send_message_system(mut server: ResMut<RenetServer>) {
    let channel_id = 0;
    // Send a text message for all clients
    // The enum DefaultChannel describe the channels used by the default configuration
    server.broadcast_message(DefaultChannel::ReliableOrdered, "server message");
}

fn receive_message_system(mut server: ResMut<RenetServer>) {
    // Receive message from all clients
    for client_id in server.clients_id() {
        while let Some(message) = server.receive_message(client_id, DefaultChannel::ReliableOrdered) {
            // Handle received message
        }
    }
}

fn handle_events_system(mut server_events: EventReader<ServerEvent>) {
    for event in server_events.read() {
        match event {
            ServerEvent::ClientConnected { client_id } => {
                println!("Client {client_id} connected");
            }
            ServerEvent::ClientDisconnected { client_id, reason } => {
                println!("Client {client_id} disconnected: {reason}");
            }
        }
    }
}

Client

fn main() {
    let mut app = App::new();
    app.add_plugin(RenetClientPlugin);

    let client = RenetClient::new(ConnectionConfig::default());
    app.insert_resource(client);

    // Setup the transport layer
    app.add_plugin(NetcodeClientPlugin);

    let authentication = ClientAuthentication::Unsecure {
        server_addr: SERVER_ADDR,
        client_id: 0,
        user_data: None,
        protocol_id: 0,
    };
    let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
    let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
    let mut transport = NetcodeClientTransport::new(current_time, authentication, socket).unwrap();

    app.insert_resource(transport);

    app.add_system(send_message_system);
    app.add_system(receive_message_system);
}

// Systems

fn send_message_system(mut client: ResMut<RenetClient>) {
    // Send a text message to the server
    client.send_message(DefaultChannel::ReliableOrdered, "server message");
}

fn receive_message_system(mut client: ResMut<RenetClient>) {
    while let Some(message) = client.receive_message(DefaultChannel::ReliableOrdered) {
        // Handle received message
    }
}

Example

You can run the simple example with:

  • Server: cargo run --features="netcode" --example simple -- server
  • Client: cargo run --features="netcode" --example simple -- client

If you want a more complex example you can checkout the demo_bevy sample:

Bevy Demo.webm

Bevy Compatibility

bevy bevy_renet
0.15 1.0
0.14 0.0.12
0.13 0.0.11
0.12 0.0.10
0.11 0.0.9
0.10 0.0.8
0.9 0.0.6
0.8 0.0.5
0.7 0.0.4

Steam

By default bevy_renet uses renet_netcode as the transport layer, but you can also use the steam transport layer if you wish by enabling the steam feature.

This adds the transport structs SteamServerTransport, SteamClientTransport and the bevy plugins SteamServerPlugin, SteamClientPlugin, the setup should be similar to default transport layer.

You can check the Bevy Demo for how to use the default and steam transport switching between them using feature flags.

Custom Schedules

If you want more control over how renet is run, instead of adding the RenetServerPlugin, RenetClientPlugin, you can manually setup the functions they implement (they are all public). Make sure to also setup the plugins for the desired Transport layer.

Dependencies

~10–22MB
~277K SLoC