Improve Lua ECS #30
|
@ -47,19 +47,20 @@ end
|
||||||
|
|
||||||
function on_first()
|
function on_first()
|
||||||
if not is_window_setup then
|
if not is_window_setup then
|
||||||
world:view(
|
local view = View.new(Window)
|
||||||
|
local res = world:view(view)
|
||||||
|
|
||||||
---@param w Window
|
---@param w Window
|
||||||
function (w)
|
for en, w in res:iter() do
|
||||||
if w.cursor_grab == CursorGrabMode.NONE then
|
if w.cursor_grab == CursorGrabMode.NONE then
|
||||||
w.cursor_grab = CursorGrabMode.LOCKED
|
w.cursor_grab = CursorGrabMode.LOCKED
|
||||||
w.cursor_visible = false
|
w.cursor_visible = false
|
||||||
return w
|
en:update(w)
|
||||||
else
|
else
|
||||||
is_window_setup = true
|
is_window_setup = true
|
||||||
print("Window setup")
|
print("Window setup")
|
||||||
end
|
end
|
||||||
end, Window
|
end
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@type EventReader
|
---@type EventReader
|
||||||
|
@ -81,7 +82,7 @@ end ]]
|
||||||
function on_update()
|
function on_update()
|
||||||
-- Get entities without WorldTransform
|
-- Get entities without WorldTransform
|
||||||
local view = View.new(Transform, Not(Has(WorldTransform)), Res(DeltaTime))
|
local view = View.new(Transform, Not(Has(WorldTransform)), Res(DeltaTime))
|
||||||
local res = world:view_query(view)
|
local res = world:view(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
|
||||||
|
@ -90,14 +91,14 @@ function on_update()
|
||||||
end
|
end
|
||||||
|
|
||||||
local changed_view = View.new(Changed(Transform))
|
local changed_view = View.new(Changed(Transform))
|
||||||
local changed_res = world:view_query(changed_view)
|
local changed_res = world:view(changed_view)
|
||||||
---@param transform Transform
|
---@param transform Transform
|
||||||
for _, transform in changed_res:iter() do
|
for _, transform in changed_res:iter() do
|
||||||
print("Entity transform changed to: '" .. tostring(transform) .. "' on tick " .. tostring(world:get_tick()))
|
print("Entity transform changed to: '" .. tostring(transform) .. "' on tick " .. tostring(world:get_tick()))
|
||||||
end
|
end
|
||||||
|
|
||||||
local tick_view = View.new(TickOf(Transform))
|
local tick_view = View.new(TickOf(Transform))
|
||||||
local tick_res = world:view_query(tick_view)
|
local tick_res = world:view(tick_view)
|
||||||
---@param tick number
|
---@param tick number
|
||||||
for _, tick in tick_res:iter() do
|
for _, tick in tick_res:iter() do
|
||||||
print("Entity transform last changed on tick " .. tostring(tick))
|
print("Entity transform last changed on tick " .. tostring(tick))
|
||||||
|
|
|
@ -9,35 +9,6 @@ World = {}
|
||||||
---@return Entity
|
---@return Entity
|
||||||
function World:spawn(...) end
|
function World:spawn(...) end
|
||||||
|
|
||||||
--- Query components from the world.
|
|
||||||
---
|
|
||||||
--- The `system` parameter is a function with the requested components. The function
|
|
||||||
--- is ran every time for an entity. If you modify a component and want the changes to be
|
|
||||||
--- stored, return it in the function. The order of the returned components do not matter.
|
|
||||||
---
|
|
||||||
--- Example:
|
|
||||||
--- ```lua
|
|
||||||
--- ---@type number
|
|
||||||
--- local dt = world:resource(DeltaTime)
|
|
||||||
---
|
|
||||||
--- world:view(
|
|
||||||
--- ---@param t Transform
|
|
||||||
--- function (t)
|
|
||||||
--- -- Move the transform of the entity a bit
|
|
||||||
--- t:translate(0, 0.15 * dt, 0)
|
|
||||||
--- -- Since the transform was modified, it must be returned so
|
|
||||||
--- -- the engine can store the changes.
|
|
||||||
--- return t
|
|
||||||
--- end,
|
|
||||||
--- -- Specify the requested components here
|
|
||||||
--- Transform
|
|
||||||
--- )
|
|
||||||
--- ```
|
|
||||||
---
|
|
||||||
---@param system fun(...): ...
|
|
||||||
---@param ... userdata
|
|
||||||
function World:view(system, ...) end
|
|
||||||
|
|
||||||
---Get an ECS resource.
|
---Get an ECS resource.
|
||||||
---
|
---
|
||||||
---Returns `nil` if the resource was not found in the world. Many resources will
|
---Returns `nil` if the resource was not found in the world. Many resources will
|
||||||
|
@ -91,9 +62,24 @@ function World:read_event(event) end
|
||||||
|
|
||||||
---View the world using the queries contained in a View.
|
---View the world using the queries contained in a View.
|
||||||
---
|
---
|
||||||
|
---Example:
|
||||||
|
---```lua
|
||||||
|
----- 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
|
||||||
|
--- transform:translate(0, 0.15 * dt, 0)
|
||||||
|
--- entity:update(transform)
|
||||||
|
---end
|
||||||
|
---```
|
||||||
|
---
|
||||||
|
---@see View
|
||||||
|
---@see ViewResult
|
||||||
---@param view View
|
---@param view View
|
||||||
---@return ViewResult
|
---@return ViewResult
|
||||||
function World:view_query(view) end
|
function World:view(view) end
|
||||||
|
|
||||||
---View a single entity in the world.
|
---View a single entity in the world.
|
||||||
---
|
---
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
use std::{ops::DerefMut, ptr::NonNull, sync::Arc};
|
use std::{ops::DerefMut, sync::Arc};
|
||||||
|
|
||||||
use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr};
|
use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr};
|
||||||
use lyra_ecs::{
|
use lyra_ecs::{CommandQueue, Commands, DynamicBundle, World};
|
||||||
query::dynamic::{DynamicViewState, DynamicViewStateIter, QueryDynamicType},
|
|
||||||
CommandQueue, Commands, DynamicBundle, World,
|
|
||||||
};
|
|
||||||
use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry};
|
use lyra_reflect::{ReflectWorldExt, RegisteredType, TypeRegistry};
|
||||||
use lyra_resource::ResourceManager;
|
use lyra_resource::ResourceManager;
|
||||||
use mlua::{IntoLua, ObjectLike};
|
use mlua::{IntoLua, ObjectLike};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ecs::{View, ViewOneResult, ViewResult}, reflect_user_data, wrappers::{LuaResHandleToComponent, LuaTick, LuaWrappedEventProxy}, Error, ReflectLuaProxy, ReflectedIterator, TypeLookup, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE
|
ecs::{View, ViewOneResult, ViewResult},
|
||||||
|
wrappers::{LuaResHandleToComponent, LuaTick, LuaWrappedEventProxy},
|
||||||
|
Error, ReflectLuaProxy, TypeLookup, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT,
|
||||||
|
FN_NAME_INTERNAL_REFLECT_TYPE,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl mlua::FromLua for ScriptEntity {
|
impl mlua::FromLua for ScriptEntity {
|
||||||
|
@ -98,145 +98,6 @@ impl mlua::UserData for ScriptWorldPtr {
|
||||||
|
|
||||||
Ok(ScriptEntity(entity))
|
Ok(ScriptEntity(entity))
|
||||||
});
|
});
|
||||||
methods.add_method_mut(
|
|
||||||
"view",
|
|
||||||
|lua, this, (system, queries): (mlua::Function, mlua::MultiValue)| {
|
|
||||||
if queries.is_empty() {
|
|
||||||
return Err(mlua::Error::BadArgument {
|
|
||||||
to: Some("World:view".into()),
|
|
||||||
pos: 2,
|
|
||||||
name: Some("query...".into()),
|
|
||||||
cause: Arc::new(mlua::Error::external(WorldError::LuaInvalidUsage(
|
|
||||||
"no component types provided".into(),
|
|
||||||
))),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let world = this.read();
|
|
||||||
let mut view = DynamicViewState::new();
|
|
||||||
|
|
||||||
for (idx, comp) in queries.into_iter().enumerate() {
|
|
||||||
match comp {
|
|
||||||
mlua::Value::Table(t) => {
|
|
||||||
let name: String = t.get(mlua::MetaMethod::Type.name())?;
|
|
||||||
|
|
||||||
let lookup =
|
|
||||||
world
|
|
||||||
.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(|| {
|
|
||||||
mlua::Error::BadArgument {
|
|
||||||
to: Some("World:view".into()),
|
|
||||||
pos: 2 + idx,
|
|
||||||
name: Some("query...".into()),
|
|
||||||
cause: Arc::new(mlua::Error::external(
|
|
||||||
WorldError::LuaInvalidUsage(format!(
|
|
||||||
"the 'Table' with name {} is unknown to the engine!",
|
|
||||||
name
|
|
||||||
)),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let dyn_type = QueryDynamicType::from_info(info.clone());
|
|
||||||
view.push(dyn_type);
|
|
||||||
}
|
|
||||||
mlua::Value::UserData(ud) => {
|
|
||||||
let reflect = ud
|
|
||||||
.call_function::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
|
|
||||||
.expect("Type does not implement 'reflect_type' properly");
|
|
||||||
let refl_comp = reflect.reflect_branch.as_component_unchecked();
|
|
||||||
|
|
||||||
let dyn_type = QueryDynamicType::from_info(refl_comp.info);
|
|
||||||
view.push(dyn_type);
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let iter = view.into_iter();
|
|
||||||
let mut reflected_iter = ReflectedIterator {
|
|
||||||
// SAFETY: bypassing the borrow checker here to get a pointer of the world
|
|
||||||
// is required since we mutably borrow below. Its safe to do so since
|
|
||||||
// only the entity ticks are updated. They are accessing different things
|
|
||||||
// from the world.
|
|
||||||
world: unsafe { NonNull::from(&*world).as_ref() },
|
|
||||||
dyn_view: DynamicViewStateIter::from(iter),
|
|
||||||
};
|
|
||||||
|
|
||||||
let current = world.current_tick();
|
|
||||||
|
|
||||||
// drop read lock and acquire the write lock.
|
|
||||||
// dropping must be done to avoid mutex deadlock
|
|
||||||
drop(world);
|
|
||||||
let mut world = this.write();
|
|
||||||
|
|
||||||
while let Some(row) = reflected_iter.next_lua(lua) {
|
|
||||||
let r = row
|
|
||||||
.row
|
|
||||||
.into_iter()
|
|
||||||
.into_iter()
|
|
||||||
.map(|r| (r.comp_val, r.comp_ptr.cast::<()>()))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let (values, ptrs) =
|
|
||||||
itertools::multiunzip::<(Vec<mlua::Value>, Vec<NonNull<()>>), _>(r);
|
|
||||||
let mult_val = mlua::MultiValue::from_iter(values.into_iter());
|
|
||||||
let res: mlua::MultiValue = system.call(mult_val)?;
|
|
||||||
|
|
||||||
// if values were returned, find the type in the type registry, and apply the new values
|
|
||||||
if res.len() <= ptrs.len() {
|
|
||||||
for (comp, ptr) in res.into_iter().zip(ptrs) {
|
|
||||||
let lua_typeid = match &comp {
|
|
||||||
mlua::Value::UserData(ud) => {
|
|
||||||
let lua_comp = reflect_user_data(ud);
|
|
||||||
let refl_comp =
|
|
||||||
lua_comp.reflect_branch.as_component_unchecked();
|
|
||||||
refl_comp.info.type_id().as_rust()
|
|
||||||
}
|
|
||||||
mlua::Value::Table(tbl) => {
|
|
||||||
let name: String = tbl.get(mlua::MetaMethod::Type.name())?;
|
|
||||||
|
|
||||||
let lookup = world.get_resource::<TypeLookup>().unwrap();
|
|
||||||
*lookup.typeid_from_name.get(&name).unwrap()
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
panic!("A userdata or table value was not returned!");
|
|
||||||
// TODO: Handle properly
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// update the component tick
|
|
||||||
let arch = world.entity_archetype_mut(row.entity).unwrap();
|
|
||||||
let idx = arch.entity_indexes().get(&row.entity).unwrap().clone();
|
|
||||||
let c = arch.get_column_mut(lua_typeid).unwrap();
|
|
||||||
c.entity_ticks[idx.0 as usize] = current;
|
|
||||||
|
|
||||||
// apply the new component data
|
|
||||||
let reg = world.get_resource::<TypeRegistry>().unwrap();
|
|
||||||
let reg_type = reg.get_type(lua_typeid).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.apply(lua, ptr, &comp)?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let msg = format!(
|
|
||||||
"Too many arguments were returned from the World view!
|
|
||||||
At most, the expected number of results is {}.",
|
|
||||||
ptrs.len()
|
|
||||||
);
|
|
||||||
return Err(mlua::Error::runtime(msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
methods.add_method_mut("resource", |lua, this, (ty,): (mlua::Value,)| {
|
methods.add_method_mut("resource", |lua, this, (ty,): (mlua::Value,)| {
|
||||||
let reflect = match ty {
|
let reflect = match ty {
|
||||||
mlua::Value::UserData(ud) => ud
|
mlua::Value::UserData(ud) => ud
|
||||||
|
@ -262,7 +123,9 @@ 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.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)
|
||||||
|
@ -349,15 +212,18 @@ impl mlua::UserData for ScriptWorldPtr {
|
||||||
data.reader(&mut world).into_lua(lua)
|
data.reader(&mut world).into_lua(lua)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
methods.add_method("view_query", |_, this, view: mlua::UserDataRef<View>| {
|
methods.add_method("view", |_, this, view: mlua::UserDataRef<View>| {
|
||||||
ViewResult::new(this.clone(), &view)
|
ViewResult::new(this.clone(), &view)
|
||||||
});
|
});
|
||||||
methods.add_method("get_tick", |_, this, ()| {
|
methods.add_method("get_tick", |_, this, ()| {
|
||||||
let w = this.read();
|
let w = this.read();
|
||||||
Ok(LuaTick(w.current_tick()))
|
Ok(LuaTick(w.current_tick()))
|
||||||
});
|
});
|
||||||
methods.add_method("view_one", |_, this, (entity, view): (ScriptEntity, mlua::UserDataRef<View>)| {
|
methods.add_method(
|
||||||
|
"view_one",
|
||||||
|
|_, this, (entity, view): (ScriptEntity, mlua::UserDataRef<View>)| {
|
||||||
ViewOneResult::new(this.clone(), *entity, &view)
|
ViewOneResult::new(this.clone(), *entity, &view)
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue