diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index 7ca5dd43add8c..05afa4f614f76 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -375,8 +375,7 @@ fn observer_system_runner( }; // TODO: Move this check into the observer cache to avoid dynamic dispatch - // SAFETY: We only access world metadata - let last_trigger = unsafe { world.world_metadata() }.last_trigger_id(); + let last_trigger = world.last_trigger_id(); if state.last_trigger_id == last_trigger { return; } diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index d4a0a74b45b0d..b9a4f3d22ea9d 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -362,9 +362,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { let world = self.world; - let query_lens_state = self - .query_state - .transmute_filtered::<(L, Entity), F>(world.components()); + let query_lens_state = self.query_state.transmute_filtered::<(L, Entity), F>(world); // SAFETY: // `self.world` has permission to access the required components. @@ -456,9 +454,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { let world = self.world; - let query_lens_state = self - .query_state - .transmute_filtered::<(L, Entity), F>(world.components()); + let query_lens_state = self.query_state.transmute_filtered::<(L, Entity), F>(world); // SAFETY: // `self.world` has permission to access the required components. @@ -558,9 +554,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { let world = self.world; - let query_lens_state = self - .query_state - .transmute_filtered::<(L, Entity), F>(world.components()); + let query_lens_state = self.query_state.transmute_filtered::<(L, Entity), F>(world); // SAFETY: // `self.world` has permission to access the required components. @@ -626,9 +620,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { let world = self.world; - let query_lens_state = self - .query_state - .transmute_filtered::<(L, Entity), F>(world.components()); + let query_lens_state = self.query_state.transmute_filtered::<(L, Entity), F>(world); // SAFETY: // `self.world` has permission to access the required components. @@ -757,9 +749,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { let world = self.world; - let query_lens_state = self - .query_state - .transmute_filtered::<(L, Entity), F>(world.components()); + let query_lens_state = self.query_state.transmute_filtered::<(L, Entity), F>(world); // SAFETY: // `self.world` has permission to access the required components. @@ -828,9 +818,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { let world = self.world; - let query_lens_state = self - .query_state - .transmute_filtered::<(L, Entity), F>(world.components()); + let query_lens_state = self.query_state.transmute_filtered::<(L, Entity), F>(world); // SAFETY: // `self.world` has permission to access the required components. @@ -900,9 +888,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> QueryIter<'w, 's, D, F> { let world = self.world; - let query_lens_state = self - .query_state - .transmute_filtered::<(L, Entity), F>(world.components()); + let query_lens_state = self.query_state.transmute_filtered::<(L, Entity), F>(world); // SAFETY: // `self.world` has permission to access the required components. diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index dcb20250c2466..a5d431edbdd67 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -1,7 +1,7 @@ use crate::{ archetype::{Archetype, ArchetypeComponentId, ArchetypeGeneration, ArchetypeId}, batching::BatchingStrategy, - component::{ComponentId, Components, Tick}, + component::{ComponentId, Tick}, entity::Entity, prelude::FromWorld, query::{ @@ -476,21 +476,27 @@ impl QueryState { /// You might end up with a mix of archetypes that only matched the original query + archetypes that only match /// the new [`QueryState`]. Most of the safe methods on [`QueryState`] call [`QueryState::update_archetypes`] internally, so this /// best used through a [`Query`](crate::system::Query). - pub fn transmute(&self, components: &Components) -> QueryState { - self.transmute_filtered::(components) + pub fn transmute<'a, NewD: QueryData>( + &self, + world: impl Into>, + ) -> QueryState { + self.transmute_filtered::(world.into()) } /// Creates a new [`QueryState`] with the same underlying [`FilteredAccess`], matched tables and archetypes /// as self but with a new type signature. /// /// Panics if `NewD` or `NewF` require accesses that this query does not have. - pub fn transmute_filtered( + pub fn transmute_filtered<'a, NewD: QueryData, NewF: QueryFilter>( &self, - components: &Components, + world: impl Into>, ) -> QueryState { + let world = world.into(); + self.validate_world(world.id()); + let mut component_access = FilteredAccess::default(); - let mut fetch_state = NewD::get_state(components).expect("Could not create fetch_state, Please initialize all referenced components before transmuting."); - let filter_state = NewF::get_state(components).expect("Could not create filter_state, Please initialize all referenced components before transmuting."); + let mut fetch_state = NewD::get_state(world.components()).expect("Could not create fetch_state, Please initialize all referenced components before transmuting."); + let filter_state = NewF::get_state(world.components()).expect("Could not create filter_state, Please initialize all referenced components before transmuting."); NewD::set_access(&mut fetch_state, &self.component_access); NewD::update_component_access(&fetch_state, &mut component_access); @@ -542,12 +548,12 @@ impl QueryState { /// ## Panics /// /// Will panic if `NewD` contains accesses not in `Q` or `OtherQ`. - pub fn join( + pub fn join<'a, OtherD: QueryData, NewD: QueryData>( &self, - components: &Components, + world: impl Into>, other: &QueryState, ) -> QueryState { - self.join_filtered::<_, (), NewD, ()>(components, other) + self.join_filtered::<_, (), NewD, ()>(world, other) } /// Use this to combine two queries. The data accessed will be the intersection @@ -557,23 +563,28 @@ impl QueryState { /// /// Will panic if `NewD` or `NewF` requires accesses not in `Q` or `OtherQ`. pub fn join_filtered< + 'a, OtherD: QueryData, OtherF: QueryFilter, NewD: QueryData, NewF: QueryFilter, >( &self, - components: &Components, + world: impl Into>, other: &QueryState, ) -> QueryState { if self.world_id != other.world_id { panic!("Joining queries initialized on different worlds is not allowed."); } + let world = world.into(); + + self.validate_world(world.id()); + let mut component_access = FilteredAccess::default(); - let mut new_fetch_state = NewD::get_state(components) + let mut new_fetch_state = NewD::get_state(world.components()) .expect("Could not create fetch_state, Please initialize all referenced components before transmuting."); - let new_filter_state = NewF::get_state(components) + let new_filter_state = NewF::get_state(world.components()) .expect("Could not create filter_state, Please initialize all referenced components before transmuting."); NewD::set_access(&mut new_fetch_state, &self.component_access); @@ -1779,7 +1790,7 @@ mod tests { world.spawn((A(1), B(0))); let query_state = world.query::<(&A, &B)>(); - let mut new_query_state = query_state.transmute::<&A>(world.components()); + let mut new_query_state = query_state.transmute::<&A>(&world); assert_eq!(new_query_state.iter(&world).len(), 1); let a = new_query_state.single(&world); @@ -1793,7 +1804,7 @@ mod tests { world.spawn((A(1), B(0), C(0))); let query_state = world.query_filtered::<(&A, &B), Without>(); - let mut new_query_state = query_state.transmute::<&A>(world.components()); + let mut new_query_state = query_state.transmute::<&A>(&world); // even though we change the query to not have Without, we do not get the component with C. let a = new_query_state.single(&world); @@ -1807,7 +1818,7 @@ mod tests { let entity = world.spawn(A(10)).id(); let q = world.query::<()>(); - let mut q = q.transmute::(world.components()); + let mut q = q.transmute::(&world); assert_eq!(q.single(&world), entity); } @@ -1817,11 +1828,11 @@ mod tests { world.spawn(A(10)); let q = world.query::<&A>(); - let mut new_q = q.transmute::>(world.components()); + let mut new_q = q.transmute::>(&world); assert!(new_q.single(&world).is_added()); let q = world.query::>(); - let _ = q.transmute::<&A>(world.components()); + let _ = q.transmute::<&A>(&world); } #[test] @@ -1830,8 +1841,8 @@ mod tests { world.spawn(A(0)); let q = world.query::<&mut A>(); - let _ = q.transmute::>(world.components()); - let _ = q.transmute::<&A>(world.components()); + let _ = q.transmute::>(&world); + let _ = q.transmute::<&A>(&world); } #[test] @@ -1840,7 +1851,7 @@ mod tests { world.spawn(A(0)); let q: QueryState> = world.query::(); - let _ = q.transmute::(world.components()); + let _ = q.transmute::(&world); } #[test] @@ -1849,8 +1860,8 @@ mod tests { world.spawn((A(0), B(0))); let query_state = world.query::<(Option<&A>, &B)>(); - let _ = query_state.transmute::>(world.components()); - let _ = query_state.transmute::<&B>(world.components()); + let _ = query_state.transmute::>(&world); + let _ = query_state.transmute::<&B>(&world); } #[test] @@ -1864,7 +1875,7 @@ mod tests { world.spawn(A(0)); let query_state = world.query::<&A>(); - let mut _new_query_state = query_state.transmute::<(&A, &B)>(world.components()); + let mut _new_query_state = query_state.transmute::<(&A, &B)>(&world); } #[test] @@ -1876,7 +1887,7 @@ mod tests { world.spawn(A(0)); let query_state = world.query::<&A>(); - let mut _new_query_state = query_state.transmute::<&mut A>(world.components()); + let mut _new_query_state = query_state.transmute::<&mut A>(&world); } #[test] @@ -1888,7 +1899,7 @@ mod tests { world.spawn(C(0)); let query_state = world.query::>(); - let mut new_query_state = query_state.transmute::<&A>(world.components()); + let mut new_query_state = query_state.transmute::<&A>(&world); let x = new_query_state.single(&world); assert_eq!(x.0, 1234); } @@ -1902,15 +1913,15 @@ mod tests { world.init_component::(); let q = world.query::(); - let _ = q.transmute::<&A>(world.components()); + let _ = q.transmute::<&A>(&world); } #[test] fn can_transmute_filtered_entity() { let mut world = World::new(); let entity = world.spawn((A(0), B(1))).id(); - let query = QueryState::<(Entity, &A, &B)>::new(&mut world) - .transmute::(world.components()); + let query = + QueryState::<(Entity, &A, &B)>::new(&mut world).transmute::(&world); let mut query = query; // Our result is completely untyped @@ -1927,7 +1938,7 @@ mod tests { let entity_a = world.spawn(A(0)).id(); let mut query = QueryState::<(Entity, &A, Has)>::new(&mut world) - .transmute_filtered::<(Entity, Has), Added>(world.components()); + .transmute_filtered::<(Entity, Has), Added>(&world); assert_eq!((entity_a, false), query.single(&world)); @@ -1947,7 +1958,7 @@ mod tests { let entity_a = world.spawn(A(0)).id(); let mut detection_query = QueryState::<(Entity, &A)>::new(&mut world) - .transmute_filtered::>(world.components()); + .transmute_filtered::>(&world); let mut change_query = QueryState::<&mut A>::new(&mut world); assert_eq!(entity_a, detection_query.single(&world)); @@ -1970,7 +1981,20 @@ mod tests { world.init_component::(); world.init_component::(); let query = QueryState::<&A>::new(&mut world); - let _new_query = query.transmute_filtered::>(world.components()); + let _new_query = query.transmute_filtered::>(&world); + } + + // Regression test for #14629 + #[test] + #[should_panic] + fn transmute_with_different_world() { + let mut world = World::new(); + world.spawn((A(1), B(2))); + + let mut world2 = World::new(); + world2.init_component::(); + + world.query::<(&A, &B)>().transmute::<&B>(&world2); } #[test] @@ -1983,8 +2007,7 @@ mod tests { let query_1 = QueryState::<&A, Without>::new(&mut world); let query_2 = QueryState::<&B, Without>::new(&mut world); - let mut new_query: QueryState = - query_1.join_filtered(world.components(), &query_2); + let mut new_query: QueryState = query_1.join_filtered(&world, &query_2); assert_eq!(new_query.single(&world), entity_ab); } @@ -1999,8 +2022,7 @@ mod tests { let query_1 = QueryState::<&A>::new(&mut world); let query_2 = QueryState::<&B, Without>::new(&mut world); - let mut new_query: QueryState = - query_1.join_filtered(world.components(), &query_2); + let mut new_query: QueryState = query_1.join_filtered(&world, &query_2); assert!(new_query.get(&world, entity_ab).is_ok()); // should not be able to get entity with c. @@ -2016,7 +2038,7 @@ mod tests { world.init_component::(); let query_1 = QueryState::<&A>::new(&mut world); let query_2 = QueryState::<&B>::new(&mut world); - let _query: QueryState<&C> = query_1.join(world.components(), &query_2); + let _query: QueryState<&C> = query_1.join(&world, &query_2); } #[test] @@ -2030,6 +2052,6 @@ mod tests { let mut world = World::new(); let query_1 = QueryState::<&A, Without>::new(&mut world); let query_2 = QueryState::<&B, Without>::new(&mut world); - let _: QueryState> = query_1.join_filtered(world.components(), &query_2); + let _: QueryState> = query_1.join_filtered(&world, &query_2); } } diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 215c8d7b1108b..e270c028d0a21 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -1368,8 +1368,7 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { pub fn transmute_lens_filtered( &mut self, ) -> QueryLens<'_, NewD, NewF> { - let components = self.world.components(); - let state = self.state.transmute_filtered::(components); + let state = self.state.transmute_filtered::(self.world); QueryLens { world: self.world, state, @@ -1460,10 +1459,9 @@ impl<'w, 's, D: QueryData, F: QueryFilter> Query<'w, 's, D, F> { &mut self, other: &mut Query, ) -> QueryLens<'_, NewD, NewF> { - let components = self.world.components(); let state = self .state - .join_filtered::(components, other.state); + .join_filtered::(self.world, other.state); QueryLens { world: self.world, state, diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 61beeff8442ef..00b5349d80c1a 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -83,6 +83,18 @@ unsafe impl Send for UnsafeWorldCell<'_> {} // SAFETY: `&World` and `&mut World` are both `Sync` unsafe impl Sync for UnsafeWorldCell<'_> {} +impl<'w> From<&'w mut World> for UnsafeWorldCell<'w> { + fn from(value: &'w mut World) -> Self { + value.as_unsafe_world_cell() + } +} + +impl<'w> From<&'w World> for UnsafeWorldCell<'w> { + fn from(value: &'w World) -> Self { + value.as_unsafe_world_cell_readonly() + } +} + impl<'w> UnsafeWorldCell<'w> { /// Creates a [`UnsafeWorldCell`] that can be used to access everything immutably #[inline] @@ -257,6 +269,15 @@ impl<'w> UnsafeWorldCell<'w> { unsafe { self.world_metadata() }.read_change_tick() } + /// Returns the id of the last ECS event that was fired. + /// Used internally to ensure observers don't trigger multiple times for the same event. + #[inline] + pub fn last_trigger_id(&self) -> u32 { + // SAFETY: + // - we only access world metadata + unsafe { self.world_metadata() }.last_trigger_id() + } + /// Returns the [`Tick`] indicating the last time that [`World::clear_trackers`] was called. /// /// If this `UnsafeWorldCell` was created from inside of an exclusive system (a [`System`] that