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