lua: create LuaHasQuery

This commit is contained in:
SeanOMik 2024-10-23 16:31:47 -04:00
parent 4dbd96832f
commit 8072ec1c7e
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
8 changed files with 103 additions and 10 deletions

View File

@ -79,13 +79,12 @@ end ]]
function on_update() function on_update()
-- Although WorldTransform isn't used, I only want to -- Although WorldTransform isn't used, I only want to
-- modify entities with that component. -- modify entities with that component.
local view = View.new(Transform, 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 _wt WorldTransform
---@param dt DeltaTime ---@param dt DeltaTime
for entity, transform, _wt, dt in res:iter() do for entity, transform, dt in res:iter() do
transform:translate(0, 0.15 * dt, 0) transform:translate(0, 0.15 * dt, 0)
entity:update(transform) entity:update(transform)
end end

View File

@ -1,13 +1,27 @@
---Create a Resource Query ---Create a Resource query that will return the specific ECS world resource.
---
---@see ResQuery
---@param resource table|userdata ---@param resource table|userdata
---@return ResQuery ---@return ResQuery
function Res(resource) function Res(resource)
return ResQuery.new(resource) return ResQuery.new(resource)
end end
---Create a `ChangedQuery` of a resource or component. ---Create a `ChangedQuery` query that will return only if the resource or component has changed
---since last tick.
---
---@see ChangedQuery
---@param val table|userdata ---@param val table|userdata
---@return ChangedQuery ---@return ChangedQuery
function Changed(val) function Changed(val)
return ChangedQuery.new(val) return ChangedQuery.new(val)
end
---Create a `HasQuery` filter that will return only if the entity has a specific component.
---
---@see HasQuery
---@param val table|userdata
---@return HasQuery
function Has(val)
return HasQuery.new(val)
end end

View File

@ -38,6 +38,7 @@ impl ReflectBranch {
/// ///
/// # Panics /// # Panics
/// If `self` is not a variant of [`ReflectBranch::Component`]. /// If `self` is not a variant of [`ReflectBranch::Component`].
#[deprecated(note = "use ReflectBranch::as_component instead")]
pub fn as_component_unchecked(&self) -> &ReflectedComponent { pub fn as_component_unchecked(&self) -> &ReflectedComponent {
match self { match self {
ReflectBranch::Component(c) => c, ReflectBranch::Component(c) => c,
@ -45,6 +46,16 @@ impl ReflectBranch {
} }
} }
/// Gets self as a [`ReflectedComponent`].
///
/// Returns `None` if `self` is not a component.
pub fn as_component(&self) -> Option<&ReflectedComponent> {
match self {
ReflectBranch::Component(c) => Some(c),
_ => None,
}
}
/// Returns a boolean indicating if `self` is a reflection of a Component. /// Returns a boolean indicating if `self` is a reflection of a Component.
pub fn is_component(&self) -> bool { pub fn is_component(&self) -> bool {
matches!(self, ReflectBranch::Component(_)) matches!(self, ReflectBranch::Component(_))

View File

@ -42,7 +42,10 @@ impl mlua::UserData for LuaChangedQuery {
} }
// get the pointer of the component in the archetype column. // get the pointer of the component in the archetype column.
let arch = world.entity_archetype(*en).unwrap(); let arch = match world.entity_archetype(*en) {
Some(a) => a,
None => return Ok(mlua::Value::Boolean(false))
};
let arch_idx = *arch.entity_indexes().get(&en).unwrap(); let arch_idx = *arch.entity_indexes().get(&en).unwrap();
let col = match arch.get_column(tyid) { let col = match arch.get_column(tyid) {

View File

@ -0,0 +1,54 @@
use crate::{
lua::{LuaComponent, FN_NAME_INTERNAL_ECS_QUERY_RESULT},
ScriptEntity, ScriptWorldPtr,
};
#[derive(Clone)]
pub struct LuaHasQuery(LuaComponent);
impl mlua::FromLua for LuaHasQuery {
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: "HasQuery".into(),
message: None,
})
.and_then(|ud| ud.borrow::<Self>())
.map(|ud| ud.clone())
}
}
impl mlua::UserData for LuaHasQuery {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_function("new", |_, comp: LuaComponent| {
let reflect = comp.reflect_type()?;
if !reflect.reflect_branch.is_component() {
Err(mlua::Error::runtime("provided type is not a component!"))
} else {
Ok(Self(comp))
}
});
methods.add_method(
FN_NAME_INTERNAL_ECS_QUERY_RESULT,
|_, this, (world, en): (ScriptWorldPtr, ScriptEntity)| {
let world = world.write();
let reflect = this.0.reflect_type()?;
let tyid = reflect.reflect_branch.reflect_type_id();
// 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),
};
let component_col = arch.get_column(tyid);
Ok(component_col.is_some())
},
);
}
}

View File

@ -4,6 +4,9 @@ pub use res::*;
mod changed; mod changed;
pub use changed::*; pub use changed::*;
mod has;
pub use has::*;
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};

View File

@ -158,7 +158,8 @@ impl ViewResult {
let reflect = ud let reflect = ud
.call_function::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ()) .call_function::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
.expect("Type does not implement 'reflect_type' properly"); .expect("Type does not implement 'reflect_type' properly");
let refl_comp = reflect.reflect_branch.as_component_unchecked(); 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); let dyn_type = QueryDynamicType::from_info(refl_comp.info);
view.push(dyn_type); view.push(dyn_type);
@ -211,18 +212,25 @@ impl ViewResult {
fn get_query_results(&self, entity: Entity) -> mlua::Result<QueryResult> { fn get_query_results(&self, entity: Entity) -> mlua::Result<QueryResult> {
let mut query_vals = vec![]; 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 { for (query, i) in &self.queries {
let qres = query.get_query_result(self.world.clone(), entity)?; let qres = query.get_query_result(self.world.clone(), entity)?;
if let Some(val) = qres.as_boolean() { if let Some(val) = qres.as_boolean() {
// do not push a boolean to values, its considered a filter
if !val { if !val {
return Ok(QueryResult::None); return Ok(QueryResult::None);
} }
index_mod += 1;
// do not push a boolean to values, its considered a filter
continue;
} else if qres.is_nil() { } else if qres.is_nil() {
return Ok(QueryResult::AlwaysNone); return Ok(QueryResult::AlwaysNone);
} }
query_vals.push((qres, *i)); let idx = (*i - index_mod).max(0);
query_vals.push((qres, idx));
} }
Ok(QueryResult::Some(query_vals)) Ok(QueryResult::Some(query_vals))

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, 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, 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:
@ -47,6 +47,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
globals.set("View", ctx.create_proxy::<View>()?)?; globals.set("View", ctx.create_proxy::<View>()?)?;
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>()?)?;
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")?;