Skip to main content

gizmo_core/query/
mod.rs

1use crate::archetype::Archetype;
2use crate::entity::Entity;
3use crate::world::World;
4use std::any::TypeId;
5use std::marker::PhantomData;
6
7mod fetch;
8mod iter;
9
10pub use fetch::{FetchComponent, Mut};
11pub use iter::{QueryChunksIter, QueryIter};
12
13// =========================================================================
14// SEALED PATTERN
15// =========================================================================
16//
17// `FetchComponent` ve `WorldQuery` motorun içsel, tamamı `unsafe` metodlardan
18// oluşan DSL trait'leridir. Kullanıcının manuel impl etmesi İSTENMEZ (yanlış
19// bir impl aliasing/UB ihlali doğurur) ve cross-crate hiçbir impl yoktur.
20// Sealed supertrait deseni hem kaçak manuel impl'leri engeller hem de gelecekte
21// metod eklemeyi non-breaking yapar.
22mod sealed {
23    pub trait SealedFetch {}
24    pub trait SealedQuery {}
25    pub trait SealedReadOnly {}
26}
27
28// =========================================================================
29// WORLD QUERY TRAIT
30// =========================================================================
31
32pub trait WorldQuery: sealed::SealedQuery {
33    type StaticType: 'static;
34    type Fetch<'w>: Copy;
35    type Item<'w>;
36    type Slice<'w>;
37
38    /// # Safety
39    /// Archetype geçerli olmalı ve döndürülen fetch pointer'ı archetype'ın yaşam süresi boyunca geçerli kalmalıdır.
40    unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, system_tick: u32) -> Option<Self::Fetch<'w>>;
41    fn check_aliasing(types: &mut Vec<(TypeId, bool)>);
42    fn matches_archetype(arch: &Archetype) -> bool;
43
44    /// # Safety
45    /// `row` değeri archetype'ın eleman sayısından küçük olmalıdır.
46    unsafe fn get_item<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32) -> Self::Item<'w>;
47
48    /// # Safety
49    /// Geçerli bir fetch ve archetype sınırları içinde bir `row` sağlanmalıdır.
50    unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32, system_tick: u32) -> bool;
51
52    /// # Safety
53    /// `len` değeri archetype'ın eleman sayısını aşmamalıdır.
54    unsafe fn get_slice<'w>(fetch: Self::Fetch<'w>, len: usize) -> Self::Slice<'w>;
55
56    /// Bu query satır-başı (`filter_row`) daraltma GEREKTİRİYOR mu — yani
57    /// `matches_archetype` bilinçli olarak GENİŞ mi ve gerçek test `filter_row`'da mı?
58    /// SparseSet `With`/`Without` (matches her arketipte true) ile `Changed`/`Added`/`Or`
59    /// (doğası gereği satır-başı) için `true`. `iter_chunks` arketipin TÜM bitişik
60    /// dilimini döndürdüğünden bu filtreleri ONURLANDIRAMAZ → bu tür query'leri reddeder
61    /// (bkz. [`Query::iter_chunks`]). Tablo `With`/`Without` için `false` (matches_archetype
62    /// yeterli) → onlarla chunk iterasyonu güvenli.
63    fn has_row_filter() -> bool {
64        false
65    }
66}
67
68// =========================================================================
69// READ-ONLY QUERY MARKER
70// =========================================================================
71//
72// Marks queries that yield ONLY shared (`&T`) access — never `&mut T`. Such a query
73// is sound to construct and iterate from a shared `&World`: any number can coexist
74// because no `&mut T` ever escapes. `Mut<T>` is deliberately NOT `ReadOnlyQuery`.
75//
76// This is what makes the safe entry points sound:
77// - [`World::query`](crate::world::World::query) bounds `Q: ReadOnlyQuery`, so a
78//   mutable query can never be built from `&World` in safe code (the dual-`Mut` UB).
79// - [`Query`] gates its `&self` accessors (`iter`/`get`/`iter_chunks`/`par_for_each`)
80//   behind `ReadOnlyQuery`; mutable access goes through the `&mut self` variants, so
81//   two live `&mut T` to the same storage are impossible without `unsafe`.
82//
83// Sealed: only this crate implements it (a wrong impl on a `Mut`-bearing query would
84// reopen the hole), and the supertrait `WorldQuery` keeps it inside the sealed DSL.
85pub trait ReadOnlyQuery: WorldQuery + sealed::SealedReadOnly {}
86
87// =========================================================================
88// QUERY STRUCT
89// =========================================================================
90
91pub struct Query<'w, Q: WorldQuery + ?Sized> {
92    world: &'w World,
93    matching_archetypes: Vec<usize>,
94    _marker: PhantomData<Q>,
95}
96
97impl<'w, Q: WorldQuery> Query<'w, Q> {
98    pub(crate) fn new(world: &'w World) -> Option<Self> {
99        let mut used_types = Vec::new();
100        Q::check_aliasing(&mut used_types);
101        let matching = world
102            .archetype_index
103            .matching_archetypes_readonly(Q::matches_archetype);
104        Some(Self {
105            world,
106            matching_archetypes: matching,
107            _marker: PhantomData,
108        })
109    }
110
111    pub(crate) fn new_cached(world: &'w mut World) -> Option<Self> {
112        let mut used_types = Vec::new();
113        Q::check_aliasing(&mut used_types);
114        let matching = world
115            .archetype_index
116            .matching_archetypes(TypeId::of::<Q::StaticType>(), Q::matches_archetype)
117            .to_vec();
118        Some(Self {
119            world,
120            matching_archetypes: matching,
121            _marker: PhantomData,
122        })
123    }
124
125    // ── PRIVATE primitives ────────────────────────────────────────────────
126    // The actual fetch logic, callable from `&self`. The PUBLIC `&self` wrappers
127    // bound `Q: ReadOnlyQuery` (so a mutable `Q` can never yield `&mut T` from a
128    // shared borrow), while the `&mut self` wrappers tie the returned items to the
129    // exclusive borrow (so two live `&mut T` from one query are impossible). Keeping
130    // these private is what makes the gating airtight.
131
132    fn iter_inner<'a>(&'a self) -> QueryIter<'a, 'w, Q> {
133        QueryIter {
134            world: self.world,
135            archetype_indices: &self.matching_archetypes,
136            current_arch_idx: 0,
137            current_row: 0,
138            current_fetch: None,
139            _marker: PhantomData,
140            _marker_w: PhantomData,
141        }
142    }
143
144    fn iter_chunks_inner<'a>(&'a self) -> QueryChunksIter<'a, 'w, Q> {
145        assert!(
146            !Q::has_row_filter(),
147            "iter_chunks does not support per-row-filtered queries \
148             (sparse With/Without, Changed, Added, Or) — they need per-row narrowing that \
149             a contiguous chunk cannot express; use iter()/iter_mut() instead"
150        );
151        QueryChunksIter {
152            world: self.world,
153            archetype_indices: &self.matching_archetypes,
154            current_arch_idx: 0,
155            _marker: PhantomData,
156        }
157    }
158
159    #[inline]
160    fn get_inner<'a>(&'a self, entity_id: u32) -> Option<Q::Item<'a>> {
161        let loc = self.world.entity_location(entity_id);
162        if !loc.is_valid() {
163            return None;
164        }
165        let arch = &self.world.archetype_index.archetypes[loc.archetype_id as usize];
166        unsafe {
167            let fetch = Q::fetch_raw(self.world, arch, self.world.tick)?;
168            if !Q::filter_row(fetch, loc.row as usize, entity_id, self.world.change_ref_tick) {
169                return None;
170            }
171            Some(Q::get_item(fetch, loc.row as usize, entity_id))
172        }
173    }
174
175    fn par_inner<F>(&self, func: F)
176    where
177        F: Fn((u32, Q::Item<'_>)) + Send + Sync,
178    {
179        #[cfg(not(target_arch = "wasm32"))]
180        use rayon::prelude::*;
181        #[cfg(target_arch = "wasm32")]
182        use crate::parallel_compat::*;
183
184        // Pointer taşıyıcı wrapper — Güvenlidir çünkü Query::new() check_aliasing yapmıştır
185        #[derive(Copy, Clone)]
186        struct FetchWrapper<T>(T);
187        unsafe impl<T> Send for FetchWrapper<T> {}
188        unsafe impl<T> Sync for FetchWrapper<T> {}
189
190        impl<T: Copy> FetchWrapper<T> {
191            fn get(&self) -> T {
192                self.0
193            }
194        }
195
196        let tick = self.world.tick;
197        let ref_tick = self.world.change_ref_tick;
198        self.matching_archetypes.par_iter().for_each(|&arch_idx| {
199            let arch = &self.world.archetype_index.archetypes[arch_idx];
200            if let Some(fetch) = unsafe { Q::fetch_raw(self.world, arch, tick) } {
201                let len = arch.len();
202                let wrapped_fetch = FetchWrapper(fetch);
203                let entities_ptr = FetchWrapper(arch.entities().as_ptr());
204                let func_ref = &func;
205
206                // Her Archetype'ı cache dostu chunk'lar halinde ayırıp process ediyoruz
207                // Chunk size: 512 (Bevy benzeri)
208                (0..len)
209                    .into_par_iter()
210                    .with_min_len(512)
211                    .for_each(move |row| unsafe {
212                        let id = *entities_ptr.get().add(row);
213                        if Q::filter_row(wrapped_fetch.get(), row, id, ref_tick) {
214                            let item = Q::get_item(wrapped_fetch.get(), row, id);
215                            func_ref((id, item));
216                        }
217                    });
218            }
219        });
220    }
221
222    // ── MUTABLE accessors (available for every `Q`) ───────────────────────
223    // Each ties its result to the EXCLUSIVE `&mut self` borrow, so two live mutable
224    // views from one query can't coexist. Combined with `query_mut`/`query_unchecked`
225    // gating creation, this closes the dual-`Mut` aliasing hole for safe code.
226
227    /// Eleman-başına `Mut<T>` veren mutable iterasyon. `&mut self` aldığından aynı query
228    /// üzerinde ikinci bir canlı mutable iterasyon derleme zamanında engellenir.
229    pub fn iter_mut<'a>(&'a mut self) -> QueryIter<'a, 'w, Q> {
230        self.iter_inner()
231    }
232
233    /// **Toplu (bulk) yazma** için mutable chunk iterasyonu (`&mut [T]` döndürür).
234    ///
235    /// Ham bir dilim verdiği için hangi elemanların yazıldığını izleyemez; bu yüzden
236    /// **verilen tüm satırları temkinli (conservative) olarak "changed" işaretler.**
237    /// Bu, gerçek bir değişikliği asla KAÇIRMAZ (change detection için güvenli taraf),
238    /// ama yalnızca bir kısmını yazarsanız yazılmayanları da "changed" gösterir
239    /// (false positive). Doğru aracı seçin:
240    /// - Sadece okuyacaksanız → [`Query::iter_chunks`] (işaretlemez).
241    /// - Bir kısmını hassas işaretleyerek yazacaksanız → `iter_mut` (eleman başına `Mut`).
242    /// - Hepsini yazacaksanız → bu metot (hepsini işaretlemek zaten doğru).
243    pub fn iter_chunks_mut<'a>(&'a mut self) -> QueryChunksIter<'a, 'w, Q> {
244        self.iter_chunks_inner()
245    }
246
247    /// Ham `u32` id ile mutable erişim — generation kontrolü yapmaz (bkz. [`Query::get`]).
248    /// `&mut self` aldığından dönen `Mut` query'yi özel olarak ödünç alır; aynı anda ikinci
249    /// bir `get_mut`/`iter_mut` derlenmez.
250    #[inline]
251    pub fn get_mut(&mut self, entity_id: u32) -> Option<Q::Item<'_>> {
252        self.get_inner(entity_id)
253    }
254
255    /// Generation-doğrulamalı mutable erişim (bkz. [`Query::get_entity`]).
256    #[inline]
257    pub fn get_mut_entity(&mut self, entity: Entity) -> Option<Q::Item<'_>> {
258        if !self.world.is_alive(entity) {
259            return None;
260        }
261        self.get_inner(entity.id())
262    }
263
264    /// İş parçacığı havuzu (Work-Stealing) ile çalışan lock-free paralel mutable iterasyon.
265    pub fn par_for_each_mut<F>(&mut self, func: F)
266    where
267        F: Fn((u32, Q::Item<'_>)) + Send + Sync,
268    {
269        self.par_inner(func);
270    }
271
272    // ── Metadata (no component access → always `&self`) ───────────────────
273
274    #[inline]
275    pub fn entity_count(&self) -> usize {
276        self.matching_archetypes
277            .iter()
278            .map(|&idx| self.world.archetype_index.archetypes[idx].len())
279            .sum()
280    }
281
282    #[inline]
283    pub fn len(&self) -> usize {
284        self.entity_count()
285    }
286
287    #[inline]
288    pub fn is_empty(&self) -> bool {
289        self.entity_count() == 0
290    }
291}
292
293// ── READ-ONLY accessors (only for queries that never yield `&mut T`) ──────
294// Sound from a shared `&self` because `Q: ReadOnlyQuery` guarantees `Q::Item` is a
295// shared borrow — any number may coexist.
296impl<'w, Q: ReadOnlyQuery> Query<'w, Q> {
297    pub fn iter<'a>(&'a self) -> QueryIter<'a, 'w, Q> {
298        self.iter_inner()
299    }
300
301    /// Salt-okunur SIMD-dostu chunk iterasyonu (`&[T]` döndürür). Değişiklik tespitini
302    /// (change detection) ETKİLEMEZ — bileşenleri okumak için kullanın.
303    ///
304    /// # Panics
305    /// Satır-başı filtre GEREKTİREN bir query'de (SparseSet `With`/`Without`,
306    /// `Changed`/`Added`, `Or`) panikler: chunk iterasyonu arketipin TÜM bitişik dilimini
307    /// döndürür, bu filtreler ise satır-başı seçer (bkz. [`WorldQuery::has_row_filter`]).
308    /// Sessizce filtrelenmemiş sonuç döndürmek yerine yüksek sesle reddeder — bunun yerine
309    /// [`Query::iter`]/[`Query::iter_mut`] kullanın. (Tablo `With`/`Without` güvenlidir.)
310    pub fn iter_chunks<'a>(&'a self) -> QueryChunksIter<'a, 'w, Q> {
311        self.iter_chunks_inner()
312    }
313
314    /// Ham `u32` id ile erişim. **DİKKAT: generation kontrolü YAPMAZ.** Despawn edilip
315    /// slotu yeniden kullanılan bir id verilirse, o slottaki YENİ entity'nin verisi
316    /// döner (use-after-free benzeri sessiz hata). Elinizde bir [`Entity`] handle'ı varsa
317    /// [`Query::get_entity`] kullanın — o, generation'ı doğrular.
318    #[inline]
319    pub fn get(&self, entity_id: u32) -> Option<Q::Item<'_>> {
320        self.get_inner(entity_id)
321    }
322
323    /// Generation-doğrulamalı erişim: `entity` artık canlı değilse (despawn edilmiş veya
324    /// slotu başka bir entity'ye verilmiş) `None` döner. Stale-handle ile yanlış entity'nin
325    /// verisini okumayı engeller. Elinizde bir [`Entity`] handle'ı varsa bunu tercih edin.
326    #[inline]
327    pub fn get_entity(&self, entity: Entity) -> Option<Q::Item<'_>> {
328        if !self.world.is_alive(entity) {
329            return None;
330        }
331        self.get_inner(entity.id())
332    }
333
334    /// Belirli bir entity'nin bu query'ye ait olup olmadığını kontrol eder.
335    #[inline]
336    pub fn contains(&self, entity_id: u32) -> bool {
337        self.get_inner(entity_id).is_some()
338    }
339
340    pub fn entities<'a>(&'a self) -> impl Iterator<Item = u32> + 'a {
341        self.iter_inner().map(|(id, _)| id)
342    }
343
344    /// İş parçacığı havuzu (Work-Stealing) ile çalışan lock-free paralel iterasyon
345    pub fn par_for_each<F>(&self, func: F)
346    where
347        F: Fn((u32, Q::Item<'_>)) + Send + Sync,
348    {
349        self.par_inner(func);
350    }
351}
352
353// =========================================================================
354// ALIASING & IMPLS
355// =========================================================================
356
357/// Mutable aliasing kontrolü — aynı `TypeId`'ye iki mutable erişim varsa **UB** olur.
358///
359/// # Invariant
360/// Bir query içinde aynı component tipine birden fazla mutable erişim (`Mut<T>`)
361/// **kesinlikle yasaktır**. `Query<(Mut<Position>, Mut<Position>)>` gibi bir kullanım
362/// çalışma zamanında panic atar. Bu kontrol compile-time'da yapılamaz çünkü Rust'ın
363/// tip sistemi `TypeId` eşitliğini const-context'te karşılaştıramaz.
364///
365/// # Güvenli Kullanım
366/// - `Query<(&Position, Mut<Velocity>)>` → ✅ (farklı tipler)
367/// - `Query<(Mut<Position>, Mut<Velocity>)>` → ✅ (farklı tipler)
368/// - `Query<(Mut<Position>, Mut<Position>)>` → ❌ PANIC!
369/// - `Query<(&Position, &Position)>` → ✅ (ikisi de immutable — aliasing güvenli)
370#[inline]
371fn check(tid: TypeId, is_mut: bool, types: &mut Vec<(TypeId, bool)>) {
372    for &(existing_tid, existing_mut) in types.iter() {
373        if existing_tid == tid && (existing_mut || is_mut) {
374            panic!(
375                "Query aliasing UB detected! Component TypeId {:?} is accessed mutably more than once \
376                 in the same query. This would cause undefined behavior. \
377                 Use separate queries for components of the same type that need independent mutable access.",
378                tid
379            );
380        }
381    }
382    types.push((tid, is_mut));
383}
384
385/// Archetype-level match shared by every component-keyed filter. SparseSet storage is
386/// stored outside archetypes, so `matches_archetype` is intentionally WIDE there (every
387/// archetype; the real per-row test lives in `filter_row`). For Table storage it matches
388/// on presence: `want_present` is `true` for `With`/`Changed`/`Added`/`&T`, `false` for
389/// `Without`. Centralizing this kills the copy-pasted `if sparse {true} else {has}` that
390/// diverged across impls (the round-1/2 sibling-divergence bug class).
391#[inline]
392fn arch_matches<T: crate::component::Component>(arch: &Archetype, want_present: bool) -> bool {
393    if T::storage_type() == crate::component::StorageType::SparseSet {
394        true
395    } else {
396        arch.has_component(TypeId::of::<T>()) == want_present
397    }
398}
399
400/// Generates the `WorldQuery` impl for a change-detection filter (`Changed`/`Added`).
401/// They differ ONLY in which `ComponentTicks` field they read, so they share one body —
402/// adding a new tick filter can't forget `check_aliasing` (the data-race guard) or
403/// `has_row_filter` (the iter_chunks guard).
404macro_rules! impl_tick_filter {
405    ($(#[$meta:meta])* $name:ident, $field:ident) => {
406        $(#[$meta])*
407        pub struct $name<T>(PhantomData<T>);
408
409        impl<T: crate::component::Component> sealed::SealedQuery for $name<T> {}
410        // Tick filters carry no data (`Item = ()`) → read-only.
411        impl<T: crate::component::Component> sealed::SealedReadOnly for $name<T> {}
412        impl<T: crate::component::Component> ReadOnlyQuery for $name<T> {}
413        impl<T: crate::component::Component> WorldQuery for $name<T> {
414            type StaticType = $name<T>;
415            // (table ticks ptr, or the sparse set ptr for SparseSet storage)
416            type Fetch<'w> = (
417                *const crate::archetype::ComponentTicks,
418                Option<*const crate::archetype::sparse_set::ComponentSparseSet>,
419            );
420            type Item<'w> = ();
421            type Slice<'w> = ();
422
423            unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, _tick: u32) -> Option<Self::Fetch<'w>> {
424                if T::storage_type() == crate::component::StorageType::SparseSet {
425                    let set = world.sparse_sets.get(&TypeId::of::<T>())?;
426                    Some((std::ptr::null(), Some(set as *const _)))
427                } else {
428                    let col = arch.get_column(TypeId::of::<T>())?;
429                    Some((col.ticks_ptr(), None))
430                }
431            }
432
433            fn check_aliasing(types: &mut Vec<(TypeId, bool)>) {
434                // Tick filters READ T's ComponentTicks — the same memory `Mut<T>` writes in
435                // deref_mut. Declare a READ so the scheduler can't co-batch a `Mut<T>` writer
436                // (unsynchronized read+write = data race).
437                check(TypeId::of::<T>(), false, types);
438            }
439
440            fn matches_archetype(arch: &Archetype) -> bool {
441                arch_matches::<T>(arch, true)
442            }
443
444            unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32, tick: u32) -> bool {
445                // `tick` = change_ref_tick (last run); rows stamped after it match.
446                if let Some(set_ptr) = fetch.1 {
447                    (*set_ptr).ticks_for(entity_id).is_some_and(|t| t.$field > tick)
448                } else {
449                    (*fetch.0.add(row)).$field > tick
450                }
451            }
452
453            unsafe fn get_item<'w>(_f: Self::Fetch<'w>, _r: usize, _e: u32) -> Self::Item<'w> {}
454            unsafe fn get_slice<'w>(_f: Self::Fetch<'w>, _l: usize) -> Self::Slice<'w> {}
455
456            fn has_row_filter() -> bool {
457                true // the tick test lives entirely in filter_row
458            }
459        }
460    };
461}
462
463/// Generates the `WorldQuery` impl for a presence filter (`With`/`Without`). They differ
464/// ONLY by the `$present` polarity, so one body guarantees they stay in lockstep — the
465/// sparse per-row check, `matches_archetype`, and `has_row_filter` can't diverge.
466macro_rules! impl_presence_filter {
467    ($(#[$meta:meta])* $name:ident, $present:expr) => {
468        $(#[$meta])*
469        pub struct $name<T>(PhantomData<T>);
470
471        impl<T: crate::component::Component> sealed::SealedQuery for $name<T> {}
472        // Presence filters carry no data (`Item = ()`) → read-only.
473        impl<T: crate::component::Component> sealed::SealedReadOnly for $name<T> {}
474        impl<T: crate::component::Component> ReadOnlyQuery for $name<T> {}
475        impl<T: crate::component::Component> WorldQuery for $name<T> {
476            type StaticType = $name<T>;
477            // (is_sparse, sparse set ptr). Table storage is always `(false, None)`.
478            type Fetch<'w> = (
479                bool,
480                Option<*const crate::archetype::sparse_set::ComponentSparseSet>,
481            );
482            type Item<'w> = ();
483            type Slice<'w> = ();
484
485            unsafe fn fetch_raw<'w>(world: &'w World, _arch: &Archetype, _tick: u32) -> Option<Self::Fetch<'w>> {
486                if T::storage_type() == crate::component::StorageType::SparseSet {
487                    Some((true, world.sparse_sets.get(&TypeId::of::<T>()).map(|s| s as *const _)))
488                } else {
489                    Some((false, None))
490                }
491            }
492
493            fn check_aliasing(_types: &mut Vec<(TypeId, bool)>) {}
494
495            fn matches_archetype(arch: &Archetype) -> bool {
496                arch_matches::<T>(arch, $present)
497            }
498
499            unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, _row: usize, entity_id: u32, _tick: u32) -> bool {
500                // Table: matches_archetype already selected by presence → always true.
501                // Sparse: matches_archetype is wide → test actual presence per row.
502                match fetch {
503                    (false, _) => true,
504                    (true, Some(set_ptr)) => (*set_ptr).contains(entity_id) == $present,
505                    (true, None) => !$present, // no sparse set yet → nobody has the component
506                }
507            }
508
509            unsafe fn get_item<'w>(_f: Self::Fetch<'w>, _r: usize, _e: u32) -> Self::Item<'w> {}
510            unsafe fn get_slice<'w>(_f: Self::Fetch<'w>, _l: usize) -> Self::Slice<'w> {}
511
512            fn has_row_filter() -> bool {
513                // Sparse needs the per-row presence test; table is archetype-level only.
514                T::storage_type() == crate::component::StorageType::SparseSet
515            }
516        }
517    };
518}
519
520impl<T0: FetchComponent> sealed::SealedQuery for T0 where T0::Component: crate::component::Component {}
521impl<T0: FetchComponent> WorldQuery for T0 where T0::Component: crate::component::Component {
522    type StaticType = T0::Component;
523    type Fetch<'w> = T0::Fetch<'w>;
524    type Item<'w> = T0::Item<'w>;
525    type Slice<'w> = T0::Slice<'w>;
526
527    unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, tick: u32) -> Option<Self::Fetch<'w>> {
528        T0::fetch_raw(world, arch, tick)
529    }
530    fn check_aliasing(types: &mut Vec<(TypeId, bool)>) {
531        check(TypeId::of::<T0::Component>(), T0::IS_MUT, types);
532    }
533    fn matches_archetype(arch: &Archetype) -> bool {
534        arch_matches::<T0::Component>(arch, true)
535    }
536
537    unsafe fn get_item<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32) -> Self::Item<'w> {
538        T0::get_item(fetch, row, entity_id)
539    }
540
541    unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, _row: usize, entity_id: u32, _tick: u32) -> bool {
542        // SparseSet bileşenleri için `matches_archetype` her arketipte `true` döndüğünden
543        // satır-başı varlık kontrolü ŞART (yoksa get_item sparse set'i sınır-dışı indeksler).
544        // Table depolamada `contains_entity` daima `true`.
545        T0::contains_entity(fetch, entity_id)
546    }
547
548    unsafe fn get_slice<'w>(fetch: Self::Fetch<'w>, len: usize) -> Self::Slice<'w> {
549        T0::get_slice(fetch, len)
550    }
551}
552
553// `&T` yields shared access only → read-only. `Mut<T>` (also a `FetchComponent`) is
554// pointedly excluded: no `SealedReadOnly`/`ReadOnlyQuery` impl exists for it.
555impl<T: crate::component::Component> sealed::SealedReadOnly for &T {}
556impl<T: crate::component::Component> ReadOnlyQuery for &T {}
557
558impl_tick_filter!(
559    /// Filter matching only entities whose `T` changed since the system last ran
560    /// (`deref_mut` on `Mut<T>` stamps the change tick). Use as a query operand.
561    Changed,
562    changed
563);
564
565impl_tick_filter!(
566    /// Filter matching only entities to which `T` was added since the system last ran.
567    Added,
568    added
569);
570
571macro_rules! impl_query_tuple {
572    ($($t:ident),*) => {
573        impl<$($t: WorldQuery),*> sealed::SealedQuery for ($($t,)*) {}
574        // A tuple is read-only iff EVERY element is read-only.
575        impl<$($t: ReadOnlyQuery),*> sealed::SealedReadOnly for ($($t,)*) {}
576        impl<$($t: ReadOnlyQuery),*> ReadOnlyQuery for ($($t,)*) {}
577        #[allow(non_snake_case)]
578        impl<$($t: WorldQuery),*> WorldQuery for ($($t,)*) {
579            type StaticType = ($($t::StaticType,)*);
580            type Fetch<'w> = ($($t::Fetch<'w>,)*);
581            type Item<'w> = ($($t::Item<'w>,)*);
582            type Slice<'w> = ($($t::Slice<'w>,)*);
583
584            unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, tick: u32) -> Option<Self::Fetch<'w>> {
585                Some(($($t::fetch_raw(world, arch, tick)?,)*))
586            }
587            fn check_aliasing(types: &mut Vec<(TypeId, bool)>) {
588                $($t::check_aliasing(types);)*
589            }
590            fn matches_archetype(arch: &Archetype) -> bool {
591                $($t::matches_archetype(arch) &&)* true
592            }
593            unsafe fn get_item<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32) -> Self::Item<'w> {
594                let ($($t,)*) = fetch;
595                ($($t::get_item($t, row, entity_id),)*)
596            }
597            unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32, tick: u32) -> bool {
598                let ($($t,)*) = fetch;
599                $($t::filter_row($t, row, entity_id, tick) &&)* true
600            }
601            unsafe fn get_slice<'w>(fetch: Self::Fetch<'w>, len: usize) -> Self::Slice<'w> {
602                let ($($t,)*) = fetch;
603                ($($t::get_slice($t, len),)*)
604            }
605            fn has_row_filter() -> bool {
606                $($t::has_row_filter() ||)* false
607            }
608        }
609    };
610}
611
612impl_query_tuple!(T0, T1);
613impl_query_tuple!(T0, T1, T2);
614impl_query_tuple!(T0, T1, T2, T3);
615impl_query_tuple!(T0, T1, T2, T3, T4);
616impl_query_tuple!(T0, T1, T2, T3, T4, T5);
617impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6);
618impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7);
619impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
620impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
621impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
622impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
623
624// =========================================================================
625// ADVANCED QUERY FILTERS
626// =========================================================================
627
628impl_presence_filter!(
629    /// Filter matching entities that HAVE `T` (without borrowing it). Use as a query operand.
630    With,
631    true
632);
633
634impl_presence_filter!(
635    /// Filter matching entities that do NOT have `T`. Use as a query operand.
636    Without,
637    false
638);
639
640pub struct Or<T1, T2>(PhantomData<(T1, T2)>);
641
642impl<T1: WorldQuery, T2: WorldQuery> sealed::SealedQuery for Or<T1, T2> {}
643// `Or` is itself a no-data filter; it's read-only when both operands are.
644impl<T1: ReadOnlyQuery, T2: ReadOnlyQuery> sealed::SealedReadOnly for Or<T1, T2> {}
645impl<T1: ReadOnlyQuery, T2: ReadOnlyQuery> ReadOnlyQuery for Or<T1, T2> {}
646impl<T1: WorldQuery, T2: WorldQuery> WorldQuery for Or<T1, T2> {
647    type StaticType = Or<T1::StaticType, T2::StaticType>;
648    // Each operand's fetch (or `None` when that operand doesn't apply to this archetype).
649    // `Or` is a FILTER, so it carries no data — but it must keep the operand fetches so
650    // it can evaluate their per-row `filter_row` (the part the old `()` Fetch dropped).
651    type Fetch<'w> = (Option<T1::Fetch<'w>>, Option<T2::Fetch<'w>>);
652    type Item<'w> = ();
653    type Slice<'w> = ();
654
655    unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, tick: u32) -> Option<Self::Fetch<'w>> {
656        // Fetch each operand only where it applies; `matches_archetype` gates which
657        // operand can contribute, and a `Some` fetch is the per-archetype proof of that.
658        let f1 = if T1::matches_archetype(arch) {
659            T1::fetch_raw(world, arch, tick)
660        } else {
661            None
662        };
663        let f2 = if T2::matches_archetype(arch) {
664            T2::fetch_raw(world, arch, tick)
665        } else {
666            None
667        };
668        Some((f1, f2))
669    }
670
671    fn check_aliasing(types: &mut Vec<(TypeId, bool)>) {
672        // Propagate operand access — otherwise `Or<Changed<A>, Changed<B>>` would declare
673        // NOTHING and the scheduler could race a `Mut` writer (the round-1 bug class).
674        T1::check_aliasing(types);
675        T2::check_aliasing(types);
676    }
677
678    fn matches_archetype(arch: &Archetype) -> bool {
679        T1::matches_archetype(arch) || T2::matches_archetype(arch)
680    }
681
682    unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32, tick: u32) -> bool {
683        // A row passes `Or` if EITHER applicable operand accepts it. `matches_archetype`
684        // alone is not enough: sparse `With` matches every archetype and Changed/Added do
685        // their whole test here, so the per-row `filter_row` MUST be consulted.
686        let a = fetch
687            .0
688            .is_some_and(|f| T1::filter_row(f, row, entity_id, tick));
689        let b = fetch
690            .1
691            .is_some_and(|f| T2::filter_row(f, row, entity_id, tick));
692        a || b
693    }
694    unsafe fn get_item<'w>(_fetch: Self::Fetch<'w>, _row: usize, _entity_id: u32) -> Self::Item<'w> {}
695    unsafe fn get_slice<'w>(_fetch: Self::Fetch<'w>, _len: usize) -> Self::Slice<'w> {}
696
697    fn has_row_filter() -> bool {
698        true
699    }
700}
701
702#[cfg(test)]
703mod tests {
704    use super::*;
705    use crate::impl_component;
706
707    #[derive(Debug, Clone, PartialEq)]
708    struct Position {
709        x: f32,
710        y: f32,
711    }
712    impl_component!(Position);
713
714    #[derive(Debug, Clone, PartialEq)]
715    struct Velocity {
716        x: f32,
717        y: f32,
718    }
719    impl_component!(Velocity);
720
721    /// `Query<(Mut<Position>, Mut<Position>)>` gibi aynı tipe çift mutable erişim
722    /// denemesi panic ile engellenmeli.
723    #[test]
724    #[should_panic(expected = "Query aliasing UB detected")]
725    fn test_same_type_mut_mut_panics() {
726        let mut types = Vec::new();
727        // İlk Mut<Position> — sorunsuz eklenir
728        check(TypeId::of::<Position>(), true, &mut types);
729        // İkinci Mut<Position> — PANIC olmalı!
730        check(TypeId::of::<Position>(), true, &mut types);
731    }
732
733    /// `Query<(&Position, Mut<Position>)>` — bir immutable, bir mutable aynı tipe erişim:
734    /// Bu da panic olmalı çünkü &T + &mut T alias oluşturur.
735    #[test]
736    #[should_panic(expected = "Query aliasing UB detected")]
737    fn test_same_type_ref_mut_panics() {
738        let mut types = Vec::new();
739        check(TypeId::of::<Position>(), false, &mut types); // &Position
740        check(TypeId::of::<Position>(), true, &mut types); // Mut<Position> — PANIC!
741    }
742
743    /// `Query<(Mut<Position>, Mut<Velocity>)>` — farklı tipler, sorunsuz çalışmalı.
744    #[test]
745    fn test_different_types_mut_mut_ok() {
746        let mut types = Vec::new();
747        check(TypeId::of::<Position>(), true, &mut types);
748        check(TypeId::of::<Velocity>(), true, &mut types);
749        assert_eq!(types.len(), 2);
750    }
751
752    /// `Query<(&Position, &Position)>` — aynı tipe çift immutable erişim güvenlidir.
753    #[test]
754    fn test_same_type_ref_ref_ok() {
755        let mut types = Vec::new();
756        check(TypeId::of::<Position>(), false, &mut types);
757        check(TypeId::of::<Position>(), false, &mut types);
758        assert_eq!(types.len(), 2);
759    }
760
761    /// World üzerinden Query oluşturulduğunda aliasing kontrolünün çalıştığını doğrular.
762    #[test]
763    fn test_query_new_with_valid_types() {
764        let mut world = crate::World::new();
765        world.register_component_type::<Position>();
766        world.register_component_type::<Velocity>();
767        let e = world.spawn();
768        world.add_component(e, Position { x: 1.0, y: 2.0 });
769        world.add_component(e, Velocity { x: 0.0, y: 0.0 });
770
771        // Farklı tipler — Query oluşturulabilmeli
772        let q = world.query_mut::<(Mut<Position>, Mut<Velocity>)>();
773        assert!(q.is_some());
774    }
775
776    /// `Changed<T>`/`Added<T>` artık referans tick'e (son çalıştırma) göre çalışır,
777    /// `== current_tick` değil. Kareler arası doğru raporlama doğrulanır.
778    #[test]
779    fn change_detection_is_relative_to_ref_tick() {
780        let mut world = crate::World::new();
781        world.register_component_type::<Position>();
782        let e = world.spawn();
783        world.add_component(e, Position { x: 1.0, y: 2.0 });
784
785        // Frame 1: ref=0 → ilk gözlem eklenen bileşeni görür.
786        world.begin_change_frame(0);
787        assert_eq!(world.query::<Changed<Position>>().unwrap().iter().count(), 1);
788        assert_eq!(world.query::<Added<Position>>().unwrap().iter().count(), 1);
789
790        // Frame 2: değişiklik yok → Changed boş olmalı.
791        let prev = world.tick;
792        world.begin_change_frame(prev);
793        assert_eq!(
794            world.query::<Changed<Position>>().unwrap().iter().count(),
795            0,
796            "değişiklik olmayan frame'de Changed boş olmalı (eski `==` davranışı her şeyi eşliyordu)"
797        );
798
799        // Frame 2 içinde mutasyon → Changed yeniden 1 olmalı.
800        {
801            let mut q = world.query_mut::<Mut<Position>>().unwrap();
802            for (_id, mut p) in q.iter_mut() {
803                p.x += 1.0;
804            }
805        }
806        assert_eq!(world.query::<Changed<Position>>().unwrap().iter().count(), 1);
807    }
808
809    /// `get_entity` generation'ı doğrular: despawn edilip slotu yeniden kullanılan bir
810    /// entity'nin eski handle'ı `None` döner; ham `get(id)` ise (footgun) yeni entity'nin
811    /// verisini döndürür.
812    #[test]
813    fn get_entity_rejects_stale_handle_after_despawn_reuse() {
814        let mut world = crate::World::new();
815        world.register_component_type::<Position>();
816
817        let e1 = world.spawn();
818        world.add_component(e1, Position { x: 1.0, y: 1.0 });
819        let stale = e1;
820
821        world.despawn(e1);
822
823        // Slotu yeniden kullan — aynı id, artmış generation.
824        let e2 = world.spawn();
825        world.add_component(e2, Position { x: 2.0, y: 2.0 });
826        assert_eq!(e2.id(), stale.id(), "slot yeniden kullanılmalı (aynı id)");
827        assert_ne!(e2.generation(), stale.generation(), "generation artmalı");
828
829        let q = world.query::<&Position>().unwrap();
830        // Ham id: generation kontrolü yok → yeni entity'nin verisi (footgun).
831        assert_eq!(q.get(stale.id()).map(|p| p.x), Some(2.0));
832        // Generation-doğrulamalı: stale handle reddedilir.
833        assert!(q.get_entity(stale).is_none(), "stale handle None dönmeli");
834        // Geçerli handle çalışır.
835        assert_eq!(q.get_entity(e2).map(|p| p.x), Some(2.0));
836    }
837
838    /// `iter_chunks_mut` ile yapılan toplu yazma, değişiklik tespitini tetiklemeli
839    /// (temkinli işaretleme → gerçek yazmayı asla kaçırmaz, false negative yok).
840    #[test]
841    fn iter_chunks_mut_triggers_change_detection() {
842        let mut world = crate::World::new();
843        world.register_component_type::<Position>();
844        let e = world.spawn();
845        world.add_component(e, Position { x: 1.0, y: 1.0 });
846
847        // Referansı bu tick'e ayarla ve frame'i ilerlet (Schedule'ın yaptığı gibi).
848        world.begin_change_frame(world.tick);
849        // Yazmadan önce: değişiklik yok.
850        assert_eq!(world.query::<Changed<Position>>().unwrap().iter().count(), 0);
851
852        // Chunked mutable yazma.
853        {
854            let mut q = world.query_mut::<Mut<Position>>().unwrap();
855            for (_ids, slice) in q.iter_chunks_mut() {
856                for p in slice.iter_mut() {
857                    p.x += 10.0;
858                }
859            }
860        }
861
862        // Yazmadan sonra: Changed tetiklenmeli ve değer güncellenmeli.
863        assert_eq!(world.query::<Changed<Position>>().unwrap().iter().count(), 1);
864        assert_eq!(world.query::<&Position>().unwrap().get(e.id()).map(|p| p.x), Some(11.0));
865    }
866
867    /// SparseSet bileşenlerinde `Changed`/`Added` artık gerçek tick takibi yapar
868    /// (eskiden her zaman `true` idi). Tablo bileşenleriyle aynı kareler-arası semantik.
869    #[test]
870    fn sparse_set_change_detection_tracks_ticks() {
871        #[derive(Clone, Debug, PartialEq)]
872        struct SparseComp(i32);
873        impl crate::component::Component for SparseComp {
874            fn storage_type() -> crate::component::StorageType {
875                crate::component::StorageType::SparseSet
876            }
877        }
878
879        let mut world = crate::World::new();
880        world.register_component_type::<SparseComp>();
881        let e = world.spawn();
882        world.add_component(e, SparseComp(1));
883
884        // Frame 1: ref=0 → eklenen bileşen Added ve Changed olarak görülmeli.
885        world.begin_change_frame(0);
886        assert_eq!(world.query::<Added<SparseComp>>().unwrap().iter().count(), 1);
887        assert_eq!(world.query::<Changed<SparseComp>>().unwrap().iter().count(), 1);
888
889        // Frame 2: değişiklik yok → ikisi de boş (eski davranış burada hep 1 verirdi).
890        let prev = world.tick;
891        world.begin_change_frame(prev);
892        assert_eq!(world.query::<Changed<SparseComp>>().unwrap().iter().count(), 0);
893        assert_eq!(world.query::<Added<SparseComp>>().unwrap().iter().count(), 0);
894
895        // Frame 2 içinde mutasyon → Changed yeniden tetiklenmeli.
896        {
897            let mut q = world.query_mut::<Mut<SparseComp>>().unwrap();
898            for (_id, mut c) in q.iter_mut() {
899                c.0 += 10;
900            }
901        }
902        assert_eq!(world.query::<Changed<SparseComp>>().unwrap().iter().count(), 1);
903        assert_eq!(world.query::<&SparseComp>().unwrap().get(e.id()).map(|c| c.0), Some(11));
904    }
905
906    // Sparse queries match EVERY archetype at the archetype level (data lives
907    // outside archetypes) and narrow per-row in filter_row. This exercises that
908    // narrowing with MIXED presence — some entities have the sparse component,
909    // some don't — which the single-entity tests and the all-uniform benches
910    // never cover. A narrowing bug would leak component-less entities (or read a
911    // non-existent sparse slot).
912    #[test]
913    fn sparse_query_mixed_presence_narrows_correctly() {
914        use crate::component::{Component, StorageType};
915        #[derive(Clone, Debug, PartialEq)]
916        struct TableC(i32);
917        impl Component for TableC {}
918        #[derive(Clone, Debug, PartialEq)]
919        struct SparseC(i32);
920        impl Component for SparseC {
921            fn storage_type() -> StorageType {
922                StorageType::SparseSet
923            }
924        }
925
926        let mut world = crate::World::new();
927        world.register_component_type::<TableC>();
928        world.register_component_type::<SparseC>();
929
930        // 3 entities with TableC + SparseC, 2 with only TableC.
931        for i in 0..3 {
932            let e = world.spawn();
933            world.add_component(e, TableC(i));
934            world.add_component(e, SparseC(i * 10));
935        }
936        let mut table_only = Vec::new();
937        for i in 3..5 {
938            let e = world.spawn();
939            world.add_component(e, TableC(i));
940            table_only.push(e);
941        }
942
943        // &SparseC must yield exactly the 3 holders with the right values.
944        {
945            let q = world.query::<&SparseC>().unwrap();
946            let mut vals: Vec<i32> = q.iter().map(|(_id, s)| s.0).collect();
947            vals.sort();
948            assert_eq!(vals, vec![0, 10, 20], "sparse query leaked/dropped rows under mixed presence");
949        }
950        // (&TableC, &SparseC): only the 3 with both.
951        assert_eq!(
952            world.query::<(&TableC, &SparseC)>().unwrap().iter().count(),
953            3,
954            "table+sparse tuple query miscounted"
955        );
956        // With<SparseC> keeps 3; Without<SparseC> keeps the 2 table-only.
957        assert_eq!(
958            world.query::<(&TableC, With<SparseC>)>().unwrap().iter().count(),
959            3,
960            "With<Sparse> miscounted"
961        );
962        assert_eq!(
963            world.query::<(&TableC, Without<SparseC>)>().unwrap().iter().count(),
964            2,
965            "Without<Sparse> miscounted"
966        );
967        // Random access: table-only entities must report no SparseC.
968        for e in &table_only {
969            assert!(
970                world.query::<&SparseC>().unwrap().get(e.id()).is_none(),
971                "get() returned a sparse component for an entity that lacks it"
972            );
973        }
974    }
975}