1use bevy_ecs::change_detection::{Mut, Ref};
2use bevy_ecs::entity::Entity;
3use bevy_ecs::prelude::World;
4use bevy_ecs::ptr::UnsafeCellDeref;
5use bevy_ecs::{
6 component::{ComponentId, Components, Tick},
7 query::{QueryData, QueryItem, ReadOnlyQueryData, WorldQuery},
8 storage::TableRow,
9 world::unsafe_world_cell::UnsafeWorldCell,
10};
11
12use crate::TraitImplMeta;
13use crate::{
14 debug_unreachable, one::FetchStorage, zip_exact, OneTraitFetch, TraitQuery, TraitQueryState,
15};
16
17pub struct One<T>(pub T);
25
26unsafe impl<Trait: ?Sized + TraitQuery> QueryData for One<&Trait> {
27 type ReadOnly = Self;
28
29 const IS_READ_ONLY: bool = true;
30
31 type Item<'w> = Ref<'w, Trait>;
32
33 #[inline]
34 fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> {
35 item
36 }
37
38 #[inline]
39 unsafe fn fetch<'w>(
40 fetch: &mut Self::Fetch<'w>,
41 entity: Entity,
42 table_row: TableRow,
43 ) -> Self::Item<'w> {
44 unsafe {
45 let table_row = table_row.as_usize();
46 let (dyn_ctor, ptr, added, changed, location) = match fetch.storage {
47 FetchStorage::Uninit => debug_unreachable(),
50 FetchStorage::Table {
51 column,
52 added_ticks,
53 changed_ticks,
54 location,
55 meta,
56 } => {
57 let ptr = column.byte_add(table_row * meta.size_bytes);
58 (
59 meta.dyn_ctor,
60 ptr,
61 added_ticks.get(table_row).deref(),
64 changed_ticks.get(table_row).deref(),
65 location,
66 )
67 }
68 FetchStorage::SparseSet { components, meta } => {
69 let (ptr, ticks, location) = components
70 .get_with_ticks(entity)
71 .unwrap_or_else(|| debug_unreachable());
72 (
73 meta.dyn_ctor,
74 ptr,
75 ticks.added.deref(),
78 ticks.changed.deref(),
79 location,
80 )
81 }
82 };
83
84 Ref::new(
85 dyn_ctor.cast(ptr),
86 added,
87 changed,
88 fetch.last_run,
89 fetch.this_run,
90 location.map(|loc| loc.deref()),
91 )
92 }
93 }
94}
95
96unsafe impl<Trait: ?Sized + TraitQuery> ReadOnlyQueryData for One<&Trait> {}
97
98unsafe impl<Trait: ?Sized + TraitQuery> WorldQuery for One<&Trait> {
101 type Fetch<'w> = OneTraitFetch<'w, Trait>;
102 type State = TraitQueryState<Trait>;
103
104 #[inline]
105 unsafe fn init_fetch<'w>(
106 world: UnsafeWorldCell<'w>,
107 _state: &Self::State,
108 _last_run: Tick,
109 _this_run: Tick,
110 ) -> OneTraitFetch<'w, Trait> {
111 unsafe {
112 OneTraitFetch {
113 storage: FetchStorage::Uninit,
114 last_run: Tick::new(0),
115 sparse_sets: &world.storages().sparse_sets,
116 this_run: Tick::new(0),
117 }
118 }
119 }
120
121 const IS_DENSE: bool = false;
122 #[inline]
125 unsafe fn set_archetype<'w>(
126 fetch: &mut OneTraitFetch<'w, Trait>,
127 state: &Self::State,
128 _archetype: &'w bevy_ecs::archetype::Archetype,
129 table: &'w bevy_ecs::storage::Table,
130 ) {
131 unsafe {
132 let row = TableRow::from_usize(0);
138 for (&component_id, &meta) in zip_exact(&*state.components, &*state.meta) {
139 if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
140 fetch.storage = table_storage;
141 return;
142 }
143 }
144 for (&component, &meta) in zip_exact(&*state.components, &*state.meta) {
145 if let Some(sparse_set) = fetch.sparse_sets.get(component) {
146 fetch.storage = FetchStorage::SparseSet {
147 components: sparse_set,
148 meta,
149 };
150 return;
151 }
152 }
153 debug_unreachable()
155 }
156 }
157
158 #[inline]
159 unsafe fn set_table<'w>(
160 fetch: &mut OneTraitFetch<'w, Trait>,
161 state: &Self::State,
162 table: &'w bevy_ecs::storage::Table,
163 ) {
164 unsafe {
165 let row = TableRow::from_usize(0);
170 for (&component_id, &meta) in std::iter::zip(&*state.components, &*state.meta) {
171 if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
172 fetch.storage = table_storage;
173 return;
174 }
175 }
176 debug_unreachable()
178 }
179 }
180
181 #[inline]
182 fn update_component_access(
183 state: &Self::State,
184 access: &mut bevy_ecs::query::FilteredAccess<ComponentId>,
185 ) {
186 let mut new_access = access.clone();
187 let mut not_first = false;
188 for &component in &*state.components {
189 assert!(
190 !access.access().has_component_write(component),
191 "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
192 std::any::type_name::<Trait>(),
193 );
194 if not_first {
195 let mut intermediate = access.clone();
196 intermediate.add_component_read(component);
197 new_access.append_or(&intermediate);
198 new_access.extend_access(&intermediate);
199 } else {
200 new_access.and_with(component);
201 new_access.access_mut().add_component_read(component);
202 not_first = true;
203 }
204 }
205 *access = new_access;
206 }
207
208 #[inline]
209 fn init_state(world: &mut World) -> Self::State {
210 TraitQueryState::init(world)
211 }
212
213 #[inline]
214 fn get_state(_: &Components) -> Option<Self::State> {
215 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");
217 }
218
219 #[inline]
220 fn matches_component_set(
221 state: &Self::State,
222 set_contains_id: &impl Fn(ComponentId) -> bool,
223 ) -> bool {
224 state.matches_component_set_one(set_contains_id)
225 }
226
227 #[inline]
228 fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
229 fetch
230 }
231}
232
233unsafe impl<'a, Trait: ?Sized + TraitQuery> QueryData for One<&'a mut Trait> {
234 type ReadOnly = One<&'a Trait>;
235
236 const IS_READ_ONLY: bool = false;
237
238 type Item<'w> = Mut<'w, Trait>;
239
240 #[inline]
241 fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> {
242 item
243 }
244
245 #[inline]
246 unsafe fn fetch<'w>(
247 fetch: &mut Self::Fetch<'w>,
248 entity: Entity,
249 table_row: TableRow,
250 ) -> Mut<'w, Trait> {
251 unsafe {
252 let table_row = table_row.as_usize();
253 let (dyn_ctor, ptr, added, changed, location) = match fetch.storage {
254 FetchStorage::Uninit => debug_unreachable(),
257 FetchStorage::Table {
258 column,
259 added_ticks,
260 changed_ticks,
261 location,
262 meta,
263 } => {
264 let ptr = column.byte_add(table_row * meta.size_bytes);
265 (
266 meta.dyn_ctor,
267 ptr.assert_unique(),
271 added_ticks.get(table_row).deref_mut(),
274 changed_ticks.get(table_row).deref_mut(),
275 location,
276 )
277 }
278 FetchStorage::SparseSet { components, meta } => {
279 let (ptr, ticks, location) = components
280 .get_with_ticks(entity)
281 .unwrap_or_else(|| debug_unreachable());
282 (
283 meta.dyn_ctor,
284 ptr.assert_unique(),
288 ticks.added.deref_mut(),
291 ticks.changed.deref_mut(),
292 location,
293 )
294 }
295 };
296
297 Mut::new(
298 dyn_ctor.cast_mut(ptr),
299 added,
300 changed,
301 fetch.last_run,
302 fetch.this_run,
303 location.map(|loc| loc.deref_mut()),
304 )
305 }
306 }
307}
308
309unsafe impl<Trait: ?Sized + TraitQuery> WorldQuery for One<&mut Trait> {
312 type Fetch<'w> = OneTraitFetch<'w, Trait>;
313 type State = TraitQueryState<Trait>;
314
315 #[inline]
316 unsafe fn init_fetch<'w>(
317 world: UnsafeWorldCell<'w>,
318 _state: &Self::State,
319 last_run: Tick,
320 this_run: Tick,
321 ) -> OneTraitFetch<'w, Trait> {
322 unsafe {
323 OneTraitFetch {
324 storage: FetchStorage::Uninit,
325 sparse_sets: &world.storages().sparse_sets,
326 last_run,
327 this_run,
328 }
329 }
330 }
331
332 const IS_DENSE: bool = false;
333
334 #[inline]
335 unsafe fn set_archetype<'w>(
336 fetch: &mut OneTraitFetch<'w, Trait>,
337 state: &Self::State,
338 _archetype: &'w bevy_ecs::archetype::Archetype,
339 table: &'w bevy_ecs::storage::Table,
340 ) {
341 unsafe {
342 let row = TableRow::from_usize(0);
347 for (&component_id, &meta) in zip_exact(&*state.components, &*state.meta) {
348 if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
349 fetch.storage = table_storage;
350 return;
351 }
352 }
353 for (&component, &meta) in zip_exact(&*state.components, &*state.meta) {
354 if let Some(sparse_set) = fetch.sparse_sets.get(component) {
355 fetch.storage = FetchStorage::SparseSet {
356 components: sparse_set,
357 meta,
358 };
359 return;
360 }
361 }
362 debug_unreachable()
364 }
365 }
366
367 #[inline]
368 unsafe fn set_table<'w>(
369 fetch: &mut OneTraitFetch<'w, Trait>,
370 state: &Self::State,
371 table: &'w bevy_ecs::storage::Table,
372 ) {
373 unsafe {
374 let row = TableRow::from_usize(0);
379 for (&component_id, &meta) in std::iter::zip(&*state.components, &*state.meta) {
380 if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
381 fetch.storage = table_storage;
382 return;
383 }
384 }
385 debug_unreachable()
387 }
388 }
389
390 #[inline]
391 fn update_component_access(
392 state: &Self::State,
393 access: &mut bevy_ecs::query::FilteredAccess<ComponentId>,
394 ) {
395 let mut new_access = access.clone();
396 let mut not_first = false;
397 for &component in &*state.components {
398 assert!(
399 !access.access().has_component_write(component),
400 "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.",
401 std::any::type_name::<Trait>(),
402 );
403 if not_first {
404 let mut intermediate = access.clone();
405 intermediate.add_component_write(component);
406 new_access.append_or(&intermediate);
407 new_access.extend_access(&intermediate);
408 } else {
409 new_access.and_with(component);
410 new_access.access_mut().add_component_write(component);
411 not_first = true;
412 }
413 }
414 *access = new_access;
415 }
416
417 #[inline]
418 fn init_state(world: &mut World) -> Self::State {
419 TraitQueryState::init(world)
420 }
421
422 #[inline]
423 fn get_state(_: &Components) -> Option<Self::State> {
424 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");
426 }
427
428 #[inline]
429 fn matches_component_set(
430 state: &Self::State,
431 set_contains_id: &impl Fn(ComponentId) -> bool,
432 ) -> bool {
433 state.matches_component_set_one(set_contains_id)
434 }
435
436 #[inline]
437 fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
438 fetch
439 }
440}
441
442#[inline]
444unsafe fn get_table_fetch_data<Trait: ?Sized + TraitQuery>(
445 table: &bevy_ecs::storage::Table,
446 component_id: ComponentId,
447 row: TableRow,
448 meta: TraitImplMeta<Trait>,
449) -> Option<FetchStorage<Trait>> {
450 unsafe {
451 let ptr = table.get_component(component_id, row)?;
452 let location = table.get_changed_by(component_id, row).transpose()?;
453 let added = table.get_added_ticks_slice_for(component_id)?;
454 let changed = table.get_changed_ticks_slice_for(component_id)?;
455 Some(FetchStorage::Table {
456 column: ptr,
457 added_ticks: added.into(),
458 changed_ticks: changed.into(),
459 location,
460 meta,
461 })
462 }
463}