Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_app/src/schedule_runner.rs
6595 views
1
use crate::{
2
app::{App, AppExit},
3
plugin::Plugin,
4
PluginsState,
5
};
6
use bevy_platform::time::Instant;
7
use core::time::Duration;
8
9
#[cfg(all(target_arch = "wasm32", feature = "web"))]
10
use {
11
alloc::{boxed::Box, rc::Rc},
12
core::cell::RefCell,
13
wasm_bindgen::{prelude::*, JsCast},
14
};
15
16
/// Determines the method used to run an [`App`]'s [`Schedule`](bevy_ecs::schedule::Schedule).
17
///
18
/// It is used in the [`ScheduleRunnerPlugin`].
19
#[derive(Copy, Clone, Debug)]
20
pub enum RunMode {
21
/// Indicates that the [`App`]'s schedule should run repeatedly.
22
Loop {
23
/// The minimum [`Duration`] to wait after a [`Schedule`](bevy_ecs::schedule::Schedule)
24
/// has completed before repeating. A value of [`None`] will not wait.
25
wait: Option<Duration>,
26
},
27
/// Indicates that the [`App`]'s schedule should run only once.
28
Once,
29
}
30
31
impl Default for RunMode {
32
fn default() -> Self {
33
RunMode::Loop { wait: None }
34
}
35
}
36
37
/// Configures an [`App`] to run its [`Schedule`](bevy_ecs::schedule::Schedule) according to a given
38
/// [`RunMode`].
39
///
40
/// [`ScheduleRunnerPlugin`] is included in the
41
/// [`MinimalPlugins`](https://docs.rs/bevy/latest/bevy/struct.MinimalPlugins.html) plugin group.
42
///
43
/// [`ScheduleRunnerPlugin`] is *not* included in the
44
/// [`DefaultPlugins`](https://docs.rs/bevy/latest/bevy/struct.DefaultPlugins.html) plugin group
45
/// which assumes that the [`Schedule`](bevy_ecs::schedule::Schedule) will be executed by other means:
46
/// typically, the `winit` event loop
47
/// (see [`WinitPlugin`](https://docs.rs/bevy/latest/bevy/winit/struct.WinitPlugin.html))
48
/// executes the schedule making [`ScheduleRunnerPlugin`] unnecessary.
49
#[derive(Default)]
50
pub struct ScheduleRunnerPlugin {
51
/// Determines whether the [`Schedule`](bevy_ecs::schedule::Schedule) is run once or repeatedly.
52
pub run_mode: RunMode,
53
}
54
55
impl ScheduleRunnerPlugin {
56
/// See [`RunMode::Once`].
57
pub fn run_once() -> Self {
58
ScheduleRunnerPlugin {
59
run_mode: RunMode::Once,
60
}
61
}
62
63
/// See [`RunMode::Loop`].
64
pub fn run_loop(wait_duration: Duration) -> Self {
65
ScheduleRunnerPlugin {
66
run_mode: RunMode::Loop {
67
wait: Some(wait_duration),
68
},
69
}
70
}
71
}
72
73
impl Plugin for ScheduleRunnerPlugin {
74
fn build(&self, app: &mut App) {
75
let run_mode = self.run_mode;
76
app.set_runner(move |mut app: App| {
77
let plugins_state = app.plugins_state();
78
if plugins_state != PluginsState::Cleaned {
79
while app.plugins_state() == PluginsState::Adding {
80
#[cfg(not(all(target_arch = "wasm32", feature = "web")))]
81
bevy_tasks::tick_global_task_pools_on_main_thread();
82
}
83
app.finish();
84
app.cleanup();
85
}
86
87
match run_mode {
88
RunMode::Once => {
89
app.update();
90
91
if let Some(exit) = app.should_exit() {
92
return exit;
93
}
94
95
AppExit::Success
96
}
97
RunMode::Loop { wait } => {
98
let tick = move |app: &mut App,
99
_wait: Option<Duration>|
100
-> Result<Option<Duration>, AppExit> {
101
let start_time = Instant::now();
102
103
app.update();
104
105
if let Some(exit) = app.should_exit() {
106
return Err(exit);
107
};
108
109
let end_time = Instant::now();
110
111
if let Some(wait) = _wait {
112
let exe_time = end_time - start_time;
113
if exe_time < wait {
114
return Ok(Some(wait - exe_time));
115
}
116
}
117
118
Ok(None)
119
};
120
121
cfg_if::cfg_if! {
122
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
123
fn set_timeout(callback: &Closure<dyn FnMut()>, dur: Duration) {
124
web_sys::window()
125
.unwrap()
126
.set_timeout_with_callback_and_timeout_and_arguments_0(
127
callback.as_ref().unchecked_ref(),
128
dur.as_millis() as i32,
129
)
130
.expect("Should register `setTimeout`.");
131
}
132
let asap = Duration::from_millis(1);
133
134
let exit = Rc::new(RefCell::new(AppExit::Success));
135
let closure_exit = exit.clone();
136
137
let mut app = Rc::new(app);
138
let moved_tick_closure = Rc::new(RefCell::new(None));
139
let base_tick_closure = moved_tick_closure.clone();
140
141
let tick_app = move || {
142
let app = Rc::get_mut(&mut app).unwrap();
143
let delay = tick(app, wait);
144
match delay {
145
Ok(delay) => set_timeout(
146
moved_tick_closure.borrow().as_ref().unwrap(),
147
delay.unwrap_or(asap),
148
),
149
Err(code) => {
150
closure_exit.replace(code);
151
}
152
}
153
};
154
*base_tick_closure.borrow_mut() =
155
Some(Closure::wrap(Box::new(tick_app) as Box<dyn FnMut()>));
156
set_timeout(base_tick_closure.borrow().as_ref().unwrap(), asap);
157
158
exit.take()
159
} else {
160
loop {
161
match tick(&mut app, wait) {
162
Ok(Some(delay)) => {
163
bevy_platform::thread::sleep(delay);
164
}
165
Ok(None) => continue,
166
Err(exit) => return exit,
167
}
168
}
169
}
170
}
171
}
172
}
173
});
174
}
175
}
176
177