1use crate::command::CommandQueueSender;
2use crate::util::{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 bevy_ecs::world::Command;
9use bevy_hierarchy::DespawnRecursive;
10use std::fmt;
11
12#[derive(Debug)]
19pub struct AsyncEntity {
20 id: Entity,
21 world: AsyncWorld,
22}
23
24impl AsyncEntity {
25 pub(crate) fn new(id: Entity, world: AsyncWorld) -> Self {
26 Self { id, world }
27 }
28
29 pub fn id(&self) -> Entity {
31 self.id
32 }
33
34 pub fn sender(&self) -> CommandQueueSender {
36 self.world.sender()
37 }
38
39 pub async fn despawn(self) {
41 self.world
42 .apply(DespawnRecursive {
43 entity: self.id,
44 warn: false,
45 })
46 .await;
47 }
48
49 pub async fn insert<B: Bundle>(&self, bundle: B) {
52 self.world.apply(insert(self.id, bundle)).await;
53 }
54
55 pub async fn remove<B: Bundle>(&self) {
57 self.world.apply(remove::<B>(self.id)).await;
58 }
59
60 pub async fn start_waiting_for<C: Component + Clone>(&self) -> AsyncComponent<C> {
66 let (start_waiting_for, rx) = StartWaitingFor::component(self.id);
67 self.world.apply(start_waiting_for).await;
68 AsyncComponent(rx)
69 }
70
71 pub async fn wait_for<C: Component + Clone>(&self) -> C {
77 self.start_waiting_for().await.wait().await
78 }
79
80 pub async fn insert_wait_remove<I: Component, WR: Component + Clone>(
84 &self,
85 component: I,
86 ) -> WR {
87 self.insert(component).await;
88 let wr = self.wait_for::<WR>().await;
89 self.remove::<WR>().await;
90 wr
91 }
92}
93
94pub struct AsyncComponent<C: Component>(Receiver<C>);
98
99impl<C: Component> fmt::Debug for AsyncComponent<C> {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 write!(f, "AsyncComponent(..)")
102 }
103}
104
105impl<C: Component> AsyncComponent<C> {
106 pub async fn wait(self) -> C {
108 recv(self.0).await
109 }
110}
111
112pub(crate) struct SpawnAndSendId<B> {
113 bundle: B,
114 sender: Sender<Entity>,
115}
116
117impl SpawnAndSendId<()> {
118 pub(crate) fn new_empty() -> (Self, Receiver<Entity>) {
119 let (sender, receiver) = async_channel::bounded(1);
120 (Self { bundle: (), sender }, receiver)
121 }
122}
123
124impl<B: Bundle> SpawnAndSendId<B> {
125 pub(crate) fn new(bundle: B) -> (Self, Receiver<Entity>) {
126 let (sender, receiver) = async_channel::bounded(1);
127 (Self { bundle, sender }, receiver)
128 }
129}
130
131impl<B: Bundle> Command for SpawnAndSendId<B> {
132 fn apply(self, world: &mut World) {
133 let id = world.spawn(self.bundle).id();
134 self.sender.try_send(id).unwrap_or_else(die);
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use crate::{AsyncEcsPlugin, AsyncWorld};
141 use bevy::prelude::*;
142 use bevy::tasks::AsyncComputeTaskPool;
143
144 #[derive(Default, Clone, Component)]
145 struct Translation(u8, u8);
146
147 #[derive(Default, Clone, Component)]
148 struct Scale(u8, u8);
149
150 #[derive(Default, Clone, Bundle)]
151 struct Transform {
152 translation: Translation,
153 scale: Scale,
154 }
155
156 #[test]
157 fn smoke() {
158 let mut app = App::new();
159 app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
160
161 let (sender, receiver) = async_channel::bounded(1);
162 let async_world = AsyncWorld::from_world(app.world_mut());
163
164 AsyncComputeTaskPool::get()
165 .spawn(async move {
166 let entity = async_world.spawn_empty().await;
167 sender.send(entity.id()).await.unwrap();
168 })
169 .detach();
170
171 let id = loop {
172 match receiver.try_recv() {
173 Ok(id) => break id,
174 Err(_) => app.update(),
175 }
176 };
177
178 assert!(app.world().get_entity(id).is_ok());
179 }
180
181 #[test]
182 fn named() {
183 let mut app = App::new();
184 app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
185
186 let (sender, receiver) = async_channel::bounded(1);
187 let async_world = AsyncWorld::from_world(app.world_mut());
188
189 AsyncComputeTaskPool::get()
190 .spawn(async move {
191 let entity = async_world.spawn_named("lol").await;
192 sender.send(entity.id).await.unwrap();
193 })
194 .detach();
195
196 let id = loop {
197 match receiver.try_recv() {
198 Ok(id) => break id,
199 Err(_) => app.update(),
200 }
201 };
202
203 let name = app.world().entity(id).get::<Name>().unwrap();
204 assert_eq!("lol", name.as_str());
205 }
206
207 #[test]
208 fn despawn() {
209 let mut app = App::new();
210 app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
211
212 let (sender, receiver) = async_channel::bounded(1);
213 let async_world = AsyncWorld::from_world(app.world_mut());
214 let id = app.world_mut().spawn_empty().id();
215
216 AsyncComputeTaskPool::get()
217 .spawn(async move {
218 let entity = async_world.entity(id);
219 entity.despawn().await;
220 sender.send(()).await.unwrap();
221 })
222 .detach();
223
224 loop {
225 match receiver.try_recv() {
226 Ok(_) => break,
227 Err(_) => app.update(),
228 }
229 }
230
231 assert!(app.world().get_entity(id).is_err());
232 }
233
234 #[test]
235 fn spawn() {
236 let mut app = App::new();
237 app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
238
239 let (sender, receiver) = async_channel::bounded(1);
240 let async_world = AsyncWorld::from_world(app.world_mut());
241
242 AsyncComputeTaskPool::get()
243 .spawn(async move {
244 let entity = async_world
245 .spawn(Transform {
246 translation: Translation(2, 3),
247 scale: Scale(1, 1),
248 })
249 .await;
250 sender.send(entity.id).await.unwrap();
251 })
252 .detach();
253
254 let id = loop {
255 match receiver.try_recv() {
256 Ok(id) => break id,
257 Err(_) => app.update(),
258 }
259 };
260
261 let translation = app.world().get::<Translation>(id).unwrap();
262 assert_eq!(2, translation.0);
263 assert_eq!(3, translation.1);
264 let scale = app.world().get::<Scale>(id).unwrap();
265 assert_eq!(1, scale.0);
266 assert_eq!(1, scale.1);
267 }
268
269 #[test]
270 fn insert() {
271 let mut app = App::new();
272 app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
273
274 let (sender, receiver) = async_channel::bounded(1);
275 let async_world = AsyncWorld::from_world(app.world_mut());
276
277 AsyncComputeTaskPool::get()
278 .spawn(async move {
279 let entity = async_world.spawn_empty().await;
280 sender.send(entity.id).await.unwrap();
281 entity
282 .insert(Transform {
283 translation: Translation(2, 3),
284 scale: Scale(1, 1),
285 })
286 .await;
287 })
288 .detach();
289
290 let id = loop {
291 match receiver.try_recv() {
292 Ok(id) => break id,
293 Err(_) => app.update(),
294 }
295 };
296 app.update();
297
298 let translation = app.world().get::<Translation>(id).unwrap();
299 assert_eq!(2, translation.0);
300 assert_eq!(3, translation.1);
301 let scale = app.world().get::<Scale>(id).unwrap();
302 assert_eq!(1, scale.0);
303 assert_eq!(1, scale.1);
304 }
305
306 #[test]
307 fn remove() {
308 let mut app = App::new();
309 app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
310
311 let async_world = AsyncWorld::from_world(app.world_mut());
312 let id = app
313 .world_mut()
314 .spawn(Transform {
315 translation: Translation(3, 4),
316 scale: Scale(1, 1),
317 })
318 .id();
319
320 AsyncComputeTaskPool::get()
321 .spawn(async move {
322 async_world.entity(id).remove::<Transform>().await;
323 })
324 .detach();
325 app.update();
326
327 assert!(app.world().get::<Translation>(id).is_none());
328 assert!(app.world().get::<Scale>(id).is_none());
329 }
330
331 #[test]
332 fn insert_wait_remove() {
333 let mut app = App::new();
334 app.add_plugins((MinimalPlugins, AsyncEcsPlugin));
335
336 let (value_tx, value_rx) = async_channel::bounded(1);
337 let async_world = AsyncWorld::from_world(app.world_mut());
338 let id = app.world_mut().spawn_empty().id();
339
340 AsyncComputeTaskPool::get()
341 .spawn(async move {
342 let scale: Scale = async_world.entity(id).insert_wait_remove(Scale(6, 7)).await;
343 value_tx.send(scale).await.unwrap();
344 })
345 .detach();
346
347 let value = loop {
348 match value_rx.try_recv() {
349 Ok(value) => break value,
350 Err(_) => app.update(),
351 }
352 };
353 app.update();
354
355 assert_eq!(6, value.0);
356 assert_eq!(7, value.1);
357 assert!(app.world().entity(id).get::<Scale>().is_none());
358 }
359}