diff --git a/crates/bevy_ecs/src/resource.rs b/crates/bevy_ecs/src/resource.rs index a94fa13277efc..62ed1a9a18d51 100644 --- a/crates/bevy_ecs/src/resource.rs +++ b/crates/bevy_ecs/src/resource.rs @@ -114,7 +114,7 @@ impl DerefMut for ResourceEntities { #[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Debug))] #[derive(Component, Debug)] #[component(on_insert, on_discard, on_despawn)] -pub struct IsResource(ComponentId); +pub struct IsResource(pub ComponentId); impl IsResource { /// Creates a new instance with the given `component_id` diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 3663087815cbb..f26e3637f2a63 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -9,12 +9,12 @@ use crate::{ bundle::Bundles, change_detection::{ComponentTicksMut, ComponentTicksRef, Tick}, component::{ComponentId, Components}, - entity::{Entities, EntityAllocator}, + entity::{Entities, Entity, EntityAllocator}, query::{ Access, FilteredAccess, FilteredAccessSet, IterQueryData, QueryData, QueryFilter, QuerySingleError, QueryState, ReadOnlyQueryData, }, - resource::{Resource, IS_RESOURCE}, + resource::{IsResource, Resource, IS_RESOURCE}, storage::NonSendData, system::{Query, Single, SystemMeta}, world::{ @@ -738,15 +738,20 @@ unsafe impl<'a, T: Resource> ReadOnlySystemParam for Res<'a, T> {} // SAFETY: Res ComponentId access is applied to SystemMeta. If this Res // conflicts with any prior access, a panic will occur. unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { - type State = ComponentId; + type State = (ComponentId, Entity); type Item<'w, 's> = Res<'w, T>; fn init_state(world: &mut World) -> Self::State { - world.components_registrator().register_component::() + let component_id = world.components_registrator().register_component::(); + if let Some(entity) = world.resource_entities().get(component_id) { + return (component_id, *entity); + } + let entity = world.spawn(IsResource(component_id)).id(); + (component_id, entity) } fn init_access( - &component_id: &Self::State, + &(component_id, _): &Self::State, system_meta: &mut SystemMeta, component_access_set: &mut FilteredAccessSet, _world: &mut World, @@ -777,13 +782,12 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { #[inline] unsafe fn validate_param( - &mut component_id: &mut Self::State, + &mut (component_id, entity): &mut Self::State, _system_meta: &SystemMeta, world: UnsafeWorldCell, ) -> Result<(), SystemParamValidationError> { // SAFETY: Read-only access to the resource - if let Some(entity) = unsafe { world.resource_entities() }.get(component_id) - && let Ok(entity_ref) = world.get_entity(*entity) + if let Ok(entity_ref) = world.get_entity(entity) && entity_ref.contains_id(component_id) { Ok(()) @@ -796,13 +800,15 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { #[inline] unsafe fn get_param<'w, 's>( - &mut component_id: &'s mut Self::State, + &mut (component_id, entity): &'s mut Self::State, system_meta: &SystemMeta, world: UnsafeWorldCell<'w>, change_tick: Tick, ) -> Self::Item<'w, 's> { let (ptr, ticks) = world - .get_resource_with_ticks(component_id) + .get_entity(entity) + .ok() + .and_then(|entity| entity.get_with_ticks(component_id)) .unwrap_or_else(|| { panic!( "Resource requested by {} does not exist: {}", @@ -826,15 +832,20 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { // SAFETY: Res ComponentId access is applied to SystemMeta. If this Res // conflicts with any prior access, a panic will occur. unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { - type State = ComponentId; + type State = (ComponentId, Entity); type Item<'w, 's> = ResMut<'w, T>; fn init_state(world: &mut World) -> Self::State { - world.components_registrator().register_component::() + let component_id = world.components_registrator().register_component::(); + if let Some(entity) = world.resource_entities().get(component_id) { + return (component_id, *entity); + } + let entity = world.spawn(IsResource(component_id)).id(); + (component_id, entity) } fn init_access( - &component_id: &Self::State, + &(component_id, _): &Self::State, system_meta: &mut SystemMeta, component_access_set: &mut FilteredAccessSet, _world: &mut World, @@ -868,13 +879,12 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { #[inline] unsafe fn validate_param( - &mut component_id: &mut Self::State, + &mut (component_id, entity): &mut Self::State, _system_meta: &SystemMeta, world: UnsafeWorldCell, ) -> Result<(), SystemParamValidationError> { // SAFETY: Read-only access to the resource. - if let Some(entity) = unsafe { world.resource_entities() }.get(component_id) - && let Ok(entity_ref) = world.get_entity(*entity) + if let Ok(entity_ref) = world.get_entity(entity) && entity_ref.contains_id(component_id) { Ok(()) @@ -887,13 +897,15 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { #[inline] unsafe fn get_param<'w, 's>( - &mut component_id: &'s mut Self::State, + &mut (component_id, entity): &'s mut Self::State, system_meta: &SystemMeta, world: UnsafeWorldCell<'w>, change_tick: Tick, ) -> Self::Item<'w, 's> { let value = world - .get_resource_mut_by_id(component_id) + .get_entity(entity) + .ok() + .and_then(|entity| entity.get_mut_by_id(component_id).ok()) .unwrap_or_else(|| { panic!( "Resource requested by {} does not exist: {}", diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 7452d545bd456..75c8cc89d7061 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -1121,6 +1121,39 @@ impl<'w> UnsafeEntityCell<'w> { } } + /// Gets the component of the given [`ComponentId`] from the entity with the tick information. + /// + /// **You should prefer to use the typed API where possible and only + /// use this in cases where the actual component types are not known at + /// compile time.** + /// + /// Unlike [`UnsafeEntityCell::get`], this returns a raw pointer to the component, + /// which is only valid while the `'w` borrow of the lifetime is active. + /// + /// # Safety + /// It is the caller's responsibility to ensure that + /// - the [`UnsafeEntityCell`] has permission to access the component + /// - no other mutable references to the component exist at the same time + #[inline] + pub unsafe fn get_with_ticks( + self, + component_id: ComponentId, + ) -> Option<(Ptr<'w>, ComponentTickCells<'w>)> { + let info = self.world.components().get_info(component_id)?; + // SAFETY: + // - caller ensures there is no `&mut World` + // - caller ensures there are no mutable borrows of this resource + // - caller ensures that we have permission to access this resource + // - storage_type and location are valid + get_component_and_ticks( + self.world, + component_id, + info.storage_type(), + self.entity, + self.location, + ) + } + /// Retrieves a mutable untyped reference to the given `entity`'s [`Component`] of the given [`ComponentId`]. /// Returns `None` if the `entity` does not have a [`Component`] of the given type. /// diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_render/src/extract_param.rs index 4d81fb9eb1903..d945854ebf1b3 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_render/src/extract_param.rs @@ -101,7 +101,7 @@ where world: UnsafeWorldCell, ) -> Result<(), SystemParamValidationError> { // SAFETY: Read-only access to world data registered in `init_state`. - let result = unsafe { world.get_resource_by_id(state.main_world_state) }; + let result = unsafe { world.get_resource_by_id(state.main_world_state.0) }; let Some(main_world) = result else { return Err(SystemParamValidationError::invalid::( "`MainWorld` resource does not exist",