bevy/crates/bevy_ecs/src/system/query/mod.rs
Lukas Orsvärn e1b995f0b0
Add documentation for bevy::ecs::Query::removed (#950)
Add documentation for bevy::ecs::Query::removed
2020-12-07 12:41:40 -08:00

233 lines
9.1 KiB
Rust

mod query_set;
pub use query_set::*;
use crate::{
ArchetypeComponent, Batch, BatchedIter, Component, ComponentError, Entity, Fetch, Mut,
QueryFilter, QueryIter, ReadOnlyFetch, TypeAccess, World, WorldQuery,
};
use bevy_tasks::ParallelIterator;
use std::marker::PhantomData;
/// Provides scoped access to a World according to a given [HecsQuery]
#[derive(Debug)]
pub struct Query<'a, Q: WorldQuery, F: QueryFilter = ()> {
pub(crate) world: &'a World,
pub(crate) component_access: &'a TypeAccess<ArchetypeComponent>,
_marker: PhantomData<(Q, F)>,
}
/// An error that occurs when using a [Query]
#[derive(Debug)]
pub enum QueryError {
CannotReadArchetype,
CannotWriteArchetype,
ComponentError(ComponentError),
NoSuchEntity,
}
impl<'a, Q: WorldQuery, F: QueryFilter> Query<'a, Q, F> {
/// # Safety
/// This will create a Query that could violate memory safety rules. Make sure that this is only called in
/// ways that ensure the Queries have unique mutable access.
#[inline]
pub(crate) unsafe fn new(
world: &'a World,
component_access: &'a TypeAccess<ArchetypeComponent>,
) -> Self {
Self {
world,
component_access,
_marker: PhantomData::default(),
}
}
/// Iterates over the query results. This can only be called for read-only queries
#[inline]
pub fn iter(&self) -> QueryIter<'_, Q, F>
where
Q::Fetch: ReadOnlyFetch,
{
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
unsafe { self.world.query_unchecked() }
}
/// Iterates over the query results
#[inline]
pub fn iter_mut(&mut self) -> QueryIter<'_, Q, F> {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
unsafe { self.world.query_unchecked() }
}
/// Iterates over the query results
/// # Safety
/// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component
#[inline]
pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, Q, F> {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
self.world.query_unchecked()
}
#[inline]
pub fn par_iter(&self, batch_size: usize) -> ParIter<'_, Q, F>
where
Q::Fetch: ReadOnlyFetch,
{
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
unsafe { ParIter::new(self.world.query_batched_unchecked(batch_size)) }
}
#[inline]
pub fn par_iter_mut(&mut self, batch_size: usize) -> ParIter<'_, Q, F> {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
unsafe { ParIter::new(self.world.query_batched_unchecked(batch_size)) }
}
/// Gets the query result for the given `entity`
#[inline]
pub fn get(&self, entity: Entity) -> Result<<Q::Fetch as Fetch>::Item, QueryError>
where
Q::Fetch: ReadOnlyFetch,
{
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
unsafe {
self.world
.query_one_unchecked::<Q, F>(entity)
.map_err(|_err| QueryError::NoSuchEntity)
}
}
/// Gets the query result for the given `entity`
#[inline]
pub fn get_mut(&mut self, entity: Entity) -> Result<<Q::Fetch as Fetch>::Item, QueryError> {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict
unsafe {
self.world
.query_one_unchecked::<Q, F>(entity)
.map_err(|_err| QueryError::NoSuchEntity)
}
}
/// Gets the query result for the given `entity`
/// # Safety
/// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component
#[inline]
pub unsafe fn get_unsafe(
&self,
entity: Entity,
) -> Result<<Q::Fetch as Fetch>::Item, QueryError> {
self.world
.query_one_unchecked::<Q, F>(entity)
.map_err(|_err| QueryError::NoSuchEntity)
}
/// Gets a reference to the entity's component of the given type. This will fail if the entity does not have
/// the given component type or if the given component type does not match this query.
pub fn get_component<T: Component>(&self, entity: Entity) -> Result<&T, QueryError> {
if let Some(location) = self.world.get_entity_location(entity) {
if self
.component_access
.is_read_or_write(&ArchetypeComponent::new::<T>(location.archetype))
{
// SAFE: we have already checked that the entity/component matches our archetype access. and systems are scheduled to run with safe archetype access
unsafe {
self.world
.get_at_location_unchecked(location)
.map_err(QueryError::ComponentError)
}
} else {
Err(QueryError::CannotReadArchetype)
}
} else {
Err(QueryError::ComponentError(ComponentError::NoSuchEntity))
}
}
/// Gets a mutable reference to the entity's component of the given type. This will fail if the entity does not have
/// the given component type or if the given component type does not match this query.
pub fn get_component_mut<T: Component>(
&mut self,
entity: Entity,
) -> Result<Mut<'_, T>, QueryError> {
let location = match self.world.get_entity_location(entity) {
None => return Err(QueryError::ComponentError(ComponentError::NoSuchEntity)),
Some(location) => location,
};
if self
.component_access
.is_write(&ArchetypeComponent::new::<T>(location.archetype))
{
// SAFE: RefMut does exclusivity checks and we have already validated the entity
unsafe {
self.world
.get_mut_at_location_unchecked(location)
.map_err(QueryError::ComponentError)
}
} else {
Err(QueryError::CannotWriteArchetype)
}
}
/// Gets a mutable reference to the entity's component of the given type. This will fail if the entity does not have
/// the given component type
/// # Safety
/// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component
pub unsafe fn get_component_unsafe<T: Component>(
&self,
entity: Entity,
) -> Result<Mut<'_, T>, QueryError> {
self.world
.get_mut_unchecked(entity)
.map_err(QueryError::ComponentError)
}
/// Returns an array containing the `Entity`s in this `Query` that had the given `Component`
/// removed in this update.
///
/// `removed::<C>()` only returns entities whose components were removed before the
/// current system started.
///
/// Regular systems do not apply `Commands` until the end of their stage. This means component
/// removals in a regular system won't be accessible through `removed::<C>()` in the same
/// stage, because the removal hasn't actually occurred yet. This can be solved by executing
/// `removed::<C>()` in a later stage. `AppBuilder::add_system_to_stage()` can be used to
/// control at what stage a system runs.
///
/// Thread local systems manipulate the world directly, so removes are applied immediately. This
/// means any system that runs after a thread local system in the same update will pick up
/// removals that happened in the thread local system, regardless of stages.
pub fn removed<C: Component>(&self) -> &[Entity] {
self.world.removed::<C>()
}
/// Sets the entity's component to the given value. This will fail if the entity does not already have
/// the given component type or if the given component type does not match this query.
pub fn set<T: Component>(&mut self, entity: Entity, component: T) -> Result<(), QueryError> {
let mut current = self.get_component_mut::<T>(entity)?;
*current = component;
Ok(())
}
}
/// Parallel version of QueryIter
pub struct ParIter<'w, Q: WorldQuery, F: QueryFilter> {
batched_iter: BatchedIter<'w, Q, F>,
}
impl<'w, Q: WorldQuery, F: QueryFilter> ParIter<'w, Q, F> {
pub fn new(batched_iter: BatchedIter<'w, Q, F>) -> Self {
Self { batched_iter }
}
}
unsafe impl<'w, Q: WorldQuery, F: QueryFilter> Send for ParIter<'w, Q, F> {}
impl<'w, Q: WorldQuery, F: QueryFilter> ParallelIterator<Batch<'w, Q, F>> for ParIter<'w, Q, F> {
type Item = <Q::Fetch as Fetch<'w>>::Item;
#[inline]
fn next_batch(&mut self) -> Option<Batch<'w, Q, F>> {
self.batched_iter.next()
}
}