Skip to main content

bevy_trait_query/one/impls/
one.rs

1use bevy_ecs::change_detection::{Mut, Ref, Tick};
2use bevy_ecs::entity::Entity;
3use bevy_ecs::prelude::World;
4use bevy_ecs::ptr::UnsafeCellDeref;
5use bevy_ecs::query::{IterQueryData, SingleEntityQueryData};
6use bevy_ecs::{
7    component::{ComponentId, Components},
8    query::{QueryData, QueryItem, ReadOnlyQueryData, WorldQuery},
9    storage::TableRow,
10    world::unsafe_world_cell::UnsafeWorldCell,
11};
12
13use crate::TraitImplMeta;
14use crate::{
15    OneTraitFetch, TraitQuery, TraitQueryState, debug_unreachable, one::FetchStorage, zip_exact,
16};
17
18/// [`WorldQuery`] adapter that fetches entities with exactly one component implementing a trait.
19///
20/// Depending on whether you requested shared or exclusive access to the trait objects, iterating
21/// over these queries yields types with different capacities
22///
23/// - `Query<One<&dyn Trait>>` yields a [`Ref`] object
24/// - `Query<One<&mut dyn Trait>>` yields a [`Mut`] object
25pub struct One<T>(pub T);
26
27unsafe impl<Trait: ?Sized + TraitQuery> IterQueryData for One<&Trait> {}
28unsafe impl<Trait: ?Sized + TraitQuery> SingleEntityQueryData for One<&Trait> {}
29unsafe impl<Trait: ?Sized + TraitQuery> QueryData for One<&Trait> {
30    type ReadOnly = Self;
31
32    const IS_READ_ONLY: bool = true;
33    const IS_ARCHETYPAL: bool = false;
34
35    type Item<'w, 's> = Ref<'w, Trait>;
36
37    #[inline]
38    fn shrink<'wlong: 'wshort, 'wshort, 's>(
39        item: QueryItem<'wlong, 's, Self>,
40    ) -> QueryItem<'wshort, 's, Self> {
41        item
42    }
43
44    #[inline]
45    unsafe fn fetch<'w, 's>(
46        _state: &'s Self::State,
47        fetch: &mut Self::Fetch<'w>,
48        entity: Entity,
49        table_row: TableRow,
50    ) -> Option<Self::Item<'w, 's>> {
51        unsafe {
52            let table_row = table_row.index();
53            let (dyn_ctor, ptr, added, changed, location) = match fetch.storage {
54                // SAFETY: This function must have been called after `set_archetype`,
55                // so we know that `self.storage` has been initialized.
56                FetchStorage::Uninit => debug_unreachable(),
57                FetchStorage::Table {
58                    column,
59                    added_ticks,
60                    changed_ticks,
61                    location,
62                    meta,
63                } => {
64                    let ptr = column.byte_add(table_row * meta.size_bytes);
65                    (
66                        meta.dyn_ctor,
67                        ptr,
68                        // SAFETY: We have read access to the component, so by extension
69                        // we have access to the corresponding `ComponentTicks`.
70                        added_ticks.get_unchecked(table_row).deref(),
71                        changed_ticks.get_unchecked(table_row).deref(),
72                        location,
73                    )
74                }
75                FetchStorage::SparseSet { components, meta } => {
76                    let (ptr, ticks) = components
77                        .get_with_ticks(entity)
78                        .unwrap_or_else(|| debug_unreachable());
79                    (
80                        meta.dyn_ctor,
81                        ptr,
82                        // SAFETY: We have read access to the component, so by extension
83                        // we have access to the corresponding `ComponentTicks`.
84                        ticks.added.deref(),
85                        ticks.changed.deref(),
86                        ticks.changed_by,
87                    )
88                }
89            };
90
91            Some(Ref::new(
92                dyn_ctor.cast(ptr),
93                added,
94                changed,
95                fetch.last_run,
96                fetch.this_run,
97                location.map(|loc| loc.deref()),
98            ))
99        }
100    }
101
102    fn iter_access(
103        _state: &Self::State,
104    ) -> impl Iterator<Item = bevy_ecs::query::EcsAccessType<'_>> {
105        core::iter::empty()
106    }
107}
108
109unsafe impl<Trait: ?Sized + TraitQuery> ReadOnlyQueryData for One<&Trait> {}
110
111// SAFETY: We only access the components registered in TraitQueryState.
112// This same set of components is used to match archetypes, and used to register world access.
113unsafe impl<Trait: ?Sized + TraitQuery> WorldQuery for One<&Trait> {
114    type Fetch<'w> = OneTraitFetch<'w, Trait>;
115    type State = TraitQueryState<Trait>;
116
117    #[inline]
118    unsafe fn init_fetch<'w>(
119        world: UnsafeWorldCell<'w>,
120        _state: &Self::State,
121        _last_run: Tick,
122        _this_run: Tick,
123    ) -> OneTraitFetch<'w, Trait> {
124        unsafe {
125            OneTraitFetch {
126                storage: FetchStorage::Uninit,
127                last_run: Tick::new(0),
128                sparse_sets: &world.storages().sparse_sets,
129                this_run: Tick::new(0),
130            }
131        }
132    }
133
134    const IS_DENSE: bool = false;
135    // const IS_ARCHETYPAL: bool = false;
136
137    #[inline]
138    unsafe fn set_archetype<'w>(
139        fetch: &mut OneTraitFetch<'w, Trait>,
140        state: &Self::State,
141        _archetype: &'w bevy_ecs::archetype::Archetype,
142        table: &'w bevy_ecs::storage::Table,
143    ) {
144        unsafe {
145            // Search for a registered trait impl that is present in the archetype.
146            // We check the table components first since it is faster to retrieve data of this type.
147            //
148            // without loss of generality we use the zero-th row since we only care about whether the
149            // component exists in the table
150            let row = TableRow::new(0_u16.into());
151            for (&component_id, &meta) in zip_exact(&*state.components, &*state.meta) {
152                if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
153                    fetch.storage = table_storage;
154                    return;
155                }
156            }
157            for (&component, &meta) in zip_exact(&*state.components, &*state.meta) {
158                if let Some(sparse_set) = fetch.sparse_sets.get(component) {
159                    fetch.storage = FetchStorage::SparseSet {
160                        components: sparse_set,
161                        meta,
162                    };
163                    return;
164                }
165            }
166            // At least one of the components must be present in the table/sparse set.
167            debug_unreachable()
168        }
169    }
170
171    #[inline]
172    unsafe fn set_table<'w>(
173        fetch: &mut OneTraitFetch<'w, Trait>,
174        state: &Self::State,
175        table: &'w bevy_ecs::storage::Table,
176    ) {
177        unsafe {
178            // Search for a registered trait impl that is present in the table.
179            //
180            // without loss of generality we use the zero-th row since we only care about whether the
181            // component exists in the table
182            let row = TableRow::new(0_u16.into());
183            for (&component_id, &meta) in core::iter::zip(&*state.components, &*state.meta) {
184                if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
185                    fetch.storage = table_storage;
186                    return;
187                }
188            }
189            // At least one of the components must be present in the table.
190            debug_unreachable()
191        }
192    }
193
194    #[inline]
195    fn update_component_access(state: &Self::State, access: &mut bevy_ecs::query::FilteredAccess) {
196        let mut new_access = access.clone();
197        let mut not_first = false;
198        for &component in &*state.components {
199            assert!(
200                !access.access().has_write(component),
201                "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
202                core::any::type_name::<Trait>(),
203            );
204            if not_first {
205                let mut intermediate = access.clone();
206                intermediate.add_read(component);
207                new_access.append_or(&intermediate);
208                new_access.extend_access(&intermediate);
209            } else {
210                new_access.and_with(component);
211                new_access.access_mut().add_read(component);
212                not_first = true;
213            }
214        }
215        *access = new_access;
216    }
217
218    #[inline]
219    fn init_state(world: &mut World) -> Self::State {
220        TraitQueryState::init(world)
221    }
222
223    #[inline]
224    fn get_state(_: &Components) -> Option<Self::State> {
225        // TODO: fix this https://github.com/bevyengine/bevy/issues/13798
226        panic!(
227            "transmuting and any other operations concerning the state of a query are currently broken and shouldn't be used. See https://github.com/JoJoJet/bevy-trait-query/issues/59"
228        );
229    }
230
231    #[inline]
232    fn matches_component_set(
233        state: &Self::State,
234        set_contains_id: &impl Fn(ComponentId) -> bool,
235    ) -> bool {
236        state.matches_component_set_one(set_contains_id)
237    }
238
239    #[inline]
240    fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
241        fetch
242    }
243}
244
245unsafe impl<Trait: ?Sized + TraitQuery> IterQueryData for One<&mut Trait> {}
246unsafe impl<Trait: ?Sized + TraitQuery> SingleEntityQueryData for One<&mut Trait> {}
247unsafe impl<'a, Trait: ?Sized + TraitQuery> QueryData for One<&'a mut Trait> {
248    type ReadOnly = One<&'a Trait>;
249
250    const IS_READ_ONLY: bool = false;
251    const IS_ARCHETYPAL: bool = false;
252
253    type Item<'w, 's> = Mut<'w, Trait>;
254
255    #[inline]
256    fn shrink<'wlong: 'wshort, 'wshort, 's>(
257        item: QueryItem<'wlong, 's, Self>,
258    ) -> QueryItem<'wshort, 's, Self> {
259        item
260    }
261
262    #[inline]
263    unsafe fn fetch<'w>(
264        _state: &Self::State,
265        fetch: &mut Self::Fetch<'w>,
266        entity: Entity,
267        table_row: TableRow,
268    ) -> Option<Mut<'w, Trait>> {
269        unsafe {
270            let table_row = table_row.index();
271            let (dyn_ctor, ptr, added, changed, location) = match fetch.storage {
272                // SAFETY: This function must have been called after `set_archetype`,
273                // so we know that `self.storage` has been initialized.
274                FetchStorage::Uninit => debug_unreachable(),
275                FetchStorage::Table {
276                    column,
277                    added_ticks,
278                    changed_ticks,
279                    location,
280                    meta,
281                } => {
282                    let ptr = column.byte_add(table_row * meta.size_bytes);
283                    (
284                        meta.dyn_ctor,
285                        // SAFETY: `column` allows for shared mutable access.
286                        // So long as the caller does not invoke this function twice with the same archetype_index,
287                        // this pointer will never be aliased.
288                        ptr.assert_unique(),
289                        // SAFETY: We have exclusive access to the component, so by extension
290                        // we have exclusive access to the corresponding `ComponentTicks`.
291                        added_ticks.get_unchecked(table_row).deref_mut(),
292                        changed_ticks.get_unchecked(table_row).deref_mut(),
293                        location,
294                    )
295                }
296                FetchStorage::SparseSet { components, meta } => {
297                    let (ptr, ticks) = components
298                        .get_with_ticks(entity)
299                        .unwrap_or_else(|| debug_unreachable());
300                    (
301                        meta.dyn_ctor,
302                        // SAFETY: We have exclusive access to the sparse set `components`.
303                        // So long as the caller does not invoke this function twice with the same archetype_index,
304                        // this pointer will never be aliased.
305                        ptr.assert_unique(),
306                        // SAFETY: We have exclusive access to the component, so by extension
307                        // we have exclusive access to the corresponding `ComponentTicks`.
308                        ticks.added.deref_mut(),
309                        ticks.changed.deref_mut(),
310                        ticks.changed_by,
311                    )
312                }
313            };
314
315            Some(Mut::new(
316                dyn_ctor.cast_mut(ptr),
317                added,
318                changed,
319                fetch.last_run,
320                fetch.this_run,
321                location.map(|loc| loc.deref_mut()),
322            ))
323        }
324    }
325
326    fn iter_access(
327        _state: &Self::State,
328    ) -> impl Iterator<Item = bevy_ecs::query::EcsAccessType<'_>> {
329        core::iter::empty()
330    }
331}
332
333// SAFETY: We only access the components registered in TraitQueryState.
334// This same set of components is used to match archetypes, and used to register world access.
335unsafe impl<Trait: ?Sized + TraitQuery> WorldQuery for One<&mut Trait> {
336    type Fetch<'w> = OneTraitFetch<'w, Trait>;
337    type State = TraitQueryState<Trait>;
338
339    #[inline]
340    unsafe fn init_fetch<'w>(
341        world: UnsafeWorldCell<'w>,
342        _state: &Self::State,
343        last_run: Tick,
344        this_run: Tick,
345    ) -> OneTraitFetch<'w, Trait> {
346        unsafe {
347            OneTraitFetch {
348                storage: FetchStorage::Uninit,
349                sparse_sets: &world.storages().sparse_sets,
350                last_run,
351                this_run,
352            }
353        }
354    }
355
356    const IS_DENSE: bool = false;
357
358    #[inline]
359    unsafe fn set_archetype<'w>(
360        fetch: &mut OneTraitFetch<'w, Trait>,
361        state: &Self::State,
362        _archetype: &'w bevy_ecs::archetype::Archetype,
363        table: &'w bevy_ecs::storage::Table,
364    ) {
365        unsafe {
366            // Search for a registered trait impl that is present in the archetype.
367            //
368            // without loss of generality we use the zero-th row since we only care about whether the
369            // component exists in the table
370            let row = TableRow::new(0_u16.into());
371            for (&component_id, &meta) in zip_exact(&*state.components, &*state.meta) {
372                if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
373                    fetch.storage = table_storage;
374                    return;
375                }
376            }
377            for (&component, &meta) in zip_exact(&*state.components, &*state.meta) {
378                if let Some(sparse_set) = fetch.sparse_sets.get(component) {
379                    fetch.storage = FetchStorage::SparseSet {
380                        components: sparse_set,
381                        meta,
382                    };
383                    return;
384                }
385            }
386            // At least one of the components must be present in the table/sparse set.
387            debug_unreachable()
388        }
389    }
390
391    #[inline]
392    unsafe fn set_table<'w>(
393        fetch: &mut OneTraitFetch<'w, Trait>,
394        state: &Self::State,
395        table: &'w bevy_ecs::storage::Table,
396    ) {
397        unsafe {
398            // Search for a registered trait impl that is present in the table.
399            //
400            // without loss of generality we use the zero-th row since we only care about whether the
401            // component exists in the table
402            let row = TableRow::new(0_u16.into());
403            for (&component_id, &meta) in core::iter::zip(&*state.components, &*state.meta) {
404                if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
405                    fetch.storage = table_storage;
406                    return;
407                }
408            }
409            // At least one of the components must be present in the table.
410            debug_unreachable()
411        }
412    }
413
414    #[inline]
415    fn update_component_access(state: &Self::State, access: &mut bevy_ecs::query::FilteredAccess) {
416        let mut new_access = access.clone();
417        let mut not_first = false;
418        for &component in &*state.components {
419            assert!(
420                !access.access().has_write(component),
421                "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.",
422                core::any::type_name::<Trait>(),
423            );
424            if not_first {
425                let mut intermediate = access.clone();
426                intermediate.add_write(component);
427                new_access.append_or(&intermediate);
428                new_access.extend_access(&intermediate);
429            } else {
430                new_access.and_with(component);
431                new_access.access_mut().add_write(component);
432                not_first = true;
433            }
434        }
435        *access = new_access;
436    }
437
438    #[inline]
439    fn init_state(world: &mut World) -> Self::State {
440        TraitQueryState::init(world)
441    }
442
443    #[inline]
444    fn get_state(_: &Components) -> Option<Self::State> {
445        // TODO: fix this https://github.com/bevyengine/bevy/issues/13798
446        panic!(
447            "transmuting and any other operations concerning the state of a query are currently broken and shouldn't be used. See https://github.com/JoJoJet/bevy-trait-query/issues/59"
448        );
449    }
450
451    #[inline]
452    fn matches_component_set(
453        state: &Self::State,
454        set_contains_id: &impl Fn(ComponentId) -> bool,
455    ) -> bool {
456        state.matches_component_set_one(set_contains_id)
457    }
458
459    #[inline]
460    fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
461        fetch
462    }
463}
464
465// gets all the relevant data repeatingly used for the table storage
466#[inline]
467unsafe fn get_table_fetch_data<Trait: ?Sized + TraitQuery>(
468    table: &'_ bevy_ecs::storage::Table,
469    component_id: ComponentId,
470    row: TableRow,
471    meta: TraitImplMeta<Trait>,
472) -> Option<FetchStorage<'_, Trait>> {
473    unsafe {
474        let ptr = table.get_component(component_id, row)?;
475        let location = table.get_changed_by(component_id, row).transpose()?;
476        let added = table.get_added_ticks_slice_for(component_id)?;
477        let changed = table.get_changed_ticks_slice_for(component_id)?;
478        Some(FetchStorage::Table {
479            column: ptr,
480            added_ticks: added.into(),
481            changed_ticks: changed.into(),
482            location,
483            meta,
484        })
485    }
486}