11 unstable releases (5 breaking)

0.7.0 Aug 25, 2024
0.7.0-alpha.2 Aug 16, 2024
0.7.0-alpha.1 Jul 30, 2024
0.6.0 Jul 30, 2024
0.1.4 Oct 9, 2023

#222 in Network programming

Download history 2/week @ 2024-06-15 66/week @ 2024-07-06 1/week @ 2024-07-13 4/week @ 2024-07-20 246/week @ 2024-07-27 15/week @ 2024-08-03 71/week @ 2024-08-10 77/week @ 2024-08-17 95/week @ 2024-08-24 17/week @ 2024-08-31 1/week @ 2024-09-07 7/week @ 2024-09-14 28/week @ 2024-09-21 16/week @ 2024-09-28

55 downloads per month
Used in 7 crates

MIT/Apache

78KB
776 lines

aeronet

crates.io docs.rs

A light-as-air client/server transport library with first-class support for Bevy, providing a consistent API which can be implemented by different transport mechanisms.

Try the example!

Start the server:

cargo run --bin move_box_server

Run a native desktop client:

cargo run --bin move_box_client

Run the client in a browser:

cargo install wasm-server-runner
cargo run --bin move_box_client --target wasm32-unknown-unknown

And connect to http://[::1]:25565.

See the examples folder for the source code.

Screenshot of the move_box example, showing some server log output in the console, and two connected clients represented as boxes controlled by the user

Transport

The main purpose of this crate is to provide an API for transmitting messages between a client and a server over any type of connection - in-memory channels, networked, WASM, etc. This is done through the traits ClientTransport and ServerTransport.

The current transport implementations available are:

  • aeronet_channel - using in-memory MPSC channels
    • Useful for non-networked scenarios, such as a local singleplayer server
    • Targets: Native + WASM
    • cargo run --package aeronet_channel --example echo --features "bevy"
  • aeronet_webtransport - using the WebTransport protocol, based on QUIC
    • Good choice for a general transport implementation
    • Targets: Native (client + server) + WASM (client)
    • cargo run --package aeronet_webtransport --example echo_client --features "bevy dangerous-configuration"
    • cargo run --package aeronet_webtransport --example echo_client --features "bevy dangerous-configuration" --target wasm32-unknown-unknown
      • Requires wasm-server-runner to be installed
    • cargo run --package aeronet_webtransport --example echo_server --features "bevy"
  • aeronet_steam - using Steam's NetworkingSockets API
    • STILL WIP
    • Targets: Native
    • cargo run --package aeronet_steam --example echo_client --features "bevy"
    • cargo run --package aeronet_steam --example echo_server --features "bevy"

Goals

This crate aims to be:

  • Generic over as many transports as possible
    • You should be able to plug nearly anything in as the underlying transport layer, and have things work
    • To achieve this, aeronet provides its own implementation of certain protocol elements such as fragmentation and reliable messages - see aeronet_proto
  • Integrated with Bevy
    • Built with apps and games in mind, the abstractions chosen closely suit Bevy's app model, and likely other similar frameworks
  • Simple in terms of API
    • The complexity of the underlying transport is abstracted away, which allows for both flexibility in implementation, and less cognitive load on the API user
    • Configuration options are still exposed, however there are always a set of sane defaults
  • Comfortable for non-async code
    • This crate abstracts away transport-related async code, and exposes a simpler sync API.
  • Lightweight and have a small footprint
    • The crate minimizes the amount of data copied by using Bytes, reducing allocations
    • Features such as reliability and ordering are implemented with a small memory footprint

This crate does not aim to be:

  • A high-level app networking library, featuring replication, rollback, etc.
    • This crate only concerns the transport of data payloads, not what the payloads actually contain
  • An async library
  • #![no_std]
  • A non-client-to-server networking library (e.g. peer-to-peer)
    • A client is expected to only have at most 1 connection to a server - although this server could also be a client who is running the same app

Overview

Messages

The smallest unit of transmission that the API exposes is a message. A message is represented as a Bytes - a container for a byte sequence which allows zero-copy networking code. It is up to the user to give meaning to these bytes.

Lanes

Lanes define the manner in which a message is delivered to the other side, such as unreliable, reliable ordered, etc. These are similar to streams or channels in some protocols, but lanes are abstractions over the manner of delivery, rather than the individual stream or channel. The types of lanes that are supported, and therefore what guarantees are given, are listed in LaneKind.

Note that if a transport does not support lanes, then it inherently guarantees the strongest guarantees provided by lanes - that is, communication is always reliable-ordered.

Typically, a transport implementation will require you to pass a configuration on creation, which defines which lanes are available to the transport, and what their properties are (i.e. is it reliable, ordered, etc).

Bevy plugin

Feature flag: bevy

This crate provides some useful items and types for working with transports, which can be added to your app as a resource. However, note that no plugins are provided - instead, it is your responsibility to drive the transport event loop manually.

Conditioning

Feature flag: condition - depends on getrandom, which may not work in WASM

A common strategy used for ensuring that your network code is robust against failure is to add artificial packet loss and delays. This crate provides a utility for this via the condition module.

Protocol

Crate: aeronet_proto

This crate provides a reusable set of transport-level abstractions which can be used by transport implementations, if they do not support certain features already. This makes providing a new transport implementation easy, since you just plug in these features into the underlying byte stream or whatever other mechanism your transport uses.

bevy_replicon integration

Crate: aeronet_replicon

Using this crate, you can plug any aeronet transport into bevy_replicon as a backend, giving you high-level networking features such as entity replication, while still being free to use any transport implementation under the hood.

Getting started

Using an existing transport

If you want to use one of the transports already supported (which is probably what you want to do), add both this crate and the transport implementation crate as dependencies to your project:

[dependencies]
aeronet = "version"
aeronet_whatever_transport_impl = "version"

The version of this crate is synced between all official subcrates of aeronet - use the same version that you use for aeronet for your transport, and you're good to go.

To create a value for your given transport, and how exactly to configure it, see the transport's Getting Started section in the readme. If using Bevy, you should insert the transport as a resource into your app. Otherwise, keep a hold of your transport somewhere where you can use it in your main update loop - you will need to manually drive it by polling.

You can use the traits ClientTransport and ServerTransport to control your client or server, such as sending and receiving messages.

Client and server state

This crate abstracts a client's connection into either disconnected, connecting, or connected; and servers into closed, opening, or open. By default, clients start disconnected, and servers start closed - you must manually start a connection or open the server, by providing a configuration such as address to connect to, port to open on, etc. This will vary depending on the transport implementation.

See ClientState and ServerState for more info.

Managing the connection

After a connection is established:

  • use send to buffer up a message for sending from this peer to the other side
  • use flush to flush all buffered messages and actually send them across the transport
  • use poll to update the internal state of the transport and receive events about what happened

It is recommended that you use send to buffer up messages for sending during your app's update, then use poll and flush at the end of each update to finalize the update.

It is up to you to encode and decode your own data into the Bytes.

use aeronet::bytes::Bytes;
use aeronet::client::{ClientEvent, ClientTransport};
use aeronet::lane::LaneIndex;

#[derive(Debug, Clone, Copy)]
enum AppLane {
    HighPriority,
    LowPriority,
}

impl From<AppLane> for LaneIndex {
    fn from(value: AppLane) -> Self {
        match value {
            AppLane::HighPriority => LaneIndex::from_raw(0),
            AppLane::LowPriority => LaneIndex::from_raw(1),
        }
    }
}

# fn run(mut client: impl ClientTransport, delta_time: web_time::Duration) {
let message: Bytes = Bytes::from_static(b"hello world");
client.send(message, AppLane::HighPriority).unwrap();

client.flush().unwrap();

for event in client.poll(delta_time) {
    match event {
        ClientEvent::Recv { msg, lane } => {
            let msg = String::from_utf8(Vec::from(msg)).unwrap();
            println!("Received on {lane:?}: {msg}");
        }
        _ => unimplemented!()
    }
}
# }

Bevy support

bevy aeronet
0.14 0.7
0.13 0.6

Dependencies

~0.5–45MB
~678K SLoC