use bevy::ecs::query::{QueryData, QueryFilter};
use bevy::ecs::system::SystemParam;
use bevy::prelude::{Entity, Query, Res};
use crate::plugins::persistence_plugin::TokioRuntime;
use crate::query::filter_expression::FilterExpression;
use crate::{DatabaseConnectionResource};
use crate::query::cache::{CachePolicy, PersistenceQueryCache};
use crate::query::deferred_ops::DeferredWorldOperations;
use crate::query::immediate_world_ptr::ImmediateWorldPtr;
use crate::query::presence_spec::ToPresenceSpec;
use crate::query::presence_spec::collect_presence_components;
use crate::query::query_data_to_components::QueryDataToComponents;
use crate::query::tls_config::{
set_filter, take_filter, set_cache_policy, take_cache_policy,
drain_additional_components, drain_without_components, set_pagination_size,
};
#[derive(SystemParam)]
pub struct PersistentQuery<'w, 's, Q: QueryData + 'static, F: QueryFilter + 'static = ()> {
pub(crate) query: Query<'w, 's, (Entity, Q), F>,
pub(crate) db: Res<'w, DatabaseConnectionResource>,
pub(crate) cache: Res<'w, PersistenceQueryCache>,
pub(crate) runtime: Res<'w, TokioRuntime>,
pub(crate) ops: Res<'w, DeferredWorldOperations>,
pub(crate) world_ptr: Option<Res<'w, ImmediateWorldPtr>>,
}
impl<'w, 's, Q, F> PersistentQuery<'w, 's, Q, F>
where
Q: QueryData<ReadOnly = Q> + 'static + QueryDataToComponents + bevy::ecs::query::ReadOnlyQueryData,
F: QueryFilter + 'static + ToPresenceSpec,
{
pub fn ensure_loaded(&mut self) -> &mut Self {
bevy::log::debug!("PersistentQuery::ensure_loaded called");
let mut fetch_names: Vec<&'static str> = Vec::new();
let mut presence_names: Vec<&'static str> = drain_additional_components();
let mut without_names: Vec<&'static str> = drain_without_components();
let tls_filter_expression: Option<FilterExpression> = take_filter();
let cache_policy: CachePolicy = take_cache_policy();
Q::push_names(&mut fetch_names);
let type_presence = <F as ToPresenceSpec>::to_presence_spec();
presence_names.extend(type_presence.withs().iter().copied());
without_names.extend(type_presence.withouts().iter().copied());
if let Some(expr) = type_presence.expr() {
collect_presence_components(expr, &mut fetch_names);
}
for &n in &presence_names {
if !fetch_names.contains(&n) {
fetch_names.push(n);
}
}
Self::sort_dedup(&mut fetch_names);
Self::sort_dedup(&mut presence_names);
Self::sort_dedup(&mut without_names);
let combined_expr: Option<FilterExpression> = match (type_presence.expr().cloned(), tls_filter_expression) {
(Some(a), Some(b)) => Some(a.and(b)),
(Some(a), None) => Some(a),
(None, Some(b)) => Some(b),
(None, None) => None,
};
self.execute_combined_load(
cache_policy,
presence_names,
without_names,
fetch_names,
combined_expr,
&[], false, );
self
}
pub fn filter(self, expr: FilterExpression) -> Self {
self.r#where(expr)
}
pub fn r#where(self, expr: FilterExpression) -> Self {
set_filter(expr);
self
}
pub fn force_refresh(self) -> Self {
set_cache_policy(CachePolicy::ForceRefresh);
self
}
#[inline]
fn sort_dedup<T: Ord>(v: &mut Vec<T>) {
v.sort_unstable();
v.dedup();
}
pub fn iter(&self) -> Box<dyn Iterator<Item = <(Entity, Q) as QueryData>::Item<'_>> + '_> {
bevy::log::trace!("PersistentQuery::iter called");
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
let mut query_state = world.query_filtered::<(Entity, Q), F>();
let results: Vec<<(Entity, Q) as QueryData>::Item<'_>> = query_state.iter(world).collect();
Box::new(results.into_iter())
} else {
Box::new(self.query.iter())
}
}
pub fn iter_mut(&mut self) -> Box<dyn Iterator<Item = <(Entity, Q) as QueryData>::Item<'_>> + '_> {
bevy::log::trace!("PersistentQuery::iter_mut called");
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
let mut query_state = world.query_filtered::<(Entity, Q), F>();
let results: Vec<<(Entity, Q) as QueryData>::Item<'_>> = query_state.iter(world).collect();
Box::new(results.into_iter())
} else {
Box::new(self.query.iter_mut())
}
}
#[inline]
pub fn get(&self, entity: Entity) -> Result<<(Entity, Q) as QueryData>::Item<'_>, bevy::ecs::query::QueryEntityError> {
bevy::log::trace!("PersistentQuery::get called for entity {:?}", entity);
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
world.query_filtered::<(Entity, Q), F>().get(world, entity)
} else {
self.query.get(entity)
}
}
#[inline]
pub fn get_mut(&mut self, entity: Entity) -> Result<<(Entity, Q) as QueryData>::Item<'_>, bevy::ecs::query::QueryEntityError> {
bevy::log::trace!("PersistentQuery::get_mut called for entity {:?}", entity);
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
world.query_filtered::<(Entity, Q), F>().get(world, entity)
} else {
self.query.get_mut(entity)
}
}
#[inline]
pub fn single(&self) -> Result<<(Entity, Q) as QueryData>::Item<'_>, bevy::ecs::query::QuerySingleError> {
bevy::log::trace!("PersistentQuery::single called");
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
world.query_filtered::<(Entity, Q), F>().single(world)
} else {
self.query.single()
}
}
#[inline]
pub fn single_mut(&mut self) -> Result<<(Entity, Q) as QueryData>::Item<'_>, bevy::ecs::query::QuerySingleError> {
bevy::log::trace!("PersistentQuery::single_mut called");
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
world.query_filtered::<(Entity, Q), F>().single(world)
} else {
self.query.single_mut()
}
}
pub fn get_many<const N: usize>(
&self,
entities: [Entity; N],
) -> Result<
[<< (Entity, Q) as QueryData>::ReadOnly as QueryData>::Item<'_>; N],
bevy::ecs::query::QueryEntityError,
> {
bevy::log::trace!("PersistentQuery::get_many called with {} entities", N);
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
for (i, &entity) in entities.iter().enumerate() {
if !world.entities().contains(entity) {
bevy::log::warn!("Entity at index {} ({:?}) does not exist in world", i, entity);
}
}
world.query_filtered::<(Entity, Q), F>().get_many(world, entities)
} else {
self.query.get_many(entities)
}
}
pub fn get_many_mut<const N: usize>(
&mut self,
entities: [Entity; N],
) -> Result<
[<< (Entity, Q) as QueryData>::ReadOnly as QueryData>::Item<'_>; N],
bevy::ecs::query::QueryEntityError,
> {
bevy::log::trace!("PersistentQuery::get_many_mut called with {} entities", N);
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
world.query_filtered::<(Entity, Q), F>().get_many(world, entities)
} else {
self.query.get_many_mut(entities)
}
}
pub fn iter_many<EntityList: IntoIterator<Item = Entity>>(
&self,
entities: EntityList,
) -> Box<dyn Iterator<Item = <(Entity, Q) as QueryData>::Item<'_>> + '_> {
bevy::log::trace!("PersistentQuery::iter_many called");
let entity_vec: Vec<Entity> = entities.into_iter().collect();
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
let mut query_state = world.query_filtered::<(Entity, Q), F>();
let mut results = Vec::new();
for entity in entity_vec {
if let Ok(item) = query_state.get(world, entity) {
results.push(item);
}
}
Box::new(results.into_iter())
} else {
Box::new(self.query.iter_many(entity_vec))
}
}
pub fn iter_many_mut<EntityList: IntoIterator<Item = Entity>>(
&mut self,
entities: EntityList,
) -> Box<dyn Iterator<Item = <(Entity, Q) as QueryData>::Item<'_>> + '_> {
bevy::log::trace!("PersistentQuery::iter_many_mut called");
let entity_vec: Vec<Entity> = entities.into_iter().collect();
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
let mut query_state = world.query_filtered::<(Entity, Q), F>();
let mut results = Vec::new();
for entity in entity_vec {
if let Ok(item) = query_state.get(world, entity) {
results.push(item);
}
}
Box::new(results.into_iter())
} else {
Box::new(self.query.iter_many_mut(entity_vec))
}
}
pub fn iter_combinations<const N: usize>(&self) -> Box<dyn Iterator<Item = [<(Entity, Q) as QueryData>::Item<'_>; N]> + '_> {
bevy::log::trace!("PersistentQuery::iter_combinations called");
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
let mut query_state = world.query_filtered::<(Entity, Q), F>();
let combinations: Vec<[<(Entity, Q) as QueryData>::Item<'_>; N]> =
query_state.iter_combinations::<N>(world).collect();
Box::new(combinations.into_iter())
} else {
Box::new(self.query.iter_combinations::<N>())
}
}
pub fn iter_combinations_mut<const N: usize>(&mut self) -> Box<dyn Iterator<Item = [<(Entity, Q) as QueryData>::Item<'_>; N]> + '_> {
bevy::log::trace!("PersistentQuery::iter_combinations_mut called");
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
let mut query_state = world.query_filtered::<(Entity, Q), F>();
let combinations: Vec<[<(Entity, Q) as QueryData>::Item<'_>; N]> =
query_state.iter_combinations::<N>(world).collect();
Box::new(combinations.into_iter())
} else {
Box::new(self.query.iter_combinations_mut::<N>())
}
}
#[inline]
pub fn contains(&self, entity: Entity) -> bool {
bevy::log::trace!("PersistentQuery::contains called for entity {:?}", entity);
if let Some(ptr) = &self.world_ptr {
let world = ptr.as_world_mut();
let last_change_tick = world.last_change_tick();
let change_tick = world.change_tick();
world.query_filtered::<(Entity, Q), F>()
.contains(entity, world, last_change_tick, change_tick)
} else {
self.query.contains(entity)
}
}
pub fn paginated_load(&mut self, page_size: usize) -> &mut Self {
set_pagination_size(page_size);
self.ensure_loaded()
}
}
impl<'w, 's, Q: QueryData + 'static, F: QueryFilter + 'static> std::ops::Deref
for PersistentQuery<'w, 's, Q, F>
{
type Target = Query<'w, 's, (Entity, Q), F>;
fn deref(&self) -> &Self::Target {
&self.query
}
}
impl<'w, 's, Q: QueryData + 'static, F: QueryFilter + 'static> std::ops::DerefMut
for PersistentQuery<'w, 's, Q, F>
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.query
}
}