Skip to main content

bevy_extract/
extract_param.rs

1use crate::MainWorld;
2use bevy_ecs::{
3    change_detection::Tick,
4    prelude::*,
5    system::{
6        ReadOnlySystemParam, SystemAccess, SystemMeta, SystemParam, SystemParamItem,
7        SystemParamValidationError, SystemState,
8    },
9    world::unsafe_world_cell::UnsafeWorldCell,
10};
11use core::ops::{Deref, DerefMut};
12
13/// A helper for accessing [`MainWorld`] content using a system parameter.
14///
15/// A [`SystemParam`] adapter which applies the contained `SystemParam` to the [`World`]
16/// contained in [`MainWorld`]. This parameter only works for systems run
17/// during the [`ExtractSchedule`](crate::ExtractSchedule).
18///
19/// This requires that the contained [`SystemParam`] does not mutate the world, as it
20/// uses a read-only reference to [`MainWorld`] internally.
21///
22/// ## Context
23///
24/// [`ExtractSchedule`] is used to extract (move) data from the simulation world ([`MainWorld`]) to the
25/// sub world.
26/// This design is used to allow performing calculations related to a prior frame at the same
27/// time as the next frame is simulated, which increases throughput (FPS).
28///
29/// [`Extract`] is used to get data from the main world during [`ExtractSchedule`].
30///
31/// ## Examples
32///
33/// ```
34/// use bevy_ecs::prelude::*;
35/// use bevy_extract::Extract;
36/// use bevy_extract::sync_world::SubEntity;
37/// use bevy_derive::AppLabel;
38/// # #[derive(AppLabel, Debug, Hash, PartialEq, Eq, Clone, Default, Copy)]
39/// # struct ExtractApp;
40/// # #[derive(Component)]
41/// // Do make sure to sync the cloud entities before extracting them.
42/// # struct Cloud;
43/// fn extract_clouds(mut commands: Commands, clouds: Extract<Query<SubEntity<ExtractApp>, With<Cloud>>>) {
44///     for cloud in &clouds {
45///         commands.entity(cloud).insert(Cloud);
46///     }
47/// }
48/// ```
49///
50/// [`ExtractSchedule`]: crate::ExtractSchedule
51pub struct Extract<'w, 's, P>
52where
53    P: ReadOnlySystemParam + 'static,
54{
55    item: SystemParamItem<'w, 's, P>,
56}
57
58#[doc(hidden)]
59pub struct ExtractState<P: SystemParam + 'static> {
60    state: SystemState<P>,
61    main_world_state: <Res<'static, MainWorld> as SystemParam>::State,
62}
63
64// SAFETY: The only `World` access (`Res<MainWorld>`) is read-only.
65unsafe impl<P> ReadOnlySystemParam for Extract<'_, '_, P> where P: ReadOnlySystemParam {}
66
67// SAFETY: The only `World` access is properly registered by `Res<MainWorld>::init_state`.
68// This call will also ensure that there are no conflicts with prior params.
69unsafe impl<P> SystemParam for Extract<'_, '_, P>
70where
71    P: ReadOnlySystemParam,
72{
73    type State = ExtractState<P>;
74    type Item<'w, 's> = Extract<'w, 's, P>;
75
76    fn init_state(world: &mut World) -> Self::State {
77        let mut main_world = world.resource_mut::<MainWorld>();
78        ExtractState {
79            state: SystemState::new(&mut main_world),
80            main_world_state: Res::<MainWorld>::init_state(world),
81        }
82    }
83
84    fn init_access(
85        state: &Self::State,
86        system_meta: &mut SystemMeta,
87        system_access: &mut SystemAccess,
88        world: &mut World,
89    ) {
90        Res::<MainWorld>::init_access(&state.main_world_state, system_meta, system_access, world);
91    }
92
93    #[inline]
94    unsafe fn get_param<'w, 's>(
95        state: &'s mut Self::State,
96        system_meta: &SystemMeta,
97        world: UnsafeWorldCell<'w>,
98        change_tick: Tick,
99    ) -> Result<Self::Item<'w, 's>, SystemParamValidationError> {
100        // SAFETY:
101        // - The caller ensures that `world` is the same one that `init_state` was called with.
102        // - The caller ensures that no other `SystemParam`s will conflict with the accesses we have registered.
103        let main_world = unsafe {
104            Res::<MainWorld>::get_param(
105                &mut state.main_world_state,
106                system_meta,
107                world,
108                change_tick,
109            )?
110        };
111        let item = state.state.get(main_world.into_inner())?;
112        Ok(Extract { item })
113    }
114}
115
116impl<'w, 's, P> Deref for Extract<'w, 's, P>
117where
118    P: ReadOnlySystemParam,
119{
120    type Target = SystemParamItem<'w, 's, P>;
121
122    #[inline]
123    fn deref(&self) -> &Self::Target {
124        &self.item
125    }
126}
127
128impl<'w, 's, P> DerefMut for Extract<'w, 's, P>
129where
130    P: ReadOnlySystemParam,
131{
132    #[inline]
133    fn deref_mut(&mut self) -> &mut Self::Target {
134        &mut self.item
135    }
136}
137
138impl<'a, 'w, 's, P> IntoIterator for &'a Extract<'w, 's, P>
139where
140    P: ReadOnlySystemParam,
141    &'a SystemParamItem<'w, 's, P>: IntoIterator,
142{
143    type Item = <&'a SystemParamItem<'w, 's, P> as IntoIterator>::Item;
144    type IntoIter = <&'a SystemParamItem<'w, 's, P> as IntoIterator>::IntoIter;
145
146    fn into_iter(self) -> Self::IntoIter {
147        (&self.item).into_iter()
148    }
149}