use std::marker::PhantomData;
use crate::{
core::messages::Frame,
global::{CallbackReturn, EntityId, OkEmpty},
internal::{component::ComponentsTuple, conversion::FromBindgen, wit},
message::Listener,
message::RuntimeMessage,
};
use ambient_shared_types::ComponentIndex;
pub fn query<Components: ComponentsTuple + Copy + Clone + 'static>(
components: Components,
) -> GeneralQueryBuilder<Components> {
GeneralQuery::create(components)
}
pub fn change_query<Components: ComponentsTuple + Copy + Clone + 'static>(
components: Components,
) -> UntrackedChangeQuery<Components> {
UntrackedChangeQuery::create(components)
}
pub fn spawn_query<Components: ComponentsTuple + Copy + Clone + 'static>(
components: Components,
) -> EventQuery<Components> {
EventQuery::create(QueryEvent::Spawn, components)
}
pub fn despawn_query<Components: ComponentsTuple + Copy + Clone + 'static>(
components: Components,
) -> EventQuery<Components> {
EventQuery::create(QueryEvent::Despawn, components)
}
pub enum QueryEvent {
Spawn,
Despawn,
}
#[derive(Clone, Copy, Debug)]
pub struct GeneralQuery<Components: ComponentsTuple + Copy + Clone + 'static>(
QueryImpl<Components>,
);
impl<Components: ComponentsTuple + Copy + Clone + 'static> GeneralQuery<Components> {
pub fn create(components: Components) -> GeneralQueryBuilder<Components> {
GeneralQueryBuilder(QueryBuilderImpl::new(components.as_indices()))
}
pub fn evaluate(&self) -> Vec<(EntityId, Components::Data)> {
self.0.evaluate()
}
pub fn each_frame<R: CallbackReturn>(
self,
callback: impl FnMut(Vec<(EntityId, Components::Data)>) -> R + 'static,
) -> Listener {
self.0.bind(callback)
}
}
pub struct GeneralQueryBuilder<Components: ComponentsTuple + Copy + Clone + 'static>(
QueryBuilderImpl<Components>,
);
impl<Components: ComponentsTuple + Copy + Clone + 'static> GeneralQueryBuilder<Components> {
pub fn requires(mut self, requires: impl ComponentsTuple) -> Self {
self.0.requires(requires);
self
}
pub fn excludes(mut self, excludes: impl ComponentsTuple) -> Self {
self.0.excludes(excludes);
self
}
pub fn build(self) -> GeneralQuery<Components> {
GeneralQuery(QueryImpl::new(
self.0.build_impl(vec![], wit::component::QueryEvent::Frame),
))
}
pub fn each_frame<R: CallbackReturn>(
self,
callback: impl FnMut(Vec<(EntityId, Components::Data)>) -> R + 'static,
) -> Listener {
self.build().each_frame(callback)
}
}
pub struct UntrackedChangeQuery<Components: ComponentsTuple + Copy + Clone + 'static>(
QueryBuilderImpl<Components>,
Vec<ComponentIndex>,
);
impl<Components: ComponentsTuple + Copy + Clone + 'static> UntrackedChangeQuery<Components> {
pub fn create(components: Components) -> Self {
Self(QueryBuilderImpl::new(components.as_indices()), vec![])
}
pub fn track_change(self, changes: impl ComponentsTuple) -> ChangeQuery<Components> {
let cqwt = ChangeQuery(self);
cqwt.track_change(changes)
}
}
pub struct ChangeQuery<Components: ComponentsTuple + Copy + Clone + 'static>(
UntrackedChangeQuery<Components>,
);
impl<Components: ComponentsTuple + Copy + Clone + 'static> ChangeQuery<Components> {
pub fn requires(mut self, requires: impl ComponentsTuple) -> Self {
self.0 .0.requires(requires);
self
}
pub fn excludes(mut self, excludes: impl ComponentsTuple) -> Self {
self.0 .0.excludes(excludes);
self
}
pub fn track_change(mut self, changes: impl ComponentsTuple) -> Self {
self.0 .1.extend_from_slice(&changes.as_indices());
self
}
pub fn bind<R: CallbackReturn>(
self,
callback: impl FnMut(Vec<(EntityId, Components::Data)>) -> R + 'static,
) -> Listener {
self.build().bind(callback)
}
fn build(self) -> QueryImpl<Components> {
QueryImpl::new(
self.0
.0
.build_impl(self.0 .1, wit::component::QueryEvent::Frame),
)
}
}
pub struct EventQuery<Components: ComponentsTuple + Copy + Clone + 'static>(
QueryBuilderImpl<Components>,
QueryEvent,
);
impl<Components: ComponentsTuple + Copy + Clone + 'static> EventQuery<Components> {
pub fn create(event: QueryEvent, components: Components) -> Self {
Self(QueryBuilderImpl::new(components.as_indices()), event)
}
pub fn requires(mut self, requires: impl ComponentsTuple) -> Self {
self.0.requires(requires);
self
}
pub fn excludes(mut self, excludes: impl ComponentsTuple) -> Self {
self.0.excludes(excludes);
self
}
pub fn bind<R: CallbackReturn>(
self,
callback: impl FnMut(Vec<(EntityId, Components::Data)>) -> R + 'static,
) -> Listener {
self.build().bind(callback)
}
fn build(self) -> QueryImpl<Components> {
QueryImpl::new(self.0.build_impl(
vec![],
match self.1 {
QueryEvent::Spawn => wit::component::QueryEvent::Spawn,
QueryEvent::Despawn => wit::component::QueryEvent::Despawn,
},
))
}
}
#[derive(Clone, Copy, Debug)]
struct QueryImpl<Components: ComponentsTuple + Copy + Clone + 'static>(
u64,
PhantomData<Components>,
);
impl<Components: ComponentsTuple + Copy + Clone + 'static> QueryImpl<Components> {
fn new(id: u64) -> Self {
Self(id, PhantomData)
}
fn evaluate(&self) -> Vec<(EntityId, Components::Data)> {
wit::component::query_eval(self.0)
.into_iter()
.map(|(id, components)| {
(
id.from_bindgen(),
Components::from_component_types(components)
.expect("invalid type conversion on component query"),
)
})
.collect()
}
fn bind<R: CallbackReturn>(
self,
mut callback: impl FnMut(Vec<(EntityId, Components::Data)>) -> R + 'static,
) -> Listener {
Frame::subscribe(move |_| {
let results = self.evaluate();
if !results.is_empty() {
callback(results).into_result()?;
}
OkEmpty
})
}
}
struct QueryBuilderImpl<Components: ComponentsTuple + Copy + Clone + 'static> {
components: Vec<ComponentIndex>,
include: Vec<ComponentIndex>,
exclude: Vec<ComponentIndex>,
_data: PhantomData<Components>,
}
impl<Components: ComponentsTuple + Copy + Clone + 'static> QueryBuilderImpl<Components> {
fn new(components: Vec<ComponentIndex>) -> QueryBuilderImpl<Components> {
Self {
components,
include: vec![],
exclude: vec![],
_data: PhantomData,
}
}
pub fn requires(&mut self, include: impl ComponentsTuple) {
self.include.extend_from_slice(&include.as_indices());
}
pub fn excludes(&mut self, exclude: impl ComponentsTuple) {
self.exclude.extend_from_slice(&exclude.as_indices());
}
fn build_impl(self, changed: Vec<ComponentIndex>, event: wit::component::QueryEvent) -> u64 {
wit::component::query(
&wit::component::QueryBuild {
components: self.components,
includes: self.include,
excludes: self.exclude,
changed,
},
event,
)
}
}