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