1use 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};
14pub use bevy_ecs_macros::Resource;
16use bevy_platform::cell::SyncUnsafeCell;
17
18#[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#[derive(Default)]
91pub struct ResourceEntities(SyncUnsafeCell<SparseArray<ComponentId, Entity>>);
92
93impl ResourceEntities {
94 #[inline]
99 pub fn iter(&self) -> impl Iterator<Item = (ComponentId, Entity)> {
100 self.deref().iter().map(|(id, entity)| (id, *entity))
101 }
102
103 #[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 unsafe { &*self.0.get() }
117 }
118}
119
120#[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 pub fn new(component_id: ComponentId) -> Self {
129 Self(component_id)
130 }
131
132 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 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 let cache = unsafe { world.as_unsafe_world_cell().resource_entities() };
177 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 let cache = unsafe { world.as_unsafe_world_cell().resource_entities() };
196 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 const IS_RESOURCE: ComponentId = ComponentId::new(crate::component::IS_RESOURCE);
215
216#[cfg(test)]
217mod tests {
218 use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
219
220 use crate::{
221 change_detection::MaybeLocation,
222 entity::Entity,
223 lifecycle::HookContext,
224 ptr::OwningPtr,
225 resource::{IsResource, Resource},
226 world::{DeferredWorld, World},
227 };
228 use alloc::vec::Vec;
229 use bevy_ecs_macros::Component;
230 use bevy_platform::prelude::String;
231
232 #[test]
233 fn unique_resource_entities() {
234 #[derive(Default, Resource)]
235 struct TestResource1;
236
237 #[derive(Resource)]
238 #[expect(dead_code, reason = "field needed for testing")]
239 struct TestResource2(String);
240
241 #[derive(Resource)]
242 #[expect(dead_code, reason = "field needed for testing")]
243 struct TestResource3(u8);
244
245 let mut world = World::new();
246 let start = world.entities().count_spawned();
247 let id1 = world.init_resource::<TestResource1>();
248 assert_eq!(world.entities().count_spawned(), start + 1);
249 world.insert_resource(TestResource2(String::from("Foo")));
250 assert_eq!(world.entities().count_spawned(), start + 2);
251 let id3 = world.register_component::<TestResource3>();
254 assert_eq!(world.entities().count_spawned(), start + 2);
255 OwningPtr::make(20_u8, |ptr| {
256 unsafe {
258 world.insert_resource_by_id(id3, ptr, MaybeLocation::caller());
259 }
260 });
261 assert_eq!(world.entities().count_spawned(), start + 3);
262 let e3 = world.resource_entities().get(id3).unwrap();
263 assert!(world.remove_resource_by_id(id3));
264 assert_eq!(world.entities().count_spawned(), start + 3);
266 OwningPtr::make(20_u8, |ptr| {
267 unsafe {
269 world.insert_resource_by_id(id3, ptr, MaybeLocation::caller());
270 }
271 });
272 assert_eq!(e3, world.resource_entities().get(id3).unwrap());
273 let e1 = world.resource_entities().get(id1).unwrap();
275 world.remove_resource::<TestResource1>();
276 assert_eq!(world.entities().count_spawned(), start + 3);
277 world.init_resource::<TestResource1>();
278 assert_eq!(e1, world.resource_entities().get(id1).unwrap());
279 world.insert_resource(TestResource2(String::from("Bar")));
281 assert_eq!(world.entities().count_spawned(), start + 3);
282 }
283
284 #[test]
285 fn is_resource_presence() {
286 #[derive(Default, Resource)]
287 struct TestResource;
288
289 let mut world = World::new();
290 let id = world.init_resource::<TestResource>();
291
292 assert!(world.get_resource::<TestResource>().is_some());
293
294 let mut query = world.query::<(Entity, &TestResource, &IsResource)>();
295 let first_entity = {
296 let resources = query.iter(&world).collect::<Vec<_>>();
297 assert_eq!(resources.len(), 1);
298 let (entity, _test_resource, is_resource) = resources[0];
299 assert_eq!(is_resource.resource_component_id(), id);
300 entity
301 };
302
303 world.entity_mut(first_entity).remove::<IsResource>();
306 assert!(world.get_resource::<TestResource>().is_none());
307
308 assert!(
309 !world.entity(first_entity).contains::<TestResource>(),
310 "Removing IsResource should also remove the Resource component it corresponds to"
311 );
312
313 world.init_resource::<TestResource>();
314 let second_entity = {
315 let resources = query.iter(&world).collect::<Vec<_>>();
316 assert_eq!(resources.len(), 1);
317 let (entity, _test_resource, is_resource) = resources[0];
318 assert_eq!(is_resource.resource_component_id(), id);
319 entity
320 };
321
322 assert_ne!(
323 first_entity, second_entity,
324 "The first resource entity was invalidated, so the second initialization should be new"
325 );
326
327 let id = world.spawn(TestResource).id();
328 assert!(world.entity(id).get::<TestResource>().is_none());
330 assert!(world.entity(id).get::<IsResource>().is_none());
331 assert!(world.entity(second_entity).get::<TestResource>().is_some());
332 assert!(world.entity(second_entity).get::<IsResource>().is_some());
333 }
334
335 #[test]
336 fn derive_resource_component_features() {
337 static ON_ADD_CALLED: AtomicBool = AtomicBool::new(false);
338
339 #[derive(Resource)]
340 #[component(immutable, on_add)]
341 struct TestResource;
342 impl TestResource {
343 fn on_add(_: DeferredWorld, _: HookContext) {
344 ON_ADD_CALLED.store(true, Relaxed);
345 }
346 }
347
348 let mut world = World::new();
349 world.insert_resource(TestResource);
350
351 assert!(ON_ADD_CALLED.load(Relaxed));
352 assert!(world.get_resource::<TestResource>().is_some());
353 }
354
355 #[test]
356 fn derive_resource_require_features() {
357 #[derive(Component, Default)]
358 struct RequiredComponent;
359
360 #[derive(Resource)]
361 #[require(RequiredComponent)]
362 struct TestResource;
363
364 let mut world = World::new();
365 world.insert_resource(TestResource);
366
367 assert_eq!(
368 world
369 .query::<(&TestResource, &RequiredComponent)>()
370 .iter(&world)
371 .count(),
372 1
373 );
374 }
375}