Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

One Shot Systems #8963

Merged
merged 114 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from 103 commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
844524b
Basic functionality
alice-i-cecile Mar 3, 2022
c8ddefd
commands.run_system
alice-i-cecile Mar 3, 2022
945c81f
Add SystemRegistry resource as part of CorePlugin
alice-i-cecile Mar 3, 2022
655bf2e
Run system by TypeId
alice-i-cecile Mar 3, 2022
7f7bc4a
Fix doc links
alice-i-cecile Mar 3, 2022
ac35f19
run_system_by_type_id for World and Commands
alice-i-cecile Mar 3, 2022
549d2eb
Always flush commands from systems
alice-i-cecile Mar 3, 2022
d6c2fd2
Always initialize SystemRegistry on the `World`
alice-i-cecile Mar 3, 2022
d39092d
Add test suite
alice-i-cecile Mar 3, 2022
63a0707
Doc improvements
alice-i-cecile Mar 7, 2022
89b045c
Store registered systems by their system labels
alice-i-cecile Mar 26, 2022
67c27fc
Ensure that run_system always runs the system exactly once
alice-i-cecile Mar 26, 2022
3e205aa
Advertise SystemRegistry in SystemState docs
alice-i-cecile Mar 26, 2022
c16fab6
Doc tests for SystemRegistry
alice-i-cecile Mar 26, 2022
6cabe3e
Improve ergonomics of the register_system API
alice-i-cecile Mar 26, 2022
157d48a
Improve run_by_label API ergonomics
alice-i-cecile Mar 26, 2022
b96c695
Improve ergonomics of registering systems
alice-i-cecile Mar 26, 2022
8df48ee
Note limitations
alice-i-cecile Mar 27, 2022
f3d15b0
Remove the need to manually update archetypes
alice-i-cecile Apr 18, 2022
6c59d39
Move SystemRegistry to a field on `World`
alice-i-cecile Apr 18, 2022
1ecf7d4
Revert "Move SystemRegistry to a field on `World`"
alice-i-cecile Apr 18, 2022
6efb44a
Clippy fixes
alice-i-cecile Jun 29, 2022
29021bb
Clippy fix
alice-i-cecile Jun 29, 2022
a7d0260
System recursion should fail
alice-i-cecile Jun 29, 2022
07e7caa
Documentation improvements
alice-i-cecile Jun 29, 2022
23b0768
Nicer error handling
alice-i-cecile Jun 30, 2022
273113b
Return index from register system methods
alice-i-cecile Jun 30, 2022
bbe6dc6
Fix off by one error
alice-i-cecile Jun 30, 2022
0c4bfb5
Reduce noisy returns
alice-i-cecile Jun 30, 2022
6e45a31
Fix broken tests
alice-i-cecile Jun 30, 2022
085a21d
Add example
alice-i-cecile Jun 30, 2022
2316077
Improve ergonomics of register_system method
alice-i-cecile Jun 30, 2022
ccbe49f
Formatting
alice-i-cecile Jun 30, 2022
f9d0348
Expose run_systems_by_boxed_label method
alice-i-cecile Jun 30, 2022
184cd6b
Create matching methods on App
alice-i-cecile Jun 30, 2022
45bba7d
Polish example
alice-i-cecile Jun 30, 2022
f63b221
Update examples README per CI
alice-i-cecile Jun 30, 2022
4e21f4b
Simplify system_registry doc tests
alice-i-cecile Jun 30, 2022
46e7092
Fix broken link
alice-i-cecile Jun 30, 2022
5d275ff
Add Callback type to bevy_ecs itself
alice-i-cecile Jun 30, 2022
24508d1
Implement PartialEq and Eq for Callback
alice-i-cecile Jun 30, 2022
0855a0f
Implement Hash for Callback
alice-i-cecile Jun 30, 2022
8effb5b
Change run_system_by_boxed_label to run_callback
alice-i-cecile Jun 30, 2022
9191f12
Fix doc example
alice-i-cecile Jun 30, 2022
973be53
Manually implement Hash
alice-i-cecile Jun 30, 2022
aedd75b
Fix doc example (for real?)
alice-i-cecile Jun 30, 2022
69e8039
Avoid box allocation
alice-i-cecile Jun 30, 2022
f2b398a
Mostly fix doc example
alice-i-cecile Jul 1, 2022
98ed955
Remove frustrating doc example
alice-i-cecile Jul 1, 2022
7476695
Typo fix
alice-i-cecile Jul 29, 2022
4756690
Clarify drawbacks of SystemState
alice-i-cecile Jul 29, 2022
56d09f5
Punctuation
alice-i-cecile Jul 29, 2022
bf42626
Typo
alice-i-cecile Jul 29, 2022
d553b1e
Fix mistake from merge conflict
alice-i-cecile Jul 29, 2022
3ec4643
Remove vague warning about callbacks
alice-i-cecile Jul 29, 2022
6a49a83
Add Send and Sync bounds to label traits
alice-i-cecile Jul 29, 2022
d60b6cf
Clean up docs and naming around system registration
alice-i-cecile Jul 29, 2022
e1b5ccd
Make App and World APIs consistent
alice-i-cecile Jul 29, 2022
2f3e570
Make doc links on higher level methods more useful
alice-i-cecile Jul 29, 2022
aa43356
fix doc tests
Pascualex Mar 9, 2023
7a2b873
remove run_systems_by_set
Pascualex Mar 10, 2023
ffdbd9b
remove run_systems_by_label from Commands
Pascualex Mar 10, 2023
eea7e16
rename params to marker
Pascualex Mar 10, 2023
95efbb0
key systems by SystemId
Pascualex Mar 13, 2023
b035e3b
shorten system registry function names
Pascualex Mar 13, 2023
383cbeb
Fixed most mistakes made during rebasing
Trashtalk217 Jun 27, 2023
cd0e7c5
Fix command_processing test, renamed apply_buffers
Trashtalk217 Jun 27, 2023
dea567f
Removed unnecessary imports, updated docs
Trashtalk217 Jun 27, 2023
f47629b
Remove all of SystemTypeIdLabel and SystemLabel
Trashtalk217 Jun 27, 2023
c396859
cargo fmt
Trashtalk217 Jun 27, 2023
7c8e15b
Added a failing test with unregistered systems
Trashtalk217 Jun 29, 2023
5061e55
Simplified SystemRegistry, removed SystemId
Trashtalk217 Jul 2, 2023
72b994f
Fix test, cleanup
Trashtalk217 Jul 3, 2023
64ad487
Better error for nested/recursive one-shot systems
Trashtalk217 Jul 3, 2023
1098cd5
Removed the option to call systems by Id
Trashtalk217 Jul 19, 2023
d9c89d9
Fixed a CI error in bevy_app
Trashtalk217 Jul 20, 2023
5e50d02
Fixed a previous rebasing error
Trashtalk217 Jul 20, 2023
c954f0d
Added a wrapper type for TypeId: SystemId
Trashtalk217 Jul 23, 2023
aa15370
Re-instated the possibillity to run a system by id
Trashtalk217 Aug 2, 2023
47c75fc
Fixed some build errors, fixed example
Trashtalk217 Aug 23, 2023
1963e63
Remove outdated comments
Trashtalk217 Aug 23, 2023
069db4b
Fix CI for Rust 1.72 (#9562)
rparrett Aug 25, 2023
afe255e
Add example
alice-i-cecile Jun 30, 2022
f565203
remove run_systems_by_set
Pascualex Mar 10, 2023
948147f
Renamed RunSystem to RunSystemCommand to avoid name collision
Trashtalk217 Aug 28, 2023
9195849
Fixed a mistake from rebasing
Trashtalk217 Aug 28, 2023
29837c6
Revert "Add example"
Trashtalk217 Aug 28, 2023
ebf4b99
Revert "Fix CI for Rust 1.72 (#9562)"
Trashtalk217 Aug 28, 2023
9d15661
Fixed tests
Trashtalk217 Aug 28, 2023
47c67b3
Moved run_system command / test over to system
Trashtalk217 Aug 28, 2023
478e5fd
cargo fmt
Trashtalk217 Aug 28, 2023
1b4e95c
Merge branch 'main' into system-registry
Trashtalk217 Aug 29, 2023
9f6a426
Rounded out remove systems from SystemRegistry
Trashtalk217 Aug 29, 2023
0a41754
Changed and added to documentation
Trashtalk217 Aug 29, 2023
12c067f
appease the CI
Trashtalk217 Aug 29, 2023
e646e9c
fixed doc test
Trashtalk217 Aug 29, 2023
17ad075
fixed doc example 2
Trashtalk217 Aug 29, 2023
d1e7e4c
fixed doc test 3
Trashtalk217 Aug 29, 2023
61986c3
Changes from code review
Trashtalk217 Aug 30, 2023
c6de53d
Changed outdated documentation
Trashtalk217 Aug 31, 2023
ea30824
Store system initialization explicitly
Trashtalk217 Aug 31, 2023
ff61574
spelling mistake
Trashtalk217 Aug 31, 2023
66b688c
Changed public API for nested one-shot systems
Trashtalk217 Sep 3, 2023
7bacdf2
Update crates/bevy_ecs/src/system/system.rs
Trashtalk217 Sep 7, 2023
83b898b
Removed SystemRegistry, systems as almost entities
Trashtalk217 Sep 8, 2023
d01a23e
Fixed documentation, added RegisteredSystemError's
Trashtalk217 Sep 9, 2023
c4c94e6
trimmed API
Trashtalk217 Sep 9, 2023
d2e9ac7
fixed documentation and an error
Trashtalk217 Sep 9, 2023
bcb7687
small fixes, removed failing test
Trashtalk217 Sep 9, 2023
0b8f8dc
renamed run_system and run_system_by_id
Trashtalk217 Sep 10, 2023
cdaecfc
cargo fmt
Trashtalk217 Sep 10, 2023
e5fbad0
fixed doc tests
Trashtalk217 Sep 10, 2023
99166fe
Updated documentation
Trashtalk217 Sep 19, 2023
aa81e3f
Remove unnecessary comment about being flexible.
james7132 Sep 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,16 @@ description = "Shows how to iterate over combinations of query results"
category = "ECS (Entity Component System)"
wasm = true

[[example]]
name = "one_shot_systems"
path = "examples/ecs/one_shot_systems.rs"

[package.metadata.example.one_shot_systems]
name = "One Shot Systems"
description = "Shows how to flexibly run systems without scheduling them"
category = "ECS (Entity Component System)"
wasm = false

[[example]]
name = "parallel_query"
path = "examples/ecs/parallel_query.rs"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_app/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod main_schedule;
mod plugin;
mod plugin_group;
mod schedule_runner;
mod system_registry;

#[cfg(feature = "bevy_ci_testing")]
pub mod ci_testing;
Expand Down
43 changes: 43 additions & 0 deletions crates/bevy_app/src/system_registry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use crate::App;
use bevy_ecs::prelude::*;
use bevy_ecs::system::RemovedSystem;
use bevy_ecs::system::{RunSystem, SystemId, SystemRegistryError};

impl App {
/// Register a system in the [`SystemRegistry`](bevy_ecs::system::SystemRegistry)
///
/// Calls [`SystemRegistry::register_system`](bevy_ecs::system::SystemRegistry::register).
pub fn register_system<M, S: IntoSystem<(), (), M> + 'static>(
&mut self,
system: S,
) -> &mut Self {
self.world.register_system(system);
self
}

/// Removes a registered system in the [`SystemRegistry`](bevy_ecs::system::SystemRegistry).
/// It returns nothing if id has no corresponding system.
///
/// Calls [`SystemRegistry::remove_system`](bevy_ecs::system::SystemRegistry::remove).
#[inline]
pub fn remove_system(&mut self, id: SystemId) -> Result<RemovedSystem, SystemRegistryError> {
self.world.remove_system(id)
}

/// Runs the supplied system on the [`World`] a single time.
///
/// Calls [`RunSystem::run_system`](bevy_ecs::system::RunSystem::run_system).
#[inline]
pub fn run_system<M, S: IntoSystem<(), (), M> + 'static>(&mut self, system: S) -> &mut Self {
self.world.run_system(system);
self
}

/// Run the systems corresponding to the id.
Trashtalk217 marked this conversation as resolved.
Show resolved Hide resolved
///
/// Calls [`SystemRegistry::run_system_by_id`](bevy_ecs::system::SystemRegistry::run_by_id).
#[inline]
pub fn run_system_by_id(&mut self, system_id: SystemId) -> Result<(), SystemRegistryError> {
self.world.run_system_by_id(system_id)
}
}
1 change: 1 addition & 0 deletions crates/bevy_ecs/src/schedule/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ impl<T> Hash for SystemTypeSet<T> {
// all systems of a given type are the same
}
}

impl<T> Clone for SystemTypeSet<T> {
fn clone(&self) -> Self {
*self
Expand Down
22 changes: 21 additions & 1 deletion crates/bevy_ecs/src/system/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{
self as bevy_ecs,
bundle::Bundle,
entity::{Entities, Entity},
system::{IntoSystem, RunSystemById, RunSystemCommand, SystemId},
world::{EntityWorldMut, FromWorld, World},
};
use bevy_ecs_macros::SystemParam;
Expand Down Expand Up @@ -519,11 +520,30 @@ impl<'w, 's> Commands<'w, 's> {
self.queue.push(RemoveResource::<R>::new());
}

/// Runs the supplied system on the [`World`] a single time.
///
/// Calls [`SystemRegistry::run_system`](crate::system::RunSystem::run_system).
pub fn run_system<
M: Send + Sync + 'static,
S: IntoSystem<(), (), M> + Send + Sync + 'static,
>(
&mut self,
system: S,
) {
self.queue.push(RunSystemCommand::new(system));
Trashtalk217 marked this conversation as resolved.
Show resolved Hide resolved
}

/// Runs the system corresponding to the given [`SystemId`].
///
/// Calls [`SystemRegistry::run_system_by_id`](crate::system::SystemRegistry::run_by_id).
pub fn run_system_by_id(&mut self, id: SystemId) {
Trashtalk217 marked this conversation as resolved.
Show resolved Hide resolved
self.queue.push(RunSystemById::new(id));
}

/// Pushes a generic [`Command`] to the command queue.
///
/// `command` can be a built-in command, custom struct that implements [`Command`] or a closure
/// that takes [`&mut World`](World) as an argument.
///
/// # Example
///
/// ```
Expand Down
6 changes: 5 additions & 1 deletion crates/bevy_ecs/src/system/function_system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ impl SystemMeta {
// (to avoid the need for unwrapping to retrieve SystemMeta)
/// Holds on to persistent state required to drive [`SystemParam`] for a [`System`].
///
/// This is a very powerful and convenient tool for working with exclusive world access,
/// This is a powerful and convenient tool for working with exclusive world access,
/// allowing you to fetch data from the [`World`] as if you were running a [`System`].
/// However, simply calling `world::run_system(my_system)` using a [`SystemRegistry`](crate::system::SystemRegistry)
/// can be significantly simpler and ensures that change detection and command flushing work as expected.
///
/// Borrow-checking is handled for you, allowing you to mutably access multiple compatible system parameters at once,
/// and arbitrary system parameters (like [`EventWriter`](crate::event::EventWriter)) can be conveniently fetched.
Expand All @@ -77,6 +79,8 @@ impl SystemMeta {
/// - [`Local`](crate::system::Local) variables that hold state
/// - [`EventReader`](crate::event::EventReader) system parameters, which rely on a [`Local`](crate::system::Local) to track which events have been seen
///
/// Note that this is automatically handled for you when using a [`SystemRegistry`](crate::system::SystemRegistry).
Trashtalk217 marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Example
///
/// Basic usage:
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_ecs/src/system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ mod query;
#[allow(clippy::module_inception)]
mod system;
mod system_param;
mod system_registry;

use std::borrow::Cow;

Expand All @@ -124,6 +125,7 @@ pub use function_system::*;
pub use query::*;
pub use system::*;
pub use system_param::*;
pub use system_registry::*;

use crate::world::World;

Expand Down
135 changes: 135 additions & 0 deletions crates/bevy_ecs/src/system/system.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use bevy_utils::tracing::warn;
use core::fmt::Debug;
use std::marker::PhantomData;

use crate::component::Tick;
use crate::system::Command;
use crate::world::unsafe_world_cell::UnsafeWorldCell;
use crate::{archetype::ArchetypeComponentId, component::ComponentId, query::Access, world::World};

Expand Down Expand Up @@ -165,6 +167,30 @@ impl<In: 'static, Out: 'static> Debug for dyn System<In = In, Out = Out> {
/// This function is not an efficient method of running systems and its meant to be used as a utility
/// for testing and/or diagnostics.
///
/// Systems called through [`run_system`](crate::system::RunSystem::run_system) do not hold onto any state,
/// as they are created and destroyed every time [`run_system`](crate::system::RunSystem::run_system) is called.
/// Practically, this means that [`Local`](crate::system::Local) variables are
/// reset on every run and change detection does not work.
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_ecs::system::RunSystem;
/// #[derive(Resource, Default)]
/// struct Counter(u8);
///
/// fn increment(mut counter: Local<Counter>) {
/// counter.0 += 1;
/// println!("{}", counter.0);
/// }
///
/// let mut world = World::default();
/// world.run_system(increment); // prints 1
/// world.run_system(increment); // still prints 1
/// ```
///
/// If you do need systems to hold onto state between runs, use the [`SystemRegistry`](crate::system::SystemRegistry)
/// andrun the system by their [`SystemId`](crate::system::SystemId).
Trashtalk217 marked this conversation as resolved.
Show resolved Hide resolved
///
/// # Usage
/// Typically, to test a system, or to extract specific diagnostics information from a world,
/// you'd need a [`Schedule`](crate::schedule::Schedule) to run the system. This can create redundant boilerplate code
Expand Down Expand Up @@ -258,9 +284,43 @@ impl RunSystem for &mut World {
}
}

/// The [`Command`] type for [`RunSystem`]
#[derive(Debug, Clone)]
pub struct RunSystemCommand<
M: Send + Sync + 'static,
S: IntoSystem<(), (), M> + Send + Sync + 'static,
> {
_phantom_marker: PhantomData<M>,
system: S,
}

impl<M: Send + Sync + 'static, S: IntoSystem<(), (), M> + Send + Sync + 'static>
RunSystemCommand<M, S>
{
/// Creates a new [`Command`] struct, which can be added to [`Commands`](crate::system::Commands)
#[inline]
#[must_use]
pub fn new(system: S) -> Self {
Self {
_phantom_marker: PhantomData,
system,
}
}
}

impl<M: Send + Sync + 'static, S: IntoSystem<(), (), M> + Send + Sync + 'static> Command
for RunSystemCommand<M, S>
{
#[inline]
fn apply(self, world: &mut World) {
world.run_system(self.system);
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate as bevy_ecs;
use crate::prelude::*;

#[test]
Expand All @@ -279,4 +339,79 @@ mod tests {
assert_eq!(n, 2);
assert_eq!(world.resource::<T>().0, 1);
}

#[derive(Resource, Default, PartialEq, Debug)]
struct Counter(u8);

#[allow(dead_code)]
fn count_up(mut counter: ResMut<Counter>) {
counter.0 += 1;
}

#[test]
/// We need to ensure that the system registry is accessible
/// even after being used once.
fn run_two_systems() {
let mut world = World::new();
world.init_resource::<Counter>();
assert_eq!(*world.resource::<Counter>(), Counter(0));
world.run_system(count_up);
assert_eq!(*world.resource::<Counter>(), Counter(1));
world.run_system(count_up);
assert_eq!(*world.resource::<Counter>(), Counter(2));
}

#[allow(dead_code)]
fn spawn_entity(mut commands: Commands) {
commands.spawn_empty();
}

#[test]
fn command_processing() {
let mut world = World::new();
assert_eq!(world.entities.len(), 0);
world.run_system(spawn_entity);
assert_eq!(world.entities.len(), 1);
}

#[test]
fn non_send_resources() {
fn non_send_count_down(mut ns: NonSendMut<Counter>) {
ns.0 -= 1;
}

let mut world = World::new();
world.insert_non_send_resource(Counter(10));
assert_eq!(*world.non_send_resource::<Counter>(), Counter(10));
world.run_system(non_send_count_down);
assert_eq!(*world.non_send_resource::<Counter>(), Counter(9));
}

#[test]
fn run_system_through_command() {
use crate::system::commands::Command;
use crate::system::RunSystemCommand;

let mut world = World::new();
let command = RunSystemCommand::new(spawn_entity);
assert_eq!(world.entities.len(), 0);
command.apply(&mut world);
assert_eq!(world.entities.len(), 1);
}

#[test]
fn system_recursion() {
fn count_to_ten(mut counter: ResMut<Counter>, mut commands: Commands) {
counter.0 += 1;
if counter.0 < 10 {
commands.run_system(count_to_ten);
}
}

let mut world = World::new();
world.init_resource::<Counter>();
assert_eq!(*world.resource::<Counter>(), Counter(0));
world.run_system(count_to_ten);
assert_eq!(*world.resource::<Counter>(), Counter(10));
}
}
Loading
Loading