|
|
|
@@ -1,6 +1,6 @@
|
|
|
|
use std::collections::{hash_map::Entry,HashMap};
|
|
|
|
use std::collections::{hash_map::Entry,HashMap};
|
|
|
|
|
|
|
|
|
|
|
|
use mlua::{FromLua,FromLuaMulti,IntoLua,IntoLuaMulti};
|
|
|
|
use mlua::{FromLuaMulti,IntoLua,IntoLuaMulti};
|
|
|
|
use rbx_types::Ref;
|
|
|
|
use rbx_types::Ref;
|
|
|
|
use rbx_dom_weak::{InstanceBuilder,WeakDom};
|
|
|
|
use rbx_dom_weak::{InstanceBuilder,WeakDom};
|
|
|
|
|
|
|
|
|
|
|
|
@@ -10,6 +10,8 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
|
|
|
//class functions store
|
|
|
|
//class functions store
|
|
|
|
lua.set_app_data(ClassFunctions::default());
|
|
|
|
lua.set_app_data(ClassFunctions::default());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lua.register_userdata_type(<Instance as mlua::UserData>::register)?;
|
|
|
|
|
|
|
|
|
|
|
|
let instance_table=lua.create_table()?;
|
|
|
|
let instance_table=lua.create_table()?;
|
|
|
|
|
|
|
|
|
|
|
|
//Instance.new
|
|
|
|
//Instance.new
|
|
|
|
@@ -237,29 +239,16 @@ impl mlua::UserData for Instance{
|
|
|
|
let class=db.classes.get(instance.class.as_str()).ok_or(mlua::Error::runtime("Class missing"))?;
|
|
|
|
let class=db.classes.get(instance.class.as_str()).ok_or(mlua::Error::runtime("Class missing"))?;
|
|
|
|
//Find existing property
|
|
|
|
//Find existing property
|
|
|
|
match instance.properties.get(index_str)
|
|
|
|
match instance.properties.get(index_str)
|
|
|
|
.cloned()
|
|
|
|
|
|
|
|
//Find default value
|
|
|
|
//Find default value
|
|
|
|
.or_else(||db.find_default_property(class,index_str).cloned())
|
|
|
|
.or_else(||db.find_default_property(class,index_str))
|
|
|
|
//Find virtual property
|
|
|
|
|
|
|
|
.or_else(||{
|
|
|
|
|
|
|
|
SuperClassIter{
|
|
|
|
|
|
|
|
database:db,
|
|
|
|
|
|
|
|
descriptor:Some(class),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
.find_map(|class|
|
|
|
|
|
|
|
|
find_virtual_property(&instance.properties,class,index_str)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Some(rbx_types::Variant::Int32(val))=>return val.into_lua(lua),
|
|
|
|
Some(&rbx_types::Variant::Int32(val))=>return Ok(val.into_lua(lua)),
|
|
|
|
Some(rbx_types::Variant::Int64(val))=>return val.into_lua(lua),
|
|
|
|
Some(&rbx_types::Variant::Int64(val))=>return Ok(val.into_lua(lua)),
|
|
|
|
Some(rbx_types::Variant::Float32(val))=>return val.into_lua(lua),
|
|
|
|
Some(&rbx_types::Variant::Float32(val))=>return Ok(val.into_lua(lua)),
|
|
|
|
Some(rbx_types::Variant::Float64(val))=>return val.into_lua(lua),
|
|
|
|
Some(&rbx_types::Variant::Float64(val))=>return Ok(val.into_lua(lua)),
|
|
|
|
Some(rbx_types::Variant::Ref(val))=>return Instance::new(val).into_lua(lua),
|
|
|
|
Some(&rbx_types::Variant::CFrame(cf))=>return Ok(Into::<super::cframe::CFrame>::into(cf).into_lua(lua)),
|
|
|
|
Some(rbx_types::Variant::CFrame(cf))=>return Into::<super::cframe::CFrame>::into(cf).into_lua(lua),
|
|
|
|
Some(&rbx_types::Variant::Vector3(v))=>return Ok(Into::<super::vector3::Vector3>::into(v).into_lua(lua)),
|
|
|
|
Some(rbx_types::Variant::Vector3(v))=>return Into::<super::vector3::Vector3>::into(v).into_lua(lua),
|
|
|
|
other=>println!("instance.properties.get(i)={other:?}"),
|
|
|
|
None=>(),
|
|
|
|
|
|
|
|
other=>return Err(mlua::Error::runtime(format!("Instance.__index Unsupported property type instance={} index={index_str} value={other:?}",instance.name))),
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//find a function with a matching name
|
|
|
|
//find a function with a matching name
|
|
|
|
if let Some(function)=class_functions_mut(lua,|cf|{
|
|
|
|
if let Some(function)=class_functions_mut(lua,|cf|{
|
|
|
|
@@ -267,18 +256,24 @@ impl mlua::UserData for Instance{
|
|
|
|
database:db,
|
|
|
|
database:db,
|
|
|
|
descriptor:Some(class),
|
|
|
|
descriptor:Some(class),
|
|
|
|
};
|
|
|
|
};
|
|
|
|
iter.find_map(|class|{
|
|
|
|
Ok(loop{
|
|
|
|
let mut class_methods=cf.get_or_create_class_methods(&class.name)?;
|
|
|
|
match iter.next(){
|
|
|
|
class_methods.get_or_create_function(lua,index_str)
|
|
|
|
Some(class)=>match cf.get_or_create_class_function(lua,&class.name,index_str)?{
|
|
|
|
.transpose()
|
|
|
|
Some(function)=>break Some(function),
|
|
|
|
}).transpose()
|
|
|
|
None=>(),
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
None=>break None,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
})?{
|
|
|
|
})?{
|
|
|
|
return function.into_lua(lua);
|
|
|
|
return Ok(function.into_lua(lua));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//find a child with a matching name
|
|
|
|
//find a child with a matching name
|
|
|
|
find_first_child(dom,instance,index_str)
|
|
|
|
Ok(
|
|
|
|
.map(|instance|Instance::new(instance.referent()))
|
|
|
|
find_first_child(dom,instance,index_str)
|
|
|
|
.into_lua(lua)
|
|
|
|
.map(|instance|Instance::new(instance.referent()))
|
|
|
|
|
|
|
|
.into_lua(lua)
|
|
|
|
|
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
});
|
|
|
|
});
|
|
|
|
methods.add_meta_function(mlua::MetaMethod::NewIndex,|lua,(this,index,value):(Instance,mlua::String,mlua::Value)|{
|
|
|
|
methods.add_meta_function(mlua::MetaMethod::NewIndex,|lua,(this,index,value):(Instance,mlua::String,mlua::Value)|{
|
|
|
|
@@ -295,7 +290,7 @@ impl mlua::UserData for Instance{
|
|
|
|
let property=iter.find_map(|cls|cls.properties.get(index_str)).ok_or(mlua::Error::runtime(format!("Property '{index_str}' missing on class '{}'",class.name)))?;
|
|
|
|
let property=iter.find_map(|cls|cls.properties.get(index_str)).ok_or(mlua::Error::runtime(format!("Property '{index_str}' missing on class '{}'",class.name)))?;
|
|
|
|
match &property.data_type{
|
|
|
|
match &property.data_type{
|
|
|
|
rbx_reflection::DataType::Value(rbx_types::VariantType::Vector3)=>{
|
|
|
|
rbx_reflection::DataType::Value(rbx_types::VariantType::Vector3)=>{
|
|
|
|
let typed_value:Vector3=*value.as_userdata().ok_or(mlua::Error::runtime("Expected Userdata"))?.borrow()?;
|
|
|
|
let typed_value:Vector3=value.as_userdata().ok_or(mlua::Error::runtime("Expected Userdata"))?.take()?;
|
|
|
|
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Vector3(typed_value.into()));
|
|
|
|
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Vector3(typed_value.into()));
|
|
|
|
},
|
|
|
|
},
|
|
|
|
rbx_reflection::DataType::Value(rbx_types::VariantType::Float32)=>{
|
|
|
|
rbx_reflection::DataType::Value(rbx_types::VariantType::Float32)=>{
|
|
|
|
@@ -311,7 +306,7 @@ impl mlua::UserData for Instance{
|
|
|
|
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)=>{
|
|
|
|
mlua::Value::UserData(any_user_data)=>{
|
|
|
|
let e:super::r#enum::Enum=*any_user_data.borrow()?;
|
|
|
|
let e:super::r#enum::Enum=any_user_data.take()?;
|
|
|
|
Ok(e.into())
|
|
|
|
Ok(e.into())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
_=>Err(mlua::Error::runtime("Expected Enum")),
|
|
|
|
_=>Err(mlua::Error::runtime("Expected Enum")),
|
|
|
|
@@ -319,7 +314,7 @@ impl mlua::UserData for Instance{
|
|
|
|
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Enum(typed_value));
|
|
|
|
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Enum(typed_value));
|
|
|
|
},
|
|
|
|
},
|
|
|
|
rbx_reflection::DataType::Value(rbx_types::VariantType::Color3)=>{
|
|
|
|
rbx_reflection::DataType::Value(rbx_types::VariantType::Color3)=>{
|
|
|
|
let typed_value:super::color3::Color3=*value.as_userdata().ok_or(mlua::Error::runtime("Expected Color3"))?.borrow()?;
|
|
|
|
let typed_value:super::color3::Color3=value.as_userdata().ok_or(mlua::Error::runtime("Expected Color3"))?.take()?;
|
|
|
|
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Color3(typed_value.into()));
|
|
|
|
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::Color3(typed_value.into()));
|
|
|
|
},
|
|
|
|
},
|
|
|
|
rbx_reflection::DataType::Value(rbx_types::VariantType::Bool)=>{
|
|
|
|
rbx_reflection::DataType::Value(rbx_types::VariantType::Bool)=>{
|
|
|
|
@@ -330,14 +325,6 @@ impl mlua::UserData for Instance{
|
|
|
|
let typed_value=value.as_str().ok_or(mlua::Error::runtime("Expected boolean"))?;
|
|
|
|
let typed_value=value.as_str().ok_or(mlua::Error::runtime("Expected boolean"))?;
|
|
|
|
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::String(typed_value.to_owned()));
|
|
|
|
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::String(typed_value.to_owned()));
|
|
|
|
},
|
|
|
|
},
|
|
|
|
rbx_reflection::DataType::Value(rbx_types::VariantType::NumberSequence)=>{
|
|
|
|
|
|
|
|
let typed_value:super::number_sequence::NumberSequence=*value.as_userdata().ok_or(mlua::Error::runtime("Expected NumberSequence"))?.borrow()?;
|
|
|
|
|
|
|
|
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::NumberSequence(typed_value.into()));
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
rbx_reflection::DataType::Value(rbx_types::VariantType::ColorSequence)=>{
|
|
|
|
|
|
|
|
let typed_value:super::color_sequence::ColorSequence=*value.as_userdata().ok_or(mlua::Error::runtime("Expected ColorSequence"))?.borrow()?;
|
|
|
|
|
|
|
|
instance.properties.insert(index_str.to_owned(),rbx_types::Variant::ColorSequence(typed_value.into()));
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
other=>return Err(mlua::Error::runtime(format!("Unimplemented property type: {other:?}"))),
|
|
|
|
other=>return Err(mlua::Error::runtime(format!("Unimplemented property type: {other:?}"))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
Ok(())
|
|
|
|
@@ -346,46 +333,29 @@ impl mlua::UserData for Instance{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// A class function definition shorthand.
|
|
|
|
fn get_service(lua:&mlua::Lua,tuple:mlua::MultiValue)->mlua::Result<mlua::MultiValue>{
|
|
|
|
macro_rules! cf{
|
|
|
|
let (_game,service):(Instance,mlua::String)=FromLuaMulti::from_lua_multi(tuple,lua)?;
|
|
|
|
($f:expr)=>{
|
|
|
|
dom_mut(lua,|dom|{
|
|
|
|
|lua,mut args|{
|
|
|
|
match &*service.to_str()?{
|
|
|
|
let this=Instance::from_lua(args.pop_front().unwrap_or(mlua::Value::Nil),lua)?;
|
|
|
|
"Lighting"=>{
|
|
|
|
$f(lua,this,FromLuaMulti::from_lua_multi(args,lua)?)?.into_lua_multi(lua)
|
|
|
|
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"))
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
Instance::new(referent).into_lua_multi(lua)
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
other=>Err::<mlua::MultiValue,_>(mlua::Error::runtime(format!("Service '{other}' not supported"))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
type ClassFunctionPointer=fn(&mlua::Lua,mlua::MultiValue)->mlua::Result<mlua::MultiValue>;
|
|
|
|
type FPointer=fn(&mlua::Lua,mlua::MultiValue)->mlua::Result<mlua::MultiValue>;
|
|
|
|
// TODO: use macros to define these with better organization
|
|
|
|
|
|
|
|
/// A double hash map of function pointers.
|
|
|
|
/// A double hash map of function pointers.
|
|
|
|
/// The class tree is walked by the Instance.__index metamethod to find available class methods.
|
|
|
|
/// The class tree is walked by the Instance.__index metamethod to find available class methods.
|
|
|
|
type CFD=phf::Map<&'static str,// Class name
|
|
|
|
static CLASS_FUNCTION_DATABASE:phf::Map<&str,phf::Map<&str,FPointer>>=phf::phf_map!{
|
|
|
|
phf::Map<&'static str,// Method name
|
|
|
|
|
|
|
|
ClassFunctionPointer
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
>;
|
|
|
|
|
|
|
|
static CLASS_FUNCTION_DATABASE:CFD=phf::phf_map!{
|
|
|
|
|
|
|
|
"DataModel"=>phf::phf_map!{
|
|
|
|
"DataModel"=>phf::phf_map!{
|
|
|
|
"GetService"=>cf!(|lua,_this,service:mlua::String|{
|
|
|
|
"GetService"=>get_service as FPointer,
|
|
|
|
dom_mut(lua,|dom|{
|
|
|
|
}
|
|
|
|
//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"))
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(Instance::new(referent))
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
other=>Err::<Instance,_>(mlua::Error::runtime(format!("Service '{other}' not supported"))),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
"Terrain"=>phf::phf_map!{
|
|
|
|
|
|
|
|
"FillBlock"=>cf!(|_lua,_,_:(super::cframe::CFrame,Vector3,super::r#enum::Enum)|mlua::Result::Ok(()))
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/// A store of created functions for each Roblox class.
|
|
|
|
/// A store of created functions for each Roblox class.
|
|
|
|
@@ -393,91 +363,49 @@ static CLASS_FUNCTION_DATABASE:CFD=phf::phf_map!{
|
|
|
|
#[derive(Default)]
|
|
|
|
#[derive(Default)]
|
|
|
|
struct ClassFunctions{
|
|
|
|
struct ClassFunctions{
|
|
|
|
classes:HashMap<&'static str,//ClassName
|
|
|
|
classes:HashMap<&'static str,//ClassName
|
|
|
|
HashMap<&'static str,//Method name
|
|
|
|
HashMap<&'static str,//Function name
|
|
|
|
mlua::Function
|
|
|
|
mlua::Function
|
|
|
|
>
|
|
|
|
>
|
|
|
|
>
|
|
|
|
>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl ClassFunctions{
|
|
|
|
impl ClassFunctions{
|
|
|
|
/// return self.classes[class] or create the ClassMethods and then return it
|
|
|
|
/// Someone please rewrite this, all it's supposed to do is
|
|
|
|
fn get_or_create_class_methods(&mut self,class:&str)->Option<ClassMethods>{
|
|
|
|
/// return self.classes[class][index] or create the function in the hashmap and then return it
|
|
|
|
// Use get_entry to get the &'static str keys of the database
|
|
|
|
fn get_or_create_class_function(&mut self,lua:&mlua::Lua,class:&str,index:&str)->mlua::Result<Option<mlua::Function>>{
|
|
|
|
|
|
|
|
// Use get_entry to get the &'static str key of the database
|
|
|
|
// and use it as a key for the classes hashmap
|
|
|
|
// and use it as a key for the classes hashmap
|
|
|
|
CLASS_FUNCTION_DATABASE.get_entry(class)
|
|
|
|
let f=match CLASS_FUNCTION_DATABASE.get_entry(class){
|
|
|
|
.map(|(&static_class_str,method_pointers)|
|
|
|
|
Some((&static_class_str,class_functions))=>{
|
|
|
|
ClassMethods{
|
|
|
|
match self.classes.entry(static_class_str){
|
|
|
|
method_pointers,
|
|
|
|
Entry::Occupied(mut occupied_entry)=>{
|
|
|
|
methods:self.classes.entry(static_class_str)
|
|
|
|
match class_functions.get_entry(index){
|
|
|
|
.or_insert_with(||HashMap::new()),
|
|
|
|
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,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ClassMethods<'a>{
|
|
|
|
|
|
|
|
method_pointers:&'static phf::Map<&'static str,ClassFunctionPointer>,
|
|
|
|
|
|
|
|
methods:&'a mut HashMap<&'static str,mlua::Function>,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ClassMethods<'_>{
|
|
|
|
|
|
|
|
/// return self.methods[index] or create the function in the hashmap and then return it
|
|
|
|
|
|
|
|
fn get_or_create_function(&mut self,lua:&mlua::Lua,index:&str)->mlua::Result<Option<mlua::Function>>{
|
|
|
|
|
|
|
|
Ok(match self.method_pointers.get_entry(index){
|
|
|
|
|
|
|
|
Some((&static_index_str,&function_pointer))=>Some(
|
|
|
|
|
|
|
|
match self.methods.entry(static_index_str){
|
|
|
|
|
|
|
|
Entry::Occupied(entry)=>entry.get().clone(),
|
|
|
|
|
|
|
|
Entry::Vacant(entry)=>entry.insert(
|
|
|
|
|
|
|
|
lua.create_function(function_pointer)?
|
|
|
|
|
|
|
|
).clone(),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
None=>None,
|
|
|
|
None=>None,
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// A virtual property pointer definition shorthand.
|
|
|
|
|
|
|
|
type VirtualPropertyFunctionPointer=fn(&rbx_types::Variant)->Option<rbx_types::Variant>;
|
|
|
|
|
|
|
|
const fn vpp(
|
|
|
|
|
|
|
|
property:&'static str,
|
|
|
|
|
|
|
|
pointer:VirtualPropertyFunctionPointer,
|
|
|
|
|
|
|
|
)->VirtualProperty{
|
|
|
|
|
|
|
|
VirtualProperty{
|
|
|
|
|
|
|
|
property,
|
|
|
|
|
|
|
|
pointer,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
struct VirtualProperty{
|
|
|
|
|
|
|
|
property:&'static str,// Source property name
|
|
|
|
|
|
|
|
pointer:VirtualPropertyFunctionPointer,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
type VPD=phf::Map<&'static str,// Class name
|
|
|
|
|
|
|
|
phf::Map<&'static str,// Virtual property name
|
|
|
|
|
|
|
|
VirtualProperty
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
>;
|
|
|
|
|
|
|
|
static VIRTUAL_PROPERTY_DATABASE:VPD=phf::phf_map!{
|
|
|
|
|
|
|
|
"BasePart"=>phf::phf_map!{
|
|
|
|
|
|
|
|
"Position"=>vpp("CFrame",|c:&rbx_types::Variant|{
|
|
|
|
|
|
|
|
let c=match c{
|
|
|
|
|
|
|
|
rbx_types::Variant::CFrame(c)=>c,
|
|
|
|
|
|
|
|
_=>return None,//fail silently and ungracefully
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(rbx_types::Variant::Vector3(c.position))
|
|
|
|
|
|
|
|
}),
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn find_virtual_property(
|
|
|
|
|
|
|
|
properties:&HashMap<String,rbx_types::Variant>,
|
|
|
|
|
|
|
|
class:&rbx_reflection::ClassDescriptor,
|
|
|
|
|
|
|
|
index:&str
|
|
|
|
|
|
|
|
)->Option<rbx_types::Variant>{
|
|
|
|
|
|
|
|
//Find virtual property
|
|
|
|
|
|
|
|
let class_virtual_properties=VIRTUAL_PROPERTY_DATABASE.get(&class.name)?;
|
|
|
|
|
|
|
|
let virtual_property=class_virtual_properties.get(index)?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Get source property
|
|
|
|
|
|
|
|
let variant=properties.get(virtual_property.property)?;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Transform Source property with provided function
|
|
|
|
|
|
|
|
(virtual_property.pointer)(variant)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|