bevy_ecs/world/
identifier.rs

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