Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/world/identifier.rs
6604 views
1
use crate::{
2
component::Tick,
3
query::FilteredAccessSet,
4
storage::SparseSetIndex,
5
system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam},
6
world::{FromWorld, World},
7
};
8
use bevy_platform::sync::atomic::{AtomicUsize, Ordering};
9
10
use super::unsafe_world_cell::UnsafeWorldCell;
11
12
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
13
// We use usize here because that is the largest `Atomic` we want to require
14
/// A unique identifier for a [`World`].
15
///
16
/// The trait [`FromWorld`] is implemented for this type, which returns the
17
/// ID of the world passed to [`FromWorld::from_world`].
18
// Note that this *is* used by external crates as well as for internal safety checks
19
pub struct WorldId(usize);
20
21
/// The next [`WorldId`].
22
static MAX_WORLD_ID: AtomicUsize = AtomicUsize::new(0);
23
24
impl WorldId {
25
/// Create a new, unique [`WorldId`]. Returns [`None`] if the supply of unique
26
/// [`WorldId`]s has been exhausted
27
///
28
/// Please note that the [`WorldId`]s created from this method are unique across
29
/// time - if a given [`WorldId`] is [`Drop`]ped its value still cannot be reused
30
pub fn new() -> Option<Self> {
31
MAX_WORLD_ID
32
// We use `Relaxed` here since this atomic only needs to be consistent with itself
33
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
34
val.checked_add(1)
35
})
36
.map(WorldId)
37
.ok()
38
}
39
}
40
41
impl FromWorld for WorldId {
42
#[inline]
43
fn from_world(world: &mut World) -> Self {
44
world.id()
45
}
46
}
47
48
// SAFETY: No world data is accessed.
49
unsafe impl ReadOnlySystemParam for WorldId {}
50
51
// SAFETY: No world data is accessed.
52
unsafe impl SystemParam for WorldId {
53
type State = ();
54
55
type Item<'world, 'state> = WorldId;
56
57
fn init_state(_: &mut World) -> Self::State {}
58
59
fn init_access(
60
_state: &Self::State,
61
_system_meta: &mut SystemMeta,
62
_component_access_set: &mut FilteredAccessSet,
63
_world: &mut World,
64
) {
65
}
66
67
#[inline]
68
unsafe fn get_param<'world, 'state>(
69
_: &'state mut Self::State,
70
_: &SystemMeta,
71
world: UnsafeWorldCell<'world>,
72
_: Tick,
73
) -> Self::Item<'world, 'state> {
74
world.id()
75
}
76
}
77
78
impl ExclusiveSystemParam for WorldId {
79
type State = WorldId;
80
type Item<'s> = WorldId;
81
82
fn init(world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {
83
world.id()
84
}
85
86
fn get_param<'s>(state: &'s mut Self::State, _system_meta: &SystemMeta) -> Self::Item<'s> {
87
*state
88
}
89
}
90
91
impl SparseSetIndex for WorldId {
92
#[inline]
93
fn sparse_set_index(&self) -> usize {
94
self.0
95
}
96
97
#[inline]
98
fn get_sparse_set_index(value: usize) -> Self {
99
Self(value)
100
}
101
}
102
103
#[cfg(test)]
104
mod tests {
105
use super::*;
106
use alloc::vec::Vec;
107
108
#[test]
109
fn world_ids_unique() {
110
let ids = core::iter::repeat_with(WorldId::new)
111
.take(50)
112
.map(Option::unwrap)
113
.collect::<Vec<_>>();
114
for (i, &id1) in ids.iter().enumerate() {
115
// For the first element, i is 0 - so skip 1
116
for &id2 in ids.iter().skip(i + 1) {
117
assert_ne!(id1, id2, "WorldIds should not repeat");
118
}
119
}
120
}
121
122
#[test]
123
fn world_id_system_param() {
124
fn test_system(world_id: WorldId) -> WorldId {
125
world_id
126
}
127
128
let mut world = World::default();
129
let system_id = world.register_system(test_system);
130
let world_id = world.run_system(system_id).unwrap();
131
assert_eq!(world.id(), world_id);
132
}
133
134
#[test]
135
fn world_id_exclusive_system_param() {
136
fn test_system(_world: &mut World, world_id: WorldId) -> WorldId {
137
world_id
138
}
139
140
let mut world = World::default();
141
let system_id = world.register_system(test_system);
142
let world_id = world.run_system(system_id).unwrap();
143
assert_eq!(world.id(), world_id);
144
}
145
146
// We cannot use this test as-is, as it causes other tests to panic due to using the same atomic variable.
147
// #[test]
148
// #[should_panic]
149
// fn panic_on_overflow() {
150
// MAX_WORLD_ID.store(usize::MAX - 50, Ordering::Relaxed);
151
// core::iter::repeat_with(WorldId::new)
152
// .take(500)
153
// .for_each(|_| ());
154
// }
155
}
156
157