From 69d43d8beada8c292e1f766357a0f98c7df9e9a5 Mon Sep 17 00:00:00 2001 From: Quaternions Date: Sat, 5 Oct 2024 12:35:38 -0700 Subject: [PATCH] class function database + update mlua to beta version to avoid unsafe --- Cargo.lock | 117 ++++++++++++++- Cargo.toml | 3 +- src/context.rs | 8 +- src/runner/cframe.rs | 6 +- src/runner/color3.rs | 6 +- src/runner/enum.rs | 22 +-- src/runner/instance.rs | 326 ++++++++++++++++++++++------------------- src/runner/macros.rs | 12 +- src/runner/runner.rs | 8 +- src/runner/vector3.rs | 6 +- 10 files changed, 321 insertions(+), 193 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bfee02f..260d89e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + [[package]] name = "blake3" version = "1.5.4" @@ -121,6 +127,16 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "luau0-src" version = "0.10.3+luau640" @@ -138,15 +154,15 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mlua" -version = "0.9.9" +version = "0.10.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d111deb18a9c9bd33e1541309f4742523bfab01d276bfa9a27519f6de9c11dc7" +checksum = "200dd4c8e5f81416d43a023b9921c3cbf01d828094b23a90b26826c3840ba4f3" dependencies = [ "bstr", "libloading", "mlua-sys", "num-traits", - "once_cell", + "parking_lot", "rustc-hash", ] @@ -172,10 +188,27 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.19.0" +name = "parking_lot" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] [[package]] name = "paste" @@ -183,6 +216,48 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pkg-config" version = "0.3.30" @@ -286,7 +361,7 @@ source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/" checksum = "d7a390c44034fa448c53bd0983dfc2d70d8d6b2f65be4f164d4bec8b6a2a2d09" dependencies = [ "base64", - "bitflags", + "bitflags 1.3.2", "blake3", "lazy_static", "rand", @@ -294,6 +369,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "rmp" version = "0.8.14" @@ -322,6 +406,7 @@ version = "0.4.2" dependencies = [ "glam", "mlua", + "phf", "rbx_dom_weak", "rbx_reflection", "rbx_reflection_database", @@ -334,6 +419,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "serde" version = "1.0.210" @@ -360,6 +451,18 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "syn" version = "2.0.77" diff --git a/Cargo.toml b/Cargo.toml index 107b70a..0172374 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,8 @@ authors = ["Rhys Lloyd "] [dependencies] glam = "0.29.0" -mlua = { version = "0.9.9", features = ["luau"] } +mlua = { version = "0.10.0-beta", features = ["luau"] } +phf = { version = "0.11.2", features = ["macros"] } rbx_dom_weak = { version = "2.7.0", registry = "strafesnet" } rbx_reflection = { version = "4.7.0", registry = "strafesnet" } rbx_reflection_database = { version = "0.2.10", registry = "strafesnet" } diff --git a/src/context.rs b/src/context.rs index 91562bd..c86320c 100644 --- a/src/context.rs +++ b/src/context.rs @@ -19,7 +19,7 @@ impl Context{ pub const fn new(dom:WeakDom)->Self{ Self{dom} } - pub fn script_singleton(source:String)->(Context,crate::runner::instance::Script,Services){ + pub fn script_singleton(source:String)->(Context,crate::runner::instance::Instance,Services){ let script=InstanceBuilder::new("Script") .with_property("Source",rbx_types::Variant::String(source)); let script_ref=script.referent(); @@ -28,7 +28,7 @@ impl Context{ .with_child(script) )); let services=context.convert_into_place(); - (context,crate::runner::instance::Script::new(script_ref),services) + (context,crate::runner::instance::Instance::new(script_ref),services) } pub fn from_ref(dom:&WeakDom)->&Context{ unsafe{&*(dom as *const WeakDom as *const Context)} @@ -42,8 +42,8 @@ impl Context{ class_is_a(instance.class.as_ref(),superclass) ).map(|instance|instance.referent()) } - pub fn scripts(&self)->Vec{ - self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::Script::new).collect() + pub fn scripts(&self)->Vec{ + self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::Instance::new).collect() } pub fn find_services(&self)->Option{ diff --git a/src/runner/cframe.rs b/src/runner/cframe.rs index c1f60fa..e9f77c2 100644 --- a/src/runner/cframe.rs +++ b/src/runner/cframe.rs @@ -59,7 +59,7 @@ impl From for CFrame{ } } -pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table<'_>)->Result<(),mlua::Error>{ +pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{ let cframe_table=lua.create_table()?; //CFrame.new @@ -126,12 +126,12 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table<'_>)->Result<(),mlua::Err } impl mlua::UserData for CFrame{ - fn add_fields<'lua,F:mlua::UserDataFields<'lua,Self>>(fields:&mut F){ + fn add_fields>(fields:&mut F){ //CFrame.p fields.add_field_method_get("p",|_,this|Ok(Vector3(this.0.translation))); } - fn add_methods<'lua,M:mlua::UserDataMethods<'lua,Self>>(methods:&mut M){ + fn add_methods>(methods:&mut M){ methods.add_method("components",|_,this,()|Ok(( this.0.translation.x, this.0.translation.y, diff --git a/src/runner/color3.rs b/src/runner/color3.rs index 7952a89..93b8c68 100644 --- a/src/runner/color3.rs +++ b/src/runner/color3.rs @@ -15,7 +15,7 @@ impl Into for Color3{ } } -pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table<'_>)->Result<(),mlua::Error>{ +pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{ let color3_table=lua.create_table()?; color3_table.raw_set("new", @@ -38,7 +38,7 @@ fn lerp(lhs:f32,rhs:f32,t:f32)->f32{ } impl mlua::UserData for Color3{ - fn add_fields<'lua,F:mlua::UserDataFields<'lua,Self>>(fields:&mut F){ + fn add_fields>(fields:&mut F){ fields.add_field_method_get("r",|_,this|Ok(this.r)); fields.add_field_method_set("r",|_,this,val|{ this.r=val; @@ -55,7 +55,7 @@ impl mlua::UserData for Color3{ Ok(()) }); } - fn add_methods<'lua,M:mlua::UserDataMethods<'lua,Self>>(methods:&mut M){ + fn add_methods>(methods:&mut M){ methods.add_method("Lerp",|_,this,(other,t):(Self,f32)| Ok(Color3::new( lerp(this.r,other.r,t), diff --git a/src/runner/enum.rs b/src/runner/enum.rs index 27f2df0..0b6bd47 100644 --- a/src/runner/enum.rs +++ b/src/runner/enum.rs @@ -2,7 +2,9 @@ use mlua::IntoLua; #[derive(Clone,Copy)] pub struct Enum(u32); +#[derive(Clone,Copy)] pub struct EnumItems; +#[derive(Clone,Copy)] pub struct EnumItem<'a>{ ed:&'a rbx_reflection::EnumDescriptor<'a>, } @@ -19,16 +21,16 @@ impl<'a> EnumItem<'a>{ } } -pub fn set_globals(_lua:&mlua::Lua,globals:&mlua::Table<'_>)->Result<(),mlua::Error>{ +pub fn set_globals(_lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{ globals.set("Enum",EnumItems) } impl mlua::UserData for EnumItem<'_>{ - fn add_fields<'lua,F:mlua::UserDataFields<'lua,Self>>(_fields:&mut F){ + fn add_fields>(_fields:&mut F){ } - fn add_methods<'lua,M:mlua::UserDataMethods<'lua,Self>>(methods:&mut M){ - methods.add_meta_function(mlua::MetaMethod::Index,|lua,(this,val):(EnumItem<'lua>,mlua::String)|{ - match this.ed.items.get(val.to_str()?){ + fn add_methods>(methods:&mut M){ + methods.add_meta_function(mlua::MetaMethod::Index,|lua,(this,val):(EnumItem<'_>,mlua::String)|{ + match this.ed.items.get(&*val.to_str()?){ Some(&id)=>Enum(id).into_lua(lua), None=>mlua::Value::Nil.into_lua(lua), } @@ -38,12 +40,12 @@ impl mlua::UserData for EnumItem<'_>{ type_from_lua_userdata_lua_lifetime!(EnumItem); impl mlua::UserData for EnumItems{ - fn add_fields<'lua,F:mlua::UserDataFields<'lua,Self>>(_fields:&mut F){ + fn add_fields>(_fields:&mut F){ } - fn add_methods<'lua,M:mlua::UserDataMethods<'lua,Self>>(methods:&mut M){ + fn add_methods>(methods:&mut M){ methods.add_meta_function(mlua::MetaMethod::Index,|lua,(_,val):(Self,mlua::String)|{ let db=rbx_reflection_database::get(); - match db.enums.get(val.to_str()?){ + match db.enums.get(&*val.to_str()?){ Some(ed)=>EnumItem::new(ed).into_lua(lua), None=>mlua::Value::Nil.into_lua(lua), } @@ -53,9 +55,9 @@ impl mlua::UserData for EnumItems{ type_from_lua_userdata!(EnumItems); impl mlua::UserData for Enum{ - fn add_fields<'lua,F:mlua::UserDataFields<'lua,Self>>(_fields:&mut F){ + fn add_fields>(_fields:&mut F){ } - fn add_methods<'lua,M:mlua::UserDataMethods<'lua,Self>>(_methods:&mut M){ + fn add_methods>(_methods:&mut M){ } } type_from_lua_userdata!(Enum); diff --git a/src/runner/instance.rs b/src/runner/instance.rs index 06355f9..0668e38 100644 --- a/src/runner/instance.rs +++ b/src/runner/instance.rs @@ -1,16 +1,21 @@ -use mlua::IntoLua; +use std::collections::{hash_map::Entry,HashMap}; + +use mlua::{FromLuaMulti,IntoLua,IntoLuaMulti}; use rbx_types::Ref; use rbx_dom_weak::{InstanceBuilder,WeakDom}; use super::vector3::Vector3; -pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table<'_>)->Result<(),mlua::Error>{ +pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{ + //class functions store + lua.set_app_data(ClassFunctions::default()); + let instance_table=lua.create_table()?; //Instance.new instance_table.raw_set("new", lua.create_function(|lua,(class_name,parent):(mlua::String,Option)|{ - let class_name_str=class_name.to_str()?; + let class_name_str=&*class_name.to_str()?; let parent=parent.ok_or(mlua::Error::runtime("Nil Parent not yet supported"))?; dom_mut(lua,|dom|{ //TODO: Nil instances @@ -29,6 +34,10 @@ fn dom_mut(lua:&mlua::Lua,mut f:impl FnMut(&mut WeakDom)->mlua::Result)->m let mut dom=lua.app_data_mut::<&'static mut WeakDom>().ok_or(mlua::Error::runtime("DataModel missing"))?; f(&mut *dom) } +fn class_functions_mut(lua:&mlua::Lua,mut f:impl FnMut(&mut ClassFunctions)->mlua::Result)->mlua::Result{ + let mut cf=lua.app_data_mut::().ok_or(mlua::Error::runtime("ClassFunctions missing"))?; + f(&mut *cf) +} fn coerce_float32(value:&mlua::Value)->Option{ match value{ @@ -48,6 +57,17 @@ fn get_full_name(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->S } full_name } +//helper function for script +pub fn get_name_source(lua:&mlua::Lua,script:Instance)->Result<(String,String),mlua::Error>{ + dom_mut(lua,|dom|{ + let instance=script.get(dom)?; + let source=match instance.properties.get("Source"){ + Some(rbx_dom_weak::types::Variant::String(s))=>s.clone(), + _=>Err(mlua::Error::external("Missing script.Source"))?, + }; + Ok((get_full_name(dom,instance),source)) + }) +} pub fn find_first_child<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str)->Option<&'a rbx_dom_weak::Instance>{ instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.name==name) @@ -63,63 +83,22 @@ pub fn find_first_descendant_of_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance dom.descendants_of(instance.referent()).find(|&inst|inst.class==class) } -//workaround until I have an enum of classes -struct Dereferent(Ref); -impl mlua::UserData for Dereferent{} -type_from_lua_userdata!(Dereferent); -impl Referent for Dereferent{ - fn referent(&self)->Ref{ - self.0 +#[derive(Clone,Copy)] +pub struct Instance{ + referent:Ref, +} +impl Instance{ + pub const fn new(referent:Ref)->Self{ + Self{referent} + } + pub fn get<'a>(&self,dom:&'a WeakDom)->mlua::Result<&'a rbx_dom_weak::Instance>{ + dom.get_by_ref(self.referent).ok_or(mlua::Error::runtime("Instance missing")) + } + pub fn get_mut<'a>(&self,dom:&'a mut WeakDom)->mlua::Result<&'a mut rbx_dom_weak::Instance>{ + dom.get_by_ref_mut(self.referent).ok_or(mlua::Error::runtime("Instance missing")) } } - -trait Referent{ - fn referent(&self)->Ref; - fn get<'a>(&self,dom:&'a WeakDom)->mlua::Result<&'a rbx_dom_weak::Instance>{ - dom.get_by_ref(self.referent()).ok_or(mlua::Error::runtime("Instance missing")) - } - fn get_mut<'a>(&self,dom:&'a mut WeakDom)->mlua::Result<&'a mut rbx_dom_weak::Instance>{ - dom.get_by_ref_mut(self.referent()).ok_or(mlua::Error::runtime("Instance missing")) - } -} - -macro_rules! class{ - ($class:ident)=>{ - pub struct $class{ - referent:Ref, - } - impl $class{ - pub const fn new(referent:Ref)->Self{ - Self{referent} - } - } - impl Referent for $class{ - fn referent(&self)->Ref{ - self.referent - } - } - type_from_lua_userdata!($class); - }; -} -macro_rules! class_composition{ - ($class:ident,($($superclass:ident),*))=>{ - impl mlua::UserData for $class{ - fn add_fields<'lua,F:mlua::UserDataFields<'lua,Self>>(fields:&mut F){ - fields.add_field_method_get("Referent",|_,this|{ - Ok(Dereferent(this.referent())) - }); - $( - $superclass::composition_add_fields(fields); - )* - } - fn add_methods<'lua,M:mlua::UserDataMethods<'lua,Self>>(methods:&mut M){ - $( - $superclass::composition_add_methods(methods); - )* - } - } - }; -} +type_from_lua_userdata!(Instance); //TODO: update rbx_reflection and use dom.superclasses_iter pub struct SuperClassIter<'a> { @@ -140,21 +119,18 @@ impl<'a> Iterator for SuperClassIter<'a> { } } -class!(Instance); -class_composition!(Instance,(Instance)); - -impl Instance{ - fn composition_add_fields<'lua,T:Referent,F:mlua::UserDataFields<'lua,T>>(fields:&mut F){ +impl mlua::UserData for Instance{ + fn add_fields>(fields:&mut F){ fields.add_field_method_get("Parent",|lua,this|{ dom_mut(lua,|dom|{ let instance=this.get(dom)?; Ok(Instance::new(instance.parent())) }) }); - fields.add_field_method_set("Parent",|lua,this,val:mlua::AnyUserData|{ - let Dereferent(referent)=mlua::AnyUserDataExt::get(&val,"Referent")?; + fields.add_field_method_set("Parent",|lua,this,val:Option|{ + let parent=val.ok_or(mlua::Error::runtime("Nil Parent not yet supported"))?; dom_mut(lua,|dom|{ - dom.transfer_within(this.referent(),referent); + dom.transfer_within(this.referent,parent.referent); Ok(()) }) }); @@ -179,7 +155,7 @@ impl Instance{ }) }); } - fn composition_add_methods<'lua,T:Referent,M:mlua::UserDataMethods<'lua,T>>(methods:&mut M){ + fn add_methods>(methods:&mut M){ methods.add_method("GetChildren",|lua,this,_:()| dom_mut(lua,|dom|{ let instance=this.get(dom)?; @@ -192,8 +168,8 @@ impl Instance{ Ok(children) }) ); - let ffc=|lua,this:&T,(name,search_descendants):(mlua::String,Option)|{ - let name_str=name.to_str()?; + fn ffc(lua:&mlua::Lua,this:&Instance,(name,search_descendants):(mlua::String,Option))->mlua::Result>{ + let name_str=&*name.to_str()?; dom_mut(lua,|dom|{ let instance=this.get(dom)?; Ok( @@ -206,11 +182,11 @@ impl Instance{ ) ) }) - }; + } methods.add_method("FindFirstChild",ffc); methods.add_method("WaitForChild",ffc); methods.add_method("FindFirstChildOfClass",|lua,this,(class,search_descendants):(mlua::String,Option)|{ - let class_str=class.to_str()?; + let class_str=&*class.to_str()?; dom_mut(lua,|dom|{ Ok( match search_descendants.unwrap_or(false){ @@ -226,7 +202,7 @@ impl Instance{ methods.add_method("GetDescendants",|lua,this,_:()| dom_mut(lua,|dom|{ let children:Vec<_>=dom - .descendants_of(this.referent()) + .descendants_of(this.referent) .map(|instance| Instance::new(instance.referent()) ) @@ -237,33 +213,58 @@ impl Instance{ methods.add_method("IsA",|lua,this,classname:mlua::String| dom_mut(lua,|dom|{ let instance=this.get(dom)?; - Ok(crate::context::class_is_a(instance.class.as_str(),classname.to_str()?)) + Ok(crate::context::class_is_a(instance.class.as_str(),&*classname.to_str()?)) }) ); methods.add_method("Destroy",|lua,this,()| dom_mut(lua,|dom|{ - dom.destroy(this.referent()); + dom.destroy(this.referent); Ok(()) }) ); - methods.add_meta_function(mlua::MetaMethod::Index,|lua,(this,index):(mlua::AnyUserData,mlua::String)|{ - let index_str=index.to_str()?; - let dereferent:Dereferent=mlua::AnyUserDataExt::get(&this,"Referent")?; + methods.add_meta_function(mlua::MetaMethod::ToString,|lua,this:Instance|{ dom_mut(lua,|dom|{ - let instance=dereferent.get(dom)?; + let instance=this.get(dom)?; + Ok(instance.name.clone()) + }) + }); + methods.add_meta_function(mlua::MetaMethod::Index,|lua,(this,index):(Instance,mlua::String)|{ + let index_str=&*index.to_str()?; + dom_mut(lua,|dom|{ + let instance=this.get(dom)?; + //println!("__index t={} i={index:?}",instance.name); + let db=rbx_reflection_database::get(); + let class=db.classes.get(instance.class.as_str()).ok_or(mlua::Error::runtime("Class missing"))?; //Find existing property match instance.properties.get(index_str) //Find default value - .or_else(||{ - let db=rbx_reflection_database::get(); - db.classes.get(instance.class.as_str()).and_then(|cd| - db.find_default_property(cd,index_str) - ) - }) + .or_else(||db.find_default_property(class,index_str)) { + Some(&rbx_types::Variant::Int32(val))=>return Ok(val.into_lua(lua)), + Some(&rbx_types::Variant::Int64(val))=>return Ok(val.into_lua(lua)), + Some(&rbx_types::Variant::Float32(val))=>return Ok(val.into_lua(lua)), + Some(&rbx_types::Variant::Float64(val))=>return Ok(val.into_lua(lua)), Some(&rbx_types::Variant::CFrame(cf))=>return Ok(Into::::into(cf).into_lua(lua)), Some(&rbx_types::Variant::Vector3(v))=>return Ok(Into::::into(v).into_lua(lua)), - _=>(), + other=>println!("instance.properties.get(i)={other:?}"), + } + //find a function with a matching name + if let Some(function)=class_functions_mut(lua,|cf|{ + let mut iter=SuperClassIter{ + database:db, + descriptor:Some(class), + }; + Ok(loop{ + match iter.next(){ + Some(class)=>match cf.get_or_create_class_function(lua,&class.name,index_str)?{ + Some(function)=>break Some(function), + None=>(), + }, + None=>break None, + } + }) + })?{ + return Ok(function.into_lua(lua)); } //find a child with a matching name Ok( @@ -273,12 +274,11 @@ impl Instance{ ) }) }); - methods.add_meta_function(mlua::MetaMethod::NewIndex,|lua,(this,index,value):(mlua::AnyUserData,mlua::String,mlua::Value)|{ - let dereferent:Dereferent=mlua::AnyUserDataExt::get(&this,"Referent")?; + methods.add_meta_function(mlua::MetaMethod::NewIndex,|lua,(this,index,value):(Instance,mlua::String,mlua::Value)|{ dom_mut(lua,|dom|{ - let instance=dereferent.get_mut(dom)?; + let instance=this.get_mut(dom)?; //println!("__newindex t={} i={index:?} v={value:?}",instance.name); - let index_str=index.to_str()?; + let index_str=&*index.to_str()?; let db=rbx_reflection_database::get(); let class=db.classes.get(instance.class.as_str()).ok_or(mlua::Error::runtime("Class missing"))?; let mut iter=SuperClassIter{ @@ -301,7 +301,7 @@ impl Instance{ &mlua::Value::Number(num)=>Ok(rbx_types::Enum::from_u32(num as u32)), mlua::Value::String(s)=>{ let e=db.enums.get(enum_name).ok_or(mlua::Error::runtime("Database DataType Enum name does not exist"))?; - Ok(rbx_types::Enum::from_u32(*e.items.get(s.to_str()?).ok_or(mlua::Error::runtime("Invalid enum item"))?)) + Ok(rbx_types::Enum::from_u32(*e.items.get(&*s.to_str()?).ok_or(mlua::Error::runtime("Invalid enum item"))?)) }, mlua::Value::UserData(any_user_data)=>{ let e:super::r#enum::Enum=any_user_data.take()?; @@ -331,81 +331,103 @@ impl Instance{ } } -class!(DataModel); -class_composition!(DataModel,(Instance,DataModel)); -impl DataModel{ - fn composition_add_fields<'lua,T:Referent,F:mlua::UserDataFields<'lua,T>>(fields:&mut F){ - fields.add_field_method_get("workspace",|lua,this|{ +/// A class function definition shorthand. +macro_rules! cf{ + ($f:expr)=>{ + |lua,this|$f(lua,FromLuaMulti::from_lua_multi(this,lua)?)?.into_lua_multi(lua) + }; +} +type FPointer=fn(&mlua::Lua,mlua::MultiValue)->mlua::Result; +/// A double hash map of function pointers. +/// The class tree is walked by the Instance.__index metamethod to find available class methods. +static CLASS_FUNCTION_DATABASE:phf::Map<&str,phf::Map<&str,FPointer>>=phf::phf_map!{ + "DataModel"=>phf::phf_map!{ + "GetService"=>cf!(|lua,(_this,service):(Instance,mlua::String)|{ dom_mut(lua,|dom|{ - Ok(find_first_child_of_class(dom,this.get(dom)?,"Workspace") - .map(|inst|Workspace::new(inst.referent())) - ) - }) - }); - fields.add_field_method_get("PlaceId",|lua,this|{ - Ok(mlua::Value::Integer(0)) - }); - } - fn composition_add_methods<'lua,T,M:mlua::UserDataMethods<'lua,T>>(methods:&mut M){ - methods.add_method("GetService",|lua,this,service:String| - dom_mut(lua,|dom|{ - match service.as_str(){ + //dom.root_ref()==this.referent ? + match &*service.to_str()?{ "Lighting"=>{ let referent=find_first_child_of_class(dom,dom.root(),"Lighting") .map(|instance|instance.referent()) .unwrap_or_else(|| dom.insert(dom.root_ref(),InstanceBuilder::new("Lighting")) ); - Lighting::new(referent).into_lua(lua) + Ok(Instance::new(referent)) }, - other=>Err::(mlua::Error::runtime(format!("Service '{other}' not supported"))), + other=>Err::(mlua::Error::runtime(format!("Service '{other}' not supported"))), } }) - ); - } -} - -class!(Workspace); -class_composition!(Workspace,(Instance,Workspace)); -impl Workspace{ - fn composition_add_fields<'lua,T:Referent,F:mlua::UserDataFields<'lua,T>>(fields:&mut F){ - fields.add_field_method_get("Terrain",|lua,this|{ + }), + "workspace"=>cf!(|lua,this:Instance|{ dom_mut(lua,|dom|{ - Ok(find_first_child_of_class(dom,this.get(dom)?,"Terrain") - .map(|inst|Terrain::new(inst.referent())) + Ok(find_first_child_of_class(dom,this.get(dom)?,"Workspace") + .map(|inst|Instance::new(inst.referent())) ) }) - }); - } - fn composition_add_methods<'lua,T,M:mlua::UserDataMethods<'lua,T>>(_methods:&mut M){ - } -} -class!(Lighting); -class_composition!(Lighting,(Instance)); + }), + }, + "Workspace"=>phf::phf_map!{ + "Terrain"=>cf!(|lua,this:Instance|{ + dom_mut(lua,|dom|{ + Ok(find_first_child_of_class(dom,this.get(dom)?,"Terrain") + .map(|inst|Instance::new(inst.referent())) + ) + }) + }), + }, + "Terrain"=>phf::phf_map!{ + "FillBlock"=>cf!(|_lua,_:(Instance,super::cframe::CFrame,Vector3,super::r#enum::Enum)|mlua::Result::Ok(())) + }, +}; -class!(Script); -class_composition!(Script,(Instance)); -impl Script{ - pub fn get_name_source(&self,lua:&mlua::Lua)->Result<(String,String),mlua::Error>{ - dom_mut(lua,|dom|{ - let instance=self.get(dom)?; - let source=match instance.properties.get("Source"){ - Some(rbx_dom_weak::types::Variant::String(s))=>s.clone(), - _=>Err(mlua::Error::external("Missing script.Source"))?, - }; - Ok((get_full_name(dom,instance),source)) - }) - } -} - -class!(Terrain); -class_composition!(Terrain,(Instance,Terrain)); -impl Terrain{ - fn composition_add_fields<'lua,T:Referent,F:mlua::UserDataFields<'lua,T>>(fields:&mut F){ - } - fn composition_add_methods<'lua,T,M:mlua::UserDataMethods<'lua,T>>(methods:&mut M){ - methods.add_method("FillBlock",|lua,this,_:(super::cframe::CFrame,Vector3,super::r#enum::Enum)| - Ok(())//Ok(mlua::Value::Nil) - ) +/// A store of created functions for each Roblox class. +/// Functions are created the first time they are accessed and stored in this data structure. +#[derive(Default)] +struct ClassFunctions{ + classes:HashMap<&'static str,//ClassName + HashMap<&'static str,//Function name + mlua::Function + > + > +} +impl ClassFunctions{ + /// Someone please rewrite this, all it's supposed to do is + /// return self.classes[class][index] or create the function in the hashmap and then return it + fn get_or_create_class_function(&mut self,lua:&mlua::Lua,class:&str,index:&str)->mlua::Result>{ + // Use get_entry to get the &'static str key of the database + // and use it as a key for the classes hashmap + let f=match CLASS_FUNCTION_DATABASE.get_entry(class){ + Some((&static_class_str,class_functions))=>{ + match self.classes.entry(static_class_str){ + Entry::Occupied(mut occupied_entry)=>{ + match class_functions.get_entry(index){ + Some((&static_index_str,function_pointer))=>{ + match occupied_entry.get_mut().entry(static_index_str){ + Entry::Occupied(occupied_entry)=>{ + Some(occupied_entry.get().clone()) + }, + Entry::Vacant(vacant_entry)=>{ + Some(vacant_entry.insert(lua.create_function(function_pointer)?).clone()) + }, + } + }, + None=>None, + } + }, + Entry::Vacant(vacant_entry)=>{ + match class_functions.get_entry(index){ + Some((&static_index_str,function_pointer))=>{ + let mut h=HashMap::new(); + h.entry(static_index_str).or_insert(lua.create_function(function_pointer)?); + vacant_entry.insert(h).get(static_index_str).map(|f|f.clone()) + }, + None=>None, + } + }, + } + }, + None=>None, + }; + Ok(f) } } diff --git a/src/runner/macros.rs b/src/runner/macros.rs index 8121af8..180c020 100644 --- a/src/runner/macros.rs +++ b/src/runner/macros.rs @@ -1,9 +1,9 @@ macro_rules! type_from_lua_userdata{ ($asd:ident)=>{ - impl<'lua> mlua::FromLua<'lua> for $asd{ - fn from_lua(value:mlua::Value<'lua>,_lua:&'lua mlua::Lua)->Result{ + impl mlua::FromLua for $asd{ + fn from_lua(value:mlua::Value,_lua:&mlua::Lua)->Result{ match value{ - mlua::Value::UserData(ud)=>ud.take(), + mlua::Value::UserData(ud)=>Ok(*ud.borrow::()?), other=>Err(mlua::Error::runtime(format!("Expected {} got {:?}",stringify!($asd),other))), } } @@ -12,10 +12,10 @@ macro_rules! type_from_lua_userdata{ } macro_rules! type_from_lua_userdata_lua_lifetime{ ($asd:ident)=>{ - impl<'lua> mlua::FromLua<'lua> for $asd<'lua>{ - fn from_lua(value:mlua::Value<'lua>,_lua:&'lua mlua::Lua)->Result{ + impl mlua::FromLua for $asd<'static>{ + fn from_lua(value:mlua::Value,_lua:&mlua::Lua)->Result{ match value{ - mlua::Value::UserData(ud)=>ud.take(), + mlua::Value::UserData(ud)=>Ok(*ud.borrow::()?), other=>Err(mlua::Error::runtime(format!("Expected {} got {:?}",stringify!($asd),other))), } } diff --git a/src/runner/runner.rs b/src/runner/runner.rs index b603aa0..48dee06 100644 --- a/src/runner/runner.rs +++ b/src/runner/runner.rs @@ -53,8 +53,8 @@ impl Runner{ pub fn runnable_context_with_services<'a>(self,context:&'a mut Context,services:&crate::context::Services)->Result,Error>{ { let globals=self.lua.globals(); - globals.set("game",super::instance::DataModel::new(services.game)).map_err(Error::RustLua)?; - globals.set("workspace",super::instance::Workspace::new(services.workspace)).map_err(Error::RustLua)?; + globals.set("game",super::instance::Instance::new(services.game)).map_err(Error::RustLua)?; + globals.set("workspace",super::instance::Instance::new(services.workspace)).map_err(Error::RustLua)?; } //this makes set_app_data shut up about the lifetime self.lua.set_app_data::<&'static mut rbx_dom_weak::WeakDom>(unsafe{core::mem::transmute(&mut context.dom)}); @@ -77,8 +77,8 @@ impl Runnable<'_>{ lua:self.lua, } } - pub fn run_script(&self,script:super::instance::Script)->Result<(),Error>{ - let (name,source)=script.get_name_source(&self.lua).map_err(Error::RustLua)?; + pub fn run_script(&self,script:super::instance::Instance)->Result<(),Error>{ + let (name,source)=super::instance::get_name_source(&self.lua,script).map_err(Error::RustLua)?; self.lua.globals().set("script",script).map_err(Error::RustLua)?; self.lua.load(source.as_str()) .set_name(name) diff --git a/src/runner/vector3.rs b/src/runner/vector3.rs index 7b33eec..2e543de 100644 --- a/src/runner/vector3.rs +++ b/src/runner/vector3.rs @@ -7,7 +7,7 @@ impl Vector3{ } } -pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table<'_>)->Result<(),mlua::Error>{ +pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{ let vector3_table=lua.create_table()?; //Vector3.new @@ -35,7 +35,7 @@ impl From for Vector3{ } impl mlua::UserData for Vector3{ - fn add_fields<'lua,F: mlua::UserDataFields<'lua,Self>>(fields: &mut F){ + fn add_fields>(fields:&mut F){ fields.add_field_method_get("magnitude",|_,this|Ok(this.0.length())); fields.add_field_method_get("x",|_,this|Ok(this.0.x)); fields.add_field_method_set("x",|_,this,val|{ @@ -54,7 +54,7 @@ impl mlua::UserData for Vector3{ }); } - fn add_methods<'lua,M: mlua::UserDataMethods<'lua,Self>>(methods:&mut M){ + fn add_methods>(methods:&mut M){ //methods.add_method("area",|_,this,()| Ok(this.length * this.width)); methods.add_meta_function(mlua::MetaMethod::Add,|_,(this,val):(Self,Self)|Ok(Self(this.0+val.0)));