diff --git a/lib/roblox_emulator/src/context.rs b/lib/roblox_emulator/src/context.rs index 5e07f7c1..f2074719 100644 --- a/lib/roblox_emulator/src/context.rs +++ b/lib/roblox_emulator/src/context.rs @@ -7,21 +7,30 @@ fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{ #[derive(Debug)] pub enum ServicesError{ - Workspace, + WorkspaceNotFound, } pub struct Services{ pub(crate) game:Ref, pub(crate) workspace:Ref, + pub(crate) nil:Ref, } impl Services{ fn get_or_create_services(dom:&mut WeakDom)->Result{ + let game=dom.root_ref(); + // error if workspace does not exist + let workspace=*dom.root().children().iter().find(|&&r| + dom.get_by_ref(r).is_some_and(|instance|instance.class=="Workspace") + ).ok_or(ServicesError::WorkspaceNotFound)?; + // the root nil instance does not have a parent + let nil=dom.insert(Ref::none(),InstanceBuilder::new("Nil") + .with_referent(::from_str("ffffffffffffffffffffffffffffffff").unwrap()) + ); Ok(Services{ - workspace:*dom.root().children().iter().find(|&&r| - dom.get_by_ref(r).is_some_and(|instance|instance.class=="Workspace") - ).ok_or(ServicesError::Workspace)?, - game:dom.root_ref(), + game, + workspace, + nil, }) } } @@ -34,7 +43,6 @@ pub struct Context{ impl Context{ pub fn from_place(mut dom:WeakDom)->Result{ - // TODO: create nil instances let services=Services::get_or_create_services(&mut dom)?; Ok(Self{dom,services}) } @@ -77,6 +85,12 @@ impl Context{ .with_property("Terrain",terrain_bldr.referent()) .with_child(terrain_bldr) ); + + //transfer original root instances into workspace + for instance in children{ + dom.transfer_within(instance,workspace); + } + { //Lowercase and upper case workspace property! let game=dom.root_mut(); @@ -84,14 +98,15 @@ impl Context{ game.properties.insert(static_ustr("workspace"),rbx_types::Variant::Ref(workspace)); game.properties.insert(static_ustr("Workspace"),rbx_types::Variant::Ref(workspace)); } + dom.insert(game,InstanceBuilder::new("Lighting")); - //transfer original root instances into workspace - for instance in children{ - dom.transfer_within(instance,workspace); - } + // the root nil instance does not have a parent + let nil=dom.insert(Ref::none(),InstanceBuilder::new("Nil") + .with_referent(::from_str("ffffffffffffffffffffffffffffffff").unwrap()) + ); - let services=Services{game,workspace}; + let services=Services{game,workspace,nil}; Self{dom,services} } } diff --git a/lib/roblox_emulator/src/runner/instance/instance.rs b/lib/roblox_emulator/src/runner/instance/instance.rs index b8674c98..eba38117 100644 --- a/lib/roblox_emulator/src/runner/instance/instance.rs +++ b/lib/roblox_emulator/src/runner/instance/instance.rs @@ -22,9 +22,8 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{ instance_table.raw_set("new", lua.create_function(|lua,(class_name,parent):(mlua::String,Option)|{ 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.unwrap_or(Instance::nil()); dom_mut(lua,|dom|{ - //TODO: Nil instances Ok(Instance::new(dom.insert(parent.referent,InstanceBuilder::new(class_name_str)))) }) })? @@ -99,6 +98,18 @@ impl Instance{ pub const fn new(referent:Ref)->Self{ Self{referent} } + pub fn maybe_nil(referent:Ref)->Option{ + match referent=="ffffffffffffffffffffffffffffffff".parse().unwrap(){ + true=>None, + false=>Some(Self{referent}) + } + } + // Using a sentinel value as a ref for nil instances because global variables are hard. + pub fn nil()->Self{ + Self{ + referent:"ffffffffffffffffffffffffffffffff".parse().unwrap() + } + } pub fn get<'a>(&self,dom:&'a WeakDom)->mlua::Result<&'a rbx_dom_weak::Instance>{ dom.get_by_ref(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing")) } @@ -113,11 +124,11 @@ impl mlua::UserData for Instance{ fields.add_field_method_get("Parent",|lua,this|{ dom_mut(lua,|dom|{ let instance=this.get(dom)?; - Ok(Instance::new(instance.parent())) + Ok(Instance::maybe_nil(instance.parent())) }) }); fields.add_field_method_set("Parent",|lua,this,val:Option|{ - let parent=val.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?; + let parent=val.unwrap_or(Instance::nil()); dom_mut(lua,|dom|{ dom.transfer_within(this.referent,parent.referent); Ok(()) @@ -207,7 +218,7 @@ impl mlua::UserData for Instance{ ); methods.add_method("Destroy",|lua,this,()| dom_mut(lua,|dom|{ - dom.destroy(this.referent); + dom.transfer_within(this.referent,Instance::nil().referent); Ok(()) }) );