diff --git a/benches/benches/bevy_ecs/scheduling/run_condition.rs b/benches/benches/bevy_ecs/scheduling/run_condition.rs index 0d6e4107c6245..4e5a793f65440 100644 --- a/benches/benches/bevy_ecs/scheduling/run_condition.rs +++ b/benches/benches/bevy_ecs/scheduling/run_condition.rs @@ -57,7 +57,7 @@ pub fn run_condition_no(criterion: &mut Criterion) { group.finish(); } -#[derive(Component, Resource)] +#[derive(Resource)] struct TestBool(pub bool); pub fn run_condition_yes_with_query(criterion: &mut Criterion) { diff --git a/crates/bevy_ecs/macros/src/component.rs b/crates/bevy_ecs/macros/src/component.rs index ba695bef49550..8cd8cc1cd8067 100644 --- a/crates/bevy_ecs/macros/src/component.rs +++ b/crates/bevy_ecs/macros/src/component.rs @@ -34,6 +34,11 @@ pub fn derive_event(input: TokenStream) -> TokenStream { } pub fn derive_resource(input: TokenStream) -> TokenStream { + // The resource derive *also* implements the Component trait + // We generate the Component implementation first, then add the Resource implementation, + // so then we can pick up all of the attributes from the Component derive + let component_derive_token_stream = derive_component(input.clone()); + let mut ast = parse_macro_input!(input as DeriveInput); let bevy_ecs_path: Path = crate::bevy_ecs_path(); @@ -45,10 +50,15 @@ pub fn derive_resource(input: TokenStream) -> TokenStream { let struct_name = &ast.ident; let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl(); - TokenStream::from(quote! { + let resource_impl_token_stream = TokenStream::from(quote! { impl #impl_generics #bevy_ecs_path::resource::Resource for #struct_name #type_generics #where_clause { } - }) + }); + + resource_impl_token_stream + .into_iter() + .chain(component_derive_token_stream.into_iter()) + .collect() } pub fn derive_component(input: TokenStream) -> TokenStream { diff --git a/crates/bevy_ecs/src/component.rs b/crates/bevy_ecs/src/component.rs index f17bbb106c8cb..4287f5cad084e 100644 --- a/crates/bevy_ecs/src/component.rs +++ b/crates/bevy_ecs/src/component.rs @@ -1116,6 +1116,9 @@ impl Default for ComponentCloneHandlers { pub struct Components { components: Vec, indices: TypeIdMap, + // Each resource corresponds to a single entity, + // and the resource data is stored as a component on that entity. + resource_entities: HashMap, resource_indices: TypeIdMap, component_clone_handlers: ComponentCloneHandlers, } @@ -1212,6 +1215,61 @@ impl Components { component_id } + /// Store a new resource entity of type `R`. + #[inline] + pub(crate) fn cache_resource_entity( + &mut self, + storages: &mut Storages, + entity: Entity, + ) { + let id = self.register_component::(storages); + self.cache_resource_entity_by_id(entity, id); + } + + /// Stores a new resource entity associated with the given component ID. + #[inline] + pub(crate) fn cache_resource_entity_by_id(&mut self, entity: Entity, id: ComponentId) { + self.resource_entities.insert(id, entity); + } + + /// Removes the resource entity of type `R`. + #[inline] + pub(crate) fn remove_resource_entity(&mut self) -> Option { + let Some(id) = self.component_id::() else { + // If the component ID was not found, we can't have registered an entity of this type. + return None; + }; + + self.remove_resource_entity_by_id(id) + } + + /// Removes the resource entity associated with the given component ID. + #[inline] + pub(crate) fn remove_resource_entity_by_id(&mut self, id: ComponentId) -> Option { + self.resource_entities.remove(&id) + } + + /// Looks up the entity associated with the given resource by type. + /// + /// If no such entity exists, this will return `None`. + /// + /// Also see [`Components::get_resource_entity_by_id`]. + #[inline] + pub fn get_resource_entity(&self) -> Option { + let id = self.component_id::()?; + self.get_resource_entity_by_id(id) + } + + /// Looks up the entity associated with the given resource by [`ComponentId`]. + /// + /// If no such entity exists, this will return `None`. + /// + /// Also see [`Components::get_resource_entity`]. + #[inline] + pub fn get_resource_entity_by_id(&self, id: ComponentId) -> Option { + self.resource_entities.get(&id).copied() + } + /// Returns the number of components registered with this instance. #[inline] pub fn len(&self) -> usize { diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 21fc5cddd0c62..00ca862c7c8ad 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -156,7 +156,7 @@ mod tests { }; use std::sync::Mutex; - #[derive(Component, Resource, Debug, PartialEq, Eq, Hash, Clone, Copy)] + #[derive(Resource, Debug, PartialEq, Eq, Hash, Clone, Copy)] struct A(usize); #[derive(Component, Debug, PartialEq, Eq, Hash, Clone, Copy)] struct B(usize); diff --git a/crates/bevy_ecs/src/reflect/resource.rs b/crates/bevy_ecs/src/reflect/resource.rs index 238fd5f4e77ae..647d54df69641 100644 --- a/crates/bevy_ecs/src/reflect/resource.rs +++ b/crates/bevy_ecs/src/reflect/resource.rs @@ -6,11 +6,12 @@ use crate::{ change_detection::Mut, - component::ComponentId, + component::{ComponentId, ComponentMutability}, resource::Resource, world::{unsafe_world_cell::UnsafeWorldCell, World}, }; use bevy_reflect::{FromReflect, FromType, PartialReflect, Reflect, TypePath, TypeRegistry}; +use disqualified::ShortName; use super::from_reflect_with_fallback; @@ -197,16 +198,25 @@ impl FromType for ReflectResource { world.insert_resource(resource); }, apply: |world, reflected_resource| { - let mut resource = world.resource_mut::(); + if !R::Mutability::MUTABLE { + let name = ShortName::of::(); + panic!("Cannot call `ReflectResource::apply` on component {name}. It is immutable, and cannot modified through reflection"); + } + + // SAFETY: `R` is mutable, as checked above + let mut resource = unsafe { world.get_resource_mut_assume_mutable::().unwrap() }; resource.apply(reflected_resource); }, apply_or_insert: |world, reflected_resource, registry| { - if let Some(mut resource) = world.get_resource_mut::() { - resource.apply(reflected_resource); - } else { + if !R::Mutability::MUTABLE | !world.contains_resource::() { let resource = from_reflect_with_fallback::(reflected_resource, world, registry); world.insert_resource(resource); + } else { + // SAFETY: `R` is mutable, as checked above + let mut resource = + unsafe { world.get_resource_mut_assume_mutable::().unwrap() }; + resource.apply(reflected_resource); } }, remove: |world| { diff --git a/crates/bevy_ecs/src/resource.rs b/crates/bevy_ecs/src/resource.rs index c3f7805631560..8fd877056a430 100644 --- a/crates/bevy_ecs/src/resource.rs +++ b/crates/bevy_ecs/src/resource.rs @@ -1,4 +1,32 @@ //! Resources are unique, singleton-like data types that can be accessed from systems and stored in the [`World`](crate::world::World). +//! +//! Under the hood, each resource of type `R` is stored in a dedicated entity in the world, +//! with the data of type `R` stored as a component on that entity. +//! These entities are marked with the [`ResourceEntity`] component and the [`IsResource`] component. +//! This strategy allows Bevy to reuse the existing ECS tools for working with resources: +//! storage, querying, hooks, observers, relationships and more. +//! +//! While resources are components, not all resources are components! +//! The [`Resource`] trait is used to mark components which can be used as such, +//! and must be derived for any type that is to be used as a resource. +//! The various methods for inserting and accessing resources require this trait bound (when working with Rust types), +//! and the simplest, clearest way to access resource data in systems is to use the [`Res`] and [`ResMut`] system parameters. +//! +//! Because resources are *also* components, queries will find the component on the entity which stores the resource +//! by default, and operate on it like any other entity. If this behavior is not desired, filter out +//! entities with the [`IsResource`] component. +//! +//! [`Res`]: crate::system::Res +//! [`ResMut`]: crate::system::ResMut + +use crate as bevy_ecs; +use crate::component::ComponentId; +use crate::entity::Entity; +use crate::prelude::{require, Component}; +use crate::query::With; +use crate::world::DeferredWorld; +use alloc::vec::Vec; +use core::marker::PhantomData; // The derive macro for the `Resource` trait pub use bevy_ecs_macros::Resource; @@ -9,6 +37,11 @@ pub use bevy_ecs_macros::Resource; /// /// Only one resource of each type can be stored in a [`World`] at any given time. /// +/// # Deriving this trait +/// +/// This trait can be derived! The derive macro also implements the [`Component`] trait for the type, +/// and any attributes that are valid for the [`Component`] derive are also applied. +/// /// # Examples /// /// ``` @@ -72,4 +105,90 @@ pub use bevy_ecs_macros::Resource; label = "invalid `Resource`", note = "consider annotating `{Self}` with `#[derive(Resource)]`" )] -pub trait Resource: Send + Sync + 'static {} +pub trait Resource: Component {} + +/// A marker component for the entity that stores the resource of type `T`. +/// +/// This component is automatically inserted when a resource of type `T` is inserted into the world, +/// and can be used to find the entity that stores a particular resource. +/// +/// By contrast, the [`IsResource`] component is used to find all entities that store resources, +/// regardless of the type of resource they store. +/// +/// This component comes with a hook that ensures that at most one entity has this component for any given `R`: +/// adding this component to an entity (or spawning an entity with this component) will despawn any other entity with this component. +#[derive(Component, Debug)] +#[require(IsResource)] +#[component(on_insert = at_most_one_hook::)] +pub struct ResourceEntity(PhantomData); + +impl Default for ResourceEntity { + fn default() -> Self { + ResourceEntity(PhantomData) + } +} + +fn at_most_one_hook( + mut deferred_world: DeferredWorld, + entity: Entity, + _component_id: ComponentId, +) { + let mut query = deferred_world + .try_query_filtered::>>() + // The component is guaranteed to have been added to the world, + // since that's why this hook is running! + .unwrap(); + + let mut offending_entities = Vec::new(); + + for detected_entity in query.iter(&deferred_world) { + if detected_entity != entity { + offending_entities.push(detected_entity); + } + } + + let mut commands = deferred_world.commands(); + for offending_entity in offending_entities { + commands.entity(offending_entity).despawn(); + } +} + +/// A marker component for entities which store resources. +/// +/// By contrast, the [`ResourceEntity`] component is used to find the entity that stores a particular resource. +/// This component is required by the [`ResourceEntity`] component, and will automatically be added. +#[derive(Component, Default, Debug)] +pub struct IsResource; + +#[cfg(test)] +mod tests { + use super::ResourceEntity; + use crate as bevy_ecs; + use crate::prelude::*; + + #[test] + fn resource_with_component_attributes() { + #[derive(Resource, Default)] + struct RA; + + #[derive(Resource)] + #[require(RA)] + struct RB; + } + + #[test] + fn at_most_one_resource_entity_exists() { + #[derive(Resource, Default)] + struct R; + + let mut world = World::default(); + world.init_resource::(); + + let mut resource_query = world.query::<&ResourceEntity>(); + assert_eq!(resource_query.iter(&world).count(), 1); + + world.insert_resource(R); + let mut resource_query = world.query::<&ResourceEntity>(); + assert_eq!(resource_query.iter(&world).count(), 1); + } +} diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index cd565173eaf06..b11e8dd8fa0c1 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -737,7 +737,7 @@ mod tests { #[derive(Event)] struct E; - #[derive(Resource, Component)] + #[derive(Resource)] struct RC; fn empty_system() {} diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index b68e46667f350..7fe1eb5abe020 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -2200,7 +2200,7 @@ mod tests { } } - #[derive(Component, Resource)] + #[derive(Resource)] struct W(T); fn simple_command(world: &mut World) { diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 824b178464960..0ffea0eced0f8 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -358,17 +358,17 @@ mod tests { No, } - #[derive(Component, Resource, Debug, Eq, PartialEq, Default)] + #[derive(Resource, Debug, Eq, PartialEq, Default)] struct A; - #[derive(Component, Resource)] + #[derive(Resource)] struct B; - #[derive(Component, Resource)] + #[derive(Resource)] struct C; - #[derive(Component, Resource)] + #[derive(Resource)] struct D; - #[derive(Component, Resource)] + #[derive(Resource)] struct E; - #[derive(Component, Resource)] + #[derive(Resource)] struct F; #[derive(Component, Debug)] diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 4f22340bff8cc..2f676caf9ba68 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -386,10 +386,9 @@ mod tests { #[test] fn run_system_once() { + #[derive(Resource)] struct T(usize); - impl Resource for T {} - fn system(In(n): In, mut commands: Commands) -> usize { commands.insert_resource(T(n)); n + 1 @@ -446,8 +445,8 @@ mod tests { #[test] fn run_system_once_invalid_params() { + #[derive(Resource)] struct T; - impl Resource for T {} fn system(_: Res) {} let mut world = World::default(); diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index 2a85afd12adad..e7cf25295575c 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -800,8 +800,8 @@ mod tests { fn run_system_invalid_params() { use crate::system::RegisteredSystemError; + #[derive(Resource)] struct T; - impl Resource for T {} fn system(_: Res) {} let mut world = World::new(); diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index d4ebad7efe7a5..1371736fd0807 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -1345,7 +1345,7 @@ impl<'w> EntityWorldMut<'w> { /// use [`get_resource_or_insert_with`](World::get_resource_or_insert_with). #[inline] #[track_caller] - pub fn resource_mut(&mut self) -> Mut<'_, R> { + pub fn resource_mut>(&mut self) -> Mut<'_, R> { self.world.resource_mut::() } @@ -1357,7 +1357,9 @@ impl<'w> EntityWorldMut<'w> { /// Gets a mutable reference to the resource of the given type if it exists #[inline] - pub fn get_resource_mut(&mut self) -> Option> { + pub fn get_resource_mut>( + &mut self, + ) -> Option> { self.world.get_resource_mut() } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index e34e2752c2b25..f7a35199fde8c 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -44,7 +44,7 @@ use crate::{ observer::Observers, query::{DebugCheckedUnwrap, QueryData, QueryFilter, QueryState}, removal_detection::RemovedComponentEvents, - resource::Resource, + resource::{Resource, ResourceEntity}, result::Result, schedule::{Schedule, ScheduleLabel, Schedules}, storage::{ResourceData, Storages}, @@ -1584,29 +1584,18 @@ impl World { /// and those default values will be here instead. #[inline] #[track_caller] + // FIXME: this should be plumbed to the spawn call, + // but no infrastructure exists for that yet. pub fn init_resource(&mut self) -> ComponentId { - #[cfg(feature = "track_location")] - let caller = Location::caller(); let component_id = self.components.register_resource::(); - if self - .storages - .resources - .get(component_id) - .is_none_or(|data| !data.is_present()) - { - let value = R::from_world(self); - OwningPtr::make(value, |ptr| { - // SAFETY: component_id was just initialized and corresponds to resource of type R. - unsafe { - self.insert_resource_by_id( - component_id, - ptr, - #[cfg(feature = "track_location")] - caller, - ); - } - }); - } + + let resource_data = R::from_world(self); + let entity = self + .spawn((resource_data, ResourceEntity::::default())) + .id(); + self.components + .cache_resource_entity::(&mut self.storages, entity); + component_id } @@ -1631,20 +1620,13 @@ impl World { pub(crate) fn insert_resource_with_caller( &mut self, value: R, - #[cfg(feature = "track_location")] caller: &'static Location, + // FIXME: this should be plumbed to the spawn call, + // but no infrastructure exists for that yet. + #[cfg(feature = "track_location")] _caller: &'static Location, ) { - let component_id = self.components.register_resource::(); - OwningPtr::make(value, |ptr| { - // SAFETY: component_id was just initialized and corresponds to resource of type R. - unsafe { - self.insert_resource_by_id( - component_id, - ptr, - #[cfg(feature = "track_location")] - caller, - ); - } - }); + let entity = self.spawn((value, ResourceEntity::::default())).id(); + self.components + .cache_resource_entity::(&mut self.storages, entity); } /// Initializes a new non-send resource and returns the [`ComponentId`] created for it. @@ -1717,10 +1699,14 @@ impl World { /// Removes the resource of a given type and returns it, if it exists. Otherwise returns `None`. #[inline] pub fn remove_resource(&mut self) -> Option { - let component_id = self.components.get_resource_id(TypeId::of::())?; - let (ptr, _, _) = self.storages.resources.get_mut(component_id)?.remove()?; - // SAFETY: `component_id` was gotten via looking up the `R` type - unsafe { Some(ptr.read::()) } + let maybe_resource_entity = self.components.remove_resource_entity::(); + if let Some(resource_entity) = maybe_resource_entity { + let data = self.entity_mut(resource_entity).take::(); + self.despawn(resource_entity); + data + } else { + None + } } /// Removes a `!Send` resource from the world and returns it, if present. @@ -1749,15 +1735,19 @@ impl World { /// Returns `true` if a resource of type `R` exists. Otherwise returns `false`. #[inline] pub fn contains_resource(&self) -> bool { - self.components - .get_resource_id(TypeId::of::()) - .and_then(|component_id| self.storages.resources.get(component_id)) - .is_some_and(ResourceData::is_present) + let maybe_resource_query = self.try_query::<&ResourceEntity>(); + + if let Some(mut query) = maybe_resource_query { + query.iter(self).next().is_some() + } else { + false + } } /// Returns `true` if a resource with provided `component_id` exists. Otherwise returns `false`. #[inline] pub fn contains_resource_by_id(&self, component_id: ComponentId) -> bool { + // FIXME: migrate this method to use resource entities self.storages .resources .get(component_id) @@ -1923,7 +1913,7 @@ impl World { /// use [`get_resource_or_insert_with`](World::get_resource_or_insert_with). #[inline] #[track_caller] - pub fn resource_mut(&mut self) -> Mut<'_, R> { + pub fn resource_mut>(&mut self) -> Mut<'_, R> { match self.get_resource_mut() { Some(x) => x, None => panic!( @@ -1939,28 +1929,41 @@ impl World { /// Gets a reference to the resource of the given type if it exists #[inline] pub fn get_resource(&self) -> Option<&R> { - // SAFETY: - // - `as_unsafe_world_cell_readonly` gives permission to access everything immutably - // - `&self` ensures nothing in world is borrowed mutably - unsafe { self.as_unsafe_world_cell_readonly().get_resource() } + let resource_entity = self.components.get_resource_entity::()?; + self.get::(resource_entity) } /// Gets a reference including change detection to the resource of the given type if it exists. #[inline] pub fn get_resource_ref(&self) -> Option> { - // SAFETY: - // - `as_unsafe_world_cell_readonly` gives permission to access everything immutably - // - `&self` ensures nothing in world is borrowed mutably - unsafe { self.as_unsafe_world_cell_readonly().get_resource_ref() } + let resource_entity = self.components.get_resource_entity::()?; + self.entity(resource_entity).get_ref::() } /// Gets a mutable reference to the resource of the given type if it exists #[inline] - pub fn get_resource_mut(&mut self) -> Option> { - // SAFETY: - // - `as_unsafe_world_cell` gives permission to access everything mutably - // - `&mut self` ensures nothing in world is borrowed - unsafe { self.as_unsafe_world_cell().get_resource_mut() } + pub fn get_resource_mut>( + &mut self, + ) -> Option> { + let resource_entity = self.components.get_resource_entity::()?; + self.get_mut::(resource_entity) + } + + /// Gets mutable access to the resource of type `R`. + /// Returns `None` if the resource of type `R` does not exist. + /// + /// # Safety + /// + /// - `R` must be a mutable component + #[inline] + pub unsafe fn get_resource_mut_assume_mutable(&mut self) -> Option> { + let resource_entity = self.components.get_resource_entity::()?; + let entity_world_mut = self.entity_mut(resource_entity); + // TODO: this conversion shouldn't be needed: `into_mut_assume_mutable` should exist on EntityWorldMut + let entity_mut = EntityMut::from(entity_world_mut); + + // SAFETY: `R` is a mutable component, as asserted by the caller. + unsafe { entity_mut.into_mut_assume_mutable::() } } /// Gets a mutable reference to the resource of type `T` if it exists, @@ -1980,38 +1983,16 @@ impl World { /// ``` #[inline] #[track_caller] - pub fn get_resource_or_insert_with( + pub fn get_resource_or_insert_with>( &mut self, func: impl FnOnce() -> R, ) -> Mut<'_, R> { - #[cfg(feature = "track_location")] - let caller = Location::caller(); - let change_tick = self.change_tick(); - let last_change_tick = self.last_change_tick(); - - let component_id = self.components.register_resource::(); - let data = self.initialize_resource_internal(component_id); - if !data.is_present() { - OwningPtr::make(func(), |ptr| { - // SAFETY: component_id was just initialized and corresponds to resource of type R. - unsafe { - data.insert( - ptr, - change_tick, - #[cfg(feature = "track_location")] - caller, - ); - } - }); + if !self.contains_resource::() { + let res = func(); + self.insert_resource(res); } - // SAFETY: The resource must be present, as we would have inserted it if it was empty. - let data = unsafe { - data.get_mut(last_change_tick, change_tick) - .debug_checked_unwrap() - }; - // SAFETY: The underlying type of the resource is `R`. - unsafe { data.with_type::() } + self.resource_mut::() } /// Gets a mutable reference to the resource of type `T` if it exists, @@ -2047,47 +2028,14 @@ impl World { /// assert_eq!(my_res.0, 30); /// ``` #[track_caller] - pub fn get_resource_or_init(&mut self) -> Mut<'_, R> { - #[cfg(feature = "track_location")] - let caller = Location::caller(); - let change_tick = self.change_tick(); - let last_change_tick = self.last_change_tick(); - - let component_id = self.components.register_resource::(); - if self - .storages - .resources - .get(component_id) - .is_none_or(|data| !data.is_present()) - { - let value = R::from_world(self); - OwningPtr::make(value, |ptr| { - // SAFETY: component_id was just initialized and corresponds to resource of type R. - unsafe { - self.insert_resource_by_id( - component_id, - ptr, - #[cfg(feature = "track_location")] - caller, - ); - } - }); + pub fn get_resource_or_init>( + &mut self, + ) -> Mut<'_, R> { + if !self.contains_resource::() { + self.init_resource::(); } - // SAFETY: The resource was just initialized if it was empty. - let data = unsafe { - self.storages - .resources - .get_mut(component_id) - .debug_checked_unwrap() - }; - // SAFETY: The resource must be present, as we would have inserted it if it was empty. - let data = unsafe { - data.get_mut(last_change_tick, change_tick) - .debug_checked_unwrap() - }; - // SAFETY: The underlying type of the resource is `R`. - unsafe { data.with_type::() } + self.resource_mut::() } /// Gets an immutable reference to the non-send resource of the given type, if it exists. @@ -2770,6 +2718,7 @@ impl World { value: OwningPtr<'_>, #[cfg(feature = "track_location")] caller: &'static Location, ) { + // FIXME: migrate this function let change_tick = self.change_tick(); let resource = self.initialize_resource_internal(component_id); @@ -3729,7 +3678,7 @@ mod tests { Drop(ID), } - #[derive(Resource, Component)] + #[derive(Resource)] struct MayPanicInDrop { drop_log: Arc>>, expected_panic_flag: Arc, diff --git a/crates/bevy_pbr/src/light/ambient_light.rs b/crates/bevy_pbr/src/light/ambient_light.rs index f09bab51f69ed..41cb2a6bb4082 100644 --- a/crates/bevy_pbr/src/light/ambient_light.rs +++ b/crates/bevy_pbr/src/light/ambient_light.rs @@ -17,7 +17,7 @@ use super::*; /// ambient_light.brightness = 100.0; /// } /// ``` -#[derive(Resource, Component, Clone, Debug, ExtractResource, ExtractComponent, Reflect)] +#[derive(Resource, Clone, Debug, ExtractResource, ExtractComponent, Reflect)] #[reflect(Resource, Component, Debug, Default)] #[require(Camera)] pub struct AmbientLight { diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index 842b985a48954..d9b596971d34c 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -698,7 +698,7 @@ mod tests { #[test] fn should_use_from_reflect() { - #[derive(Resource, Component, Reflect)] + #[derive(Resource, Reflect)] #[reflect(Resource, Component)] struct SomeType(i32); diff --git a/examples/games/game_menu.rs b/examples/games/game_menu.rs index ad6bb4604251e..01e75536bf1e1 100644 --- a/examples/games/game_menu.rs +++ b/examples/games/game_menu.rs @@ -16,7 +16,7 @@ enum GameState { } // One of the two settings that can be set through the menu. It will be a resource in the app -#[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)] +#[derive(Resource, Debug, PartialEq, Eq, Clone, Copy)] enum DisplayQuality { Low, Medium, @@ -24,7 +24,7 @@ enum DisplayQuality { } // One of the two settings that can be set through the menu. It will be a resource in the app -#[derive(Resource, Debug, Component, PartialEq, Eq, Clone, Copy)] +#[derive(Resource, Debug, PartialEq, Eq, Clone, Copy)] struct Volume(u32); fn main() {