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