15次发布

0.0.16 2024年7月20日
0.0.15 2024年2月21日
0.0.14 2023年11月12日
0.0.13 2023年7月20日
0.0.4 2021年12月27日

#133 in 网络编程

Download history 250/week @ 2024-04-25 223/week @ 2024-05-02 243/week @ 2024-05-09 275/week @ 2024-05-16 336/week @ 2024-05-23 412/week @ 2024-05-30 295/week @ 2024-06-06 368/week @ 2024-06-13 399/week @ 2024-06-20 312/week @ 2024-06-27 281/week @ 2024-07-04 341/week @ 2024-07-11 659/week @ 2024-07-18 643/week @ 2024-07-25 402/week @ 2024-08-01 496/week @ 2024-08-08

2,253 每月下载量
用于 9 个Crate(直接使用5个)

MIT/Apache

155KB
3K SLoC

Renet

Latest version Documentation MIT Apache

Renet 是用 Rust 编写的 Server/Client 游戏网络库。它专注于快速游戏,如 FPS 和竞技游戏。提供以下功能

  • 客户端/服务器连接管理
  • 基于通道的消息通信,它们可以有不同的保证
    • ReliableOrdered:保证消息交付和顺序
    • ReliableUnordered:保证消息交付但无顺序
    • Unreliable:不保证消息交付或顺序
  • 数据包分片和重组
  • 身份验证和加密,使用 renetcode
    • 传输层可自定义。默认传输可以禁用并替换为自定义的

通道

Renet 通信基于消息,通道描述了消息的交付方式。通道是单向的,ConnectionConfig.client_channels_config 描述了客户端发送给服务器的通道,而 ConnectionConfig.server_channels_config 描述了服务器发送给客户端的通道。

每个通道都有自己的配置 ChannelConfig

// No guarantee of message delivery or order
let send_type = SendType::Unreliable;
// guarantee 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
};

使用方法

Renet 旨在拥有一个简单且易于与任何代码库集成的API。使用 update 在帧开始时轮询新消息。从传输层调用 send_packets 向客户端/服务器发送数据包。

服务器

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
}

客户端

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
}

示例

您可以通过 echo 示例 了解库的简单用法。使用方法

  • 服务器: cargo run --example echo -- server 5000
  • 客户端:cargo run --example echo -- client 127.0.0.1:5000 CoolNickName

或者您可以查看两个更复杂地使用了renet的演示。

Bevy 演示
一个简单的Bevy应用,展示了如何使用renet复制实体并从服务器/客户端发送可靠的命令消息。

Bevy Demo.webm

仓库

聊天演示
一个简单的聊天应用,使用egui制作,展示了如何处理错误、状态转换和客户端自托管。

Chat Demo.webm

仓库

插件

如果您想在Bevy 引擎中使用renet作为插件,请查看bevy_renet

如果您想使用steam传输层而不是默认的传输层,请查看renet_steam

可视化工具

如果您想查看从renet客户端和服务器收集的指标数据,请查看renet_visualizer

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

依赖

~0.2–3.5MB
~62K SLoC