roblox_emulator: use extended instances
This commit is contained in:
parent
bef16f65ff
commit
0a3825ca2d
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{
|
||||
class==superclass
|
||||
@ -19,7 +20,7 @@ impl Context{
|
||||
pub const fn new(dom:WeakDom)->Self{
|
||||
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")
|
||||
.with_property("Source",rbx_types::Variant::String(source));
|
||||
let script_ref=script.referent();
|
||||
@ -28,7 +29,7 @@ impl Context{
|
||||
.with_child(script)
|
||||
));
|
||||
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{
|
||||
unsafe{&*(dom as *const WeakDom as *const Context)}
|
||||
@ -39,24 +40,24 @@ impl Context{
|
||||
/// 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{
|
||||
self.dom.descendants().filter(|&instance|
|
||||
class_is_a(instance.class.as_ref(),superclass)
|
||||
).map(|instance|instance.referent())
|
||||
class_is_a(instance.as_ref().class.as_ref(),superclass)
|
||||
).map(|instance|instance.as_ref().referent())
|
||||
}
|
||||
pub fn scripts(&self)->Vec<crate::runner::instance::Instance>{
|
||||
self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::Instance::new).collect()
|
||||
pub fn scripts(&self)->Vec<crate::runner::instance::InstanceRef>{
|
||||
self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::InstanceRef::new).collect()
|
||||
}
|
||||
|
||||
pub fn find_services(&self)->Option<Services>{
|
||||
Some(Services{
|
||||
workspace:*self.dom.root().children().iter().find(|&&r|
|
||||
self.dom.get_by_ref(r).is_some_and(|instance|instance.class=="Workspace")
|
||||
workspace:*self.dom.root().as_ref().children().iter().find(|&&r|
|
||||
self.dom.get_by_ref(r).is_some_and(|instance|instance.as_ref().class=="Workspace")
|
||||
)?,
|
||||
game:self.dom.root_ref(),
|
||||
})
|
||||
}
|
||||
pub fn convert_into_place(&mut self)->Services{
|
||||
//snapshot root instances
|
||||
let children=self.dom.root().children().to_owned();
|
||||
let children=self.dom.root().as_ref().children().to_owned();
|
||||
|
||||
//insert services
|
||||
let game=self.dom.root_ref();
|
||||
@ -69,7 +70,7 @@ impl Context{
|
||||
);
|
||||
{
|
||||
//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));
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub mod types;
|
||||
pub mod runner;
|
||||
pub mod context;
|
||||
#[cfg(feature="run-service")]
|
||||
|
@ -2,25 +2,25 @@ use std::collections::{hash_map::Entry,HashMap};
|
||||
|
||||
use mlua::{FromLua,FromLuaMulti,IntoLua,IntoLuaMulti};
|
||||
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::types::{WeakDom,Instance};
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
//class functions store
|
||||
lua.set_app_data(ClassMethodsStore::default());
|
||||
lua.set_app_data(InstanceValueStore::default());
|
||||
|
||||
let instance_table=lua.create_table()?;
|
||||
|
||||
//Instance.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 parent=parent.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?;
|
||||
dom_mut(lua,|dom|{
|
||||
//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 pref=instance.parent();
|
||||
while let Some(parent)=dom.get_by_ref(pref){
|
||||
let parent=parent.as_ref();
|
||||
full_name.insert(0,'.');
|
||||
full_name.insert_str(0,parent.name.as_str());
|
||||
pref=parent.parent();
|
||||
@ -55,10 +57,10 @@ 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>{
|
||||
pub fn get_name_source(lua:&mlua::Lua,script:InstanceRef)->Result<(String,String),mlua::Error>{
|
||||
dom_mut(lua,|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(),
|
||||
_=>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>{
|
||||
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.name==name)
|
||||
pub fn find_first_child<'a>(dom:&'a WeakDom,instance:&Instance,name:&str)->Option<&'a Instance>{
|
||||
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>{
|
||||
dom.descendants_of(instance.referent()).find(|&inst|inst.name==name)
|
||||
pub fn find_first_descendant<'a>(dom:&'a WeakDom,instance:&Instance,name:&str)->Option<&'a Instance>{
|
||||
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>{
|
||||
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.class==class)
|
||||
pub fn find_first_child_of_class<'a>(dom:&'a WeakDom,instance:&Instance,class:&str)->Option<&'a Instance>{
|
||||
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>{
|
||||
dom.descendants_of(instance.referent()).find(|&inst|inst.class==class)
|
||||
pub fn find_first_descendant_of_class<'a>(dom:&'a WeakDom,instance:&Instance,class:&str)->Option<&'a Instance>{
|
||||
dom.descendants_of(instance.as_ref().referent()).find(|&inst|inst.as_ref().class==class)
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct Instance{
|
||||
pub struct InstanceRef{
|
||||
referent:Ref,
|
||||
}
|
||||
impl Instance{
|
||||
impl InstanceRef{
|
||||
pub const fn new(referent:Ref)->Self{
|
||||
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"))
|
||||
}
|
||||
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"))
|
||||
}
|
||||
}
|
||||
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){
|
||||
fields.add_field_method_get("Parent",|lua,this|{
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
Ok(Instance::new(instance.parent()))
|
||||
let instance=this.get(dom)?.as_ref();
|
||||
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"))?;
|
||||
dom_mut(lua,|dom|{
|
||||
dom.transfer_within(this.referent,parent.referent);
|
||||
@ -114,13 +116,13 @@ impl mlua::UserData for Instance{
|
||||
});
|
||||
fields.add_field_method_get("Name",|lua,this|{
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
let instance=this.get(dom)?.as_ref();
|
||||
Ok(instance.name.clone())
|
||||
})
|
||||
});
|
||||
fields.add_field_method_set("Name",|lua,this,val:mlua::String|{
|
||||
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?
|
||||
instance.name=val.to_str()?.to_owned();
|
||||
Ok(())
|
||||
@ -128,7 +130,7 @@ impl mlua::UserData for Instance{
|
||||
});
|
||||
fields.add_field_method_get("ClassName",|lua,this|{
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
let instance=this.get(dom)?.as_ref();
|
||||
Ok(instance.class.to_owned())
|
||||
})
|
||||
});
|
||||
@ -136,17 +138,17 @@ impl mlua::UserData for Instance{
|
||||
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
||||
methods.add_method("GetChildren",|lua,this,_:()|
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
let instance=this.get(dom)?.as_ref();
|
||||
let children:Vec<_>=instance
|
||||
.children()
|
||||
.iter()
|
||||
.copied()
|
||||
.map(Instance::new)
|
||||
.map(InstanceRef::new)
|
||||
.collect();
|
||||
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()?;
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
@ -156,7 +158,7 @@ impl mlua::UserData for Instance{
|
||||
false=>find_first_child(dom,instance,name_str),
|
||||
}
|
||||
.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),
|
||||
}
|
||||
.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
|
||||
.descendants_of(this.referent)
|
||||
.map(|instance|
|
||||
Instance::new(instance.referent())
|
||||
InstanceRef::new(instance.as_ref().referent())
|
||||
)
|
||||
.collect();
|
||||
Ok(children)
|
||||
@ -190,7 +192,7 @@ impl mlua::UserData for Instance{
|
||||
);
|
||||
methods.add_method("IsA",|lua,this,classname:mlua::String|
|
||||
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()?))
|
||||
})
|
||||
);
|
||||
@ -200,39 +202,40 @@ impl mlua::UserData for Instance{
|
||||
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|{
|
||||
let instance=this.get(dom)?;
|
||||
let instance=this.get(dom)?.as_ref();
|
||||
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_ustr=ustr(index_str);
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
let instance_ref=instance.as_ref();
|
||||
//println!("__index t={} i={index:?}",instance.name);
|
||||
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
|
||||
match instance.properties.get(&index_ustr)
|
||||
match instance_ref.properties.get(&index_ustr)
|
||||
.cloned()
|
||||
//Find default value
|
||||
.or_else(||db.find_default_property(class,index_str).cloned())
|
||||
//Find virtual property
|
||||
.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::Int64(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::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::Vector3(v))=>return Into::<crate::runner::vector3::Vector3>::into(v).into_lua(lua),
|
||||
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
|
||||
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
|
||||
if let Some(value)=instance_value_store_mut(lua,|ivs|{
|
||||
//TODO: walk class tree somehow
|
||||
match ivs.get_or_create_instance_values(&instance){
|
||||
Some(mut instance_values)=>instance_values.get_or_create_value(lua,index_str),
|
||||
None=>Ok(None)
|
||||
}
|
||||
})?{
|
||||
let instance=this.get_mut(dom)?;
|
||||
if let Some(value)=instance.get_or_create_userdata(lua,index_ustr)?{
|
||||
return value.into_lua(lua);
|
||||
}
|
||||
// drop mutable borrow
|
||||
//find a child with a matching name
|
||||
let instance=this.get(dom)?;
|
||||
find_first_child(dom,instance,index_str)
|
||||
.map(|instance|Instance::new(instance.referent()))
|
||||
.map(|instance|InstanceRef::new(instance.as_ref().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):(InstanceRef,mlua::String,mlua::Value)|{
|
||||
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);
|
||||
let index_str=&*index.to_str()?;
|
||||
let index_ustr=ustr(index_str);
|
||||
@ -331,7 +331,7 @@ impl mlua::UserData for Instance{
|
||||
macro_rules! cf{
|
||||
($f:expr)=>{
|
||||
|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)
|
||||
}
|
||||
};
|
||||
@ -354,13 +354,13 @@ static CLASS_FUNCTION_DATABASE:CFD=phf::phf_map!{
|
||||
match service{
|
||||
"Lighting"|"RunService"=>{
|
||||
let referent=find_first_child_of_class(dom,dom.root(),service)
|
||||
.map(|instance|instance.referent())
|
||||
.map(|instance|instance.as_ref().referent())
|
||||
.unwrap_or_else(||
|
||||
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
|
||||
(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 use instance::Instance;
|
||||
pub use instance::InstanceRef;
|
||||
|
@ -7,7 +7,7 @@ mod color3;
|
||||
mod cframe;
|
||||
mod vector3;
|
||||
pub mod instance;
|
||||
mod script_signal;
|
||||
pub mod script_signal;
|
||||
mod color_sequence;
|
||||
mod number_sequence;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use crate::context::Context;
|
||||
#[cfg(feature="run-service")]
|
||||
use crate::scheduler::scheduler_mut;
|
||||
use crate::types::WeakDom;
|
||||
|
||||
pub struct Runner{
|
||||
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>{
|
||||
{
|
||||
let globals=self.lua.globals();
|
||||
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)?;
|
||||
globals.set("game",super::instance::InstanceRef::new(services.game)).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
|
||||
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")]
|
||||
self.lua.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default());
|
||||
Ok(Runnable{
|
||||
@ -81,14 +82,14 @@ pub struct Runnable<'a>{
|
||||
}
|
||||
impl Runnable<'_>{
|
||||
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")]
|
||||
self.lua.remove_app_data::<crate::scheduler::Scheduler>();
|
||||
Runner{
|
||||
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)?;
|
||||
self.lua.globals().raw_set("script",script).map_err(Error::RustLua)?;
|
||||
let f=self.lua.load(source.as_str())
|
||||
@ -123,20 +124,15 @@ impl Runnable<'_>{
|
||||
}
|
||||
#[cfg(feature="run-service")]
|
||||
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"))?;
|
||||
super::instance::instance::instance_value_store_mut(&self.lua,|instance_value_store|{
|
||||
//unwrap because I trust my find_first_child_of_class function to
|
||||
let mut instance_values=instance_value_store.get_or_create_instance_values(run_service).ok_or_else(||mlua::Error::runtime("RunService InstanceValues missing"))?;
|
||||
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)
|
||||
Ok(match run_service.userdata.get(&rbx_dom_weak::ustr("RenderStepped")){
|
||||
Some(render_stepped)=>Some(render_stepped.borrow::<super::script_signal::ScriptSignal>()?.clone()),
|
||||
None=>None
|
||||
})
|
||||
})?;
|
||||
if let Some(render_stepped)=render_stepped{
|
||||
let signal:&super::script_signal::ScriptSignal=&*render_stepped.borrow()?;
|
||||
signal.fire(&mlua::MultiValue::new());
|
||||
if let Some(render_stepped_signal)=render_stepped_signal{
|
||||
render_stepped_signal.fire(&mlua::MultiValue::new());
|
||||
}
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user