bevy_async_system/runner/
once.rs1use bevy::app::AppExit;
2use bevy::ecs::schedule::ScheduleLabel;
3use bevy::ecs::system::EntityCommands;
4use bevy::prelude::{Commands, Event, EventWriter, FromWorld, In, IntoSystem, IntoSystemConfigs, NextState, Query, ResMut, Resource, Schedules, States, World};
5
6use crate::async_schedules::TaskSender;
7use crate::prelude::{AsyncSchedule, AsyncScheduleCommand, IntoAsyncScheduleCommand};
8use crate::runner::{schedule_initialize, task_running};
9use crate::runner::config::AsyncSystemConfig;
10
11#[inline(always)]
45pub fn run<Out, Marker, Sys>(system: Sys) -> impl IntoAsyncScheduleCommand<Out>
46 where
47 Out: Send + Sync + 'static,
48 Marker: Send + Sync + 'static,
49 Sys: IntoSystem<(), Out, Marker> + Send + Sync + 'static
50{
51 OnceOnMain(AsyncSystemConfig::<Out, Marker, Sys>::new(system))
52}
53
54
55#[inline]
76pub fn set_state<S: States + Copy>(to: S) -> impl IntoAsyncScheduleCommand {
77 run(move |mut state: ResMut<NextState<S>>| {
78 state.set(to);
79 })
80}
81
82
83#[inline]
101pub fn send<E: Event + Clone>(event: E) -> impl IntoAsyncScheduleCommand {
102 run(move |mut ew: EventWriter<E>| {
103 ew.send(event.clone());
104 })
105}
106
107
108#[inline(always)]
110pub fn app_exit() -> impl IntoAsyncScheduleCommand {
111 send(AppExit)
112}
113
114
115#[inline]
134pub fn insert_resource<R: Resource + Clone>(resource: R) -> impl IntoAsyncScheduleCommand {
135 run(move |mut commands: Commands| {
136 commands.insert_resource(resource.clone());
137 })
138}
139
140
141#[inline]
157pub fn init_resource<R: Resource + Default>() -> impl IntoAsyncScheduleCommand {
158 run(|mut commands: Commands| {
159 commands.init_resource::<R>();
160 })
161}
162
163
164#[inline]
181pub fn init_non_send_resource<R: FromWorld + 'static>() -> impl IntoAsyncScheduleCommand {
182 run(move |world: &mut World| {
183 world.init_non_send_resource::<R>();
184 })
185}
186
187
188struct OnceOnMain<Out, Marker, Sys>(AsyncSystemConfig<Out, Marker, Sys>);
189
190
191impl<Out, Marker, Sys> IntoAsyncScheduleCommand<Out> for OnceOnMain<Out, Marker, Sys>
192 where
193 Out: Send + Sync + 'static,
194 Marker: Send + Sync + 'static,
195 Sys: IntoSystem<(), Out, Marker> + Send + Sync + 'static
196{
197 fn into_schedule_command(self, sender: TaskSender<Out>, schedule_label: impl ScheduleLabel + Clone) -> AsyncScheduleCommand {
198 AsyncScheduleCommand::new(OnceRunner {
199 config: self.0,
200 sender,
201 schedule_label,
202 })
203 }
204}
205
206
207struct OnceRunner<Out, Marker, Sys, Label> {
208 config: AsyncSystemConfig<Out, Marker, Sys>,
209 sender: TaskSender<Out>,
210 schedule_label: Label,
211}
212
213
214impl<Out, Marker, Sys, Label> AsyncSchedule for OnceRunner<Out, Marker, Sys, Label>
215 where
216 Out: Send + Sync + 'static,
217 Sys: IntoSystem<(), Out, Marker> + Send + Sync,
218 Marker: Send + Sync + 'static,
219 Label: ScheduleLabel + Clone
220{
221 fn initialize(self: Box<Self>, entity_commands: &mut EntityCommands, schedules: &mut Schedules) {
222 let schedule = schedule_initialize(schedules, &self.schedule_label);
223 entity_commands.insert(self.sender);
224 let entity = entity_commands.id();
225 schedule.add_systems(self
226 .config
227 .system
228 .pipe(move |In(input): In<Out>, mut senders: Query<&mut TaskSender<Out>>| {
229 if let Ok(mut sender) = senders.get_mut(entity) {
230 let _ = sender.try_send(input);
231 sender.close_channel();
232 }
233 })
234 .run_if(task_running::<Out>(entity)));
235 }
236}
237
238
239#[cfg(test)]
240mod tests {
241 use bevy::app::{PreUpdate, Startup, Update};
242 use bevy::ecs::event::ManualEventReader;
243 use bevy::prelude::{Commands, NonSendMut, Res, Resource};
244
245 use crate::ext::spawn_async_system::SpawnAsyncSystem;
246 use crate::runner::once;
247 use crate::test_util::{FirstEvent, is_first_event_already_coming, is_second_event_already_coming, new_app, SecondEvent, test_state_finished, TestState};
248
249 #[test]
250 fn set_state() {
251 let mut app = new_app();
252 app.add_systems(Startup, |mut commands: Commands| {
253 commands.spawn_async(|schedules| async move {
254 schedules.add_system(PreUpdate, once::set_state(TestState::Finished)).await;
255 });
256 });
257
258 app.update();
259
260 assert!(test_state_finished(&mut app));
261 }
262
263
264 #[test]
265 fn send_event() {
266 let mut app = new_app();
267 app.add_systems(Startup, |mut commands: Commands| {
268 commands.spawn_async(|schedules| async move {
269 schedules.add_system(Update, once::send(FirstEvent)).await;
270 schedules.add_system(Update, once::send(SecondEvent)).await;
271 });
272 });
273
274 let mut er_first = ManualEventReader::default();
275 let mut er_second = ManualEventReader::default();
276
277 app.update();
278
279 assert!(is_first_event_already_coming(&mut app, &mut er_first));
280 assert!(!is_second_event_already_coming(&mut app, &mut er_second));
281
282 app.update();
283 assert!(!is_first_event_already_coming(&mut app, &mut er_first));
284 assert!(is_second_event_already_coming(&mut app, &mut er_second));
285
286 app.update();
287 assert!(!is_first_event_already_coming(&mut app, &mut er_first));
288 assert!(!is_second_event_already_coming(&mut app, &mut er_second));
289 }
290
291
292 #[test]
293 fn output() {
294 let mut app = new_app();
295 app.add_systems(Startup, setup);
296
297 app.update();
298 app.update();
299 }
300
301
302 fn setup(mut commands: Commands) {
303 commands.spawn_async(|schedules| async move {
304 schedules.add_system(Update, once::run(without_output)).await;
305 let count: u32 = schedules.add_system(Update, once::run(with_output)).await;
306 assert_eq!(count, 10);
307 });
308 }
309
310 fn without_output(mut commands: Commands) {
311 commands.insert_resource(Count(10));
312 }
313
314
315 fn with_output(count: Res<Count>) -> u32 {
316 count.0
317 }
318
319 #[derive(Resource)]
320 struct Count(u32);
321
322
323 #[test]
324 fn init_non_send_resource() {
325 let mut app = new_app();
326 app.add_systems(Startup, |mut commands: Commands| {
327 commands.spawn_async(|schedules| async move {
328 schedules.add_system(Update, once::init_non_send_resource::<NonSendNum>()).await;
329 schedules.add_system(Update, once::run(|mut r: NonSendMut<NonSendNum>| {
330 r.0 = 3;
331 })).await;
332 });
333 });
334
335 app.update();
336 assert_eq!(app.world.non_send_resource::<NonSendNum>().0, 0);
337 app.update();
338 assert_eq!(app.world.non_send_resource::<NonSendNum>().0, 3);
339
340 #[derive(Default)]
341 struct NonSendNum(usize);
342 }
343}