bevy_trait_query/one/impls/
one_added.rs1use bevy_ecs::{
2 ptr::UnsafeCellDeref,
3 query::{IterQueryData, SingleEntityQueryData},
4};
5use core::marker::PhantomData;
6
7use bevy_ecs::{
8 archetype::Archetype,
9 change_detection::Tick,
10 component::{ComponentId, Components},
11 prelude::{Entity, World},
12 query::{FilteredAccess, QueryData, QueryFilter, ReadOnlyQueryData, WorldQuery},
13 storage::{Table, TableRow},
14 world::unsafe_world_cell::UnsafeWorldCell,
15};
16
17use crate::{
18 ChangeDetectionFetch, ChangeDetectionStorage, TraitQuery, TraitQueryState, debug_unreachable,
19};
20
21pub struct OneAdded<Trait: ?Sized + TraitQuery> {
24 marker: PhantomData<&'static Trait>,
25}
26
27unsafe impl<Trait: ?Sized + TraitQuery> IterQueryData for OneAdded<Trait> {}
28unsafe impl<Trait: ?Sized + TraitQuery> SingleEntityQueryData for OneAdded<Trait> {}
29unsafe impl<Trait: ?Sized + TraitQuery> QueryData for OneAdded<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> = bool;
36
37 fn shrink<'wlong: 'wshort, 'wshort, 's>(
38 item: Self::Item<'wlong, 's>,
39 ) -> Self::Item<'wshort, 's> {
40 item
41 }
42
43 #[inline(always)]
44 unsafe fn fetch<'w, 's>(
45 _state: &'s Self::State,
46 fetch: &mut Self::Fetch<'w>,
47 entity: Entity,
48 table_row: TableRow,
49 ) -> Option<Self::Item<'w, 's>> {
50 unsafe {
51 let ticks_ptr = match fetch.storage {
52 ChangeDetectionStorage::Uninit => {
53 debug_unreachable()
55 }
56 ChangeDetectionStorage::Table { ticks } => ticks.get_unchecked(table_row.index()),
57 ChangeDetectionStorage::SparseSet { components } => components
58 .get_added_tick(entity)
59 .unwrap_or_else(|| debug_unreachable()),
60 };
61
62 Some(
63 (*ticks_ptr)
64 .deref()
65 .is_newer_than(fetch.last_run, fetch.this_run),
66 )
67 }
68 }
69
70 fn iter_access(
71 _state: &Self::State,
72 ) -> impl Iterator<Item = bevy_ecs::query::EcsAccessType<'_>> {
73 core::iter::empty()
74 }
75}
76
77unsafe impl<Trait: ?Sized + TraitQuery> WorldQuery for OneAdded<Trait> {
78 type Fetch<'w> = ChangeDetectionFetch<'w>;
79 type State = TraitQueryState<Trait>;
80
81 unsafe fn init_fetch<'w>(
82 world: UnsafeWorldCell<'w>,
83 _state: &Self::State,
84 last_run: Tick,
85 this_run: Tick,
86 ) -> Self::Fetch<'w> {
87 unsafe {
88 Self::Fetch::<'w> {
89 storage: ChangeDetectionStorage::Uninit,
90 sparse_sets: &world.storages().sparse_sets,
91 last_run,
92 this_run,
93 }
94 }
95 }
96
97 const IS_DENSE: bool = false;
100
101 #[inline]
102 unsafe fn set_archetype<'w>(
103 fetch: &mut Self::Fetch<'w>,
104 state: &Self::State,
105 _archetype: &'w Archetype,
106 table: &'w Table,
107 ) {
108 unsafe {
109 for &component in &*state.components {
112 if let Some(added) = table.get_added_ticks_slice_for(component) {
113 fetch.storage = ChangeDetectionStorage::Table {
114 ticks: added.into(),
115 };
116 return;
117 }
118 }
119 for &component in &*state.components {
120 if let Some(components) = fetch.sparse_sets.get(component) {
121 fetch.storage = ChangeDetectionStorage::SparseSet { components };
122 return;
123 }
124 }
125 debug_unreachable()
127 }
128 }
129
130 #[inline]
131 unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) {
132 unsafe {
133 debug_unreachable()
135 }
136 }
137
138 #[inline]
139 fn update_component_access(state: &Self::State, access: &mut FilteredAccess) {
140 let mut new_access = access.clone();
141 let mut not_first = false;
142 for &component in &*state.components {
143 assert!(
144 !access.access().has_write(component),
145 "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
146 core::any::type_name::<Trait>(),
147 );
148 if not_first {
149 let mut intermediate = access.clone();
150 intermediate.add_read(component);
151 new_access.append_or(&intermediate);
152 new_access.extend_access(&intermediate);
153 } else {
154 new_access.and_with(component);
155 new_access.access_mut().add_read(component);
156 not_first = true;
157 }
158 }
159 *access = new_access;
160 }
161
162 #[inline]
163 fn init_state(world: &mut World) -> Self::State {
164 TraitQueryState::init(world)
165 }
166
167 #[inline]
168 fn get_state(_: &Components) -> Option<Self::State> {
169 panic!(
171 "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"
172 );
173 }
174
175 fn matches_component_set(
176 state: &Self::State,
177 set_contains_id: &impl Fn(ComponentId) -> bool,
178 ) -> bool {
179 state.matches_component_set_one(set_contains_id)
180 }
181
182 #[inline]
183 fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
184 fetch
185 }
186}
187
188unsafe impl<Trait: ?Sized + TraitQuery> ReadOnlyQueryData for OneAdded<Trait> {}
190unsafe impl<Trait: ?Sized + TraitQuery> QueryFilter for OneAdded<Trait> {
191 const IS_ARCHETYPAL: bool = false;
192 unsafe fn filter_fetch(
193 state: &Self::State,
194 fetch: &mut Self::Fetch<'_>,
195 entity: Entity,
196 table_row: TableRow,
197 ) -> bool {
198 unsafe { <Self as QueryData>::fetch(state, fetch, entity, table_row) }
199 .is_some_and(|inner_true| inner_true)
200 }
201}