Skip to content

Commit cf19650

Browse files
hymmonkoechescock
authored
Remove systems from schedule (#20298)
# Objective - Add ability to remove systems from a schedule. - Fixes #279. ## Solution - add `remove_systems_in_set` method which removes all the systems by their set name. In most cases systems are added to a schedule once, so just passing the system (which is an implicit set by it's type name) will just remove the one system. If a system is added multiple times such as `apply_deferred`, then you may need to give the system a unique set, if you just want to remove just one system. - The method also tries to remove all the configuration for the set and the systems found. - We need to pass world to the method because user build passes are allowed to write to the world. ## Testing - Added some unit tests. ## Future work - In the future of systems as entities this will probably look much different. We'd probably store all the edges as relationships, so hopefully cleanup would be easier. - It'd be better if remove_systems_in_set could take a tuple of system sets. I looked into using IntoScheduleConfigs, but that won't work since the system set version of it errors if you try to pass a system type set. This would allow for less rebuilding of the schedule. The schedule needs to be rebuilt to each call to be able to find the system set. --------- Co-authored-by: Barrett Ray <66580279+onkoe@users.noreply.github.com> Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com>
1 parent bec588f commit cf19650

File tree

6 files changed

+528
-16
lines changed

6 files changed

+528
-16
lines changed

crates/bevy_app/src/app.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ use bevy_ecs::{
1515
intern::Interned,
1616
message::{message_update_system, MessageCursor},
1717
prelude::*,
18-
schedule::{InternedSystemSet, ScheduleBuildSettings, ScheduleLabel},
18+
schedule::{
19+
InternedSystemSet, ScheduleBuildSettings, ScheduleCleanupPolicy, ScheduleError,
20+
ScheduleLabel,
21+
},
1922
system::{IntoObserverSystem, ScheduleSystem, SystemId, SystemInput},
2023
};
2124
use bevy_platform::collections::HashMap;
@@ -325,6 +328,38 @@ impl App {
325328
self
326329
}
327330

331+
/// Removes all systems in a [`SystemSet`]. This will cause the schedule to be rebuilt when
332+
/// the schedule is run again and can be slow. A [`ScheduleError`] is returned if the schedule needs to be
333+
/// [`Schedule::initialize`]'d or the `set` is not found.
334+
///
335+
/// Note that this can remove all systems of a type if you pass
336+
/// the system to this function as systems implicitly create a set based
337+
/// on the system type.
338+
///
339+
/// ## Example
340+
/// ```
341+
/// # use bevy_app::prelude::*;
342+
/// # use bevy_ecs::schedule::ScheduleCleanupPolicy;
343+
/// #
344+
/// # let mut app = App::new();
345+
/// # fn system_a() {}
346+
/// # fn system_b() {}
347+
/// #
348+
/// // add the system
349+
/// app.add_systems(Update, system_a);
350+
///
351+
/// // remove the system
352+
/// app.remove_systems_in_set(Update, system_a, ScheduleCleanupPolicy::RemoveSystemsOnly);
353+
/// ```
354+
pub fn remove_systems_in_set<M>(
355+
&mut self,
356+
schedule: impl ScheduleLabel,
357+
set: impl IntoSystemSet<M>,
358+
policy: ScheduleCleanupPolicy,
359+
) -> Result<usize, ScheduleError> {
360+
self.main_mut().remove_systems_in_set(schedule, set, policy)
361+
}
362+
328363
/// Registers a system and returns a [`SystemId`] so it can later be called by [`World::run_system`].
329364
///
330365
/// It's possible to register the same systems more than once, they'll be stored separately.

crates/bevy_app/src/sub_app.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use alloc::{boxed::Box, string::String, vec::Vec};
33
use bevy_ecs::{
44
message::MessageRegistry,
55
prelude::*,
6-
schedule::{InternedScheduleLabel, InternedSystemSet, ScheduleBuildSettings, ScheduleLabel},
6+
schedule::{
7+
InternedScheduleLabel, InternedSystemSet, ScheduleBuildSettings, ScheduleCleanupPolicy,
8+
ScheduleError, ScheduleLabel,
9+
},
710
system::{ScheduleSystem, SystemId, SystemInput},
811
};
912
use bevy_platform::collections::{HashMap, HashSet};
@@ -219,6 +222,18 @@ impl SubApp {
219222
self
220223
}
221224

225+
/// See [`App::remove_systems_in_set`]
226+
pub fn remove_systems_in_set<M>(
227+
&mut self,
228+
schedule: impl ScheduleLabel,
229+
set: impl IntoSystemSet<M>,
230+
policy: ScheduleCleanupPolicy,
231+
) -> Result<usize, ScheduleError> {
232+
self.world.schedule_scope(schedule, |world, schedule| {
233+
schedule.remove_systems_in_set(set, world, policy)
234+
})
235+
}
236+
222237
/// See [`App::register_system`].
223238
pub fn register_system<I, O, M>(
224239
&mut self,

crates/bevy_ecs/src/schedule/error.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,26 @@ impl ScheduleBuildWarning {
259259
}
260260
}
261261
}
262+
263+
/// Error returned from some `Schedule` methods
264+
#[derive(Error, Debug)]
265+
pub enum ScheduleError {
266+
/// Operation cannot be completed because the schedule has changed and `Schedule::initialize` needs to be called
267+
#[error("Operation cannot be completed because the schedule has changed and `Schedule::initialize` needs to be called")]
268+
Uninitialized,
269+
/// Method could not find set
270+
#[error("Set not found")]
271+
SetNotFound,
272+
/// Schedule not found
273+
#[error("Schedule not found.")]
274+
ScheduleNotFound,
275+
/// Error initializing schedule
276+
#[error("{0}")]
277+
ScheduleBuildError(ScheduleBuildError),
278+
}
279+
280+
impl From<ScheduleBuildError> for ScheduleError {
281+
fn from(value: ScheduleBuildError) -> Self {
282+
Self::ScheduleBuildError(value)
283+
}
284+
}

crates/bevy_ecs/src/schedule/node.rs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -487,14 +487,10 @@ impl Systems {
487487
self.nodes.get_mut(key).and_then(|node| node.get_mut())
488488
}
489489

490-
/// Returns a mutable reference to the system with the given key, panicking
491-
/// if it does not exist.
492-
///
493-
/// # Panics
494-
///
495-
/// If the system with the given key does not exist in this container.
496-
pub(crate) fn node_mut(&mut self, key: SystemKey) -> &mut SystemNode {
497-
&mut self.nodes[key]
490+
/// Returns a mutable reference to the system with the given key. Will return
491+
/// `None` if the key does not exist.
492+
pub(crate) fn node_mut(&mut self, key: SystemKey) -> Option<&mut SystemNode> {
493+
self.nodes.get_mut(key)
498494
}
499495

500496
/// Returns `true` if the system with the given key has conditions.
@@ -554,6 +550,25 @@ impl Systems {
554550
key
555551
}
556552

553+
/// Remove a system with [`SystemKey`]
554+
pub(crate) fn remove(&mut self, key: SystemKey) -> bool {
555+
let mut found = false;
556+
if self.nodes.remove(key).is_some() {
557+
found = true;
558+
}
559+
560+
if self.conditions.remove(key).is_some() {
561+
found = true;
562+
}
563+
564+
if let Some(index) = self.uninit.iter().position(|value| *value == key) {
565+
self.uninit.remove(index);
566+
found = true;
567+
}
568+
569+
found
570+
}
571+
557572
/// Returns `true` if all systems in this container have been initialized.
558573
pub fn is_initialized(&self) -> bool {
559574
self.uninit.is_empty()
@@ -646,6 +661,11 @@ impl SystemSets {
646661
self.sets.get(key).map(|set| &**set)
647662
}
648663

664+
/// Returns the key for the given system set, returns None if it does not exist.
665+
pub fn get_key(&self, set: InternedSystemSet) -> Option<SystemSetKey> {
666+
self.ids.get(&set).copied()
667+
}
668+
649669
/// Returns the key for the given system set, inserting it into this
650670
/// container if it does not already exist.
651671
pub fn get_key_or_insert(&mut self, set: InternedSystemSet) -> SystemSetKey {
@@ -716,6 +736,14 @@ impl SystemSets {
716736
key
717737
}
718738

739+
/// Remove a set with a [`SystemSetKey`]
740+
pub(crate) fn remove(&mut self, key: SystemSetKey) -> bool {
741+
self.sets.remove(key);
742+
self.conditions.remove(key);
743+
self.uninit.retain(|uninit| uninit.key != key);
744+
true
745+
}
746+
719747
/// Returns `true` if all system sets' conditions in this container have
720748
/// been initialized.
721749
pub fn is_initialized(&self) -> bool {

0 commit comments

Comments
 (0)