Compare commits

...

4 Commits
master ... emu

13 changed files with 196 additions and 249 deletions

74
Cargo.lock generated

@ -1409,7 +1409,7 @@ dependencies = [
"js-sys", "js-sys",
"log", "log",
"wasm-bindgen", "wasm-bindgen",
"windows-core 0.61.0", "windows-core",
] ]
[[package]] [[package]]
@ -1843,7 +1843,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets 0.52.6", "windows-targets 0.48.5",
] ]
[[package]] [[package]]
@ -3098,9 +3098,9 @@ dependencies = [
[[package]] [[package]]
name = "rbx_binary" name = "rbx_binary"
version = "1.0.0" version = "1.1.0-sn3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "9573fee5e073d7b303f475c285197fdc8179468de66ca60ee115a58fbac99296" checksum = "f3f4cd69381a3631e117ee9542f5f8d9a2ab3b5dd8c7bdc7eb1680cc805233bc"
dependencies = [ dependencies = [
"ahash", "ahash",
"log", "log",
@ -3115,9 +3115,9 @@ dependencies = [
[[package]] [[package]]
name = "rbx_dom_weak" name = "rbx_dom_weak"
version = "3.0.0" version = "3.1.0-sn1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "04425cf6e9376e5486f4fb35906c120d1b1b45618a490318cf563fab1fa230a9" checksum = "c449e42d26c5af9d50e036219513e7a3e3bd0bc31fa32b74b82d9d4d481d74a5"
dependencies = [ dependencies = [
"ahash", "ahash",
"rbx_types", "rbx_types",
@ -3175,9 +3175,9 @@ dependencies = [
[[package]] [[package]]
name = "rbx_xml" name = "rbx_xml"
version = "1.0.0" version = "1.1.0-sn3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "bb623833c31cc43bbdaeb32f5e91db8ecd63fc46e438d0d268baf9e61539cf1c" checksum = "ce7d7ec62a3dc88179a61efa4827c36887a1b2bcbc258aec58de0c5149c565eb"
dependencies = [ dependencies = [
"ahash", "ahash",
"base64 0.13.1", "base64 0.13.1",
@ -4870,7 +4870,7 @@ dependencies = [
"web-sys", "web-sys",
"wgpu-types", "wgpu-types",
"windows", "windows",
"windows-core 0.58.0", "windows-core",
] ]
[[package]] [[package]]
@ -4902,7 +4902,7 @@ version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [ dependencies = [
"windows-core 0.58.0", "windows-core",
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
@ -4912,26 +4912,13 @@ version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [ dependencies = [
"windows-implement 0.58.0", "windows-implement",
"windows-interface 0.58.0", "windows-interface",
"windows-result 0.2.0", "windows-result 0.2.0",
"windows-strings 0.1.0", "windows-strings 0.1.0",
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "windows-core"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-implement 0.60.0",
"windows-interface 0.59.1",
"windows-link",
"windows-result 0.3.2",
"windows-strings 0.4.0",
]
[[package]] [[package]]
name = "windows-implement" name = "windows-implement"
version = "0.58.0" version = "0.58.0"
@ -4943,17 +4930,6 @@ dependencies = [
"syn 2.0.100", "syn 2.0.100",
] ]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]] [[package]]
name = "windows-interface" name = "windows-interface"
version = "0.58.0" version = "0.58.0"
@ -4965,17 +4941,6 @@ dependencies = [
"syn 2.0.100", "syn 2.0.100",
] ]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]] [[package]]
name = "windows-link" name = "windows-link"
version = "0.1.1" version = "0.1.1"
@ -5030,15 +4995,6 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "windows-strings"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97"
dependencies = [
"windows-link",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.45.0" version = "0.45.0"

@ -13,12 +13,12 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
bytemuck = "1.14.3" bytemuck = "1.14.3"
glam = "0.30.0" glam = "0.30.0"
lazy-regex = "3.1.0" lazy-regex = "3.1.0"
rbx_binary = "1.0.0" rbx_binary = { version = "1.1.0-sn3", registry = "strafesnet"}
rbx_dom_weak = "3.0.0" rbx_dom_weak = { version = "3.1.0-sn1", registry = "strafesnet"}
rbx_mesh = "0.3.1" rbx_mesh = "0.3.1"
rbx_reflection_database = "1.0.0" rbx_reflection_database = "1.0.0"
rbx_xml = "1.0.0" rbx_xml = { version = "1.1.0-sn3", registry = "strafesnet"}
rbxassetid = { version = "0.1.0", path = "../rbxassetid", registry = "strafesnet" } rbxassetid = { version = "0.1.0", path = "../rbxassetid", registry = "strafesnet" }
roblox_emulator = { version = "0.4.7", path = "../roblox_emulator", registry = "strafesnet" } roblox_emulator = { version = "0.4.7", path = "../roblox_emulator", default-features = false, registry = "strafesnet" }
strafesnet_common = { version = "0.6.0", path = "../common", registry = "strafesnet" } strafesnet_common = { version = "0.6.0", path = "../common", registry = "strafesnet" }
strafesnet_deferred_loader = { version = "0.5.0", path = "../deferred_loader", registry = "strafesnet" } strafesnet_deferred_loader = { version = "0.5.0", path = "../deferred_loader", registry = "strafesnet" }

@ -1,5 +1,5 @@
use std::io::Read; use std::io::Read;
use rbx_dom_weak::WeakDom; use roblox_emulator::types::WeakDom;
use strafesnet_deferred_loader::deferred_loader::{LoadFailureMode,MeshDeferredLoader,RenderConfigDeferredLoader}; use strafesnet_deferred_loader::deferred_loader::{LoadFailureMode,MeshDeferredLoader,RenderConfigDeferredLoader};
mod rbx; mod rbx;
@ -95,8 +95,8 @@ pub fn read<R:Read>(input:R)->Result<Model,ReadError>{
let mut buf=std::io::BufReader::new(input); let mut buf=std::io::BufReader::new(input);
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?; let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
match &peek[0..8]{ match &peek[0..8]{
b"<roblox!"=>rbx_binary::from_reader(buf).map(Model::new).map_err(ReadError::RbxBinary), b"<roblox!"=>rbx_binary::from_reader_generic(buf).map(Model::new).map_err(ReadError::RbxBinary),
b"<roblox "=>rbx_xml::from_reader_default(buf).map(Model::new).map_err(ReadError::RbxXml), b"<roblox "=>rbx_xml::from_reader_generic(buf,Default::default()).map(Model::new).map_err(ReadError::RbxXml),
_=>Err(ReadError::UnknownFileFormat), _=>Err(ReadError::UnknownFileFormat),
} }
} }

@ -2,6 +2,7 @@ use std::collections::HashMap;
use crate::loader::MeshIndex; use crate::loader::MeshIndex;
use crate::primitives::{self,CubeFace,CubeFaceDescription,WedgeFaceDescription,CornerWedgeFaceDescription,FaceDescription,Primitives}; use crate::primitives::{self,CubeFace,CubeFaceDescription,WedgeFaceDescription,CornerWedgeFaceDescription,FaceDescription,Primitives};
use rbx_dom_weak::ustr; use rbx_dom_weak::ustr;
use roblox_emulator::types::{WeakDom,Instance};
use strafesnet_common::aabb::Aabb; use strafesnet_common::aabb::Aabb;
use strafesnet_common::map; use strafesnet_common::map;
use strafesnet_common::model; use strafesnet_common::model;
@ -14,30 +15,23 @@ use strafesnet_deferred_loader::deferred_loader::{RenderConfigDeferredLoader,Mes
use strafesnet_deferred_loader::mesh::Meshes; use strafesnet_deferred_loader::mesh::Meshes;
use strafesnet_deferred_loader::texture::{RenderConfigs,Texture}; use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
fn class_is_a(class: &str, superclass: &str) -> bool { fn recursive_collect_superclass(
if class==superclass { objects:&mut std::vec::Vec<rbx_dom_weak::types::Ref>,
return true dom:&WeakDom,
} instance:&Instance,
let class_descriptor=rbx_reflection_database::get().classes.get(class); superclass:&str
if let Some(descriptor) = &class_descriptor { ){
if let Some(class_super) = &descriptor.superclass { let instance=instance.as_ref();
return class_is_a(&class_super, superclass) let db=rbx_reflection_database::get();
} let Some(superclass)=db.classes.get(superclass)else{
} return;
false };
} objects.extend(
fn recursive_collect_superclass(objects: &mut std::vec::Vec<rbx_dom_weak::types::Ref>,dom: &rbx_dom_weak::WeakDom, instance: &rbx_dom_weak::Instance, superclass: &str){ dom.descendants_of(instance.as_ref().referent()).filter_map(|instance|{
let mut stack=vec![instance]; let class=db.classes.get(instance.as_ref().class.as_str())?;
while let Some(item)=stack.pop(){ db.has_superclass(class,superclass).then(||instance.as_ref().referent())
for &referent in item.children(){ })
if let Some(c)=dom.get_by_ref(referent){ );
if class_is_a(c.class.as_str(),superclass){
objects.push(c.referent());//copy ref
}
stack.push(c);
}
}
}
} }
fn planar64_affine3_from_roblox(cf:&rbx_dom_weak::types::CFrame,size:&rbx_dom_weak::types::Vector3)->Planar64Affine3{ fn planar64_affine3_from_roblox(cf:&rbx_dom_weak::types::CFrame,size:&rbx_dom_weak::types::Vector3)->Planar64Affine3{
Planar64Affine3::new( Planar64Affine3::new(
@ -405,19 +399,20 @@ fn get_content_url(content:&rbx_dom_weak::types::Content)->Option<&str>{
fn get_texture_description<'a>( fn get_texture_description<'a>(
temp_objects:&mut Vec<rbx_dom_weak::types::Ref>, temp_objects:&mut Vec<rbx_dom_weak::types::Ref>,
render_config_deferred_loader:&mut RenderConfigDeferredLoader<&'a str>, render_config_deferred_loader:&mut RenderConfigDeferredLoader<&'a str>,
dom:&'a rbx_dom_weak::WeakDom, dom:&'a WeakDom,
object:&rbx_dom_weak::Instance, object:&Instance,
size:&rbx_dom_weak::types::Vector3, size:&rbx_dom_weak::types::Vector3,
)->RobloxPartDescription{ )->RobloxPartDescription{
//use the biggest one and cut it down later... //use the biggest one and cut it down later...
let mut part_texture_description=RobloxPartDescription::default(); let mut part_texture_description=RobloxPartDescription::default();
temp_objects.clear(); temp_objects.clear();
recursive_collect_superclass(temp_objects,&dom,object,"Decal"); recursive_collect_superclass(temp_objects,dom,object,"Decal");
for &mut decal_ref in temp_objects{ for &mut decal_ref in temp_objects{
let Some(decal)=dom.get_by_ref(decal_ref) else{ let Some(decal)=dom.get_by_ref(decal_ref) else{
println!("Decal get_by_ref failed"); println!("Decal get_by_ref failed");
continue; continue;
}; };
let decal=decal.as_ref();
let ( let (
Some(rbx_dom_weak::types::Variant::ContentId(content)), Some(rbx_dom_weak::types::Variant::ContentId(content)),
Some(rbx_dom_weak::types::Variant::Enum(normalid)), Some(rbx_dom_weak::types::Variant::Enum(normalid)),
@ -521,7 +516,7 @@ struct GetAttributesArgs<'a>{
velocity:Planar64Vec3, velocity:Planar64Vec3,
} }
pub fn convert<'a>( pub fn convert<'a>(
dom:&'a rbx_dom_weak::WeakDom, dom:&'a WeakDom,
render_config_deferred_loader:&mut RenderConfigDeferredLoader<&'a str>, render_config_deferred_loader:&mut RenderConfigDeferredLoader<&'a str>,
mesh_deferred_loader:&mut MeshDeferredLoader<MeshIndex<'a>>, mesh_deferred_loader:&mut MeshDeferredLoader<MeshIndex<'a>>,
)->PartialMap1<'a>{ )->PartialMap1<'a>{
@ -536,9 +531,10 @@ pub fn convert<'a>(
let mut object_refs=Vec::new(); let mut object_refs=Vec::new();
let mut temp_objects=Vec::new(); let mut temp_objects=Vec::new();
recursive_collect_superclass(&mut object_refs, &dom, dom.root(),"BasePart"); recursive_collect_superclass(&mut object_refs,dom,dom.root(),"BasePart");
for object_ref in object_refs { for object_ref in object_refs{
if let Some(object)=dom.get_by_ref(object_ref){ if let Some(object_extended)=dom.get_by_ref(object_ref){
let object=object_extended.as_ref();
if let ( if let (
Some(rbx_dom_weak::types::Variant::CFrame(cf)), Some(rbx_dom_weak::types::Variant::CFrame(cf)),
Some(rbx_dom_weak::types::Variant::Vector3(size)), Some(rbx_dom_weak::types::Variant::Vector3(size)),
@ -561,6 +557,7 @@ pub fn convert<'a>(
let mut parent_ref=object.parent(); let mut parent_ref=object.parent();
let mut full_path=object.name.clone(); let mut full_path=object.name.clone();
while let Some(parent)=dom.get_by_ref(parent_ref){ while let Some(parent)=dom.get_by_ref(parent_ref){
let parent=parent.as_ref();
full_path=format!("{}.{}",parent.name,full_path); full_path=format!("{}.{}",parent.name,full_path);
parent_ref=parent.parent(); parent_ref=parent.parent();
} }
@ -590,7 +587,7 @@ pub fn convert<'a>(
let (availability,mesh_id)=match shape{ let (availability,mesh_id)=match shape{
Shape::Primitive(primitive_shape)=>{ Shape::Primitive(primitive_shape)=>{
//TODO: TAB TAB //TODO: TAB TAB
let part_texture_description=get_texture_description(&mut temp_objects,render_config_deferred_loader,dom,object,size); let part_texture_description=get_texture_description(&mut temp_objects,render_config_deferred_loader,dom,object_extended,size);
//obscure rust syntax "slice pattern" //obscure rust syntax "slice pattern"
let RobloxPartDescription([ let RobloxPartDescription([
f0,//Cube::Right f0,//Cube::Right
@ -671,7 +668,7 @@ pub fn convert<'a>(
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(&ustr("PhysicsData")){ if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(&ustr("PhysicsData")){
physics_data=data.as_ref(); physics_data=data.as_ref();
} }
let part_texture_description=get_texture_description(&mut temp_objects,render_config_deferred_loader,dom,object,size); let part_texture_description=get_texture_description(&mut temp_objects,render_config_deferred_loader,dom,object_extended,size);
let mesh_index=MeshIndex::union(content,mesh_data,physics_data,size,part_texture_description.clone()); let mesh_index=MeshIndex::union(content,mesh_data,physics_data,size,part_texture_description.clone());
let mesh_id=mesh_deferred_loader.acquire_mesh_id(mesh_index); let mesh_id=mesh_deferred_loader.acquire_mesh_id(mesh_index);
(MeshAvailability::DeferredUnion(part_texture_description),mesh_id) (MeshAvailability::DeferredUnion(part_texture_description),mesh_id)

@ -15,7 +15,7 @@ run-service=[]
glam = "0.30.0" glam = "0.30.0"
mlua = { version = "0.10.1", features = ["luau"] } mlua = { version = "0.10.1", features = ["luau"] }
phf = { version = "0.11.2", features = ["macros"] } phf = { version = "0.11.2", features = ["macros"] }
rbx_dom_weak = "3.0.0" rbx_dom_weak = { version = "3.1.0-sn1", registry = "strafesnet"}
rbx_reflection = "5.0.0" rbx_reflection = "5.0.0"
rbx_reflection_database = "1.0.0" rbx_reflection_database = "1.0.0"
rbx_types = "2.0.0" rbx_types = "2.0.0"

@ -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{ pub fn class_is_a(class:&str,superclass:&str)->bool{
class==superclass class==superclass
@ -19,7 +20,7 @@ impl Context{
pub const fn new(dom:WeakDom)->Self{ pub const fn new(dom:WeakDom)->Self{
Self{dom} 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") let script=InstanceBuilder::new("Script")
.with_property("Source",rbx_types::Variant::String(source)); .with_property("Source",rbx_types::Variant::String(source));
let script_ref=script.referent(); let script_ref=script.referent();
@ -28,7 +29,7 @@ impl Context{
.with_child(script) .with_child(script)
)); ));
let services=context.convert_into_place(); 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{ pub fn from_ref(dom:&WeakDom)->&Context{
unsafe{&*(dom as *const WeakDom as *const 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. /// 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{ pub fn superclass_iter<'a>(&'a self,superclass:&'a str)->impl Iterator<Item=Ref>+'a{
self.dom.descendants().filter(|&instance| self.dom.descendants().filter(|&instance|
class_is_a(instance.class.as_ref(),superclass) class_is_a(instance.as_ref().class.as_ref(),superclass)
).map(|instance|instance.referent()) ).map(|instance|instance.as_ref().referent())
} }
pub fn scripts(&self)->Vec<crate::runner::instance::Instance>{ pub fn scripts(&self)->Vec<crate::runner::instance::InstanceRef>{
self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::Instance::new).collect() self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::InstanceRef::new).collect()
} }
pub fn find_services(&self)->Option<Services>{ pub fn find_services(&self)->Option<Services>{
Some(Services{ Some(Services{
workspace:*self.dom.root().children().iter().find(|&&r| workspace:*self.dom.root().as_ref().children().iter().find(|&&r|
self.dom.get_by_ref(r).is_some_and(|instance|instance.class=="Workspace") self.dom.get_by_ref(r).is_some_and(|instance|instance.as_ref().class=="Workspace")
)?, )?,
game:self.dom.root_ref(), game:self.dom.root_ref(),
}) })
} }
pub fn convert_into_place(&mut self)->Services{ pub fn convert_into_place(&mut self)->Services{
//snapshot root instances //snapshot root instances
let children=self.dom.root().children().to_owned(); let children=self.dom.root().as_ref().children().to_owned();
//insert services //insert services
let game=self.dom.root_ref(); let game=self.dom.root_ref();
@ -69,7 +70,7 @@ impl Context{
); );
{ {
//Lowercase and upper case workspace property! //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));
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 runner;
pub mod context; pub mod context;
#[cfg(feature="run-service")] #[cfg(feature="run-service")]

@ -2,25 +2,25 @@ use std::collections::{hash_map::Entry,HashMap};
use mlua::{FromLua,FromLuaMulti,IntoLua,IntoLuaMulti}; use mlua::{FromLua,FromLuaMulti,IntoLua,IntoLuaMulti};
use rbx_types::Ref; 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::runner::vector3::Vector3;
use crate::types::{WeakDom,Instance};
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 instance_table=lua.create_table()?; let instance_table=lua.create_table()?;
//Instance.new //Instance.new
instance_table.raw_set("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 class_name_str=&*class_name.to_str()?;
let parent=parent.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?; let parent=parent.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?;
dom_mut(lua,|dom|{ dom_mut(lua,|dom|{
//TODO: Nil instances //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 full_name=instance.name.clone();
let mut pref=instance.parent(); let mut pref=instance.parent();
while let Some(parent)=dom.get_by_ref(pref){ while let Some(parent)=dom.get_by_ref(pref){
let parent=parent.as_ref();
full_name.insert(0,'.'); full_name.insert(0,'.');
full_name.insert_str(0,parent.name.as_str()); full_name.insert_str(0,parent.name.as_str());
pref=parent.parent(); pref=parent.parent();
@ -55,10 +57,10 @@ fn get_full_name(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->S
full_name full_name
} }
//helper function for script //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|{ dom_mut(lua,|dom|{
let instance=script.get(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(), Some(rbx_dom_weak::types::Variant::String(s))=>s.clone(),
_=>Err(mlua::Error::external("Missing script.Source"))?, _=>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>{ pub fn find_first_child<'a>(dom:&'a WeakDom,instance:&Instance,name:&str)->Option<&'a Instance>{
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.name==name) 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>{ pub fn find_first_descendant<'a>(dom:&'a WeakDom,instance:&Instance,name:&str)->Option<&'a Instance>{
dom.descendants_of(instance.referent()).find(|&inst|inst.name==name) 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>{ pub fn find_first_child_of_class<'a>(dom:&'a WeakDom,instance:&Instance,class:&str)->Option<&'a Instance>{
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.class==class) 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>{ pub fn find_first_descendant_of_class<'a>(dom:&'a WeakDom,instance:&Instance,class:&str)->Option<&'a Instance>{
dom.descendants_of(instance.referent()).find(|&inst|inst.class==class) dom.descendants_of(instance.as_ref().referent()).find(|&inst|inst.as_ref().class==class)
} }
#[derive(Clone,Copy)] #[derive(Clone,Copy)]
pub struct Instance{ pub struct InstanceRef{
referent:Ref, referent:Ref,
} }
impl Instance{ impl InstanceRef{
pub const fn new(referent:Ref)->Self{ pub const fn new(referent:Ref)->Self{
Self{referent} 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")) 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")) 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){ fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
fields.add_field_method_get("Parent",|lua,this|{ fields.add_field_method_get("Parent",|lua,this|{
dom_mut(lua,|dom|{ dom_mut(lua,|dom|{
let instance=this.get(dom)?; let instance=this.get(dom)?.as_ref();
Ok(Instance::new(instance.parent())) 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"))?; let parent=val.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?;
dom_mut(lua,|dom|{ dom_mut(lua,|dom|{
dom.transfer_within(this.referent,parent.referent); dom.transfer_within(this.referent,parent.referent);
@ -114,13 +116,13 @@ impl mlua::UserData for Instance{
}); });
fields.add_field_method_get("Name",|lua,this|{ fields.add_field_method_get("Name",|lua,this|{
dom_mut(lua,|dom|{ dom_mut(lua,|dom|{
let instance=this.get(dom)?; let instance=this.get(dom)?.as_ref();
Ok(instance.name.clone()) Ok(instance.name.clone())
}) })
}); });
fields.add_field_method_set("Name",|lua,this,val:mlua::String|{ fields.add_field_method_set("Name",|lua,this,val:mlua::String|{
dom_mut(lua,|dom|{ 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? //Why does this need to be cloned?
instance.name=val.to_str()?.to_owned(); instance.name=val.to_str()?.to_owned();
Ok(()) Ok(())
@ -128,7 +130,7 @@ impl mlua::UserData for Instance{
}); });
fields.add_field_method_get("ClassName",|lua,this|{ fields.add_field_method_get("ClassName",|lua,this|{
dom_mut(lua,|dom|{ dom_mut(lua,|dom|{
let instance=this.get(dom)?; let instance=this.get(dom)?.as_ref();
Ok(instance.class.to_owned()) Ok(instance.class.to_owned())
}) })
}); });
@ -136,17 +138,17 @@ impl mlua::UserData for Instance{
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){ fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
methods.add_method("GetChildren",|lua,this,_:()| methods.add_method("GetChildren",|lua,this,_:()|
dom_mut(lua,|dom|{ dom_mut(lua,|dom|{
let instance=this.get(dom)?; let instance=this.get(dom)?.as_ref();
let children:Vec<_>=instance let children:Vec<_>=instance
.children() .children()
.iter() .iter()
.copied() .copied()
.map(Instance::new) .map(InstanceRef::new)
.collect(); .collect();
Ok(children) 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()?; let name_str=&*name.to_str()?;
dom_mut(lua,|dom|{ dom_mut(lua,|dom|{
let instance=this.get(dom)?; let instance=this.get(dom)?;
@ -156,7 +158,7 @@ impl mlua::UserData for Instance{
false=>find_first_child(dom,instance,name_str), false=>find_first_child(dom,instance,name_str),
} }
.map(|instance| .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), false=>find_first_child_of_class(dom,this.get(dom)?,class_str),
} }
.map(|instance| .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 let children:Vec<_>=dom
.descendants_of(this.referent) .descendants_of(this.referent)
.map(|instance| .map(|instance|
Instance::new(instance.referent()) InstanceRef::new(instance.as_ref().referent())
) )
.collect(); .collect();
Ok(children) Ok(children)
@ -190,7 +192,7 @@ impl mlua::UserData for Instance{
); );
methods.add_method("IsA",|lua,this,classname:mlua::String| methods.add_method("IsA",|lua,this,classname:mlua::String|
dom_mut(lua,|dom|{ 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()?)) Ok(crate::context::class_is_a(instance.class.as_str(),&*classname.to_str()?))
}) })
); );
@ -200,39 +202,40 @@ impl mlua::UserData for Instance{
Ok(()) 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|{ dom_mut(lua,|dom|{
let instance=this.get(dom)?; let instance=this.get(dom)?.as_ref();
Ok(instance.name.clone()) 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_str=&*index.to_str()?;
let index_ustr=ustr(index_str); let index_ustr=ustr(index_str);
dom_mut(lua,|dom|{ dom_mut(lua,|dom|{
let instance=this.get(dom)?; let instance=this.get(dom)?;
let instance_ref=instance.as_ref();
//println!("__index t={} i={index:?}",instance.name); //println!("__index t={} i={index:?}",instance.name);
let db=rbx_reflection_database::get(); 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 //Find existing property
match instance.properties.get(&index_ustr) match instance_ref.properties.get(&index_ustr)
.cloned() .cloned()
//Find default value //Find default value
.or_else(||db.find_default_property(class,index_str).cloned()) .or_else(||db.find_default_property(class,index_str).cloned())
//Find virtual property //Find virtual property
.or_else(||db.superclasses_iter(class).find_map(|class| .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::Int32(val))=>return val.into_lua(lua),
Some(rbx_types::Variant::Int64(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::Float32(val))=>return val.into_lua(lua),
Some(rbx_types::Variant::Float64(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::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), Some(rbx_types::Variant::Vector3(v))=>return Into::<crate::runner::vector3::Vector3>::into(v).into_lua(lua),
None=>(), 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 //find a function with a matching name
if let Some(function)=class_methods_store_mut(lua,|cf|{ 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 //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)=instance.get_or_create_userdata(lua,index_ustr)?{
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(instance.referent())) .map(|instance|InstanceRef::new(instance.as_ref().referent()))
.into_lua(lua) .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|{ 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); //println!("__newindex t={} i={index:?} v={value:?}",instance.name);
let index_str=&*index.to_str()?; let index_str=&*index.to_str()?;
let index_ustr=ustr(index_str); let index_ustr=ustr(index_str);
@ -331,7 +331,7 @@ impl mlua::UserData for Instance{
macro_rules! cf{ macro_rules! cf{
($f:expr)=>{ ($f:expr)=>{
|lua,mut args|{ |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) $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{ match service{
"Lighting"|"RunService"=>{ "Lighting"|"RunService"=>{
let referent=find_first_child_of_class(dom,dom.root(),service) let referent=find_first_child_of_class(dom,dom.root(),service)
.map(|instance|instance.referent()) .map(|instance|instance.as_ref().referent())
.unwrap_or_else(|| .unwrap_or_else(||
dom.insert(dom.root_ref(),InstanceBuilder::new(service)) 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 //Transform Source property with provided function
(virtual_property.pointer)(variant) (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 mod instance;
pub use instance::Instance; pub use instance::InstanceRef;

@ -7,7 +7,7 @@ mod color3;
mod cframe; mod cframe;
mod vector3; mod vector3;
pub mod instance; pub mod instance;
mod script_signal; pub mod script_signal;
mod color_sequence; mod color_sequence;
mod number_sequence; mod number_sequence;

@ -1,6 +1,7 @@
use crate::context::Context; use crate::context::Context;
#[cfg(feature="run-service")] #[cfg(feature="run-service")]
use crate::scheduler::scheduler_mut; use crate::scheduler::scheduler_mut;
use crate::types::WeakDom;
pub struct Runner{ pub struct Runner{
lua:mlua::Lua, 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>{ 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(); let globals=self.lua.globals();
globals.set("game",super::instance::Instance::new(services.game)).map_err(Error::RustLua)?; globals.set("game",super::instance::InstanceRef::new(services.game)).map_err(Error::RustLua)?;
globals.set("workspace",super::instance::Instance::new(services.workspace)).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 //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")] #[cfg(feature="run-service")]
self.lua.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default()); self.lua.set_app_data::<crate::scheduler::Scheduler>(crate::scheduler::Scheduler::default());
Ok(Runnable{ Ok(Runnable{
@ -81,14 +82,14 @@ pub struct Runnable<'a>{
} }
impl Runnable<'_>{ impl Runnable<'_>{
pub fn drop_context(self)->Runner{ 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")] #[cfg(feature="run-service")]
self.lua.remove_app_data::<crate::scheduler::Scheduler>(); self.lua.remove_app_data::<crate::scheduler::Scheduler>();
Runner{ Runner{
lua:self.lua, 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)?; 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)?; self.lua.globals().raw_set("script",script).map_err(Error::RustLua)?;
let f=self.lua.load(source.as_str()) let f=self.lua.load(source.as_str())
@ -123,20 +124,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(())
} }

@ -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,
})
}
}

@ -14,10 +14,10 @@ image = "0.25.2"
image_dds = "0.7.1" image_dds = "0.7.1"
lazy-regex = "3.1.0" lazy-regex = "3.1.0"
rbx_asset = { version = "0.4.4", registry = "strafesnet" } rbx_asset = { version = "0.4.4", registry = "strafesnet" }
rbx_binary = "1.0.0" rbx_binary = { version = "1.1.0-sn3", registry = "strafesnet"}
rbx_dom_weak = "3.0.0" rbx_dom_weak = { version = "3.1.0-sn1", registry = "strafesnet"}
rbx_reflection_database = "1.0.0" rbx_reflection_database = "1.0.0"
rbx_xml = "1.0.0" rbx_xml = { version = "1.1.0-sn3", registry = "strafesnet"}
rbxassetid = { version = "0.1.0", registry = "strafesnet" } rbxassetid = { version = "0.1.0", registry = "strafesnet" }
strafesnet_bsp_loader = { version = "0.3.0", path = "../lib/bsp_loader", registry = "strafesnet" } strafesnet_bsp_loader = { version = "0.3.0", path = "../lib/bsp_loader", registry = "strafesnet" }
strafesnet_deferred_loader = { version = "0.5.0", path = "../lib/deferred_loader", registry = "strafesnet" } strafesnet_deferred_loader = { version = "0.5.0", path = "../lib/deferred_loader", registry = "strafesnet" }