roblox_emulator: use extended instances
This commit is contained in:
lib/roblox_emulator/src
@ -1,4 +1,5 @@
|
|||||||
use rbx_dom_weak::{types::Ref,ustr,InstanceBuilder,WeakDom};
|
use crate::types::WeakDom;
|
||||||
|
use rbx_dom_weak::{types::Ref,ustr,InstanceBuilder};
|
||||||
|
|
||||||
pub fn class_is_a(class:&str,superclass:&str)->bool{
|
pub fn class_is_a(class:&str,superclass:&str)->bool{
|
||||||
class==superclass
|
class==superclass
|
||||||
@ -19,7 +20,7 @@ impl Context{
|
|||||||
pub const fn new(dom:WeakDom)->Self{
|
pub const fn new(dom:WeakDom)->Self{
|
||||||
Self{dom}
|
Self{dom}
|
||||||
}
|
}
|
||||||
pub fn script_singleton(source:String)->(Context,crate::runner::instance::Instance,Services){
|
pub fn script_singleton(source:String)->(Context,crate::runner::instance::InstanceRef,Services){
|
||||||
let script=InstanceBuilder::new("Script")
|
let script=InstanceBuilder::new("Script")
|
||||||
.with_property("Source",rbx_types::Variant::String(source));
|
.with_property("Source",rbx_types::Variant::String(source));
|
||||||
let script_ref=script.referent();
|
let script_ref=script.referent();
|
||||||
@ -28,7 +29,7 @@ impl Context{
|
|||||||
.with_child(script)
|
.with_child(script)
|
||||||
));
|
));
|
||||||
let services=context.convert_into_place();
|
let services=context.convert_into_place();
|
||||||
(context,crate::runner::instance::Instance::new(script_ref),services)
|
(context,crate::runner::instance::InstanceRef::new(script_ref),services)
|
||||||
}
|
}
|
||||||
pub fn from_ref(dom:&WeakDom)->&Context{
|
pub fn from_ref(dom:&WeakDom)->&Context{
|
||||||
unsafe{&*(dom as *const WeakDom as *const Context)}
|
unsafe{&*(dom as *const WeakDom as *const Context)}
|
||||||
@ -39,24 +40,24 @@ impl Context{
|
|||||||
/// Creates an iterator over all items of a particular class.
|
/// Creates an iterator over all items of a particular class.
|
||||||
pub fn superclass_iter<'a>(&'a self,superclass:&'a str)->impl Iterator<Item=Ref>+'a{
|
pub fn superclass_iter<'a>(&'a self,superclass:&'a str)->impl Iterator<Item=Ref>+'a{
|
||||||
self.dom.descendants().filter(|&instance|
|
self.dom.descendants().filter(|&instance|
|
||||||
class_is_a(instance.class.as_ref(),superclass)
|
class_is_a(instance.as_ref().class.as_ref(),superclass)
|
||||||
).map(|instance|instance.referent())
|
).map(|instance|instance.as_ref().referent())
|
||||||
}
|
}
|
||||||
pub fn scripts(&self)->Vec<crate::runner::instance::Instance>{
|
pub fn scripts(&self)->Vec<crate::runner::instance::InstanceRef>{
|
||||||
self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::Instance::new).collect()
|
self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::InstanceRef::new).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_services(&self)->Option<Services>{
|
pub fn find_services(&self)->Option<Services>{
|
||||||
Some(Services{
|
Some(Services{
|
||||||
workspace:*self.dom.root().children().iter().find(|&&r|
|
workspace:*self.dom.root().as_ref().children().iter().find(|&&r|
|
||||||
self.dom.get_by_ref(r).is_some_and(|instance|instance.class=="Workspace")
|
self.dom.get_by_ref(r).is_some_and(|instance|instance.as_ref().class=="Workspace")
|
||||||
)?,
|
)?,
|
||||||
game:self.dom.root_ref(),
|
game:self.dom.root_ref(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn convert_into_place(&mut self)->Services{
|
pub fn convert_into_place(&mut self)->Services{
|
||||||
//snapshot root instances
|
//snapshot root instances
|
||||||
let children=self.dom.root().children().to_owned();
|
let children=self.dom.root().as_ref().children().to_owned();
|
||||||
|
|
||||||
//insert services
|
//insert services
|
||||||
let game=self.dom.root_ref();
|
let game=self.dom.root_ref();
|
||||||
@ -69,7 +70,7 @@ impl Context{
|
|||||||
);
|
);
|
||||||
{
|
{
|
||||||
//Lowercase and upper case workspace property!
|
//Lowercase and upper case workspace property!
|
||||||
let game=self.dom.root_mut();
|
let game=self.dom.root_mut().as_mut();
|
||||||
game.properties.insert(ustr("workspace"),rbx_types::Variant::Ref(workspace));
|
game.properties.insert(ustr("workspace"),rbx_types::Variant::Ref(workspace));
|
||||||
game.properties.insert(ustr("Workspace"),rbx_types::Variant::Ref(workspace));
|
game.properties.insert(ustr("Workspace"),rbx_types::Variant::Ref(workspace));
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
pub mod types;
|
||||||
pub mod runner;
|
pub mod runner;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
#[cfg(feature="run-service")]
|
#[cfg(feature="run-service")]
|
||||||
|
@ -2,25 +2,25 @@ use std::collections::{hash_map::Entry,HashMap};
|
|||||||
|
|
||||||
use mlua::{FromLua,FromLuaMulti,IntoLua,IntoLuaMulti};
|
use mlua::{FromLua,FromLuaMulti,IntoLua,IntoLuaMulti};
|
||||||
use rbx_types::Ref;
|
use rbx_types::Ref;
|
||||||
use rbx_dom_weak::{ustr,Ustr,InstanceBuilder,WeakDom};
|
use rbx_dom_weak::{ustr,Ustr,InstanceBuilder};
|
||||||
|
|
||||||
use crate::runner::vector3::Vector3;
|
use crate::runner::vector3::Vector3;
|
||||||
|
use crate::types::{WeakDom,Instance};
|
||||||
|
|
||||||
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
|
//class functions store
|
||||||
lua.set_app_data(ClassMethodsStore::default());
|
lua.set_app_data(ClassMethodsStore::default());
|
||||||
lua.set_app_data(InstanceValueStore::default());
|
|
||||||
|
|
||||||
let instance_table=lua.create_table()?;
|
let instance_table=lua.create_table()?;
|
||||||
|
|
||||||
//Instance.new
|
//Instance.new
|
||||||
instance_table.raw_set("new",
|
instance_table.raw_set("new",
|
||||||
lua.create_function(|lua,(class_name,parent):(mlua::String,Option<Instance>)|{
|
lua.create_function(|lua,(class_name,parent):(mlua::String,Option<InstanceRef>)|{
|
||||||
let class_name_str=&*class_name.to_str()?;
|
let class_name_str=&*class_name.to_str()?;
|
||||||
let parent=parent.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?;
|
let parent=parent.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?;
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
//TODO: Nil instances
|
//TODO: Nil instances
|
||||||
Ok(Instance::new(dom.insert(parent.referent,InstanceBuilder::new(class_name_str))))
|
Ok(InstanceRef::new(dom.insert(parent.referent,InstanceBuilder::new(class_name_str))))
|
||||||
})
|
})
|
||||||
})?
|
})?
|
||||||
)?;
|
)?;
|
||||||
@ -44,10 +44,12 @@ fn coerce_float32(value:&mlua::Value)->Option<f32>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_full_name(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->String{
|
fn get_full_name(dom:&WeakDom,instance:&Instance)->String{
|
||||||
|
let instance=instance.as_ref();
|
||||||
let mut full_name=instance.name.clone();
|
let mut full_name=instance.name.clone();
|
||||||
let mut pref=instance.parent();
|
let mut pref=instance.parent();
|
||||||
while let Some(parent)=dom.get_by_ref(pref){
|
while let Some(parent)=dom.get_by_ref(pref){
|
||||||
|
let parent=parent.as_ref();
|
||||||
full_name.insert(0,'.');
|
full_name.insert(0,'.');
|
||||||
full_name.insert_str(0,parent.name.as_str());
|
full_name.insert_str(0,parent.name.as_str());
|
||||||
pref=parent.parent();
|
pref=parent.parent();
|
||||||
@ -55,10 +57,10 @@ fn get_full_name(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->S
|
|||||||
full_name
|
full_name
|
||||||
}
|
}
|
||||||
//helper function for script
|
//helper function for script
|
||||||
pub fn get_name_source(lua:&mlua::Lua,script:Instance)->Result<(String,String),mlua::Error>{
|
pub fn get_name_source(lua:&mlua::Lua,script:InstanceRef)->Result<(String,String),mlua::Error>{
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
let instance=script.get(dom)?;
|
let instance=script.get(dom)?;
|
||||||
let source=match instance.properties.get(&ustr("Source")){
|
let source=match instance.as_ref().properties.get(&ustr("Source")){
|
||||||
Some(rbx_dom_weak::types::Variant::String(s))=>s.clone(),
|
Some(rbx_dom_weak::types::Variant::String(s))=>s.clone(),
|
||||||
_=>Err(mlua::Error::external("Missing script.Source"))?,
|
_=>Err(mlua::Error::external("Missing script.Source"))?,
|
||||||
};
|
};
|
||||||
@ -66,46 +68,46 @@ pub fn get_name_source(lua:&mlua::Lua,script:Instance)->Result<(String,String),m
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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>{
|
pub fn find_first_child<'a>(dom:&'a WeakDom,instance:&Instance,name:&str)->Option<&'a Instance>{
|
||||||
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.name==name)
|
instance.as_ref().children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.as_ref().name==name)
|
||||||
}
|
}
|
||||||
pub fn find_first_descendant<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str)->Option<&'a rbx_dom_weak::Instance>{
|
pub fn find_first_descendant<'a>(dom:&'a WeakDom,instance:&Instance,name:&str)->Option<&'a Instance>{
|
||||||
dom.descendants_of(instance.referent()).find(|&inst|inst.name==name)
|
dom.descendants_of(instance.as_ref().referent()).find(|&inst|inst.as_ref().name==name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_first_child_of_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,class:&str)->Option<&'a rbx_dom_weak::Instance>{
|
pub fn find_first_child_of_class<'a>(dom:&'a WeakDom,instance:&Instance,class:&str)->Option<&'a Instance>{
|
||||||
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.class==class)
|
instance.as_ref().children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.as_ref().class==class)
|
||||||
}
|
}
|
||||||
pub fn find_first_descendant_of_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,class:&str)->Option<&'a rbx_dom_weak::Instance>{
|
pub fn find_first_descendant_of_class<'a>(dom:&'a WeakDom,instance:&Instance,class:&str)->Option<&'a Instance>{
|
||||||
dom.descendants_of(instance.referent()).find(|&inst|inst.class==class)
|
dom.descendants_of(instance.as_ref().referent()).find(|&inst|inst.as_ref().class==class)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone,Copy)]
|
#[derive(Clone,Copy)]
|
||||||
pub struct Instance{
|
pub struct InstanceRef{
|
||||||
referent:Ref,
|
referent:Ref,
|
||||||
}
|
}
|
||||||
impl Instance{
|
impl InstanceRef{
|
||||||
pub const fn new(referent:Ref)->Self{
|
pub const fn new(referent:Ref)->Self{
|
||||||
Self{referent}
|
Self{referent}
|
||||||
}
|
}
|
||||||
pub fn get<'a>(&self,dom:&'a WeakDom)->mlua::Result<&'a rbx_dom_weak::Instance>{
|
pub fn get<'a>(&self,dom:&'a WeakDom)->mlua::Result<&'a Instance>{
|
||||||
dom.get_by_ref(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing"))
|
dom.get_by_ref(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing"))
|
||||||
}
|
}
|
||||||
pub fn get_mut<'a>(&self,dom:&'a mut WeakDom)->mlua::Result<&'a mut rbx_dom_weak::Instance>{
|
pub fn get_mut<'a>(&self,dom:&'a mut WeakDom)->mlua::Result<&'a mut Instance>{
|
||||||
dom.get_by_ref_mut(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing"))
|
dom.get_by_ref_mut(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type_from_lua_userdata!(Instance);
|
type_from_lua_userdata!(InstanceRef);
|
||||||
|
|
||||||
impl mlua::UserData for Instance{
|
impl mlua::UserData for InstanceRef{
|
||||||
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
|
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
|
||||||
fields.add_field_method_get("Parent",|lua,this|{
|
fields.add_field_method_get("Parent",|lua,this|{
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
let instance=this.get(dom)?;
|
let instance=this.get(dom)?.as_ref();
|
||||||
Ok(Instance::new(instance.parent()))
|
Ok(InstanceRef::new(instance.parent()))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
fields.add_field_method_set("Parent",|lua,this,val:Option<Instance>|{
|
fields.add_field_method_set("Parent",|lua,this,val:Option<InstanceRef>|{
|
||||||
let parent=val.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?;
|
let parent=val.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?;
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
dom.transfer_within(this.referent,parent.referent);
|
dom.transfer_within(this.referent,parent.referent);
|
||||||
@ -114,13 +116,13 @@ impl mlua::UserData for Instance{
|
|||||||
});
|
});
|
||||||
fields.add_field_method_get("Name",|lua,this|{
|
fields.add_field_method_get("Name",|lua,this|{
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
let instance=this.get(dom)?;
|
let instance=this.get(dom)?.as_ref();
|
||||||
Ok(instance.name.clone())
|
Ok(instance.name.clone())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
fields.add_field_method_set("Name",|lua,this,val:mlua::String|{
|
fields.add_field_method_set("Name",|lua,this,val:mlua::String|{
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
let instance=this.get_mut(dom)?;
|
let instance=this.get_mut(dom)?.as_mut();
|
||||||
//Why does this need to be cloned?
|
//Why does this need to be cloned?
|
||||||
instance.name=val.to_str()?.to_owned();
|
instance.name=val.to_str()?.to_owned();
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -128,7 +130,7 @@ impl mlua::UserData for Instance{
|
|||||||
});
|
});
|
||||||
fields.add_field_method_get("ClassName",|lua,this|{
|
fields.add_field_method_get("ClassName",|lua,this|{
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
let instance=this.get(dom)?;
|
let instance=this.get(dom)?.as_ref();
|
||||||
Ok(instance.class.to_owned())
|
Ok(instance.class.to_owned())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -136,17 +138,17 @@ impl mlua::UserData for Instance{
|
|||||||
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
||||||
methods.add_method("GetChildren",|lua,this,_:()|
|
methods.add_method("GetChildren",|lua,this,_:()|
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
let instance=this.get(dom)?;
|
let instance=this.get(dom)?.as_ref();
|
||||||
let children:Vec<_>=instance
|
let children:Vec<_>=instance
|
||||||
.children()
|
.children()
|
||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.map(Instance::new)
|
.map(InstanceRef::new)
|
||||||
.collect();
|
.collect();
|
||||||
Ok(children)
|
Ok(children)
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
fn ffc(lua:&mlua::Lua,this:&Instance,(name,search_descendants):(mlua::String,Option<bool>))->mlua::Result<Option<Instance>>{
|
fn ffc(lua:&mlua::Lua,this:&InstanceRef,(name,search_descendants):(mlua::String,Option<bool>))->mlua::Result<Option<InstanceRef>>{
|
||||||
let name_str=&*name.to_str()?;
|
let name_str=&*name.to_str()?;
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
let instance=this.get(dom)?;
|
let instance=this.get(dom)?;
|
||||||
@ -156,7 +158,7 @@ impl mlua::UserData for Instance{
|
|||||||
false=>find_first_child(dom,instance,name_str),
|
false=>find_first_child(dom,instance,name_str),
|
||||||
}
|
}
|
||||||
.map(|instance|
|
.map(|instance|
|
||||||
Instance::new(instance.referent())
|
InstanceRef::new(instance.as_ref().referent())
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -172,7 +174,7 @@ impl mlua::UserData for Instance{
|
|||||||
false=>find_first_child_of_class(dom,this.get(dom)?,class_str),
|
false=>find_first_child_of_class(dom,this.get(dom)?,class_str),
|
||||||
}
|
}
|
||||||
.map(|instance|
|
.map(|instance|
|
||||||
Instance::new(instance.referent())
|
InstanceRef::new(instance.as_ref().referent())
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -182,7 +184,7 @@ impl mlua::UserData for Instance{
|
|||||||
let children:Vec<_>=dom
|
let children:Vec<_>=dom
|
||||||
.descendants_of(this.referent)
|
.descendants_of(this.referent)
|
||||||
.map(|instance|
|
.map(|instance|
|
||||||
Instance::new(instance.referent())
|
InstanceRef::new(instance.as_ref().referent())
|
||||||
)
|
)
|
||||||
.collect();
|
.collect();
|
||||||
Ok(children)
|
Ok(children)
|
||||||
@ -190,7 +192,7 @@ impl mlua::UserData for Instance{
|
|||||||
);
|
);
|
||||||
methods.add_method("IsA",|lua,this,classname:mlua::String|
|
methods.add_method("IsA",|lua,this,classname:mlua::String|
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
let instance=this.get(dom)?;
|
let instance=this.get(dom)?.as_ref();
|
||||||
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()?))
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -200,39 +202,40 @@ impl mlua::UserData for Instance{
|
|||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
methods.add_meta_function(mlua::MetaMethod::ToString,|lua,this:Instance|{
|
methods.add_meta_function(mlua::MetaMethod::ToString,|lua,this:InstanceRef|{
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
let instance=this.get(dom)?;
|
let instance=this.get(dom)?.as_ref();
|
||||||
Ok(instance.name.clone())
|
Ok(instance.name.clone())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
methods.add_meta_function(mlua::MetaMethod::Index,|lua,(this,index):(Instance,mlua::String)|{
|
methods.add_meta_function(mlua::MetaMethod::Index,|lua,(this,index):(InstanceRef,mlua::String)|{
|
||||||
let index_str=&*index.to_str()?;
|
let index_str=&*index.to_str()?;
|
||||||
let index_ustr=ustr(index_str);
|
let index_ustr=ustr(index_str);
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
let instance=this.get(dom)?;
|
let instance=this.get(dom)?;
|
||||||
|
let instance_ref=instance.as_ref();
|
||||||
//println!("__index t={} i={index:?}",instance.name);
|
//println!("__index t={} i={index:?}",instance.name);
|
||||||
let db=rbx_reflection_database::get();
|
let db=rbx_reflection_database::get();
|
||||||
let class=db.classes.get(instance.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?;
|
let class=db.classes.get(instance_ref.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?;
|
||||||
//Find existing property
|
//Find existing property
|
||||||
match instance.properties.get(&index_ustr)
|
match instance_ref.properties.get(&index_ustr)
|
||||||
.cloned()
|
.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).cloned())
|
||||||
//Find virtual property
|
//Find virtual property
|
||||||
.or_else(||db.superclasses_iter(class).find_map(|class|
|
.or_else(||db.superclasses_iter(class).find_map(|class|
|
||||||
find_virtual_property(&instance.properties,class,&index_ustr)
|
find_virtual_property(&instance_ref.properties,class,&index_ustr)
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
Some(rbx_types::Variant::Int32(val))=>return val.into_lua(lua),
|
Some(rbx_types::Variant::Int32(val))=>return val.into_lua(lua),
|
||||||
Some(rbx_types::Variant::Int64(val))=>return val.into_lua(lua),
|
Some(rbx_types::Variant::Int64(val))=>return val.into_lua(lua),
|
||||||
Some(rbx_types::Variant::Float32(val))=>return val.into_lua(lua),
|
Some(rbx_types::Variant::Float32(val))=>return val.into_lua(lua),
|
||||||
Some(rbx_types::Variant::Float64(val))=>return val.into_lua(lua),
|
Some(rbx_types::Variant::Float64(val))=>return val.into_lua(lua),
|
||||||
Some(rbx_types::Variant::Ref(val))=>return Instance::new(val).into_lua(lua),
|
Some(rbx_types::Variant::Ref(val))=>return InstanceRef::new(val).into_lua(lua),
|
||||||
Some(rbx_types::Variant::CFrame(cf))=>return Into::<crate::runner::cframe::CFrame>::into(cf).into_lua(lua),
|
Some(rbx_types::Variant::CFrame(cf))=>return Into::<crate::runner::cframe::CFrame>::into(cf).into_lua(lua),
|
||||||
Some(rbx_types::Variant::Vector3(v))=>return Into::<crate::runner::vector3::Vector3>::into(v).into_lua(lua),
|
Some(rbx_types::Variant::Vector3(v))=>return Into::<crate::runner::vector3::Vector3>::into(v).into_lua(lua),
|
||||||
None=>(),
|
None=>(),
|
||||||
other=>return Err(mlua::Error::runtime(format!("Instance.__index Unsupported property type instance={} index={index_str} value={other:?}",instance.name))),
|
other=>return Err(mlua::Error::runtime(format!("Instance.__index Unsupported property type instance={} index={index_str} value={other:?}",instance_ref.name))),
|
||||||
}
|
}
|
||||||
//find a function with a matching name
|
//find a function with a matching name
|
||||||
if let Some(function)=class_methods_store_mut(lua,|cf|{
|
if let Some(function)=class_methods_store_mut(lua,|cf|{
|
||||||
@ -246,24 +249,21 @@ impl mlua::UserData for Instance{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//find or create an associated userdata object
|
//find or create an associated userdata object
|
||||||
if let Some(value)=instance_value_store_mut(lua,|ivs|{
|
let instance=this.get_mut(dom)?;
|
||||||
//TODO: walk class tree somehow
|
if let Some(value)=instance.get_or_create_userdata(lua,index_ustr)?{
|
||||||
match ivs.get_or_create_instance_values(&instance){
|
|
||||||
Some(mut instance_values)=>instance_values.get_or_create_value(lua,index_str),
|
|
||||||
None=>Ok(None)
|
|
||||||
}
|
|
||||||
})?{
|
|
||||||
return value.into_lua(lua);
|
return value.into_lua(lua);
|
||||||
}
|
}
|
||||||
|
// drop mutable borrow
|
||||||
//find a child with a matching name
|
//find a child with a matching name
|
||||||
|
let instance=this.get(dom)?;
|
||||||
find_first_child(dom,instance,index_str)
|
find_first_child(dom,instance,index_str)
|
||||||
.map(|instance|Instance::new(instance.referent()))
|
.map(|instance|InstanceRef::new(instance.as_ref().referent()))
|
||||||
.into_lua(lua)
|
.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):(InstanceRef,mlua::String,mlua::Value)|{
|
||||||
dom_mut(lua,|dom|{
|
dom_mut(lua,|dom|{
|
||||||
let instance=this.get_mut(dom)?;
|
let instance=this.get_mut(dom)?.as_mut();
|
||||||
//println!("__newindex t={} i={index:?} v={value:?}",instance.name);
|
//println!("__newindex t={} i={index:?} v={value:?}",instance.name);
|
||||||
let index_str=&*index.to_str()?;
|
let index_str=&*index.to_str()?;
|
||||||
let index_ustr=ustr(index_str);
|
let index_ustr=ustr(index_str);
|
||||||
@ -331,7 +331,7 @@ impl mlua::UserData for Instance{
|
|||||||
macro_rules! cf{
|
macro_rules! cf{
|
||||||
($f:expr)=>{
|
($f:expr)=>{
|
||||||
|lua,mut args|{
|
|lua,mut args|{
|
||||||
let this=Instance::from_lua(args.pop_front().unwrap_or(mlua::Value::Nil),lua)?;
|
let this=InstanceRef::from_lua(args.pop_front().unwrap_or(mlua::Value::Nil),lua)?;
|
||||||
$f(lua,this,FromLuaMulti::from_lua_multi(args,lua)?)?.into_lua_multi(lua)
|
$f(lua,this,FromLuaMulti::from_lua_multi(args,lua)?)?.into_lua_multi(lua)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -354,13 +354,13 @@ static CLASS_FUNCTION_DATABASE:CFD=phf::phf_map!{
|
|||||||
match service{
|
match service{
|
||||||
"Lighting"|"RunService"=>{
|
"Lighting"|"RunService"=>{
|
||||||
let referent=find_first_child_of_class(dom,dom.root(),service)
|
let referent=find_first_child_of_class(dom,dom.root(),service)
|
||||||
.map(|instance|instance.referent())
|
.map(|instance|instance.as_ref().referent())
|
||||||
.unwrap_or_else(||
|
.unwrap_or_else(||
|
||||||
dom.insert(dom.root_ref(),InstanceBuilder::new(service))
|
dom.insert(dom.root_ref(),InstanceBuilder::new(service))
|
||||||
);
|
);
|
||||||
Ok(Instance::new(referent))
|
Ok(InstanceRef::new(referent))
|
||||||
},
|
},
|
||||||
other=>Err::<Instance,_>(mlua::Error::runtime(format!("Service '{other}' not supported"))),
|
other=>Err::<InstanceRef,_>(mlua::Error::runtime(format!("Service '{other}' not supported"))),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
@ -467,64 +467,3 @@ fn find_virtual_property(
|
|||||||
//Transform Source property with provided function
|
//Transform Source property with provided function
|
||||||
(virtual_property.pointer)(variant)
|
(virtual_property.pointer)(variant)
|
||||||
}
|
}
|
||||||
|
|
||||||
// lazy-loaded per-instance userdata values
|
|
||||||
// This whole thing is a bad idea and a garbage collection nightmare.
|
|
||||||
// TODO: recreate rbx_dom_weak with my own instance type that owns this data.
|
|
||||||
type CreateUserData=fn(&mlua::Lua)->mlua::Result<mlua::AnyUserData>;
|
|
||||||
type LUD=phf::Map<&'static str,// Class name
|
|
||||||
phf::Map<&'static str,// Value name
|
|
||||||
CreateUserData
|
|
||||||
>
|
|
||||||
>;
|
|
||||||
static LAZY_USER_DATA:LUD=phf::phf_map!{
|
|
||||||
"RunService"=>phf::phf_map!{
|
|
||||||
"RenderStepped"=>|lua|{
|
|
||||||
lua.create_any_userdata(crate::runner::script_signal::ScriptSignal::new())
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct InstanceValueStore{
|
|
||||||
values:HashMap<Ref,
|
|
||||||
HashMap<&'static str,
|
|
||||||
mlua::AnyUserData
|
|
||||||
>
|
|
||||||
>,
|
|
||||||
}
|
|
||||||
pub struct InstanceValues<'a>{
|
|
||||||
named_values:&'static phf::Map<&'static str,CreateUserData>,
|
|
||||||
values:&'a mut HashMap<&'static str,mlua::AnyUserData>,
|
|
||||||
}
|
|
||||||
impl InstanceValueStore{
|
|
||||||
pub fn get_or_create_instance_values(&mut self,instance:&rbx_dom_weak::Instance)->Option<InstanceValues>{
|
|
||||||
LAZY_USER_DATA.get(instance.class.as_str())
|
|
||||||
.map(|named_values|
|
|
||||||
InstanceValues{
|
|
||||||
named_values,
|
|
||||||
values:self.values.entry(instance.referent())
|
|
||||||
.or_insert_with(||HashMap::new()),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl InstanceValues<'_>{
|
|
||||||
pub fn get_or_create_value(&mut self,lua:&mlua::Lua,index:&str)->mlua::Result<Option<mlua::AnyUserData>>{
|
|
||||||
Ok(match self.named_values.get_entry(index){
|
|
||||||
Some((&static_index_str,&function_pointer))=>Some(
|
|
||||||
match self.values.entry(static_index_str){
|
|
||||||
Entry::Occupied(entry)=>entry.get().clone(),
|
|
||||||
Entry::Vacant(entry)=>entry.insert(
|
|
||||||
function_pointer(lua)?
|
|
||||||
).clone(),
|
|
||||||
}
|
|
||||||
),
|
|
||||||
None=>None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn instance_value_store_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut InstanceValueStore)->mlua::Result<T>)->mlua::Result<T>{
|
|
||||||
let mut cf=lua.app_data_mut::<InstanceValueStore>().ok_or_else(||mlua::Error::runtime("InstanceValueStore missing"))?;
|
|
||||||
f(&mut *cf)
|
|
||||||
}
|
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
pub mod instance;
|
pub mod instance;
|
||||||
pub use instance::Instance;
|
pub use instance::InstanceRef;
|
||||||
|
@ -7,7 +7,7 @@ mod color3;
|
|||||||
mod cframe;
|
mod cframe;
|
||||||
mod vector3;
|
mod vector3;
|
||||||
pub mod instance;
|
pub mod instance;
|
||||||
mod script_signal;
|
pub mod script_signal;
|
||||||
mod color_sequence;
|
mod color_sequence;
|
||||||
mod number_sequence;
|
mod number_sequence;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
#[cfg(feature="run-service")]
|
#[cfg(feature="run-service")]
|
||||||
use crate::scheduler::scheduler_mut;
|
use crate::scheduler::scheduler_mut;
|
||||||
|
use crate::types::WeakDom;
|
||||||
|
|
||||||
pub struct Runner{
|
pub struct Runner{
|
||||||
lua:mlua::Lua,
|
lua:mlua::Lua,
|
||||||
@ -60,11 +61,11 @@ impl Runner{
|
|||||||
pub fn runnable_context_with_services<'a>(self,context:&'a mut Context,services:&crate::context::Services)->Result<Runnable<'a>,Error>{
|
pub fn runnable_context_with_services<'a>(self,context:&'a mut Context,services:&crate::context::Services)->Result<Runnable<'a>,Error>{
|
||||||
{
|
{
|
||||||
let globals=self.lua.globals();
|
let globals=self.lua.globals();
|
||||||
globals.set("game",super::instance::Instance::new(services.game)).map_err(Error::RustLua)?;
|
globals.set("game",super::instance::InstanceRef::new(services.game)).map_err(Error::RustLua)?;
|
||||||
globals.set("workspace",super::instance::Instance::new(services.workspace)).map_err(Error::RustLua)?;
|
globals.set("workspace",super::instance::InstanceRef::new(services.workspace)).map_err(Error::RustLua)?;
|
||||||
}
|
}
|
||||||
//this makes set_app_data shut up about the lifetime
|
//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)});
|
self.lua.set_app_data::<&'static mut WeakDom>(unsafe{core::mem::transmute(&mut context.dom)});
|
||||||
#[cfg(feature="run-service")]
|
#[cfg(feature="run-service")]
|
||||||
self.lua.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default());
|
self.lua.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default());
|
||||||
Ok(Runnable{
|
Ok(Runnable{
|
||||||
@ -81,14 +82,14 @@ pub struct Runnable<'a>{
|
|||||||
}
|
}
|
||||||
impl Runnable<'_>{
|
impl Runnable<'_>{
|
||||||
pub fn drop_context(self)->Runner{
|
pub fn drop_context(self)->Runner{
|
||||||
self.lua.remove_app_data::<&'static mut rbx_dom_weak::WeakDom>();
|
self.lua.remove_app_data::<&'static mut WeakDom>();
|
||||||
#[cfg(feature="run-service")]
|
#[cfg(feature="run-service")]
|
||||||
self.lua.remove_app_data::<crate::scheduler::Scheduler>();
|
self.lua.remove_app_data::<crate::scheduler::Scheduler>();
|
||||||
Runner{
|
Runner{
|
||||||
lua:self.lua,
|
lua:self.lua,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn run_script(&self,script:super::instance::Instance)->Result<(),Error>{
|
pub fn run_script(&self,script:super::instance::InstanceRef)->Result<(),Error>{
|
||||||
let (name,source)=super::instance::instance::get_name_source(&self.lua,script).map_err(Error::RustLua)?;
|
let (name,source)=super::instance::instance::get_name_source(&self.lua,script).map_err(Error::RustLua)?;
|
||||||
self.lua.globals().raw_set("script",script).map_err(Error::RustLua)?;
|
self.lua.globals().raw_set("script",script).map_err(Error::RustLua)?;
|
||||||
let f=self.lua.load(source.as_str())
|
let f=self.lua.load(source.as_str())
|
||||||
@ -123,20 +124,15 @@ impl Runnable<'_>{
|
|||||||
}
|
}
|
||||||
#[cfg(feature="run-service")]
|
#[cfg(feature="run-service")]
|
||||||
pub fn run_service_step(&self)->Result<(),mlua::Error>{
|
pub fn run_service_step(&self)->Result<(),mlua::Error>{
|
||||||
let render_stepped=super::instance::instance::dom_mut(&self.lua,|dom|{
|
let render_stepped_signal=super::instance::instance::dom_mut(&self.lua,|dom|{
|
||||||
let run_service=super::instance::instance::find_first_child_of_class(dom,dom.root(),"RunService").ok_or_else(||mlua::Error::runtime("RunService missing"))?;
|
let run_service=super::instance::instance::find_first_child_of_class(dom,dom.root(),"RunService").ok_or_else(||mlua::Error::runtime("RunService missing"))?;
|
||||||
super::instance::instance::instance_value_store_mut(&self.lua,|instance_value_store|{
|
Ok(match run_service.userdata.get(&rbx_dom_weak::ustr("RenderStepped")){
|
||||||
//unwrap because I trust my find_first_child_of_class function to
|
Some(render_stepped)=>Some(render_stepped.borrow::<super::script_signal::ScriptSignal>()?.clone()),
|
||||||
let mut instance_values=instance_value_store.get_or_create_instance_values(run_service).ok_or_else(||mlua::Error::runtime("RunService InstanceValues missing"))?;
|
None=>None
|
||||||
let render_stepped=instance_values.get_or_create_value(&self.lua,"RenderStepped")?;
|
|
||||||
//let stepped=instance_values.get_or_create_value(&self.lua,"Stepped")?;
|
|
||||||
//let heartbeat=instance_values.get_or_create_value(&self.lua,"Heartbeat")?;
|
|
||||||
Ok(render_stepped)
|
|
||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
if let Some(render_stepped)=render_stepped{
|
if let Some(render_stepped_signal)=render_stepped_signal{
|
||||||
let signal:&super::script_signal::ScriptSignal=&*render_stepped.borrow()?;
|
render_stepped_signal.fire(&mlua::MultiValue::new());
|
||||||
signal.fire(&mlua::MultiValue::new());
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
57
lib/roblox_emulator/src/types.rs
Normal file
57
lib/roblox_emulator/src/types.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
pub struct Instance{
|
||||||
|
pub(crate) instance:rbx_dom_weak::Instance,
|
||||||
|
pub(crate) userdata:rbx_dom_weak::UstrMap<mlua::AnyUserData>,
|
||||||
|
}
|
||||||
|
impl AsRef<rbx_dom_weak::Instance> for Instance{
|
||||||
|
fn as_ref(&self)->&rbx_dom_weak::Instance{
|
||||||
|
&self.instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsMut<rbx_dom_weak::Instance> for Instance{
|
||||||
|
fn as_mut(&mut self)->&mut rbx_dom_weak::Instance{
|
||||||
|
&mut self.instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Instance> for rbx_dom_weak::Instance{
|
||||||
|
fn from(instance:Instance)->Self{
|
||||||
|
instance.instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<rbx_dom_weak::Instance> for Instance{
|
||||||
|
fn from(instance:rbx_dom_weak::Instance)->Self{
|
||||||
|
Self{
|
||||||
|
instance,
|
||||||
|
userdata:Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type WeakDom=rbx_dom_weak::GenericWeakDom<Instance>;
|
||||||
|
|
||||||
|
// lazy-loaded per-instance userdata values
|
||||||
|
type CreateUserData=fn(&mlua::Lua)->mlua::Result<mlua::AnyUserData>;
|
||||||
|
type LUD=phf::Map<&'static str,// Class name
|
||||||
|
phf::Map<&'static str,// Value name
|
||||||
|
CreateUserData
|
||||||
|
>
|
||||||
|
>;
|
||||||
|
static LAZY_USER_DATA:LUD=phf::phf_map!{
|
||||||
|
"RunService"=>phf::phf_map!{
|
||||||
|
"RenderStepped"=>|lua|lua.create_any_userdata(crate::runner::script_signal::ScriptSignal::new()),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
impl Instance{
|
||||||
|
pub fn get_or_create_userdata(&mut self,lua:&mlua::Lua,index:rbx_dom_weak::Ustr)->mlua::Result<Option<mlua::AnyUserData>>{
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
|
Ok(match LAZY_USER_DATA.get(self.instance.class.as_str()){
|
||||||
|
Some(userdata_map)=>match self.userdata.entry(index){
|
||||||
|
Entry::Occupied(entry)=>Some(entry.get().clone()),
|
||||||
|
Entry::Vacant(entry)=>match userdata_map.get(index.as_str()){
|
||||||
|
Some(create_userdata)=>Some(entry.insert(create_userdata(lua)?).clone()),
|
||||||
|
None=>None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
None=>None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user