Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/resource.rs
9368 views
1
//! Resources are unique, singleton-like data types that can be accessed from systems and stored in the [`World`](crate::world::World).
2
3
use core::ops::{Deref, DerefMut};
4
use log::warn;
5
6
use crate::{
7
component::{Component, ComponentId, Mutable},
8
entity::Entity,
9
lifecycle::HookContext,
10
storage::SparseSet,
11
world::DeferredWorld,
12
};
13
#[cfg(feature = "bevy_reflect")]
14
use {crate::reflect::ReflectComponent, bevy_reflect::Reflect};
15
// The derive macro for the `Resource` trait
16
pub use bevy_ecs_macros::Resource;
17
use bevy_platform::cell::SyncUnsafeCell;
18
19
/// A type that can be inserted into a [`World`] as a singleton.
20
///
21
/// You can access resource data in systems using the [`Res`] and [`ResMut`] system parameters
22
///
23
/// Only one resource of each type can be stored in a [`World`] at any given time.
24
///
25
/// # Examples
26
///
27
/// ```
28
/// # let mut world = World::default();
29
/// # let mut schedule = Schedule::default();
30
/// # use bevy_ecs::prelude::*;
31
/// #[derive(Resource)]
32
/// struct MyResource { value: u32 }
33
///
34
/// world.insert_resource(MyResource { value: 42 });
35
///
36
/// fn read_resource_system(resource: Res<MyResource>) {
37
/// assert_eq!(resource.value, 42);
38
/// }
39
///
40
/// fn write_resource_system(mut resource: ResMut<MyResource>) {
41
/// assert_eq!(resource.value, 42);
42
/// resource.value = 0;
43
/// assert_eq!(resource.value, 0);
44
/// }
45
/// # schedule.add_systems((read_resource_system, write_resource_system).chain());
46
/// # schedule.run(&mut world);
47
/// ```
48
///
49
/// # `!Sync` Resources
50
/// A `!Sync` type cannot implement `Resource`. However, it is possible to wrap a `Send` but not `Sync`
51
/// type in [`SyncCell`] or the currently unstable [`Exclusive`] to make it `Sync`. This forces only
52
/// having mutable access (`&mut T` only, never `&T`), but makes it safe to reference across multiple
53
/// threads.
54
///
55
/// This will fail to compile since `RefCell` is `!Sync`.
56
/// ```compile_fail
57
/// # use std::cell::RefCell;
58
/// # use bevy_ecs::resource::Resource;
59
///
60
/// #[derive(Resource)]
61
/// struct NotSync {
62
/// counter: RefCell<usize>,
63
/// }
64
/// ```
65
///
66
/// This will compile since the `RefCell` is wrapped with `SyncCell`.
67
/// ```
68
/// # use std::cell::RefCell;
69
/// # use bevy_ecs::resource::Resource;
70
/// use bevy_platform::cell::SyncCell;
71
///
72
/// #[derive(Resource)]
73
/// struct ActuallySync {
74
/// counter: SyncCell<RefCell<usize>>,
75
/// }
76
/// ```
77
///
78
/// [`Exclusive`]: https://doc.rust-lang.org/nightly/std/sync/struct.Exclusive.html
79
/// [`World`]: crate::world::World
80
/// [`Res`]: crate::system::Res
81
/// [`ResMut`]: crate::system::ResMut
82
/// [`SyncCell`]: bevy_platform::cell::SyncCell
83
#[diagnostic::on_unimplemented(
84
message = "`{Self}` is not a `Resource`",
85
label = "invalid `Resource`",
86
note = "consider annotating `{Self}` with `#[derive(Resource)]`"
87
)]
88
pub trait Resource: Component<Mutability = Mutable> {}
89
90
/// A cache that links each `ComponentId` from a resource to the corresponding entity.
91
#[derive(Default)]
92
pub struct ResourceEntities(SyncUnsafeCell<SparseSet<ComponentId, Entity>>);
93
94
impl Deref for ResourceEntities {
95
type Target = SparseSet<ComponentId, Entity>;
96
97
fn deref(&self) -> &Self::Target {
98
// SAFETY: There are no other mutable references to the map.
99
// The underlying `SyncUnsafeCell` is never exposed outside this module,
100
// so mutable references are only created by the resource hooks.
101
// We only expose `&ResourceCache` to code with access to a resource (such as `&World`),
102
// and that would conflict with the `DeferredWorld` passed to the resource hook.
103
unsafe { &*self.0.get() }
104
}
105
}
106
107
impl DerefMut for ResourceEntities {
108
fn deref_mut(&mut self) -> &mut Self::Target {
109
self.0.get_mut()
110
}
111
}
112
113
/// A marker component for entities that have a Resource component.
114
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Debug))]
115
#[derive(Component, Debug)]
116
#[component(on_insert, on_replace, on_despawn)]
117
pub struct IsResource(ComponentId);
118
119
impl IsResource {
120
/// Creates a new instance with the given `component_id`
121
pub fn new(component_id: ComponentId) -> Self {
122
Self(component_id)
123
}
124
125
/// The [`ComponentId`] of the resource component (the _actual_ resource value component, not the [`IsResource`] component).
126
pub fn resource_component_id(&self) -> ComponentId {
127
self.0
128
}
129
130
pub(crate) fn on_insert(mut world: DeferredWorld, context: HookContext) {
131
let resource_component_id = world
132
.entity(context.entity)
133
.get::<Self>()
134
.unwrap()
135
.resource_component_id();
136
137
if let Some(&original_entity) = world.resource_entities.get(resource_component_id) {
138
if !world.entities().contains(original_entity) {
139
let name = world
140
.components()
141
.get_name(resource_component_id)
142
.expect("resource is registered");
143
panic!(
144
"Resource entity {} of {} has been despawned, when it's not supposed to be.",
145
original_entity, name
146
);
147
}
148
149
if original_entity != context.entity {
150
// the resource already exists and the new one should be removed
151
world
152
.commands()
153
.entity(context.entity)
154
.remove_by_id(resource_component_id);
155
world
156
.commands()
157
.entity(context.entity)
158
.remove_by_id(context.component_id);
159
let name = world
160
.components()
161
.get_name(resource_component_id)
162
.expect("resource is registered");
163
warn!("Tried inserting the resource {} while one already exists.
164
Resources are unique components stored on a single entity.
165
Inserting on a different entity, when one already exists, causes the new value to be removed.", name);
166
}
167
} else {
168
// SAFETY: We have exclusive world access (as long as we don't make structural changes).
169
let cache = unsafe { world.as_unsafe_world_cell().resource_entities() };
170
// SAFETY: There are no shared references to the map.
171
// We only expose `&ResourceCache` to code with access to a resource (such as `&World`),
172
// and that would conflict with the `DeferredWorld` passed to the resource hook.
173
unsafe { &mut *cache.0.get() }.insert(resource_component_id, context.entity);
174
}
175
}
176
177
pub(crate) fn on_replace(mut world: DeferredWorld, context: HookContext) {
178
let resource_component_id = world
179
.entity(context.entity)
180
.get::<Self>()
181
.unwrap()
182
.resource_component_id();
183
184
if let Some(resource_entity) = world.resource_entities.get(resource_component_id)
185
&& *resource_entity == context.entity
186
{
187
// SAFETY: We have exclusive world access (as long as we don't make structural changes).
188
let cache = unsafe { world.as_unsafe_world_cell().resource_entities() };
189
// SAFETY: There are no shared references to the map.
190
// We only expose `&ResourceCache` to code with access to a resource (such as `&World`),
191
// and that would conflict with the `DeferredWorld` passed to the resource hook.
192
unsafe { &mut *cache.0.get() }.remove(resource_component_id);
193
194
world
195
.commands()
196
.entity(context.entity)
197
.remove_by_id(resource_component_id);
198
}
199
}
200
201
pub(crate) fn on_despawn(_world: DeferredWorld, _context: HookContext) {
202
warn!("Resource entities are not supposed to be despawned.");
203
}
204
}
205
206
/// [`ComponentId`] of the [`IsResource`] component.
207
pub const IS_RESOURCE: ComponentId = ComponentId::new(crate::component::IS_RESOURCE);
208
209
#[cfg(test)]
210
mod tests {
211
use crate::{
212
change_detection::MaybeLocation,
213
entity::Entity,
214
ptr::OwningPtr,
215
resource::{IsResource, Resource},
216
world::World,
217
};
218
use alloc::vec::Vec;
219
use bevy_platform::prelude::String;
220
221
#[test]
222
fn unique_resource_entities() {
223
#[derive(Default, Resource)]
224
struct TestResource1;
225
226
#[derive(Resource)]
227
#[expect(dead_code, reason = "field needed for testing")]
228
struct TestResource2(String);
229
230
#[derive(Resource)]
231
#[expect(dead_code, reason = "field needed for testing")]
232
struct TestResource3(u8);
233
234
let mut world = World::new();
235
let start = world.entities().count_spawned();
236
world.init_resource::<TestResource1>();
237
assert_eq!(world.entities().count_spawned(), start + 1);
238
world.insert_resource(TestResource2(String::from("Foo")));
239
assert_eq!(world.entities().count_spawned(), start + 2);
240
// like component registration, which just makes it known to the world that a component exists,
241
// registering a resource should not spawn an entity.
242
let id = world.register_resource::<TestResource3>();
243
assert_eq!(world.entities().count_spawned(), start + 2);
244
OwningPtr::make(20_u8, |ptr| {
245
// SAFETY: id was just initialized and corresponds to a resource.
246
unsafe {
247
world.insert_resource_by_id(id, ptr, MaybeLocation::caller());
248
}
249
});
250
assert_eq!(world.entities().count_spawned(), start + 3);
251
assert!(world.remove_resource_by_id(id));
252
// the entity is stable: removing the resource should only remove the component from the entity, not despawn the entity
253
assert_eq!(world.entities().count_spawned(), start + 3);
254
// again, the entity is stable: see previous explanation
255
world.remove_resource::<TestResource1>();
256
assert_eq!(world.entities().count_spawned(), start + 3);
257
// make sure that trying to add a resource twice results, doesn't change the entity count
258
world.insert_resource(TestResource2(String::from("Bar")));
259
assert_eq!(world.entities().count_spawned(), start + 3);
260
}
261
262
#[test]
263
fn is_resource_presence() {
264
#[derive(Default, Resource)]
265
struct TestResource;
266
267
let mut world = World::new();
268
let id = world.init_resource::<TestResource>();
269
270
assert!(world.get_resource::<TestResource>().is_some());
271
272
let mut query = world.query::<(Entity, &TestResource, &IsResource)>();
273
let first_entity = {
274
let resources = query.iter(&world).collect::<Vec<_>>();
275
assert_eq!(resources.len(), 1);
276
let (entity, _test_resource, is_resource) = resources[0];
277
assert_eq!(is_resource.resource_component_id(), id);
278
entity
279
};
280
281
// Removing IsResource should invalidate the current TestResource entity
282
// This uses commands because IsResource's despawn-on-removal invalidates the EntityWorldMut and panics
283
world.entity_mut(first_entity).remove::<IsResource>();
284
assert!(world.get_resource::<TestResource>().is_none());
285
286
assert!(
287
!world.entity(first_entity).contains::<TestResource>(),
288
"Removing IsResource should also remove the Resource component it corresponds to"
289
);
290
291
world.init_resource::<TestResource>();
292
let second_entity = {
293
let resources = query.iter(&world).collect::<Vec<_>>();
294
assert_eq!(resources.len(), 1);
295
let (entity, _test_resource, is_resource) = resources[0];
296
assert_eq!(is_resource.resource_component_id(), id);
297
entity
298
};
299
300
assert_ne!(
301
first_entity, second_entity,
302
"The first resource entity was invalidated, so the second initialization should be new"
303
);
304
305
let id = world.spawn(TestResource).id();
306
// This spawned resource conflicts with the canonical resource, so it was cleaned up.
307
assert!(world.entity(id).get::<TestResource>().is_none());
308
assert!(world.entity(id).get::<IsResource>().is_none());
309
assert!(world.entity(second_entity).get::<TestResource>().is_some());
310
assert!(world.entity(second_entity).get::<IsResource>().is_some());
311
}
312
}
313
314