1use std::marker::PhantomData;
17
18use bevy_app::prelude::*;
19use bevy_ecs::prelude::*;
20use bevy_ecs::schedule::ScheduleLabel;
21use bevy_ecs::system::SystemId;
22use bevy_ecs::world::DeferredWorld;
23use bevy_log::prelude::*;
24
25use crate::Static;
26
27pub struct DefaultDeferredSystemsPlugin;
29
30impl Plugin for DefaultDeferredSystemsPlugin {
31 fn build(&self, app: &mut App) {
32 app.add_systems(First, run_deferred_systems::<First>)
33 .add_systems(PreUpdate, run_deferred_systems::<PreUpdate>)
34 .add_systems(Update, run_deferred_systems::<Update>)
35 .add_systems(PostUpdate, run_deferred_systems::<PostUpdate>)
36 .add_systems(Last, run_deferred_systems::<Last>);
37 }
38}
39
40pub trait RunDeferredSystem {
42 fn run_deferred_system<S: ScheduleLabel, M>(
68 &mut self,
69 schedule: S,
70 system: impl Static + IntoSystem<(), (), M>,
71 );
72
73 fn run_deferred_system_with<S: ScheduleLabel, I: Static, M>(
76 &mut self,
77 schedule: S,
78 system: impl Static + IntoSystem<In<I>, (), M>,
79 input: I,
80 );
81}
82
83impl RunDeferredSystem for World {
84 fn run_deferred_system<S: ScheduleLabel, M>(
85 &mut self,
86 _schedule: S,
87 system: impl Static + IntoSystem<(), (), M>,
88 ) {
89 let system = self.register_system_cached(system);
90 self.get_resource_or_init::<DeferredSystems<S>>()
91 .0
92 .push(Box::new(DeferredSystem(system)));
93 }
94
95 fn run_deferred_system_with<S: ScheduleLabel, I: Static, M>(
96 &mut self,
97 _schedule: S,
98 system: impl 'static + IntoSystem<In<I>, (), M>,
99 input: I,
100 ) {
101 let system = self.register_system_cached(system);
102 self.get_resource_or_init::<DeferredSystems<S>>()
103 .0
104 .push(Box::new(DeferredSystemWith(system, input)));
105 }
106}
107
108impl RunDeferredSystem for DeferredWorld<'_> {
109 fn run_deferred_system<S: ScheduleLabel, M>(
110 &mut self,
111 schedule: S,
112 system: impl Static + IntoSystem<(), (), M>,
113 ) {
114 self.commands()
115 .queue(move |world: &mut World| world.run_deferred_system(schedule, system));
116 }
117
118 fn run_deferred_system_with<S: ScheduleLabel, I: Static, M>(
119 &mut self,
120 schedule: S,
121 system: impl Static + IntoSystem<In<I>, (), M>,
122 input: I,
123 ) {
124 self.commands().queue(move |world: &mut World| {
125 world.run_deferred_system_with(schedule, system, input)
126 });
127 }
128}
129
130#[derive(Resource)]
131struct DeferredSystems<S: ScheduleLabel>(Vec<Box<dyn AnyDeferredSystem>>, PhantomData<S>);
132
133impl<S: ScheduleLabel> Default for DeferredSystems<S> {
134 fn default() -> Self {
135 Self(Vec::new(), PhantomData)
136 }
137}
138
139impl<S: ScheduleLabel> DeferredSystems<S> {
140 fn take(&mut self) -> Self {
141 Self(self.0.drain(..).collect(), PhantomData)
142 }
143
144 fn run(self, world: &mut World) {
145 for system in self.0 {
146 system.run(world);
147 }
148 }
149}
150
151trait AnyDeferredSystem: Static {
152 fn run(self: Box<Self>, world: &mut World);
153}
154
155struct DeferredSystem(SystemId);
156
157impl AnyDeferredSystem for DeferredSystem {
158 fn run(self: Box<Self>, world: &mut World) {
159 if let Err(why) = world.run_system(self.0) {
160 error!("deferred system error: {why}");
161 }
162 }
163}
164
165struct DeferredSystemWith<I: Static>(SystemId<In<I>>, I);
166
167impl<I: Static> AnyDeferredSystem for DeferredSystemWith<I> {
168 fn run(self: Box<Self>, world: &mut World) {
169 if let Err(why) = world.run_system_with(self.0, self.1) {
170 error!("deferred system error: {why}");
171 }
172 }
173}
174
175pub fn run_deferred_systems<S: ScheduleLabel>(world: &mut World) {
177 let Some(mut systems) = world.get_resource_mut::<DeferredSystems<S>>() else {
178 return;
179 };
180
181 systems.take().run(world);
182}
183
184#[test]
185fn test_deferred_system() {
186 use bevy::prelude::*;
187 use bevy::MinimalPlugins;
188
189 #[derive(Resource)]
190 struct Success;
191
192 let mut app = App::new();
193 app.add_plugins((MinimalPlugins, DefaultDeferredSystemsPlugin));
194
195 app.world_mut()
196 .run_deferred_system(Update, |mut commands: Commands| {
197 commands.insert_resource(Success)
198 });
199
200 app.world_mut().flush(); assert!(!app.world().contains_resource::<Success>());
202
203 app.update();
204 assert!(app.world_mut().remove_resource::<Success>().is_some());
205
206 app.update();
208 assert!(!app.world().contains_resource::<Success>());
209}