use crate::archetype::Archetype;
use crate::entity::Entity;
use crate::world::World;
use std::any::TypeId;
use std::marker::PhantomData;
mod fetch;
mod iter;
pub use fetch::{FetchComponent, Mut};
pub use iter::{QueryChunksIter, QueryIter};
mod sealed {
pub trait SealedFetch {}
pub trait SealedQuery {}
pub trait SealedReadOnly {}
}
pub trait WorldQuery: sealed::SealedQuery {
type StaticType: 'static;
type Fetch<'w>: Copy;
type Item<'w>;
type Slice<'w>;
unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, system_tick: u32) -> Option<Self::Fetch<'w>>;
fn check_aliasing(types: &mut Vec<(TypeId, bool)>);
fn matches_archetype(arch: &Archetype) -> bool;
unsafe fn get_item<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32) -> Self::Item<'w>;
unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32, system_tick: u32) -> bool;
unsafe fn get_slice<'w>(fetch: Self::Fetch<'w>, len: usize) -> Self::Slice<'w>;
fn has_row_filter() -> bool {
false
}
}
pub trait ReadOnlyQuery: WorldQuery + sealed::SealedReadOnly {}
pub struct Query<'w, Q: WorldQuery + ?Sized> {
world: &'w World,
matching_archetypes: Vec<usize>,
_marker: PhantomData<Q>,
}
#[inline]
fn check(tid: TypeId, is_mut: bool, types: &mut Vec<(TypeId, bool)>) {
for &(existing_tid, existing_mut) in types.iter() {
if existing_tid == tid && (existing_mut || is_mut) {
panic!(
"Query aliasing UB detected! Component TypeId {:?} is accessed mutably more than once \
in the same query. This would cause undefined behavior. \
Use separate queries for components of the same type that need independent mutable access.",
tid
);
}
}
types.push((tid, is_mut));
}
#[inline]
fn arch_matches<T: crate::component::Component>(arch: &Archetype, want_present: bool) -> bool {
if T::storage_type() == crate::component::StorageType::SparseSet {
true
} else {
arch.has_component(TypeId::of::<T>()) == want_present
}
}
macro_rules! impl_tick_filter {
($(#[$meta:meta])* $name:ident, $field:ident) => {
$(#[$meta])*
pub struct $name<T>(PhantomData<T>);
impl<T: crate::component::Component> sealed::SealedQuery for $name<T> {}
impl<T: crate::component::Component> sealed::SealedReadOnly for $name<T> {}
impl<T: crate::component::Component> ReadOnlyQuery for $name<T> {}
impl<T: crate::component::Component> WorldQuery for $name<T> {
type StaticType = $name<T>;
type Fetch<'w> = (
*const crate::archetype::ComponentTicks,
Option<*const crate::archetype::sparse_set::ComponentSparseSet>,
);
type Item<'w> = ();
type Slice<'w> = ();
unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, _tick: u32) -> Option<Self::Fetch<'w>> {
if T::storage_type() == crate::component::StorageType::SparseSet {
let set = world.sparse_sets.get(&TypeId::of::<T>())?;
Some((std::ptr::null(), Some(set as *const _)))
} else {
let col = arch.get_column(TypeId::of::<T>())?;
Some((col.ticks_ptr(), None))
}
}
fn check_aliasing(types: &mut Vec<(TypeId, bool)>) {
check(TypeId::of::<T>(), false, types);
}
fn matches_archetype(arch: &Archetype) -> bool {
arch_matches::<T>(arch, true)
}
unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32, tick: u32) -> bool {
if let Some(set_ptr) = fetch.1 {
(*set_ptr).ticks_for(entity_id).is_some_and(|t| t.$field > tick)
} else {
(*fetch.0.add(row)).$field > tick
}
}
unsafe fn get_item<'w>(_f: Self::Fetch<'w>, _r: usize, _e: u32) -> Self::Item<'w> {}
unsafe fn get_slice<'w>(_f: Self::Fetch<'w>, _l: usize) -> Self::Slice<'w> {}
fn has_row_filter() -> bool {
true }
}
};
}
macro_rules! impl_presence_filter {
($(#[$meta:meta])* $name:ident, $present:expr) => {
$(#[$meta])*
pub struct $name<T>(PhantomData<T>);
impl<T: crate::component::Component> sealed::SealedQuery for $name<T> {}
impl<T: crate::component::Component> sealed::SealedReadOnly for $name<T> {}
impl<T: crate::component::Component> ReadOnlyQuery for $name<T> {}
impl<T: crate::component::Component> WorldQuery for $name<T> {
type StaticType = $name<T>;
type Fetch<'w> = (
bool,
Option<*const crate::archetype::sparse_set::ComponentSparseSet>,
);
type Item<'w> = ();
type Slice<'w> = ();
unsafe fn fetch_raw<'w>(world: &'w World, _arch: &Archetype, _tick: u32) -> Option<Self::Fetch<'w>> {
if T::storage_type() == crate::component::StorageType::SparseSet {
Some((true, world.sparse_sets.get(&TypeId::of::<T>()).map(|s| s as *const _)))
} else {
Some((false, None))
}
}
fn check_aliasing(_types: &mut Vec<(TypeId, bool)>) {}
fn matches_archetype(arch: &Archetype) -> bool {
arch_matches::<T>(arch, $present)
}
unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, _row: usize, entity_id: u32, _tick: u32) -> bool {
match fetch {
(false, _) => true,
(true, Some(set_ptr)) => (*set_ptr).contains(entity_id) == $present,
(true, None) => !$present, }
}
unsafe fn get_item<'w>(_f: Self::Fetch<'w>, _r: usize, _e: u32) -> Self::Item<'w> {}
unsafe fn get_slice<'w>(_f: Self::Fetch<'w>, _l: usize) -> Self::Slice<'w> {}
fn has_row_filter() -> bool {
T::storage_type() == crate::component::StorageType::SparseSet
}
}
};
}
impl<T0: FetchComponent> sealed::SealedQuery for T0 where T0::Component: crate::component::Component {}
impl<T0: FetchComponent> WorldQuery for T0 where T0::Component: crate::component::Component {
type StaticType = T0::Component;
type Fetch<'w> = T0::Fetch<'w>;
type Item<'w> = T0::Item<'w>;
type Slice<'w> = T0::Slice<'w>;
unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, tick: u32) -> Option<Self::Fetch<'w>> {
T0::fetch_raw(world, arch, tick)
}
fn check_aliasing(types: &mut Vec<(TypeId, bool)>) {
check(TypeId::of::<T0::Component>(), T0::IS_MUT, types);
}
fn matches_archetype(arch: &Archetype) -> bool {
arch_matches::<T0::Component>(arch, true)
}
unsafe fn get_item<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32) -> Self::Item<'w> {
T0::get_item(fetch, row, entity_id)
}
unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, _row: usize, entity_id: u32, _tick: u32) -> bool {
T0::contains_entity(fetch, entity_id)
}
unsafe fn get_slice<'w>(fetch: Self::Fetch<'w>, len: usize) -> Self::Slice<'w> {
T0::get_slice(fetch, len)
}
}
impl<T: crate::component::Component> sealed::SealedReadOnly for &T {}
impl<T: crate::component::Component> ReadOnlyQuery for &T {}
impl_tick_filter!(
Changed,
changed
);
impl_tick_filter!(
Added,
added
);
macro_rules! impl_query_tuple {
($($t:ident),*) => {
impl<$($t: WorldQuery),*> sealed::SealedQuery for ($($t,)*) {}
impl<$($t: ReadOnlyQuery),*> sealed::SealedReadOnly for ($($t,)*) {}
impl<$($t: ReadOnlyQuery),*> ReadOnlyQuery for ($($t,)*) {}
#[allow(non_snake_case)]
impl<$($t: WorldQuery),*> WorldQuery for ($($t,)*) {
type StaticType = ($($t::StaticType,)*);
type Fetch<'w> = ($($t::Fetch<'w>,)*);
type Item<'w> = ($($t::Item<'w>,)*);
type Slice<'w> = ($($t::Slice<'w>,)*);
unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, tick: u32) -> Option<Self::Fetch<'w>> {
Some(($($t::fetch_raw(world, arch, tick)?,)*))
}
fn check_aliasing(types: &mut Vec<(TypeId, bool)>) {
$($t::check_aliasing(types);)*
}
fn matches_archetype(arch: &Archetype) -> bool {
$($t::matches_archetype(arch) &&)* true
}
unsafe fn get_item<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32) -> Self::Item<'w> {
let ($($t,)*) = fetch;
($($t::get_item($t, row, entity_id),)*)
}
unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32, tick: u32) -> bool {
let ($($t,)*) = fetch;
$($t::filter_row($t, row, entity_id, tick) &&)* true
}
unsafe fn get_slice<'w>(fetch: Self::Fetch<'w>, len: usize) -> Self::Slice<'w> {
let ($($t,)*) = fetch;
($($t::get_slice($t, len),)*)
}
fn has_row_filter() -> bool {
$($t::has_row_filter() ||)* false
}
}
};
}
impl_query_tuple!(T0, T1);
impl_query_tuple!(T0, T1, T2);
impl_query_tuple!(T0, T1, T2, T3);
impl_query_tuple!(T0, T1, T2, T3, T4);
impl_query_tuple!(T0, T1, T2, T3, T4, T5);
impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6);
impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7);
impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
impl_query_tuple!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl_presence_filter!(
With,
true
);
impl_presence_filter!(
Without,
false
);
pub struct Or<T1, T2>(PhantomData<(T1, T2)>);
impl<T1: WorldQuery, T2: WorldQuery> sealed::SealedQuery for Or<T1, T2> {}
impl<T1: ReadOnlyQuery, T2: ReadOnlyQuery> sealed::SealedReadOnly for Or<T1, T2> {}
impl<T1: ReadOnlyQuery, T2: ReadOnlyQuery> ReadOnlyQuery for Or<T1, T2> {}
impl<T1: WorldQuery, T2: WorldQuery> WorldQuery for Or<T1, T2> {
type StaticType = Or<T1::StaticType, T2::StaticType>;
type Fetch<'w> = (Option<T1::Fetch<'w>>, Option<T2::Fetch<'w>>);
type Item<'w> = ();
type Slice<'w> = ();
unsafe fn fetch_raw<'w>(world: &'w World, arch: &Archetype, tick: u32) -> Option<Self::Fetch<'w>> {
let f1 = if T1::matches_archetype(arch) {
T1::fetch_raw(world, arch, tick)
} else {
None
};
let f2 = if T2::matches_archetype(arch) {
T2::fetch_raw(world, arch, tick)
} else {
None
};
Some((f1, f2))
}
fn check_aliasing(types: &mut Vec<(TypeId, bool)>) {
T1::check_aliasing(types);
T2::check_aliasing(types);
}
fn matches_archetype(arch: &Archetype) -> bool {
T1::matches_archetype(arch) || T2::matches_archetype(arch)
}
unsafe fn filter_row<'w>(fetch: Self::Fetch<'w>, row: usize, entity_id: u32, tick: u32) -> bool {
let a = fetch
.0
.is_some_and(|f| T1::filter_row(f, row, entity_id, tick));
let b = fetch
.1
.is_some_and(|f| T2::filter_row(f, row, entity_id, tick));
a || b
}
unsafe fn get_item<'w>(_fetch: Self::Fetch<'w>, _row: usize, _entity_id: u32) -> Self::Item<'w> {}
unsafe fn get_slice<'w>(_fetch: Self::Fetch<'w>, _len: usize) -> Self::Slice<'w> {}
fn has_row_filter() -> bool {
true
}
}
mod access;
#[cfg(test)]
mod tests;