generated from tipragot/rust
This commit is contained in:
parent
9f9ac40a13
commit
fb2850deef
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue