Skip to main content

bevy_ecs/
resource.rs

1//! Resources are unique, singleton-like data types that can be accessed from systems and stored in the [`World`](crate::world::World).
2
3use log::warn;
4
5use crate::{
6    component::{Component, ComponentId},
7    entity::Entity,
8    lifecycle::HookContext,
9    storage::SparseArray,
10    world::DeferredWorld,
11};
12#[cfg(feature = "bevy_reflect")]
13use {crate::reflect::ReflectComponent, bevy_reflect::Reflect};
14// The derive macro for the `Resource` trait
15pub use bevy_ecs_macros::Resource;
16use bevy_platform::cell::SyncUnsafeCell;
17
18/// A type that can be inserted into a [`World`] as a singleton.
19///
20/// You can access resource data in systems using the [`Res`] and [`ResMut`] system parameters
21///
22/// Only one resource of each type can be stored in a [`World`] at any given time.
23///
24/// # Examples
25///
26/// ```
27/// # let mut world = World::default();
28/// # let mut schedule = Schedule::default();
29/// # use bevy_ecs::prelude::*;
30/// #[derive(Resource)]
31/// struct MyResource { value: u32 }
32///
33/// world.insert_resource(MyResource { value: 42 });
34///
35/// fn read_resource_system(resource: Res<MyResource>) {
36///     assert_eq!(resource.value, 42);
37/// }
38///
39/// fn write_resource_system(mut resource: ResMut<MyResource>) {
40///     assert_eq!(resource.value, 42);
41///     resource.value = 0;
42///     assert_eq!(resource.value, 0);
43/// }
44/// # schedule.add_systems((read_resource_system, write_resource_system).chain());
45/// # schedule.run(&mut world);
46/// ```
47///
48/// # `!Sync` Resources
49/// A `!Sync` type cannot implement `Resource`. However, it is possible to wrap a `Send` but not `Sync`
50/// type in [`SyncCell`] or the currently unstable [`Exclusive`] to make it `Sync`. This forces only
51/// having mutable access (`&mut T` only, never `&T`), but makes it safe to reference across multiple
52/// threads.
53///
54/// This will fail to compile since `RefCell` is `!Sync`.
55/// ```compile_fail
56/// # use std::cell::RefCell;
57/// # use bevy_ecs::resource::Resource;
58///
59/// #[derive(Resource)]
60/// struct NotSync {
61///    counter: RefCell<usize>,
62/// }
63/// ```
64///
65/// This will compile since the `RefCell` is wrapped with `SyncCell`.
66/// ```
67/// # use std::cell::RefCell;
68/// # use bevy_ecs::resource::Resource;
69/// use bevy_platform::cell::SyncCell;
70///
71/// #[derive(Resource)]
72/// struct ActuallySync {
73///    counter: SyncCell<RefCell<usize>>,
74/// }
75/// ```
76///
77/// [`Exclusive`]: https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html
78/// [`World`]: crate::world::World
79/// [`Res`]: crate::system::Res
80/// [`ResMut`]: crate::system::ResMut
81/// [`SyncCell`]: bevy_platform::cell::SyncCell
82#[diagnostic::on_unimplemented(
83    message = "`{Self}` is not a `Resource`",
84    label = "invalid `Resource`",
85    note = "consider annotating `{Self}` with `#[derive(Resource)]`"
86)]
87pub trait Resource: Component {}
88
89/// A cache that links each `ComponentId` from a resource to the corresponding entity.
90#[derive(Default)]
91pub struct ResourceEntities(SyncUnsafeCell<SparseArray<ComponentId, Entity>>);
92
93impl ResourceEntities {
94    /// Returns an iterator over all registered resource components and their corresponding entity.
95    ///
96    /// This must scan the entire array of components to find non-empty values,
97    /// which may be slow even if there are few resources.
98    #[inline]
99    pub fn iter(&self) -> impl Iterator<Item = (ComponentId, Entity)> {
100        self.deref().iter().map(|(id, entity)| (id, *entity))
101    }
102
103    /// Returns the entity for the given resource component, or `None` if there is no entity.
104    #[inline]
105    pub fn get(&self, id: ComponentId) -> Option<Entity> {
106        self.deref().get(id).copied()
107    }
108
109    #[inline]
110    fn deref(&self) -> &SparseArray<ComponentId, Entity> {
111        // SAFETY: There are no other mutable references to the map.
112        // The underlying `SyncUnsafeCell` is never exposed outside this module,
113        // so mutable references are only created by the resource hooks.
114        // We only expose `&ResourceCache` to code with access to a resource (such as `&World`),
115        // and that would conflict with the `DeferredWorld` passed to the resource hook.
116        unsafe { &*self.0.get() }
117    }
118}
119
120/// A marker component for entities that have a Resource component.
121#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Debug))]
122#[derive(Component, Debug)]
123#[component(on_insert, on_discard, on_despawn)]
124pub struct IsResource(ComponentId);
125
126impl IsResource {
127    /// Creates a new instance with the given `component_id`
128    pub fn new(component_id: ComponentId) -> Self {
129        Self(component_id)
130    }
131
132    /// The [`ComponentId`] of the resource component (the _actual_ resource value component, not the [`IsResource`] component).
133    pub fn resource_component_id(&self) -> ComponentId {
134        self.0
135    }
136
137    pub(crate) fn on_insert(mut world: DeferredWorld, context: HookContext) {
138        let resource_component_id = world
139            .entity(context.entity)
140            .get::<Self>()
141            .unwrap()
142            .resource_component_id();
143
144        if let Some(original_entity) = world.resource_entities.get(resource_component_id) {
145            if !world.entities().contains(original_entity) {
146                let name = world
147                    .components()
148                    .get_name(resource_component_id)
149                    .expect("resource is registered");
150                panic!(
151                    "Resource entity {} of {} has been despawned, when it's not supposed to be.",
152                    original_entity, name
153                );
154            }
155
156            if original_entity != context.entity {
157                // the resource already exists and the new one should be removed
158                world
159                    .commands()
160                    .entity(context.entity)
161                    .remove_by_id(resource_component_id);
162                world
163                    .commands()
164                    .entity(context.entity)
165                    .remove_by_id(context.component_id);
166                let name = world
167                    .components()
168                    .get_name(resource_component_id)
169                    .expect("resource is registered");
170                warn!("Tried inserting the resource {} while one already exists. \
171                Resources are unique components stored on a single entity. \
172                Inserting on a different entity, when one already exists, causes the new value to be removed.", name);
173            }
174        } else {
175            // SAFETY: We have exclusive world access (as long as we don't make structural changes).
176            let cache = unsafe { world.as_unsafe_world_cell().resource_entities() };
177            // SAFETY: There are no shared references to the map.
178            // We only expose `&ResourceCache` to code with access to a resource (such as `&World`),
179            // and that would conflict with the `DeferredWorld` passed to the resource hook.
180            unsafe { &mut *cache.0.get() }.insert(resource_component_id, context.entity);
181        }
182    }
183
184    pub(crate) fn on_discard(mut world: DeferredWorld, context: HookContext) {
185        let resource_component_id = world
186            .entity(context.entity)
187            .get::<Self>()
188            .unwrap()
189            .resource_component_id();
190
191        if let Some(resource_entity) = world.resource_entities.get(resource_component_id)
192            && resource_entity == context.entity
193        {
194            // SAFETY: We have exclusive world access (as long as we don't make structural changes).
195            let cache = unsafe { world.as_unsafe_world_cell().resource_entities() };
196            // SAFETY: There are no shared references to the map.
197            // We only expose `&ResourceCache` to code with access to a resource (such as `&World`),
198            // and that would conflict with the `DeferredWorld` passed to the resource hook.
199            unsafe { &mut *cache.0.get() }.remove(resource_component_id);
200
201            world
202                .commands()
203                .entity(context.entity)
204                .remove_by_id(resource_component_id);
205        }
206    }
207
208    pub(crate) fn on_despawn(_world: DeferredWorld, _context: HookContext) {
209        warn!("Resource entities are not supposed to be despawned.");
210    }
211}
212
213pub use crate::component::IS_RESOURCE;
214
215#[cfg(test)]
216mod tests {
217    use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
218
219    use crate::{
220        change_detection::MaybeLocation,
221        component::Components,
222        entity::Entity,
223        lifecycle::HookContext,
224        prelude::{EntityRef, Query, SystemParamBuilder},
225        ptr::OwningPtr,
226        resource::{IsResource, Resource, ResourceEntities},
227        system::{ParamBuilder, QueryParamBuilder, RunSystemOnce},
228        world::{DeferredWorld, FilteredEntityRef, World},
229    };
230    use alloc::vec::Vec;
231    use bevy_ecs_macros::Component;
232    use bevy_platform::prelude::String;
233
234    #[test]
235    fn unique_resource_entities() {
236        #[derive(Default, Resource)]
237        struct TestResource1;
238
239        #[derive(Resource)]
240        #[expect(dead_code, reason = "field needed for testing")]
241        struct TestResource2(String);
242
243        #[derive(Resource)]
244        #[expect(dead_code, reason = "field needed for testing")]
245        struct TestResource3(u8);
246
247        let mut world = World::new();
248        let start = world.entities().count_spawned();
249        let id1 = world.init_resource::<TestResource1>();
250        assert_eq!(world.entities().count_spawned(), start + 1);
251        world.insert_resource(TestResource2(String::from("Foo")));
252        assert_eq!(world.entities().count_spawned(), start + 2);
253        // like component registration, which just makes it known to the world that a component exists,
254        // registering a resource should not spawn an entity.
255        let id3 = world.register_component::<TestResource3>();
256        assert_eq!(world.entities().count_spawned(), start + 2);
257        OwningPtr::make(20_u8, |ptr| {
258            // SAFETY: id was just initialized and corresponds to a resource.
259            unsafe {
260                world.insert_resource_by_id(id3, ptr, MaybeLocation::caller());
261            }
262        });
263        assert_eq!(world.entities().count_spawned(), start + 3);
264        let e3 = world.resource_entities().get(id3).unwrap();
265        assert!(world.remove_resource_by_id(id3));
266        // the entity is stable: removing the resource should only remove the component from the entity, not despawn the entity
267        assert_eq!(world.entities().count_spawned(), start + 3);
268        OwningPtr::make(20_u8, |ptr| {
269            // SAFETY: id was just initialized and corresponds to a resource.
270            unsafe {
271                world.insert_resource_by_id(id3, ptr, MaybeLocation::caller());
272            }
273        });
274        assert_eq!(e3, world.resource_entities().get(id3).unwrap());
275        // again, the entity is stable: see previous explanation
276        let e1 = world.resource_entities().get(id1).unwrap();
277        world.remove_resource::<TestResource1>();
278        assert_eq!(world.entities().count_spawned(), start + 3);
279        world.init_resource::<TestResource1>();
280        assert_eq!(e1, world.resource_entities().get(id1).unwrap());
281        // make sure that trying to add a resource twice results, doesn't change the entity count
282        world.insert_resource(TestResource2(String::from("Bar")));
283        assert_eq!(world.entities().count_spawned(), start + 3);
284    }
285
286    #[test]
287    fn is_resource_presence() {
288        #[derive(Default, Resource)]
289        struct TestResource;
290
291        let mut world = World::new();
292        let id = world.init_resource::<TestResource>();
293
294        assert!(world.get_resource::<TestResource>().is_some());
295
296        let mut query = world.query::<(Entity, &TestResource, &IsResource)>();
297        let first_entity = {
298            let resources = query.iter(&world).collect::<Vec<_>>();
299            assert_eq!(resources.len(), 1);
300            let (entity, _test_resource, is_resource) = resources[0];
301            assert_eq!(is_resource.resource_component_id(), id);
302            entity
303        };
304
305        // Removing IsResource should invalidate the current TestResource entity
306        // This uses commands because IsResource's despawn-on-removal invalidates the EntityWorldMut and panics
307        world.entity_mut(first_entity).remove::<IsResource>();
308        assert!(world.get_resource::<TestResource>().is_none());
309
310        assert!(
311            !world.entity(first_entity).contains::<TestResource>(),
312            "Removing IsResource should also remove the Resource component it corresponds to"
313        );
314
315        world.init_resource::<TestResource>();
316        let second_entity = {
317            let resources = query.iter(&world).collect::<Vec<_>>();
318            assert_eq!(resources.len(), 1);
319            let (entity, _test_resource, is_resource) = resources[0];
320            assert_eq!(is_resource.resource_component_id(), id);
321            entity
322        };
323
324        assert_ne!(
325            first_entity, second_entity,
326            "The first resource entity was invalidated, so the second initialization should be new"
327        );
328
329        let id = world.spawn(TestResource).id();
330        // This spawned resource conflicts with the canonical resource, so it was cleaned up.
331        assert!(world.entity(id).get::<TestResource>().is_none());
332        assert!(world.entity(id).get::<IsResource>().is_none());
333        assert!(world.entity(second_entity).get::<TestResource>().is_some());
334        assert!(world.entity(second_entity).get::<IsResource>().is_some());
335    }
336
337    #[test]
338    fn derive_resource_component_features() {
339        static ON_ADD_CALLED: AtomicBool = AtomicBool::new(false);
340
341        #[derive(Resource)]
342        #[component(immutable, on_add)]
343        struct TestResource;
344        impl TestResource {
345            fn on_add(_: DeferredWorld, _: HookContext) {
346                ON_ADD_CALLED.store(true, Relaxed);
347            }
348        }
349
350        let mut world = World::new();
351        world.insert_resource(TestResource);
352
353        assert!(ON_ADD_CALLED.load(Relaxed));
354        assert!(world.get_resource::<TestResource>().is_some());
355    }
356
357    #[test]
358    fn derive_resource_require_features() {
359        #[derive(Component, Default)]
360        struct RequiredComponent;
361
362        #[derive(Resource)]
363        #[require(RequiredComponent)]
364        struct TestResource;
365
366        let mut world = World::new();
367        world.insert_resource(TestResource);
368
369        assert_eq!(
370            world
371                .query::<(&TestResource, &RequiredComponent)>()
372                .iter(&world)
373                .count(),
374            1
375        );
376    }
377
378    #[test]
379    fn get_multiple_dynamic_resources() {
380        use std::any::TypeId;
381
382        #[derive(Resource)]
383        struct ResA(u8);
384
385        #[derive(Resource)]
386        struct ResB(u8);
387
388        let mut world = World::default();
389        world.insert_resource(ResA(12));
390        world.insert_resource(ResB(34));
391
392        let system = (
393            QueryParamBuilder::new(|builder| {
394                builder.data::<EntityRef>();
395                builder.with::<IsResource>();
396                builder.or(|builder| {
397                    builder.with::<ResA>();
398                    builder.with::<ResB>();
399                });
400            }),
401            ParamBuilder,
402            ParamBuilder,
403        )
404            .build_state(&mut world)
405            .build_system(resource_system);
406
407        fn resource_system(
408            query: Query<FilteredEntityRef>,
409            resource_entities: &ResourceEntities,
410            components: &Components,
411        ) {
412            let component_id_a = components.get_id(TypeId::of::<ResA>()).unwrap();
413            let component_id_b = components.get_id(TypeId::of::<ResB>()).unwrap();
414
415            let entity_a = resource_entities.get(component_id_a).unwrap();
416            let entity_b = resource_entities.get(component_id_b).unwrap();
417
418            let entity_ref_a: FilteredEntityRef = query.get(entity_a).unwrap();
419            assert_eq!(entity_ref_a.get::<ResA>().unwrap().0, 12);
420
421            let entity_ref_b: FilteredEntityRef = query.get(entity_b).unwrap();
422            assert_eq!(entity_ref_b.get::<ResB>().unwrap().0, 34);
423        }
424
425        let _ = world.run_system_once(system);
426    }
427}