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

View File

@ -25,3 +25,12 @@ end
function Has(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

View File

@ -7,14 +7,20 @@ pub use changed::*;
mod has;
pub use has::*;
mod not;
pub use not::*;
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)]
enum QueryInner {
Component(LuaComponent),
Function(mlua::Function)
Function(mlua::Function),
}
#[derive(Clone)]
@ -33,15 +39,39 @@ impl LuaQuery {
///
/// > WARNING: ensure that the world pointer is not locked. If its locked when you call this,
/// 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);
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)),
}
}
}
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)]
pub enum LuaQueryResult {
None,
@ -59,20 +89,20 @@ impl mlua::IntoLua for LuaQueryResult {
match self {
LuaQueryResult::None => {
t.set("result", "none")?;
},
}
LuaQueryResult::AlwaysNone => {
t.set("result", "always_none")?;
},
}
LuaQueryResult::FilterPass => {
t.set("result", "filter_pass")?;
},
}
LuaQueryResult::FilterDeny => {
t.set("result", "filter_deny")?;
},
}
LuaQueryResult::Some(value) => {
t.set("result", "some")?;
t.set("val", value)?;
},
}
}
t.into_lua(lua)
@ -93,17 +123,22 @@ fn malformed_table_error_query_result(ty: &'static str, missing_field: &str) ->
mlua::Error::FromLuaConversionError {
from: ty,
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 {
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
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"))?;
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"))?;
if var_name != "query_result" {
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"))?;
let result_str = result.as_str();
@ -123,17 +159,16 @@ impl mlua::FromLua for LuaQueryResult {
"filter_pass" => Ok(Self::FilterPass),
"filter_deny" => Ok(Self::FilterDeny),
"value" => {
let val: mlua::Value = table.get("val")
let val: mlua::Value = table
.get("val")
.map_err(|_| malformed_table_error_query_result(ty, "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_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:
@ -48,6 +48,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
globals.set("ResQuery", ctx.create_proxy::<LuaResQuery>()?)?;
globals.set("ChangedQuery", ctx.create_proxy::<LuaChangedQuery>()?)?;
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::<LuaFreeFlyCamera>(&ctx, &globals, "FreeFlyCamera")?;