pub(crate) mod reflect;
use crate::{AsyncOperation, CowStr, OperationSender};
use async_channel::Sender;
use bevy_core::Name;
use bevy_ecs::prelude::*;
use bevy_ecs::system::Command;
use bevy_hierarchy::DespawnRecursiveExt;
use bevy_reflect::prelude::*;
use reflect::ReflectOperation;
use std::any::TypeId;
pub(crate) use reflect::wait_for_reflect_components;
pub use reflect::AsyncComponent;
#[derive(Debug)]
#[non_exhaustive]
pub enum EntityOperation {
SpawnEmpty(Sender<Entity>),
SpawnNamed(CowStr, Sender<Entity>),
Despawn(Entity),
Reflect(ReflectOperation),
}
impl Command for EntityOperation {
fn apply(self, world: &mut World) {
match self {
EntityOperation::SpawnEmpty(sender) => {
let id = world.spawn_empty().id();
sender.try_send(id).expect("invariant broken");
}
EntityOperation::SpawnNamed(name, sender) => {
let id = world.spawn(Name::new(name)).id();
sender.try_send(id).expect("invariant broken");
}
EntityOperation::Despawn(id) => world.entity_mut(id).despawn_recursive(),
EntityOperation::Reflect(reflect) => reflect.apply(world),
}
}
}
#[derive(Debug)]
pub struct AsyncEntity {
id: Entity,
sender: OperationSender,
}
impl From<EntityOperation> for AsyncOperation {
fn from(entity_op: EntityOperation) -> Self {
Self::Entity(entity_op)
}
}
impl AsyncEntity {
pub fn id(&self) -> Entity {
self.id
}
pub(crate) fn new(id: Entity, sender: OperationSender) -> Self {
Self { id, sender }
}
pub(crate) async fn new_empty(sender: OperationSender) -> Self {
let (id_sender, id_receiver) = async_channel::bounded(1);
let operation = EntityOperation::SpawnEmpty(id_sender);
sender.send(operation).await;
let id = id_receiver.recv().await.expect("invariant broken");
Self { id, sender }
}
pub(crate) async fn new_named(name: CowStr, sender: OperationSender) -> Self {
let (id_sender, id_receiver) = async_channel::bounded(1);
let operation = EntityOperation::SpawnNamed(name, id_sender);
sender.send(operation).await;
let id = id_receiver.recv().await.expect("invariant broken");
Self { id, sender }
}
pub(crate) async fn new_bundle(bundle: Box<dyn Reflect>, sender: OperationSender) -> Self {
let (id_sender, id_receiver) = async_channel::bounded(1);
let operation = ReflectOperation::SpawnWithBundle(bundle, id_sender);
sender.send(operation).await;
let id = id_receiver.recv().await.expect("invariant broken");
Self { id, sender }
}
pub async fn despawn(self) {
self.sender.send(EntityOperation::Despawn(self.id)).await;
}
pub async fn insert_component<C: Component + Reflect>(&self, component: C) {
let operation = ReflectOperation::InsertComponent(self.id, Box::new(component));
self.sender.send(operation).await;
}
pub async fn insert_bundle<B: Bundle + Reflect>(&self, bundle: B) {
let operation = ReflectOperation::InsertBundle(self.id, Box::new(bundle));
self.sender.send(operation).await;
}
pub async fn remove_component<C: Component + Reflect>(&self) {
let operation = ReflectOperation::RemoveComponent(self.id, TypeId::of::<C>());
self.sender.send(operation).await;
}
pub async fn remove_bundle<B: Bundle + Reflect>(&self) {
let operation = ReflectOperation::RemoveBundle(self.id, TypeId::of::<B>());
self.sender.send(operation).await;
}
pub async fn start_waiting_for<C: Component + FromReflect>(&self) -> AsyncComponent<C> {
let (sender, receiver) = async_channel::bounded(1);
let operation = ReflectOperation::WaitForComponent(self.id, TypeId::of::<C>(), sender);
self.sender.send(operation).await;
AsyncComponent::new(receiver)
}
pub async fn wait_for<C: Component + FromReflect>(&self) -> C {
self.start_waiting_for().await.wait().await
}
pub async fn insert_wait_remove<I: Component + Reflect, WR: Component + FromReflect>(
&self,
component: I,
) -> WR {
self.insert_component(component).await;
let wr = self.wait_for::<WR>().await;
self.remove_component::<WR>().await;
wr
}
}
#[cfg(test)]
mod tests {
use crate::{AsyncEcsPlugin, AsyncWorld};
use bevy::prelude::*;
use futures_lite::future;
#[test]
fn smoke() {
let mut app = App::new();
app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
let (sender, receiver) = async_channel::bounded(1);
let async_world = AsyncWorld::from_world(&mut app.world);
std::thread::spawn(move || {
future::block_on(async move {
let entity = async_world.spawn_empty().await;
sender.send(entity.id()).await.unwrap();
});
});
let id = loop {
match receiver.try_recv() {
Ok(id) => break id,
Err(_) => app.update(),
}
};
assert!(app.world.get_entity(id).is_some());
}
#[test]
fn named() {
let mut app = App::new();
app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
let (sender, receiver) = async_channel::bounded(1);
let async_world = AsyncWorld::from_world(&mut app.world);
std::thread::spawn(move || {
future::block_on(async move {
let entity = async_world.spawn_named("lol").await;
sender.send(entity.id).await.unwrap();
});
});
let id = loop {
match receiver.try_recv() {
Ok(id) => break id,
Err(_) => app.update(),
}
};
app.update();
let name = app.world.entity(id).get::<Name>().unwrap();
assert_eq!("lol", name.as_str());
}
#[test]
fn despawn() {
let mut app = App::new();
app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
let (sender, receiver) = async_channel::bounded(1);
let async_world = AsyncWorld::from_world(&mut app.world);
let id = app.world.spawn_empty().id();
std::thread::spawn(move || {
future::block_on(async move {
let entity = async_world.entity(id);
entity.despawn().await;
sender.send(()).await.unwrap();
});
});
loop {
match receiver.try_recv() {
Ok(_) => break,
Err(_) => app.update(),
}
}
app.update();
assert!(app.world.get_entity(id).is_none());
}
}