scripting: fix lua scripting (#13), create an example for it
ci/woodpecker/push/debug Pipeline failed Details

This commit is contained in:
SeanOMik 2024-04-27 00:52:47 -04:00
parent 1b08482ef7
commit 29c68abbbb
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
26 changed files with 845 additions and 264 deletions

16
Cargo.lock generated
View File

@ -1803,6 +1803,18 @@ dependencies = [
"tracing-subscriber", "tracing-subscriber",
] ]
[[package]]
name = "lua-scripting"
version = "0.1.0"
dependencies = [
"anyhow",
"async-std",
"fps_counter",
"lyra-engine",
"rand 0.8.5",
"tracing",
]
[[package]] [[package]]
name = "lyra-ecs" name = "lyra-ecs"
version = "0.1.0" version = "0.1.0"
@ -1927,6 +1939,7 @@ dependencies = [
"anyhow", "anyhow",
"lyra-ecs", "lyra-ecs",
"lyra-math", "lyra-math",
"lyra-reflect",
] ]
[[package]] [[package]]
@ -1934,6 +1947,7 @@ name = "lyra-scripting"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"atomic_refcell",
"elua", "elua",
"itertools 0.12.0", "itertools 0.12.0",
"lyra-ecs", "lyra-ecs",
@ -1941,6 +1955,7 @@ dependencies = [
"lyra-reflect", "lyra-reflect",
"lyra-resource", "lyra-resource",
"lyra-scripting-derive", "lyra-scripting-derive",
"paste",
"thiserror", "thiserror",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@ -1950,6 +1965,7 @@ dependencies = [
name = "lyra-scripting-derive" name = "lyra-scripting-derive"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"paste",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.51", "syn 2.0.51",

View File

@ -10,7 +10,14 @@ members = [
"lyra-ecs", "lyra-ecs",
"lyra-reflect", "lyra-reflect",
"lyra-scripting", "lyra-scripting",
"lyra-game", "lyra-math", "lyra-scene", "examples/many-lights", "examples/fixed-timestep-rotating-model"] "lyra-game",
"lyra-math",
"lyra-scene",
"examples/many-lights",
"examples/fixed-timestep-rotating-model",
"examples/lua-scripting"
]
[features] [features]
scripting = ["dep:lyra-scripting"] scripting = ["dep:lyra-scripting"]

View File

@ -0,0 +1,22 @@
[package]
name = "lua-scripting"
version = "0.1.0"
edition = "2021"
[dependencies]
lyra-engine = { path = "../../", features = ["tracy", "lua_scripting"] }
anyhow = "1.0.75"
async-std = "1.12.0"
tracing = "0.1.37"
rand = "0.8.5"
fps_counter = "3.0.0"
[target.x86_64-unknown-linux-gnu]
linker = "/usr/bin/clang"
rustflags = ["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"]
[profile.dev]
opt-level = 1
[profile.release]
debug = true

View File

@ -0,0 +1,59 @@
---Return the userdata's name from its metatable
---@param val userdata
---@return string
function udname(val)
return getmetatable(val).__name
end
function on_init()
local cube = world:request_res("../assets/cube-texture-embedded.gltf")
print("Loaded textured cube (" .. udname(cube) .. ")")
cube:wait_until_loaded()
local scenes = cube:scenes()
local cube_scene = scenes[1]
local pos = Transform.from_translation(Vec3.new(0, 0, -8.0))
local e = world:spawn(pos, cube_scene)
print("spawned entity " .. tostring(e))
end
--[[ function on_first()
print("Lua's first function was called")
end
function on_pre_update()
print("Lua's pre-update function was called")
end ]]
function on_update()
--[[ ---@type number
local dt = world:resource(DeltaTime)
local act = world:resource(ActionHandler)
---@type number
local move_objs = act:get_axis("ObjectsMoveUpDown")
world:view(function (t)
if move_objs ~= nil then
t:translate(0, move_objs * 0.35 * dt, 0)
return t
end
end, Transform) ]]
---@type number
local dt = world:resource(DeltaTime)
world:view(function (t)
t:translate(0, 0.15 * dt, 0)
return t
end, Transform)
end
--[[ function on_post_update()
print("Lua's post-update function was called")
end
function on_last()
print("Lua's last function was called")
end ]]

View File

@ -0,0 +1,151 @@
use std::ptr::NonNull;
use lyra_engine::{
assets::{gltf::Gltf, ResourceManager}, ecs::{
query::{Res, ResMut, View},
system::{BatchedSystem, Criteria, CriteriaSchedule, IntoSystem},
World,
}, game::Game, input::{
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
}, lua::{LuaScript, LuaScriptingPlugin}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN,
ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN,
}, DeltaTime, Script, ScriptList
};
use tracing::info;
#[async_std::main]
async fn main() {
let action_handler_plugin = |game: &mut Game| {
let action_handler = ActionHandler::builder()
.add_layout(LayoutId::from(0))
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
.add_action(ACTLBL_MOVE_LEFT_RIGHT, Action::new(ActionKind::Axis))
.add_action(ACTLBL_MOVE_UP_DOWN, Action::new(ActionKind::Axis))
.add_action(ACTLBL_LOOK_LEFT_RIGHT, Action::new(ActionKind::Axis))
.add_action(ACTLBL_LOOK_UP_DOWN, Action::new(ActionKind::Axis))
.add_action(ACTLBL_LOOK_ROLL, Action::new(ActionKind::Axis))
.add_action("Debug", Action::new(ActionKind::Button))
.add_mapping(
ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0))
.bind(
ACTLBL_MOVE_FORWARD_BACKWARD,
&[
ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0),
],
)
.bind(
ACTLBL_MOVE_LEFT_RIGHT,
&[
ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0),
],
)
.bind(
ACTLBL_MOVE_UP_DOWN,
&[
ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0),
ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0),
],
)
.bind(
ACTLBL_LOOK_LEFT_RIGHT,
&[
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0),
],
)
.bind(
ACTLBL_LOOK_UP_DOWN,
&[
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0),
],
)
.bind(
ACTLBL_LOOK_ROLL,
&[
ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0),
ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0),
],
)
.bind(
"Debug",
&[ActionSource::Keyboard(KeyCode::B).into_binding()],
)
.finish(),
)
.finish();
let world = game.world_mut();
world.add_resource(action_handler);
game.with_plugin(InputActionPlugin);
};
Game::initialize()
.await
.with_plugin(lyra_engine::DefaultPlugins)
.with_plugin(setup_scene_plugin)
.with_plugin(action_handler_plugin)
.with_plugin(setup_script_plugin)
//.with_plugin(camera_debug_plugin)
.with_plugin(FreeFlyCameraPlugin)
.run()
.await;
}
fn setup_scene_plugin(game: &mut Game) {
let world = game.world_mut();
let resman = world.get_resource_mut::<ResourceManager>();
let camera_gltf = resman
.request::<Gltf>("../assets/AntiqueCamera.glb")
.unwrap();
camera_gltf.wait_recurse_dependencies_load();
let camera_mesh = &camera_gltf.data_ref().unwrap().scenes[0];
drop(resman);
world.spawn((
camera_mesh.clone(),
WorldTransform::default(),
Transform::from_xyz(0.0, -5.0, -2.0),
));
{
let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0);
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
light_tran.rotate_x(math::Angle::Degrees(-45.0));
light_tran.rotate_y(math::Angle::Degrees(25.0));
world.spawn((
DirectionalLight {
enabled: true,
color: Vec3::ONE,
intensity: 0.15, //..Default::default()
},
light_tran,
));
}
let mut camera = CameraComponent::new_3d();
camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5);
world.spawn((camera, FreeFlyCamera::default()));
}
fn setup_script_plugin(game: &mut Game) {
game.with_plugin(LuaScriptingPlugin);
let world = game.world_mut();
let res_man = world.get_resource_mut::<ResourceManager>();
let script = res_man.request::<LuaScript>("scripts/test.lua").unwrap();
res_man.watch("scripts/test.lua", false).unwrap();
drop(res_man);
let script = Script::new("test.lua", script);
let scripts = ScriptList::new(vec![script]);
world.spawn((scripts,));
}

View File

@ -9,3 +9,4 @@ edition = "2021"
anyhow = "1.0.81" anyhow = "1.0.81"
lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] } lyra-ecs = { path = "../lyra-ecs", features = [ "math" ] }
lyra-math = { path = "../lyra-math" } lyra-math = { path = "../lyra-math" }
lyra-reflect = { path = "../lyra-reflect" }

View File

@ -1,4 +1,5 @@
mod node; mod node;
use lyra_reflect::Reflect;
pub use node::*; pub use node::*;
mod world_transform; mod world_transform;
@ -12,6 +13,10 @@ pub(crate) mod lyra_engine {
pub(crate) mod ecs { pub(crate) mod ecs {
pub use lyra_ecs::*; pub use lyra_ecs::*;
} }
pub(crate) mod reflect {
pub use lyra_reflect::*;
}
} }
/// A flag spawned on all scene node entities /// A flag spawned on all scene node entities
@ -27,7 +32,9 @@ pub struct SceneNodeRoot;
/// This SceneGraph is special in the sense that it is literally just an ECS world with methods /// This SceneGraph is special in the sense that it is literally just an ECS world with methods
/// implemented for it that make it easier to use for a SceneGraph. /// implemented for it that make it easier to use for a SceneGraph.
//#[derive(Default)] //#[derive(Default)]
#[derive(Clone, Reflect)]
pub struct SceneGraph { pub struct SceneGraph {
#[reflect(skip)]
pub(crate) world: World, pub(crate) world: World,
root_node: SceneNode, root_node: SceneNode,
} }

View File

@ -1,11 +1,14 @@
use lyra_ecs::{query::Entities, relation::{ChildOf, RelationOriginComponent}, Bundle, Entity}; use lyra_ecs::{query::Entities, relation::{ChildOf, RelationOriginComponent}, Bundle, Entity};
use lyra_reflect::Reflect;
use crate::SceneGraph; use crate::{SceneGraph, lyra_engine};
/// A node inside of the Scene /// A node inside of the Scene
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq, Reflect)]
pub struct SceneNode { pub struct SceneNode {
#[reflect(skip)]
parent: Option<Entity>, parent: Option<Entity>,
#[reflect(skip)]
entity: Entity entity: Entity
} }

View File

@ -19,11 +19,13 @@ lyra-game = { path = "../lyra-game" }
thiserror = "1.0.50" thiserror = "1.0.50"
anyhow = "1.0.77" anyhow = "1.0.77"
tracing = "0.1.37" tracing = "0.1.37"
atomic_refcell = "0.1.13"
# enabled with lua feature # enabled with lua feature
#mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe? #mlua = { version = "0.9.2", features = ["lua54"], optional = true } # luajit maybe?
elua = { path = "./elua", optional = true } elua = { path = "./elua", optional = true }
itertools = "0.12.0" itertools = "0.12.0"
paste = "1.0.14"
[dev-dependencies] [dev-dependencies]

@ -1 +1 @@
Subproject commit 54c9926a04cdef657289fd67730c0b85d1bdda3e Subproject commit a761f4094bc18190285b4687ec804161fea874b6

View File

@ -12,3 +12,4 @@ proc-macro = true
proc-macro2 = "1.0.70" proc-macro2 = "1.0.70"
quote = "1.0.33" quote = "1.0.33"
syn = "2.0.41" syn = "2.0.41"
paste = "1.0.14"

View File

@ -0,0 +1,149 @@
use quote::{format_ident, quote};
use syn::{parse_macro_input, Ident, Token};
pub(crate) struct HandleWrapUsage {
pub type_path: syn::Path,
/// The extra derives of the type.
pub override_name: Option<Ident>,
pub extra_builds: Option<syn::Block>,
}
impl syn::parse::Parse for HandleWrapUsage {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let type_path: syn::Path = input.parse()?;
let mut s = Self {
type_path,
override_name: None,
extra_builds: None,
};
while input.peek(Token![,]) {
let _: Token![,] = input.parse()?;
if input.peek(syn::Ident) {
let ident: syn::Ident = input.parse()?;
let ident_str = ident.to_string();
let ident_str = ident_str.as_str();
match ident_str {
"name" => {
let _eq: Token![=] = input.parse()?;
let name: Ident = input.parse()?;
s.override_name = Some(name);
},
_ => {
return Err(syn::Error::new_spanned(ident, "unknown wrapper command"));
}
}
}/* else if input.parse::<Block(syn::Block) {
let block = input.parse()?;
s.extra_builds = block;
} */
if let Ok(block) = input.parse::<syn::Block>() {
s.extra_builds = Some(block);
}
}
Ok(s)
}
}
pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as HandleWrapUsage);
let handle_name = &input.type_path.segments.last().unwrap().ident;
let base_name = input.override_name.unwrap_or_else(|| handle_name.clone());
let wrapper_name = format_ident!("Lua{}Handle", base_name);
//let wrapper_name = Ident::new(&format!("Lua{}", handle_name.to_string()), handle_name.span());
let ud_name = format!("{}Handle", base_name.to_string());
let extras = input.extra_builds;
quote! {
#[derive(Clone, Reflect)]
pub struct #wrapper_name(pub ResHandle<#handle_name>);
impl Deref for #wrapper_name {
type Target = ResHandle<#handle_name>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<ResHandle<#handle_name>> for #wrapper_name {
fn from(value: ResHandle<#handle_name>) -> Self {
#wrapper_name(value)
}
}
impl elua::Userdata for #wrapper_name {
fn name() -> String {
#ud_name.to_string()
}
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
builder.field_getter("path", |_, this| Ok(this.path()));
builder.field_getter("version", |_, this| Ok(this.version()));
builder.field_getter("uuid", |_, this| Ok(this.uuid().to_string()));
builder.field_getter("state", |_, this| {
let name = if this.is_loaded() {
"ready"
} else if this.get_error().is_some() {
"error"
} else { "loading" };
Ok(name)
});
builder.method("is_watched", |_, this, ()| {
Ok(this.is_watched())
});
builder.method("is_loaded", |_, this, ()| {
Ok(this.is_loaded())
});
builder.method("is_loaded", |_, this, ()| {
Ok(this.is_loaded())
});
builder.method("wait_until_loaded", |_, this, ()| {
this.wait_recurse_dependencies_load();
Ok(())
});
builder.function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
Ok(ScriptBorrow::from_component::<ResHandle<#handle_name>>(None))
});
builder.method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
Ok(ScriptBorrow::from_component(Some(this.0.clone())))
});
#extras
}
}
impl<'a> FromLua<'a> for #wrapper_name {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> {
let tyname = val.type_name();
let ud = val.as_userdata()
.ok_or(elua::Error::type_mismatch(#ud_name, &tyname))?;
let ud = ud.as_ref::<#wrapper_name>()?;
Ok(ud.clone())
}
}
impl LuaWrapper for #wrapper_name {
fn wrapped_type_id() -> std::any::TypeId {
TypeId::of::<ResHandle<#handle_name>>()
}
}
}.into()
}

View File

@ -1,3 +1,5 @@
use handle_macro::lua_wrap_handle_impl;
use lua_macro::wrap_lua_struct_impl;
use proc_macro2::{Ident, Span}; use proc_macro2::{Ident, Span};
use quote::quote; use quote::quote;
use syn::{parse_macro_input, Path, Token, token, parenthesized, punctuated::Punctuated, braced}; use syn::{parse_macro_input, Path, Token, token, parenthesized, punctuated::Punctuated, braced};
@ -5,8 +7,14 @@ use syn::{parse_macro_input, Path, Token, token, parenthesized, punctuated::Punc
mod mat_wrapper; mod mat_wrapper;
use mat_wrapper::MatWrapper; use mat_wrapper::MatWrapper;
const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type"; mod lua_macro;
const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect";
mod handle_macro;
// pub use lua_macro::wrap_lua_struct;
pub(crate) const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type";
pub(crate) const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect";
pub(crate) struct MetaMethod { pub(crate) struct MetaMethod {
pub ident: Ident, pub ident: Ident,
@ -721,3 +729,13 @@ pub fn wrap_math_vec_copy(input: proc_macro::TokenStream) -> proc_macro::TokenSt
} }
}) })
} }
#[proc_macro]
pub fn wrap_lua_struct(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
wrap_lua_struct_impl(input)
}
#[proc_macro]
pub fn lua_wrap_handle(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
lua_wrap_handle_impl(input)
}

View File

@ -0,0 +1,164 @@
use proc_macro2::Span;
use quote::quote;
use syn::{parse_macro_input, Path, Ident};
use crate::{VecWrapper, WrapUsage, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE};
/// Creates a wrapper type for a VecN from the engine math library.
pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as WrapUsage);
let path: Path = input.type_path;
let type_name = &path.segments.last()
.expect("Failure to find typename in macro usage!")
.ident;
let wrapper_typename = Ident::new(&format!("Lua{}", type_name), Span::call_site());
let vec_wrapper = {
let name_str = type_name.to_string();
if name_str.contains("Vec") {
Some(VecWrapper {})
} else {
None
}
};
// TODO: fix this so it doesn't cause a stack overflow
/* let vec_wrapper_fields = vec_wrapper.as_ref().map(|vec|
vec.to_field_tokens(&path, &wrapper_typename)); */
let vec_wrapper_fields: Option<proc_macro2::TokenStream> = None;
let vec_wrapper_methods = vec_wrapper.as_ref().map(|vec|
vec.to_method_tokens(&path, &wrapper_typename));
let derive_idents_iter = input.derive_idents.iter();
let custom_methods = input.custom_methods;
let custom_fields = input.custom_fields;
let field_get_set_pairs = input.field_idents.iter().map(|i| {
let is = i.to_string();
quote! {
builder.field_getter(#is, |_, this| {
Ok(this.#i)
});
builder.field_setter(#is, |_, this, #i| {
this.#i = #i;
Ok(())
});
}
});
let new_fn_idents = {
let idents = if input.new_fn_idents.is_empty() {
input.field_idents.iter()
} else {
input.new_fn_idents.iter()
};
let idents_c = idents.clone();
if !input.skip_new_fn {
quote! {
builder.function("new", |_, ( #(#idents_c),* )| {
Ok(#wrapper_typename(#path::new( #(#idents),* )))
});
}
} else { quote!() }
};
let matrix_wrapper_methods = input.matrix.as_ref().map(|m|
m.to_method_tokens(&path, &wrapper_typename));
let matrix_wrapper_fields = input.matrix.as_ref().map(|m|
m.to_field_tokens(&path, &wrapper_typename));
let meta_method_idents = {
let idents = input.meta_method_idents.iter().map(|metamethod| {
metamethod.to_tokens(&wrapper_typename)
});
quote! {
#(#idents)*
}
};
proc_macro::TokenStream::from(quote! {
#[derive(Clone, Copy, lyra_reflect::Reflect, #(#derive_idents_iter),*)]
pub struct #wrapper_typename(#[reflect(skip)] pub(crate) #path);
impl std::ops::Deref for #wrapper_typename {
type Target = #path;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for #wrapper_typename {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'lua> elua::FromLua<'lua> for #wrapper_typename {
fn from_lua(_lua: &'lua elua::State, value: elua::Value<'lua>) -> elua::Result<Self> {
match value {
elua::Value::Userdata(ud) => Ok(*ud.as_ref::<Self>()?),
_ => panic!("Attempt to get {} from a {} value", stringify!(#wrapper_typename), value.type_name()),
}
}
}
impl elua::Userdata for #wrapper_typename {
fn name() -> String {
stringify!(#type_name).to_string()
}
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
#(#field_get_set_pairs)*
#matrix_wrapper_fields
#vec_wrapper_fields
#custom_fields
#new_fn_idents
builder.method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone())))
});
builder.function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
Ok(crate::ScriptBorrow::from_component::<#path>(None))
});
#meta_method_idents
#matrix_wrapper_methods
#vec_wrapper_methods
#custom_methods
}
}
impl lyra_scripting::lua::LuaWrapper for #wrapper_typename {
fn wrapped_type_id() -> std::any::TypeId {
let t = std::any::TypeId::of::<#path>();
println!("Got id of {}, it is {:?}", stringify!(#path), t);
t
}
}
impl<'a> elua::FromLuaVec<'a> for #wrapper_typename {
fn from_lua_value_vec(state: &'a elua::State, mut values: elua::ValueVec<'a>) -> elua::Result<Self> {
use elua::FromLua;
if let Some(val) = values.pop_front() {
#wrapper_typename::from_lua(state, val)
} else {
Err(elua::Error::IncorrectArgCount {
arg_expected: 1,
arg_count: values.len() as i32,
})
}
}
}
})
}

View File

@ -1,4 +1,4 @@
use std::collections::HashMap; use std::{collections::HashMap, sync::{Arc, Mutex}};
use lyra_ecs::{ResourceObject, Entity, World}; use lyra_ecs::{ResourceObject, Entity, World};
@ -38,7 +38,7 @@ pub struct ScriptData {
} }
/// Provides an API to a scripting context. /// Provides an API to a scripting context.
pub trait ScriptApiProvider { pub trait ScriptApiProvider: Send + Sync {
/// The type used as the script's context. /// The type used as the script's context.
type ScriptContext; type ScriptContext;
@ -62,7 +62,7 @@ pub trait ScriptApiProvider {
/// A storage for a [`ScriptHost`]'s api providers /// A storage for a [`ScriptHost`]'s api providers
#[derive(Default)] #[derive(Default)]
pub struct ScriptApiProviders<T: ScriptHost> { pub struct ScriptApiProviders<T: ScriptHost> {
pub apis: Vec<Box<dyn ScriptApiProvider<ScriptContext = T::ScriptContext>>>, pub apis: Vec<Arc<Mutex<dyn ScriptApiProvider<ScriptContext = T::ScriptContext>>>>,
} }
impl<T: ScriptHost> ScriptApiProviders<T> { impl<T: ScriptHost> ScriptApiProviders<T> {
@ -70,7 +70,7 @@ impl<T: ScriptHost> ScriptApiProviders<T> {
where where
P: ScriptApiProvider<ScriptContext = T::ScriptContext> + 'static P: ScriptApiProvider<ScriptContext = T::ScriptContext> + 'static
{ {
self.apis.push(Box::new(provider)); self.apis.push(Arc::new(Mutex::new(provider)));
} }
} }

View File

@ -1,6 +1,6 @@
use std::{ptr::NonNull, ops::{Range, Deref}}; use std::{ptr::NonNull, ops::Deref};
use lyra_ecs::{ComponentColumn, ComponentInfo, Archetype, ArchetypeId, ArchetypeEntityId, query::dynamic::{DynamicType, QueryDynamicType}, query::Fetch, Entity}; use lyra_ecs::{query::dynamic::DynamicViewStateIter, Entity};
use lyra_reflect::TypeRegistry; use lyra_reflect::TypeRegistry;
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
@ -8,130 +8,6 @@ use super::ReflectLuaProxy;
use crate::ScriptWorldPtr; use crate::ScriptWorldPtr;
/// A reimplementation of lyra_ecs::FetchDynamicType that doesn't store references and
/// uses a pointer instead.
/// This makes it easier to expose to Lua
#[derive(Clone)]
pub struct FetchDynamicType {
col: NonNull<ComponentColumn>,
info: ComponentInfo,
}
impl<'a> Fetch<'a> for FetchDynamicType {
type Item = DynamicType;
fn dangling() -> Self {
unreachable!()
}
unsafe fn get_item(&mut self, entity: ArchetypeEntityId) -> Self::Item {
let ptr = unsafe { self.col.as_ref().borrow_ptr() };
let ptr = NonNull::new_unchecked(ptr.as_ptr()
.add(entity.0 as usize * self.info.layout().size()));
DynamicType {
info: self.info,
ptr,
}
}
}
impl<'a> From<lyra_ecs::query::dynamic::FetchDynamicType<'a>> for FetchDynamicType {
fn from(value: lyra_ecs::query::dynamic::FetchDynamicType<'a>) -> Self {
Self {
col: NonNull::from(value.col),
info: value.info,
}
}
}
pub struct DynamicViewRow {
entity: Entity,
item: Vec<DynamicType>,
}
#[derive(Clone)]
pub struct DynamicViewIter {
world_ptr: ScriptWorldPtr,
queries: Vec<QueryDynamicType>,
fetchers: Vec<FetchDynamicType>,
archetypes: Vec<NonNull<Archetype>>,
next_archetype: usize,
component_indices: Range<u64>,
}
impl<'a> From<lyra_ecs::query::dynamic::DynamicViewIter<'a>> for DynamicViewIter {
fn from(value: lyra_ecs::query::dynamic::DynamicViewIter<'a>) -> Self {
Self {
world_ptr: ScriptWorldPtr::from_ref(value.world),
queries: value.queries,
fetchers: value.fetchers.into_iter().map(|f| FetchDynamicType::from(f)).collect(),
archetypes: value.archetypes.into_iter().map(|a| NonNull::from(a)).collect(),
next_archetype: value.next_archetype,
component_indices: value.component_indices,
}
}
}
impl Iterator for DynamicViewIter {
type Item = DynamicViewRow;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(entity_index) = self.component_indices.next() {
let mut fetch_res = vec![];
let entity_index = ArchetypeEntityId(entity_index);
for fetcher in self.fetchers.iter_mut() {
if !fetcher.can_visit_item(entity_index) {
break;
} else {
let i = unsafe { fetcher.get_item(entity_index) };
fetch_res.push(i);
}
}
if fetch_res.len() != self.fetchers.len() {
continue;
}
let arch = unsafe { self.archetypes.get_unchecked(self.next_archetype - 1).as_ref() };
let entity = arch.entity_at_index(entity_index).unwrap();
let row = DynamicViewRow {
entity,
item: fetch_res,
};
return Some(row);
} else {
if self.next_archetype >= self.archetypes.len() {
return None; // ran out of archetypes to go through
}
let arch_id = self.next_archetype;
self.next_archetype += 1;
let arch = unsafe { self.archetypes.get_unchecked(arch_id).as_ref() };
if arch.entity_indexes().len() == 0 {
continue;
}
if self.queries.iter().any(|q| !q.can_visit_archetype(arch)) {
continue;
}
let world = self.world_ptr.as_ref();
self.fetchers = self.queries.iter()
.map(|q| unsafe { q.fetch(world, ArchetypeId(arch_id as u64), arch) } )
.map(|f| FetchDynamicType::from(f))
.collect();
self.component_indices = 0..arch.entity_indexes().len() as u64;
}
}
}
}
#[cfg(feature = "lua")] #[cfg(feature = "lua")]
pub struct ReflectedItem<'a> { pub struct ReflectedItem<'a> {
//pub proxy: &'a ReflectLuaProxy, //pub proxy: &'a ReflectLuaProxy,
@ -147,7 +23,7 @@ pub struct ReflectedRow<'a> {
pub struct ReflectedIterator { pub struct ReflectedIterator {
pub world: ScriptWorldPtr, pub world: ScriptWorldPtr,
pub dyn_view: DynamicViewIter, pub dyn_view: DynamicViewStateIter,
pub reflected_components: Option<NonNull<TypeRegistry>> pub reflected_components: Option<NonNull<TypeRegistry>>
} }
@ -156,7 +32,8 @@ impl ReflectedIterator {
pub fn next_lua<'a>(&mut self, lua: &'a elua::State) -> Option<ReflectedRow<'a>> { pub fn next_lua<'a>(&mut self, lua: &'a elua::State) -> Option<ReflectedRow<'a>> {
use elua::AsLua; use elua::AsLua;
let n = self.dyn_view.next(); let world = self.world.as_ref();
let n = self.dyn_view.next(world);
if let Some(row) = n { if let Some(row) = n {
if self.reflected_components.is_none() { if self.reflected_components.is_none() {
@ -166,7 +43,7 @@ impl ReflectedIterator {
} }
let mut dynamic_row = vec![]; let mut dynamic_row = vec![];
for d in row.item.iter() { for d in row.row.iter() {
let id = d.info.type_id().as_rust(); let id = d.info.type_id().as_rust();
let reflected_components = let reflected_components =
unsafe { self.reflected_components.as_ref().unwrap().as_ref() }; unsafe { self.reflected_components.as_ref().unwrap().as_ref() };

View File

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use lyra_resource::{ResourceLoader, ResHandle}; use lyra_resource::{loader::{PinedBoxLoaderFuture, ResourceLoader}, ResHandle, ResourceData};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LuaScript { pub struct LuaScript {
@ -8,6 +8,20 @@ pub struct LuaScript {
pub(crate) bytes: Vec<u8>, pub(crate) bytes: Vec<u8>,
} }
impl ResourceData for LuaScript {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn dependencies(&self) -> Vec<lyra_resource::UntypedResHandle> {
vec![]
}
}
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct LuaLoader; pub struct LuaLoader;
@ -20,24 +34,33 @@ impl ResourceLoader for LuaLoader {
&["text/lua"] &["text/lua"]
} }
fn load(&self, _resource_manager: &mut lyra_resource::ResourceManager, path: &str) -> Result<std::sync::Arc<dyn lyra_resource::ResourceStorage>, lyra_resource::LoaderError> { fn load(&self, _resource_manager: lyra_resource::ResourceManager, path: &str) -> PinedBoxLoaderFuture {
let bytes = std::fs::read(path)?; let path = path.to_string();
Box::pin(async move {
let bytes = std::fs::read(&path)?;
let s = ResHandle::new_ready(path, LuaScript { let s = LuaScript {
bytes bytes
}); };
Ok(Arc::new(s)) Ok(Box::new(s) as Box<dyn ResourceData>)
})
} }
fn load_bytes(&self, _resource_manager: &mut lyra_resource::ResourceManager, bytes: Vec<u8>, offset: usize, length: usize) -> Result<std::sync::Arc<dyn lyra_resource::ResourceStorage>, lyra_resource::LoaderError> { fn load_bytes(&self, _resource_manager: lyra_resource::ResourceManager, bytes: Vec<u8>, offset: usize, length: usize) -> PinedBoxLoaderFuture {
let end = offset + length; Box::pin(async move {
let bytes = bytes[offset..end].to_vec(); let end = offset + length;
let bytes = bytes[offset..end].to_vec();
let s = ResHandle::new_ready("from bytes", LuaScript { let s = LuaScript {
bytes bytes
}); };
Ok(Arc::new(s)) Ok(Box::new(s) as Box<dyn ResourceData>)
})
}
fn create_erased_handle(&self) -> Arc<dyn lyra_resource::ResourceStorage> {
Arc::from(ResHandle::<LuaScript>::new_loading(None))
} }
} }

View File

@ -1,7 +1,10 @@
use lyra_ecs::ResourceObject; use std::any::TypeId;
use lyra_reflect::Reflect;
use crate::{lua::{wrappers::{LuaActionHandler, LuaDeltaTime, LuaModelComponent}, LuaContext, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr}; use lyra_ecs::ResourceObject;
use lyra_reflect::{Reflect, TypeRegistry};
use lyra_resource::gltf::Gltf;
use crate::{lua::{wrappers::{LuaGltfHandle, LuaActionHandler, LuaDeltaTime, LuaResHandleToComponent, LuaSceneHandle}, LuaContext, ReflectLuaProxy, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr};
#[derive(Default)] #[derive(Default)]
pub struct LyraEcsApiProvider; pub struct LyraEcsApiProvider;
@ -11,8 +14,26 @@ impl ScriptApiProvider for LyraEcsApiProvider {
fn prepare_world(&mut self, world: &mut lyra_ecs::World) { fn prepare_world(&mut self, world: &mut lyra_ecs::World) {
world.register_lua_convert::<LuaDeltaTime>(); world.register_lua_convert::<LuaDeltaTime>();
world.register_lua_wrapper::<LuaModelComponent>(); world.register_lua_wrapper::<LuaSceneHandle>();
world.register_lua_wrapper::<LuaActionHandler>(); world.register_lua_wrapper::<LuaActionHandler>();
let mut registry = world.get_resource_mut::<TypeRegistry>();
let reg_type = registry.get_type_or_default(TypeId::of::<Gltf>());
reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<LuaGltfHandle>());
let l = LuaResHandleToComponent::new(
|lua, res| {
if let Some(gltf) = res.as_typed::<Gltf>() {
Some(lua.create_userdata(LuaGltfHandle(gltf)).unwrap())
} else {
None
}
}
);
reg_type.add_data(l);
//reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<LuaGltfHandle>());
} }
fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> { fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
@ -21,7 +42,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
let globals = ctx.globals()?; let globals = ctx.globals()?;
globals.set("World", ctx.create_proxy::<ScriptWorldPtr>()?)?; globals.set("World", ctx.create_proxy::<ScriptWorldPtr>()?)?;
globals.set("DynamicBundle", ctx.create_proxy::<ScriptDynamicBundle>()?)?; globals.set("DynamicBundle", ctx.create_proxy::<ScriptDynamicBundle>()?)?;
globals.set("ModelComponent", ctx.create_proxy::<LuaModelComponent>()?)?; globals.set("SceneComponent", ctx.create_proxy::<LuaSceneHandle>()?)?;
globals.set("ActionHandler", ctx.create_proxy::<LuaActionHandler>()?)?; globals.set("ActionHandler", ctx.create_proxy::<LuaActionHandler>()?)?;
let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?; let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?;

View File

@ -30,6 +30,7 @@ impl ScriptHost for LuaHost {
}); });
for provider in providers.apis.iter_mut() { for provider in providers.apis.iter_mut() {
let mut provider = provider.lock().unwrap();
provider.expose_api(script_data, &mut ctx)?; provider.expose_api(script_data, &mut ctx)?;
} }
@ -44,6 +45,7 @@ impl ScriptHost for LuaHost {
fn setup_script(&mut self, script_data: &crate::ScriptData, ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders<Self>) -> Result<(), ScriptError> { fn setup_script(&mut self, script_data: &crate::ScriptData, ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders<Self>) -> Result<(), ScriptError> {
for provider in providers.apis.iter_mut() { for provider in providers.apis.iter_mut() {
let mut provider = provider.lock().unwrap();
provider.setup_script(script_data, ctx)?; provider.setup_script(script_data, ctx)?;
} }
@ -57,6 +59,7 @@ impl ScriptHost for LuaHost {
ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders<Self>, ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders<Self>,
function_name: &str) -> Result<(), ScriptError> { function_name: &str) -> Result<(), ScriptError> {
for provider in providers.apis.iter_mut() { for provider in providers.apis.iter_mut() {
let mut provider = provider.lock().unwrap();
provider.update_script_environment(world.clone(), script_data, ctx)?; provider.update_script_environment(world.clone(), script_data, ctx)?;
} }

View File

@ -29,7 +29,7 @@ pub fn lua_scripts_create_contexts(
let script_name = script.name(); let script_name = script.name();
let _span = debug_span!("lua", script = script_name).entered(); let _span = debug_span!("lua", script = script_name).entered();
if let Some(script_res) = &script.res_handle().try_data_ref() { if let Some(script_res) = &script.res_handle().data_ref() {
debug!("Loading script..."); debug!("Loading script...");
let mut script_ctx = let mut script_ctx =
host.load_script(&script_res.bytes, &script_data, &mut providers)?; host.load_script(&script_res.bytes, &script_data, &mut providers)?;
@ -74,14 +74,14 @@ pub fn lua_scripts_create_contexts(
/// Note: This only works if the script is watched. See [`lyra_resource::ResourceManager::watch`]. /// Note: This only works if the script is watched. See [`lyra_resource::ResourceManager::watch`].
pub fn lua_scripts_reload_system( pub fn lua_scripts_reload_system(
mut contexts: ResMut<ScriptContexts<LuaContext>>, mut contexts: ResMut<ScriptContexts<LuaContext>>,
mut resman: ResMut<ResourceManager>, resman: ResMut<ResourceManager>,
view: View<&ScriptList<LuaScript>>, view: View<&ScriptList<LuaScript>>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
for scripts in view.into_iter() { for scripts in view.into_iter() {
for script in scripts.iter() { for script in scripts.iter() {
let handle = script.res_handle(); let handle = script.res_handle();
if handle.is_watched() { if handle.is_watched() {
let handle_path = handle.path(); let handle_path = handle.path().unwrap();
let watch_recv = resman.watcher_event_recv(&handle_path).unwrap(); let watch_recv = resman.watcher_event_recv(&handle_path).unwrap();
match watch_recv.try_recv() { match watch_recv.try_recv() {
@ -199,7 +199,7 @@ impl Plugin for LuaScriptingPlugin {
world.add_resource_default::<ScriptApiProviders<LuaHost>>(); world.add_resource_default::<ScriptApiProviders<LuaHost>>();
world.add_resource_default::<ScriptContexts<LuaContext>>(); world.add_resource_default::<ScriptContexts<LuaContext>>();
let mut loader = world let loader = world
.try_get_resource_mut::<ResourceManager>() .try_get_resource_mut::<ResourceManager>()
.expect("Add 'ResourceManager' to the world before trying to add this plugin"); .expect("Add 'ResourceManager' to the world before trying to add this plugin");
loader.register_loader::<LuaLoader>(); loader.register_loader::<LuaLoader>();

View File

@ -2,12 +2,12 @@ use std::{ptr::NonNull, sync::Arc};
use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr}; use crate::{ScriptBorrow, ScriptEntity, ScriptWorldPtr};
use elua::AsLua; use elua::AsLua;
use lyra_ecs::{query::dynamic::QueryDynamicType, CommandQueue, Commands, DynamicBundle, World}; use lyra_ecs::{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 super::{ use super::{
reflect_user_data, wrappers::LuaResHandle, DynamicViewIter, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE reflect_user_data, wrappers::LuaResHandleToComponent, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE
}; };
impl<'lua> elua::FromLua<'lua> for ScriptEntity { impl<'lua> elua::FromLua<'lua> for ScriptEntity {
@ -114,7 +114,8 @@ impl elua::Userdata for ScriptWorldPtr {
} }
let world = unsafe { this.inner.as_ref() }; let world = unsafe { this.inner.as_ref() };
let mut view = world.dynamic_view(); //let mut view = world.dynamic_view();
let mut view = DynamicViewState::new();
for (idx, comp) in queries.into_iter().enumerate() { for (idx, comp) in queries.into_iter().enumerate() {
match comp { match comp {
@ -160,7 +161,7 @@ impl elua::Userdata for ScriptWorldPtr {
let iter = view.into_iter(); let iter = view.into_iter();
let mut reflected_iter = ReflectedIterator { let mut reflected_iter = ReflectedIterator {
world: this.clone(), world: this.clone(),
dyn_view: DynamicViewIter::from(iter), dyn_view: DynamicViewStateIter::from(iter),
reflected_components: None, reflected_components: None,
}; };
@ -297,12 +298,19 @@ impl elua::Userdata for ScriptWorldPtr {
Ok(()) Ok(())
}) })
.method_mut("request_res", |_, this, path: String| { .method_mut("request_res", |lua, this, path: String| {
let world = this.as_mut(); let world: &mut World = this.as_mut();
let mut man = world.get_resource_mut::<ResourceManager>(); let man = world.get_resource_mut::<ResourceManager>();
let handle = man.request_raw(&path).unwrap(); let handle = man.request_raw(&path).unwrap();
Ok(LuaResHandle::from(handle)) // get the actual resource handle wrapper
let registry = world.get_resource::<TypeRegistry>();
let ty = registry.get_type(handle.resource_type_id())
.expect("Could not find asset type in registry");
let data = ty.get_data::<LuaResHandleToComponent>()
.expect("Asset type does not have 'LuaResHandleToComponent' as TypeData");
Ok((data.fn_to_lua)(lua, handle).unwrap())
}); });
} }
} }

View File

@ -0,0 +1,128 @@
use std::{any::TypeId, ops::Deref};
use elua::{AnyUserdata, AsLua, FromLua, State, Value};
use lyra_resource::{gltf::Gltf, ResHandle, UntypedResHandle};
use lyra_game::scene::SceneGraph;
use lyra_reflect::{Reflect, TypeData};
use lyra_scripting_derive::lua_wrap_handle;
use crate::{lua::{LuaWrapper, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, lyra_engine, ScriptBorrow};
pub struct LuaResHandleToComponent {
/// Create the userdata component that
pub fn_to_lua: fn(&State, UntypedResHandle) -> Option<AnyUserdata>,
}
impl LuaResHandleToComponent {
pub fn new(f: fn(&State, UntypedResHandle) -> Option<AnyUserdata>) -> Self {
Self {
fn_to_lua: f
}
}
}
impl TypeData for LuaResHandleToComponent {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn boxed_clone(&self) -> Box<dyn TypeData> {
todo!()
}
}
#[derive(Clone)]
pub struct LuaResHandle(pub UntypedResHandle);
impl Deref for LuaResHandle {
type Target = UntypedResHandle;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<UntypedResHandle> for LuaResHandle {
fn from(value: UntypedResHandle) -> Self {
LuaResHandle(value)
}
}
impl elua::Userdata for LuaResHandle {
fn name() -> String {
"Handle".to_string()
}
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
builder.field_getter("path", |_, this| Ok(this.path()));
builder.field_getter("version", |_, this| Ok(this.version()));
builder.field_getter("uuid", |_, this| Ok(this.uuid().to_string()));
builder.field_getter("state", |_, this| {
let name = if this.is_loaded() {
"ready"
} else if this.get_error().is_some() {
"error"
} else { "loading" };
Ok(name)
});
builder.method("is_watched", |_, this, ()| {
Ok(this.is_watched())
});
builder.method("is_loaded", |_, this, ()| {
Ok(this.is_loaded())
});
builder.method("wait_until_loaded", |_, this, ()| {
this.wait_recurse_dependencies_load();
Ok(())
});
builder.method(FN_NAME_INTERNAL_AS_COMPONENT, |lua, this, ()| {
let handle = &this.0;
if let Some(handle) = handle.as_typed::<SceneGraph>() {
LuaSceneHandle(handle).as_lua(lua)
} else if let Some(handle) = handle.as_typed::<Gltf>() {
LuaGltfHandle(handle).as_lua(lua)
} else {
Ok(elua::Value::Nil)
}
});
}
}
impl<'a> FromLua<'a> for LuaResHandle {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> {
let tyname = val.type_name();
let ud = val.as_userdata()
.ok_or(elua::Error::type_mismatch("Handle", &tyname))?;
let handle = ud.as_ref::<LuaResHandle>()?;
Ok(handle.clone())
}
}
lua_wrap_handle!(SceneGraph, name=Scene, {});
lua_wrap_handle!(Gltf, {
builder.method("scenes", |lua, this, ()| {
if let Some(data) = this.0.data_ref() {
let table = lua.create_table()?;
for (i, scene) in data.scenes.iter().enumerate() {
let i = i as i64 + 1; // lua indexes start at 1
table.raw_seti(i, LuaSceneHandle(scene.clone()))?;
}
Ok(Value::Table(table))
} else {
Ok(Value::Nil)
}
});
});

View File

@ -4,11 +4,8 @@ pub use math::*;
pub mod delta_time; pub mod delta_time;
pub use delta_time::*; pub use delta_time::*;
pub mod res_handle;
pub use res_handle::*;
pub mod model_comp;
pub use model_comp::*;
pub mod input_actions; pub mod input_actions;
pub use input_actions::*; pub use input_actions::*;
pub mod asset;
pub use asset::*;

View File

@ -1,77 +0,0 @@
use std::{ops::Deref, sync::Arc};
use elua::{AsLua, FromLua};
use lyra_game::scene::ModelComponent;
use lyra_resource::{Model, ResHandle, ResourceStorage};
use crate::lua::FN_NAME_INTERNAL_AS_COMPONENT;
use super::LuaModelComponent;
#[derive(Clone)]
pub struct LuaResHandle(pub Arc<dyn ResourceStorage>);
impl Deref for LuaResHandle {
type Target = dyn ResourceStorage;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl From<Arc<dyn ResourceStorage>> for LuaResHandle {
fn from(value: Arc<dyn ResourceStorage>) -> Self {
LuaResHandle(value)
}
}
impl elua::Userdata for LuaResHandle {
fn name() -> String {
"Handle".to_string()
}
fn build<'a>(builder: &mut elua::UserdataBuilder<'a, Self>) {
builder.field_getter("path", |_, this| Ok(this.path()));
builder.field_getter("version", |_, this| Ok(this.version()));
builder.field_getter("uuid", |_, this| Ok(this.uuid().to_string()));
builder.field_getter("state", |_, this| {
let name = match this.state() {
lyra_resource::ResourceState::Loading => "loading",
lyra_resource::ResourceState::Ready => "ready",
};
Ok(name)
});
builder.method("is_watched", |_, this, ()| {
Ok(this.is_watched())
});
builder.method("is_loaded", |_, this, ()| {
Ok(this.is_loaded())
});
builder.method(FN_NAME_INTERNAL_AS_COMPONENT, |lua, this, ()| {
let any = this.0.as_any();
match any.downcast_ref::<ResHandle<Model>>() {
Some(model) => {
LuaModelComponent(ModelComponent(model.clone())).as_lua(lua)
},
None => {
Ok(elua::Value::Nil)
}
}
});
}
}
impl<'a> FromLua<'a> for LuaResHandle {
fn from_lua(_: &'a elua::State, val: elua::Value<'a>) -> elua::Result<Self> {
let tyname = val.type_name();
let ud = val.as_userdata()
.ok_or(elua::Error::type_mismatch("Handle", &tyname))?;
let handle = ud.as_ref::<LuaResHandle>()?;
Ok(handle.clone())
}
}

View File

@ -1,20 +1,20 @@
use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::atomic::{AtomicU64, Ordering};
use lyra_ecs::Component; use lyra_ecs::Component;
use lyra_resource::ResHandle; use lyra_resource::{ResHandle, ResourceData};
use crate::lyra_engine; use crate::lyra_engine;
static SCRIPT_ID_COUNTER: AtomicU64 = AtomicU64::new(0); static SCRIPT_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
#[derive(Clone)] #[derive(Clone)]
pub struct Script<T> { pub struct Script<T: ResourceData> {
res: ResHandle<T>, res: ResHandle<T>,
name: String, name: String,
id: u64 id: u64
} }
impl<T> Script<T> { impl<T: ResourceData> Script<T> {
pub fn new(name: &str, script: ResHandle<T>) -> Self { pub fn new(name: &str, script: ResHandle<T>) -> Self {
Self { Self {
res: script, res: script,
@ -38,15 +38,15 @@ impl<T> Script<T> {
/// A list of scripts /// A list of scripts
#[derive(Clone, Default, Component)] #[derive(Clone, Default, Component)]
pub struct ScriptList<T: 'static>(Vec<Script<T>>); pub struct ScriptList<T: ResourceData>(Vec<Script<T>>);
impl<T> ScriptList<T> { impl<T: ResourceData> ScriptList<T> {
pub fn new(list: Vec<Script<T>>) -> Self { pub fn new(list: Vec<Script<T>>) -> Self {
Self(list) Self(list)
} }
} }
impl<T> std::ops::Deref for ScriptList<T> { impl<T: ResourceData> std::ops::Deref for ScriptList<T> {
type Target = Vec<Script<T>>; type Target = Vec<Script<T>>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
@ -54,7 +54,7 @@ impl<T> std::ops::Deref for ScriptList<T> {
} }
} }
impl<T> std::ops::DerefMut for ScriptList<T> { impl<T: ResourceData> std::ops::DerefMut for ScriptList<T> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0 &mut self.0
} }

View File

@ -16,6 +16,7 @@ pkgs.mkShell.override {
buildInputs = [ buildInputs = [
rust rust
] ++ (with pkgs; [ ] ++ (with pkgs; [
cargo-expand
pkg-config openssl udev pkg-config openssl udev
wasm-pack trunk wasm-pack trunk
heaptrack valgrind heaptrack valgrind