bevy_ecs/world/
identifier.rs

1use crate::{
2    component::Tick,
3    query::FilteredAccessSet,
4    storage::SparseSetIndex,
5    system::{ExclusiveSystemParam, ReadOnlySystemParam, SystemMeta, SystemParam},
6    world::{FromWorld, World},
7};
8use bevy_platform::sync::atomic::{AtomicUsize, Ordering};
9
10use 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
19pub struct WorldId(usize);
20
21/// The next [`WorldId`].
22static MAX_WORLD_ID: AtomicUsize = AtomicUsize::new(0);
23
24impl 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
41impl 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.
49unsafe impl ReadOnlySystemParam for WorldId {}
50
51// SAFETY: No world data is accessed.
52unsafe 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
78impl 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
91impl 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)]
104mod 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}