use crate::set::StdbSet;
use bevy_app::{App, Plugin, PreUpdate};
use bevy_ecs::prelude::{IntoScheduleConfigs, Message, Messages, Mut, Resource, World};
use crossbeam_channel::{Sender, unbounded};
use std::any::{Any, TypeId, type_name};
struct ChannelEntry {
type_id: TypeId,
drain: Box<dyn Fn(&mut World) + Send + Sync>,
sender: Box<dyn Any + Send + Sync>,
}
#[derive(Resource, Default)]
struct ChannelRegistry {
channels: Vec<ChannelEntry>,
}
pub(crate) struct ChannelBridgePlugin;
impl Plugin for ChannelBridgePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<ChannelRegistry>();
app.add_systems(PreUpdate, drain_channels.in_set(StdbSet::Flush));
}
}
fn drain_channels(world: &mut World) {
world.resource_scope(|world, registry: Mut<ChannelRegistry>| {
for entry in ®istry.channels {
(entry.drain)(world);
}
});
}
pub(crate) fn register_channel<T: Message>(app: &mut App) {
assert!(
!app.world()
.resource::<ChannelRegistry>()
.channels
.iter()
.any(|entry| entry.type_id == TypeId::of::<T>()),
"attempted to register channel for message type `{}` more than once",
type_name::<T>(),
);
let (tx, rx) = unbounded::<T>();
app.add_message::<T>();
app.world_mut()
.resource_mut::<ChannelRegistry>()
.channels
.push(ChannelEntry {
type_id: TypeId::of::<T>(),
drain: Box::new(move |world: &mut World| {
world
.resource_mut::<Messages<T>>()
.write_batch(rx.try_iter());
}),
sender: Box::new(tx),
});
}
pub(crate) fn channel_sender<T: Message>(world: &World) -> Sender<T> {
let registry = world.resource::<ChannelRegistry>();
let entry = registry
.channels
.iter()
.find(|entry| entry.type_id == TypeId::of::<T>())
.unwrap_or_else(|| panic!("unregistered channel for `{}`", type_name::<T>()));
entry
.sender
.as_ref()
.downcast_ref::<Sender<T>>()
.unwrap_or_else(|| panic!("unexpected type for sender `{}`", type_name::<T>(),))
.clone()
}