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 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
31 type Item<'w, 's> = Ref<'w, Trait>;
32
33 #[inline]
34 fn shrink<'wlong: 'wshort, 'wshort, 's>(
35 item: QueryItem<'wlong, 's, Self>,
36 ) -> QueryItem<'wshort, 's, Self> {
37 item
38 }
39
40 #[inline]
41 unsafe fn fetch<'w, 's>(
42 _state: &'s Self::State,
43 fetch: &mut Self::Fetch<'w>,
44 entity: Entity,
45 table_row: TableRow,
46 ) -> Self::Item<'w, 's> {
47 unsafe {
48 let table_row = table_row.index();
49 let (dyn_ctor, ptr, added, changed, location) = match fetch.storage {
50 FetchStorage::Uninit => debug_unreachable(),
53 FetchStorage::Table {
54 column,
55 added_ticks,
56 changed_ticks,
57 location,
58 meta,
59 } => {
60 let ptr = column.byte_add(table_row * meta.size_bytes);
61 (
62 meta.dyn_ctor,
63 ptr,
64 added_ticks.get(table_row).deref(),
67 changed_ticks.get(table_row).deref(),
68 location,
69 )
70 }
71 FetchStorage::SparseSet { components, meta } => {
72 let (ptr, ticks, location) = components
73 .get_with_ticks(entity)
74 .unwrap_or_else(|| debug_unreachable());
75 (
76 meta.dyn_ctor,
77 ptr,
78 ticks.added.deref(),
81 ticks.changed.deref(),
82 location,
83 )
84 }
85 };
86
87 Ref::new(
88 dyn_ctor.cast(ptr),
89 added,
90 changed,
91 fetch.last_run,
92 fetch.this_run,
93 location.map(|loc| loc.deref()),
94 )
95 }
96 }
97}
98
99unsafe impl<Trait: ?Sized + TraitQuery> ReadOnlyQueryData for One<&Trait> {}
100
101unsafe impl<Trait: ?Sized + TraitQuery> WorldQuery for One<&Trait> {
104 type Fetch<'w> = OneTraitFetch<'w, Trait>;
105 type State = TraitQueryState<Trait>;
106
107 #[inline]
108 unsafe fn init_fetch<'w>(
109 world: UnsafeWorldCell<'w>,
110 _state: &Self::State,
111 _last_run: Tick,
112 _this_run: Tick,
113 ) -> OneTraitFetch<'w, Trait> {
114 unsafe {
115 OneTraitFetch {
116 storage: FetchStorage::Uninit,
117 last_run: Tick::new(0),
118 sparse_sets: &world.storages().sparse_sets,
119 this_run: Tick::new(0),
120 }
121 }
122 }
123
124 const IS_DENSE: bool = false;
125 #[inline]
128 unsafe fn set_archetype<'w>(
129 fetch: &mut OneTraitFetch<'w, Trait>,
130 state: &Self::State,
131 _archetype: &'w bevy_ecs::archetype::Archetype,
132 table: &'w bevy_ecs::storage::Table,
133 ) {
134 unsafe {
135 let row = TableRow::new(0_u16.into());
141 for (&component_id, &meta) in zip_exact(&*state.components, &*state.meta) {
142 if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
143 fetch.storage = table_storage;
144 return;
145 }
146 }
147 for (&component, &meta) in zip_exact(&*state.components, &*state.meta) {
148 if let Some(sparse_set) = fetch.sparse_sets.get(component) {
149 fetch.storage = FetchStorage::SparseSet {
150 components: sparse_set,
151 meta,
152 };
153 return;
154 }
155 }
156 debug_unreachable()
158 }
159 }
160
161 #[inline]
162 unsafe fn set_table<'w>(
163 fetch: &mut OneTraitFetch<'w, Trait>,
164 state: &Self::State,
165 table: &'w bevy_ecs::storage::Table,
166 ) {
167 unsafe {
168 let row = TableRow::new(0_u16.into());
173 for (&component_id, &meta) in std::iter::zip(&*state.components, &*state.meta) {
174 if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
175 fetch.storage = table_storage;
176 return;
177 }
178 }
179 debug_unreachable()
181 }
182 }
183
184 #[inline]
185 fn update_component_access(state: &Self::State, access: &mut bevy_ecs::query::FilteredAccess) {
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!(
217 "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"
218 );
219 }
220
221 #[inline]
222 fn matches_component_set(
223 state: &Self::State,
224 set_contains_id: &impl Fn(ComponentId) -> bool,
225 ) -> bool {
226 state.matches_component_set_one(set_contains_id)
227 }
228
229 #[inline]
230 fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
231 fetch
232 }
233}
234
235unsafe impl<'a, Trait: ?Sized + TraitQuery> QueryData for One<&'a mut Trait> {
236 type ReadOnly = One<&'a Trait>;
237
238 const IS_READ_ONLY: bool = false;
239
240 type Item<'w, 's> = Mut<'w, Trait>;
241
242 #[inline]
243 fn shrink<'wlong: 'wshort, 'wshort, 's>(
244 item: QueryItem<'wlong, 's, Self>,
245 ) -> QueryItem<'wshort, 's, Self> {
246 item
247 }
248
249 #[inline]
250 unsafe fn fetch<'w>(
251 _state: &Self::State,
252 fetch: &mut Self::Fetch<'w>,
253 entity: Entity,
254 table_row: TableRow,
255 ) -> Mut<'w, Trait> {
256 unsafe {
257 let table_row = table_row.index();
258 let (dyn_ctor, ptr, added, changed, location) = match fetch.storage {
259 FetchStorage::Uninit => debug_unreachable(),
262 FetchStorage::Table {
263 column,
264 added_ticks,
265 changed_ticks,
266 location,
267 meta,
268 } => {
269 let ptr = column.byte_add(table_row * meta.size_bytes);
270 (
271 meta.dyn_ctor,
272 ptr.assert_unique(),
276 added_ticks.get(table_row).deref_mut(),
279 changed_ticks.get(table_row).deref_mut(),
280 location,
281 )
282 }
283 FetchStorage::SparseSet { components, meta } => {
284 let (ptr, ticks, location) = components
285 .get_with_ticks(entity)
286 .unwrap_or_else(|| debug_unreachable());
287 (
288 meta.dyn_ctor,
289 ptr.assert_unique(),
293 ticks.added.deref_mut(),
296 ticks.changed.deref_mut(),
297 location,
298 )
299 }
300 };
301
302 Mut::new(
303 dyn_ctor.cast_mut(ptr),
304 added,
305 changed,
306 fetch.last_run,
307 fetch.this_run,
308 location.map(|loc| loc.deref_mut()),
309 )
310 }
311 }
312}
313
314unsafe impl<Trait: ?Sized + TraitQuery> WorldQuery for One<&mut Trait> {
317 type Fetch<'w> = OneTraitFetch<'w, Trait>;
318 type State = TraitQueryState<Trait>;
319
320 #[inline]
321 unsafe fn init_fetch<'w>(
322 world: UnsafeWorldCell<'w>,
323 _state: &Self::State,
324 last_run: Tick,
325 this_run: Tick,
326 ) -> OneTraitFetch<'w, Trait> {
327 unsafe {
328 OneTraitFetch {
329 storage: FetchStorage::Uninit,
330 sparse_sets: &world.storages().sparse_sets,
331 last_run,
332 this_run,
333 }
334 }
335 }
336
337 const IS_DENSE: bool = false;
338
339 #[inline]
340 unsafe fn set_archetype<'w>(
341 fetch: &mut OneTraitFetch<'w, Trait>,
342 state: &Self::State,
343 _archetype: &'w bevy_ecs::archetype::Archetype,
344 table: &'w bevy_ecs::storage::Table,
345 ) {
346 unsafe {
347 let row = TableRow::new(0_u16.into());
352 for (&component_id, &meta) in zip_exact(&*state.components, &*state.meta) {
353 if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
354 fetch.storage = table_storage;
355 return;
356 }
357 }
358 for (&component, &meta) in zip_exact(&*state.components, &*state.meta) {
359 if let Some(sparse_set) = fetch.sparse_sets.get(component) {
360 fetch.storage = FetchStorage::SparseSet {
361 components: sparse_set,
362 meta,
363 };
364 return;
365 }
366 }
367 debug_unreachable()
369 }
370 }
371
372 #[inline]
373 unsafe fn set_table<'w>(
374 fetch: &mut OneTraitFetch<'w, Trait>,
375 state: &Self::State,
376 table: &'w bevy_ecs::storage::Table,
377 ) {
378 unsafe {
379 let row = TableRow::new(0_u16.into());
384 for (&component_id, &meta) in std::iter::zip(&*state.components, &*state.meta) {
385 if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
386 fetch.storage = table_storage;
387 return;
388 }
389 }
390 debug_unreachable()
392 }
393 }
394
395 #[inline]
396 fn update_component_access(state: &Self::State, access: &mut bevy_ecs::query::FilteredAccess) {
397 let mut new_access = access.clone();
398 let mut not_first = false;
399 for &component in &*state.components {
400 assert!(
401 !access.access().has_component_write(component),
402 "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.",
403 std::any::type_name::<Trait>(),
404 );
405 if not_first {
406 let mut intermediate = access.clone();
407 intermediate.add_component_write(component);
408 new_access.append_or(&intermediate);
409 new_access.extend_access(&intermediate);
410 } else {
411 new_access.and_with(component);
412 new_access.access_mut().add_component_write(component);
413 not_first = true;
414 }
415 }
416 *access = new_access;
417 }
418
419 #[inline]
420 fn init_state(world: &mut World) -> Self::State {
421 TraitQueryState::init(world)
422 }
423
424 #[inline]
425 fn get_state(_: &Components) -> Option<Self::State> {
426 panic!(
428 "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"
429 );
430 }
431
432 #[inline]
433 fn matches_component_set(
434 state: &Self::State,
435 set_contains_id: &impl Fn(ComponentId) -> bool,
436 ) -> bool {
437 state.matches_component_set_one(set_contains_id)
438 }
439
440 #[inline]
441 fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
442 fetch
443 }
444}
445
446#[inline]
448unsafe fn get_table_fetch_data<Trait: ?Sized + TraitQuery>(
449 table: &'_ bevy_ecs::storage::Table,
450 component_id: ComponentId,
451 row: TableRow,
452 meta: TraitImplMeta<Trait>,
453) -> Option<FetchStorage<'_, Trait>> {
454 unsafe {
455 let ptr = table.get_component(component_id, row)?;
456 let location = table.get_changed_by(component_id, row).transpose()?;
457 let added = table.get_added_ticks_slice_for(component_id)?;
458 let changed = table.get_changed_ticks_slice_for(component_id)?;
459 Some(FetchStorage::Table {
460 column: ptr,
461 added_ticks: added.into(),
462 changed_ticks: changed.into(),
463 location,
464 meta,
465 })
466 }
467}