Improve Lua ECS #30

Merged
SeanOMik merged 15 commits from feat/improve-lua-ecs-29 into main 2024-10-30 03:22:50 +00:00
6 changed files with 89 additions and 67 deletions
Showing only changes of commit f2ff2a9855 - Show all commits

View File

@ -6,6 +6,8 @@ use crate::{
ReflectBranch, ScriptEntity, ScriptWorldPtr,
};
use super::LuaQueryResult;
#[derive(Clone)]
pub struct LuaChangedQuery(LuaComponent);
@ -38,13 +40,13 @@ impl mlua::UserData for LuaChangedQuery {
match &reflect.reflect_branch {
ReflectBranch::Component(comp) => {
if !comp.reflect_is_changed(&world, *en).unwrap_or(false) {
return Ok(mlua::Value::Boolean(false));
return Ok(LuaQueryResult::FilterDeny);
}
// get the pointer of the component in the archetype column.
let arch = match world.entity_archetype(*en) {
Some(a) => a,
None => return Ok(mlua::Value::Boolean(false))
None => return Ok(LuaQueryResult::FilterDeny),
};
let arch_idx = *arch.entity_indexes().get(&en).unwrap();
@ -52,7 +54,7 @@ impl mlua::UserData for LuaChangedQuery {
Some(col) => col,
None => {
// the entity doesn't have the component
return Ok(mlua::Value::Boolean(false));
return Ok(LuaQueryResult::FilterDeny);
}
};
@ -67,17 +69,17 @@ impl mlua::UserData for LuaChangedQuery {
// this should actually be safe since the ReflectedIterator
// attempts to get the type data before it is tried here
.expect("Type does not have ReflectLuaProxy as a TypeData");
proxy.as_lua(lua, col_ptr)
Ok(LuaQueryResult::Some(proxy.as_lua(lua, col_ptr)?))
}
ReflectBranch::Resource(res) => {
// Check if the resource was changed. Per API spec, must return false.
match res.reflect_is_changed(&world) {
Some(false) => {
return Ok(mlua::Value::Boolean(false));
return Ok(LuaQueryResult::FilterDeny);
}
None => {
// the resource was not found
return Ok(mlua::Value::Nil);
return Ok(LuaQueryResult::AlwaysNone);
}
_ => {}
}
@ -92,7 +94,11 @@ impl mlua::UserData for LuaChangedQuery {
.get_data::<ReflectLuaProxy>()
.expect("Type does not have ReflectLuaProxy as a TypeData");
proxy.as_lua(lua, res_ptr.cast()).and_then(|ud| ud.into_lua(lua))
Ok(LuaQueryResult::Some(
proxy
.as_lua(lua, res_ptr.cast())
.and_then(|ud| ud.into_lua(lua))?,
))
}
}
},

View File

@ -3,6 +3,8 @@ use crate::{
ScriptEntity, ScriptWorldPtr,
};
use super::LuaQueryResult;
#[derive(Clone)]
pub struct LuaHasQuery(LuaComponent);
@ -43,11 +45,15 @@ impl mlua::UserData for LuaHasQuery {
// try to find the entity's archetype and the component column in the archetype
let arch = match world.entity_archetype(*en) {
Some(a) => a,
None => return Ok(false),
None => return Ok(LuaQueryResult::FilterDeny)
};
let component_col = arch.get_column(tyid);
Ok(component_col.is_some())
if component_col.is_some() {
Ok(LuaQueryResult::FilterPass)
} else {
Ok(LuaQueryResult::FilterDeny)
}
},
);
}

View File

@ -43,7 +43,7 @@ impl LuaQuery {
&self,
world: ScriptWorldPtr,
entity: Entity,
) -> mlua::Result<mlua::Value> {
) -> mlua::Result<LuaQueryResult> {
let lua_en = ScriptEntity(entity);
match &self.0 {
QueryInner::Component(comp) => {
@ -158,7 +158,7 @@ impl mlua::FromLua for LuaQueryResult {
"always_none" => Ok(Self::AlwaysNone),
"filter_pass" => Ok(Self::FilterPass),
"filter_deny" => Ok(Self::FilterDeny),
"value" => {
"some" => {
let val: mlua::Value = table
.get("val")
.map_err(|_| malformed_table_error_query_result(ty, "val"))?;

View File

@ -3,7 +3,7 @@ use crate::{
ScriptEntity, ScriptWorldPtr,
};
use super::LuaQuery;
use super::{LuaQuery, LuaQueryResult};
#[derive(Clone)]
pub struct LuaNotQuery(LuaQuery);
@ -34,12 +34,12 @@ impl mlua::UserData for LuaNotQuery {
|_, this, (world, en): (ScriptWorldPtr, ScriptEntity)| {
let res = this.0.get_query_result(world, en.0)?;
if let Some(res_bool) = res.as_boolean() {
Ok(!res_bool)
} else if res.is_nil() {
Ok(true)
} else {
Ok(false)
match res {
LuaQueryResult::None => Ok(LuaQueryResult::FilterPass),
super::LuaQueryResult::AlwaysNone => Ok(LuaQueryResult::FilterPass),
super::LuaQueryResult::FilterPass => Ok(LuaQueryResult::FilterDeny),
super::LuaQueryResult::FilterDeny => Ok(LuaQueryResult::FilterPass),
super::LuaQueryResult::Some(_) => Ok(LuaQueryResult::FilterDeny),
}
},
);

View File

@ -2,9 +2,12 @@ use lyra_reflect::{ReflectWorldExt, RegisteredType};
use mlua::IntoLua;
use crate::{
lua::{LuaComponent, ReflectLuaProxy, FN_NAME_INTERNAL_ECS_QUERY_RESULT}, ScriptEntity, ScriptWorldPtr
lua::{LuaComponent, ReflectLuaProxy, FN_NAME_INTERNAL_ECS_QUERY_RESULT},
ScriptEntity, ScriptWorldPtr,
};
use super::LuaQueryResult;
#[derive(Clone)]
pub struct LuaResQuery {
ty: LuaComponent,
@ -27,30 +30,34 @@ impl mlua::FromLua for LuaResQuery {
impl mlua::UserData for LuaResQuery {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_function("new", |_, comp: LuaComponent| {
Ok(Self {
ty: comp
})
});
methods.add_function("new", |_, comp: LuaComponent| Ok(Self { ty: comp }));
methods.add_method(FN_NAME_INTERNAL_ECS_QUERY_RESULT, |lua, this, (world, _): (ScriptWorldPtr, ScriptEntity)| {
let mut world = world.write();
let reflect = this.ty.reflect_type()?;
methods.add_method(
FN_NAME_INTERNAL_ECS_QUERY_RESULT,
|lua, this, (world, _): (ScriptWorldPtr, ScriptEntity)| {
let mut world = world.write();
let reflect = this.ty.reflect_type()?;
let res = reflect.reflect_branch.as_resource_unchecked();
if let Some(res_ptr) = res.reflect_ptr(&mut world) {
let reg_type = world
.get_type::<RegisteredType>(reflect.reflect_branch.reflect_type_id())
.expect("Resource is not type registered!");
let proxy = reg_type
.get_data::<ReflectLuaProxy>()
.expect("Type does not have ReflectLuaProxy as a TypeData");
let res = reflect.reflect_branch.as_resource_unchecked();
if let Some(res_ptr) = res.reflect_ptr(&mut world) {
let reg_type = world
.get_type::<RegisteredType>(reflect.reflect_branch.reflect_type_id())
.expect("Resource is not type registered!");
let proxy = reg_type
.get_data::<ReflectLuaProxy>()
.expect("Type does not have ReflectLuaProxy as a TypeData");
proxy.as_lua(lua, res_ptr.cast()).and_then(|ud| ud.into_lua(lua))
} else {
// if the resource is not found in the world, return nil
Ok(mlua::Value::Nil)
}
});
Ok(LuaQueryResult::Some(
proxy
.as_lua(lua, res_ptr.cast())
.and_then(|ud| ud.into_lua(lua))?,
))
} else {
// if the resource is not found in the world, return nil
//Ok(mlua::Value::Nil)
Ok(LuaQueryResult::AlwaysNone)
}
},
);
}
}

View File

@ -15,7 +15,7 @@ use crate::{
ScriptBorrow, ScriptWorldPtr,
};
use super::query::LuaQuery;
use super::query::{LuaQuery, LuaQueryResult};
#[derive(Clone)]
enum ViewQueryItem {
@ -104,10 +104,12 @@ impl mlua::UserData for View {
}
}
enum QueryResult {
#[derive(Debug, Clone)]
enum QueryRowResult {
None,
AlwaysNone,
Some(Vec<(mlua::Value, u32)>)
FilterDeny,
Some(Vec<(mlua::Value, u32)>),
}
#[derive(Clone)]
@ -209,7 +211,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<QueryResult> {
fn get_query_results(&self, entity: Entity) -> mlua::Result<QueryRowResult> {
let mut query_vals = vec![];
// A modifier is used that will be incremented every time a filter allowed the query.
@ -217,23 +219,23 @@ impl ViewResult {
let mut index_mod = 0;
for (query, i) in &self.queries {
let qres = query.get_query_result(self.world.clone(), entity)?;
if let Some(val) = qres.as_boolean() {
if !val {
return Ok(QueryResult::None);
}
index_mod += 1;
// do not push a boolean to values, its considered a filter
continue;
} else if qres.is_nil() {
return Ok(QueryResult::AlwaysNone);
match qres {
LuaQueryResult::None => return Ok(QueryRowResult::None),
LuaQueryResult::AlwaysNone => return Ok(QueryRowResult::AlwaysNone),
LuaQueryResult::FilterPass => {
// do not push a boolean to values, its considered a filter
index_mod += 1;
},
LuaQueryResult::FilterDeny => return Ok(QueryRowResult::FilterDeny),
LuaQueryResult::Some(value) => {
let idx = (*i - index_mod).max(0);
query_vals.push((value, idx));
},
}
let idx = (*i - index_mod).max(0);
query_vals.push((qres, idx));
}
Ok(QueryResult::Some(query_vals))
Ok(QueryRowResult::Some(query_vals))
}
}
@ -259,14 +261,14 @@ impl mlua::UserData for ViewResult {
Some((en, mut vals)) => {
loop {
let query_vals = match this.get_query_results(en)? {
QueryResult::Some(v) => v,
QueryResult::AlwaysNone => {
QueryRowResult::Some(v) => v,
QueryRowResult::AlwaysNone => {
return mlua::Value::Nil.into_lua_multi(lua);
},
QueryResult::None => {
QueryRowResult::None | QueryRowResult::FilterDeny => {
// try to get it next loop
continue;
}
},
};
// insert query values to the result row
@ -300,13 +302,14 @@ impl mlua::UserData for ViewResult {
LuaEntityRef::new(this.world.clone(), en).into_lua(lua)?;
let query_vals = match this.get_query_results(en)? {
QueryResult::Some(v) => v,
QueryResult::AlwaysNone => {
QueryRowResult::Some(v) => v,
QueryRowResult::AlwaysNone => {
return mlua::Value::Nil.into_lua_multi(lua);
},
QueryResult::None => {
QueryRowResult::None | QueryRowResult::FilterDeny => {
// try to get it next loop
continue;
}
},
};
// insert query values to the result row