bevy_async_ecs/
world.rs

1use crate::CowStr;
2use crate::command::BoxedCommand;
3use crate::command::CommandQueueBuilder;
4use crate::command::CommandQueueReceiver;
5use crate::command::CommandQueueSender;
6use crate::die;
7use crate::entity::AsyncEntity;
8use crate::entity::SpawnAndSendId;
9use crate::recv;
10use crate::system::AsyncIOSystem;
11use crate::system::AsyncSystem;
12use crate::util::insert_resource;
13use crate::util::remove_resource;
14use crate::util::trigger_event;
15use crate::wait_for::StartWaitingFor;
16use async_channel::Receiver;
17use bevy_ecs::prelude::*;
18use bevy_ecs::system::RunSystemOnce;
19use std::fmt;
20
21/// Exposes asynchronous access to the Bevy ECS `World`.
22///
23/// The easiest way to get an `AsyncWorld` is with `AsyncWorld::from_world()`.
24///
25/// ## Commands
26/// Apply any `Command` asynchronously with `AsyncWorld::apply_command`.
27///
28/// ## Systems
29/// Just like their synchronous variants, asynchronous `System`s must be registered
30/// before they can be used. Systems can optionally accept and return values asynchronously
31/// if they are registered with `AsyncWorld::register_io_system`.
32///
33/// ## Entities
34/// Spawn entities with the `AsyncWorld::spawn_*` family.
35///
36/// ## Resources
37/// Insert, remove, and wait for resources to exist.
38#[derive(Clone, Debug)]
39pub struct AsyncWorld(CommandQueueSender);
40
41impl AsyncWorld {
42	/// Returns a copy of the underlying `CommandQueueSender`.
43	pub fn sender(&self) -> CommandQueueSender {
44		self.0.clone()
45	}
46
47	/// Applies the given `Command` to the world.
48	pub async fn apply<C: Command>(&self, command: C) {
49		self.0.send_single(BoxedCommand::new(command)).await
50	}
51
52	/// Starts building a `CommandQueue`.
53	pub fn start_queue(&self) -> CommandQueueBuilder {
54		CommandQueueBuilder::new(self.sender())
55	}
56
57	/// Run a [`System`] once.
58	pub async fn run_system<M>(self, system: impl IntoSystem<(), (), M> + Send + 'static) {
59		self.apply(|world: &mut World| {
60			_ = world.run_system_once(system);
61		})
62		.await
63	}
64
65	/// Registers a `System` and returns an `AsyncSystem` that can be used to run the system on demand.
66	pub async fn register_system<M>(
67		&self,
68		system: impl IntoSystem<(), (), M> + Send,
69	) -> AsyncSystem {
70		let system = Box::new(IntoSystem::into_system(system));
71		AsyncSystem::new(system, self.clone()).await
72	}
73
74	/// Registers a `System` and returns an `AsyncIOSystem` that can be used to run the system on demand
75	/// while supplying an input value and receiving an output value.
76	pub async fn register_io_system<I: Send + 'static, O: Send + 'static, M>(
77		&self,
78		system: impl IntoSystem<In<I>, O, M> + Send,
79	) -> AsyncIOSystem<I, O> {
80		AsyncIOSystem::new(system, self.clone()).await
81	}
82
83	/// Constructs an `AsyncEntity` for the given `Entity`. If the entity does not exist, any operation
84	/// performed on it will panic.
85	pub fn entity(&self, id: Entity) -> AsyncEntity {
86		AsyncEntity::new(id, self.clone())
87	}
88
89	/// Spawns a new `Entity` and returns an `AsyncEntity` that represents it, which can be used
90	/// to further manipulate the entity.
91	pub async fn spawn_empty(&self) -> AsyncEntity {
92		let (command, receiver) = SpawnAndSendId::new_empty();
93		self.apply(command).await;
94		let id = recv(receiver).await;
95		AsyncEntity::new(id, self.clone())
96	}
97
98	/// Spawns a new `Entity` with the given `Bundle` and returns an `AsyncEntity` that represents it,
99	/// which can be used to further manipulate the entity.
100	pub async fn spawn<B: Bundle>(&self, bundle: B) -> AsyncEntity {
101		let (command, receiver) = SpawnAndSendId::new(bundle);
102		self.apply(command).await;
103		let id = recv(receiver).await;
104		AsyncEntity::new(id, self.clone())
105	}
106
107	/// Spawns a new `Entity` and returns an `AsyncEntity` that represents it, which can be used
108	/// to further manipulate the entity. This function attaches a bevy `Name` component with the given
109	/// value.
110	pub async fn spawn_named(&self, name: impl Into<CowStr> + Send) -> AsyncEntity {
111		self.spawn(Name::new(name)).await
112	}
113
114	/// Inserts a new resource or updates an existing resource with the given value.
115	pub async fn insert_resource<R: Resource>(&self, resource: R) {
116		self.apply(insert_resource(resource)).await;
117	}
118
119	/// Removes the resource of a given type, if it exists.
120	pub async fn remove_resource<R: Resource>(&self) {
121		self.apply(remove_resource::<R>()).await;
122	}
123
124	/// Start waiting for the `Resource` of a given type. Returns an `AsyncResource` which can be further
125	/// waited to receive the value of the resource.
126	///
127	/// `AsyncWorld::wait_for_resource().await` is equivalent to
128	/// `AsyncWorld::start_waiting_for_resource().await.wait().await`.
129	pub async fn start_waiting_for_resource<R: Resource + Clone>(&self) -> AsyncResource<R> {
130		let (start_waiting_for, rx) = StartWaitingFor::resource();
131		self.apply(start_waiting_for).await;
132		AsyncResource(rx)
133	}
134
135	/// Wait for the `Resource` of a given type. Returns the value of the resource, once it exists.
136	///
137	/// `AsyncWorld::wait_for_resource().await` is equivalent to
138	/// `AsyncWorld::start_waiting_for_resource().await.wait().await`.
139	pub async fn wait_for_resource<R: Resource + Clone>(&self) -> R {
140		self.start_waiting_for_resource().await.wait().await
141	}
142
143	/// Send a `Message` to the bevy world.
144	pub async fn send_message<M: Message>(&self, message: M) {
145		self.apply(WriteMessage(message)).await;
146	}
147
148	/// Start listening for `Message`s coming from the main bevy world.
149	/// Returns an `AsyncMessages` which can be further waited to receive these messages.
150	///
151	/// `AsyncWorld::wait_for_message().await` is equivalent to
152	/// `AsyncWorld::start_waiting_for_messages().await.wait().await`.
153	pub async fn start_waiting_for_messages<M: Message + Clone>(&self) -> AsyncMessages<M> {
154		let (start_waiting_for, rx) = StartWaitingFor::messages();
155		self.apply(start_waiting_for).await;
156		AsyncMessages(rx)
157	}
158
159	/// Wait for the `Message` of a given type. Returns the value of the message, once it is received.
160	///
161	/// `AsyncWorld::wait_for_message().await` is equivalent to
162	/// `AsyncWorld::start_waiting_for_messages().await.wait().await`.
163	pub async fn wait_for_message<M: Message + Clone>(&self) -> M {
164		self.start_waiting_for_messages().await.wait().await
165	}
166
167	/// Triggers the given [`Event`], which will run any [`Observer`]s watching for it.
168	pub async fn trigger<'a, T: Default, E: Event<Trigger<'a> = T> + Send + Sync + 'static>(
169		&self,
170		event: E,
171	) {
172		self.apply(trigger_event(event)).await;
173	}
174}
175
176impl From<CommandQueueSender> for AsyncWorld {
177	fn from(sender: CommandQueueSender) -> Self {
178		Self(sender)
179	}
180}
181
182impl FromWorld for AsyncWorld {
183	fn from_world(world: &mut World) -> Self {
184		let (sender, receiver) = async_channel::unbounded();
185		world.spawn((
186			CommandQueueReceiver::new(receiver),
187			Name::new("CommandQueueReceiver"),
188		));
189		CommandQueueSender::new(sender).into()
190	}
191}
192
193/// Represents a `Resource` being retrieved.
194///
195/// The easiest way to get an `AsyncResource` is with `AsyncWorld::start_waiting_for_resource()`.
196pub struct AsyncResource<R: Resource>(Receiver<R>);
197
198impl<R: Resource> fmt::Debug for AsyncResource<R> {
199	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200		write!(f, "AsyncResource(..)")
201	}
202}
203
204impl<R: Resource> AsyncResource<R> {
205	/// Wait for the `Resource` to exist, and retrieve its value.
206	pub async fn wait(self) -> R {
207		recv(self.0).await
208	}
209}
210
211struct WriteMessage<M: Message>(M);
212
213impl<M: Message> Command for WriteMessage<M> {
214	fn apply(self, world: &mut World) {
215		world
216			.write_message(self.0)
217			.ok_or("failed to write message")
218			.unwrap_or_else(die);
219	}
220}
221
222/// Represents Bevy `Message`s being received asynchronously
223///
224/// The easiest way to get an `AsyncMessages` is with `AsyncWorld::start_waiting_for_messages()`.
225pub struct AsyncMessages<M: Message>(Receiver<M>);
226
227impl<M: Message> fmt::Debug for AsyncMessages<M> {
228	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229		write!(f, "AsyncMessages(..)")
230	}
231}
232
233impl<M: Message> AsyncMessages<M> {
234	/// Wait for a `Message` to be received from the vanilla Bevy world. This function can be called repeatedly
235	/// to get more messages as they are received.
236	pub async fn wait(&self) -> M {
237		recv(self.0.clone()).await
238	}
239}