Compare commits

..

4 Commits
emu2 ... emu

13 changed files with 178 additions and 144 deletions

13
Cargo.lock generated
View File

@@ -3098,9 +3098,9 @@ dependencies = [
[[package]]
name = "rbx_binary"
version = "1.1.0-sn4"
version = "1.1.0-sn3"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "dc304e66f18316b8ee8729cec639528c9c5182aacbef1f8967619bd8b2363be4"
checksum = "f3f4cd69381a3631e117ee9542f5f8d9a2ab3b5dd8c7bdc7eb1680cc805233bc"
dependencies = [
"ahash",
"log",
@@ -3115,12 +3115,11 @@ dependencies = [
[[package]]
name = "rbx_dom_weak"
version = "3.1.0-sn2"
version = "3.1.0-sn1"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "14eb241422ff3882dd00b6f8367cae9ed2c13464447fc00879e37542013ca9b0"
checksum = "c449e42d26c5af9d50e036219513e7a3e3bd0bc31fa32b74b82d9d4d481d74a5"
dependencies = [
"ahash",
"mlua",
"rbx_types",
"serde",
"ustr",
@@ -3176,9 +3175,9 @@ dependencies = [
[[package]]
name = "rbx_xml"
version = "1.1.0-sn4"
version = "1.1.0-sn3"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "08750f2f23366cda4a71d05c11c40b55bfeccd4bbc22037264b510c90809f7bd"
checksum = "ce7d7ec62a3dc88179a61efa4827c36887a1b2bcbc258aec58de0c5149c565eb"
dependencies = [
"ahash",
"base64 0.13.1",

View File

@@ -13,12 +13,12 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
bytemuck = "1.14.3"
glam = "0.30.0"
lazy-regex = "3.1.0"
rbx_binary = { version = "1.1.0-sn4", registry = "strafesnet" }
rbx_dom_weak = { version = "3.1.0-sn2", registry = "strafesnet", features = ["instance-userdata"] }
rbx_binary = { version = "1.1.0-sn3", registry = "strafesnet"}
rbx_dom_weak = { version = "3.1.0-sn1", registry = "strafesnet"}
rbx_mesh = "0.3.1"
rbx_reflection_database = "1.0.0"
rbx_xml = { version = "1.1.0-sn4", registry = "strafesnet" }
rbx_xml = { version = "1.1.0-sn3", 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_deferred_loader = { version = "0.5.0", path = "../deferred_loader", registry = "strafesnet" }

View File

@@ -1,5 +1,5 @@
use std::io::Read;
use rbx_dom_weak::WeakDom;
use roblox_emulator::types::WeakDom;
use strafesnet_deferred_loader::deferred_loader::{LoadFailureMode,MeshDeferredLoader,RenderConfigDeferredLoader};
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 peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
match &peek[0..8]{
b"<roblox!"=>rbx_binary::from_reader(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_binary::from_reader_generic(buf).map(Model::new).map_err(ReadError::RbxBinary),
b"<roblox "=>rbx_xml::from_reader_generic(buf,Default::default()).map(Model::new).map_err(ReadError::RbxXml),
_=>Err(ReadError::UnknownFileFormat),
}
}

View File

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

View File

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

View File

@@ -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{
class==superclass
@@ -19,7 +20,7 @@ impl Context{
pub const fn new(dom:WeakDom)->Self{
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")
.with_property("Source",rbx_types::Variant::String(source));
let script_ref=script.referent();
@@ -28,7 +29,7 @@ impl Context{
.with_child(script)
));
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{
unsafe{&*(dom as *const WeakDom as *const Context)}
@@ -39,24 +40,24 @@ impl Context{
/// 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{
self.dom.descendants().filter(|&instance|
class_is_a(instance.class.as_ref(),superclass)
).map(|instance|instance.referent())
class_is_a(instance.as_ref().class.as_ref(),superclass)
).map(|instance|instance.as_ref().referent())
}
pub fn scripts(&self)->Vec<crate::runner::instance::Instance>{
self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::Instance::new).collect()
pub fn scripts(&self)->Vec<crate::runner::instance::InstanceRef>{
self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::InstanceRef::new).collect()
}
pub fn find_services(&self)->Option<Services>{
Some(Services{
workspace:*self.dom.root().children().iter().find(|&&r|
self.dom.get_by_ref(r).is_some_and(|instance|instance.class=="Workspace")
workspace:*self.dom.root().as_ref().children().iter().find(|&&r|
self.dom.get_by_ref(r).is_some_and(|instance|instance.as_ref().class=="Workspace")
)?,
game:self.dom.root_ref(),
})
}
pub fn convert_into_place(&mut self)->Services{
//snapshot root instances
let children=self.dom.root().children().to_owned();
let children=self.dom.root().as_ref().children().to_owned();
//insert services
let game=self.dom.root_ref();
@@ -69,7 +70,7 @@ impl Context{
);
{
//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));
}

View File

@@ -1,3 +1,4 @@
pub mod types;
pub mod runner;
pub mod context;
#[cfg(feature="run-service")]

View File

@@ -2,9 +2,10 @@ use std::collections::{hash_map::Entry,HashMap};
use mlua::{FromLua,FromLuaMulti,IntoLua,IntoLuaMulti};
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::types::{WeakDom,Instance};
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
//class functions store
@@ -14,12 +15,12 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
//Instance.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 parent=parent.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?;
dom_mut(lua,|dom|{
//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))))
})
})?
)?;
@@ -43,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 pref=instance.parent();
while let Some(parent)=dom.get_by_ref(pref){
let parent=parent.as_ref();
full_name.insert(0,'.');
full_name.insert_str(0,parent.name.as_str());
pref=parent.parent();
@@ -54,10 +57,10 @@ fn get_full_name(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->S
full_name
}
//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|{
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(),
_=>Err(mlua::Error::external("Missing script.Source"))?,
};
@@ -65,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>{
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.name==name)
pub fn find_first_child<'a>(dom:&'a WeakDom,instance:&Instance,name:&str)->Option<&'a Instance>{
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>{
dom.descendants_of(instance.referent()).find(|&inst|inst.name==name)
pub fn find_first_descendant<'a>(dom:&'a WeakDom,instance:&Instance,name:&str)->Option<&'a Instance>{
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>{
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.class==class)
pub fn find_first_child_of_class<'a>(dom:&'a WeakDom,instance:&Instance,class:&str)->Option<&'a Instance>{
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>{
dom.descendants_of(instance.referent()).find(|&inst|inst.class==class)
pub fn find_first_descendant_of_class<'a>(dom:&'a WeakDom,instance:&Instance,class:&str)->Option<&'a Instance>{
dom.descendants_of(instance.as_ref().referent()).find(|&inst|inst.as_ref().class==class)
}
#[derive(Clone,Copy)]
pub struct Instance{
pub struct InstanceRef{
referent:Ref,
}
impl Instance{
impl InstanceRef{
pub const fn new(referent:Ref)->Self{
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"))
}
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"))
}
}
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){
fields.add_field_method_get("Parent",|lua,this|{
dom_mut(lua,|dom|{
let instance=this.get(dom)?;
Ok(Instance::new(instance.parent()))
let instance=this.get(dom)?.as_ref();
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"))?;
dom_mut(lua,|dom|{
dom.transfer_within(this.referent,parent.referent);
@@ -113,13 +116,13 @@ impl mlua::UserData for Instance{
});
fields.add_field_method_get("Name",|lua,this|{
dom_mut(lua,|dom|{
let instance=this.get(dom)?;
let instance=this.get(dom)?.as_ref();
Ok(instance.name.clone())
})
});
fields.add_field_method_set("Name",|lua,this,val:mlua::String|{
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?
instance.name=val.to_str()?.to_owned();
Ok(())
@@ -127,7 +130,7 @@ impl mlua::UserData for Instance{
});
fields.add_field_method_get("ClassName",|lua,this|{
dom_mut(lua,|dom|{
let instance=this.get(dom)?;
let instance=this.get(dom)?.as_ref();
Ok(instance.class.to_owned())
})
});
@@ -135,17 +138,17 @@ impl mlua::UserData for Instance{
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
methods.add_method("GetChildren",|lua,this,_:()|
dom_mut(lua,|dom|{
let instance=this.get(dom)?;
let instance=this.get(dom)?.as_ref();
let children:Vec<_>=instance
.children()
.iter()
.copied()
.map(Instance::new)
.map(InstanceRef::new)
.collect();
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()?;
dom_mut(lua,|dom|{
let instance=this.get(dom)?;
@@ -155,7 +158,7 @@ impl mlua::UserData for Instance{
false=>find_first_child(dom,instance,name_str),
}
.map(|instance|
Instance::new(instance.referent())
InstanceRef::new(instance.as_ref().referent())
)
)
})
@@ -171,7 +174,7 @@ impl mlua::UserData for Instance{
false=>find_first_child_of_class(dom,this.get(dom)?,class_str),
}
.map(|instance|
Instance::new(instance.referent())
InstanceRef::new(instance.as_ref().referent())
)
)
})
@@ -181,7 +184,7 @@ impl mlua::UserData for Instance{
let children:Vec<_>=dom
.descendants_of(this.referent)
.map(|instance|
Instance::new(instance.referent())
InstanceRef::new(instance.as_ref().referent())
)
.collect();
Ok(children)
@@ -189,7 +192,7 @@ impl mlua::UserData for Instance{
);
methods.add_method("IsA",|lua,this,classname:mlua::String|
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()?))
})
);
@@ -199,39 +202,40 @@ impl mlua::UserData for Instance{
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|{
let instance=this.get(dom)?;
let instance=this.get(dom)?.as_ref();
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_ustr=ustr(index_str);
dom_mut(lua,|dom|{
let instance=this.get(dom)?;
let instance_ref=instance.as_ref();
//println!("__index t={} i={index:?}",instance.name);
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
match instance.properties.get(&index_ustr)
match instance_ref.properties.get(&index_ustr)
.cloned()
//Find default value
.or_else(||db.find_default_property(class,index_str).cloned())
//Find virtual property
.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::Int64(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::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::Vector3(v))=>return Into::<crate::runner::vector3::Vector3>::into(v).into_lua(lua),
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
if let Some(function)=class_methods_store_mut(lua,|cf|{
@@ -246,20 +250,20 @@ impl mlua::UserData for Instance{
//find or create an associated userdata object
let instance=this.get_mut(dom)?;
if let Some(value)=get_or_create_userdata(instance,lua,index_ustr)?{
if let Some(value)=instance.get_or_create_userdata(lua,index_ustr)?{
return value.into_lua(lua);
}
// drop mutable borrow
//find a child with a matching name
let instance=this.get(dom)?;
find_first_child(dom,instance,index_str)
.map(|instance|Instance::new(instance.referent()))
.map(|instance|InstanceRef::new(instance.as_ref().referent()))
.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|{
let instance=this.get_mut(dom)?;
let instance=this.get_mut(dom)?.as_mut();
//println!("__newindex t={} i={index:?} v={value:?}",instance.name);
let index_str=&*index.to_str()?;
let index_ustr=ustr(index_str);
@@ -327,7 +331,7 @@ impl mlua::UserData for Instance{
macro_rules! cf{
($f:expr)=>{
|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)
}
};
@@ -350,13 +354,13 @@ static CLASS_FUNCTION_DATABASE:CFD=phf::phf_map!{
match service{
"Lighting"|"RunService"=>{
let referent=find_first_child_of_class(dom,dom.root(),service)
.map(|instance|instance.referent())
.map(|instance|instance.as_ref().referent())
.unwrap_or_else(||
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"))),
}
})
}),
@@ -463,29 +467,3 @@ fn find_virtual_property(
//Transform Source property with provided function
(virtual_property.pointer)(variant)
}
// 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()),
},
};
fn get_or_create_userdata(instance:&mut rbx_dom_weak::Instance,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(instance.class.as_str()){
Some(userdata_map)=>match instance.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,
})
}

View File

@@ -1,2 +1,2 @@
pub mod instance;
pub use instance::Instance;
pub use instance::InstanceRef;

View File

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

View File

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

View File

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

View File

@@ -14,10 +14,10 @@ image = "0.25.2"
image_dds = "0.7.1"
lazy-regex = "3.1.0"
rbx_asset = { version = "0.4.4", registry = "strafesnet" }
rbx_binary = { version = "1.1.0-sn4", registry = "strafesnet" }
rbx_dom_weak = { version = "3.1.0-sn2", registry = "strafesnet" }
rbx_binary = { version = "1.1.0-sn3", registry = "strafesnet"}
rbx_dom_weak = { version = "3.1.0-sn1", registry = "strafesnet"}
rbx_reflection_database = "1.0.0"
rbx_xml = { version = "1.1.0-sn4", registry = "strafesnet" }
rbx_xml = { version = "1.1.0-sn3", registry = "strafesnet"}
rbxassetid = { version = "0.1.0", 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" }