engine, ecs: dynamically create components for entities
parent
dd543f83f2
commit
8b49f8324e
|
@ -1,15 +1,33 @@
|
||||||
print("Hello, Lua2!")
|
print("Hello, Lua!")
|
||||||
--for k,v in pairs(components) do
|
for k,v in pairs(components) do
|
||||||
-- print("Component", k, v)
|
print("Lua Component", k, v)
|
||||||
--end
|
end
|
||||||
|
|
||||||
local sent = {}
|
local sent = {}
|
||||||
sent.register = function (name, cls)
|
sent.register = function (cfg)
|
||||||
|
if cfg.name == nil then
|
||||||
|
error("sent.register: needs name")
|
||||||
|
end
|
||||||
|
if cfg.cls == nil then
|
||||||
|
error("sent.register: needs cls")
|
||||||
|
end
|
||||||
|
local name = cfg.name
|
||||||
|
local cls = cfg.cls
|
||||||
|
local components = cfg.components or {}
|
||||||
|
|
||||||
if cls.__sent_class_id ~= nil then
|
if cls.__sent_class_id ~= nil then
|
||||||
print("Attempting to re-register " .. name)
|
error(string.format("sent.register: %s already registered", name))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local sent_class_id = __sent_register(name, cls)
|
|
||||||
|
-- Recreate config when calling native function, to ensure no metatable
|
||||||
|
-- fuckery.
|
||||||
|
local sent_class_id = __sent_register({
|
||||||
|
name = name,
|
||||||
|
cls = cls,
|
||||||
|
components = components,
|
||||||
|
})
|
||||||
|
|
||||||
cls.__sent_class_id = sent_class_id
|
cls.__sent_class_id = sent_class_id
|
||||||
cls.new = function(...)
|
cls.new = function(...)
|
||||||
local arg = {...}
|
local arg = {...}
|
||||||
|
@ -32,7 +50,14 @@ function Test:tick()
|
||||||
print("components " .. tostring(self.components))
|
print("components " .. tostring(self.components))
|
||||||
end
|
end
|
||||||
|
|
||||||
sent.register("Test", Test)
|
sent.register({
|
||||||
|
name = "Test",
|
||||||
|
cls = Test,
|
||||||
|
components = {
|
||||||
|
components.Transform.new(0, 0, 0),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
local t1 = Test.new(123)
|
local t1 = Test.new(123)
|
||||||
t1:tick()
|
t1:tick()
|
||||||
local t2 = Test.new(234)
|
local t2 = Test.new(234)
|
||||||
|
|
|
@ -216,12 +216,13 @@ fn main() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
world.register_component_lua_bindings(Transform::bindings());
|
||||||
let mut renderer = render::Renderer::initialize(&mut world);
|
let mut renderer = render::Renderer::initialize(&mut world);
|
||||||
let main = Main::new(&mut world, &mut renderer);
|
let main = Main::new(&mut world, &mut renderer);
|
||||||
|
|
||||||
let context = scripting::WorldContext::new(&world);
|
let context = scripting::WorldContext::new(&world);
|
||||||
let init = util::file::resource("//engine/init.lua").unwrap().string().unwrap();
|
let init = util::file::resource("//engine/init.lua").unwrap().string().unwrap();
|
||||||
context.eval_init(init).unwrap();
|
context.eval_init(&world, init).unwrap();
|
||||||
|
|
||||||
log::info!("Starting...");
|
log::info!("Starting...");
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ impl XYZ {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct LinearF32 {
|
pub struct LinearF32 {
|
||||||
pub d: f32,
|
pub d: f32,
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ use crate::render::vulkan::data;
|
||||||
|
|
||||||
/// An Omni point light, with position in 3d space, and 'color' defined in lumens per CIE XYZ
|
/// An Omni point light, with position in 3d space, and 'color' defined in lumens per CIE XYZ
|
||||||
/// color channel.
|
/// color channel.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Omni {
|
pub struct Omni {
|
||||||
/// Luminour power/flux defined as lumens per XYZ color channel.
|
/// Luminour power/flux defined as lumens per XYZ color channel.
|
||||||
pub color: color::XYZ,
|
pub color: color::XYZ,
|
||||||
|
@ -57,6 +58,7 @@ impl Omni {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Light {
|
pub enum Light {
|
||||||
Omni(Omni),
|
Omni(Omni),
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ use crate::render::vulkan::data;
|
||||||
use crate::render::vulkan::material::ChannelLayoutVulkan;
|
use crate::render::vulkan::material::ChannelLayoutVulkan;
|
||||||
use crate::util::file;
|
use crate::util::file;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Texture<T: ChannelLayoutVulkan> {
|
pub enum Texture<T: ChannelLayoutVulkan> {
|
||||||
Color(T),
|
Color(T),
|
||||||
ImageRef(String),
|
ImageRef(String),
|
||||||
|
@ -55,6 +56,7 @@ impl<T: ChannelLayoutVulkan> Texture<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Material {
|
pub struct Material {
|
||||||
diffuse: Texture<color::XYZ>,
|
diffuse: Texture<color::XYZ>,
|
||||||
roughness: Texture<color::LinearF32>,
|
roughness: Texture<color::LinearF32>,
|
||||||
|
|
|
@ -24,6 +24,7 @@ use vulkano::sync::GpuFuture;
|
||||||
|
|
||||||
use crate::render::vulkan::data;
|
use crate::render::vulkan::data;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Mesh {
|
pub struct Mesh {
|
||||||
vertices: Arc<Vec<data::Vertex>>,
|
vertices: Arc<Vec<data::Vertex>>,
|
||||||
indices: Arc<Vec<u16>>,
|
indices: Arc<Vec<u16>>,
|
||||||
|
|
|
@ -16,18 +16,29 @@
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash;
|
use std::hash;
|
||||||
|
use std::cell::Ref;
|
||||||
|
|
||||||
use cgmath as cgm;
|
use cgmath as cgm;
|
||||||
|
|
||||||
use ecs::Component;
|
use ecs::{Component, ComponentLuaBindings};
|
||||||
use crate::render::{Light, Mesh, Material};
|
use crate::render::{Light, Mesh, Material};
|
||||||
use crate::render::resource::{ResourceID};
|
use crate::render::resource::{ResourceID};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct Transform(pub cgm::Matrix4<f32>);
|
pub struct Transform(pub cgm::Matrix4<f32>);
|
||||||
|
|
||||||
pub struct TransformBindings {}
|
impl Component for Transform {
|
||||||
|
fn id(&self) -> ecs::component::ID {
|
||||||
|
ecs::component::component_id::<Transform>()
|
||||||
|
}
|
||||||
|
fn clone_dyn(&self) -> Box<dyn Component> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ecs::ComponentLuaBindings for TransformBindings {
|
struct TransformBindings;
|
||||||
|
|
||||||
|
impl ComponentLuaBindings for TransformBindings {
|
||||||
fn globals<'a>(&self, lua: &'a mlua::Lua) -> mlua::Table<'a> {
|
fn globals<'a>(&self, lua: &'a mlua::Lua) -> mlua::Table<'a> {
|
||||||
let res = lua.create_table().unwrap();
|
let res = lua.create_table().unwrap();
|
||||||
res.set("new", lua.create_function(|_, args: mlua::Variadic<mlua::Number>| {
|
res.set("new", lua.create_function(|_, args: mlua::Variadic<mlua::Number>| {
|
||||||
|
@ -50,16 +61,20 @@ impl ecs::ComponentLuaBindings for TransformBindings {
|
||||||
}).unwrap()).unwrap();
|
}).unwrap()).unwrap();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
fn id(&self) -> &'static str {
|
fn idstr(&self) -> &'static str {
|
||||||
"Transform"
|
"Transform"
|
||||||
}
|
}
|
||||||
|
fn id(&self) -> ecs::component::ID {
|
||||||
|
ecs::component::component_id::<Transform>()
|
||||||
|
}
|
||||||
|
fn any_into_dyn<'a>(&self, ud: &'a mlua::AnyUserData) -> Option<Box<dyn Component>> {
|
||||||
|
match ud.borrow::<Transform>() {
|
||||||
|
Ok(v) => Some(Box::new(Transform::clone(&v))),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Transform {
|
|
||||||
fn lua_bindings(&self) -> Option<Box<dyn ecs::ComponentLuaBindings>> {
|
|
||||||
Some(Box::new(TransformBindings{}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl mlua::UserData for Transform {
|
impl mlua::UserData for Transform {
|
||||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
@ -85,12 +100,22 @@ impl Transform {
|
||||||
pub fn m4(&self) -> &cgm::Matrix4<f32> {
|
pub fn m4(&self) -> &cgm::Matrix4<f32> {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
pub fn bindings() -> Box<dyn ComponentLuaBindings> {
|
||||||
|
Box::new(TransformBindings)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub enum Renderable {
|
pub enum Renderable {
|
||||||
Light(ResourceID<Light>),
|
Light(ResourceID<Light>),
|
||||||
Mesh(ResourceID<Mesh>, ResourceID<Material>),
|
Mesh(ResourceID<Mesh>, ResourceID<Material>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Renderable {
|
impl Component for Renderable {
|
||||||
|
fn id(&self) -> ecs::component::ID {
|
||||||
|
ecs::component::component_id::<Renderable>()
|
||||||
|
}
|
||||||
|
fn clone_dyn(&self) -> Box<dyn Component> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ impl Resource for Material {
|
||||||
fn map_mut(rm: &mut Manager) -> &mut Map<Self> { &mut rm.materials }
|
fn map_mut(rm: &mut Manager) -> &mut Map<Self> { &mut rm.materials }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ResourceID<T: Resource> {
|
pub struct ResourceID<T: Resource> {
|
||||||
numerical: u64,
|
numerical: u64,
|
||||||
phantom: std::marker::PhantomData<T>,
|
phantom: std::marker::PhantomData<T>,
|
||||||
|
|
|
@ -22,7 +22,7 @@ use vulkano::format::Format;
|
||||||
|
|
||||||
use cgmath as cgm;
|
use cgmath as cgm;
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone)]
|
#[derive(Default, Copy, Clone, Debug)]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
pos: [f32; 3],
|
pos: [f32; 3],
|
||||||
normal: [f32; 3],
|
normal: [f32; 3],
|
||||||
|
@ -70,7 +70,7 @@ pub struct FragmentUniformBufferObject {
|
||||||
pub omni_lights: [OmniLight; 4],
|
pub omni_lights: [OmniLight; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Textures {
|
pub struct Textures {
|
||||||
// diffuse: RGB
|
// diffuse: RGB
|
||||||
pub diffuse: Arc<vm::ImmutableImage<Format>>,
|
pub diffuse: Arc<vm::ImmutableImage<Format>>,
|
||||||
|
@ -82,3 +82,9 @@ pub struct VertexData {
|
||||||
pub vbuffer: Arc<vb::ImmutableBuffer<[Vertex]>>,
|
pub vbuffer: Arc<vb::ImmutableBuffer<[Vertex]>>,
|
||||||
pub ibuffer: Arc<vb::ImmutableBuffer<[u16]>>,
|
pub ibuffer: Arc<vb::ImmutableBuffer<[u16]>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for VertexData {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("VertexData").finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::{Arc, Mutex, atomic};
|
use std::sync::{Arc, Mutex, atomic};
|
||||||
|
|
||||||
|
use mlua::prelude::LuaError::RuntimeError;
|
||||||
|
|
||||||
fn debug_str(v: &mlua::Value) -> String {
|
fn debug_str(v: &mlua::Value) -> String {
|
||||||
match v {
|
match v {
|
||||||
mlua::Value::String(s) => s.to_str().map_or(format!("{:?}", v), |s| s.to_string()),
|
mlua::Value::String(s) => s.to_str().map_or(format!("{:?}", v), |s| s.to_string()),
|
||||||
|
@ -9,10 +11,18 @@ fn debug_str(v: &mlua::Value) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ComponentID {
|
||||||
|
id: ecs::component::ID,
|
||||||
|
idstr: String,
|
||||||
|
}
|
||||||
|
impl mlua::UserData for ComponentID {}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ScriptedEntityClass {
|
struct ScriptedEntityClass {
|
||||||
name: String,
|
name: String,
|
||||||
table: mlua::RegistryKey,
|
cls: mlua::RegistryKey,
|
||||||
|
components: Vec<Box<dyn ecs::Component>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -24,18 +34,19 @@ impl mlua::UserData for ScriptedEntityClassID {}
|
||||||
static GLOBAL_SCRIPTED_ENTITY_ID: atomic::AtomicU64 = atomic::AtomicU64::new(0);
|
static GLOBAL_SCRIPTED_ENTITY_ID: atomic::AtomicU64 = atomic::AtomicU64::new(0);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ScriptedEntity {
|
enum ScriptedEntityStatus {
|
||||||
class_name: String,
|
QueuedForCreation,
|
||||||
internal_id: u64,
|
Exists(ecs::EntityID),
|
||||||
ecs_id: Option<ecs::EntityID>,
|
FailedToCreate,
|
||||||
table: mlua::RegistryKey,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ScriptedEntityID {
|
struct ScriptedEntity {
|
||||||
|
class_name: String,
|
||||||
internal_id: u64,
|
internal_id: u64,
|
||||||
|
status: ScriptedEntityStatus,
|
||||||
|
table: mlua::RegistryKey,
|
||||||
}
|
}
|
||||||
impl mlua::UserData for ScriptedEntityID {}
|
|
||||||
|
|
||||||
impl ScriptedEntity {
|
impl ScriptedEntity {
|
||||||
fn new(class_name: String, table: mlua::RegistryKey) -> Self {
|
fn new(class_name: String, table: mlua::RegistryKey) -> Self {
|
||||||
|
@ -43,12 +54,18 @@ impl ScriptedEntity {
|
||||||
Self {
|
Self {
|
||||||
class_name,
|
class_name,
|
||||||
internal_id,
|
internal_id,
|
||||||
ecs_id: None,
|
status: ScriptedEntityStatus::QueuedForCreation,
|
||||||
table,
|
table,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ScriptedEntityID {
|
||||||
|
internal_id: u64,
|
||||||
|
}
|
||||||
|
impl mlua::UserData for ScriptedEntityID {}
|
||||||
|
|
||||||
pub struct WorldContext {
|
pub struct WorldContext {
|
||||||
// TODO(q3k): this leaks memory, right?
|
// TODO(q3k): this leaks memory, right?
|
||||||
lua: &'static mlua::Lua,
|
lua: &'static mlua::Lua,
|
||||||
|
@ -89,21 +106,66 @@ impl WorldContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scope_sent(
|
fn global_set_components(&self, world: &ecs::World) -> mlua::Result<()> {
|
||||||
|
let globals = self.lua.globals();
|
||||||
|
let components = match globals.get("components") {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(_) => {
|
||||||
|
let components = self.lua.create_table()?;
|
||||||
|
globals.set("components", components.clone())?;
|
||||||
|
components
|
||||||
|
}
|
||||||
|
};
|
||||||
|
components.set_metatable(None);
|
||||||
|
for (idstr, id, bindings) in world.get_component_lua_bindings() {
|
||||||
|
let methods = bindings.globals(&self.lua);
|
||||||
|
methods.set("__component_component_id", ComponentID {
|
||||||
|
id,
|
||||||
|
idstr: idstr.clone(),
|
||||||
|
});
|
||||||
|
components.set(idstr, methods);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scope_sent<'a, 'lua, 'scope>(
|
||||||
&self,
|
&self,
|
||||||
scope: &mlua::Scope,
|
scope: &mlua::Scope<'lua, 'scope>,
|
||||||
) -> mlua::Result<()> {
|
world: &'a ecs::World,
|
||||||
|
) -> mlua::Result<()>
|
||||||
|
where
|
||||||
|
'a: 'scope,
|
||||||
|
{
|
||||||
let globals = self.lua.globals();
|
let globals = self.lua.globals();
|
||||||
{
|
{
|
||||||
let classes = self.classes.clone();
|
let classes = self.classes.clone();
|
||||||
globals.set("__sent_register", scope.create_function(move |lua, args: (mlua::String, mlua::Table)| -> mlua::Result<_> {
|
globals.set("__sent_register", scope.create_function(move |lua, cfg: mlua::Table| -> mlua::Result<_> {
|
||||||
let name = args.0.to_str()?.to_string();
|
let name: String = cfg.get("name")?;
|
||||||
let cls = args.1;
|
let cls: mlua::Table = cfg.get("cls")?;
|
||||||
|
|
||||||
|
let components: mlua::Table = cfg.get("components")?;
|
||||||
|
let components: Vec<Box<dyn ecs::Component>> = components
|
||||||
|
.pairs::<mlua::Integer, mlua::AnyUserData>()
|
||||||
|
.map(|pair| match pair {
|
||||||
|
Ok((_, v)) => match world.lua_any_into_dyn::<'a, '_>(&v) {
|
||||||
|
Some(b) => Ok(b),
|
||||||
|
None => Err(RuntimeError(
|
||||||
|
format!("cfg.components type error: not a component")
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
Err(err) => Err(RuntimeError(
|
||||||
|
format!("cfg.components iter error: {:?}", err)
|
||||||
|
)),
|
||||||
|
})
|
||||||
|
.collect::<mlua::Result<Vec<Box<dyn ecs::Component>>>>()?;
|
||||||
|
|
||||||
log::info!("Registering Scripted Entity class {} at {:?}", name, cls);
|
log::info!("Registering Scripted Entity class {} at {:?}", name, cls);
|
||||||
|
log::info!("Components: {:?}", components);
|
||||||
|
|
||||||
let sec = ScriptedEntityClass {
|
let sec = ScriptedEntityClass {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
table: lua.create_registry_value(cls)?,
|
cls: lua.create_registry_value(cls)?,
|
||||||
|
components,
|
||||||
};
|
};
|
||||||
classes.lock().unwrap().insert(name.clone(), sec);
|
classes.lock().unwrap().insert(name.clone(), sec);
|
||||||
Ok(ScriptedEntityClassID {
|
Ok(ScriptedEntityClassID {
|
||||||
|
@ -119,9 +181,9 @@ impl WorldContext {
|
||||||
let classes = classes.lock().unwrap();
|
let classes = classes.lock().unwrap();
|
||||||
let sec = match classes.get(&secid.name) {
|
let sec = match classes.get(&secid.name) {
|
||||||
Some(el) => el,
|
Some(el) => el,
|
||||||
None => return Err(mlua::prelude::LuaError::RuntimeError(format!("lost secid {:?}", secid.name))),
|
None => return Err(RuntimeError(format!("lost secid {:?}", secid.name))),
|
||||||
};
|
};
|
||||||
let cls: mlua::Table = lua.registry_value(&sec.table)?;
|
let cls: mlua::Table = lua.registry_value(&sec.cls)?;
|
||||||
|
|
||||||
// (meta)table tree for entity objects:
|
// (meta)table tree for entity objects:
|
||||||
//
|
//
|
||||||
|
@ -169,13 +231,14 @@ impl WorldContext {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_init<T>(&self, val: T) -> mlua::Result<()>
|
pub fn eval_init<T>(&self, world: &ecs::World, val: T) -> mlua::Result<()>
|
||||||
where
|
where
|
||||||
T: Into<String>
|
T: Into<String>
|
||||||
{
|
{
|
||||||
let val: String = val.into();
|
let val: String = val.into();
|
||||||
self.lua.scope(|scope| {
|
self.lua.scope(|scope| {
|
||||||
self.scope_sent(scope)?;
|
self.global_set_components(world)?;
|
||||||
|
self.scope_sent(scope, world)?;
|
||||||
self.lua.load(&val).exec()
|
self.lua.load(&val).exec()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -185,16 +248,30 @@ impl <'system> ecs::System<'system> for WorldContext {
|
||||||
type SystemData = ecs::ReadWriteAll<'system>;
|
type SystemData = ecs::ReadWriteAll<'system>;
|
||||||
fn run(&mut self, sd: Self::SystemData) {
|
fn run(&mut self, sd: Self::SystemData) {
|
||||||
let mut instances = self.instances.lock().unwrap();
|
let mut instances = self.instances.lock().unwrap();
|
||||||
|
let classes = self.classes.lock().unwrap();
|
||||||
|
|
||||||
// Lazily create enqueued entities.
|
// Lazily create enqueued entities.
|
||||||
if instances.queue.len() > 0 {
|
if instances.queue.len() > 0 {
|
||||||
let mut queue = std::mem::replace(&mut instances.queue, Vec::new());
|
let mut queue = std::mem::replace(&mut instances.queue, Vec::new());
|
||||||
|
|
||||||
for mut el in queue.into_iter() {
|
for mut el in queue.into_iter() {
|
||||||
let ecsid = sd.new_entity_lazy().build(&sd);
|
match classes.get(&el.class_name) {
|
||||||
el.ecs_id = Some(ecsid);
|
Some(sec) => {
|
||||||
instances.internal_to_ecs.insert(el.internal_id, ecsid);
|
let mut entity = sd.new_entity_lazy();
|
||||||
log::debug!("Created sent of type {} with ECS ID {} and internal ID {}", el.class_name, ecsid, el.internal_id);
|
for component in sec.components.iter() {
|
||||||
instances.ecs.insert(ecsid, el);
|
entity = entity.with_dyn(component.clone_dyn());
|
||||||
|
}
|
||||||
|
let ecsid = entity.build(&sd);
|
||||||
|
el.status = ScriptedEntityStatus::Exists(ecsid);
|
||||||
|
log::debug!("Created sent of type {} with ECS ID {} and internal ID {}", el.class_name, ecsid, el.internal_id);
|
||||||
|
instances.internal_to_ecs.insert(el.internal_id, ecsid);
|
||||||
|
instances.ecs.insert(ecsid, el);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
log::warn!("Failed to create entity with internal ID {}: no class {}", el.internal_id, el.class_name);
|
||||||
|
el.status = ScriptedEntityStatus::FailedToCreate;
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
|
use std::cell::Ref;
|
||||||
|
|
||||||
pub type ID = std::any::TypeId;
|
pub type ID = std::any::TypeId;
|
||||||
|
|
||||||
pub trait LuaBindings {
|
pub trait LuaBindings {
|
||||||
fn globals<'a>(&self, lua: &'a mlua::Lua) -> mlua::Table<'a>;
|
fn globals<'a>(&self, lua: &'a mlua::Lua) -> mlua::Table<'a>;
|
||||||
fn id(&self) -> &'static str;
|
fn idstr(&self) -> &'static str;
|
||||||
|
fn id(&self) -> ID;
|
||||||
|
fn any_into_dyn<'a>(&self, ud: &'a mlua::AnyUserData) -> Option<Box<dyn Component>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Component: 'static {
|
pub trait Component: std::fmt::Debug + 'static {
|
||||||
fn lua_bindings(&self) -> Option<Box<dyn LuaBindings>> {
|
fn id(&self) -> ID;
|
||||||
None
|
fn clone_dyn(&self) -> Box<dyn Component>;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn component_id<T: Component>() -> ID {
|
pub fn component_id<T: Component>() -> ID {
|
||||||
|
|
|
@ -53,6 +53,11 @@ impl LazyEntityBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_dyn(mut self, c: Box<dyn component::Component>) -> Self {
|
||||||
|
self.components.push((c.id(), c));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self, w: &world::World) -> ID {
|
pub fn build(self, w: &world::World) -> ID {
|
||||||
for (cid, c) in self.components.into_iter() {
|
for (cid, c) in self.components.into_iter() {
|
||||||
w.enqueue_register_component_entity(cid, c, self.ent);
|
w.enqueue_register_component_entity(cid, c, self.ent);
|
||||||
|
|
|
@ -186,25 +186,40 @@ impl World {
|
||||||
entity::LazyEntityBuilder::new(self.allocate_next_id())
|
entity::LazyEntityBuilder::new(self.allocate_next_id())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn register_component_lua_bindings(
|
||||||
|
&mut self,
|
||||||
|
bindings: Box<dyn component::LuaBindings>,
|
||||||
|
) {
|
||||||
|
let idstr = bindings.idstr();
|
||||||
|
let cid = bindings.id();
|
||||||
|
if let Some(_) = self.component_by_idstr.get(idstr) {
|
||||||
|
log::warn!("Ignored attempted re-registration of Lua bindings for component {} (duplicate idstr)", idstr);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let Some(_) = self.component_lua_bindings.get(&cid) {
|
||||||
|
log::warn!("Ignored attempted re-registration of Lua bindings for component {} (duplicate ID)", idstr);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.component_by_idstr.insert(idstr, cid);
|
||||||
|
self.component_lua_bindings.insert(cid, bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lua_any_into_dyn<'a, 'b>(&'a self, ud: &'a mlua::AnyUserData) -> Option<Box<dyn component::Component>> {
|
||||||
|
for (_, bindings) in self.component_lua_bindings.iter() {
|
||||||
|
if let Some(b) = bindings.any_into_dyn(ud) {
|
||||||
|
return Some(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register_component_entity(
|
pub fn register_component_entity(
|
||||||
&mut self,
|
&mut self,
|
||||||
cid: component::ID,
|
cid: component::ID,
|
||||||
c: Box<dyn component::Component>,
|
c: Box<dyn component::Component>,
|
||||||
e: entity::Entity
|
e: entity::Entity
|
||||||
) {
|
) {
|
||||||
if let Some(bindings) = c.lua_bindings() {
|
let map = self.components.entry(cid).or_insert(ComponentMap::new());
|
||||||
// TODO(q3k): optimize this to not happen on every registration.
|
|
||||||
self.component_by_idstr.insert(bindings.id(), cid);
|
|
||||||
self.component_lua_bindings.insert(cid, bindings);
|
|
||||||
}
|
|
||||||
let map = self.components.entry(cid).or_insert_with(|| {
|
|
||||||
if let Some(bindings) = c.lua_bindings() {
|
|
||||||
log::info!("Registered component {}", bindings.id());
|
|
||||||
} else {
|
|
||||||
log::warn!("Component {:?} has no .lua_bindings() defined, will not be accessible from scripting.", cid);
|
|
||||||
}
|
|
||||||
ComponentMap::new()
|
|
||||||
});
|
|
||||||
map.insert(e.id(), c).unwrap();
|
map.insert(e.id(), c).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,6 +240,15 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_component_lua_bindings(
|
||||||
|
&self,
|
||||||
|
) -> Vec<(String, component::ID, &Box<dyn component::LuaBindings>)> {
|
||||||
|
self.component_by_idstr.iter().filter_map(|(idstr, cid)| {
|
||||||
|
let bindings = self.component_lua_bindings.get(cid)?;
|
||||||
|
Some((idstr.to_string(), *cid, bindings))
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn components<'a, T: component::Component>(&'a self) -> ReadComponent<'a, T> {
|
pub fn components<'a, T: component::Component>(&'a self) -> ReadComponent<'a, T> {
|
||||||
ReadComponent {
|
ReadComponent {
|
||||||
world: self,
|
world: self,
|
||||||
|
|
Loading…
Reference in New Issue