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