Skip to main content

bevy_ecs/world/
identifier.rs

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