use bevy::{ ecs::{component::Mutable, query::QueryFilter}, prelude::*, }; use uuid::Uuid; use super::{ packet::{InboundPacket, OutboundPacket, Packet}, peer::PeerID, }; /// Entities wishing to be networked must have this in their bundle #[derive(Component, Debug)] pub struct EntityNetworkID(Uuid); impl Default for EntityNetworkID { fn default() -> Self { Self(Uuid::new_v4()) } } #[derive(Component, Debug)] pub struct PeerOwned; pub trait NetworkEncodable { fn encode(&self) -> Vec; } impl NetworkEncodable for T where T: Clone + Into>, { fn encode(&self) -> Vec { self.clone().into() } } pub trait NetworkDecodable: Sized { type DecodeError; fn decode(buffer: Vec) -> std::result::Result; } impl NetworkDecodable for T where T: TryFrom>, { type DecodeError = T::Error; fn decode(buffer: Vec) -> std::result::Result { T::try_from(buffer) } } /// Components wishing to be networked must implement this type pub trait Networked: Component + NetworkEncodable + NetworkDecodable { type LocalFilter: QueryFilter; type RemoteFilter: QueryFilter; } impl Networked for T where T: Component + NetworkEncodable + NetworkDecodable, { type LocalFilter = Without; type RemoteFilter = With; } fn incoming_network_entity< T: NetworkDecodable + Component, F: QueryFilter, >( mut inbound: MessageReader, mut components: Query<(&mut T, &EntityNetworkID), F>, mut commands: Commands, ) { 'packets: for InboundPacket(packet) in inbound.read() { if let Ok(component) = T::decode(packet.message.clone()) { for (mut existing_component, id) in &mut components { if id.0 == packet.entity { *existing_component = component; continue 'packets; } } commands.spawn((component, EntityNetworkID(packet.entity), PeerOwned)); } } } fn new_peer( add: On, components: Query<(&T, &EntityNetworkID), Without>, peers: Query<&PeerID>, mut outbound: MessageWriter, ) -> Result { let peer = peers.get(add.entity)?; for (component, id) in components { outbound.write(Packet::create(peer.id, id.0, component.encode())); } Ok(()) } fn new_local_entity( add: On, components: Query<(&T, &EntityNetworkID), Without>, peers: Query<&PeerID>, mut outbound: MessageWriter, ) { if let Ok((component, id)) = components.get(add.entity) { for peer in peers { outbound.write(Packet::create(peer.id, id.0, component.encode())); } } } fn changed_local_entity( components: Query<(&T, &EntityNetworkID), (F, Changed)>, peers: Query<&PeerID>, mut outbound: MessageWriter, ) { for (component, id) in components { for peer in peers { outbound.write(Packet::create(peer.id, id.0, component.encode())); } } } pub fn distribution_plugin(app: &mut App) { app.add_systems( FixedUpdate, ( changed_local_entity::, incoming_network_entity::, ), ) .add_observer(new_peer::) .add_observer(new_local_entity::); }