diff --git a/src/runner/instance.rs b/src/runner/instance.rs index 688b7cc..d055567 100644 --- a/src/runner/instance.rs +++ b/src/runner/instance.rs @@ -9,6 +9,7 @@ use super::vector3::Vector3; 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()?; @@ -30,7 +31,7 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{ } // LMAO look at this function! -fn dom_mut(lua:&mlua::Lua,mut f:impl FnMut(&mut WeakDom)->mlua::Result)->mlua::Result{ +pub fn dom_mut(lua:&mlua::Lua,mut f:impl FnMut(&mut WeakDom)->mlua::Result)->mlua::Result{ let mut dom=lua.app_data_mut::<&'static mut WeakDom>().ok_or(mlua::Error::runtime("DataModel missing"))?; f(&mut *dom) } @@ -271,6 +272,17 @@ impl mlua::UserData for Instance{ })?{ return function.into_lua(lua); } + + //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) + } + })?{ + return value.into_lua(lua); + } //find a child with a matching name find_first_child(dom,instance,index_str) .map(|instance|Instance::new(instance.referent())) @@ -481,3 +493,59 @@ 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; +type LUD=phf::Map<&'static str,// Class name + phf::Map<&'static str,// Value name + CreateUserData + > +>; +static LAZY_USER_DATA:LUD=phf::phf_map!{ +}; +#[derive(Default)] +pub struct InstanceValueStore{ + values:HashMap + >, +} +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{ + 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>{ + 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(lua:&mlua::Lua,mut f:impl FnMut(&mut InstanceValueStore)->mlua::Result)->mlua::Result{ + let mut cf=lua.app_data_mut::().ok_or(mlua::Error::runtime("InstanceValueStore missing"))?; + f(&mut *cf) +}