roblox_emulator: nil instances

This commit is contained in:
2025-04-22 16:05:34 -07:00
parent 03b16db10a
commit 8bb20ffe81
2 changed files with 42 additions and 16 deletions

View File

@@ -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<Services,ServicesError>{
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(<Ref as std::str::FromStr>::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<Context,ServicesError>{
// 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(<Ref as std::str::FromStr>::from_str("ffffffffffffffffffffffffffffffff").unwrap())
);
let services=Services{game,workspace};
let services=Services{game,workspace,nil};
Self{dom,services}
}
}

View File

@@ -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<Instance>)|{
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<Self>{
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<Instance>|{
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(())
})
);