roblox_emulator: use extended instances

This commit is contained in:
Quaternions 2025-04-17 18:05:33 -07:00
parent 05bbe05979
commit bb8e131464
Signed by: Quaternions
GPG Key ID: D0DF5964F79AC131
2 changed files with 27 additions and 63 deletions
lib/roblox_emulator/src/runner

@ -15,7 +15,6 @@ fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
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 table=lua.create_table()?; let table=lua.create_table()?;
@ -329,16 +328,13 @@ 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)=get_or_create_userdata(instance,lua,index_str)?{
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_unchecked(instance.referent())) .map(|instance|Instance::new_unchecked(instance.referent()))
.into_lua(lua) .into_lua(lua)
@ -585,8 +581,6 @@ fn find_virtual_property(
} }
// lazy-loaded per-instance userdata values // 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 CreateUserData=fn(&mlua::Lua)->mlua::Result<mlua::AnyUserData>;
type LUD=phf::Map<&'static str,// Class name type LUD=phf::Map<&'static str,// Class name
phf::Map<&'static str,// Value name phf::Map<&'static str,// Value name
@ -616,47 +610,22 @@ static LAZY_USER_DATA:LUD=phf::phf_map!{
"DescendantRemoved"=>create_script_signal, "DescendantRemoved"=>create_script_signal,
}, },
}; };
#[derive(Default)] fn get_or_create_userdata(instance:&mut rbx_dom_weak::Instance,lua:&mlua::Lua,index:&str)->mlua::Result<Option<mlua::AnyUserData>>{
pub struct InstanceValueStore{ use std::collections::hash_map::Entry;
values:HashMap<Ref, let db=rbx_reflection_database::get();
HashMap<&'static str, let Some(class)=db.classes.get(instance.class.as_str())else{
mlua::AnyUserData return Ok(None)
> };
>, if let Some((&static_str,create_userdata))=db.superclasses_iter(class).find_map(|superclass|
} // find pair (class,index)
pub struct InstanceValues<'a>{ LAZY_USER_DATA.get(&superclass.name)
named_values:&'static phf::Map<&'static str,CreateUserData>, .and_then(|map|map.get_entry(index))
values:&'a mut HashMap<&'static str,mlua::AnyUserData>, ){
} let index_ustr=static_ustr(static_str);
impl InstanceValueStore{ return Ok(Some(match instance.userdata.entry(index_ustr){
pub fn get_or_create_instance_values(&mut self,instance:&rbx_dom_weak::Instance)->Option<InstanceValues>{ Entry::Occupied(entry)=>entry.get().clone(),
LAZY_USER_DATA.get(instance.class.as_str()) Entry::Vacant(entry)=>entry.insert(create_userdata(lua)?).clone(),
.map(|named_values| }));
InstanceValues{
named_values,
values:self.values.entry(instance.referent())
.or_insert_with(||HashMap::new()),
}
)
} }
} Ok(None)
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)
} }

@ -123,20 +123,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(())
} }