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