Skip to content

Commit 0b9e277

Browse files
re-implement dynamic queries
Co-authored-by: Lily <mistrustfully@gmail.com>
1 parent 21e3333 commit 0b9e277

File tree

8 files changed

+320
-38
lines changed

8 files changed

+320
-38
lines changed

crates/bevy_mod_scripting_core/src/bindings/query.rs

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
use super::{ReflectReference, WorldCallbackAccess};
2-
use crate::prelude::ScriptResult;
1+
use super::{ReflectReference, WorldAccessGuard, WorldCallbackAccess};
2+
use crate::{
3+
bindings::{CONCURRENT_WORLD_ACCESS_MSG, STALE_WORLD_MSG},
4+
prelude::ScriptResult,
5+
};
36
use bevy::{
47
ecs::{component::ComponentId, entity::Entity},
8+
prelude::{EntityRef, QueryBuilder},
59
reflect::TypeRegistration,
610
};
7-
use std::{any::TypeId, ops::Deref, sync::Arc};
11+
use std::{any::TypeId, collections::VecDeque, sync::Arc};
812

913
/// A wrapper around a `TypeRegistration` that provides additional information about the type.
1014
///
@@ -65,24 +69,14 @@ impl std::fmt::Display for ScriptTypeRegistration {
6569
}
6670
}
6771

68-
#[derive(Clone)]
72+
#[derive(Clone, Default)]
6973
pub struct ScriptQueryBuilder {
70-
world: WorldCallbackAccess,
7174
components: Vec<ScriptTypeRegistration>,
7275
with: Vec<ScriptTypeRegistration>,
7376
without: Vec<ScriptTypeRegistration>,
7477
}
7578

7679
impl ScriptQueryBuilder {
77-
pub fn new(world: WorldCallbackAccess) -> Self {
78-
Self {
79-
world,
80-
components: vec![],
81-
with: vec![],
82-
without: vec![],
83-
}
84-
}
85-
8680
pub fn components(&mut self, components: Vec<ScriptTypeRegistration>) -> &mut Self {
8781
self.components.extend(components);
8882
self
@@ -97,29 +91,76 @@ impl ScriptQueryBuilder {
9791
self.without.extend(without);
9892
self
9993
}
100-
101-
pub fn build(&mut self) -> ScriptResult<Vec<ScriptQueryResult>> {
102-
self.world.query(
103-
std::mem::take(&mut self.components),
104-
std::mem::take(&mut self.with),
105-
std::mem::take(&mut self.without),
106-
)
107-
}
10894
}
10995

11096
#[derive(Clone)]
11197
pub struct ScriptQueryResult(pub Entity, pub Vec<ReflectReference>);
11298

11399
impl WorldCallbackAccess {
114-
pub fn query(
115-
&mut self,
116-
components: Vec<ScriptTypeRegistration>,
117-
with: Vec<ScriptTypeRegistration>,
118-
without: Vec<ScriptTypeRegistration>,
119-
) -> ScriptResult<Vec<ScriptQueryResult>> {
120-
// for c in components {
121-
122-
// }
123-
todo!()
100+
pub fn query(&self, query: ScriptQueryBuilder) -> ScriptResult<VecDeque<ScriptQueryResult>> {
101+
// find the set of components
102+
self.read()
103+
.unwrap_or_else(|| panic!("{STALE_WORLD_MSG}"))
104+
.query(query)
124105
}
125106
}
107+
108+
impl<'w> WorldAccessGuard<'w> {
109+
pub fn query(&self, query: ScriptQueryBuilder) -> ScriptResult<VecDeque<ScriptQueryResult>> {
110+
let world = self
111+
.get_whole_world_access()
112+
.unwrap_or_else(|| panic!("{CONCURRENT_WORLD_ACCESS_MSG}"));
113+
114+
let mut dynamic_query = QueryBuilder::<EntityRef>::new(world);
115+
116+
// we don't actually want to fetch the data for components now, only figure out
117+
// which entities match the query
118+
// so we might be being slightly overkill
119+
for c in &query.components {
120+
dynamic_query.ref_id(c.component_id().unwrap());
121+
}
122+
123+
for w in query.with {
124+
dynamic_query.with_id(w.component_id.unwrap());
125+
}
126+
127+
for without_id in query.without {
128+
dynamic_query.without_id(without_id.component_id.unwrap());
129+
}
130+
131+
let mut built_query = dynamic_query.build();
132+
let query_result = built_query.iter(world);
133+
134+
Ok(query_result
135+
.map(|r| {
136+
let references: Vec<_> = query
137+
.components
138+
.iter()
139+
.map(|c| ReflectReference {
140+
base: super::ReflectBaseType {
141+
type_id: c.type_id(),
142+
base_id: super::ReflectBase::Component(r.id(), c.component_id.unwrap()),
143+
},
144+
reflect_path: vec![],
145+
})
146+
.collect();
147+
ScriptQueryResult(r.id(), references)
148+
})
149+
.collect())
150+
}
151+
}
152+
153+
#[cfg(test)]
154+
mod test {
155+
use test_utils::test_data::setup_world;
156+
157+
use super::*;
158+
159+
// #[test]
160+
// fn test_simple_query() {
161+
// let world = setup_world(|w,r|{
162+
// w.spawn(TestComponent::init())
163+
// w.spawn(Te)
164+
// })
165+
// }
166+
}

crates/bevy_mod_scripting_core/src/bindings/world.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,11 @@ pub const DEFAULT_INTERVAL: Duration = Duration::from_millis(10);
245245
/// Provides safe access to the world via [`WorldAccess`] permissions, which enforce aliasing rules at runtime in multi-thread environments
246246
#[derive(Clone)]
247247
pub struct WorldAccessGuard<'w> {
248-
cell: UnsafeWorldCell<'w>,
248+
pub cell: UnsafeWorldCell<'w>,
249249
// TODO: this is fairly hefty, explore other ways to hand out locks on WorldAccess
250-
accesses: Arc<LockableHashMap<ReflectAccessId, Option<WorldAccessUnit<'w>>>>,
250+
pub accesses: Arc<LockableHashMap<ReflectAccessId, Option<WorldAccessUnit<'w>>>>,
251251
/// true if anybody has any access to the world
252-
accesses_count: Arc<AtomicUsize>,
252+
pub accesses_count: Arc<AtomicUsize>,
253253
// TODO can we track code/stack locations of things holding onto theese locks for debugging?
254254
}
255255

crates/languages/bevy_mod_scripting_lua/src/bindings/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use bevy_mod_scripting_core::bindings::WorldCallbackAccess;
2-
31
pub mod providers;
42
pub mod proxy;
3+
pub mod query;
54
pub mod reference;
65
pub mod std;
76
pub mod type_registration;
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
use std::ops::{Deref, DerefMut};
2+
3+
use bevy::prelude::Entity;
4+
use bevy_mod_scripting_core::bindings::{
5+
ReflectAllocator, ReflectReference, ScriptQueryBuilder, ScriptTypeRegistration, Unproxy,
6+
ValProxy,
7+
};
8+
use tealr::{
9+
mlu::{
10+
mlua::{IntoLua, Value},
11+
TealData, TypedFunction,
12+
},
13+
ToTypename,
14+
};
15+
16+
use crate::{impl_userdata_from_lua, impl_userdata_with_tealdata, util::Variadic};
17+
18+
use super::{
19+
proxy::{LuaProxied, LuaValProxy},
20+
reference::LuaReflectReference,
21+
world::GetWorld,
22+
};
23+
24+
#[derive(Default, Clone)]
25+
pub struct LuaQueryBuilder(pub(crate) ScriptQueryBuilder);
26+
27+
impl Deref for LuaQueryBuilder {
28+
type Target = ScriptQueryBuilder;
29+
30+
fn deref(&self) -> &Self::Target {
31+
&self.0
32+
}
33+
}
34+
35+
impl DerefMut for LuaQueryBuilder {
36+
fn deref_mut(&mut self) -> &mut Self::Target {
37+
&mut self.0
38+
}
39+
}
40+
41+
impl LuaQueryBuilder {}
42+
43+
impl LuaProxied for ScriptQueryBuilder {
44+
type Proxy = LuaQueryBuilder;
45+
}
46+
47+
impl_userdata_from_lua!(LuaQueryBuilder);
48+
impl_userdata_with_tealdata!(LuaQueryBuilder);
49+
50+
impl TealData for LuaQueryBuilder {
51+
fn add_methods<'lua, T: tealr::mlu::TealDataMethods<'lua, Self>>(methods: &mut T) {
52+
methods.add_function(
53+
"with",
54+
|_, (mut this, mut query): (Self, Variadic<LuaValProxy<ScriptTypeRegistration>>)| {
55+
let registrations: Vec<_> = query
56+
.iter_mut()
57+
.map(|v| v.unproxy())
58+
.collect::<Result<_, _>>()
59+
.map_err(tealr::mlu::mlua::Error::external)?;
60+
this.0.with(registrations);
61+
Ok(this)
62+
},
63+
);
64+
65+
methods.add_function(
66+
"without",
67+
|_, (mut this, mut query): (Self, Variadic<LuaValProxy<ScriptTypeRegistration>>)| {
68+
let registrations: Vec<_> = query
69+
.iter_mut()
70+
.map(|v| v.unproxy())
71+
.collect::<Result<_, _>>()
72+
.map_err(tealr::mlu::mlua::Error::external)?;
73+
this.0.without(registrations);
74+
Ok(this)
75+
},
76+
);
77+
78+
methods.add_function("iter", |l, this: LuaQueryBuilder| {
79+
let world = l.get_world()?;
80+
let mut result = world
81+
.query(this.0)
82+
.map_err(tealr::mlu::mlua::Error::external)?;
83+
let mut len = result.len();
84+
let iterator = TypedFunction::from_rust_mut(
85+
move |lua, ()| {
86+
if len > 0 {
87+
let result = result
88+
.pop_front()
89+
.expect("invariant: len of array = len && len > 0");
90+
let entity =
91+
world.with_resource::<ReflectAllocator, _, _>(|_, mut allocator| {
92+
ReflectReference::new_allocated(result.0, &mut allocator)
93+
});
94+
let proxy_entity =
95+
<Entity as LuaProxied>::Proxy::from(entity).into_lua(lua)?;
96+
let component_refs: Vec<_> = result
97+
.1
98+
.into_iter()
99+
.map(|r| LuaReflectReference(r).to_lua_proxy(lua))
100+
.collect::<Result<_, _>>()?;
101+
102+
len -= 1;
103+
104+
Ok(Variadic::new(
105+
std::iter::once(proxy_entity).chain(component_refs.into_iter()),
106+
))
107+
} else {
108+
Ok(Variadic::new(vec![Value::Nil, Value::Nil]))
109+
}
110+
},
111+
l,
112+
)?;
113+
Ok(iterator)
114+
});
115+
}
116+
}
117+
118+
impl ToTypename for LuaQueryBuilder {
119+
fn to_typename() -> tealr::Type {
120+
tealr::Type::new_single("QueryBuilder", tealr::KindOfType::External)
121+
}
122+
}

crates/languages/bevy_mod_scripting_lua/src/bindings/world.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ use tealr::{
2020
};
2121

2222
use super::proxy::LuaReflectRefProxy;
23+
use super::query::LuaQueryBuilder;
2324
use super::{
2425
providers::bevy_ecs::LuaEntity,
2526
proxy::{
2627
ErrorProxy, LuaIdentityProxy, LuaProxied, LuaReflectValProxy, LuaValProxy, TypenameProxy,
2728
},
2829
type_registration::LuaTypeRegistration,
2930
};
31+
use crate::util::Variadic;
3032
use crate::{impl_userdata_from_lua, impl_userdata_with_tealdata};
3133

3234
pub struct Nil;
@@ -375,6 +377,25 @@ impl TealData for LuaWorld {
375377
Ok(TypenameProxy::<_, Nil>::new(out))
376378
},
377379
);
380+
381+
methods.add_method(
382+
"query",
383+
|_, this, mut components: Variadic<LuaValProxy<ScriptTypeRegistration>>| {
384+
let world = this.0.read().ok_or_else(|| {
385+
mlua::Error::external(ScriptError::new_reflection_error("Stale world access"))
386+
})?;
387+
let mut builder = LuaQueryBuilder::default();
388+
let deque = components.0;
389+
builder.components(
390+
deque
391+
.into_iter()
392+
.map(|mut c| c.unproxy())
393+
.collect::<Result<_, _>>()
394+
.map_err(tealr::mlu::mlua::Error::external)?,
395+
);
396+
Ok(builder)
397+
},
398+
);
378399
}
379400

380401
fn add_fields<'lua, F: tealr::mlu::TealDataFields<'lua, Self>>(_fields: &mut F) {}

0 commit comments

Comments
 (0)