From 1f99363de90cafc596f162e100511c2356cabd67 Mon Sep 17 00:00:00 2001 From: bilsen Date: Thu, 3 Feb 2022 23:43:25 +0000 Subject: [PATCH] Add &World as SystemParam (#2923) # Objective Make it possible to use `&World` as a system parameter ## Solution It seems like all the pieces were already in place, very simple impl Co-authored-by: Carter Anderson --- crates/bevy_ecs/src/query/access.rs | 4 ++ .../src/schedule/executor_parallel.rs | 29 ++++++++++ crates/bevy_ecs/src/system/mod.rs | 1 + crates/bevy_ecs/src/system/system_param.rs | 57 ++++++++++++++++++- 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index 25c1f39054..328df83834 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -186,6 +186,10 @@ impl FilteredAccess { self.with.union_with(&access.with); self.without.union_with(&access.without); } + + pub fn read_all(&mut self) { + self.access.read_all(); + } } pub struct FilteredAccessSet { diff --git a/crates/bevy_ecs/src/schedule/executor_parallel.rs b/crates/bevy_ecs/src/schedule/executor_parallel.rs index c3448f203f..ec8d5a3079 100644 --- a/crates/bevy_ecs/src/schedule/executor_parallel.rs +++ b/crates/bevy_ecs/src/schedule/executor_parallel.rs @@ -443,6 +443,35 @@ mod tests { assert_eq!(receive_events(&world), vec![StartedSystems(2),]); } + #[test] + fn world() { + let mut world = World::new(); + world.spawn().insert(W(0usize)); + fn wants_world(_: &World) {} + fn wants_mut(_: Query<&mut W>) {} + let mut stage = SystemStage::parallel() + .with_system(wants_mut) + .with_system(wants_mut); + stage.run(&mut world); + assert_eq!( + receive_events(&world), + vec![StartedSystems(1), StartedSystems(1),] + ); + let mut stage = SystemStage::parallel() + .with_system(wants_mut) + .with_system(wants_world); + stage.run(&mut world); + assert_eq!( + receive_events(&world), + vec![StartedSystems(1), StartedSystems(1),] + ); + let mut stage = SystemStage::parallel() + .with_system(wants_world) + .with_system(wants_world); + stage.run(&mut world); + assert_eq!(receive_events(&world), vec![StartedSystems(2),]); + } + #[test] fn non_send_resource() { use std::thread; diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 9eec231db3..7a84674ea8 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -56,6 +56,7 @@ //! - [`EventWriter`](crate::event::EventWriter) //! - [`NonSend`] and `Option` //! - [`NonSendMut`] and `Option` +//! - [`&World`](crate::world::World) //! - [`RemovedComponents`] //! - [`SystemChangeTick`] //! - [`Archetypes`](crate::archetype::Archetypes) (Provides Archetype metadata) diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 56900decf2..344b2d71ce 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -6,7 +6,8 @@ use crate::{ component::{Component, ComponentId, ComponentTicks, Components}, entity::{Entities, Entity}, query::{ - FilterFetch, FilteredAccess, FilteredAccessSet, QueryState, ReadOnlyFetch, WorldQuery, + Access, FilterFetch, FilteredAccess, FilteredAccessSet, QueryState, ReadOnlyFetch, + WorldQuery, }, system::{CommandQueue, Commands, Query, SystemMeta}, world::{FromWorld, World}, @@ -532,6 +533,60 @@ impl<'w, 's> SystemParamFetch<'w, 's> for CommandQueue { } } +/// SAFE: only reads world +unsafe impl ReadOnlySystemParamFetch for WorldState {} + +/// The [`SystemParamState`] of [`&World`](crate::world::World). +pub struct WorldState; + +impl<'w, 's> SystemParam for &'w World { + type Fetch = WorldState; +} + +unsafe impl<'w, 's> SystemParamState for WorldState { + type Config = (); + + fn init(_world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self { + let mut access = Access::default(); + access.read_all(); + if !system_meta + .archetype_component_access + .is_compatible(&access) + { + panic!("&World conflicts with a previous mutable system parameter. Allowing this would break Rust's mutability rules"); + } + system_meta.archetype_component_access.extend(&access); + + let mut filtered_access = FilteredAccess::default(); + + filtered_access.read_all(); + if !system_meta + .component_access_set + .get_conflicts(&filtered_access) + .is_empty() + { + panic!("&World conflicts with a previous mutable system parameter. Allowing this would break Rust's mutability rules"); + } + system_meta.component_access_set.add(filtered_access); + + WorldState + } + + fn default_config() -> Self::Config {} +} + +impl<'w, 's> SystemParamFetch<'w, 's> for WorldState { + type Item = &'w World; + unsafe fn get_param( + _state: &'s mut Self, + _system_meta: &SystemMeta, + world: &'w World, + _change_tick: u32, + ) -> Self::Item { + world + } +} + /// A system local [`SystemParam`]. /// /// A local may only be accessed by the system itself and is therefore not visible to other systems.