use bevy::ecs::query::{QueryData, QueryFilter, QueryState};
use bevy::ecs::system::SystemParam;
use bevy::prelude::{Entity, Query, Res, World};
use crate::plugins::persistence_plugin::{PersistencePluginConfig, 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,
set_store, take_store,
};
#[derive(SystemParam)]
pub struct PersistentQueryParam<'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>>,
pub(crate) config: Res<'w, PersistencePluginConfig>,
}
pub type PersistentQuery<'w, 's, Q, F = ()> = PersistentQueryParam<'w, 's, Q, F>;
impl<'w, 's, Q, F> PersistentQuery<'w, 's, Q, F>
where
Q: QueryData + QueryDataToComponents,
F: QueryFilter + ToPresenceSpec,
{
#[inline]
fn immediate_world_ptr(&self) -> Option<*mut World> {
self.world_ptr.as_ref().map(|p| p.0)
}
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();
let store = take_store().unwrap_or_else(|| self.config.default_store.clone());
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, store,
);
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
}
pub fn store(self, store: impl Into<String>) -> Self {
set_store(store);
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>::ReadOnly as QueryData>::Item<'_, '_>> + '_> {
bevy::log::trace!("PersistentQuery::iter called");
if let Some(ptr) = self.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
let items: Vec<_> = state.iter(world).collect();
let items: Vec<_> = unsafe { std::mem::transmute(items) };
return Box::new(items.into_iter());
}
Box::new(self.query.iter())
}
pub fn iter_mut(&mut self) -> Box<dyn Iterator<Item = <(Entity, Q) as QueryData>::Item<'_, 's>> + '_> {
bevy::log::trace!("PersistentQuery::iter_mut called");
if let Some(ptr) = self.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
let items: Vec<_> = state.iter_mut(world).collect();
let items: Vec<_> = unsafe { std::mem::transmute(items) };
return Box::new(items.into_iter());
}
Box::new(self.query.iter_mut())
}
#[inline]
pub fn get(&self, entity: Entity) -> Result<<<(Entity, Q) as QueryData>::ReadOnly as QueryData>::Item<'_, '_>, bevy::ecs::query::QueryEntityError> {
bevy::log::trace!("PersistentQuery::get called for entity {:?}", entity);
if let Some(ptr) = self.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
let res = state.get(world, entity);
return unsafe { std::mem::transmute(res) };
}
self.query.get(entity)
}
#[inline]
pub fn get_mut(&mut self, entity: Entity) -> Result<<(Entity, Q) as QueryData>::Item<'_, 's>, bevy::ecs::query::QueryEntityError> {
bevy::log::trace!("PersistentQuery::get_mut called for entity {:?}", entity);
if let Some(ptr) = self.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
let res = state.get_mut(world, entity);
return unsafe { std::mem::transmute(res) };
}
self.query.get_mut(entity)
}
#[inline]
pub fn single(&self) -> Result<<<(Entity, Q) as QueryData>::ReadOnly as QueryData>::Item<'_, '_>, bevy::ecs::query::QuerySingleError> {
bevy::log::trace!("PersistentQuery::single called");
if let Some(ptr) = self.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
let res = state.single(world);
return unsafe { std::mem::transmute(res) };
}
self.query.single()
}
#[inline]
pub fn single_mut(&mut self) -> Result<<(Entity, Q) as QueryData>::Item<'_, 's>, bevy::ecs::query::QuerySingleError> {
bevy::log::trace!("PersistentQuery::single_mut called");
if let Some(ptr) = self.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
let res = state.single_mut(world);
return unsafe { std::mem::transmute(res) };
}
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.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
let res = state.get_many(world, entities);
return unsafe { std::mem::transmute(res) };
}
self.query.get_many(entities)
}
pub fn get_many_mut<const N: usize>(
&mut self,
entities: [Entity; N],
) -> Result<
[<(Entity, Q) as QueryData>::Item<'_, 's>; N],
bevy::ecs::query::QueryEntityError,
> {
bevy::log::trace!("PersistentQuery::get_many_mut called with {} entities", N);
if let Some(ptr) = self.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
let res = state.get_many_mut(world, entities);
return unsafe { std::mem::transmute(res) };
}
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>::ReadOnly as QueryData>::Item<'_, '_>> + '_> {
bevy::log::trace!("PersistentQuery::iter_many called");
let entity_vec: Vec<Entity> = entities.into_iter().collect();
if let Some(ptr) = self.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
let items: Vec<_> = state.iter_many(world, entity_vec).collect();
let items: Vec<_> = unsafe { std::mem::transmute(items) };
return Box::new(items.into_iter());
}
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<'_, 's>> + '_>
where
Q: bevy::ecs::query::ReadOnlyQueryData,
{
bevy::log::trace!("PersistentQuery::iter_many_mut called");
let entity_vec: Vec<Entity> = entities.into_iter().collect();
if let Some(ptr) = self.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
let items: Vec<_> = state.iter_many_mut(world, entity_vec).collect();
let items: Vec<_> = unsafe { std::mem::transmute(items) };
return Box::new(items.into_iter());
}
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>::ReadOnly as QueryData>::Item<'_, '_>; N]> + '_> {
bevy::log::trace!("PersistentQuery::iter_combinations called");
if let Some(ptr) = self.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
let items: Vec<_> = state.iter_combinations::<N>(world).collect();
let items: Vec<_> = unsafe { std::mem::transmute(items) };
return Box::new(items.into_iter());
}
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<'_, 's>; N]> + '_>
where
Q: bevy::ecs::query::ReadOnlyQueryData,
{
bevy::log::trace!("PersistentQuery::iter_combinations_mut called");
if let Some(ptr) = self.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
let items: Vec<_> = state.iter_combinations_mut::<N>(world).collect();
let items: Vec<_> = unsafe { std::mem::transmute(items) };
return Box::new(items.into_iter());
}
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.immediate_world_ptr() {
let world: &mut World = unsafe { &mut *ptr };
let mut state: QueryState<(Entity, Q), F> = QueryState::new(world);
return state.iter(world).any(|(e, _)| e == entity);
}
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 PersistentQueryParam<'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 PersistentQueryParam<'w, 's, Q, F>
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.query
}
}