Crates.io | aeronet |
lib.rs | aeronet |
version | 0.4.0 |
source | src |
created_at | 2023-10-07 17:04:26.202778 |
updated_at | 2023-12-14 21:55:12.373149 |
description | Lightweight client/server network library |
homepage | |
repository | https://github.com/aecsocket/aeronet |
max_upload_size | |
id | 996480 |
size | 45,031 |
aeronet
Note: This currently uses Rust nightly. When #91611 is stabilized, this will be switched to stable.
A light-as-air client/server networking library with first-class support for Bevy, providing a consistent API which can be implemented by different transport mechanisms.
Aeronet's main feature is the transport - an interface for sending data to and receiving data from an endpoint, either the client or the server. You write your code against this interface (or use the Bevy plugin which provides events used by the transport), and you don't have to worry about the underlying mechanism used to transport your data.
aeronet_channel
via in-memory MPSC channels, useful
for local singleplayer serversaeronet_wt_native
via a Rust implementation of
WebTransport, useful for a generic client-server architecture with support for WASM clientsaeronet_wt_wasm
via the browser's implementation of
WebTransport, useful for a WASM app which requires a networking clientFirst, you will need two [Message
] types to use for sending client-to-server (C2S) and
server-to-client messages (S2C). They may be the same type.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum C2S {
Move(f32),
Shoot,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum S2C {
AddPlayer(String),
UpdateHealth(f32),
}
fn assert_is_message<T: aeronet::Message>() {}
assert_is_message::<C2S>();
assert_is_message::<S2C>();
Create a type implementing [TransportProtocol
] which uses these message types.
use aeronet::TransportProtocol;
pub enum C2S { /* ... */ }
pub enum S2C { /* ... */ }
#[derive(Debug)]
pub struct AppProtocol;
impl TransportProtocol for AppProtocol {
type C2S = C2S;
type S2C = S2C;
}
Note that certain transport implementations may need you to implement extra traits on your protocol type.
Then, you will need a transport implementation to use. Select one from the list above that suits
your needs. Afterwards, use the [TransportClient
] and [TransportServer
] traits to interact with
the transport, to do functions such as sending and receiving data.
use std::time::Duration;
use aeronet::{TransportClient, Rtt};
# #[derive(Debug)]
# pub enum C2S { Shoot }
#
# fn run<P, Client, ConnInfo>(mut client: Client)
# where
# P: aeronet::TransportProtocol<C2S = C2S>,
# Client: TransportClient<P, ConnectionInfo = ConnInfo>,
# ConnInfo: Rtt,
# {
client.send(C2S::Shoot);
if let Some(conn_info) = client.connection_info() {
let rtt: Duration = conn_info.rtt();
println!("Latency to server: {rtt:?}");
}
# }
The traits defined in this crate lay out a client/server architecture - one central server which multiple clients can connect to. The most popular alternative is peer-to-peer, but explaining the differences, advantages, and disadvantages of these architectures are outside the scope of this.
A transport is not necessarily networked - that is, one that communicates to other computers, probably using the internet. Instead, transport can also work using something as simple as in-memory channels or some other non-networked method.
The method used to transport the data itself (i.e. unreliable, reliable ordered, etc.) is also
exposed by this crate - see [ChannelKind
] for more info.
The type of data that is sent between endpoints is a type implementing [Message
], but the exact
type is left up to the user of the transport. The user must define their own type implementing
[Protocol
], which specifies what type of message is sent client-to-server and server-to-client,
then use this protocol type throughout their transports.
Transport traits provide no guarantees about in what form the messages are transported. The
memory (and therefore ownership) of the value may be sent directly, in the case of an in-memory
MPSC channel, or may have to be serialized to/from a byte form before being transported. In this
case, [TryFromBytes
] and [TryIntoBytes
] are useful to look at.
This crate abstracts over the complexities of connection by defining two states:
Although the networking implementation is likely to be much more complex, including encryption, handshakes, etc., these two states can be used as a basic contract for networking code. However, the implementation may also choose to expose some more of these details.