Library / SDK
lucaspoffo/renet avatar
lucaspoffo/renet

renet keeps the transport out of your game's networking code

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

959 stars116 forksRustApache-2.0

At a glance

What is it?
renet is a Rust networking library for client and server games, built around message channels with configurable delivery guarantees, packet fragmentation and a pluggable transport layer. The core crate handles connections and messages, while netcode and Steam transports live in sibling crates, so the protocol underneath stays a separate decision.
Who is it for?
renet suits a Rust multiplayer game that needs reliable and unreliable message channels, connection events and fragmentation without writing a transport, and it suits teams that want to try netcode first and move to a Steam transport later. It is the wrong tool for a project that has to hand roll its own protocol anyway, and the wrong tool if you need the transport to be part of the same crate rather than a separate layer.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 8 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • lucaspoffo/renet959 ★ · +6
Star history
Repository2026-10-212026-11-07
lucaspoffo/renet953959

6 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The core crate is deliberately transport agnostic

Renet is described as a network library for server and client games written in Rust, focused on fast-paced games such as FPS and competitive games.

The important structural decision is what lives in the core crate and what does not. The feature list names client and server connection management, message based communication using channels, packet fragmentation and reassembly, and authentication and encryption. But the authentication and encryption item points at renet_netcode, and the README also points at renet_steam for using the Steam transport and authentication layer.

The transport and authentication layer can be customized, and you can write your own if necessary. That sentence is the whole design in miniature. Renet owns the session: who is connected, which messages arrived, in what order, and when a resend is due. It does not own the wire.

For a game team this matters more than it first appears. A transport choice is close to irreversible in a live game. UDP with a netcode-style protocol, Steam's networking, and a custom reliable layer have different latency behaviour, different NAT traversal stories and different deployment requirements. Keeping that choice behind an interface means the message and channel code above it stays the same when the answer changes.

The workspace confirms the split in the directory names, with renet, renet_netcode, renet_steam and renetcode as separate members alongside the demos.

Channels carry the delivery guarantees you promise players

Renet communication is message based, and channels describe how the messages should be delivered. There are three kinds, and choosing between them is the main design work a game team does with this library.

ReliableOrdered guarantees message delivery and order. ReliableUnordered guarantees delivery but not order. Unreliable guarantees neither delivery nor order, which is the right choice for state that is superseded constantly, such as a position update that will be replaced a frame later anyway.

The configuration carries the parameters for that choice. A reliable channel resends a lost message after a configured duration, expressed as a resend_time, while an unreliable channel has no such field:

rust
// 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)
};

Each channel also has its own budget. max_memory_usage_bytes is the maximum number of bytes the channel may hold without acknowledgement of messages before becoming full, and the example sets it to 5 megabytes. The channel_id must be unique within its own list but can be repeated between the server and client lists.

Channels are unidirectional, and the two directions are configured separately. ConnectionConfig.client_channels_config describes the channels the client sends to the server, and ConnectionConfig.server_channels_config describes the channels the server sends to the clients.

The memory limit is the parameter worth understanding. When a channel fills up because the peer is not acknowledging, new messages have nowhere to go, so the backpressure appears at the channel rather than as unbounded growth in the game loop. The resend_time is the other end of that bargain, and choosing it too small turns a network problem into a bandwidth problem.

The API is shaped around a frame loop

Renet aims to have a simple API that is easy to integrate with any code base, and the shape of that API follows the game loop rather than the network.

The rule stated in the README is short: poll for new messages at the start of a frame with update, and call send_packets from the transport layer to send packets to the client or server. Everything else hangs off that.

On the server, the loop updates the connection state and then the transport, drains connection events, reads messages per client, and finally asks the transport to send:

rust
    let delta_time = Duration::from_millis(16);
    // Receive new messages and update clients
    server.update(delta_time);
    transport.update(delta_time, &mut server)?;

The event drain is what handles joins and leaves, with ServerEvent::ClientConnected and ServerEvent::ClientDisconnected carrying a client id and, for disconnections, a reason. The message reading is a while let loop over server.receive_message for a given channel and client id, so a frame drains everything queued rather than one message.

Sending has three shapes: broadcast_message to everyone, broadcast_message_except to everyone but one client, and send_message to a single client. The transport is then asked to send_packets, which is where the actual bytes leave.

The client side mirrors this with client.update, transport.update, an is_connected check, receive_message and send_message.

Two consequences are visible in the example loop and worth stating. First, both update calls take a delta_time, so renet is time-driven rather than event-driven, and the example runs at 60hz with a sleep. Second, the transport is a separate object that borrows the session mutably in update and send_packets, which is why the transport type is a parameter to those calls rather than a field of the session.

Fragmentation and the example that runs unsecure

Two entries in the feature list deserve to be pulled out, because they change what you have to think about.

The first is packet fragmentation and reassembly. If a message is larger than a network packet can carry, renet splits it and puts it back together on the other side. That is a service to a game team, because without it a large message either fails or has to be chunked by hand in every call site.

The second is the authentication story, and the examples show a detail that deserves attention. The server configuration in the README is built with ServerAuthentication::Unsecure, and the client with ClientAuthentication::Unsecure, with a server address, a client id, no user data and a protocol id of zero.

Those examples are for a local run, and Unsecure means the transport is not authenticating the peer. The real transport lives in renet_netcode, which is what provides the authentication and encryption the feature list refers to, and renet_steam is the alternative for games distributed through Steam. The point for a reader is that the security property lives in the transport crate, not in the core session code, so the examples are not a demonstration of a secured server, they are a demonstration of the API shape.

The other configuration fields are visible in the same example. max_clients is 64, protocol_id is 0, and public_addresses holds the server address. The protocol id is the value a client and server must agree on before they will talk to each other, and setting it to something non-trivial is one of the cheapest ways to avoid unrelated clients connecting to your server.

The workspace shows the surrounding ecosystem

The Cargo workspace lists eight members, and reading them tells you what the project has grown into.

The members are renet, demo_chat, demo_bevy, renetcode, bevy_renet, renet_visualizer, renet_steam and renet_netcode. Three of them are the library layers, renet for the core, renet_netcode for the netcode transport and renet_steam for the Steam transport, with renetcode sitting alongside as the protocol implementation that renet_netcode uses.

bevy_renet is the integration for the Bevy engine, and the release history shows it moving in step with the engine, with bevy_renet-5.0 published on 2026-06-20 and titled as support for Bevy 0.19, and an earlier 1.2.0 release titled as Bevy 0.17 support. If your game is in Bevy, this is the crate you will actually depend on, and its version has to match your engine version rather than your own choice.

renet_visualizer is the one that deserves a note, since a networking library is otherwise opaque. A visualizer that renders the traffic is the difference between debugging a network problem from logs and debugging it from a picture of what went over the wire.

The two demos, demo_chat and demo_bevy, are the reference implementations, and they sit in the workspace next to the library rather than in a separate repository, so they are built and versioned with it.

The repository also carries deny.toml, which is the configuration for cargo-deny, a tool that checks dependency licences and advisories, and a rustfmt.toml for formatting. The presence of deny.toml is a small signal about how the project treats its supply chain, since it means licence compatibility of dependencies is checked rather than assumed.

Demos, releases and the licensing choice

The quickest way to see the library work is the echo example, which lives in the renet crate and has a documented invocation:

bash
cargo run --example echo -- server 5000
cargo run --example echo -- client 127.0.0.1:5000 CoolNickName

One process is the server and the other is the client, and the third argument is the nickname the client sends. Beyond that there are the two more complex demos, including a simple Bevy application that demonstrates replicating entities and sending reliable messages, and a chat demo for the plain client and server path.

The release history shows a project that is versioned across the whole workspace rather than as a single crate. The recent tags are bevy_renet-5.0 on 2026-06-20 for support to Bevy 0.19, Renet 2.0 on 2026-01-20, and 1.2.0 on 2025-10-09, which is the release tagged as Bevy 0.17 support. The last push to the repository was on 2026-06-20, and the repository is not archived. There is no stated release cadence, so the tags are the only guide to how quickly this moves.

The licensing is dual, with both LICENSE-MIT and LICENSE-APACHE in the repository and the README carrying badges for both. That is a common choice for a library that wants to be usable inside projects with different licence policies, and it means you can take either terms depending on what your game is distributed under.

One packaging detail worth knowing before you add the dependency. The core crate and the transport crates are separate packages on crates.io, so the version of renet you pin does not automatically pin renet_netcode, and the transport is what carries the authentication behaviour.

Editorial conclusion

renet suits a Rust multiplayer game that needs reliable and unreliable message channels, connection events and fragmentation without writing a transport, and it suits teams that want to try netcode first and move to a Steam transport later. It is the wrong tool for a project that has to hand roll its own protocol anyway, and the wrong tool if you need the transport to be part of the same crate rather than a separate layer. Before you commit, check the transport you actually want, since renet_netcode and renet_steam are separate crates with their own versions, and read the example configuration carefully, because the sample server runs with ServerAuthentication::Unsecure and a protocol id of zero, both of which need changing before this goes anywhere near a public server.

Frequently asked questions

What does renet do for a multiplayer game?

Renet provides client and server connection management, message based communication over channels with different delivery guarantees, packet fragmentation and reassembly, and a transport layer for authentication and encryption. It is focused on fast-paced games such as FPS and competitive games.

How do I choose between reliable and unreliable channels in renet?

ReliableOrdered guarantees delivery and order, ReliableUnordered guarantees delivery but not order, and Unreliable guarantees neither. A reliable channel also takes a resend_time, which is how long to wait before resending a lost message, and every channel has a max_memory_usage_bytes limit before it becomes full.

Which transport layer should I use with renet?

The README points at renet_netcode for UDP with the netcode protocol and renet_steam for the Steam transport and authentication layer. The transport and authentication layer can be customized, and you can write your own if necessary.

How do I run the renet echo example?

Run cargo run --example echo -- server 5000 for the server and cargo run --example echo -- client 127.0.0.1:5000 CoolNickName for the client. The repository also contains demo_chat and demo_bevy, and a Bevy application demonstrating entity replication with reliable messages.

What licence does renet use and how is it versioned?

Renet is dual licensed under MIT and Apache, with both LICENSE-MIT and LICENSE-APACHE in the repository. Releases cover the whole workspace, with Renet 2.0 published on 2026-01-20 and bevy_renet-5.0 on 2026-06-20 for support to Bevy 0.19.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. lucaspoffo/renet on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/lucaspoffo-renet.svg)](https://hysenlabs.com/projects/lucaspoffo-renet)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/lucaspoffo-renet.svg)](https://hysenlabs.com/projects/lucaspoffo-renet)

Was this useful?