use core::net::IpAddr; use bevy::{ app::{Plugin, Update}, ecs::{ component::Component, entity::Entity, lifecycle::HookContext, name::Name, resource::Resource, system::{Commands, Res, ResMut}, world::DeferredWorld, }, prelude::Deref, }; use rapidhash::RapidHashSet; use crate::net::DiscoverResponse; #[derive(Debug, Resource, Default)] pub struct UniqueDevices(pub RapidHashSet); #[derive(Debug, Component)] pub struct Device; #[derive(Debug, Clone, Component, Hash, PartialEq, Eq)] #[component(immutable, on_remove = on_remove_device)] pub struct DeviceSocket { pub address: String, pub port: u16, pub ip: IpAddr, } #[derive(Debug, Component, Default, Clone, Copy)] pub struct DeviceDetector { pub blip_threshold: usize, pub blip_size: usize, pub max_duty: u16, pub duty: u16, } fn on_remove_device(mut world: DeferredWorld, context: HookContext) { let component = world .entity(context.entity) .get::() .cloned() .unwrap(); let mut devices = world.resource_mut::(); devices.0.remove(&component); } #[derive(Debug, Component)] #[component(immutable)] pub struct StormLevel(pub u16); #[derive(Debug, Component, Deref)] #[component(immutable)] pub struct Timestamp(pub jiff::Timestamp); impl Timestamp { pub fn from_seconds(stamp: i64) -> Result { jiff::Timestamp::from_second(stamp).map(Self) } pub fn from_microseconds(stamp: i64) -> Result { jiff::Timestamp::from_microsecond(stamp).map(Self) } } #[derive(Debug, Component, Deref)] #[component(immutable)] pub struct StormSignal(Vec); impl StormSignal { pub fn new(value: Vec) -> Self { Self(value) } } #[derive(Debug, Component, Deref)] #[component(immutable)] pub struct SignalAverage(pub u16); impl SignalAverage { pub fn new(value: u16) -> Self { Self(value) } } #[derive(Debug, Component, Deref)] #[component(immutable)] pub struct SignalPeaks(Vec); impl SignalPeaks { pub fn new(value: Vec) -> Self { Self(value) } } #[derive(Debug, Component)] #[relationship(relationship_target = Signals)] pub struct SignalSource(pub Entity); #[derive(Component, Debug, Deref)] #[relationship_target(relationship = SignalSource, linked_spawn)] pub struct Signals { signals: Vec, } #[derive(Debug, Component)] #[relationship(relationship_target = StormLevels)] pub struct StormSource(pub Entity); #[derive(Component, Debug, Deref)] #[relationship_target(relationship = StormSource, linked_spawn)] pub struct StormLevels { levels: Vec, } #[derive(Debug)] pub enum ConnectionState { Disconnected, Connecting, Connected, } impl core::fmt::Display for ConnectionState { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Disconnected => write!(f, "Disconnected"), Self::Connecting => write!(f, "Connecting"), Self::Connected => write!(f, "Connected"), } } } #[derive(Debug, Resource)] pub struct ConnectedDevice { pub device: Entity, pub connection_state: ConnectionState, } fn register_devices( incoming: Res, mut unique: ResMut, mut commands: Commands, ) { let mut devices = Vec::new(); while let Ok(discovered) = incoming.0.try_recv() { let device_addr = DeviceSocket { address: discovered.address, port: discovered.port, ip: discovered.ip, }; if !unique.0.contains(&device_addr) { unique.0.insert(device_addr.clone()); devices.push((Device, Name::new(discovered.host), device_addr)); } } if !devices.is_empty() { commands.spawn_batch(devices); } } #[derive(Debug)] pub struct DevicePlugin; impl Plugin for DevicePlugin { fn build(&self, app: &mut bevy::app::App) { app.init_resource::() .add_systems(Update, register_devices); } }