(&self, packet: &P) {
+ let mut data = bincode::serialize(packet).expect("Failed to serialize packet");
data.extend(P::packet_id().to_be_bytes());
self.0.send(data);
}
@@ -83,14 +83,14 @@ impl Plugin for ClientPlugin {
/// An extension to add packet handlers.
pub trait ClientAppExt {
/// Add a new packet handler.
- fn add_client_packet_handler(&mut self, handler: H) -> &mut Self
+ fn add_packet_handler
(&mut self, handler: H) -> &mut Self
where
P: Packet,
H: Fn(Vec, &mut World) + Send + Sync + 'static;
}
impl ClientAppExt for App {
- fn add_client_packet_handler(&mut self, handler: H) -> &mut Self
+ fn add_packet_handler
(&mut self, handler: H) -> &mut Self
where
P: Packet,
H: Fn(Vec, &mut World) + Send + Sync + 'static,
diff --git a/src/client/sync.rs b/src/client/sync.rs
new file mode 100644
index 0000000..fa1b195
--- /dev/null
+++ b/src/client/sync.rs
@@ -0,0 +1,129 @@
+use std::{any::type_name, marker::PhantomData};
+
+use super::{ClientAppExt, ServerConnection};
+use crate::Packet;
+use bevy::prelude::*;
+use serde::{de::DeserializeOwned, Serialize};
+
+/// An event that comes from the server.
+#[derive(Deref)]
+pub struct FromServer {
+ /// The event.
+ pub event: E,
+}
+
+/// Mark an [Entity] as synced by the server.
+#[derive(Component)]
+pub struct ServerEntity(Entity);
+
+/// A plugin for the syncronization system.
+pub struct ClientSyncPlugin;
+
+impl ClientSyncPlugin {
+ /// Removes [ServerEntity] when disconnected.
+ fn remove_synced(mut commands: Commands, entities: Query>) {
+ for entity in entities.iter() {
+ commands.entity(entity).despawn();
+ }
+ }
+}
+
+impl Plugin for ClientSyncPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_packet_handler::(|data, world| {
+ match bincode::deserialize::(&data) {
+ Ok(entity) => {
+ if let Some((local_entity, _)) = world
+ .query::<(Entity, &ServerEntity)>()
+ .iter(world)
+ .find(|(_, server_entity)| server_entity.0 == entity)
+ {
+ println!("Despawning {:?}", local_entity);
+ world.despawn(local_entity);
+ } else {
+ println!("Spawning {:?}", entity);
+ world.spawn(ServerEntity(entity));
+ }
+ }
+ Err(_) => println!("Failed to deserialize packet: {}", type_name::()),
+ }
+ });
+ app.add_system(Self::remove_synced.run_if(resource_removed::()));
+ }
+}
+
+/// An extention to add syncronization to the client.
+pub trait ClientSyncExt {
+ /// Register syncronization for an [Event] that comes from the server.
+ fn server_event_sync(&mut self) -> &mut Self;
+
+ /// Register syncronization for an [Event] that can be sent by the client.
+ fn client_event_sync(&mut self) -> &mut Self;
+
+ /// Register a [Component] to be synced.
+ fn sync_component(&mut self) -> &mut Self;
+}
+
+impl ClientSyncExt for App {
+ fn server_event_sync(&mut self) -> &mut Self {
+ self.add_event::>()
+ .add_packet_handler::(|data, world| match bincode::deserialize::(&data) {
+ Ok(event) => world.send_event(FromServer { event }),
+ Err(_) => println!("Failed to deserialize packet: {}", type_name::()),
+ })
+ }
+
+ fn client_event_sync(&mut self) -> &mut Self {
+ self.add_event::().add_system(
+ |mut events: EventReader, connection: Option>| {
+ if let Some(connection) = connection {
+ for event in events.iter() {
+ connection.send(event);
+ }
+ }
+ },
+ )
+ }
+
+ fn sync_component(&mut self) -> &mut Self {
+ self.add_packet_handler::<(Entity, C), _>(|data, world| {
+ match bincode::deserialize::<(Entity, C)>(&data) {
+ Ok((entity, component)) => {
+ if let Some((local_entity, _)) = world
+ .query::<(Entity, &ServerEntity)>()
+ .iter(world)
+ .find(|(_, server_entity)| server_entity.0 == entity)
+ {
+ let mut local_entity = world.entity_mut(local_entity);
+ match local_entity.get_mut::() {
+ Some(mut local_component) => {
+ println!("CA CHANGE: {}", type_name::());
+ *local_component = component;
+ }
+ None => {
+ local_entity.insert(component);
+ }
+ }
+ } else {
+ println!("Received component for unknown entity: {:?}", entity);
+ }
+ }
+ Err(_) => println!("Failed to deserialize packet: {}", type_name::()),
+ }
+ })
+ .add_packet_handler::<(Entity, PhantomData), _>(
+ |data, world| match bincode::deserialize::<(Entity, PhantomData)>(&data) {
+ Ok((entity, _)) => {
+ if let Some((local_entity, _)) = world
+ .query::<(Entity, &ServerEntity)>()
+ .iter(world)
+ .find(|(_, server_entity)| server_entity.0 == entity)
+ {
+ world.entity_mut(local_entity).remove::();
+ }
+ }
+ Err(_) => println!("Failed to deserialize packet: {}", type_name::()),
+ },
+ )
+ }
+}
diff --git a/src/server.rs b/src/server/mod.rs
similarity index 93%
rename from src/server.rs
rename to src/server/mod.rs
index 2bced84..9a31ad3 100644
--- a/src/server.rs
+++ b/src/server/mod.rs
@@ -5,6 +5,8 @@ use crate::{
use bevy::prelude::*;
use std::{collections::HashMap, io, net::ToSocketAddrs, sync::Arc};
+pub mod sync;
+
/// A function that handle a received [Packet] on the server.
pub type ServerPacketHandler =
Box, &mut World) + Send + Sync>;
@@ -30,8 +32,8 @@ pub struct ClientConnection(Arc);
impl ClientConnection {
/// Sends a packet through this connection.
- pub fn send(&self, packet: P) {
- let mut data = bincode::serialize(&packet).expect("Failed to serialize packet");
+ pub fn send(&self, packet: &P) {
+ let mut data = bincode::serialize(packet).expect("Failed to serialize packet");
data.extend(P::packet_id().to_be_bytes());
self.0.send(data);
}
@@ -107,14 +109,14 @@ impl Plugin for ServerPlugin {
/// An extension to add packet handlers.
pub trait ServerAppExt {
/// Add a new packet handler.
- fn add_server_packet_handler(&mut self, handler: H) -> &mut Self
+ fn add_packet_handler
(&mut self, handler: H) -> &mut Self
where
P: Packet,
H: Fn(Entity, ClientConnection, Vec, &mut World) + Send + Sync + 'static;
}
impl ServerAppExt for App {
- fn add_server_packet_handler(&mut self, handler: H) -> &mut Self
+ fn add_packet_handler
(&mut self, handler: H) -> &mut Self
where
P: Packet,
H: Fn(Entity, ClientConnection, Vec, &mut World) + Send + Sync + 'static,
diff --git a/src/server/sync.rs b/src/server/sync.rs
new file mode 100644
index 0000000..ad2e7b7
--- /dev/null
+++ b/src/server/sync.rs
@@ -0,0 +1,154 @@
+use super::ServerAppExt;
+use crate::{server::ClientConnection, Packet};
+use bevy::prelude::*;
+use serde::{de::DeserializeOwned, Serialize};
+use std::{any::type_name, marker::PhantomData, ops::Deref};
+
+/// An event that comes from a client.
+pub struct FromClient {
+ /// The entity of the [ClientConnection] that sent the event.
+ pub entity: Entity,
+
+ /// The [ClientConnection] that sent the event.
+ pub connection: ClientConnection,
+
+ /// The event.
+ pub event: E,
+}
+
+impl Deref for FromClient {
+ type Target = E;
+
+ fn deref(&self) -> &Self::Target {
+ &self.event
+ }
+}
+
+/// Mark an [Entity] to be synced.
+#[derive(Component)]
+pub struct Synced;
+
+/// A plugin for the syncronization system.
+pub struct ServerSyncPlugin;
+
+impl ServerSyncPlugin {
+ /// Send to clients the [Synced] entity that has been added to the server.
+ fn send_added(
+ added_entities: Query>,
+ connections: Query<&ClientConnection>,
+ ) {
+ for entity in added_entities.iter() {
+ for connection in connections.iter() {
+ connection.send(&entity);
+ }
+ }
+ }
+
+ /// Send [Synced] entities to new clients.
+ fn send_synced(
+ synced_entities: Query>,
+ new_connections: Query<&ClientConnection, Added>,
+ ) {
+ for entity in synced_entities.iter() {
+ for connection in new_connections.iter() {
+ connection.send(&entity);
+ }
+ }
+ }
+
+ /// Send to clients the [Synced] entity that has been removed.
+ fn send_removed(
+ mut removed_entities: RemovedComponents,
+ connections: Query<&ClientConnection>,
+ ) {
+ for entity in removed_entities.iter() {
+ for connection in connections.iter() {
+ connection.send(&entity);
+ }
+ }
+ }
+}
+
+impl Plugin for ServerSyncPlugin {
+ fn build(&self, app: &mut App) {
+ app.add_system(Self::send_added);
+ app.add_system(Self::send_synced);
+ app.add_system(Self::send_removed);
+ }
+}
+
+/// An extention to add syncronization to the server.
+pub trait ServerSyncExt {
+ /// Register syncronization for an [Event] that can be sent by the server.
+ fn server_event_sync(&mut self) -> &mut Self;
+
+ /// Register syncronization for an [Event] that comes from the client.
+ fn client_event_sync(&mut self) -> &mut Self;
+
+ /// Register a [Component] to be synced.
+ fn sync_component(&mut self) -> &mut Self;
+}
+
+impl ServerSyncExt for App {
+ fn server_event_sync(&mut self) -> &mut Self {
+ self.add_event::().add_system(
+ |mut events: EventReader, connections: Query<&ClientConnection>| {
+ for event in events.iter() {
+ for connection in connections.iter() {
+ connection.send(event);
+ }
+ }
+ },
+ )
+ }
+
+ fn client_event_sync(&mut self) -> &mut Self {
+ self.add_event::>()
+ .add_packet_handler::(
+ |entity, connection, data, world| match bincode::deserialize::(&data) {
+ Ok(event) => world.send_event(FromClient {
+ entity,
+ connection,
+ event,
+ }),
+ Err(_) => println!("Failed to deserialize packet: {}", type_name::()),
+ },
+ )
+ }
+
+ fn sync_component(&mut self) -> &mut Self {
+ let update_components =
+ |changed_components: Query<(Entity, &C), (Changed, With)>,
+ connections: Query<&ClientConnection>| {
+ for (entity, component) in changed_components.iter() {
+ for connection in connections.iter() {
+ connection.send(&(entity, component.clone()));
+ }
+ }
+ };
+ let send_components =
+ |components: Query<(Entity, &C), With>,
+ new_connections: Query<&ClientConnection, Added>| {
+ for (entity, component) in components.iter() {
+ for connection in new_connections.iter() {
+ connection.send(&(entity, component.clone()));
+ }
+ }
+ };
+ let remove_components =
+ |mut removed_components: RemovedComponents,
+ synced_entities: Query>,
+ connections: Query<&ClientConnection>| {
+ for entity in removed_components.iter() {
+ if synced_entities.contains(entity) {
+ for connection in connections.iter() {
+ connection.send(&(entity, PhantomData::));
+ }
+ }
+ }
+ };
+ self.add_system(update_components.after(ServerSyncPlugin::send_added))
+ .add_system(send_components.after(ServerSyncPlugin::send_synced))
+ .add_system(remove_components.after(update_components))
+ }
+}