lua: create LuaConvert derive macro
This commit is contained in:
parent
8248c4918a
commit
2ecb48c89e
12 changed files with 766 additions and 187 deletions
crates
lyra-reflect/lyra-reflect-derive/src
lyra-scripting
lyra-scripting-derive/src
src
|
@ -1,7 +1,9 @@
|
|||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Ident;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, Attribute, DeriveInput, GenericParam, Generics, Path, TypeParamBound};
|
||||
use syn::{
|
||||
parse_macro_input, Attribute, DeriveInput, GenericParam, Generics, Path, TypeParamBound,
|
||||
};
|
||||
|
||||
mod enum_derive;
|
||||
#[allow(unused_imports)]
|
||||
|
@ -24,15 +26,21 @@ impl FieldAttributes {
|
|||
/// Searches for a usage of the 'reflect' attribute and returns a list of the
|
||||
/// things used in the usage.
|
||||
pub fn from_vec(v: &Vec<syn::Attribute>) -> Result<Self, syn::Error> {
|
||||
let s: Result<Vec<ReflectAttribute>, _> = v.iter().filter_map(|att| match &att.meta {
|
||||
syn::Meta::Path(_) => None,
|
||||
syn::Meta::List(l) => {
|
||||
Some(syn::parse::<ReflectAttribute>(l.tokens.clone().into()))
|
||||
}
|
||||
syn::Meta::NameValue(_) => None
|
||||
}).collect();
|
||||
let mut attrs = vec![];
|
||||
|
||||
Ok(Self(s?))
|
||||
for attr in v {
|
||||
if attr.path().is_ident("reflect") {
|
||||
match &attr.meta {
|
||||
syn::Meta::List(l) => {
|
||||
let a = syn::parse::<ReflectAttribute>(l.tokens.clone().into())?;
|
||||
attrs.push(a);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self(attrs))
|
||||
}
|
||||
|
||||
pub fn has_skip(&self) -> bool {
|
||||
|
@ -42,7 +50,7 @@ impl FieldAttributes {
|
|||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum ReflectAttribute {
|
||||
Skip
|
||||
Skip,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for ReflectAttribute {
|
||||
|
@ -52,7 +60,10 @@ impl syn::parse::Parse for ReflectAttribute {
|
|||
|
||||
match ident_str.as_str() {
|
||||
"skip" => Ok(Self::Skip),
|
||||
_ => Err(syn::Error::new(ident.span(), "Unknown reflect attribute flag"))
|
||||
_ => Err(syn::Error::new(
|
||||
ident.span(),
|
||||
format!("Unknown reflect attribute flag: '{}'", ident_str),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +73,7 @@ pub(crate) struct ReflectDef {
|
|||
//pub ident: Ident,
|
||||
pub type_path: Path,
|
||||
pub generics: Generics,
|
||||
pub attributes: Vec<Attribute>
|
||||
pub attributes: Vec<Attribute>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for ReflectDef {
|
||||
|
@ -89,14 +100,10 @@ pub fn derive_reflect(input: proc_macro::TokenStream) -> proc_macro::TokenStream
|
|||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let stream = match &input.data {
|
||||
syn::Data::Enum(e) => {
|
||||
enum_derive::derive_reflect_enum(&input, e)
|
||||
},
|
||||
syn::Data::Struct(s) => {
|
||||
struct_derive::derive_reflect_struct(&input, s)
|
||||
},
|
||||
syn::Data::Enum(e) => enum_derive::derive_reflect_enum(&input, e),
|
||||
syn::Data::Struct(s) => struct_derive::derive_reflect_struct(&input, s),
|
||||
//syn::Data::Union(_) => todo!(),
|
||||
_ => todo!()
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
proc_macro::TokenStream::from(stream)
|
||||
|
@ -111,7 +118,9 @@ pub fn impl_reflect_trait_value(input: TokenStream) -> TokenStream {
|
|||
let type_path = reflect.type_path;
|
||||
// convert the type path to a string. This would not create a leading separator
|
||||
let type_path_str = {
|
||||
let idents: Vec<String> = type_path.segments.iter()
|
||||
let idents: Vec<String> = type_path
|
||||
.segments
|
||||
.iter()
|
||||
.map(|segment| segment.ident.to_string())
|
||||
.collect();
|
||||
idents.join("::")
|
||||
|
@ -124,15 +133,15 @@ pub fn impl_reflect_trait_value(input: TokenStream) -> TokenStream {
|
|||
fn name(&self) -> ::std::string::String {
|
||||
#type_path_str.to_string()
|
||||
}
|
||||
|
||||
|
||||
fn type_id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<#type_path #ty_generics>()
|
||||
}
|
||||
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
@ -140,7 +149,7 @@ pub fn impl_reflect_trait_value(input: TokenStream) -> TokenStream {
|
|||
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
fn apply(&mut self, val: &dyn lyra_engine::reflect::Reflect) {
|
||||
let val = val.as_any().downcast_ref::<Self>()
|
||||
.expect("The type of `val` is not the same as `self`");
|
||||
|
@ -170,7 +179,10 @@ pub fn impl_reflect_trait_value(input: TokenStream) -> TokenStream {
|
|||
})
|
||||
}
|
||||
|
||||
pub(crate) fn add_trait_bounds(mut generics: Generics, add_bounds: Vec<TypeParamBound>) -> Generics {
|
||||
pub(crate) fn add_trait_bounds(
|
||||
mut generics: Generics,
|
||||
add_bounds: Vec<TypeParamBound>,
|
||||
) -> Generics {
|
||||
for param in &mut generics.params {
|
||||
if let GenericParam::Type(ref mut type_param) = *param {
|
||||
for bound in add_bounds.iter() {
|
||||
|
@ -184,4 +196,4 @@ pub(crate) fn add_trait_bounds(mut generics: Generics, add_bounds: Vec<TypeParam
|
|||
#[proc_macro]
|
||||
pub fn impl_reflect_simple_struct(input: TokenStream) -> TokenStream {
|
||||
struct_macro::impl_reflect_simple_struct(input)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use quote::quote;
|
||||
use syn::{parenthesized, token, Token};
|
||||
|
||||
pub(crate) enum FieldType {
|
||||
|
@ -107,6 +108,76 @@ impl Field {
|
|||
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
pub fn table_setter(&self) -> proc_macro2::TokenStream {
|
||||
if self.skip_setter {
|
||||
return quote!();
|
||||
}
|
||||
|
||||
let ident = &self.field;
|
||||
|
||||
match &self.setter {
|
||||
Some(set) => quote! {
|
||||
table.set(stringify!(#ident), #set)?;
|
||||
},
|
||||
None => {
|
||||
if let Some(wrap_with) = &self.wrap_with {
|
||||
quote! {
|
||||
let v = #wrap_with::into_lua(lua, &self.#ident)?;
|
||||
table.set(stringify!(#ident), v)?;
|
||||
}
|
||||
} else if let Some(ty) = self.field_ty.get_type_path() {
|
||||
let arg = if self.field_ty.is_wrapped() {
|
||||
quote!(#ty(self.#ident.clone()))
|
||||
} else {
|
||||
quote!(self.#ident.clone())
|
||||
};
|
||||
|
||||
quote! {
|
||||
table.set(stringify!(#ident), #arg)?;
|
||||
}
|
||||
} else {
|
||||
syn::Error::new_spanned(
|
||||
ident,
|
||||
format!("field type not specified: '{}'", ident.to_string()),
|
||||
)
|
||||
.into_compile_error()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn table_getter(&self) -> proc_macro2::TokenStream {
|
||||
let ident = &self.field;
|
||||
|
||||
match &self.getter {
|
||||
Some(get) => {
|
||||
quote! {
|
||||
let #ident = #get;
|
||||
}
|
||||
}
|
||||
None => match &self.wrap_with {
|
||||
Some(wrap_with) => {
|
||||
quote! {
|
||||
let #ident = {
|
||||
let t = table.get(stringify!(#ident))?;
|
||||
#wrap_with::from_lua(_lua, &t)?
|
||||
};
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let ty = self
|
||||
.field_ty
|
||||
.get_type_path()
|
||||
.expect("no field type specified");
|
||||
|
||||
quote! {
|
||||
let #ident: #ty = table.get(stringify!(#ident))?;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for Field {
|
||||
|
|
|
@ -7,11 +7,13 @@ use quote::quote;
|
|||
use syn::{parse_macro_input, Token};
|
||||
|
||||
mod vec_wrapper;
|
||||
use to_lua_derive::derive_lua_convert_impl;
|
||||
use to_lua_macro::to_lua_struct_impl;
|
||||
use vec_wrapper::VecWrapper;
|
||||
|
||||
mod field;
|
||||
mod lua_macro;
|
||||
mod to_lua_derive;
|
||||
mod to_lua_macro;
|
||||
|
||||
mod handle_macro;
|
||||
|
@ -20,15 +22,18 @@ pub(crate) const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_
|
|||
pub(crate) const FN_NAME_INTERNAL_REFLECT: &str = "__lyra_internal_reflect";
|
||||
|
||||
fn script_crate_path() -> proc_macro2::TokenStream {
|
||||
let name =
|
||||
proc_macro_crate::crate_name("lyra-scripting").expect("lyra-scripting is not a depdency");
|
||||
let name = proc_macro_crate::crate_name("lyra-scripting"); //.expect("lyra-scripting is not a depdency");
|
||||
|
||||
match name {
|
||||
proc_macro_crate::FoundCrate::Itself => quote!(crate),
|
||||
proc_macro_crate::FoundCrate::Name(n) => {
|
||||
Ok(proc_macro_crate::FoundCrate::Itself) => quote!(crate),
|
||||
Ok(proc_macro_crate::FoundCrate::Name(n)) => {
|
||||
let ident = syn::Ident::new(&n, Span::call_site());
|
||||
quote!(#ident)
|
||||
}
|
||||
// assume that this crate is being used through an import of lyra_engine
|
||||
Err(_) => {
|
||||
quote!(lyra_engine::script)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,6 +55,11 @@ pub fn to_lua_convert(input: proc_macro::TokenStream) -> proc_macro::TokenStream
|
|||
to_lua_struct_impl(input)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(LuaConvert, attributes(lua))]
|
||||
pub fn derive_lua_convert(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
derive_lua_convert_impl(input)
|
||||
}
|
||||
|
||||
pub(crate) struct VecExtensionInputs {
|
||||
#[allow(dead_code)]
|
||||
pub type_path: syn::Path,
|
||||
|
|
|
@ -585,7 +585,7 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
|
||||
proc_macro::TokenStream::from(quote! {
|
||||
#[derive(Clone, lyra_engine::reflect::Reflect, #(#derive_idents_iter),*)]
|
||||
pub struct #wrapper_typename(#[reflect(skip)] pub(crate) #path);
|
||||
pub struct #wrapper_typename(#[reflect(skip)] pub #path);
|
||||
|
||||
impl std::ops::Deref for #wrapper_typename {
|
||||
type Target = #path;
|
||||
|
|
468
crates/lyra-scripting/lyra-scripting-derive/src/to_lua_derive.rs
Normal file
468
crates/lyra-scripting/lyra-scripting-derive/src/to_lua_derive.rs
Normal file
|
@ -0,0 +1,468 @@
|
|||
use quote::{quote, ToTokens};
|
||||
use syn::{parse_macro_input, spanned::Spanned};
|
||||
|
||||
use crate::{
|
||||
field::{Field, FieldType},
|
||||
to_lua_macro::{ReflectType, StructType},
|
||||
};
|
||||
|
||||
pub(crate) fn derive_lua_convert_impl(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(item as syn::DeriveInput);
|
||||
|
||||
match &input.data {
|
||||
syn::Data::Struct(s) => lua_convert_struct_impl(&input, s),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn lua_convert_struct_impl(
|
||||
input: &syn::DeriveInput,
|
||||
struc: &syn::DataStruct,
|
||||
) -> proc_macro::TokenStream {
|
||||
let crate_path = quote!(lyra_engine::script);
|
||||
let mlua = quote!(#crate_path::lua::mlua);
|
||||
|
||||
let dattrs = DeriveAttrs::new(&input.attrs);
|
||||
let ty = &input.ident;
|
||||
let lua_name = dattrs.rename().cloned().unwrap_or(ty.to_string());
|
||||
|
||||
let reflect_type = dattrs.reflect_type();
|
||||
let struct_type = match struc.fields {
|
||||
syn::Fields::Named(_) => StructType::Fields,
|
||||
syn::Fields::Unnamed(_) => StructType::Tuple,
|
||||
syn::Fields::Unit => todo!("Handle unit structs"),
|
||||
};
|
||||
|
||||
let fields = struc
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| {
|
||||
let attrs = FieldAttrs::new(&f.attrs);
|
||||
let ident = f.ident.clone().expect("TODO: support enum structs");
|
||||
|
||||
let ty = if let Some(wrap) = attrs.wrapper() {
|
||||
FieldType::Wrapped(wrap.clone())
|
||||
} else {
|
||||
match &f.ty {
|
||||
syn::Type::Path(path) => FieldType::Type(path.path.clone()),
|
||||
_ => todo!("struc.fields.map handle invalid field type"),
|
||||
}
|
||||
};
|
||||
|
||||
Field {
|
||||
field: ident,
|
||||
field_ty: ty,
|
||||
skip_setter: false,
|
||||
getter: attrs.get().cloned(),
|
||||
setter: attrs.set().cloned(),
|
||||
wrap_with: attrs.wrap_with().cloned(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Field>>();
|
||||
let field_getters_iter = fields.iter().map(Field::table_getter);
|
||||
let field_setters_iter = fields.iter().map(Field::table_setter);
|
||||
|
||||
let struct_creator = wrapper_creation(ty, struct_type, None, &fields);
|
||||
let reflect_fn = get_reflect_lua_functions(&crate_path, &reflect_type, ty, true);
|
||||
let reflect_type_fn = get_reflect_lua_functions(&crate_path, &reflect_type, ty, false);
|
||||
quote! {
|
||||
impl #mlua::FromLua for #ty {
|
||||
fn from_lua(val: #mlua::Value, _lua: &#mlua::Lua) -> #mlua::Result<Self> {
|
||||
let ty = val.type_name();
|
||||
let table = val.as_table().ok_or(#mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "Table".into(),
|
||||
message: Some("expected Table".into()),
|
||||
})?;
|
||||
|
||||
|
||||
#(
|
||||
#field_getters_iter
|
||||
)*
|
||||
|
||||
Ok(#struct_creator)
|
||||
}
|
||||
}
|
||||
|
||||
impl #mlua::IntoLua for #ty {
|
||||
fn into_lua(self, lua: &#mlua::Lua) -> #mlua::Result<#mlua::Value> {
|
||||
use #crate_path::lua::LuaWrapper;
|
||||
|
||||
let table = lua.create_table()?;
|
||||
#(
|
||||
#field_setters_iter
|
||||
)*
|
||||
|
||||
table.set(
|
||||
#crate_path::lua::FN_NAME_INTERNAL_REFLECT,
|
||||
lua.create_function(|_, this: Self| {
|
||||
#reflect_fn
|
||||
})?,
|
||||
)?;
|
||||
|
||||
table.set(
|
||||
#crate_path::lua::FN_NAME_INTERNAL_REFLECT_TYPE,
|
||||
lua.create_function(|_, ()| {
|
||||
#reflect_type_fn
|
||||
})?,
|
||||
)?;
|
||||
|
||||
table.set(#mlua::MetaMethod::Type.name(), #lua_name)?;
|
||||
|
||||
Ok(#mlua::Value::Table(table))
|
||||
}
|
||||
}
|
||||
|
||||
impl #crate_path::lua::LuaWrapper for #ty {
|
||||
type Wrap = #ty;
|
||||
|
||||
#[inline(always)]
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
std::any::TypeId::of::<#ty>()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
.into_token_stream()
|
||||
.into()
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
enum LuaFuncKind {
|
||||
Function,
|
||||
Method,
|
||||
MethodMut,
|
||||
MetaMethod,
|
||||
MetaMethodMut,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct LuaFunc {
|
||||
kind: LuaFuncKind,
|
||||
name: String,
|
||||
// name: type
|
||||
args: Vec<(syn::Ident, proc_macro2::TokenStream)>,
|
||||
block: proc_macro2::TokenStream,
|
||||
}
|
||||
|
||||
impl LuaFunc {
|
||||
fn as_tokens(&self, builder: &syn::Ident) -> proc_macro2::TokenStream {
|
||||
let fn_name = &self.name;
|
||||
let fn_block = &self.block;
|
||||
let (arg_names, arg_types): (Vec<_>, Vec<_>) = self.args.iter().cloned().unzip();
|
||||
let arg_names_iter = arg_names.into_iter();
|
||||
let arg_types_iter = arg_types.into_iter();
|
||||
let closure_args = quote!( (#( #arg_names_iter ),*): (#( #arg_types_iter ),*) );
|
||||
|
||||
match self.kind {
|
||||
LuaFuncKind::Function => quote! {
|
||||
#builder.add_function(#fn_name, |lua, #closure_args| {
|
||||
#fn_block
|
||||
});
|
||||
},
|
||||
LuaFuncKind::Method => quote! {
|
||||
#builder.add_method(#fn_name, |lua, this, #closure_args| {
|
||||
#fn_block
|
||||
});
|
||||
},
|
||||
LuaFuncKind::MethodMut => quote! {
|
||||
#builder.add_method_mut(#fn_name, |lua, this, #closure_args| {
|
||||
#fn_block
|
||||
});
|
||||
},
|
||||
LuaFuncKind::MetaMethod => quote! {
|
||||
#builder.add_meta_method(#fn_name, |lua, this, #closure_args| {
|
||||
#fn_block
|
||||
});
|
||||
},
|
||||
LuaFuncKind::MetaMethodMut => quote! {
|
||||
#builder.add_meta_method_mut(#fn_name, |lua, this, #closure_args| {
|
||||
#fn_block
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum DeriveAttr {
|
||||
Rename(String),
|
||||
ReflectType(ReflectType),
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for DeriveAttr {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let ident: syn::Ident = input.parse()?;
|
||||
let ident_str = ident.to_string().to_lowercase();
|
||||
let _eq: syn::Token![=] = input.parse()?;
|
||||
|
||||
match ident_str.as_str() {
|
||||
"rename" => {
|
||||
let s: syn::LitStr = input.parse()?;
|
||||
Ok(Self::Rename(s.value()))
|
||||
}
|
||||
"reflect" => {
|
||||
let tyid: syn::Ident = input.parse()?;
|
||||
let id = tyid.to_string().to_lowercase();
|
||||
let id = id.as_str();
|
||||
|
||||
let ty = match id {
|
||||
"component" => Ok(ReflectType::Component),
|
||||
"resource" => Ok(ReflectType::Resource),
|
||||
_ => Err(syn::Error::new(
|
||||
tyid.span(),
|
||||
"unknown reflect type, expected 'component' or 'resource'",
|
||||
)),
|
||||
}?;
|
||||
|
||||
Ok(Self::ReflectType(ty))
|
||||
}
|
||||
_ => Err(syn::Error::new(
|
||||
ident.span(),
|
||||
format!("Unknown field attribute flag: '{}'", ident_str),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct DeriveAttrs(Vec<DeriveAttr>);
|
||||
|
||||
impl DeriveAttrs {
|
||||
fn new(attrs: &Vec<syn::Attribute>) -> Self {
|
||||
let mut s = Self::default();
|
||||
|
||||
for value in attrs {
|
||||
if !value.path().is_ident("lua") {
|
||||
continue;
|
||||
}
|
||||
|
||||
match &value.meta {
|
||||
syn::Meta::Path(_) => todo!("handle invalid path field attribute"),
|
||||
syn::Meta::List(list) => {
|
||||
let f = list
|
||||
.parse_args_with(
|
||||
syn::punctuated::Punctuated::<_, syn::Token![,]>::parse_terminated,
|
||||
)
|
||||
.unwrap();
|
||||
s.0 = f.into_iter().collect();
|
||||
}
|
||||
syn::Meta::NameValue(_) => todo!("handle invalid name value field attribute"),
|
||||
}
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
fn rename(&self) -> Option<&String> {
|
||||
self.0.iter().find_map(|a| {
|
||||
if let DeriveAttr::Rename(s) = a {
|
||||
Some(s)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn reflect_type(&self) -> ReflectType {
|
||||
self.0
|
||||
.iter()
|
||||
.find_map(|a| {
|
||||
if let DeriveAttr::ReflectType(r) = a {
|
||||
Some(*r)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum FieldAttr {
|
||||
Wrapper(syn::Path),
|
||||
Set(syn::Block),
|
||||
Get(syn::Block),
|
||||
WrapWith(syn::Path),
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for FieldAttr {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let ident: syn::Ident = input.parse()?;
|
||||
let ident_str = ident.to_string().to_lowercase();
|
||||
let _eq: syn::Token![=] = input.parse()?;
|
||||
|
||||
match ident_str.as_str() {
|
||||
"wrap" => Ok(Self::Wrapper(input.parse()?)),
|
||||
"set" => Ok(Self::Set(input.parse()?)),
|
||||
"get" => Ok(Self::Get(input.parse()?)),
|
||||
"wrap_with" => Ok(Self::WrapWith(input.parse()?)),
|
||||
_ => Err(syn::Error::new(
|
||||
ident.span(),
|
||||
format!("Unknown field attribute flag: '{}'", ident_str),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct FieldAttrs(Vec<FieldAttr>);
|
||||
|
||||
impl FieldAttrs {
|
||||
fn new(attrs: &Vec<syn::Attribute>) -> Self {
|
||||
let mut s = Self::default();
|
||||
|
||||
for value in attrs {
|
||||
if !value.path().is_ident("lua") {
|
||||
continue;
|
||||
}
|
||||
|
||||
match &value.meta {
|
||||
syn::Meta::Path(_) => todo!("handle invalid path field attribute"),
|
||||
syn::Meta::List(list) => {
|
||||
let f = list
|
||||
.parse_args_with(
|
||||
syn::punctuated::Punctuated::<_, syn::Token![,]>::parse_terminated,
|
||||
)
|
||||
.unwrap();
|
||||
s.0 = f.into_iter().collect();
|
||||
}
|
||||
syn::Meta::NameValue(_) => todo!("handle invalid name value field attribute"),
|
||||
}
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
fn wrapper(&self) -> Option<&syn::Path> {
|
||||
self.0.iter().find_map(|a| {
|
||||
if let FieldAttr::Wrapper(p) = a {
|
||||
Some(p)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn set(&self) -> Option<&syn::Block> {
|
||||
self.0.iter().find_map(|a| {
|
||||
if let FieldAttr::Set(b) = a {
|
||||
Some(b)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get(&self) -> Option<&syn::Block> {
|
||||
self.0.iter().find_map(|a| {
|
||||
if let FieldAttr::Get(b) = a {
|
||||
Some(b)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn wrap_with(&self) -> Option<&syn::Path> {
|
||||
self.0.iter().find_map(|a| {
|
||||
if let FieldAttr::WrapWith(p) = a {
|
||||
Some(p)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// impl From<&Vec<syn::Attribute>> for FieldAttrs {
|
||||
// fn from(attrs: &Vec<syn::Attribute>) -> Self {
|
||||
// let mut s = Self::default();
|
||||
|
||||
// for value in attrs {
|
||||
// if !value.path().is_ident("lua") {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// match &value.meta {
|
||||
// syn::Meta::Path(_) => todo!("handle invalid path field attribute"),
|
||||
// syn::Meta::List(list) => {
|
||||
// let f = list.parse_args_with(
|
||||
// syn::punctuated::Punctuated::<FieldAttr, syn::Token![,]>::parse_terminated,
|
||||
// ).unwrap();
|
||||
// s.0 = f.into_iter().collect();
|
||||
// }
|
||||
// syn::Meta::NameValue(_) => todo!("handle invalid name value field attribute"),
|
||||
// }
|
||||
|
||||
// s
|
||||
// }
|
||||
// }
|
||||
|
||||
pub(crate) fn get_reflect_lua_functions(
|
||||
crate_path: &proc_macro2::TokenStream,
|
||||
reflect_ty: &ReflectType,
|
||||
ty: &syn::Ident,
|
||||
set_data: bool,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let data = if set_data {
|
||||
quote!(Some(this.clone()))
|
||||
} else {
|
||||
quote!(None)
|
||||
};
|
||||
|
||||
match reflect_ty {
|
||||
ReflectType::Component => {
|
||||
quote! {
|
||||
Ok(#crate_path::ScriptBorrow::from_component::<#ty>(#data))
|
||||
}
|
||||
}
|
||||
ReflectType::Resource => {
|
||||
quote! {
|
||||
Ok(#crate_path::ScriptBorrow::from_resource::<#ty>(#data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn wrapper_creation(
|
||||
ty: &syn::Ident,
|
||||
struct_type: StructType,
|
||||
create: Option<&syn::Block>,
|
||||
fields: &Vec<Field>,
|
||||
) -> proc_macro2::TokenStream {
|
||||
match create {
|
||||
Some(b) => quote!(#b),
|
||||
None => {
|
||||
let field_iter = fields.iter().map(|f| {
|
||||
let ident = &f.field;
|
||||
if f.field_ty.is_wrapped() && struct_type == StructType::Fields {
|
||||
quote!(#ident: (*#ident).clone())
|
||||
} else {
|
||||
quote!(#ident)
|
||||
}
|
||||
});
|
||||
|
||||
match struct_type {
|
||||
StructType::Fields => {
|
||||
quote! {
|
||||
#ty {
|
||||
#(
|
||||
#field_iter
|
||||
),*
|
||||
}
|
||||
}
|
||||
}
|
||||
StructType::Tuple => {
|
||||
quote! {
|
||||
#ty( #(#field_iter),* )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,75 +4,7 @@ use syn::{braced, parenthesized, parse_macro_input, punctuated::Punctuated, toke
|
|||
|
||||
use crate::{field::Field, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE};
|
||||
|
||||
fn field_table_setter(field: &Field) -> syn::Result<proc_macro2::TokenStream> {
|
||||
let ident = &field.field;
|
||||
|
||||
match &field.setter {
|
||||
Some(set) => Ok(quote! {
|
||||
table.set(stringify!(#ident), #set)?;
|
||||
}),
|
||||
None => {
|
||||
if let Some(ty) = field.field_ty.get_type_path() {
|
||||
let arg = if field.field_ty.is_wrapped() {
|
||||
quote!(#ty(self.#ident.clone()))
|
||||
} else {
|
||||
quote!(self.#ident.clone())
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
table.set(stringify!(#ident), #arg)?;
|
||||
})
|
||||
} else {
|
||||
match &field.wrap_with {
|
||||
Some(wrap_with) => Ok(quote! {
|
||||
let v = #wrap_with::into_lua(lua, &self.#ident)?;
|
||||
table.set(stringify!(#ident), v)?;
|
||||
}),
|
||||
None => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
ident,
|
||||
format!("field type not specified: '{}'", ident.to_string()),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn field_table_getter(field: &Field) -> proc_macro2::TokenStream {
|
||||
let ident = &field.field;
|
||||
|
||||
match &field.getter {
|
||||
Some(get) => {
|
||||
quote! {
|
||||
let #ident = #get;
|
||||
}
|
||||
}
|
||||
None => match &field.wrap_with {
|
||||
Some(wrap_with) => {
|
||||
quote! {
|
||||
let #ident = {
|
||||
let t = table.get(stringify!(#ident))?;
|
||||
#wrap_with::from_lua(_lua, &t)?
|
||||
};
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let ty = field
|
||||
.field_ty
|
||||
.get_type_path()
|
||||
.expect("no field type specified");
|
||||
|
||||
quote! {
|
||||
let #ident: #ty = table.get(stringify!(#ident))?;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn wrapper_creation(
|
||||
pub(crate) fn wrapper_creation(
|
||||
wrapper: &syn::Ident,
|
||||
type_path: &syn::Path,
|
||||
struct_type: StructType,
|
||||
|
@ -116,7 +48,7 @@ fn wrapper_creation(
|
|||
}
|
||||
}
|
||||
|
||||
fn get_reflect_lua_functions(
|
||||
pub(crate) fn get_reflect_lua_functions(
|
||||
crate_path: &proc_macro2::TokenStream,
|
||||
ty: &ReflectType,
|
||||
type_path: &syn::Path,
|
||||
|
@ -142,16 +74,17 @@ fn get_reflect_lua_functions(
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ReflectType {
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub(crate) enum ReflectType {
|
||||
//Unknown,
|
||||
#[default]
|
||||
Component,
|
||||
Resource,
|
||||
}
|
||||
|
||||
/// The type of the wrapping struct
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
enum StructType {
|
||||
pub(crate) enum StructType {
|
||||
#[default]
|
||||
Fields,
|
||||
Tuple,
|
||||
|
@ -303,6 +236,7 @@ impl syn::parse::Parse for IntoLuaUsage {
|
|||
pub fn to_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as IntoLuaUsage);
|
||||
let crate_path = crate::CRATE_PATH.clone();
|
||||
let mlua = quote!(#crate_path::lua::mlua);
|
||||
|
||||
// unwrap is fine since `Some` is ensured in parse impl
|
||||
let reflect_type = input.reflection_type.as_ref().unwrap();
|
||||
|
@ -319,17 +253,8 @@ pub fn to_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
|||
let derives_iter = input.derives.into_iter();
|
||||
|
||||
let lua_name = &input.table_name;
|
||||
let field_getters_iter = input.fields.iter().map(|f| field_table_getter(f));
|
||||
let field_setters_iter = input.fields.iter().map(|f| {
|
||||
if f.skip_setter {
|
||||
quote! {}
|
||||
} else {
|
||||
match field_table_setter(f) {
|
||||
Ok(stream) => stream,
|
||||
Err(e) => e.into_compile_error(),
|
||||
}
|
||||
}
|
||||
});
|
||||
let field_getters_iter = input.fields.iter().map(Field::table_getter);
|
||||
let field_setters_iter = input.fields.iter().map(Field::table_setter);
|
||||
let struct_creator = wrapper_creation(
|
||||
&wrapper,
|
||||
type_path,
|
||||
|
@ -359,10 +284,10 @@ pub fn to_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
|||
}
|
||||
}
|
||||
|
||||
impl mlua::FromLua for #wrapper {
|
||||
fn from_lua(val: mlua::Value, _lua: &mlua::Lua) -> mlua::Result<Self> {
|
||||
impl #mlua::FromLua for #wrapper {
|
||||
fn from_lua(val: #mlua::Value, _lua: &#mlua::Lua) -> #mlua::Result<Self> {
|
||||
let ty = val.type_name();
|
||||
let table = val.as_table().ok_or(mlua::Error::FromLuaConversionError {
|
||||
let table = val.as_table().ok_or(#mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "Table".into(),
|
||||
message: Some("expected Table".into()),
|
||||
|
@ -376,8 +301,8 @@ pub fn to_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
|||
}
|
||||
}
|
||||
|
||||
impl mlua::IntoLua for #wrapper {
|
||||
fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
|
||||
impl #mlua::IntoLua for #wrapper {
|
||||
fn into_lua(self, lua: &#mlua::Lua) -> #mlua::Result<#mlua::Value> {
|
||||
use #crate_path::lua::LuaWrapper;
|
||||
|
||||
let table = lua.create_table()?;
|
||||
|
@ -399,9 +324,9 @@ pub fn to_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
|||
})?,
|
||||
)?;
|
||||
|
||||
table.set(mlua::MetaMethod::Type.name(), #lua_name)?;
|
||||
table.set(#mlua::MetaMethod::Type.name(), #lua_name)?;
|
||||
|
||||
Ok(mlua::Value::Table(table))
|
||||
Ok(#mlua::Value::Table(table))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use std::{collections::HashMap, sync::{Arc, Mutex}};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use lyra_ecs::{ResourceObject, Entity, World};
|
||||
use lyra_ecs::{Entity, ResourceObject, World};
|
||||
|
||||
use crate::ScriptWorldPtr;
|
||||
|
||||
|
@ -55,27 +58,47 @@ pub trait ScriptApiProvider: Send + Sync {
|
|||
}
|
||||
|
||||
/// Exposes an API in the provided script context.
|
||||
fn expose_api(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), ScriptError>;
|
||||
fn expose_api(
|
||||
&mut self,
|
||||
data: &ScriptData,
|
||||
ctx: &mut Self::ScriptContext,
|
||||
) -> Result<(), ScriptError>;
|
||||
|
||||
/// Setup a script right before its 'init' method is called.
|
||||
fn setup_script(&mut self, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), ScriptError>;
|
||||
fn setup_script(
|
||||
&mut self,
|
||||
data: &ScriptData,
|
||||
ctx: &mut Self::ScriptContext,
|
||||
) -> Result<(), ScriptError>;
|
||||
|
||||
/// A function that is used to update a script's environment.
|
||||
///
|
||||
///
|
||||
/// This is used to update stuff like the world pointer in the script context.
|
||||
fn update_script_environment(&mut self, world: ScriptWorldPtr, data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), ScriptError>;
|
||||
fn update_script_environment(
|
||||
&mut self,
|
||||
world: ScriptWorldPtr,
|
||||
data: &ScriptData,
|
||||
ctx: &mut Self::ScriptContext,
|
||||
) -> Result<(), ScriptError>;
|
||||
}
|
||||
|
||||
/// A storage for a [`ScriptHost`]'s api providers
|
||||
#[derive(Default)]
|
||||
pub struct ScriptApiProviders<T: ScriptHost> {
|
||||
pub apis: Vec<Arc<Mutex<dyn ScriptApiProvider<ScriptContext = T::ScriptContext>>>>,
|
||||
}
|
||||
|
||||
impl<T: ScriptHost> Default for ScriptApiProviders<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
apis: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ScriptHost> ScriptApiProviders<T> {
|
||||
pub fn add_provider<P>(&mut self, provider: P)
|
||||
where
|
||||
P: ScriptApiProvider<ScriptContext = T::ScriptContext> + 'static
|
||||
P: ScriptApiProvider<ScriptContext = T::ScriptContext> + 'static,
|
||||
{
|
||||
self.apis.push(Arc::new(Mutex::new(provider)));
|
||||
}
|
||||
|
@ -86,20 +109,32 @@ pub trait ScriptHost: Default + ResourceObject {
|
|||
type ScriptContext;
|
||||
|
||||
/// Loads a script and creates a context for it.
|
||||
///
|
||||
///
|
||||
/// Before the script is executed, the API providers are exposed to the script.
|
||||
fn load_script(&mut self, script: &[u8], script_data: &ScriptData,
|
||||
providers: &mut crate::ScriptApiProviders<Self>)
|
||||
-> Result<Self::ScriptContext, ScriptError>;
|
||||
fn load_script(
|
||||
&mut self,
|
||||
script: &[u8],
|
||||
script_data: &ScriptData,
|
||||
providers: &mut crate::ScriptApiProviders<Self>,
|
||||
) -> Result<Self::ScriptContext, ScriptError>;
|
||||
|
||||
/// Setup a script for execution.
|
||||
fn setup_script(&mut self, script_data: &ScriptData, ctx: &mut Self::ScriptContext,
|
||||
providers: &mut ScriptApiProviders<Self>) -> Result<(), ScriptError>;
|
||||
fn setup_script(
|
||||
&mut self,
|
||||
script_data: &ScriptData,
|
||||
ctx: &mut Self::ScriptContext,
|
||||
providers: &mut ScriptApiProviders<Self>,
|
||||
) -> Result<(), ScriptError>;
|
||||
|
||||
/// Calls a event function in the script.
|
||||
fn call_script(&mut self, world: ScriptWorldPtr, script_data: &crate::ScriptData,
|
||||
ctx: &mut Self::ScriptContext, providers: &mut crate::ScriptApiProviders<Self>,
|
||||
event_fn_name: &str) -> Result<(), ScriptError>;
|
||||
fn call_script(
|
||||
&mut self,
|
||||
world: ScriptWorldPtr,
|
||||
script_data: &crate::ScriptData,
|
||||
ctx: &mut Self::ScriptContext,
|
||||
providers: &mut crate::ScriptApiProviders<Self>,
|
||||
event_fn_name: &str,
|
||||
) -> Result<(), ScriptError>;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -109,9 +144,7 @@ pub struct ScriptContexts<T> {
|
|||
|
||||
impl<T> ScriptContexts<T> {
|
||||
pub fn new(contexts: HashMap<u64, T>) -> Self {
|
||||
Self {
|
||||
contexts,
|
||||
}
|
||||
Self { contexts }
|
||||
}
|
||||
|
||||
pub fn add_context(&mut self, script_id: u64, context: T) {
|
||||
|
@ -137,4 +170,4 @@ impl<T> ScriptContexts<T> {
|
|||
pub fn len(&self) -> usize {
|
||||
self.contexts.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ pub use script::*;
|
|||
|
||||
use lyra_game::game::App;
|
||||
|
||||
pub use lyra_scripting_derive::*;
|
||||
|
||||
// required for some proc macros :(
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) mod lyra_engine {
|
||||
|
@ -146,7 +148,7 @@ impl GameScriptExt for App {
|
|||
{
|
||||
let world = &mut self.world;
|
||||
provider.prepare_world(world);
|
||||
let mut providers = world.get_resource_mut::<ScriptApiProviders<T>>().unwrap();
|
||||
let mut providers = world.get_resource_or_default::<ScriptApiProviders<T>>();
|
||||
providers.add_provider(provider);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,19 +29,18 @@ use wrappers::{LuaHandleWrapper, LuaResHandleToComponent, LuaWrappedEventProxy};
|
|||
|
||||
use std::{any::TypeId, sync::Mutex};
|
||||
|
||||
use crate::ScriptBorrow;
|
||||
use lyra_ecs::World;
|
||||
use lyra_reflect::{FromType, Reflect, TypeRegistry};
|
||||
use crate::ScriptBorrow;
|
||||
|
||||
pub use mlua;
|
||||
|
||||
pub type LuaContext = Mutex<mlua::Lua>;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("mismatched type, expected `{expected}`, got: `{got}`")]
|
||||
TypeMismatch {
|
||||
expected: String,
|
||||
got: String,
|
||||
},
|
||||
TypeMismatch { expected: String, got: String },
|
||||
#[error("received nil value from Lua")]
|
||||
Nil,
|
||||
#[error("{0}")]
|
||||
|
@ -65,14 +64,17 @@ impl From<Error> for mlua::Error {
|
|||
fn from(value: Error) -> Self {
|
||||
match value {
|
||||
Error::Mlua(error) => error,
|
||||
_ => mlua::Error::external(value)
|
||||
_ => mlua::Error::external(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn type_mismatch(expected: &str, got: &str) -> Self {
|
||||
Self::TypeMismatch { expected: expected.into(), got: got.into() }
|
||||
Self::TypeMismatch {
|
||||
expected: expected.into(),
|
||||
got: got.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unimplemented(msg: &str) -> Self {
|
||||
|
@ -81,55 +83,55 @@ impl Error {
|
|||
}
|
||||
|
||||
/// Name of a Lua function that is used to Reflect the Userdata, but without a value.
|
||||
///
|
||||
///
|
||||
/// This is used for reflecting the userdata as an ECS Component or Resource. This **function**
|
||||
/// returns a [`ScriptBorrow`] with data as `None`.
|
||||
/// returns a [`ScriptBorrow`] with data as `None`.
|
||||
pub const FN_NAME_INTERNAL_REFLECT_TYPE: &str = "__lyra_internal_reflect_type";
|
||||
|
||||
/// Name of a Lua function that is used to Reflect the Userdata.
|
||||
///
|
||||
///
|
||||
/// This is used for reflecting the userdata as an ECS Component or Resource. This **method**
|
||||
/// returns a [`ScriptBorrow`] with data as `Some`. **Anything that calls this expects the
|
||||
/// method to return data**.
|
||||
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.
|
||||
///
|
||||
///
|
||||
/// The function must match the following definition: `fn(ScriptWorldPtr, Entity) -> LuaValue`.
|
||||
///
|
||||
///
|
||||
/// When `nil` is returned, its considered that the query will not result in anything for this
|
||||
/// [`View`], **no matter the entity**. When the query is used in a [`View`] and returns `nil`,
|
||||
/// 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.
|
||||
pub const FN_NAME_INTERNAL_ECS_QUERY_RESULT: &str = "__lyra_internal_ecs_query_result";
|
||||
|
||||
/// Name of a Lua function implemented for Userdata types that can be made into components.
|
||||
///
|
||||
///
|
||||
/// This is used for types that can be converted into components. When implementing this function,
|
||||
/// you must return a [`ScriptBorrow`] that contains the component for this userdata.
|
||||
/// You can return [`mlua::Value::Nil`] if for some reason the type could not be converted
|
||||
/// into a component.
|
||||
///
|
||||
///
|
||||
/// A good example of this is `LuaResHandle`. The resource handle is requested from the
|
||||
/// world, and could be a 3d model. The 3d model could then be manually wrapped as
|
||||
/// [`LuaModelComponent`] with its `new` function. But for quality of life, this internal
|
||||
/// function was created so that the userdata can be converted into its component
|
||||
/// type without having to wrap it.
|
||||
///
|
||||
///
|
||||
/// Without implementing this function:
|
||||
/// ```lua
|
||||
/// local cube = world:request_res("assets/cube-texture-embedded.gltf")
|
||||
/// local cube_comp = ModelComponent.new(cube) -- annoying to write
|
||||
///
|
||||
///
|
||||
/// local pos = Transform.from_translation(Vec3.new(0, 0, -8.0))
|
||||
/// world:spawn(pos, cube_comp)
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// With this function:
|
||||
/// ```lua
|
||||
/// local cube = world:request_res("assets/cube-texture-embedded.gltf")
|
||||
|
@ -157,7 +159,7 @@ pub trait RegisterLuaType {
|
|||
T: Clone + mlua::FromLua + mlua::IntoLua + LuaWrapper + 'static;
|
||||
|
||||
/// Registers a type that can be converted to and from lua and adds a lookup entry.
|
||||
///
|
||||
///
|
||||
/// This is a shortcut for `register_lua_convert` and `add_component_lookup_entry`.
|
||||
fn register_lua_convert_component<T>(&mut self, name: &str)
|
||||
where
|
||||
|
@ -190,7 +192,7 @@ pub trait RegisterLuaType {
|
|||
impl RegisterLuaType for World {
|
||||
fn register_lua_type<'a, T>(&mut self)
|
||||
where
|
||||
T: Reflect + LuaProxy + Clone + mlua::FromLua + mlua::UserData
|
||||
T: Reflect + LuaProxy + Clone + mlua::FromLua + mlua::UserData,
|
||||
{
|
||||
let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
|
||||
|
||||
|
@ -202,13 +204,13 @@ impl RegisterLuaType for World {
|
|||
|
||||
fn register_lua_wrapper<'a, W>(&mut self)
|
||||
where
|
||||
W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua + mlua::UserData
|
||||
W: Reflect + LuaProxy + LuaWrapper + Clone + mlua::FromLua + mlua::UserData,
|
||||
{
|
||||
let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
|
||||
|
||||
let reg_type = registry.get_type_or_default(W::wrapped_type_id());
|
||||
reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<W>());
|
||||
}
|
||||
}
|
||||
|
||||
fn register_lua_convert<T>(&mut self)
|
||||
where
|
||||
|
@ -223,7 +225,7 @@ impl RegisterLuaType for World {
|
|||
fn register_lua_convert_component<T>(&mut self, name: &str)
|
||||
where
|
||||
T: Clone + mlua::FromLua + mlua::IntoLua + LuaWrapper + 'static,
|
||||
T::Wrap: lyra_ecs::Component + Reflect
|
||||
T::Wrap: lyra_ecs::Component + Reflect,
|
||||
{
|
||||
self.register_lua_convert::<T>();
|
||||
self.add_component_lookup_entry::<T::Wrap>(name);
|
||||
|
@ -232,7 +234,7 @@ impl RegisterLuaType for World {
|
|||
fn register_asset_handle<T>(&mut self, name: &str)
|
||||
where
|
||||
T: LuaHandleWrapper + Reflect + LuaProxy,
|
||||
T::ResourceType: ResourceData
|
||||
T::ResourceType: ResourceData,
|
||||
{
|
||||
{
|
||||
let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
|
||||
|
@ -248,7 +250,7 @@ impl RegisterLuaType for World {
|
|||
fn add_lua_event<T>(&mut self, name: &str)
|
||||
where
|
||||
T: Reflect + LuaWrapper + mlua::FromLua + mlua::IntoLua + Send + Sync,
|
||||
T::Wrap: Clone + lyra_game::Event
|
||||
T::Wrap: Clone + lyra_game::Event,
|
||||
{
|
||||
{
|
||||
let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
|
||||
|
@ -262,19 +264,25 @@ impl RegisterLuaType for World {
|
|||
|
||||
fn add_component_lookup_entry<T>(&mut self, name: &str)
|
||||
where
|
||||
T: lyra_ecs::Component
|
||||
T: lyra_ecs::Component,
|
||||
{
|
||||
let mut lookup = self.get_resource_or_default::<TypeLookup>();
|
||||
lookup.comp_info_from_name.insert(name.into(), lyra_ecs::ComponentInfo::new::<T>());
|
||||
lookup.typeid_from_name.insert(name.into(), std::any::TypeId::of::<T>());
|
||||
lookup
|
||||
.comp_info_from_name
|
||||
.insert(name.into(), lyra_ecs::ComponentInfo::new::<T>());
|
||||
lookup
|
||||
.typeid_from_name
|
||||
.insert(name.into(), std::any::TypeId::of::<T>());
|
||||
}
|
||||
|
||||
fn add_lookup_entry<T>(&mut self, name: &str)
|
||||
where
|
||||
T: 'static
|
||||
T: 'static,
|
||||
{
|
||||
let mut lookup = self.get_resource_or_default::<TypeLookup>();
|
||||
lookup.typeid_from_name.insert(name.into(), std::any::TypeId::of::<T>());
|
||||
lookup
|
||||
.typeid_from_name
|
||||
.insert(name.into(), std::any::TypeId::of::<T>());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,22 +295,34 @@ impl mlua::FromLua for ScriptBorrow {
|
|||
}
|
||||
}
|
||||
|
||||
impl mlua::UserData for ScriptBorrow {
|
||||
|
||||
}
|
||||
impl mlua::UserData for ScriptBorrow {}
|
||||
|
||||
/// Helper function used for reflecting userdata as a ScriptBorrow
|
||||
pub fn reflect_user_data(ud: &mlua::AnyUserData) -> ScriptBorrow {
|
||||
let ud_name = ud.metatable().and_then(|mt| mt.get::<String>(mlua::MetaMethod::Type))
|
||||
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, ())
|
||||
.unwrap_or_else(|_| panic!("UserData of name '{}' does not implement internal reflect method properly", ud_name))
|
||||
.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))
|
||||
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))
|
||||
}
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"UserData of name '{}' does not implement internal reflect type function properly",
|
||||
ud_name
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use lyra_ecs::ResourceObject;
|
||||
use lyra_reflect::Reflect;
|
||||
use sprite::{
|
||||
LuaActiveAtlasAnimation, LuaAtlasAnimations, LuaAtlasSprite, LuaSprite, LuaSpriteAnimation,
|
||||
LuaTextureAtlasHandle, LuaTile, LuaTileMap, LuaTileMapPos,
|
||||
LuaActiveAtlasAnimation, LuaAtlasAnimations, LuaAtlasAnimationsHandle, LuaAtlasSprite,
|
||||
LuaSprite, LuaSpriteAnimation, LuaTextureAtlasHandle, LuaTile, LuaTileMap, LuaTileMapPos,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -47,6 +47,7 @@ impl ScriptApiProvider for LyraEcsApiProvider {
|
|||
world.register_lua_convert_component::<LuaTileMapPos>("TileMapPos");
|
||||
world.register_lua_convert_component::<LuaSpriteAnimation>("SpriteAnimation");
|
||||
world.register_lua_convert_component::<LuaAtlasAnimations>("AtlasAnimations");
|
||||
world.register_asset_handle::<LuaAtlasAnimationsHandle>("AtlasAnimationsHandle");
|
||||
world.register_lua_wrapper::<LuaActiveAtlasAnimation>();
|
||||
world.register_lua_convert_component::<LuaTile>("Tile");
|
||||
world.register_asset_handle::<LuaTextureAtlasHandle>("TextureAtlas");
|
||||
|
@ -175,7 +176,7 @@ where
|
|||
///
|
||||
/// This creates the reflection functions on a table specified in globals.
|
||||
/// The table name is set to `name`, which is also how the script will use the table.
|
||||
fn expose_comp_table_wrapper<T>(
|
||||
pub fn expose_comp_table_wrapper<T>(
|
||||
lua: &mlua::Lua,
|
||||
globals: &mlua::Table,
|
||||
name: &str,
|
||||
|
|
|
@ -32,6 +32,21 @@ pub mod wrap_uvec2_using_luavec2 {
|
|||
}
|
||||
}
|
||||
|
||||
pub mod wrap_ivec2_using_luavec2 {
|
||||
pub type FromType = LuaVec2;
|
||||
type BaseTy = lyra_game::math::IVec2;
|
||||
|
||||
use crate::lua::wrappers::LuaVec2;
|
||||
use mlua::IntoLua;
|
||||
|
||||
pub fn from_lua(_: &mlua::Lua, val: &FromType) -> mlua::Result<BaseTy> {
|
||||
Ok(val.0.as_ivec2())
|
||||
}
|
||||
|
||||
pub fn into_lua(lua: &mlua::Lua, val: &BaseTy) -> mlua::Result<mlua::Value> {
|
||||
LuaVec2(val.as_vec2()).into_lua(lua)
|
||||
}
|
||||
}
|
||||
/// Wrap a field of type `URect` from a `LuaRect`.
|
||||
pub mod wrap_urect_using_luarect {
|
||||
pub type FromType = LuaRect;
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||
|
||||
use lyra_game::math::URect;
|
||||
use lyra_game::sprite::{ActiveAtlasAnimation, AtlasAnimations, SpriteAnimation};
|
||||
use lyra_scripting_derive::{to_lua_convert, wrap_lua_struct};
|
||||
use lyra_scripting_derive::{lua_wrap_handle, to_lua_convert, wrap_lua_struct};
|
||||
use mlua::FromLuaMulti;
|
||||
|
||||
use super::LuaTextureAtlasHandle;
|
||||
|
@ -41,6 +41,28 @@ to_lua_convert!(
|
|||
}
|
||||
);
|
||||
|
||||
lua_wrap_handle!(
|
||||
AtlasAnimations,
|
||||
field_getters={
|
||||
// ResHandle<TextureAtlas>
|
||||
(atlas, {
|
||||
LuaTextureAtlasHandle(data.atlas.clone()).into_lua(lua)
|
||||
}),
|
||||
// HashMap<String, SpriteAnimation>
|
||||
(animations, {
|
||||
let anims = lua.create_table()?;
|
||||
for (key, val) in &data.animations {
|
||||
anims.raw_set(key.clone(), LuaSpriteAnimation(val.clone()))?;
|
||||
}
|
||||
|
||||
Ok(mlua::Value::Table(anims))
|
||||
}),
|
||||
(sprite_pivot, {
|
||||
super::wrap_pivot_enum::into_lua(lua, &data.sprite_pivot)
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
// TODO: create AtlasAnimations::from_animations and `get_active` in Lua
|
||||
to_lua_convert!(
|
||||
// Struct that is being wrapped
|
||||
|
|
Loading…
Add table
Reference in a new issue