Improve Lua ECS #30
|
@ -90,15 +90,19 @@ function on_update()
|
||||||
end
|
end
|
||||||
end, Transform) ]]
|
end, Transform) ]]
|
||||||
|
|
||||||
|
-- although WorldTransform isn't used, I only want to modify entities with that component.
|
||||||
local view = View.new(Transform, WorldTransform, Res(DeltaTime))
|
local view = View.new(Transform, WorldTransform, Res(DeltaTime))
|
||||||
local res = world:view_query(view)
|
local res = world:view_query(view)
|
||||||
|
for entity, transform, _wt, dt in res:iter() do
|
||||||
for entity, transform, world_tran, dt in res:iter() do
|
|
||||||
print("Entity is at: " .. tostring(world_tran))
|
|
||||||
|
|
||||||
transform:translate(0, 0.15 * dt, 0)
|
transform:translate(0, 0.15 * dt, 0)
|
||||||
entity:update(transform)
|
entity:update(transform)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local changed_view = View.new(Changed(Transform))
|
||||||
|
local changed_res = world:view_query(changed_view)
|
||||||
|
for _, transform in changed_res:iter() do
|
||||||
|
print("Entity transform changed to: " .. tostring(transform))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[ function on_post_update()
|
--[[ function on_post_update()
|
||||||
|
|
|
@ -214,16 +214,34 @@ impl ComponentColumn {
|
||||||
moved_index
|
moved_index
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the pointer of the component for the entity.
|
/// Get the pointer of the component for an entity.
|
||||||
///
|
///
|
||||||
/// It is assumed that the component will be mutated, meaning the component's tick will be
|
/// It is assumed that the component will be mutated, meaning the component's tick will be
|
||||||
/// updated.
|
/// updated.
|
||||||
pub fn get_entity_ptr(&mut self, entity_index: usize, tick: &Tick) -> NonNull<u8> {
|
pub fn component_ptr(&mut self, entity_index: usize, tick: &Tick) -> NonNull<u8> {
|
||||||
self.entity_ticks.insert(entity_index, *tick);
|
self.entity_ticks[entity_index] = *tick;
|
||||||
let size = self.info.layout().size();
|
let size = self.info.layout().size();
|
||||||
unsafe { NonNull::new_unchecked(self.borrow_ptr().as_ptr().add(entity_index * size)) }
|
unsafe { NonNull::new_unchecked(self.borrow_ptr().as_ptr().add(entity_index * size)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the pointer of the component for an entity without ticking.
|
||||||
|
///
|
||||||
|
/// Since this does not tick, only use this if you know the pointer will not be mutated.
|
||||||
|
pub fn component_ptr_non_tick(&self, entity_index: usize) -> NonNull<u8> {
|
||||||
|
let size = self.info.layout().size();
|
||||||
|
unsafe { NonNull::new_unchecked(self.borrow_ptr().as_ptr().add(entity_index * size)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the tick of a component for an entity.
|
||||||
|
pub fn component_tick(&self, entity_index: usize) -> Option<Tick> {
|
||||||
|
self.entity_ticks.get(entity_index).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn component_has_changed(&self, entity_index: usize, world_tick: Tick) -> Option<bool> {
|
||||||
|
self.component_tick(entity_index)
|
||||||
|
.map(|tick| *tick >= *world_tick - 1)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn borrow_ptr(&self) -> Ref<NonNull<u8>> {
|
pub fn borrow_ptr(&self) -> Ref<NonNull<u8>> {
|
||||||
self.data.borrow()
|
self.data.borrow()
|
||||||
}
|
}
|
||||||
|
@ -272,7 +290,7 @@ pub struct Archetype {
|
||||||
/// Can be used to map `ArchetypeEntityId` to an Entity since `ArchetypeEntityId` has
|
/// Can be used to map `ArchetypeEntityId` to an Entity since `ArchetypeEntityId` has
|
||||||
/// the index that the entity is stored at.
|
/// the index that the entity is stored at.
|
||||||
pub(crate) entities: Vec<Entity>,
|
pub(crate) entities: Vec<Entity>,
|
||||||
pub(crate) columns: Vec<ComponentColumn>,
|
pub columns: Vec<ComponentColumn>,
|
||||||
capacity: usize,
|
capacity: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,4 +77,4 @@ impl Entities {
|
||||||
pub(crate) fn insert_entity_record(&mut self, entity: Entity, record: Record) {
|
pub(crate) fn insert_entity_record(&mut self, entity: Entity, record: Record) {
|
||||||
self.arch_index.insert(entity.id, record);
|
self.arch_index.insert(entity.id, record);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -24,6 +24,10 @@ impl DynamicViewState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn queries_num(&self) -> usize {
|
||||||
|
self.queries.len()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, dyn_query: QueryDynamicType) {
|
pub fn push(&mut self, dyn_query: QueryDynamicType) {
|
||||||
self.queries.push(dyn_query);
|
self.queries.push(dyn_query);
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,7 @@ impl<'a, T: ResourceObject> Res<'a, T> {
|
||||||
|
|
||||||
/// Returns a boolean indicating if the resource changed.
|
/// Returns a boolean indicating if the resource changed.
|
||||||
pub fn changed(&self) -> bool {
|
pub fn changed(&self) -> bool {
|
||||||
*self.inner.tick - 1 >= *self.world_tick - 1
|
*self.inner.tick >= *self.world_tick - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The tick that this resource was last modified at
|
/// The tick that this resource was last modified at
|
||||||
|
|
|
@ -26,11 +26,11 @@ pub struct Record {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct World {
|
pub struct World {
|
||||||
pub(crate) archetypes: HashMap<ArchetypeId, Archetype>,
|
pub archetypes: HashMap<ArchetypeId, Archetype>,
|
||||||
next_archetype_id: ArchetypeId,
|
next_archetype_id: ArchetypeId,
|
||||||
resources: HashMap<TypeId, ResourceData>,
|
resources: HashMap<TypeId, ResourceData>,
|
||||||
tracker: TickTracker,
|
tracker: TickTracker,
|
||||||
pub(crate) entities: Entities,
|
pub entities: Entities,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for World {
|
impl Default for World {
|
||||||
|
@ -467,6 +467,13 @@ impl World {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the tick of a resource.
|
||||||
|
///
|
||||||
|
/// This tick represents the last time the resource was mutated.
|
||||||
|
pub fn get_resource_tick<T: ResourceObject>(&self) -> Option<Tick> {
|
||||||
|
self.get_tracked_resource::<T>().map(|r| r.tick)
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets a reference to a change tracked resource.
|
/// Gets a reference to a change tracked resource.
|
||||||
///
|
///
|
||||||
/// You will have to manually downcast the inner resource. Most people don't need this, see
|
/// You will have to manually downcast the inner resource. Most people don't need this, see
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{any::{Any, TypeId}, cell::{Ref, RefMut}};
|
use std::{any::{Any, TypeId}, cell::{Ref, RefMut}};
|
||||||
|
|
||||||
use lyra_ecs::{Component, ComponentInfo, World, Entity, DynamicBundle};
|
use lyra_ecs::{query::{filter::Changed, TickOf}, Component, ComponentInfo, DynamicBundle, Entity, Tick, World};
|
||||||
|
|
||||||
use crate::{Reflect, FromType};
|
use crate::{Reflect, FromType};
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ pub struct ReflectedComponent {
|
||||||
fn_bundle_insert: for<'a> fn (dynamic_bundle: &'a mut DynamicBundle, component: Box<dyn Reflect>),
|
fn_bundle_insert: for<'a> fn (dynamic_bundle: &'a mut DynamicBundle, component: Box<dyn Reflect>),
|
||||||
fn_reflect: for<'a> fn (world: &'a World, entity: Entity) -> Option<Ref<'a, dyn Reflect>>,
|
fn_reflect: for<'a> fn (world: &'a World, entity: Entity) -> Option<Ref<'a, dyn Reflect>>,
|
||||||
fn_reflect_mut: for<'a> fn (world: &'a mut World, entity: Entity) -> Option<RefMut<'a, dyn Reflect>>,
|
fn_reflect_mut: for<'a> fn (world: &'a mut World, entity: Entity) -> Option<RefMut<'a, dyn Reflect>>,
|
||||||
|
fn_reflect_tick: for<'a> fn (world: &'a World, entity: Entity) -> Option<Tick>,
|
||||||
|
fn_reflect_is_changed: for<'a> fn (world: &'a World, entity: Entity) -> Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReflectedComponent {
|
impl ReflectedComponent {
|
||||||
|
@ -40,6 +42,14 @@ impl ReflectedComponent {
|
||||||
pub fn reflect_mut<'a>(&'a mut self, world: &'a mut World, entity: Entity) -> Option<RefMut<'a, dyn Reflect>> {
|
pub fn reflect_mut<'a>(&'a mut self, world: &'a mut World, entity: Entity) -> Option<RefMut<'a, dyn Reflect>> {
|
||||||
(self.fn_reflect_mut)(world, entity)
|
(self.fn_reflect_mut)(world, entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reflect_tick<'a>(&'a self, world: &'a World, entity: Entity) -> Option<Tick> {
|
||||||
|
(self.fn_reflect_tick)(world, entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_is_changed<'a>(&'a self, world: &'a World, entity: Entity) -> Option<bool> {
|
||||||
|
(self.fn_reflect_is_changed)(world, entity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Component + Reflect> FromType<C> for ReflectedComponent {
|
impl<C: Component + Reflect> FromType<C> for ReflectedComponent {
|
||||||
|
@ -69,6 +79,12 @@ impl<C: Component + Reflect> FromType<C> for ReflectedComponent {
|
||||||
world.view_one::<&mut C>(entity)
|
world.view_one::<&mut C>(entity)
|
||||||
.get().map(|c| c as RefMut<dyn Reflect>)
|
.get().map(|c| c as RefMut<dyn Reflect>)
|
||||||
},
|
},
|
||||||
|
fn_reflect_tick: |world: &World, entity: Entity| {
|
||||||
|
world.view_one::<TickOf<C>>(entity).get()
|
||||||
|
},
|
||||||
|
fn_reflect_is_changed: |world: &World, entity: Entity| {
|
||||||
|
world.view_one::<Changed<C>>(entity).get()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{any::{Any, TypeId}, ptr::NonNull};
|
use std::{any::{Any, TypeId}, ptr::NonNull};
|
||||||
|
|
||||||
use lyra_ecs::{AtomicRef, AtomicRefMut, ResourceObject, World};
|
use lyra_ecs::{AtomicRef, AtomicRefMut, ResourceObject, Tick, World};
|
||||||
|
|
||||||
use crate::{Reflect, FromType};
|
use crate::{Reflect, FromType};
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ pub struct ReflectedResource {
|
||||||
pub type_id: TypeId,
|
pub type_id: TypeId,
|
||||||
|
|
||||||
fn_reflect: for<'a> fn (world: &'a World) -> Option<AtomicRef<'a, dyn Reflect>>,
|
fn_reflect: for<'a> fn (world: &'a World) -> Option<AtomicRef<'a, dyn Reflect>>,
|
||||||
|
fn_reflect_tick: for<'a> fn (world: &'a World) -> Option<Tick>,
|
||||||
|
fn_reflect_is_changed: fn (world: &World) -> Option<bool>,
|
||||||
fn_reflect_mut: for<'a> fn (world: &'a mut World) -> Option<AtomicRefMut<'a, dyn Reflect>>,
|
fn_reflect_mut: for<'a> fn (world: &'a mut World) -> Option<AtomicRefMut<'a, dyn Reflect>>,
|
||||||
fn_reflect_ptr: fn (world: &mut World) -> Option<NonNull<u8>>,
|
fn_reflect_ptr: fn (world: &mut World) -> Option<NonNull<u8>>,
|
||||||
fn_refl_insert: fn (world: &mut World, this: Box<dyn Reflect>),
|
fn_refl_insert: fn (world: &mut World, this: Box<dyn Reflect>),
|
||||||
|
@ -20,6 +22,14 @@ impl ReflectedResource {
|
||||||
(self.fn_reflect)(world)
|
(self.fn_reflect)(world)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reflect_tick(&self, world: &World) -> Option<Tick> {
|
||||||
|
(self.fn_reflect_tick)(world)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reflect_is_changed(&self, world: &World) -> Option<bool> {
|
||||||
|
(self.fn_reflect_is_changed)(world)
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieves a mutable reflected resource from the world.
|
/// Retrieves a mutable reflected resource from the world.
|
||||||
pub fn reflect_mut<'a>(&self, world: &'a mut World) -> Option<AtomicRefMut<'a, dyn Reflect>> {
|
pub fn reflect_mut<'a>(&self, world: &'a mut World) -> Option<AtomicRefMut<'a, dyn Reflect>> {
|
||||||
(self.fn_reflect_mut)(world)
|
(self.fn_reflect_mut)(world)
|
||||||
|
@ -47,6 +57,13 @@ impl<T: ResourceObject + Reflect> FromType<T> for ReflectedResource {
|
||||||
AtomicRef::map(r, |r| r as &dyn Reflect)
|
AtomicRef::map(r, |r| r as &dyn Reflect)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
fn_reflect_tick: |world: &World| {
|
||||||
|
world.get_resource_tick::<T>()
|
||||||
|
},
|
||||||
|
fn_reflect_is_changed: |world: &World| {
|
||||||
|
world.get_resource::<T>()
|
||||||
|
.map(|r| r.changed())
|
||||||
|
},
|
||||||
fn_reflect_mut: |world: &mut World| {
|
fn_reflect_mut: |world: &mut World| {
|
||||||
world.get_resource_mut::<T>()
|
world.get_resource_mut::<T>()
|
||||||
.map(|r| {
|
.map(|r| {
|
||||||
|
|
|
@ -3,4 +3,11 @@
|
||||||
---@return ResQuery
|
---@return ResQuery
|
||||||
function Res(resource)
|
function Res(resource)
|
||||||
return ResQuery.new(resource)
|
return ResQuery.new(resource)
|
||||||
|
end
|
||||||
|
|
||||||
|
---Create a Changed Query of a resource or component.
|
||||||
|
---@param resource table|userdata
|
||||||
|
---@return ChangedQuery
|
||||||
|
function Changed(val)
|
||||||
|
return ChangedQuery.new(val)
|
||||||
end
|
end
|
|
@ -57,15 +57,13 @@ fn next_lua(lua: &mlua::Lua, world: &lyra_ecs::World, dyn_view: &mut DynamicView
|
||||||
let mut dynamic_row = vec![];
|
let mut dynamic_row = vec![];
|
||||||
for d in row.iter() {
|
for d in row.iter() {
|
||||||
let id = d.info.type_id().as_rust();
|
let id = d.info.type_id().as_rust();
|
||||||
/* let reflected_components =
|
|
||||||
unsafe { reflected_components.as_ref().unwrap().as_ref() }; */
|
|
||||||
|
|
||||||
let reg_type = reflected_components.get_type(id)
|
let reg_type = reflected_components.get_type(id)
|
||||||
.expect("Requested type was not found in TypeRegistry");
|
.expect("Requested type was not found in TypeRegistry");
|
||||||
let proxy = reg_type.get_data::<ReflectLuaProxy>()
|
let proxy = reg_type.get_data::<ReflectLuaProxy>()
|
||||||
// TODO: properly handle this error
|
// TODO: properly handle this error
|
||||||
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
||||||
let value = (proxy.fn_as_lua)(lua, d.ptr.cast()).unwrap()
|
let value = proxy.as_lua(lua, d.ptr.cast()).unwrap()
|
||||||
.into_lua(lua).unwrap();
|
.into_lua(lua).unwrap();
|
||||||
|
|
||||||
dynamic_row.push(ReflectedItem {
|
dynamic_row.push(ReflectedItem {
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry};
|
||||||
|
use mlua::IntoLua;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
lua::{LuaComponent, ReflectLuaProxy, FN_NAME_INTERNAL_ECS_QUERY_RESULT},
|
||||||
|
ReflectBranch, ScriptEntity, ScriptWorldPtr,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LuaChangedQuery(LuaComponent);
|
||||||
|
|
||||||
|
impl mlua::FromLua for LuaChangedQuery {
|
||||||
|
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: "ChangedQuery".into(),
|
||||||
|
message: None,
|
||||||
|
})
|
||||||
|
.and_then(|ud| ud.borrow::<Self>())
|
||||||
|
.map(|ud| ud.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl mlua::UserData for LuaChangedQuery {
|
||||||
|
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||||
|
methods.add_function("new", |_, comp: LuaComponent| Ok(Self(comp)));
|
||||||
|
|
||||||
|
methods.add_method(
|
||||||
|
FN_NAME_INTERNAL_ECS_QUERY_RESULT,
|
||||||
|
|lua, this, (world, en): (ScriptWorldPtr, ScriptEntity)| {
|
||||||
|
let mut world = world.write();
|
||||||
|
let reflect = this.0.reflect_type()?;
|
||||||
|
|
||||||
|
let tyid = reflect.reflect_branch.reflect_type_id();
|
||||||
|
match &reflect.reflect_branch {
|
||||||
|
ReflectBranch::Component(comp) => {
|
||||||
|
if !comp.reflect_is_changed(&world, *en).unwrap_or(false) {
|
||||||
|
return Ok(mlua::Value::Boolean(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the pointer of the component in the archetype column.
|
||||||
|
let arch = world.entity_archetype(*en).unwrap();
|
||||||
|
let arch_idx = *arch.entity_indexes().get(&en).unwrap();
|
||||||
|
|
||||||
|
let col = match arch.get_column(tyid) {
|
||||||
|
Some(col) => col,
|
||||||
|
None => {
|
||||||
|
// the entity doesn't have the component
|
||||||
|
return Ok(mlua::Value::Boolean(false));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let col_ptr = col.component_ptr_non_tick(*arch_idx as usize).cast();
|
||||||
|
|
||||||
|
// get the type registry to apply the new value
|
||||||
|
let reg = world.get_resource::<TypeRegistry>().unwrap();
|
||||||
|
let reg_type = reg.get_type(tyid).unwrap();
|
||||||
|
|
||||||
|
let proxy = reg_type
|
||||||
|
.get_data::<ReflectLuaProxy>()
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// the resource was not found
|
||||||
|
return Ok(mlua::Value::Nil);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unwrap is safe here since the match above would verify that the
|
||||||
|
// resource exists.
|
||||||
|
let res_ptr = res.reflect_ptr(&mut world).unwrap();
|
||||||
|
let reg_type = world
|
||||||
|
.get_type::<RegisteredType>(tyid)
|
||||||
|
.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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +1,40 @@
|
||||||
mod res;
|
mod res;
|
||||||
pub use res::*;
|
pub use res::*;
|
||||||
|
|
||||||
use crate::{lua::{LuaComponent, FN_NAME_INTERNAL_ECS_QUERY_RESULT}, ScriptWorldPtr};
|
mod changed;
|
||||||
|
pub use changed::*;
|
||||||
|
|
||||||
|
use lyra_ecs::Entity;
|
||||||
|
|
||||||
|
use crate::{lua::{LuaComponent, FN_NAME_INTERNAL_ECS_QUERY_RESULT}, ScriptEntity, ScriptWorldPtr};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LuaQuery {
|
enum QueryInner {
|
||||||
query: LuaComponent
|
Component(LuaComponent),
|
||||||
|
Function(mlua::Function)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LuaQuery(QueryInner);
|
||||||
|
|
||||||
impl LuaQuery {
|
impl LuaQuery {
|
||||||
pub fn new(query: LuaComponent) -> Self {
|
pub fn new(query: LuaComponent) -> Self {
|
||||||
Self {
|
Self(QueryInner::Component(query))
|
||||||
query
|
}
|
||||||
}
|
|
||||||
|
pub fn from_function(f: mlua::Function) -> Self {
|
||||||
|
Self(QueryInner::Function(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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) -> mlua::Result<mlua::Value> {
|
pub fn get_query_result(&self, world: ScriptWorldPtr, entity: Entity) -> mlua::Result<mlua::Value> {
|
||||||
self.query.call_method(FN_NAME_INTERNAL_ECS_QUERY_RESULT, world)
|
let lua_en = ScriptEntity(entity);
|
||||||
|
match &self.0 {
|
||||||
|
QueryInner::Component(comp) => comp.call_method(FN_NAME_INTERNAL_ECS_QUERY_RESULT, (world, lua_en)),
|
||||||
|
QueryInner::Function(function) => function.call((world, lua_en)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,8 +2,7 @@ use lyra_reflect::{ReflectWorldExt, RegisteredType};
|
||||||
use mlua::IntoLua;
|
use mlua::IntoLua;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
lua::{LuaComponent, ReflectLuaProxy, FN_NAME_INTERNAL_ECS_QUERY_RESULT},
|
lua::{LuaComponent, ReflectLuaProxy, FN_NAME_INTERNAL_ECS_QUERY_RESULT}, ScriptEntity, ScriptWorldPtr
|
||||||
ScriptWorldPtr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -34,7 +33,7 @@ impl mlua::UserData for LuaResQuery {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_method(FN_NAME_INTERNAL_ECS_QUERY_RESULT, |lua, this, world: ScriptWorldPtr| {
|
methods.add_method(FN_NAME_INTERNAL_ECS_QUERY_RESULT, |lua, this, (world, _): (ScriptWorldPtr, ScriptEntity)| {
|
||||||
let mut world = world.write();
|
let mut world = world.write();
|
||||||
let reflect = this.ty.reflect_type()?;
|
let reflect = this.ty.reflect_type()?;
|
||||||
|
|
||||||
|
@ -47,7 +46,7 @@ impl mlua::UserData for LuaResQuery {
|
||||||
.get_data::<ReflectLuaProxy>()
|
.get_data::<ReflectLuaProxy>()
|
||||||
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
||||||
|
|
||||||
(proxy.fn_as_lua)(lua, res_ptr.cast()).and_then(|ud| ud.into_lua(lua))
|
proxy.as_lua(lua, res_ptr.cast()).and_then(|ud| ud.into_lua(lua))
|
||||||
} else {
|
} else {
|
||||||
// if the resource is not found in the world, return nil
|
// if the resource is not found in the world, return nil
|
||||||
Ok(mlua::Value::Nil)
|
Ok(mlua::Value::Nil)
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
use std::{ptr::NonNull, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use lyra_ecs::{query::dynamic::{DynamicViewState, QueryDynamicType}, Entity};
|
use atomic_refcell::AtomicRefCell;
|
||||||
|
use lyra_ecs::{
|
||||||
|
query::dynamic::{DynamicViewState, QueryDynamicType},
|
||||||
|
Entity,
|
||||||
|
};
|
||||||
use mlua::{IntoLua, IntoLuaMulti, ObjectLike};
|
use mlua::{IntoLua, IntoLuaMulti, ObjectLike};
|
||||||
|
|
||||||
use crate::{lua::{LuaComponent, LuaEntityRef, ReflectedIteratorOwned, TypeLookup, WorldError, FN_NAME_INTERNAL_ECS_QUERY_RESULT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptBorrow, ScriptWorldPtr};
|
use crate::{
|
||||||
|
lua::{
|
||||||
|
LuaComponent, LuaEntityRef, ReflectedIteratorOwned, TypeLookup, WorldError,
|
||||||
|
FN_NAME_INTERNAL_ECS_QUERY_RESULT, FN_NAME_INTERNAL_REFLECT_TYPE,
|
||||||
|
},
|
||||||
|
ScriptBorrow, ScriptWorldPtr,
|
||||||
|
};
|
||||||
|
|
||||||
use super::query::LuaQuery;
|
use super::query::LuaQuery;
|
||||||
|
|
||||||
|
@ -31,44 +41,37 @@ impl mlua::FromLua for ViewQueryItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ViewQueryItem {
|
impl ViewQueryItem {
|
||||||
pub fn is_filter(&self) -> bool {
|
|
||||||
// TODO: check if UserData is a rust implemented filter
|
|
||||||
matches!(self, ViewQueryItem::Function(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the QueryItem has a function of `name`.
|
/// Returns `true` if the QueryItem has a function of `name`.
|
||||||
///
|
///
|
||||||
/// Returns `false` if self is a function.
|
/// Returns `false` if self is a function.
|
||||||
pub fn has_function(&self, name: &str) -> mlua::Result<bool> {
|
pub fn has_function(&self, name: &str) -> mlua::Result<bool> {
|
||||||
match self {
|
match self {
|
||||||
Self::UserData(ud) => {
|
Self::UserData(ud) => ud.get::<mlua::Value>(name).map(|v| !v.is_nil()),
|
||||||
ud.get::<mlua::Value>(name).map(|v| !v.is_nil())
|
Self::Table(t) => t.contains_key(name),
|
||||||
},
|
|
||||||
Self::Table(t) => {
|
|
||||||
t.contains_key(name)
|
|
||||||
},
|
|
||||||
Self::Function(_) => Ok(false),
|
Self::Function(_) => Ok(false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if self is a Query.
|
||||||
|
///
|
||||||
|
/// If self is a function, it will return true. Else, it checks for a function with the
|
||||||
|
/// name of [`FN_NAME_INTERNAL_ECS_QUERY_RESULT`] on the table or userdata. If the function
|
||||||
|
/// is found, it returns true.
|
||||||
pub fn is_query(&self) -> mlua::Result<bool> {
|
pub fn is_query(&self) -> mlua::Result<bool> {
|
||||||
self.has_function(FN_NAME_INTERNAL_ECS_QUERY_RESULT)
|
Ok(matches!(self, ViewQueryItem::Function(_))
|
||||||
|
|| self.has_function(FN_NAME_INTERNAL_ECS_QUERY_RESULT)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clone self into a [`LuaComponent`].
|
/// Get self as a [`LuaQuery`].
|
||||||
pub fn as_component(&self) -> Option<LuaComponent> {
|
///
|
||||||
|
/// If self is a function, it assumes that it is a filter.
|
||||||
|
pub fn as_query(&self) -> LuaQuery {
|
||||||
match self.clone() {
|
match self.clone() {
|
||||||
ViewQueryItem::UserData(ud) => Some(LuaComponent::UserData(ud)),
|
ViewQueryItem::UserData(ud) => LuaQuery::new(LuaComponent::UserData(ud)),
|
||||||
ViewQueryItem::Table(t) => Some(LuaComponent::Table(t)),
|
ViewQueryItem::Table(t) => LuaQuery::new(LuaComponent::Table(t)),
|
||||||
ViewQueryItem::Function(_) => None
|
ViewQueryItem::Function(function) => LuaQuery::from_function(function),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pub fn get_lookup(&self) -> Option<Res<TypeLookup>> {
|
|
||||||
match self {
|
|
||||||
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -79,7 +82,8 @@ pub struct View {
|
||||||
impl mlua::FromLua for View {
|
impl mlua::FromLua for View {
|
||||||
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||||
let tyname = value.type_name();
|
let tyname = value.type_name();
|
||||||
value.as_userdata()
|
value
|
||||||
|
.as_userdata()
|
||||||
.ok_or(mlua::Error::FromLuaConversionError {
|
.ok_or(mlua::Error::FromLuaConversionError {
|
||||||
from: tyname,
|
from: tyname,
|
||||||
to: "View".into(),
|
to: "View".into(),
|
||||||
|
@ -100,13 +104,18 @@ impl mlua::UserData for View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum QueryResult {
|
||||||
|
None,
|
||||||
|
AlwaysNone,
|
||||||
|
Some(Vec<(mlua::Value, u32)>)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ViewResult {
|
pub struct ViewResult {
|
||||||
world: ScriptWorldPtr,
|
world: ScriptWorldPtr,
|
||||||
reflect_iter: Arc<atomic_refcell::AtomicRefCell<ReflectedIteratorOwned>>,
|
reflected_iter: Arc<atomic_refcell::AtomicRefCell<ReflectedIteratorOwned>>,
|
||||||
/// The queries and the index they would be inserted in the result.
|
/// The queries and the index they would be inserted in the result.
|
||||||
queries: Vec<(LuaQuery, u32)>,
|
queries: Vec<(LuaQuery, u32)>,
|
||||||
reg_key: Arc<atomic_refcell::AtomicRefCell<Option<mlua::RegistryKey>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for ViewResult {}
|
unsafe impl Send for ViewResult {}
|
||||||
|
@ -120,8 +129,7 @@ impl ViewResult {
|
||||||
|
|
||||||
for (idx, comp) in items.iter().enumerate() {
|
for (idx, comp) in items.iter().enumerate() {
|
||||||
if comp.is_query()? {
|
if comp.is_query()? {
|
||||||
// Unwrap is safe since `is_query` ensures that the component is convertible.
|
queries.push((comp.as_query(), idx as u32));
|
||||||
queries.push((LuaQuery::new(comp.as_component().unwrap()), idx as u32));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,23 +137,17 @@ impl ViewResult {
|
||||||
ViewQueryItem::Table(t) => {
|
ViewQueryItem::Table(t) => {
|
||||||
let name: String = t.get(mlua::MetaMethod::Type.name())?;
|
let name: String = t.get(mlua::MetaMethod::Type.name())?;
|
||||||
|
|
||||||
let lookup =
|
let lookup = w.get_resource::<TypeLookup>().ok_or(mlua::Error::runtime(
|
||||||
w
|
"Unable to lookup table proxy, none were ever registered!",
|
||||||
.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(|| {
|
let info = lookup.comp_info_from_name.get(&name).ok_or_else(|| {
|
||||||
mlua::Error::BadArgument {
|
mlua::Error::BadArgument {
|
||||||
to: Some("ViewResult.new".into()),
|
to: Some("ViewResult.new".into()),
|
||||||
pos: 2 + idx,
|
pos: 2 + idx,
|
||||||
name: Some("query...".into()),
|
name: Some("query...".into()),
|
||||||
cause: Arc::new(mlua::Error::external(
|
cause: Arc::new(mlua::Error::external(WorldError::LuaInvalidUsage(
|
||||||
WorldError::LuaInvalidUsage(format!(
|
format!("the 'Table' with name {} is unknown to the engine!", name),
|
||||||
"the 'Table' with name {} is unknown to the engine!",
|
))),
|
||||||
name
|
|
||||||
)),
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -161,7 +163,9 @@ impl ViewResult {
|
||||||
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);
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
// functions are queries, the if statement at the start would cause this to
|
||||||
|
// be unreachable.
|
||||||
|
ViewQueryItem::Function(_) => unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,24 +179,24 @@ impl ViewResult {
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
world,
|
world,
|
||||||
reflect_iter: Arc::new(atomic_refcell::AtomicRefCell::new(reflected_iter)),
|
reflected_iter: Arc::new(AtomicRefCell::new(reflected_iter)),
|
||||||
queries,
|
queries,
|
||||||
reg_key: Arc::new(atomic_refcell::AtomicRefCell::new(None)),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the next row of components
|
/// Get the next row of components
|
||||||
fn next_components(&mut self, lua: &mlua::Lua) -> Result<Option<(Entity, mlua::MultiValue)>, mlua::Error> {
|
fn next_components(
|
||||||
let mut query_iter = self.reflect_iter.borrow_mut();
|
&mut self,
|
||||||
|
lua: &mlua::Lua,
|
||||||
|
) -> Result<Option<(Entity, mlua::MultiValue)>, mlua::Error> {
|
||||||
|
let mut query_iter = self.reflected_iter.borrow_mut();
|
||||||
if let Some(row) = query_iter.next_lua(lua) {
|
if let Some(row) = query_iter.next_lua(lua) {
|
||||||
let r = row
|
let (values, _): (Vec<_>, Vec<_>) = row
|
||||||
.row
|
.row
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|r| (r.comp_val, r.comp_ptr.cast::<()>()))
|
.map(|r| (r.comp_val, r.comp_ptr.cast::<()>()))
|
||||||
.collect::<Vec<_>>();
|
.unzip();
|
||||||
let (values, _) =
|
|
||||||
itertools::multiunzip::<(Vec<mlua::Value>, Vec<NonNull<()>>), _>(r);
|
|
||||||
let mult_val = mlua::MultiValue::from_iter(values.into_iter());
|
let mult_val = mlua::MultiValue::from_iter(values.into_iter());
|
||||||
Ok(Some((row.entity, mult_val)))
|
Ok(Some((row.entity, mult_val)))
|
||||||
} else {
|
} else {
|
||||||
|
@ -200,37 +204,36 @@ impl ViewResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the query results
|
/// Get the query results and the indexes that they were provided in.
|
||||||
fn get_query_results(&self) -> mlua::Result<Option<Vec<(mlua::Value, u32)>>> {
|
///
|
||||||
|
/// 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> {
|
||||||
let mut query_vals = vec![];
|
let mut query_vals = vec![];
|
||||||
let mut query_allows = true;
|
|
||||||
for (query, i) in &self.queries {
|
for (query, i) in &self.queries {
|
||||||
let qres = query.get_query_result(self.world.clone())?;
|
let qres = query.get_query_result(self.world.clone(), entity)?;
|
||||||
if let Some(val) = qres.as_boolean() {
|
if let Some(val) = qres.as_boolean() {
|
||||||
if val {
|
// do not push a boolean to values, its considered a filter
|
||||||
query_allows = false;
|
if !val {
|
||||||
break;
|
return Ok(QueryResult::None);
|
||||||
}
|
}
|
||||||
} else if qres.is_nil() {
|
} else if qres.is_nil() {
|
||||||
query_allows = false;
|
return Ok(QueryResult::AlwaysNone);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
query_vals.push((qres, *i));
|
query_vals.push((qres, *i));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !query_allows {
|
Ok(QueryResult::Some(query_vals))
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
Ok(Some(query_vals))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl mlua::FromLua for ViewResult {
|
impl mlua::FromLua for ViewResult {
|
||||||
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||||
let tyname = value.type_name();
|
let tyname = value.type_name();
|
||||||
value.as_userdata()
|
value
|
||||||
|
.as_userdata()
|
||||||
.ok_or(mlua::Error::FromLuaConversionError {
|
.ok_or(mlua::Error::FromLuaConversionError {
|
||||||
from: tyname,
|
from: tyname,
|
||||||
to: "View".into(),
|
to: "View".into(),
|
||||||
|
@ -244,65 +247,78 @@ impl mlua::FromLua for ViewResult {
|
||||||
impl mlua::UserData for ViewResult {
|
impl mlua::UserData for ViewResult {
|
||||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||||
methods.add_method_mut("next", |lua, this, ()| {
|
methods.add_method_mut("next", |lua, this, ()| {
|
||||||
let query_vals = match this.get_query_results()? {
|
|
||||||
Some(v) => v,
|
|
||||||
None => {
|
|
||||||
return mlua::Value::Nil.into_lua_multi(lua);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match this.next_components(lua)? {
|
match this.next_components(lua)? {
|
||||||
Some((en, mut vals)) => {
|
Some((en, mut vals)) => {
|
||||||
// insert query values to the result row
|
loop {
|
||||||
for (qval, qi) in query_vals {
|
let query_vals = match this.get_query_results(en)? {
|
||||||
vals.insert(qi as _, qval);
|
QueryResult::Some(v) => v,
|
||||||
}
|
QueryResult::AlwaysNone => {
|
||||||
|
return mlua::Value::Nil.into_lua_multi(lua);
|
||||||
|
},
|
||||||
|
QueryResult::None => {
|
||||||
|
// try to get it next loop
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
vals.push_front(LuaEntityRef::new(this.world.clone(), en).into_lua(lua)?);
|
// insert query values to the result row
|
||||||
Ok(vals)
|
for (qval, qi) in query_vals {
|
||||||
},
|
vals.insert(qi as _, qval);
|
||||||
None => mlua::Value::Nil.into_lua_multi(lua)
|
}
|
||||||
|
|
||||||
|
vals.push_front(LuaEntityRef::new(this.world.clone(), en).into_lua(lua)?);
|
||||||
|
return Ok(vals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => mlua::Value::Nil.into_lua_multi(lua),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_method("iter", |lua, this, ()| {
|
methods.add_method("iter", |lua, this, ()| {
|
||||||
let key_arc = Arc::new(atomic_refcell::AtomicRefCell::new(Some(lua.create_registry_value(this.clone())?)));
|
let key_arc = Arc::new(atomic_refcell::AtomicRefCell::new(Some(
|
||||||
|
lua.create_registry_value(this.clone())?,
|
||||||
|
)));
|
||||||
|
|
||||||
lua.create_function(move |lua, ()| {
|
lua.create_function(move |lua, ()| {
|
||||||
let mut key_mut = key_arc.borrow_mut();
|
let mut key_mut = key_arc.borrow_mut();
|
||||||
|
|
||||||
if let Some(key) = key_mut.as_ref() {
|
if let Some(key) = key_mut.as_ref() {
|
||||||
let mut this = lua.registry_value::<Self>(&key)?;
|
let mut this = lua.registry_value::<mlua::UserDataRefMut<Self>>(&key)?;
|
||||||
|
|
||||||
let query_vals = match this.get_query_results()? {
|
loop {
|
||||||
Some(v) => v,
|
match this.next_components(lua)? {
|
||||||
None => {
|
Some((en, mut vals)) => {
|
||||||
return mlua::Value::Nil.into_lua_multi(lua);
|
let lua_en =
|
||||||
}
|
LuaEntityRef::new(this.world.clone(), en).into_lua(lua)?;
|
||||||
};
|
|
||||||
|
|
||||||
match this.next_components(lua)? {
|
let query_vals = match this.get_query_results(en)? {
|
||||||
Some((en, mut vals)) => {
|
QueryResult::Some(v) => v,
|
||||||
let lua_en = LuaEntityRef::new(this.world, en)
|
QueryResult::AlwaysNone => {
|
||||||
.into_lua(lua)?;
|
return mlua::Value::Nil.into_lua_multi(lua);
|
||||||
|
},
|
||||||
|
QueryResult::None => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// insert query values to the result row
|
// insert query values to the result row
|
||||||
for (qval, qi) in query_vals {
|
for (qval, qi) in query_vals {
|
||||||
vals.insert(qi as _, qval);
|
vals.insert(qi as _, qval);
|
||||||
|
}
|
||||||
|
|
||||||
|
vals.push_front(lua_en);
|
||||||
|
return Ok(vals);
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
// If this is the last row, remove the registry value
|
||||||
|
// This doesn't protect against iterators that aren't fully consumed,
|
||||||
|
// that would cause a leak in the lua registry.
|
||||||
|
// TODO: fix leak
|
||||||
|
let key = key_mut.take().unwrap();
|
||||||
|
lua.remove_registry_value(key)?;
|
||||||
|
|
||||||
vals.push_front(lua_en);
|
return mlua::Value::Nil.into_lua_multi(lua);
|
||||||
Ok(vals)
|
}
|
||||||
},
|
|
||||||
None => {
|
|
||||||
// If this is the last row, remove the registry value
|
|
||||||
// This doesn't protect against iterators that aren't fully consumed,
|
|
||||||
// that would cause a leak in the lua registry.
|
|
||||||
// TODO: fix leak
|
|
||||||
let key = key_mut.take().unwrap();
|
|
||||||
lua.remove_registry_value(key)?;
|
|
||||||
|
|
||||||
mlua::Value::Nil.into_lua_multi(lua)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -311,4 +327,4 @@ impl mlua::UserData for ViewResult {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use mlua::{IntoLua, ObjectLike};
|
||||||
|
|
||||||
use crate::{ScriptBorrow, ScriptWorldPtr};
|
use crate::{ScriptBorrow, ScriptWorldPtr};
|
||||||
|
|
||||||
use super::{reflect_user_data, Error, ReflectLuaProxy, TypeLookup, FN_NAME_INTERNAL_REFLECT_TYPE};
|
use super::{reflect_type_user_data, Error, ReflectLuaProxy, TypeLookup, FN_NAME_INTERNAL_REFLECT_TYPE};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum LuaComponent {
|
pub enum LuaComponent {
|
||||||
|
@ -49,7 +49,7 @@ impl LuaComponent {
|
||||||
lookup.typeid_from_name.get(&name).cloned()
|
lookup.typeid_from_name.get(&name).cloned()
|
||||||
}
|
}
|
||||||
Self::UserData(ud) => {
|
Self::UserData(ud) => {
|
||||||
let lua_comp = reflect_user_data(ud);
|
let lua_comp = reflect_type_user_data(ud);
|
||||||
let refl_comp = lua_comp.reflect_branch.as_component_unchecked();
|
let refl_comp = lua_comp.reflect_branch.as_component_unchecked();
|
||||||
Some(refl_comp.info.type_id().as_rust())
|
Some(refl_comp.info.type_id().as_rust())
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ impl mlua::UserData for LuaEntityRef {
|
||||||
let arch = world.entity_archetype_mut(this.en).unwrap();
|
let arch = world.entity_archetype_mut(this.en).unwrap();
|
||||||
let arch_idx = *arch.entity_indexes().get(&this.en).unwrap();
|
let arch_idx = *arch.entity_indexes().get(&this.en).unwrap();
|
||||||
let col = arch.get_column_mut(tid).unwrap();
|
let col = arch.get_column_mut(tid).unwrap();
|
||||||
let col_ptr = col.get_entity_ptr(*arch_idx as usize, &world_tick).cast();
|
let col_ptr = col.component_ptr(*arch_idx as usize, &world_tick).cast();
|
||||||
|
|
||||||
// get the type registry to apply the new value
|
// get the type registry to apply the new value
|
||||||
let reg = world.get_resource::<TypeRegistry>().unwrap();
|
let reg = world.get_resource::<TypeRegistry>().unwrap();
|
||||||
|
@ -149,7 +149,7 @@ impl mlua::UserData for LuaEntityRef {
|
||||||
// this should actually be safe since the ReflectedIterator
|
// this should actually be safe since the ReflectedIterator
|
||||||
// attempts to get the type data before it is tried here
|
// attempts to get the type data before it is tried here
|
||||||
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
||||||
(proxy.fn_apply)(lua, col_ptr, &comp)?;
|
proxy.apply(lua, col_ptr, &comp)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -95,11 +95,16 @@ pub const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect";
|
||||||
|
|
||||||
/// Name of a Lua function to retrieve the query result from a Userdata, or Table.
|
/// Name of a Lua function to retrieve the query result from a Userdata, or Table.
|
||||||
///
|
///
|
||||||
/// This function must return a Lua value and take in a single argument: [`ScriptWorldPtr`].
|
/// The function must match the following definition: `fn(ScriptWorldPtr, Entity) -> LuaValue`.
|
||||||
/// If `nil` is returned, the query in a [`View`](crate::lua::ecs::View) will not provide any
|
///
|
||||||
/// results.\
|
/// When `nil` is returned, its considered that the query will not result in anything for this
|
||||||
/// If it returns a boolean, the result will act as a filter, and the value will not be in
|
/// [`View`], **no matter the entity**. When the query is used in a [`View`] and returns `nil`,
|
||||||
/// the result.\
|
/// it will NOT check for other entities. This is used in the [`ResQuery`] Lua query. If the
|
||||||
|
/// resource is missing, it will always be missing for the [`View`], no matter the entity.
|
||||||
|
///
|
||||||
|
/// If it returns a boolean, the query will act as a filter. The boolean value will not be in the
|
||||||
|
/// result. When the boolean is `false`, other entities will be checked by the [`View`].
|
||||||
|
///
|
||||||
/// Any other value will be included in the result.
|
/// Any other value will be included in the result.
|
||||||
pub const FN_NAME_INTERNAL_ECS_QUERY_RESULT: &str = "__lyra_internal_ecs_query_result";
|
pub const FN_NAME_INTERNAL_ECS_QUERY_RESULT: &str = "__lyra_internal_ecs_query_result";
|
||||||
|
|
||||||
|
@ -288,6 +293,16 @@ impl mlua::UserData for ScriptBorrow {
|
||||||
|
|
||||||
/// Helper function used for reflecting userdata as a ScriptBorrow
|
/// Helper function used for reflecting userdata as a ScriptBorrow
|
||||||
pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow {
|
pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow {
|
||||||
|
let ud_name = ud.metatable().and_then(|mt| mt.get::<String>(mlua::MetaMethod::Type))
|
||||||
|
.unwrap_or("Unknown".to_string());
|
||||||
ud.call_method::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())
|
ud.call_method::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT, ())
|
||||||
.expect("Type does not implement internal reflect method properly")
|
.unwrap_or_else(|_| panic!("UserData of name '{}' does not implement internal reflect method properly", ud_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function used for reflecting userdata type as a ScriptBorrow
|
||||||
|
pub fn reflect_type_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow {
|
||||||
|
let ud_name = ud.metatable().and_then(|mt| mt.get::<String>(mlua::MetaMethod::Type))
|
||||||
|
.unwrap_or("Unknown".to_string());
|
||||||
|
ud.call_function::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
|
||||||
|
.unwrap_or_else(|_| panic!("UserData of name '{}' does not implement internal reflect type function properly", ud_name))
|
||||||
}
|
}
|
|
@ -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::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, 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:
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
|
||||||
globals.set("Window", ctx.create_proxy::<LuaWindow>()?)?;
|
globals.set("Window", ctx.create_proxy::<LuaWindow>()?)?;
|
||||||
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>()?)?;
|
||||||
|
|
||||||
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")?;
|
||||||
|
|
|
@ -78,9 +78,9 @@ pub struct TypeLookup {
|
||||||
/// A struct used for Proxying types to and from Lua.
|
/// A struct used for Proxying types to and from Lua.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ReflectLuaProxy {
|
pub struct ReflectLuaProxy {
|
||||||
pub fn_as_lua:
|
fn_as_lua:
|
||||||
for<'a> fn(lua: &'a mlua::Lua, this_ptr: NonNull<()>) -> mlua::Result<mlua::Value>,
|
for<'a> fn(lua: &'a mlua::Lua, this_ptr: NonNull<()>) -> mlua::Result<mlua::Value>,
|
||||||
pub fn_apply: for<'a> fn(
|
fn_apply: for<'a> fn(
|
||||||
lua: &'a mlua::Lua,
|
lua: &'a mlua::Lua,
|
||||||
this_ptr: NonNull<()>,
|
this_ptr: NonNull<()>,
|
||||||
value: &'a mlua::Value,
|
value: &'a mlua::Value,
|
||||||
|
@ -125,6 +125,16 @@ impl ReflectLuaProxy {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reflect the pointer to get a Lua value.
|
||||||
|
pub fn as_lua(&self, lua: &mlua::Lua, this_ptr: NonNull<()>) -> mlua::Result<mlua::Value> {
|
||||||
|
(self.fn_as_lua)(lua, this_ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the contents in the pointer to a Lua value.
|
||||||
|
pub fn apply(&self, lua: &mlua::Lua, this_ptr: NonNull<()>, value: &mlua::Value) -> mlua::Result<()> {
|
||||||
|
(self.fn_apply)(lua, this_ptr, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl mlua::FromLua for ScriptDynamicBundle {
|
impl mlua::FromLua for ScriptDynamicBundle {
|
||||||
|
|
|
@ -222,7 +222,7 @@ impl mlua::UserData for ScriptWorldPtr {
|
||||||
// this should actually be safe since the ReflectedIterator
|
// this should actually be safe since the ReflectedIterator
|
||||||
// attempts to get the type data before it is tried here
|
// attempts to get the type data before it is tried here
|
||||||
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
||||||
(proxy.fn_apply)(lua, ptr, &comp)?;
|
proxy.apply(lua, ptr, &comp)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
|
@ -262,7 +262,7 @@ impl mlua::UserData for ScriptWorldPtr {
|
||||||
.get_data::<ReflectLuaProxy>()
|
.get_data::<ReflectLuaProxy>()
|
||||||
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
.expect("Type does not have ReflectLuaProxy as a TypeData");
|
||||||
|
|
||||||
(proxy.fn_as_lua)(lua, res_ptr.cast()).and_then(|ud| ud.into_lua(lua))
|
proxy.as_lua(lua, res_ptr.cast()).and_then(|ud| ud.into_lua(lua))
|
||||||
} else {
|
} else {
|
||||||
// if the resource is not found in the world, return nil
|
// if the resource is not found in the world, return nil
|
||||||
Ok(mlua::Value::Nil)
|
Ok(mlua::Value::Nil)
|
||||||
|
|
Loading…
Reference in New Issue