Sync system
This commit is contained in:
parent
69a7f37518
commit
39db941d52
|
@ -1,67 +0,0 @@
|
|||
use bevnet::{
|
||||
client::{ClientAppExt, ClientPlugin, ServerConnection},
|
||||
server::{ClientListener, ServerAppExt, ServerPlugin},
|
||||
};
|
||||
use bevy::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Ping;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Pong;
|
||||
|
||||
fn start_server(mut commands: Commands, keys: Res<Input<KeyCode>>) {
|
||||
if keys.just_pressed(KeyCode::B) {
|
||||
println!("Starting server...");
|
||||
match ClientListener::bind("127.0.0.1:8000") {
|
||||
Ok(listener) => {
|
||||
commands.insert_resource(listener);
|
||||
println!("Server started");
|
||||
}
|
||||
Err(e) => println!("Failed to start server: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn connect(mut commands: Commands, keys: Res<Input<KeyCode>>) {
|
||||
if keys.just_pressed(KeyCode::C) {
|
||||
println!("Connecting to server...");
|
||||
match ServerConnection::connect("127.0.0.1:8000") {
|
||||
Ok(connection) => {
|
||||
commands.insert_resource(connection);
|
||||
println!("Connected to server");
|
||||
}
|
||||
Err(e) => println!("Failed to connect: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_ping(connection: Option<Res<ServerConnection>>, keys: Res<Input<KeyCode>>) {
|
||||
if keys.just_pressed(KeyCode::S) {
|
||||
println!("Sending ping...");
|
||||
if let Some(connection) = connection {
|
||||
connection.send(Ping);
|
||||
println!("Ping sent");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_plugin(ServerPlugin)
|
||||
.add_system(start_server)
|
||||
.add_server_packet_handler::<Ping, _>(|entity, connection, _, _| {
|
||||
println!("Received ping from {:?}", entity);
|
||||
connection.send(Pong);
|
||||
println!("Sent pong to {:?}", entity);
|
||||
})
|
||||
.add_plugin(ClientPlugin)
|
||||
.add_system(connect)
|
||||
.add_client_packet_handler::<Pong, _>(|_, _| {
|
||||
println!("Received pong");
|
||||
})
|
||||
.add_system(send_ping)
|
||||
.run();
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
use crate::{tcp::Connection, Packet};
|
||||
use bevy::prelude::*;
|
||||
use std::{collections::HashMap, io, net::ToSocketAddrs, sync::Arc};
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::{tcp::Connection, Packet};
|
||||
pub mod sync;
|
||||
|
||||
/// A function that handle a received [Packet] on the client.
|
||||
pub type ClientPacketHandler = Box<dyn Fn(Vec<u8>, &mut World) + Send + Sync>;
|
||||
|
@ -22,8 +22,8 @@ impl ServerConnection {
|
|||
}
|
||||
|
||||
/// Sends a packet through this connection.
|
||||
pub fn send<P: Packet>(&self, packet: P) {
|
||||
let mut data = bincode::serialize(&packet).expect("Failed to serialize packet");
|
||||
pub fn send<P: Packet>(&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<P, H>(&mut self, handler: H) -> &mut Self
|
||||
fn add_packet_handler<P, H>(&mut self, handler: H) -> &mut Self
|
||||
where
|
||||
P: Packet,
|
||||
H: Fn(Vec<u8>, &mut World) + Send + Sync + 'static;
|
||||
}
|
||||
|
||||
impl ClientAppExt for App {
|
||||
fn add_client_packet_handler<P, H>(&mut self, handler: H) -> &mut Self
|
||||
fn add_packet_handler<P, H>(&mut self, handler: H) -> &mut Self
|
||||
where
|
||||
P: Packet,
|
||||
H: Fn(Vec<u8>, &mut World) + Send + Sync + 'static,
|
129
src/client/sync.rs
Normal file
129
src/client/sync.rs
Normal file
|
@ -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<E: Event + Packet> {
|
||||
/// 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<Entity, With<ServerEntity>>) {
|
||||
for entity in entities.iter() {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for ClientSyncPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_packet_handler::<Entity, _>(|data, world| {
|
||||
match bincode::deserialize::<Entity>(&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::<Entity>()),
|
||||
}
|
||||
});
|
||||
app.add_system(Self::remove_synced.run_if(resource_removed::<ServerConnection>()));
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<E: Event + Packet>(&mut self) -> &mut Self;
|
||||
|
||||
/// Register syncronization for an [Event] that can be sent by the client.
|
||||
fn client_event_sync<E: Event + Packet>(&mut self) -> &mut Self;
|
||||
|
||||
/// Register a [Component] to be synced.
|
||||
fn sync_component<C: Component + DeserializeOwned + Serialize>(&mut self) -> &mut Self;
|
||||
}
|
||||
|
||||
impl ClientSyncExt for App {
|
||||
fn server_event_sync<E: Event + Packet>(&mut self) -> &mut Self {
|
||||
self.add_event::<FromServer<E>>()
|
||||
.add_packet_handler::<E, _>(|data, world| match bincode::deserialize::<E>(&data) {
|
||||
Ok(event) => world.send_event(FromServer { event }),
|
||||
Err(_) => println!("Failed to deserialize packet: {}", type_name::<E>()),
|
||||
})
|
||||
}
|
||||
|
||||
fn client_event_sync<E: Event + Packet>(&mut self) -> &mut Self {
|
||||
self.add_event::<E>().add_system(
|
||||
|mut events: EventReader<E>, connection: Option<Res<ServerConnection>>| {
|
||||
if let Some(connection) = connection {
|
||||
for event in events.iter() {
|
||||
connection.send(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn sync_component<C: Component + DeserializeOwned + Serialize>(&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::<C>() {
|
||||
Some(mut local_component) => {
|
||||
println!("CA CHANGE: {}", type_name::<C>());
|
||||
*local_component = component;
|
||||
}
|
||||
None => {
|
||||
local_entity.insert(component);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("Received component for unknown entity: {:?}", entity);
|
||||
}
|
||||
}
|
||||
Err(_) => println!("Failed to deserialize packet: {}", type_name::<C>()),
|
||||
}
|
||||
})
|
||||
.add_packet_handler::<(Entity, PhantomData<C>), _>(
|
||||
|data, world| match bincode::deserialize::<(Entity, PhantomData<C>)>(&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::<C>();
|
||||
}
|
||||
}
|
||||
Err(_) => println!("Failed to deserialize packet: {}", type_name::<C>()),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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<dyn Fn(Entity, ClientConnection, Vec<u8>, &mut World) + Send + Sync>;
|
||||
|
@ -30,8 +32,8 @@ pub struct ClientConnection(Arc<Connection>);
|
|||
|
||||
impl ClientConnection {
|
||||
/// Sends a packet through this connection.
|
||||
pub fn send<P: Packet>(&self, packet: P) {
|
||||
let mut data = bincode::serialize(&packet).expect("Failed to serialize packet");
|
||||
pub fn send<P: Packet>(&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<P, H>(&mut self, handler: H) -> &mut Self
|
||||
fn add_packet_handler<P, H>(&mut self, handler: H) -> &mut Self
|
||||
where
|
||||
P: Packet,
|
||||
H: Fn(Entity, ClientConnection, Vec<u8>, &mut World) + Send + Sync + 'static;
|
||||
}
|
||||
|
||||
impl ServerAppExt for App {
|
||||
fn add_server_packet_handler<P, H>(&mut self, handler: H) -> &mut Self
|
||||
fn add_packet_handler<P, H>(&mut self, handler: H) -> &mut Self
|
||||
where
|
||||
P: Packet,
|
||||
H: Fn(Entity, ClientConnection, Vec<u8>, &mut World) + Send + Sync + 'static,
|
154
src/server/sync.rs
Normal file
154
src/server/sync.rs
Normal file
|
@ -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<E: Event + Packet> {
|
||||
/// 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<E: Event + Packet> Deref for FromClient<E> {
|
||||
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<Entity, Added<Synced>>,
|
||||
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<Entity, With<Synced>>,
|
||||
new_connections: Query<&ClientConnection, Added<ClientConnection>>,
|
||||
) {
|
||||
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<Synced>,
|
||||
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<E: Event + Packet>(&mut self) -> &mut Self;
|
||||
|
||||
/// Register syncronization for an [Event] that comes from the client.
|
||||
fn client_event_sync<E: Event + Packet>(&mut self) -> &mut Self;
|
||||
|
||||
/// Register a [Component] to be synced.
|
||||
fn sync_component<C: Component + DeserializeOwned + Serialize + Clone>(&mut self) -> &mut Self;
|
||||
}
|
||||
|
||||
impl ServerSyncExt for App {
|
||||
fn server_event_sync<E: Event + Packet>(&mut self) -> &mut Self {
|
||||
self.add_event::<E>().add_system(
|
||||
|mut events: EventReader<E>, connections: Query<&ClientConnection>| {
|
||||
for event in events.iter() {
|
||||
for connection in connections.iter() {
|
||||
connection.send(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn client_event_sync<E: Event + Packet>(&mut self) -> &mut Self {
|
||||
self.add_event::<FromClient<E>>()
|
||||
.add_packet_handler::<E, _>(
|
||||
|entity, connection, data, world| match bincode::deserialize::<E>(&data) {
|
||||
Ok(event) => world.send_event(FromClient {
|
||||
entity,
|
||||
connection,
|
||||
event,
|
||||
}),
|
||||
Err(_) => println!("Failed to deserialize packet: {}", type_name::<E>()),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn sync_component<C: Component + DeserializeOwned + Serialize + Clone>(&mut self) -> &mut Self {
|
||||
let update_components =
|
||||
|changed_components: Query<(Entity, &C), (Changed<C>, With<Synced>)>,
|
||||
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<Synced>>,
|
||||
new_connections: Query<&ClientConnection, Added<ClientConnection>>| {
|
||||
for (entity, component) in components.iter() {
|
||||
for connection in new_connections.iter() {
|
||||
connection.send(&(entity, component.clone()));
|
||||
}
|
||||
}
|
||||
};
|
||||
let remove_components =
|
||||
|mut removed_components: RemovedComponents<C>,
|
||||
synced_entities: Query<Entity, With<Synced>>,
|
||||
connections: Query<&ClientConnection>| {
|
||||
for entity in removed_components.iter() {
|
||||
if synced_entities.contains(entity) {
|
||||
for connection in connections.iter() {
|
||||
connection.send(&(entity, PhantomData::<C>));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
self.add_system(update_components.after(ServerSyncPlugin::send_added))
|
||||
.add_system(send_components.after(ServerSyncPlugin::send_synced))
|
||||
.add_system(remove_components.after(update_components))
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue