bevy_trait_query/all/core/
read.rs

1use bevy_ecs::{
2    change_detection::{DetectChanges, Ref},
3    component::{ComponentId, Tick},
4    entity::Entity,
5    ptr::UnsafeCellDeref,
6    storage::{SparseSets, Table, TableRow},
7};
8
9use crate::{zip_exact, TraitImplMeta, TraitImplRegistry, TraitQuery};
10
11/// Read-access to all components implementing a trait for a given entity.
12///
13/// This supports change detection and detection for added objects via
14///
15/// - [`ReadTraits::iter_changed`]
16/// - [`ReadTraits::iter_added`]
17pub struct ReadTraits<'a, Trait: ?Sized + TraitQuery> {
18    // Read-only access to the global trait registry.
19    // Since no one outside of the crate can name the registry type,
20    // we can be confident that no write accesses will conflict with this.
21    pub(crate) registry: &'a TraitImplRegistry<Trait>,
22    pub(crate) table: &'a Table,
23    pub(crate) table_row: TableRow,
24    /// This grants shared access to all sparse set components,
25    /// but in practice we will only read the components specified in `self.registry`.
26    /// The fetch impl registers read-access for all of these components,
27    /// so there will be no runtime conflicts.
28    pub(crate) sparse_sets: &'a SparseSets,
29    pub(crate) last_run: Tick,
30    pub(crate) this_run: Tick,
31}
32
33#[doc(hidden)]
34pub type CombinedReadTraitsIter<'a, Trait> =
35    std::iter::Chain<ReadTableTraitsIter<'a, Trait>, ReadSparseTraitsIter<'a, Trait>>;
36
37#[doc(hidden)]
38pub struct ReadTableTraitsIter<'a, Trait: ?Sized> {
39    // SAFETY: These two iterators must have equal length.
40    pub(crate) components: std::slice::Iter<'a, ComponentId>,
41    pub(crate) meta: std::slice::Iter<'a, TraitImplMeta<Trait>>,
42    pub(crate) table_row: TableRow,
43    // Grants shared access to the components corresponding to `components` in this table.
44    // Not all components are guaranteed to exist in the table.
45    pub(crate) table: &'a Table,
46    pub(crate) last_run: Tick,
47    pub(crate) this_run: Tick,
48}
49
50impl<'a, Trait: ?Sized + TraitQuery> Iterator for ReadTableTraitsIter<'a, Trait> {
51    type Item = Ref<'a, Trait>;
52    fn next(&mut self) -> Option<Self::Item> {
53        // Iterate the remaining table components that are registered,
54        // until we find one that exists in the table.
55        let (ptr, component_id, meta) = unsafe { zip_exact(&mut self.components, &mut self.meta) }
56            .find_map(|(&component, meta)| {
57                // SAFETY: we know that the `table_row` is a valid index.
58                let ptr = unsafe { self.table.get_component(component, self.table_row) }?;
59                Some((ptr, component, meta))
60            })?;
61        let trait_object = unsafe { meta.dyn_ctor.cast(ptr) };
62
63        // SAFETY:
64        // Read access has been registered, so we can dereference it immutably.
65        let added_tick = unsafe {
66            self.table
67                .get_added_tick(component_id, self.table_row)?
68                .deref()
69        };
70        let changed_tick = unsafe {
71            self.table
72                .get_changed_tick(component_id, self.table_row)?
73                .deref()
74        };
75        let location = unsafe {
76            self.table
77                .get_changed_by(component_id, self.table_row)
78                .transpose()?
79                .map(|loc| loc.deref())
80        };
81
82        Some(Ref::new(
83            trait_object,
84            added_tick,
85            changed_tick,
86            self.last_run,
87            self.this_run,
88            location,
89        ))
90    }
91}
92
93#[doc(hidden)]
94pub struct ReadSparseTraitsIter<'a, Trait: ?Sized> {
95    // SAFETY: These two iterators must have equal length.
96    pub(crate) components: std::slice::Iter<'a, ComponentId>,
97    pub(crate) meta: std::slice::Iter<'a, TraitImplMeta<Trait>>,
98    pub(crate) entity: Entity,
99    // Grants shared access to the components corresponding to both `components` and `entity`.
100    pub(crate) sparse_sets: &'a SparseSets,
101    pub(crate) last_run: Tick,
102    pub(crate) this_run: Tick,
103}
104
105impl<'a, Trait: ?Sized + TraitQuery> Iterator for ReadSparseTraitsIter<'a, Trait> {
106    type Item = Ref<'a, Trait>;
107    fn next(&mut self) -> Option<Self::Item> {
108        // Iterate the remaining sparse set components that are registered,
109        // until we find one that exists in the archetype.
110        let (ptr, ticks_ptr, meta, location) =
111            unsafe { zip_exact(&mut self.components, &mut self.meta) }.find_map(
112                |(&component, meta)| {
113                    let set = self.sparse_sets.get(component)?;
114                    let (ptr, ticks, location) = set.get_with_ticks(self.entity)?;
115                    Some((ptr, ticks, meta, location))
116                },
117            )?;
118        let trait_object = unsafe { meta.dyn_ctor.cast(ptr) };
119        let added_tick = unsafe { ticks_ptr.added.deref() };
120        let changed_tick = unsafe { ticks_ptr.changed.deref() };
121        Some(Ref::new(
122            trait_object,
123            added_tick,
124            changed_tick,
125            self.last_run,
126            self.this_run,
127            location.map(|loc| unsafe { loc.deref() }),
128        ))
129    }
130}
131
132impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for ReadTraits<'w, Trait> {
133    type Item = Ref<'w, Trait>;
134    type IntoIter = CombinedReadTraitsIter<'w, Trait>;
135    #[inline]
136    fn into_iter(self) -> Self::IntoIter {
137        let table = ReadTableTraitsIter {
138            components: self.registry.table_components.iter(),
139            meta: self.registry.table_meta.iter(),
140            table: self.table,
141            table_row: self.table_row,
142            last_run: self.last_run,
143            this_run: self.this_run,
144        };
145        let sparse = ReadSparseTraitsIter {
146            components: self.registry.sparse_components.iter(),
147            meta: self.registry.sparse_meta.iter(),
148            entity: self.table.entities()[self.table_row.as_usize()],
149            sparse_sets: self.sparse_sets,
150            last_run: self.last_run,
151            this_run: self.this_run,
152        };
153        table.chain(sparse)
154    }
155}
156
157impl<'w, Trait: ?Sized + TraitQuery> IntoIterator for &ReadTraits<'w, Trait> {
158    type Item = Ref<'w, Trait>;
159    type IntoIter = CombinedReadTraitsIter<'w, Trait>;
160    #[inline]
161    fn into_iter(self) -> Self::IntoIter {
162        let table = ReadTableTraitsIter {
163            components: self.registry.table_components.iter(),
164            meta: self.registry.table_meta.iter(),
165            table: self.table,
166            table_row: self.table_row,
167            last_run: self.last_run,
168            this_run: self.this_run,
169        };
170        let sparse = ReadSparseTraitsIter {
171            components: self.registry.sparse_components.iter(),
172            meta: self.registry.sparse_meta.iter(),
173            entity: self.table.entities()[self.table_row.as_usize()],
174            sparse_sets: self.sparse_sets,
175            last_run: self.last_run,
176            this_run: self.this_run,
177        };
178        table.chain(sparse)
179    }
180}
181
182impl<'w, Trait: ?Sized + TraitQuery> ReadTraits<'w, Trait> {
183    /// Returns an iterator over the components implementing `Trait` for the current entity.
184    pub fn iter(&self) -> CombinedReadTraitsIter<'w, Trait> {
185        self.into_iter()
186    }
187
188    /// Returns an iterator over the components implementing `Trait` for the current entity
189    /// that were added since the last time the system was run.
190    pub fn iter_added(&self) -> impl Iterator<Item = Ref<'w, Trait>> + use<'w, Trait> {
191        self.iter().filter(DetectChanges::is_added)
192    }
193
194    /// Returns an iterator over the components implementing `Trait` for the current entity
195    /// whose values were changed since the last time the system was run.
196    pub fn iter_changed(&self) -> impl Iterator<Item = Ref<'w, Trait>> + use<'w, Trait> {
197        self.iter().filter(DetectChanges::is_changed)
198    }
199}