distributed_physics_test/src/net/distribution.rs
Michael Bradley 3dfeae14f7
All checks were successful
CI / Formatting (push) Successful in 42s
Initial change propagation work
2025-10-26 00:44:30 -04:00

136 lines
3.6 KiB
Rust

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<u8>;
}
impl<T> NetworkEncodable for T
where
T: Clone + Into<Vec<u8>>,
{
fn encode(&self) -> Vec<u8> {
self.clone().into()
}
}
pub trait NetworkDecodable: Sized {
type DecodeError;
fn decode(buffer: Vec<u8>) -> std::result::Result<Self, Self::DecodeError>;
}
impl<T> NetworkDecodable for T
where
T: TryFrom<Vec<u8>>,
{
type DecodeError = T::Error;
fn decode(buffer: Vec<u8>) -> std::result::Result<Self, Self::DecodeError> {
T::try_from(buffer)
}
}
/// Components wishing to be networked must implement this type
pub trait Networked: Component<Mutability = Mutable> + NetworkEncodable + NetworkDecodable {
type LocalFilter: QueryFilter;
type RemoteFilter: QueryFilter;
}
impl<T> Networked for T
where
T: Component<Mutability = Mutable> + NetworkEncodable + NetworkDecodable,
{
type LocalFilter = Without<PeerOwned>;
type RemoteFilter = With<PeerOwned>;
}
fn incoming_network_entity<
T: NetworkDecodable + Component<Mutability = Mutable>,
F: QueryFilter,
>(
mut inbound: MessageReader<InboundPacket>,
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<T: NetworkEncodable + Component>(
add: On<Add, PeerID>,
components: Query<(&T, &EntityNetworkID), Without<PeerOwned>>,
peers: Query<&PeerID>,
mut outbound: MessageWriter<OutboundPacket>,
) -> 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<T: NetworkEncodable + Component>(
add: On<Add, T>,
components: Query<(&T, &EntityNetworkID), Without<PeerOwned>>,
peers: Query<&PeerID>,
mut outbound: MessageWriter<OutboundPacket>,
) {
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<T: NetworkEncodable + Component, F: QueryFilter>(
components: Query<(&T, &EntityNetworkID), (F, Changed<T>)>,
peers: Query<&PeerID>,
mut outbound: MessageWriter<OutboundPacket>,
) {
for (component, id) in components {
for peer in peers {
outbound.write(Packet::create(peer.id, id.0, component.encode()));
}
}
}
pub fn distribution_plugin<T: Networked>(app: &mut App) {
app.add_systems(
FixedUpdate,
(
changed_local_entity::<T, T::LocalFilter>,
incoming_network_entity::<T, T::RemoteFilter>,
),
)
.add_observer(new_peer::<T>)
.add_observer(new_local_entity::<T>);
}