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