Skip to content
Draft
2 changes: 1 addition & 1 deletion benches/benches/bevy_ecs/scheduling/run_condition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
14 changes: 12 additions & 2 deletions crates/bevy_ecs/macros/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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 {
Expand Down
58 changes: 58 additions & 0 deletions crates/bevy_ecs/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1116,6 +1116,9 @@ impl Default for ComponentCloneHandlers {
pub struct Components {
components: Vec<ComponentInfo>,
indices: TypeIdMap<ComponentId>,
// Each resource corresponds to a single entity,
// and the resource data is stored as a component on that entity.
resource_entities: HashMap<ComponentId, Entity>,
resource_indices: TypeIdMap<ComponentId>,
component_clone_handlers: ComponentCloneHandlers,
}
Expand Down Expand Up @@ -1212,6 +1215,61 @@ impl Components {
component_id
}

/// Store a new resource entity of type `R`.
#[inline]
pub(crate) fn cache_resource_entity<R: Resource>(
&mut self,
storages: &mut Storages,
entity: Entity,
) {
let id = self.register_component::<R>(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<R: Resource>(&mut self) -> Option<Entity> {
let Some(id) = self.component_id::<R>() 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<Entity> {
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<R: Resource>(&self) -> Option<Entity> {
let id = self.component_id::<R>()?;
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<Entity> {
self.resource_entities.get(&id).copied()
}

/// Returns the number of components registered with this instance.
#[inline]
pub fn len(&self) -> usize {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
20 changes: 15 additions & 5 deletions crates/bevy_ecs/src/reflect/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -197,16 +198,25 @@ impl<R: Resource + FromReflect + TypePath> FromType<R> for ReflectResource {
world.insert_resource(resource);
},
apply: |world, reflected_resource| {
let mut resource = world.resource_mut::<R>();
if !R::Mutability::MUTABLE {
let name = ShortName::of::<R>();
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::<R>().unwrap() };
resource.apply(reflected_resource);
},
apply_or_insert: |world, reflected_resource, registry| {
if let Some(mut resource) = world.get_resource_mut::<R>() {
resource.apply(reflected_resource);
} else {
if !R::Mutability::MUTABLE | !world.contains_resource::<R>() {
let resource =
from_reflect_with_fallback::<R>(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::<R>().unwrap() };
resource.apply(reflected_resource);
}
},
remove: |world| {
Expand Down
121 changes: 120 additions & 1 deletion crates/bevy_ecs/src/resource.rs
Original file line number Diff line number Diff line change
@@ -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<R>`] 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;
Expand All @@ -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
///
/// ```
Expand Down Expand Up @@ -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::<R>)]
pub struct ResourceEntity<R: Resource>(PhantomData<R>);

impl<R: Resource> Default for ResourceEntity<R> {
fn default() -> Self {
ResourceEntity(PhantomData)
}
}

fn at_most_one_hook<R: Resource>(
mut deferred_world: DeferredWorld,
entity: Entity,
_component_id: ComponentId,
) {
let mut query = deferred_world
.try_query_filtered::<Entity, With<ResourceEntity<R>>>()
// 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<R>`] component is used to find the entity that stores a particular resource.
/// This component is required by the [`ResourceEntity<R>`] 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::<R>();

let mut resource_query = world.query::<&ResourceEntity<R>>();
assert_eq!(resource_query.iter(&world).count(), 1);

world.insert_resource(R);
let mut resource_query = world.query::<&ResourceEntity<R>>();
assert_eq!(resource_query.iter(&world).count(), 1);
}
}
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/schedule/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,7 @@ mod tests {
#[derive(Event)]
struct E;

#[derive(Resource, Component)]
#[derive(Resource)]
struct RC;

fn empty_system() {}
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/system/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2200,7 +2200,7 @@ mod tests {
}
}

#[derive(Component, Resource)]
#[derive(Resource)]
struct W<T>(T);

fn simple_command(world: &mut World) {
Expand Down
12 changes: 6 additions & 6 deletions crates/bevy_ecs/src/system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
5 changes: 2 additions & 3 deletions crates/bevy_ecs/src/system/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize>, mut commands: Commands) -> usize {
commands.insert_resource(T(n));
n + 1
Expand Down Expand Up @@ -446,8 +445,8 @@ mod tests {

#[test]
fn run_system_once_invalid_params() {
#[derive(Resource)]
struct T;
impl Resource for T {}
fn system(_: Res<T>) {}

let mut world = World::default();
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/system/system_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>) {}

let mut world = World::new();
Expand Down
6 changes: 4 additions & 2 deletions crates/bevy_ecs/src/world/entity_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<R: Resource>(&mut self) -> Mut<'_, R> {
pub fn resource_mut<R: Resource + Component<Mutability = Mutable>>(&mut self) -> Mut<'_, R> {
self.world.resource_mut::<R>()
}

Expand All @@ -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<R: Resource>(&mut self) -> Option<Mut<'_, R>> {
pub fn get_resource_mut<R: Resource + Component<Mutability = Mutable>>(
&mut self,
) -> Option<Mut<'_, R>> {
self.world.get_resource_mut()
}

Expand Down
Loading
Loading