bevy_trait_query/one/impls/
one_added.rs

1use bevy_ecs::ptr::UnsafeCellDeref;
2use std::marker::PhantomData;
3
4use bevy_ecs::{
5    archetype::Archetype,
6    component::{ComponentId, Components, Tick},
7    prelude::{Entity, World},
8    query::{FilteredAccess, QueryData, QueryFilter, ReadOnlyQueryData, WorldQuery},
9    storage::{Table, TableRow},
10    world::unsafe_world_cell::UnsafeWorldCell,
11};
12
13use crate::{
14    ChangeDetectionFetch, ChangeDetectionStorage, TraitQuery, TraitQueryState, debug_unreachable,
15};
16
17/// [`WorldQuery`] filter for entities with exactly [one](crate::One) component
18/// implementing a trait, whose value has changed since the last time the system ran.
19pub struct OneAdded<Trait: ?Sized + TraitQuery> {
20    marker: PhantomData<&'static Trait>,
21}
22
23unsafe impl<Trait: ?Sized + TraitQuery> QueryData for OneAdded<Trait> {
24    type ReadOnly = Self;
25
26    const IS_READ_ONLY: bool = true;
27
28    type Item<'w, 's> = bool;
29
30    fn shrink<'wlong: 'wshort, 'wshort, 's>(
31        item: Self::Item<'wlong, 's>,
32    ) -> Self::Item<'wshort, 's> {
33        item
34    }
35
36    #[inline(always)]
37    unsafe fn fetch<'w, 's>(
38        _state: &'s Self::State,
39        fetch: &mut Self::Fetch<'w>,
40        entity: Entity,
41        table_row: TableRow,
42    ) -> Self::Item<'w, 's> {
43        unsafe {
44            let ticks_ptr = match fetch.storage {
45                ChangeDetectionStorage::Uninit => {
46                    // set_archetype must have been called already
47                    debug_unreachable()
48                }
49                ChangeDetectionStorage::Table { ticks } => ticks.get(table_row.index()),
50                ChangeDetectionStorage::SparseSet { components } => components
51                    .get_added_tick(entity)
52                    .unwrap_or_else(|| debug_unreachable()),
53            };
54
55            (*ticks_ptr)
56                .deref()
57                .is_newer_than(fetch.last_run, fetch.this_run)
58        }
59    }
60}
61
62unsafe impl<Trait: ?Sized + TraitQuery> WorldQuery for OneAdded<Trait> {
63    type Fetch<'w> = ChangeDetectionFetch<'w>;
64    type State = TraitQueryState<Trait>;
65
66    unsafe fn init_fetch<'w>(
67        world: UnsafeWorldCell<'w>,
68        _state: &Self::State,
69        last_run: Tick,
70        this_run: Tick,
71    ) -> Self::Fetch<'w> {
72        unsafe {
73            Self::Fetch::<'w> {
74                storage: ChangeDetectionStorage::Uninit,
75                sparse_sets: &world.storages().sparse_sets,
76                last_run,
77                this_run,
78            }
79        }
80    }
81
82    // This will always be false for us, as we (so far) do not know at compile time whether the
83    // components our trait has been impl'd for are stored in table or in sparse set
84    const IS_DENSE: bool = false;
85
86    #[inline]
87    unsafe fn set_archetype<'w>(
88        fetch: &mut Self::Fetch<'w>,
89        state: &Self::State,
90        _archetype: &'w Archetype,
91        table: &'w Table,
92    ) {
93        unsafe {
94            // Search for a registered trait impl that is present in the archetype.
95            // We check the table components first since it is faster to retrieve data of this type.
96            for &component in &*state.components {
97                if let Some(added) = table.get_added_ticks_slice_for(component) {
98                    fetch.storage = ChangeDetectionStorage::Table {
99                        ticks: added.into(),
100                    };
101                    return;
102                }
103            }
104            for &component in &*state.components {
105                if let Some(components) = fetch.sparse_sets.get(component) {
106                    fetch.storage = ChangeDetectionStorage::SparseSet { components };
107                    return;
108                }
109            }
110            // At least one of the components must be present in the table/sparse set.
111            debug_unreachable()
112        }
113    }
114
115    #[inline]
116    unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) {
117        unsafe {
118            // only gets called if IS_DENSE == true, which does not hold for us
119            debug_unreachable()
120        }
121    }
122
123    #[inline]
124    fn update_component_access(state: &Self::State, access: &mut FilteredAccess) {
125        let mut new_access = access.clone();
126        let mut not_first = false;
127        for &component in &*state.components {
128            assert!(
129                !access.access().has_component_write(component),
130                "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
131                std::any::type_name::<Trait>(),
132            );
133            if not_first {
134                let mut intermediate = access.clone();
135                intermediate.add_component_read(component);
136                new_access.append_or(&intermediate);
137                new_access.extend_access(&intermediate);
138            } else {
139                new_access.and_with(component);
140                new_access.access_mut().add_component_read(component);
141                not_first = true;
142            }
143        }
144        *access = new_access;
145    }
146
147    #[inline]
148    fn init_state(world: &mut World) -> Self::State {
149        TraitQueryState::init(world)
150    }
151
152    #[inline]
153    fn get_state(_: &Components) -> Option<Self::State> {
154        // TODO: fix this https://github.com/bevyengine/bevy/issues/13798
155        panic!(
156            "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"
157        );
158    }
159
160    fn matches_component_set(
161        state: &Self::State,
162        set_contains_id: &impl Fn(ComponentId) -> bool,
163    ) -> bool {
164        state.matches_component_set_one(set_contains_id)
165    }
166
167    #[inline]
168    fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
169        fetch
170    }
171}
172
173/// SAFETY: read-only access
174unsafe impl<Trait: ?Sized + TraitQuery> ReadOnlyQueryData for OneAdded<Trait> {}
175unsafe impl<Trait: ?Sized + TraitQuery> QueryFilter for OneAdded<Trait> {
176    const IS_ARCHETYPAL: bool = false;
177    unsafe fn filter_fetch(
178        state: &Self::State,
179        fetch: &mut Self::Fetch<'_>,
180        entity: Entity,
181        table_row: TableRow,
182    ) -> bool {
183        unsafe { <Self as QueryData>::fetch(state, fetch, entity, table_row) }
184    }
185}