bevy_async_ecs/
entity.rs

1use crate::command::CommandQueueSender;
2use crate::util::{despawn, insert, remove};
3use crate::wait_for::StartWaitingFor;
4use crate::world::AsyncWorld;
5use crate::{die, recv};
6use async_channel::{Receiver, Sender};
7use bevy_ecs::prelude::*;
8use std::fmt;
9
10/// Represents an `Entity` that can be manipulated asynchronously.
11///
12/// The easiest way to get an `AsyncEntity` is with `AsyncWorld::spawn_empty()`.
13///
14/// Dropping the `AsyncEntity` **WILL NOT** despawn the corresponding entity in the synchronous world.
15/// Use `AsyncEntity::despawn` to despawn an entity asynchronously.
16#[derive(Debug)]
17pub struct AsyncEntity {
18	id: Entity,
19	world: AsyncWorld,
20}
21
22impl AsyncEntity {
23	pub(crate) fn new(id: Entity, world: AsyncWorld) -> Self {
24		Self { id, world }
25	}
26
27	/// Returns the underlying `Entity` being represented.
28	pub fn id(&self) -> Entity {
29		self.id
30	}
31
32	/// Returns a copy of the underlying `CommandQueueSender`.
33	pub fn sender(&self) -> CommandQueueSender {
34		self.world.sender()
35	}
36
37	/// Recursively despawns the represented entity.
38	pub async fn despawn(self) {
39		self.world.apply(despawn(self.id)).await;
40	}
41
42	/// Adds a `Bundle` of components to the entity. This will overwrite any previous value(s) of
43	/// the same component type.
44	pub async fn insert<B: Bundle>(&self, bundle: B) {
45		self.world.apply(insert(self.id, bundle)).await;
46	}
47
48	/// Removes a `Bundle` of components from the entity.
49	pub async fn remove<B: Bundle>(&self) {
50		self.world.apply(remove::<B>(self.id)).await;
51	}
52
53	/// Start waiting for the `Component` of a given type. Returns an `AsyncComponent` which can be further
54	/// waited to receive the value of the component.
55	///
56	/// `AsyncComponent::wait_for().await` is equivalent to
57	/// `AsyncComponent::start_waiting_for().await.wait().await`.
58	pub async fn start_waiting_for<C: Component + Clone>(&self) -> AsyncComponent<C> {
59		let (start_waiting_for, rx) = StartWaitingFor::component(self.id);
60		self.world.apply(start_waiting_for).await;
61		AsyncComponent(rx)
62	}
63
64	/// Wait for the `Component` of a given type. Returns the value of the component, once it exists
65	/// on the represented entity.
66	///
67	/// `AsyncComponent::wait_for().await` is equivalent to
68	/// `AsyncComponent::start_waiting_for().await.wait().await`.
69	pub async fn wait_for<C: Component + Clone>(&self) -> C {
70		self.start_waiting_for().await.wait().await
71	}
72
73	/// Insert the given `Component` of type `I` onto the entity, then immediately wait for a
74	/// component of type `WR` to be added to the entity. After one is received, this will then
75	/// remove the component of type `WR`.
76	pub async fn insert_wait_remove<I: Component, WR: Component + Clone>(
77		&self,
78		component: I,
79	) -> WR {
80		self.insert(component).await;
81		let wr = self.wait_for::<WR>().await;
82		self.remove::<WR>().await;
83		wr
84	}
85}
86
87/// Represents a `Component` being retrieved.
88///
89/// The easiest way to get an `AsyncComponent` is with `AsyncEntity::start_waiting_for()`.
90pub struct AsyncComponent<C: Component>(Receiver<C>);
91
92impl<C: Component> fmt::Debug for AsyncComponent<C> {
93	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94		write!(f, "AsyncComponent(..)")
95	}
96}
97
98impl<C: Component> AsyncComponent<C> {
99	/// Wait for the `Component` to exist, and retrieve its value.
100	pub async fn wait(self) -> C {
101		recv(self.0).await
102	}
103}
104
105pub(crate) struct SpawnAndSendId<B> {
106	bundle: B,
107	sender: Sender<Entity>,
108}
109
110impl SpawnAndSendId<()> {
111	pub(crate) fn new_empty() -> (Self, Receiver<Entity>) {
112		let (sender, receiver) = async_channel::bounded(1);
113		(Self { bundle: (), sender }, receiver)
114	}
115}
116
117impl<B: Bundle> SpawnAndSendId<B> {
118	pub(crate) fn new(bundle: B) -> (Self, Receiver<Entity>) {
119		let (sender, receiver) = async_channel::bounded(1);
120		(Self { bundle, sender }, receiver)
121	}
122}
123
124impl<B: Bundle> Command for SpawnAndSendId<B> {
125	fn apply(self, world: &mut World) {
126		let id = world.spawn(self.bundle).id();
127		self.sender.try_send(id).unwrap_or_else(die);
128	}
129}
130
131#[cfg(test)]
132mod tests {
133	use crate::{AsyncEcsPlugin, AsyncWorld};
134	use bevy::prelude::*;
135	use bevy::tasks::AsyncComputeTaskPool;
136
137	#[derive(Default, Clone, Component)]
138	struct Translation(u8, u8);
139
140	#[derive(Default, Clone, Component)]
141	struct Scale(u8, u8);
142
143	#[derive(Default, Clone, Bundle)]
144	struct Transform {
145		translation: Translation,
146		scale: Scale,
147	}
148
149	#[test]
150	fn smoke() {
151		let mut app = App::new();
152		app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
153
154		let (sender, receiver) = async_channel::bounded(1);
155		let async_world = AsyncWorld::from_world(app.world_mut());
156
157		AsyncComputeTaskPool::get()
158			.spawn(async move {
159				let entity = async_world.spawn_empty().await;
160				sender.send(entity.id()).await.unwrap();
161			})
162			.detach();
163
164		let id = loop {
165			match receiver.try_recv() {
166				Ok(id) => break id,
167				Err(_) => app.update(),
168			}
169		};
170
171		assert!(app.world().get_entity(id).is_ok());
172	}
173
174	#[test]
175	fn named() {
176		let mut app = App::new();
177		app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
178
179		let (sender, receiver) = async_channel::bounded(1);
180		let async_world = AsyncWorld::from_world(app.world_mut());
181
182		AsyncComputeTaskPool::get()
183			.spawn(async move {
184				let entity = async_world.spawn_named("lol").await;
185				sender.send(entity.id).await.unwrap();
186			})
187			.detach();
188
189		let id = loop {
190			match receiver.try_recv() {
191				Ok(id) => break id,
192				Err(_) => app.update(),
193			}
194		};
195
196		let name = app.world().entity(id).get::<Name>().unwrap();
197		assert_eq!("lol", name.as_str());
198	}
199
200	#[test]
201	fn despawn() {
202		let mut app = App::new();
203		app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
204
205		let (sender, receiver) = async_channel::bounded(1);
206		let async_world = AsyncWorld::from_world(app.world_mut());
207		let id = app.world_mut().spawn_empty().id();
208
209		AsyncComputeTaskPool::get()
210			.spawn(async move {
211				let entity = async_world.entity(id);
212				entity.despawn().await;
213				sender.send(()).await.unwrap();
214			})
215			.detach();
216
217		loop {
218			match receiver.try_recv() {
219				Ok(_) => break,
220				Err(_) => app.update(),
221			}
222		}
223
224		assert!(app.world().get_entity(id).is_err());
225	}
226
227	#[test]
228	fn spawn() {
229		let mut app = App::new();
230		app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
231
232		let (sender, receiver) = async_channel::bounded(1);
233		let async_world = AsyncWorld::from_world(app.world_mut());
234
235		AsyncComputeTaskPool::get()
236			.spawn(async move {
237				let entity = async_world
238					.spawn(Transform {
239						translation: Translation(2, 3),
240						scale: Scale(1, 1),
241					})
242					.await;
243				sender.send(entity.id).await.unwrap();
244			})
245			.detach();
246
247		let id = loop {
248			match receiver.try_recv() {
249				Ok(id) => break id,
250				Err(_) => app.update(),
251			}
252		};
253
254		let translation = app.world().get::<Translation>(id).unwrap();
255		assert_eq!(2, translation.0);
256		assert_eq!(3, translation.1);
257		let scale = app.world().get::<Scale>(id).unwrap();
258		assert_eq!(1, scale.0);
259		assert_eq!(1, scale.1);
260	}
261
262	#[test]
263	fn insert() {
264		let mut app = App::new();
265		app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
266
267		let (sender, receiver) = async_channel::bounded(1);
268		let async_world = AsyncWorld::from_world(app.world_mut());
269
270		AsyncComputeTaskPool::get()
271			.spawn(async move {
272				let entity = async_world.spawn_empty().await;
273				sender.send(entity.id).await.unwrap();
274				entity
275					.insert(Transform {
276						translation: Translation(2, 3),
277						scale: Scale(1, 1),
278					})
279					.await;
280			})
281			.detach();
282
283		let id = loop {
284			match receiver.try_recv() {
285				Ok(id) => break id,
286				Err(_) => app.update(),
287			}
288		};
289		app.update();
290
291		let translation = app.world().get::<Translation>(id).unwrap();
292		assert_eq!(2, translation.0);
293		assert_eq!(3, translation.1);
294		let scale = app.world().get::<Scale>(id).unwrap();
295		assert_eq!(1, scale.0);
296		assert_eq!(1, scale.1);
297	}
298
299	#[test]
300	fn remove() {
301		let mut app = App::new();
302		app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
303
304		let async_world = AsyncWorld::from_world(app.world_mut());
305		let id = app
306			.world_mut()
307			.spawn(Transform {
308				translation: Translation(3, 4),
309				scale: Scale(1, 1),
310			})
311			.id();
312
313		AsyncComputeTaskPool::get()
314			.spawn(async move {
315				async_world.entity(id).remove::<Transform>().await;
316			})
317			.detach();
318		app.update();
319
320		assert!(app.world().get::<Translation>(id).is_none());
321		assert!(app.world().get::<Scale>(id).is_none());
322	}
323
324	#[test]
325	fn insert_wait_remove() {
326		let mut app = App::new();
327		app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
328
329		let (value_tx, value_rx) = async_channel::bounded(1);
330		let async_world = AsyncWorld::from_world(app.world_mut());
331		let id = app.world_mut().spawn_empty().id();
332
333		AsyncComputeTaskPool::get()
334			.spawn(async move {
335				let scale: Scale = async_world.entity(id).insert_wait_remove(Scale(6, 7)).await;
336				value_tx.send(scale).await.unwrap();
337			})
338			.detach();
339
340		let value = loop {
341			match value_rx.try_recv() {
342				Ok(value) => break value,
343				Err(_) => app.update(),
344			}
345		};
346		app.update();
347
348		assert_eq!(6, value.0);
349		assert_eq!(7, value.1);
350		assert!(app.world().entity(id).get::<Scale>().is_none());
351	}
352}