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