Skip to content

Commit c497b43

Browse files
Dynamic Queries (#118)
* Initial dynamic query implementation * Rename Tuple -> Variadic
1 parent 3fee82e commit c497b43

File tree

5 files changed

+422
-19
lines changed

5 files changed

+422
-19
lines changed

crates/bevy_script_api/src/common/bevy/mod.rs

Lines changed: 145 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use std::{
2-
ops::{Deref, DerefMut},
3-
sync::Arc,
4-
};
5-
61
use crate::ReflectReference;
72
/// Common functionality for all script hosts
83
use bevy::{
9-
ecs::system::Command,
4+
ecs::{
5+
component::ComponentId,
6+
query::QueryBuilder,
7+
system::Command,
8+
world::{EntityRef, World},
9+
},
1010
prelude::{
1111
AppTypeRegistry, BuildWorldChildren, Children, DespawnChildrenRecursive, DespawnRecursive,
1212
Entity, Parent, ReflectComponent, ReflectDefault, ReflectResource,
@@ -17,6 +17,12 @@ use bevy::{
1717
},
1818
};
1919
use bevy_mod_scripting_core::{prelude::ScriptError, world::WorldPointer};
20+
use parking_lot::MappedRwLockWriteGuard;
21+
use std::{
22+
any::Any,
23+
ops::{Deref, DerefMut},
24+
sync::Arc,
25+
};
2026

2127
/// Helper trait for retrieving a world pointer from a script context.
2228
pub trait GetWorld {
@@ -65,6 +71,51 @@ impl Deref for ScriptTypeRegistration {
6571
}
6672
}
6773

74+
#[derive(Clone)]
75+
pub struct ScriptQueryBuilder {
76+
world: ScriptWorld,
77+
components: Vec<ScriptTypeRegistration>,
78+
with: Vec<ScriptTypeRegistration>,
79+
without: Vec<ScriptTypeRegistration>,
80+
}
81+
82+
impl ScriptQueryBuilder {
83+
pub fn new(world: ScriptWorld) -> Self {
84+
Self {
85+
world,
86+
components: vec![],
87+
with: vec![],
88+
without: vec![],
89+
}
90+
}
91+
92+
pub fn components(&mut self, components: Vec<ScriptTypeRegistration>) -> &mut Self {
93+
self.components.extend(components);
94+
self
95+
}
96+
97+
pub fn with(&mut self, with: Vec<ScriptTypeRegistration>) -> &mut Self {
98+
self.with.extend(with);
99+
self
100+
}
101+
102+
pub fn without(&mut self, without: Vec<ScriptTypeRegistration>) -> &mut Self {
103+
self.without.extend(without);
104+
self
105+
}
106+
107+
pub fn build(&mut self) -> Result<Vec<ScriptQueryResult>, ScriptError> {
108+
self.world.query(
109+
std::mem::take(&mut self.components),
110+
std::mem::take(&mut self.with),
111+
std::mem::take(&mut self.without),
112+
)
113+
}
114+
}
115+
116+
#[derive(Clone)]
117+
pub struct ScriptQueryResult(pub Entity, pub Vec<ReflectReference>);
118+
68119
#[derive(Clone, Debug)]
69120
pub struct ScriptWorld(WorldPointer);
70121

@@ -104,6 +155,7 @@ impl ScriptWorld {
104155
pub fn new(ptr: WorldPointer) -> Self {
105156
Self(ptr)
106157
}
158+
107159
pub fn get_children(&self, parent: Entity) -> Vec<Entity> {
108160
let w = self.read();
109161
w.get::<Children>(parent)
@@ -292,6 +344,7 @@ impl ScriptWorld {
292344

293345
Ok(resource_data.reflect(&w).is_some())
294346
}
347+
295348
pub fn remove_resource(&mut self, res_type: ScriptTypeRegistration) -> Result<(), ScriptError> {
296349
let mut w = self.write();
297350

@@ -301,4 +354,90 @@ impl ScriptWorld {
301354
resource_data.remove(&mut w);
302355
Ok(())
303356
}
357+
358+
pub fn query(
359+
&mut self,
360+
components: Vec<ScriptTypeRegistration>,
361+
with: Vec<ScriptTypeRegistration>,
362+
without: Vec<ScriptTypeRegistration>,
363+
) -> Result<Vec<ScriptQueryResult>, ScriptError> {
364+
let mut w = self.write();
365+
366+
let get_id = |component: &ScriptTypeRegistration,
367+
w: &MappedRwLockWriteGuard<World>|
368+
-> Result<ComponentId, ScriptError> {
369+
w.components()
370+
.get_id(component.type_info().type_id())
371+
.ok_or_else(|| {
372+
ScriptError::Other(format!("Not a component {}", component.short_name()))
373+
})
374+
};
375+
376+
let components: Vec<(ReflectComponent, ComponentId)> = components
377+
.into_iter()
378+
.map(|component| {
379+
let reflect_component = component.data::<ReflectComponent>().ok_or_else(|| {
380+
ScriptError::Other(format!("Not a component {}", component.short_name()))
381+
});
382+
383+
let component_id = get_id(&component, &w);
384+
reflect_component.map(|v1| component_id.map(|v2| (v1.clone(), v2)))?
385+
})
386+
.collect::<Result<Vec<_>, ScriptError>>()?;
387+
388+
let with_ids: Vec<ComponentId> = with
389+
.iter()
390+
.map(|component| get_id(component, &w))
391+
.collect::<Result<Vec<_>, ScriptError>>()?;
392+
393+
let without_ids: Vec<ComponentId> = without
394+
.iter()
395+
.map(|component| get_id(component, &w))
396+
.collect::<Result<Vec<_>, ScriptError>>()?;
397+
398+
let mut q = QueryBuilder::<EntityRef>::new(&mut w);
399+
400+
for (_, id) in &components {
401+
q.ref_id(*id);
402+
}
403+
404+
for with_id in with_ids {
405+
q.with_id(with_id);
406+
}
407+
408+
for without_id in without_ids {
409+
q.without_id(without_id);
410+
}
411+
412+
let query_result: Vec<EntityRef<'_>> = q.build().iter_mut(&mut w).collect();
413+
414+
query_result
415+
.into_iter()
416+
.map(|filtered_entity| {
417+
components
418+
.clone()
419+
.into_iter()
420+
.map(|(reflect_component, _)| {
421+
let type_id = reflect_component.type_id();
422+
reflect_component
423+
.reflect(filtered_entity)
424+
.map(|_component| {
425+
ReflectReference::new_component_ref(
426+
reflect_component,
427+
filtered_entity.id(),
428+
self.clone().into(),
429+
)
430+
})
431+
.ok_or_else(|| {
432+
ScriptError::Other(format!(
433+
"Failed to reflect component during query: {:?}",
434+
type_id
435+
))
436+
})
437+
})
438+
.collect::<Result<Vec<_>, ScriptError>>()
439+
.map(|references| ScriptQueryResult(filtered_entity.id(), references))
440+
})
441+
.collect::<Result<Vec<_>, ScriptError>>()
442+
}
304443
}

crates/bevy_script_api/src/core_providers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ impl bevy_mod_scripting_core::hosts::APIProvider for LuaCoreBevyAPIProvider {
5656
.process_type::<bevy_mod_scripting_lua::tealr::mlu::UserDataProxy<crate::lua::bevy::LuaScriptData>>()
5757
.process_type::<crate::lua::bevy::LuaTypeRegistration>()
5858
.process_type::<crate::lua::std::LuaVec<T>>()
59+
.process_type::<crate::lua::bevy::LuaQueryBuilder>()
5960
},
6061
))
6162
}

crates/bevy_script_api/src/lua/bevy/mod.rs

Lines changed: 98 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
1-
use crate::common::bevy::{ScriptTypeRegistration, ScriptWorld};
1+
use crate::common::bevy::{
2+
ScriptQueryBuilder, ScriptQueryResult, ScriptTypeRegistration, ScriptWorld,
3+
};
4+
use crate::lua::{
5+
mlua::prelude::{IntoLuaMulti, LuaError, LuaMultiValue},
6+
tealr::{mlu::TypedFunction, ToTypename},
7+
util::{VariadicComponents, VariadicQueryResult},
8+
Lua,
9+
};
210
use crate::providers::bevy_ecs::LuaEntity;
311
use crate::{impl_from_lua_with_clone, impl_tealr_type};
4-
5-
use std::sync::Arc;
6-
712
use bevy::hierarchy::BuildWorldChildren;
8-
use bevy::prelude::AppTypeRegistry;
9-
10-
use bevy::prelude::ReflectResource;
13+
use bevy::prelude::{AppTypeRegistry, ReflectResource};
1114
use bevy_mod_scripting_core::prelude::*;
12-
use bevy_mod_scripting_lua::tealr;
15+
use bevy_mod_scripting_lua::{prelude::IntoLua, tealr};
16+
use std::sync::Arc;
1317

1418
use tealr::mlu::{
1519
mlua::{self},
@@ -63,6 +67,84 @@ impl TealData for LuaScriptData {
6367
}
6468
}
6569

70+
pub type LuaQueryResult = ScriptQueryResult;
71+
72+
impl_from_lua_with_clone!(LuaQueryResult);
73+
74+
impl IntoLuaMulti<'_> for LuaQueryResult {
75+
fn into_lua_multi(self, lua: &Lua) -> Result<LuaMultiValue<'_>, LuaError> {
76+
let mut values = LuaMultiValue::from_vec(
77+
self.1
78+
.into_iter()
79+
.map(|v| v.into_lua(lua))
80+
.collect::<Result<Vec<_>, LuaError>>()?,
81+
);
82+
values.push_front(LuaEntity::new(self.0).into_lua(lua)?);
83+
Ok(values)
84+
}
85+
}
86+
87+
impl ToTypename for LuaQueryResult {
88+
fn to_typename() -> bevy_mod_scripting_lua::tealr::Type {
89+
bevy_mod_scripting_lua::tealr::Type::new_single(
90+
stringify!(QueryResult),
91+
bevy_mod_scripting_lua::tealr::KindOfType::External,
92+
)
93+
}
94+
}
95+
96+
pub type LuaQueryBuilder = ScriptQueryBuilder;
97+
98+
impl_tealr_type!(LuaQueryBuilder);
99+
impl_from_lua_with_clone!(LuaQueryBuilder);
100+
101+
impl TealData for LuaQueryBuilder {
102+
fn add_fields<'lua, F: tealr::mlu::TealDataFields<'lua, Self>>(fields: &mut F) {
103+
fields.document("A Builder object which allows for filtering and iterating over components and entities in the world.");
104+
}
105+
106+
fn add_methods<'lua, T: TealDataMethods<'lua, Self>>(methods: &mut T) {
107+
methods.document("Filters out entities without any of the components passed");
108+
methods.add_method_mut("with", |_, s, components: VariadicComponents| {
109+
s.with(components.0);
110+
Ok(s.clone())
111+
});
112+
113+
methods.document("Filters out entities with any components passed");
114+
methods.add_method_mut("without", |_, s, components: VariadicComponents| {
115+
s.without(components.0);
116+
Ok(s.clone())
117+
});
118+
119+
methods
120+
.document("Queries the world and returns an iterator over the entity and components.");
121+
methods.add_method_mut("iter", |ctx, s, _: ()| {
122+
let query_result = s
123+
.build()
124+
.map_err(|e| mlua::Error::RuntimeError(e.to_string()))?;
125+
126+
let len = query_result.len();
127+
let mut curr_idx = 0;
128+
TypedFunction::from_rust_mut(
129+
move |_, ()| {
130+
let o = if curr_idx < len {
131+
let query_result = query_result.get(curr_idx).unwrap();
132+
VariadicQueryResult::Some(
133+
LuaEntity::new(query_result.0),
134+
query_result.1.clone(),
135+
)
136+
} else {
137+
VariadicQueryResult::None
138+
};
139+
curr_idx += 1;
140+
Ok(o)
141+
},
142+
ctx,
143+
)
144+
});
145+
}
146+
}
147+
66148
pub type LuaWorld = ScriptWorld;
67149

68150
impl_tealr_type!(LuaWorld);
@@ -118,6 +200,14 @@ impl TealData for LuaWorld {
118200
},
119201
);
120202

203+
methods.document("Creates a LuaQueryBuilder, querying for the passed components types.");
204+
methods.document("Can be iterated over using `LuaQueryBuilder:iter()`");
205+
methods.add_method_mut("query", |_, world, components: VariadicComponents| {
206+
Ok(LuaQueryBuilder::new(world.clone())
207+
.components(components.0)
208+
.clone())
209+
});
210+
121211
methods
122212
.document("Returns `true` if the given entity contains a component of the given type.");
123213
methods.add_method(

0 commit comments

Comments
 (0)