Improve Lua ECS #30
|
@ -1,4 +1,5 @@
|
|||
local is_window_setup = false
|
||||
local cube_entity = nil
|
||||
|
||||
---Return the userdata's name from its metatable.
|
||||
---
|
||||
|
@ -40,6 +41,7 @@ function on_init()
|
|||
local pos = Transform.from_translation(Vec3.new(0, 0, -8.0))
|
||||
|
||||
local e = world:spawn(pos, cube_scene)
|
||||
cube_entity = e
|
||||
print("spawned entity " .. tostring(e))
|
||||
end
|
||||
|
||||
|
@ -100,6 +102,14 @@ function on_update()
|
|||
for _, tick in tick_res:iter() do
|
||||
print("Entity transform last changed on tick " .. tostring(tick))
|
||||
end
|
||||
|
||||
local pos_view = View.new(Transform)
|
||||
local vone = world:view_one(cube_entity, pos_view)
|
||||
local r = vone()
|
||||
if r ~= nil then
|
||||
local pos = r[1]
|
||||
print("Found cube entity at '" .. tostring(pos) .. "'")
|
||||
end
|
||||
end
|
||||
|
||||
--[[ function on_post_update()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate::{query::Fetch, Entity, World};
|
||||
|
||||
use super::{DynamicType, FetchDynamicTypeUnsafe, QueryDynamicType};
|
||||
|
@ -9,16 +11,28 @@ use super::{DynamicType, FetchDynamicTypeUnsafe, QueryDynamicType};
|
|||
/// since Rust doesn't actually need to know the types of what its iterating over.
|
||||
pub struct DynamicViewOne<'a> {
|
||||
world: &'a World,
|
||||
pub entity: Entity,
|
||||
pub queries: Vec<QueryDynamicType>
|
||||
inner: DynamicViewOneOwned,
|
||||
}
|
||||
|
||||
impl<'a> Deref for DynamicViewOne<'a> {
|
||||
type Target = DynamicViewOneOwned;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for DynamicViewOne<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DynamicViewOne<'a> {
|
||||
pub fn new(world: &'a World, entity: Entity) -> Self {
|
||||
Self {
|
||||
world,
|
||||
entity,
|
||||
queries: vec![],
|
||||
inner: DynamicViewOneOwned::new(entity)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,36 +40,67 @@ impl<'a> DynamicViewOne<'a> {
|
|||
pub fn new_with(world: &'a World, entity: Entity, queries: Vec<QueryDynamicType>) -> Self {
|
||||
Self {
|
||||
world,
|
||||
inner: DynamicViewOneOwned::new_with(entity, queries)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(self) -> Option<Vec<DynamicType>> {
|
||||
self.inner.get(&self.world)
|
||||
}
|
||||
}
|
||||
|
||||
/// A variant of [`DynamicViewOne`] that doesn't store a borrow of the world.
|
||||
#[derive(Clone)]
|
||||
pub struct DynamicViewOneOwned {
|
||||
pub entity: Entity,
|
||||
pub queries: Vec<QueryDynamicType>
|
||||
}
|
||||
|
||||
impl DynamicViewOneOwned {
|
||||
pub fn new(entity: Entity) -> Self {
|
||||
Self {
|
||||
entity,
|
||||
queries: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new [`DynamicViewOne`] with queries.
|
||||
pub fn new_with(entity: Entity, queries: Vec<QueryDynamicType>) -> Self {
|
||||
Self {
|
||||
entity,
|
||||
queries
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(self) -> Option<Vec<DynamicType>> {
|
||||
let arch = self.world.entity_archetype(self.entity)?;
|
||||
let aid = arch.entity_indexes().get(&self.entity)?;
|
||||
|
||||
// get all fetchers for the queries
|
||||
let mut fetchers: Vec<FetchDynamicTypeUnsafe> = self.queries.iter()
|
||||
.map(|q| unsafe { q.fetch(self.world, arch.id(), arch) } )
|
||||
.collect();
|
||||
pub fn get(self, world: &World) -> Option<Vec<DynamicType>> {
|
||||
dynamic_view_one_get_impl(world, &self.queries, self.entity)
|
||||
}
|
||||
}
|
||||
|
||||
let mut fetch_res = vec![];
|
||||
for fetcher in fetchers.iter_mut() {
|
||||
if !fetcher.can_visit_item(*aid) {
|
||||
return None;
|
||||
} else {
|
||||
let i = unsafe { fetcher.get_item(*aid) };
|
||||
fetch_res.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
if fetch_res.is_empty() {
|
||||
None
|
||||
fn dynamic_view_one_get_impl(world: &World, queries: &Vec<QueryDynamicType>, entity: Entity) -> Option<Vec<DynamicType>> {
|
||||
let arch = world.entity_archetype(entity)?;
|
||||
let aid = arch.entity_indexes().get(&entity)?;
|
||||
|
||||
// get all fetchers for the queries
|
||||
let mut fetchers: Vec<FetchDynamicTypeUnsafe> = queries.iter()
|
||||
.map(|q| unsafe { q.fetch(world, arch.id(), arch) } )
|
||||
.collect();
|
||||
|
||||
let mut fetch_res = vec![];
|
||||
for fetcher in fetchers.iter_mut() {
|
||||
if !fetcher.can_visit_item(*aid) {
|
||||
return None;
|
||||
} else {
|
||||
Some(fetch_res)
|
||||
let i = unsafe { fetcher.get_item(*aid) };
|
||||
fetch_res.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
if fetch_res.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(fetch_res)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -10,7 +10,6 @@ use super::ReflectLuaProxy;
|
|||
|
||||
#[cfg(feature = "lua")]
|
||||
pub struct ReflectedItem {
|
||||
//pub proxy: &'a ReflectLuaProxy,
|
||||
pub comp_ptr: NonNull<u8>,
|
||||
pub comp_val: mlua::Value,
|
||||
}
|
||||
|
@ -24,7 +23,6 @@ pub struct ReflectedRow {
|
|||
pub struct ReflectedIteratorOwned {
|
||||
pub world_ptr: ScriptWorldPtr,
|
||||
pub dyn_view: DynamicViewStateIter,
|
||||
//pub reflected_components: Option<NonNull<TypeRegistry>>
|
||||
}
|
||||
|
||||
impl ReflectedIteratorOwned {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
mod view;
|
||||
pub use view::*;
|
||||
|
||||
mod view_one;
|
||||
pub use view_one::*;
|
||||
|
||||
pub mod query;
|
|
@ -18,7 +18,7 @@ use crate::{
|
|||
use super::query::{LuaQuery, LuaQueryResult};
|
||||
|
||||
#[derive(Clone)]
|
||||
enum ViewQueryItem {
|
||||
pub(crate) enum ViewQueryItem {
|
||||
UserData(mlua::AnyUserData),
|
||||
Table(mlua::Table),
|
||||
Function(mlua::Function),
|
||||
|
@ -76,7 +76,7 @@ impl ViewQueryItem {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct View {
|
||||
items: Vec<ViewQueryItem>,
|
||||
pub(crate) items: Vec<ViewQueryItem>,
|
||||
}
|
||||
|
||||
impl mlua::FromLua for View {
|
||||
|
@ -104,11 +104,15 @@ impl mlua::UserData for View {
|
|||
}
|
||||
}
|
||||
|
||||
/// Results of queries in a View.
|
||||
///
|
||||
/// Represents the results of multiple queries.
|
||||
#[derive(Debug, Clone)]
|
||||
enum QueryRowResult {
|
||||
pub(crate) enum ViewQueryResult {
|
||||
None,
|
||||
AlwaysNone,
|
||||
FilterDeny,
|
||||
/// The results of the queries and the index they should be inserted at in the resulting row.
|
||||
Some(Vec<(mlua::Value, u32)>),
|
||||
}
|
||||
|
||||
|
@ -211,7 +215,7 @@ impl ViewResult {
|
|||
///
|
||||
/// The indexes are used to make sure that the results are in the same order that the script
|
||||
/// requested them in.
|
||||
fn get_query_results(&self, entity: Entity) -> mlua::Result<QueryRowResult> {
|
||||
fn get_query_results(&self, entity: Entity) -> mlua::Result<ViewQueryResult> {
|
||||
let mut query_vals = vec![];
|
||||
|
||||
// A modifier is used that will be incremented every time a filter allowed the query.
|
||||
|
@ -221,13 +225,13 @@ impl ViewResult {
|
|||
let qres = query.get_query_result(self.world.clone(), entity)?;
|
||||
|
||||
match qres {
|
||||
LuaQueryResult::None => return Ok(QueryRowResult::None),
|
||||
LuaQueryResult::AlwaysNone => return Ok(QueryRowResult::AlwaysNone),
|
||||
LuaQueryResult::None => return Ok(ViewQueryResult::None),
|
||||
LuaQueryResult::AlwaysNone => return Ok(ViewQueryResult::AlwaysNone),
|
||||
LuaQueryResult::FilterPass => {
|
||||
// do not push a boolean to values, its considered a filter
|
||||
index_mod += 1;
|
||||
},
|
||||
LuaQueryResult::FilterDeny => return Ok(QueryRowResult::FilterDeny),
|
||||
LuaQueryResult::FilterDeny => return Ok(ViewQueryResult::FilterDeny),
|
||||
LuaQueryResult::Some(value) => {
|
||||
let idx = (*i - index_mod).max(0);
|
||||
query_vals.push((value, idx));
|
||||
|
@ -235,7 +239,7 @@ impl ViewResult {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(QueryRowResult::Some(query_vals))
|
||||
Ok(ViewQueryResult::Some(query_vals))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,11 +265,11 @@ impl mlua::UserData for ViewResult {
|
|||
Some((en, mut vals)) => {
|
||||
loop {
|
||||
let query_vals = match this.get_query_results(en)? {
|
||||
QueryRowResult::Some(v) => v,
|
||||
QueryRowResult::AlwaysNone => {
|
||||
ViewQueryResult::Some(v) => v,
|
||||
ViewQueryResult::AlwaysNone => {
|
||||
return mlua::Value::Nil.into_lua_multi(lua);
|
||||
},
|
||||
QueryRowResult::None | QueryRowResult::FilterDeny => {
|
||||
ViewQueryResult::None | ViewQueryResult::FilterDeny => {
|
||||
// try to get it next loop
|
||||
continue;
|
||||
},
|
||||
|
@ -302,11 +306,11 @@ impl mlua::UserData for ViewResult {
|
|||
LuaEntityRef::new(this.world.clone(), en).into_lua(lua)?;
|
||||
|
||||
let query_vals = match this.get_query_results(en)? {
|
||||
QueryRowResult::Some(v) => v,
|
||||
QueryRowResult::AlwaysNone => {
|
||||
ViewQueryResult::Some(v) => v,
|
||||
ViewQueryResult::AlwaysNone => {
|
||||
return mlua::Value::Nil.into_lua_multi(lua);
|
||||
},
|
||||
QueryRowResult::None | QueryRowResult::FilterDeny => {
|
||||
ViewQueryResult::None | ViewQueryResult::FilterDeny => {
|
||||
// try to get it next loop
|
||||
continue;
|
||||
},
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use lyra_ecs::{query::dynamic::{DynamicViewOneOwned, QueryDynamicType}, Entity};
|
||||
use lyra_reflect::TypeRegistry;
|
||||
use mlua::{IntoLua, IntoLuaMulti, ObjectLike};
|
||||
|
||||
use crate::{lua::{ReflectLuaProxy, TypeLookup, WorldError, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptBorrow, ScriptWorldPtr};
|
||||
|
||||
use super::{query::{LuaQuery, LuaQueryResult}, View, ViewQueryItem, ViewQueryResult};
|
||||
|
||||
/// The result of an ecs world View of a single entity.
|
||||
#[derive(Clone)]
|
||||
pub struct ViewOneResult {
|
||||
world: ScriptWorldPtr,
|
||||
dynamic_view: DynamicViewOneOwned,
|
||||
/// The queries and the index they would be inserted in the result.
|
||||
queries: Vec<(LuaQuery, u32)>,
|
||||
}
|
||||
|
||||
impl mlua::FromLua for ViewOneResult {
|
||||
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let tyname = value.type_name();
|
||||
value
|
||||
.as_userdata()
|
||||
.ok_or(mlua::Error::FromLuaConversionError {
|
||||
from: tyname,
|
||||
to: "View".into(),
|
||||
message: None,
|
||||
})
|
||||
.and_then(|ud| ud.borrow::<Self>())
|
||||
.map(|ud| ud.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewOneResult {
|
||||
pub fn new(world: ScriptWorldPtr, entity: Entity, view: &View) -> Result<Self, mlua::Error> {
|
||||
let items = view.items.clone();
|
||||
let w = world.read();
|
||||
let mut view = DynamicViewOneOwned::new(entity);
|
||||
let mut queries = vec![];
|
||||
|
||||
for (idx, comp) in items.iter().enumerate() {
|
||||
if comp.is_query()? {
|
||||
queries.push((comp.as_query(), idx as u32));
|
||||
continue;
|
||||
}
|
||||
|
||||
match comp {
|
||||
ViewQueryItem::Table(t) => {
|
||||
let name: String = t.get(mlua::MetaMethod::Type.name())?;
|
||||
|
||||
let lookup = w.get_resource::<TypeLookup>().ok_or(mlua::Error::runtime(
|
||||
"Unable to lookup table proxy, none were ever registered!",
|
||||
))?;
|
||||
let info = lookup.comp_info_from_name.get(&name).ok_or_else(|| {
|
||||
mlua::Error::BadArgument {
|
||||
to: Some("ViewOneResult.new".into()),
|
||||
pos: 2 + idx,
|
||||
name: Some("query...".into()),
|
||||
cause: Arc::new(mlua::Error::external(WorldError::LuaInvalidUsage(
|
||||
format!("the 'Table' with name {} is unknown to the engine!", name),
|
||||
))),
|
||||
}
|
||||
})?;
|
||||
|
||||
let dyn_type = QueryDynamicType::from_info(info.clone());
|
||||
view.queries.push(dyn_type);
|
||||
}
|
||||
ViewQueryItem::UserData(ud) => {
|
||||
let reflect = ud
|
||||
.call_function::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
|
||||
.expect("Type does not implement 'reflect_type' properly");
|
||||
let refl_comp = reflect.reflect_branch.as_component()
|
||||
.expect("`self` is not an instance of `ReflectBranch::Component`");
|
||||
|
||||
let dyn_type = QueryDynamicType::from_info(refl_comp.info);
|
||||
view.queries.push(dyn_type);
|
||||
}
|
||||
// functions are queries, the if statement at the start would cause this to
|
||||
// be unreachable.
|
||||
ViewQueryItem::Function(_) => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
drop(w);
|
||||
|
||||
Ok(Self {
|
||||
world,
|
||||
dynamic_view: view,
|
||||
queries,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the query results and the indexes that they were provided in.
|
||||
///
|
||||
/// The indexes are used to make sure that the results are in the same order that the script
|
||||
/// requested them in.
|
||||
fn get_query_results(&self, entity: Entity) -> mlua::Result<ViewQueryResult> {
|
||||
let mut query_vals = vec![];
|
||||
|
||||
// A modifier is used that will be incremented every time a filter allowed the query.
|
||||
// this is used to remove the value of a filter without leaving a gap in the results.
|
||||
let mut index_mod = 0;
|
||||
for (query, i) in &self.queries {
|
||||
let qres = query.get_query_result(self.world.clone(), entity)?;
|
||||
|
||||
match qres {
|
||||
LuaQueryResult::None => return Ok(ViewQueryResult::None),
|
||||
LuaQueryResult::AlwaysNone => return Ok(ViewQueryResult::AlwaysNone),
|
||||
LuaQueryResult::FilterPass => {
|
||||
// do not push a boolean to values, its considered a filter
|
||||
index_mod += 1;
|
||||
},
|
||||
LuaQueryResult::FilterDeny => return Ok(ViewQueryResult::FilterDeny),
|
||||
LuaQueryResult::Some(value) => {
|
||||
let idx = (*i - index_mod).max(0);
|
||||
query_vals.push((value, idx));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ViewQueryResult::Some(query_vals))
|
||||
}
|
||||
|
||||
fn get_res_impl(&self, lua: &mlua::Lua) -> mlua::Result<mlua::MultiValue> {
|
||||
let world = self.world.read();
|
||||
|
||||
let qresults = self.get_query_results(self.dynamic_view.entity)?;
|
||||
let qvals = match qresults {
|
||||
ViewQueryResult::None => return mlua::Value::Nil.into_lua_multi(lua),
|
||||
ViewQueryResult::AlwaysNone => return mlua::Value::Nil.into_lua_multi(lua),
|
||||
ViewQueryResult::FilterDeny => return mlua::Value::Nil.into_lua_multi(lua),
|
||||
ViewQueryResult::Some(vec) => vec,
|
||||
};
|
||||
|
||||
let dv = self.dynamic_view.clone();
|
||||
if let Some(row) = dv.get(&world) {
|
||||
let reg = world.get_resource::<TypeRegistry>().unwrap();
|
||||
let mut vals = vec![];
|
||||
for d in row.iter() {
|
||||
let id = d.info.type_id().as_rust();
|
||||
|
||||
let reg_type = reg.get_type(id)
|
||||
.expect("Requested type was not found in TypeRegistry");
|
||||
let proxy = reg_type.get_data::<ReflectLuaProxy>()
|
||||
// TODO: properly handle this error
|
||||
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
||||
let value = proxy.as_lua(lua, d.ptr.cast()).unwrap()
|
||||
.into_lua(lua).unwrap();
|
||||
|
||||
vals.push(value);
|
||||
}
|
||||
|
||||
// insert query values to the result row
|
||||
for (v, i) in qvals {
|
||||
vals.insert(i as _, v);
|
||||
}
|
||||
|
||||
vals.into_lua_multi(lua)
|
||||
} else {
|
||||
mlua::Value::Nil.into_lua_multi(lua)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::UserData for ViewOneResult {
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method_mut("get", |lua, this, ()| {
|
||||
this.get_res_impl(lua)
|
||||
});
|
||||
methods.add_meta_method(mlua::MetaMethod::Call, |lua, this, ()| {
|
||||
this.get_res_impl(lua)
|
||||
});
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ use lyra_resource::ResourceManager;
|
|||
use mlua::{IntoLua, ObjectLike};
|
||||
|
||||
use super::{
|
||||
ecs::{View, ViewResult}, reflect_user_data, wrappers::{LuaResHandleToComponent, LuaTick, LuaWrappedEventProxy}, Error, ReflectLuaProxy, ReflectedIterator, TypeLookup, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE
|
||||
ecs::{View, ViewOneResult, ViewResult}, reflect_user_data, wrappers::{LuaResHandleToComponent, LuaTick, LuaWrappedEventProxy}, Error, ReflectLuaProxy, ReflectedIterator, TypeLookup, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE
|
||||
};
|
||||
|
||||
impl mlua::FromLua for ScriptEntity {
|
||||
|
@ -356,5 +356,8 @@ impl mlua::UserData for ScriptWorldPtr {
|
|||
let w = this.read();
|
||||
Ok(LuaTick(w.current_tick()))
|
||||
});
|
||||
methods.add_method("view_one", |_, this, (entity, view): (ScriptEntity, mlua::UserDataRef<View>)| {
|
||||
ViewOneResult::new(this.clone(), *entity, &view)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue