Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 44 additions & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,12 @@ mod tests {
component::{Component, ComponentId, RequiredComponents, RequiredComponentsError},
entity::{Entity, EntityMapper},
entity_disabling::DefaultQueryFilters,
lifecycle::HookContext,
prelude::Or,
query::{Added, Changed, FilteredAccess, QueryFilter, With, Without},
resource::Resource,
world::{EntityMut, EntityRef, Mut, World},
system::SystemId,
world::{DeferredWorld, EntityMut, EntityRef, Mut, World},
};
use alloc::{
string::{String, ToString},
Expand Down Expand Up @@ -2776,4 +2778,45 @@ mod tests {

fn custom_clone(_source: &SourceComponent, _ctx: &mut ComponentCloneCtx) {}
}

#[test]
fn register_system_cached_on_deferred_world() {
let mut world = World::new();

let first_entity = world.spawn(ComponentWithOnAddHook).id();
let second_entity = world.spawn(ComponentWithOnAddHook).id();

let first_system = world
.entity(first_entity)
.get::<RegisteredSystem>()
.unwrap()
.0;
let second_system = world
.entity(second_entity)
.get::<RegisteredSystem>()
.unwrap()
.0;

assert_eq!(first_system, second_system);

#[derive(Component)]
#[component(on_add = Self::on_add)]
struct ComponentWithOnAddHook;

impl ComponentWithOnAddHook {
fn on_add(mut world: DeferredWorld, context: HookContext) {
let system_id =
world.register_system_cached(Self::the_system_that_will_be_registered);
world
.commands()
.entity(context.entity)
.insert(RegisteredSystem(system_id));
}

fn the_system_that_will_be_registered() {}
}

#[derive(Component)]
struct RegisteredSystem(SystemId);
}
}
40 changes: 39 additions & 1 deletion crates/bevy_ecs/src/world/deferred_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
query::{QueryData, QueryFilter},
relationship::RelationshipHookMode,
resource::Resource,
system::{Commands, Query},
system::{CachedSystemId, Commands, IntoSystem, Query, SystemId, SystemInput},
traversal::Traversal,
world::{error::EntityMutableFetchError, EntityFetcher, WorldEntityFetch},
};
Expand Down Expand Up @@ -890,4 +890,42 @@ impl<'w> DeferredWorld<'w> {
pub(crate) fn as_unsafe_world_cell(&mut self) -> UnsafeWorldCell {
self.world
}

/// Registers a system or returns its cached [`SystemId`].
///
/// If you want to run the system immediately and you don't need its `SystemId`, see
/// [`Commands::run_system_cached`].
///
/// The first time this function is called for a particular system, it will register it and
/// store its [`SystemId`] in a [`CachedSystemId`] resource for later. If you would rather
/// manage the `SystemId` yourself, or register multiple copies of the same system, use
/// [`Commands::register_system`] instead.
///
/// # Limitations
///
/// If this function is called twice for the same system before commands are executed, then it will register the system twice.
///
/// This function only accepts ZST (zero-sized) systems to guarantee that any two systems of
/// the same type must be equal. This means that closures that capture the environment, and
/// function pointers, are not accepted.
///
/// If you want to access values from the environment within a system, consider passing them in
/// as inputs via [`Commands::run_system_cached_with`]. If that's not an option, consider
/// [`Commands::register_system`] instead.
pub fn register_system_cached<I, O, M, S>(&mut self, system: S) -> SystemId<I, O>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe get_or_insert_system would be more idiomatic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

World already has register_system_cached, and so I named this function likewise.

where
I: SystemInput + Send + 'static,
O: Send + 'static,
S: IntoSystem<I, O, M> + 'static,
{
match self.get_resource::<CachedSystemId<S>>() {
Some(cached_system) => SystemId::from_entity(cached_system.entity),
None => {
let mut commands = self.commands();
let system_id = commands.register_system(system);
commands.insert_resource(CachedSystemId::<S>::new(system_id));
system_id
}
}
}
}