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
5 changed files with 118 additions and 28 deletions
Showing only changes of commit 7c2efe3c6f - Show all commits

View File

@ -77,11 +77,9 @@ end
end ]] end ]]
function on_update() function on_update()
-- Although WorldTransform isn't used, I only want to -- Get entities without WorldTransform
-- modify entities with that component. local view = View.new(Transform, Not(Has(WorldTransform)), Res(DeltaTime))
local view = View.new(Transform, Has(WorldTransform), Res(DeltaTime))
local res = world:view_query(view) local res = world:view_query(view)
---@param transform Transform ---@param transform Transform
---@param dt DeltaTime ---@param dt DeltaTime
for entity, transform, dt in res:iter() do for entity, transform, dt in res:iter() do

View File

@ -24,4 +24,13 @@ end
---@return HasQuery ---@return HasQuery
function Has(val) function Has(val)
return HasQuery.new(val) return HasQuery.new(val)
end
---Create a `NotQuery` filter that will allow results if
---
---@see NotQuery
---@param val function|table|userdata
---@return NotQuery
function Not(val)
return NotQuery.new(val)
end end

View File

@ -7,14 +7,20 @@ pub use changed::*;
mod has; mod has;
pub use has::*; pub use has::*;
mod not;
pub use not::*;
use lyra_ecs::Entity; use lyra_ecs::Entity;
use crate::{lua::{LuaComponent, FN_NAME_INTERNAL_ECS_QUERY_RESULT}, ScriptEntity, ScriptWorldPtr}; use crate::{
lua::{LuaComponent, FN_NAME_INTERNAL_ECS_QUERY_RESULT},
ScriptEntity, ScriptWorldPtr,
};
#[derive(Clone)] #[derive(Clone)]
enum QueryInner { enum QueryInner {
Component(LuaComponent), Component(LuaComponent),
Function(mlua::Function) Function(mlua::Function),
} }
#[derive(Clone)] #[derive(Clone)]
@ -30,18 +36,42 @@ impl LuaQuery {
} }
/// Get the result of the query /// Get the result of the query
/// ///
/// > WARNING: ensure that the world pointer is not locked. If its locked when you call this, /// > WARNING: ensure that the world pointer is not locked. If its locked when you call this,
/// you WILL cause a deadlock. /// you WILL cause a deadlock.
pub fn get_query_result(&self, world: ScriptWorldPtr, entity: Entity) -> mlua::Result<mlua::Value> { pub fn get_query_result(
&self,
world: ScriptWorldPtr,
entity: Entity,
) -> mlua::Result<mlua::Value> {
let lua_en = ScriptEntity(entity); let lua_en = ScriptEntity(entity);
match &self.0 { match &self.0 {
QueryInner::Component(comp) => comp.call_method(FN_NAME_INTERNAL_ECS_QUERY_RESULT, (world, lua_en)), QueryInner::Component(comp) => {
comp.call_method(FN_NAME_INTERNAL_ECS_QUERY_RESULT, (world, lua_en))
}
QueryInner::Function(function) => function.call((world, lua_en)), QueryInner::Function(function) => function.call((world, lua_en)),
} }
} }
} }
impl mlua::FromLua for LuaQuery {
fn from_lua(value: mlua::Value, lua: &mlua::Lua) -> mlua::Result<Self> {
let tyname = value.type_name();
if let Some(f) = value.as_function() {
Ok(Self(QueryInner::Function(f.clone())))
} else if let Ok(c) = LuaComponent::from_lua(value, lua) {
Ok(Self(QueryInner::Component(c)))
} else {
Err(mlua::Error::FromLuaConversionError {
from: tyname,
to: "Query".into(),
message: Some("expected query function, table, or user data".into()),
})
}
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum LuaQueryResult { pub enum LuaQueryResult {
None, None,
@ -59,20 +89,20 @@ impl mlua::IntoLua for LuaQueryResult {
match self { match self {
LuaQueryResult::None => { LuaQueryResult::None => {
t.set("result", "none")?; t.set("result", "none")?;
}, }
LuaQueryResult::AlwaysNone => { LuaQueryResult::AlwaysNone => {
t.set("result", "always_none")?; t.set("result", "always_none")?;
}, }
LuaQueryResult::FilterPass => { LuaQueryResult::FilterPass => {
t.set("result", "filter_pass")?; t.set("result", "filter_pass")?;
}, }
LuaQueryResult::FilterDeny => { LuaQueryResult::FilterDeny => {
t.set("result", "filter_deny")?; t.set("result", "filter_deny")?;
}, }
LuaQueryResult::Some(value) => { LuaQueryResult::Some(value) => {
t.set("result", "some")?; t.set("result", "some")?;
t.set("val", value)?; t.set("val", value)?;
}, }
} }
t.into_lua(lua) t.into_lua(lua)
@ -93,17 +123,22 @@ fn malformed_table_error_query_result(ty: &'static str, missing_field: &str) ->
mlua::Error::FromLuaConversionError { mlua::Error::FromLuaConversionError {
from: ty, from: ty,
to: "QueryResult".into(), to: "QueryResult".into(),
message: Some(format!("malformed table, cannot convert, failed to get field '{}'", missing_field)), message: Some(format!(
"malformed table, cannot convert, failed to get field '{}'",
missing_field
)),
} }
} }
impl mlua::FromLua for LuaQueryResult { impl mlua::FromLua for LuaQueryResult {
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> { fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
let ty = value.type_name(); let ty = value.type_name();
let table = value.as_table() let table = value
.as_table()
.ok_or(from_lua_error_query_result(ty, "expected Table"))?; .ok_or(from_lua_error_query_result(ty, "expected Table"))?;
let var_name: String = table.get("enum_ty") let var_name: String = table
.get("enum_ty")
.map_err(|_| malformed_table_error_query_result(ty, "enum_ty"))?; .map_err(|_| malformed_table_error_query_result(ty, "enum_ty"))?;
if var_name != "query_result" { if var_name != "query_result" {
return Err(mlua::Error::FromLuaConversionError { return Err(mlua::Error::FromLuaConversionError {
@ -113,7 +148,8 @@ impl mlua::FromLua for LuaQueryResult {
}); });
} }
let result: String = table.get("result") let result: String = table
.get("result")
.map_err(|_| malformed_table_error_query_result(ty, "result"))?; .map_err(|_| malformed_table_error_query_result(ty, "result"))?;
let result_str = result.as_str(); let result_str = result.as_str();
@ -123,17 +159,16 @@ impl mlua::FromLua for LuaQueryResult {
"filter_pass" => Ok(Self::FilterPass), "filter_pass" => Ok(Self::FilterPass),
"filter_deny" => Ok(Self::FilterDeny), "filter_deny" => Ok(Self::FilterDeny),
"value" => { "value" => {
let val: mlua::Value = table.get("val") let val: mlua::Value = table
.get("val")
.map_err(|_| malformed_table_error_query_result(ty, "val"))?; .map_err(|_| malformed_table_error_query_result(ty, "val"))?;
Ok(Self::Some(val)) Ok(Self::Some(val))
},
_ => {
Err(mlua::Error::FromLuaConversionError {
from: ty,
to: "QueryResult".into(),
message: Some(format!("unknown result type: '{}'", result_str)),
})
} }
_ => Err(mlua::Error::FromLuaConversionError {
from: ty,
to: "QueryResult".into(),
message: Some(format!("unknown result type: '{}'", result_str)),
}),
} }
} }
} }

View File

@ -0,0 +1,47 @@
use crate::{
lua::FN_NAME_INTERNAL_ECS_QUERY_RESULT,
ScriptEntity, ScriptWorldPtr,
};
use super::LuaQuery;
#[derive(Clone)]
pub struct LuaNotQuery(LuaQuery);
impl mlua::FromLua for LuaNotQuery {
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: "NotQuery".into(),
message: None,
})
.and_then(|ud| ud.borrow::<Self>())
.map(|ud| ud.clone())
}
}
impl mlua::UserData for LuaNotQuery {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_function("new", |_, q: LuaQuery| {
Ok(Self(q))
});
methods.add_method(
FN_NAME_INTERNAL_ECS_QUERY_RESULT,
|_, 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)
}
},
);
}
}

View File

@ -1,7 +1,7 @@
use lyra_ecs::ResourceObject; use lyra_ecs::ResourceObject;
use lyra_reflect::Reflect; use lyra_reflect::Reflect;
use crate::{lua::{ecs::{query::{LuaChangedQuery, LuaHasQuery, LuaResQuery}, View}, wrappers::*, LuaContext, LuaWrapper, RegisterLuaType, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr}; use crate::{lua::{ecs::{query::{LuaChangedQuery, LuaHasQuery, LuaNotQuery, LuaResQuery}, View}, wrappers::*, LuaContext, LuaWrapper, RegisterLuaType, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr};
//fn register_lua_proxy::<T: //fn register_lua_proxy::<T:
@ -48,6 +48,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
globals.set("ResQuery", ctx.create_proxy::<LuaResQuery>()?)?; globals.set("ResQuery", ctx.create_proxy::<LuaResQuery>()?)?;
globals.set("ChangedQuery", ctx.create_proxy::<LuaChangedQuery>()?)?; globals.set("ChangedQuery", ctx.create_proxy::<LuaChangedQuery>()?)?;
globals.set("HasQuery", ctx.create_proxy::<LuaHasQuery>()?)?; globals.set("HasQuery", ctx.create_proxy::<LuaHasQuery>()?)?;
globals.set("NotQuery", ctx.create_proxy::<LuaNotQuery>()?)?;
expose_comp_table_wrapper::<LuaCamera>(&ctx, &globals, "Camera")?; expose_comp_table_wrapper::<LuaCamera>(&ctx, &globals, "Camera")?;
expose_comp_table_wrapper::<LuaFreeFlyCamera>(&ctx, &globals, "FreeFlyCamera")?; expose_comp_table_wrapper::<LuaFreeFlyCamera>(&ctx, &globals, "FreeFlyCamera")?;