Adding bevy networking
Some checks failed
Rust Checks / checks (push) Has been cancelled

This commit is contained in:
Tipragot 2024-02-10 13:06:32 +01:00
parent 9f9ac40a13
commit fb2850deef
3 changed files with 258 additions and 3 deletions

11
Cargo.lock generated
View file

@ -403,8 +403,10 @@ dependencies = [
"aes-gcm", "aes-gcm",
"base64 0.21.7", "base64 0.21.7",
"bevy", "bevy",
"bincode",
"igd", "igd",
"local-ip-address", "local-ip-address",
"serde",
] ]
[[package]] [[package]]
@ -1159,6 +1161,15 @@ dependencies = [
"winit", "winit",
] ]
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "bindgen" name = "bindgen"
version = "0.69.4" version = "0.69.4"

View file

@ -17,3 +17,5 @@ aes-gcm = "0.10.3"
base64 = "0.21.7" base64 = "0.21.7"
igd = "0.12.1" igd = "0.12.1"
bevy = "0.12.1" bevy = "0.12.1"
serde = "1.0.196"
bincode = "1.3.3"

View file

@ -50,11 +50,15 @@ use std::sync::Mutex;
use aes_gcm::aead::{Aead, AeadCore, KeyInit, OsRng}; use aes_gcm::aead::{Aead, AeadCore, KeyInit, OsRng};
use aes_gcm::{Aes128Gcm, Key, Nonce}; use aes_gcm::{Aes128Gcm, Key, Nonce};
use base64::prelude::*; use base64::prelude::*;
use bevy::ecs::schedule::SystemConfigs;
use bevy::prelude::*; use bevy::prelude::*;
use igd::{Gateway, PortMappingProtocol}; use igd::{Gateway, PortMappingProtocol};
use local_ip_address::local_ip; use local_ip_address::local_ip;
use serde::de::DeserializeOwned;
use serde::Serialize;
/// A non-blocking tcp connection. /// A non-blocking tcp connection.
#[derive(Component, Resource)]
pub struct Connection { pub struct Connection {
/// The underlying [TcpStream] used for the connection. /// The underlying [TcpStream] used for the connection.
stream: TcpStream, stream: TcpStream,
@ -231,7 +235,7 @@ impl Connection {
} }
// Returning success. // Returning success.
Ok(self.send_buffers.is_empty()) Ok(())
} }
/// Receives a byte block from the connection. /// Receives a byte block from the connection.
@ -375,6 +379,7 @@ impl Connection {
} }
/// A non-blocking tcp listener. /// A non-blocking tcp listener.
#[derive(Resource)]
pub struct Listener(TcpListener, Gateway, SocketAddrV4, Key<Aes128Gcm>); pub struct Listener(TcpListener, Gateway, SocketAddrV4, Key<Aes128Gcm>);
impl Listener { impl Listener {
@ -441,5 +446,242 @@ impl Drop for Listener {
#[derive(Resource, Default)] #[derive(Resource, Default)]
struct LastEventId(u16); struct LastEventId(u16);
/// An extension trait for the bevy [App] to allow registering network events. /// An utility function that sends an [Event] to a [Connection].
pub trait NetworkAppExt {} fn send_event<T: Event + Serialize + DeserializeOwned>(
connection: &Connection,
event_id: u16,
event: &T,
) {
// Serializing the event.
let message = match bincode::serialize(event) {
Ok(message) => message,
Err(e) => {
error!("failed to serialize event: {e}");
return;
}
};
// Sending the event.
if let Err(e) = connection.send(&message, event_id) {
info!("failed to send event: {e}");
}
}
/// An [Event] received from the server on the client.
#[derive(Event)]
pub struct ServerEvent<T: Event + Serialize + DeserializeOwned>(pub T);
impl<T: Event + Serialize + DeserializeOwned> ServerEvent<T> {
/// Returns a system that receives the events from the server.
pub fn receive(event_id: u16) -> SystemConfigs {
(move |connection: Res<Connection>, mut events: EventWriter<Self>| {
// Get all received messages.
let messages = match connection.receive(event_id) {
Ok(messages) => messages,
Err(e) => {
error!("failed to receive event: {e}");
return;
}
};
// Send the events in the world.
for message in messages {
// Deserializing the message.
let event: T = match bincode::deserialize(&message) {
Ok(event) => event,
Err(e) => {
error!("failed to deserialize event: {e}");
continue;
}
};
// Sending the event.
events.send(Self(event));
}
})
.run_if(resource_exists::<Connection>())
}
}
/// An [Event] received from a specific client on the server.
#[derive(Event)]
pub struct ClientEvent<T: Event + Serialize + DeserializeOwned>(pub Entity, pub T);
impl<T: Event + Serialize + DeserializeOwned> ClientEvent<T> {
/// Returns a system that receives the events from the clients on the
/// server.
pub fn receive(event_id: u16) -> impl Fn(Query<(Entity, &Connection)>, EventWriter<Self>) {
move |connections: Query<(Entity, &Connection)>, mut events: EventWriter<Self>| {
for (entity, connection) in connections.iter() {
// Get all received messages.
let messages = match connection.receive(event_id) {
Ok(messages) => messages,
Err(e) => {
error!("failed to receive event: {e}");
continue;
}
};
// Send the events in the world.
for message in messages {
// Deserializing the message.
let event: T = match bincode::deserialize(&message) {
Ok(event) => event,
Err(e) => {
error!("failed to deserialize event: {e}");
continue;
}
};
// Sending the event.
events.send(Self(entity, event));
}
}
}
}
}
/// An [Event] used to send a network event to all the connected clients on the
/// server.
#[derive(Event)]
pub struct SendAll<T: Event + Serialize + DeserializeOwned>(pub T);
impl<T: Event + Serialize + DeserializeOwned> SendAll<T> {
/// Returns a system that sends the events to all the connected clients on
/// the server.
pub fn send(event_id: u16) -> impl Fn(EventReader<Self>, Query<&Connection>) {
move |mut events: EventReader<Self>, connections: Query<&Connection>| {
for event in events.read() {
for connection in connections.iter() {
send_event(connection, event_id, &event.0);
}
}
}
}
}
/// An [Event] used to send a network event to a specific client on the server.
#[derive(Event)]
pub struct SendTo<T: Event + Serialize + DeserializeOwned>(pub Entity, pub T);
impl<T: Event + Serialize + DeserializeOwned> SendTo<T> {
/// Returns a system that sends the events to a specific client on the
/// server.
pub fn send(event_id: u16) -> impl Fn(EventReader<Self>, Query<&Connection>) {
move |mut events: EventReader<Self>, connections: Query<&Connection>| {
for event in events.read() {
match connections.get(event.0) {
Ok(connection) => send_event(connection, event_id, &event.1),
Err(e) => error!("tried to send event to non-existent connection: {e}"),
}
}
}
}
}
/// An [Event] used to send an [Event] from a client to the server.
#[derive(Event)]
pub struct SendToServer<T: Event + Serialize + DeserializeOwned>(pub T);
impl<T: Event + Serialize + DeserializeOwned> SendToServer<T> {
/// Returns a system that sends the events from a client to the server.
pub fn send(event_id: u16) -> SystemConfigs {
(move |mut events: EventReader<Self>, connection: Option<Res<Connection>>| {
for event in events.read() {
if let Some(connection) = connection.as_ref() {
send_event(connection, event_id, &event.0);
} else {
error!("tried to send event to non-existent connection");
}
}
})
.run_if(resource_exists::<Connection>())
}
}
/// A plugin that manages network [Connection]s.
pub struct NetworkPlugin;
impl NetworkPlugin {
/// A system that update the client [Connection]s on the server.
fn update_client_connections(
mut commands: Commands,
mut connections: Query<(Entity, &mut Connection)>,
) {
for (entity, mut connection) in &mut connections {
if let Err(e) = connection.update() {
info!("closing client connection: {e}");
commands.entity(entity).remove::<Connection>();
}
}
}
/// A system that update the server [Connection] on the client.
fn update_server_connection(mut commands: Commands, mut connection: ResMut<Connection>) {
if let Err(e) = connection.update() {
info!("closing server connection: {e}");
commands.remove_resource::<Connection>();
}
}
}
impl Plugin for NetworkPlugin {
fn build(&self, app: &mut App) {
app.add_systems(
Last,
(
Self::update_client_connections,
Self::update_server_connection.run_if(resource_exists::<Connection>()),
),
);
}
}
/// An extension trait for a bevy [App] that adds network related features.
pub trait NetworkAppExt {
/// Setup the application to manage network events of type `T`.
///
/// This will automatically define multiple events:
/// - [ServerEvent]: an [Event] received from the server on the client.
/// - [ClientEvent]: an [Event] received from a specific client on the
/// server.
/// - [SendAll]: used to send a network event to all the connected clients
/// on the server.
/// - [SendTo]: used to send a network event to a specific client on the
/// server.
/// - [SendToServer]: used to send an [Event] from a client to the server.
///
/// # Examples
///
/// ```
/// use bevnet::NetworkAppExt;
/// use bevy::prelude::*;
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Event, Deserialize, Serialize)]
/// struct MyEvent;
///
/// let mut app = App::new();
/// app.add_network_event::<MyEvent>();
/// ```
fn add_network_event<T: Event + DeserializeOwned + Serialize>(&mut self) -> &mut Self;
}
impl NetworkAppExt for App {
fn add_network_event<T: Event + DeserializeOwned + Serialize>(&mut self) -> &mut Self {
let mut event_id = self.world.get_resource_or_insert_with(LastEventId::default);
event_id.0 += 1;
let event_id = event_id.0;
self.add_event::<ServerEvent<T>>()
.add_systems(PostUpdate, ServerEvent::<T>::receive(event_id))
.add_event::<ClientEvent<T>>()
.add_systems(PostUpdate, ClientEvent::<T>::receive(event_id))
.add_event::<SendAll<T>>()
.add_systems(PreUpdate, SendAll::<T>::send(event_id))
.add_event::<SendTo<T>>()
.add_systems(PreUpdate, SendTo::<T>::send(event_id))
.add_event::<SendToServer<T>>()
.add_systems(PreUpdate, SendToServer::<T>::send(event_id))
}
}