Skip to main content

bevy_ecs/world/
identifier.rs

1use crate::{
2    change_detection::Tick,
3    storage::SparseSetIndex,
4    system::{SystemAccess, SystemMeta, SystemParam, SystemParamValidationError},
5    world::{unsafe_world_cell::UnsafeWorldCell, FromWorld, World},
6};
7use bevy_platform::sync::atomic::{AtomicUsize, Ordering};
8
9#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
10// We use usize here because that is the largest `Atomic` we want to require
11/// A unique identifier for a [`World`].
12///
13/// The trait [`FromWorld`] is implemented for this type, which returns the
14/// ID of the world passed to [`FromWorld::from_world`].
15// Note that this *is* used by external crates as well as for internal safety checks
16pub struct WorldId(usize);
17
18/// The next [`WorldId`].
19static MAX_WORLD_ID: AtomicUsize = AtomicUsize::new(0);
20
21impl WorldId {
22    /// Create a new, unique [`WorldId`]. Returns [`None`] if the supply of unique
23    /// [`WorldId`]s has been exhausted
24    ///
25    /// Please note that the [`WorldId`]s created from this method are unique across
26    /// time - if a given [`WorldId`] is [`Drop`]ped its value still cannot be reused
27    pub fn new() -> Option<Self> {
28        // NOTE: this is not really a std vs. no_std change.
29        // The split is done to silence a warning and also satisfy builds for an older no_std target on CI.
30        // Once you see the deprecation warning for no_std, collapse the function into this first branch.
31        #[cfg(feature = "std")]
32        {
33            MAX_WORLD_ID
34                // We use `Relaxed` here since this atomic only needs to be consistent with itself
35                .try_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
36                    val.checked_add(1)
37                })
38                .map(WorldId)
39                .ok()
40        }
41        #[cfg(not(feature = "std"))]
42        {
43            MAX_WORLD_ID
44                // We use `Relaxed` here since this atomic only needs to be consistent with itself
45                .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |val| {
46                    val.checked_add(1)
47                })
48                .map(WorldId)
49                .ok()
50        }
51    }
52}
53
54impl FromWorld for WorldId {
55    #[inline]
56    fn from_world(world: &mut World) -> Self {
57        world.id()
58    }
59}
60
61// SAFETY: World metadata is registered and accessed.
62unsafe impl SystemParam for WorldId {
63    type State = ();
64
65    type Item<'world, 'state> = WorldId;
66
67    fn init_state(_: &mut World) -> Self::State {}
68
69    fn init_access(
70        _state: &Self::State,
71        system_meta: &mut SystemMeta,
72        system_access: &mut SystemAccess,
73        _world: &mut World,
74    ) {
75        system_access.require_shared_access::<Self>(system_meta);
76    }
77
78    #[inline]
79    unsafe fn get_param<'world, 'state>(
80        _: &'state mut Self::State,
81        _: &SystemMeta,
82        world: UnsafeWorldCell<'world>,
83        _: Tick,
84    ) -> Result<Self::Item<'world, 'state>, SystemParamValidationError> {
85        Ok(world.id())
86    }
87}
88
89impl SparseSetIndex for WorldId {
90    #[inline]
91    fn sparse_set_index(&self) -> usize {
92        self.0
93    }
94
95    #[inline]
96    fn get_sparse_set_index(value: usize) -> Self {
97        Self(value)
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use crate::system::assert_is_system;
104
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    #[should_panic]
136    fn world_id_cannot_be_exclusive_system_param() {
137        fn test_system(_world: &mut World, world_id: WorldId) -> WorldId {
138            world_id
139        }
140        assert_is_system(test_system);
141    }
142
143    // We cannot use this test as-is, as it causes other tests to panic due to using the same atomic variable.
144    // #[test]
145    // #[should_panic]
146    // fn panic_on_overflow() {
147    //     MAX_WORLD_ID.store(usize::MAX - 50, Ordering::Relaxed);
148    //     core::iter::repeat_with(WorldId::new)
149    //         .take(500)
150    //         .for_each(|_| ());
151    // }
152}