use crate::{
archetype::{Archetype, ArchetypeComponentId},
component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType},
entity::Entity,
query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery},
storage::{ComponentSparseSet, Table},
world::World,
};
use bevy_ecs_macros::all_tuples;
use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref};
use std::{cell::UnsafeCell, marker::PhantomData};
use super::ReadOnlyWorldQuery;
pub struct With<T>(PhantomData<T>);
unsafe impl<T: Component> WorldQuery for With<T> {
type Fetch<'w> = ();
type Item<'w> = ();
type ReadOnly = Self;
type State = ComponentId;
fn shrink<'wlong: 'wshort, 'wshort>(_: Self::Item<'wlong>) -> Self::Item<'wshort> {}
unsafe fn init_fetch(
_world: &World,
_state: &ComponentId,
_last_change_tick: u32,
_change_tick: u32,
) {
}
unsafe fn clone_fetch<'w>(_fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {}
const IS_DENSE: bool = {
match T::Storage::STORAGE_TYPE {
StorageType::Table => true,
StorageType::SparseSet => false,
}
};
const IS_ARCHETYPAL: bool = true;
#[inline]
unsafe fn set_table(_fetch: &mut (), _state: &ComponentId, _table: &Table) {}
#[inline]
unsafe fn set_archetype(
_fetch: &mut (),
_state: &ComponentId,
_archetype: &Archetype,
_table: &Table,
) {
}
#[inline(always)]
unsafe fn fetch<'w>(
_fetch: &mut Self::Fetch<'w>,
_entity: Entity,
_table_row: usize,
) -> Self::Item<'w> {
}
#[inline]
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
access.add_with(id);
}
#[inline]
fn update_archetype_component_access(
_state: &ComponentId,
_archetype: &Archetype,
_access: &mut Access<ArchetypeComponentId>,
) {
}
fn init_state(world: &mut World) -> ComponentId {
world.init_component::<T>()
}
fn matches_component_set(
&id: &ComponentId,
set_contains_id: &impl Fn(ComponentId) -> bool,
) -> bool {
set_contains_id(id)
}
}
unsafe impl<T: Component> ReadOnlyWorldQuery for With<T> {}
pub struct Without<T>(PhantomData<T>);
unsafe impl<T: Component> WorldQuery for Without<T> {
type Fetch<'w> = ();
type Item<'w> = ();
type ReadOnly = Self;
type State = ComponentId;
fn shrink<'wlong: 'wshort, 'wshort>(_: Self::Item<'wlong>) -> Self::Item<'wshort> {}
unsafe fn init_fetch(
_world: &World,
_state: &ComponentId,
_last_change_tick: u32,
_change_tick: u32,
) {
}
unsafe fn clone_fetch<'w>(_fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {}
const IS_DENSE: bool = {
match T::Storage::STORAGE_TYPE {
StorageType::Table => true,
StorageType::SparseSet => false,
}
};
const IS_ARCHETYPAL: bool = true;
#[inline]
unsafe fn set_table(_fetch: &mut (), _state: &Self::State, _table: &Table) {}
#[inline]
unsafe fn set_archetype(
_fetch: &mut (),
_state: &ComponentId,
_archetype: &Archetype,
_table: &Table,
) {
}
#[inline(always)]
unsafe fn fetch<'w>(
_fetch: &mut Self::Fetch<'w>,
_entity: Entity,
_table_row: usize,
) -> Self::Item<'w> {
}
#[inline]
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
access.add_without(id);
}
#[inline]
fn update_archetype_component_access(
_state: &ComponentId,
_archetype: &Archetype,
_access: &mut Access<ArchetypeComponentId>,
) {
}
fn init_state(world: &mut World) -> ComponentId {
world.init_component::<T>()
}
fn matches_component_set(
&id: &ComponentId,
set_contains_id: &impl Fn(ComponentId) -> bool,
) -> bool {
!set_contains_id(id)
}
}
unsafe impl<T: Component> ReadOnlyWorldQuery for Without<T> {}
pub struct Or<T>(pub T);
#[doc(hidden)]
pub struct OrFetch<'w, T: WorldQuery> {
fetch: T::Fetch<'w>,
matches: bool,
}
macro_rules! impl_query_filter_tuple {
($(($filter: ident, $state: ident)),*) => {
#[allow(unused_variables)]
#[allow(non_snake_case)]
#[allow(clippy::unused_unit)]
unsafe impl<$($filter: WorldQuery),*> WorldQuery for Or<($($filter,)*)> {
type Fetch<'w> = ($(OrFetch<'w, $filter>,)*);
type Item<'w> = bool;
type ReadOnly = Or<($($filter::ReadOnly,)*)>;
type State = ($($filter::State,)*);
fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
item
}
const IS_DENSE: bool = true $(&& $filter::IS_DENSE)*;
const IS_ARCHETYPAL: bool = true $(&& $filter::IS_ARCHETYPAL)*;
unsafe fn init_fetch<'w>(world: &'w World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> Self::Fetch<'w> {
let ($($filter,)*) = state;
($(OrFetch {
fetch: $filter::init_fetch(world, $filter, last_change_tick, change_tick),
matches: false,
},)*)
}
unsafe fn clone_fetch<'w>(
fetch: &Self::Fetch<'w>,
) -> Self::Fetch<'w> {
let ($($filter,)*) = &fetch;
($(
OrFetch {
fetch: $filter::clone_fetch(&$filter.fetch),
matches: $filter.matches,
},
)*)
}
#[inline]
unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) {
let ($($filter,)*) = fetch;
let ($($state,)*) = state;
$(
$filter.matches = $filter::matches_component_set($state, &|id| table.has_column(id));
if $filter.matches {
$filter::set_table(&mut $filter.fetch, $state, table);
}
)*
}
#[inline]
unsafe fn set_archetype<'w>(
fetch: &mut Self::Fetch<'w>,
state: & Self::State,
archetype: &'w Archetype,
table: &'w Table
) {
let ($($filter,)*) = fetch;
let ($($state,)*) = &state;
$(
$filter.matches = $filter::matches_component_set($state, &|id| archetype.contains(id));
if $filter.matches {
$filter::set_archetype(&mut $filter.fetch, $state, archetype, table);
}
)*
}
#[inline(always)]
unsafe fn fetch<'w>(
fetch: &mut Self::Fetch<'w>,
_entity: Entity,
_table_row: usize
) -> Self::Item<'w> {
let ($($filter,)*) = fetch;
false $(|| ($filter.matches && $filter::filter_fetch(&mut $filter.fetch, _entity, _table_row)))*
}
#[inline(always)]
unsafe fn filter_fetch<'w>(
fetch: &mut Self::Fetch<'w>,
entity: Entity,
table_row: usize
) -> bool {
Self::fetch(fetch, entity, table_row)
}
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
let ($($filter,)*) = state;
let mut _intersected_access = access.clone();
let mut _not_first = false;
$(
if _not_first {
let mut intermediate = access.clone();
$filter::update_component_access($filter, &mut intermediate);
_intersected_access.extend_intersect_filter(&intermediate);
_intersected_access.extend_access(&intermediate);
} else {
$filter::update_component_access($filter, &mut _intersected_access);
_not_first = true;
}
)*
*access = _intersected_access;
}
fn update_archetype_component_access(state: &Self::State, archetype: &Archetype, access: &mut Access<ArchetypeComponentId>) {
let ($($filter,)*) = state;
$($filter::update_archetype_component_access($filter, archetype, access);)*
}
fn init_state(world: &mut World) -> Self::State {
($($filter::init_state(world),)*)
}
fn matches_component_set(_state: &Self::State, _set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
let ($($filter,)*) = _state;
false $(|| $filter::matches_component_set($filter, _set_contains_id))*
}
}
unsafe impl<$($filter: ReadOnlyWorldQuery),*> ReadOnlyWorldQuery for Or<($($filter,)*)> {}
};
}
all_tuples!(impl_query_filter_tuple, 0, 15, F, S);
macro_rules! impl_tick_filter {
(
$(#[$meta:meta])*
$name: ident,
$(#[$fetch_meta:meta])*
$fetch_name: ident,
$is_detected: expr
) => {
$(#[$meta])*
pub struct $name<T>(PhantomData<T>);
#[doc(hidden)]
$(#[$fetch_meta])*
pub struct $fetch_name<'w, T> {
table_ticks: Option<ThinSlicePtr<'w, UnsafeCell<ComponentTicks>>>,
marker: PhantomData<T>,
sparse_set: Option<&'w ComponentSparseSet>,
last_change_tick: u32,
change_tick: u32,
}
unsafe impl<T: Component> WorldQuery for $name<T> {
type Fetch<'w> = $fetch_name<'w, T>;
type Item<'w> = bool;
type ReadOnly = Self;
type State = ComponentId;
fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
item
}
unsafe fn init_fetch<'w>(world: &'w World, &id: &ComponentId, last_change_tick: u32, change_tick: u32) -> Self::Fetch<'w> {
Self::Fetch::<'w> {
table_ticks: None,
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet)
.then(|| {
world.storages()
.sparse_sets
.get(id)
.debug_checked_unwrap()
}),
marker: PhantomData,
last_change_tick,
change_tick,
}
}
unsafe fn clone_fetch<'w>(
fetch: &Self::Fetch<'w>,
) -> Self::Fetch<'w> {
$fetch_name {
table_ticks: fetch.table_ticks,
sparse_set: fetch.sparse_set,
last_change_tick: fetch.last_change_tick,
change_tick: fetch.change_tick,
marker: PhantomData,
}
}
const IS_DENSE: bool = {
match T::Storage::STORAGE_TYPE {
StorageType::Table => true,
StorageType::SparseSet => false,
}
};
const IS_ARCHETYPAL: bool = false;
#[inline]
unsafe fn set_table<'w>(
fetch: &mut Self::Fetch<'w>,
&component_id: &ComponentId,
table: &'w Table
) {
fetch.table_ticks = Some(
table.get_column(component_id)
.debug_checked_unwrap()
.get_ticks_slice()
.into()
);
}
#[inline]
unsafe fn set_archetype<'w>(
fetch: &mut Self::Fetch<'w>,
component_id: &ComponentId,
_archetype: &'w Archetype,
table: &'w Table
) {
if Self::IS_DENSE {
Self::set_table(fetch, component_id, table);
}
}
#[inline(always)]
unsafe fn fetch<'w>(
fetch: &mut Self::Fetch<'w>,
entity: Entity,
table_row: usize
) -> Self::Item<'w> {
match T::Storage::STORAGE_TYPE {
StorageType::Table => {
$is_detected(&*(
fetch.table_ticks
.debug_checked_unwrap()
.get(table_row))
.deref(),
fetch.last_change_tick,
fetch.change_tick
)
}
StorageType::SparseSet => {
let ticks = &*fetch
.sparse_set
.debug_checked_unwrap()
.get_ticks(entity)
.debug_checked_unwrap()
.get();
$is_detected(ticks, fetch.last_change_tick, fetch.change_tick)
}
}
}
#[inline(always)]
unsafe fn filter_fetch<'w>(
fetch: &mut Self::Fetch<'w>,
entity: Entity,
table_row: usize
) -> bool {
Self::fetch(fetch, entity, table_row)
}
#[inline]
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
if access.access().has_write(id) {
panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
std::any::type_name::<T>());
}
access.add_read(id);
}
#[inline]
fn update_archetype_component_access(
&id: &ComponentId,
archetype: &Archetype,
access: &mut Access<ArchetypeComponentId>,
) {
if let Some(archetype_component_id) = archetype.get_archetype_component_id(id) {
access.add_read(archetype_component_id);
}
}
fn init_state(world: &mut World) -> ComponentId {
world.init_component::<T>()
}
fn matches_component_set(&id: &ComponentId, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
set_contains_id(id)
}
}
unsafe impl<T: Component> ReadOnlyWorldQuery for $name<T> {}
};
}
impl_tick_filter!(
Added,
AddedFetch,
ComponentTicks::is_added
);
impl_tick_filter!(
Changed,
ChangedFetch,
ComponentTicks::is_changed
);
pub trait ArchetypeFilter {}
impl<T> ArchetypeFilter for With<T> {}
impl<T> ArchetypeFilter for Without<T> {}
macro_rules! impl_archetype_filter_tuple {
($($filter: ident),*) => {
impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for ($($filter,)*) {}
impl<$($filter: ArchetypeFilter),*> ArchetypeFilter for Or<($($filter,)*)> {}
};
}
all_tuples!(impl_archetype_filter_tuple, 0, 15, F);