Compare commits

...

3 Commits

Author SHA1 Message Date
260da12ac7
temp: panic 2025-05-07 15:39:50 -07:00
ef7236f5ea
drop ustr 2025-05-07 15:39:30 -07:00
b46fb04a5d
directly use rbx-dom by path 2025-05-03 22:12:15 -07:00
15 changed files with 136 additions and 181 deletions

37
Cargo.lock generated

@ -3098,11 +3098,10 @@ dependencies = [
[[package]]
name = "rbx_binary"
version = "1.1.0-sn4"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "dc304e66f18316b8ee8729cec639528c9c5182aacbef1f8967619bd8b2363be4"
version = "1.0.0"
dependencies = [
"ahash",
"bumpalo",
"log",
"lz4",
"profiling",
@ -3115,15 +3114,11 @@ dependencies = [
[[package]]
name = "rbx_dom_weak"
version = "3.1.0-sn4"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "5fcb44ffa83385055a42e5e499be3a4cce04b2ceb9af2b9825f70dcc4d2578ce"
version = "3.0.0"
dependencies = [
"ahash",
"mlua",
"rbx_types",
"serde",
"ustr",
]
[[package]]
@ -3139,9 +3134,8 @@ dependencies = [
[[package]]
name = "rbx_reflection"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b6d0d62baa613556b058a5f94a53b01cf0ccde0ea327ce03056e335b982e77e"
dependencies = [
"ahash",
"rbx_types",
"serde",
"thiserror 1.0.69",
@ -3149,9 +3143,7 @@ dependencies = [
[[package]]
name = "rbx_reflection_database"
version = "1.0.2+roblox-670"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5349b19e5e94fbcaba7a52175263ab64011e0a13f17ff57729f2f560ccdec615"
version = "1.0.3+roblox-670"
dependencies = [
"lazy_static",
"rbx_reflection",
@ -3162,8 +3154,6 @@ dependencies = [
[[package]]
name = "rbx_types"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78e4fdde46493def107e5f923d82e813dec9b3eef52c2f75fbad3a716023eda2"
dependencies = [
"base64 0.13.1",
"bitflags 1.3.2",
@ -3176,9 +3166,7 @@ dependencies = [
[[package]]
name = "rbx_xml"
version = "1.1.0-sn4"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "08750f2f23366cda4a71d05c11c40b55bfeccd4bbc22037264b510c90809f7bd"
version = "1.0.0"
dependencies = [
"ahash",
"base64 0.13.1",
@ -4315,19 +4303,6 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "ustr"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18b19e258aa08450f93369cf56dd78063586adf19e92a75b338a800f799a0208"
dependencies = [
"ahash",
"byteorder 1.5.0",
"lazy_static",
"parking_lot",
"serde",
]
[[package]]
name = "utf16_iter"
version = "1.0.5"

@ -13,11 +13,11 @@ 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-sn4", registry = "strafesnet", features = ["instance-userdata"] }
rbx_mesh = "0.3.1"
rbx_reflection_database = "1.0.0"
rbx_xml = { version = "1.1.0-sn4", registry = "strafesnet" }
rbx_binary = { path = "../../../../git/rbx-dom/rbx_binary", registry = "strafesnet" }
rbx_dom_weak = { path = "../../../../git/rbx-dom/rbx_dom_weak", registry = "strafesnet" }
rbx_reflection_database = { path = "../../../../git/rbx-dom/rbx_reflection_database"}
rbx_xml = { path = "../../../../git/rbx-dom/rbx_xml", registry = "strafesnet" }
rbxassetid = { version = "0.1.0", path = "../rbxassetid", registry = "strafesnet" }
roblox_emulator = { version = "0.5.0", path = "../roblox_emulator", default-features = false, registry = "strafesnet" }
strafesnet_common = { version = "0.6.0", path = "../common", registry = "strafesnet" }

@ -21,28 +21,28 @@ pub mod data{
}
}
pub struct Model{
dom:WeakDom,
pub struct Model<'a>{
dom:WeakDom<'a>,
}
impl Model{
fn new(dom:WeakDom)->Self{
impl<'a> Model<'a>{
fn new(dom:WeakDom<'a>)->Self{
Self{dom}
}
pub fn to_snf(&self,failure_mode:LoadFailureMode)->Result<strafesnet_common::map::CompleteMap,LoadError>{
to_snf(self,failure_mode)
}
}
impl AsRef<WeakDom> for Model{
fn as_ref(&self)->&WeakDom{
impl<'a> AsRef<WeakDom<'a>> for Model<'a>{
fn as_ref(&self)->&WeakDom<'a>{
&self.dom
}
}
pub struct Place{
context:Context,
pub struct Place<'a>{
context:Context<'a>,
}
impl Place{
pub fn new(dom:WeakDom)->Result<Self,roblox_emulator::context::ServicesError>{
impl<'a> Place<'a>{
pub fn new(dom:WeakDom<'a>)->Result<Self,roblox_emulator::context::ServicesError>{
let context=Context::from_place(dom)?;
Ok(Self{
context,
@ -63,13 +63,13 @@ impl Place{
to_snf(self,failure_mode)
}
}
impl AsRef<WeakDom> for Place{
fn as_ref(&self)->&WeakDom{
impl<'a> AsRef<WeakDom<'a>> for Place<'a>{
fn as_ref(&self)->&WeakDom<'a>{
self.context.as_ref()
}
}
impl From<Model> for Place{
fn from(model:Model)->Self{
impl<'a> From<Model<'a>> for Place<'a>{
fn from(model:Model<'a>)->Self{
let context=Context::from_model(model.dom);
Self{
context,
@ -91,11 +91,11 @@ impl std::fmt::Display for ReadError{
}
impl std::error::Error for ReadError{}
pub fn read<R:Read>(input:R)->Result<Model,ReadError>{
pub fn read<R:Read>(input:R)->Result<Model<'static>,ReadError>{
let mut buf=std::io::BufReader::new(input);
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
match peek.get(0..8){
Some(b"<roblox!")=>rbx_binary::from_reader(buf).map(Model::new).map_err(ReadError::RbxBinary),
Some(b"<roblox!")=>rbx_binary::from_reader_default(buf).map(Model::new).map_err(ReadError::RbxBinary),
Some(b"<roblox ")=>rbx_xml::from_reader_default(buf).map(Model::new).map_err(ReadError::RbxXml),
_=>Err(ReadError::UnknownFileFormat),
}
@ -123,7 +123,7 @@ impl From<loader::MeshError> for LoadError{
}
}
fn to_snf(dom:impl AsRef<WeakDom>,failure_mode:LoadFailureMode)->Result<strafesnet_common::map::CompleteMap,LoadError>{
fn to_snf<'a>(dom:impl AsRef<WeakDom<'a>>,failure_mode:LoadFailureMode)->Result<strafesnet_common::map::CompleteMap,LoadError>{
let dom=dom.as_ref();
let mut texture_deferred_loader=RenderConfigDeferredLoader::new();

@ -6,10 +6,6 @@ use strafesnet_deferred_loader::{loader::Loader,texture::Texture};
use crate::data::RobloxMeshBytes;
use crate::rbx::RobloxPartDescription;
// disallow non-static lifetimes
fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
rbx_dom_weak::ustr(s)
}
fn read_entire_file(path:impl AsRef<std::path::Path>)->Result<Vec<u8>,std::io::Error>{
let mut file=std::fs::File::open(path)?;
@ -168,7 +164,7 @@ impl Loader for MeshLoader{
let RobloxAssetId(asset_id)=index.content.parse()?;
let file_name=format!("unions/{}",asset_id);
let data=read_entire_file(file_name)?;
let dom=rbx_binary::from_reader(std::io::Cursor::new(data))?;
let dom=rbx_binary::from_reader_default(data.as_slice())?;
let &[referent]=dom.root().children()else{
return Err(MeshError::OneChildPolicy);
};
@ -176,12 +172,12 @@ impl Loader for MeshLoader{
return Err(MeshError::MissingInstance);
};
if physics_data.is_empty(){
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(&static_ustr("PhysicsData")){
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get("PhysicsData"){
physics_data=data.as_ref();
}
}
if mesh_data.is_empty(){
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(&static_ustr("MeshData")){
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get("MeshData"){
mesh_data=data.as_ref();
}
}

@ -13,11 +13,6 @@ use strafesnet_deferred_loader::deferred_loader::{RenderConfigDeferredLoader,Mes
use strafesnet_deferred_loader::mesh::Meshes;
use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
// disallow non-static lifetimes
fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
rbx_dom_weak::ustr(s)
}
fn recursive_collect_superclass(
objects:&mut std::vec::Vec<rbx_dom_weak::types::Ref>,
dom:&rbx_dom_weak::WeakDom,
@ -31,7 +26,7 @@ fn recursive_collect_superclass(
};
objects.extend(
dom.descendants_of(instance.referent()).filter_map(|instance|{
let class=db.classes.get(instance.class.as_str())?;
let class=db.classes.get(instance.class)?;
db.has_superclass(class,superclass).then(||instance.referent())
})
);
@ -422,10 +417,10 @@ fn get_texture_description<'a>(
Some(rbx_dom_weak::types::Variant::Color3(decal_color3)),
Some(rbx_dom_weak::types::Variant::Float32(decal_transparency)),
)=(
decal.properties.get(&static_ustr("TextureContent")),
decal.properties.get(&static_ustr("Face")),
decal.properties.get(&static_ustr("Color3")),
decal.properties.get(&static_ustr("Transparency")),
decal.properties.get("TextureContent"),
decal.properties.get("Face"),
decal.properties.get("Color3"),
decal.properties.get("Transparency"),
)else{
println!("Decal is missing a required property");
continue;
@ -447,10 +442,10 @@ fn get_texture_description<'a>(
Some(&rbx_dom_weak::types::Variant::Float32(studs_per_tile_u)),
Some(&rbx_dom_weak::types::Variant::Float32(studs_per_tile_v)),
) = (
decal.properties.get(&static_ustr("OffsetStudsU")),
decal.properties.get(&static_ustr("OffsetStudsV")),
decal.properties.get(&static_ustr("StudsPerTileU")),
decal.properties.get(&static_ustr("StudsPerTileV")),
decal.properties.get("OffsetStudsU"),
decal.properties.get("OffsetStudsV"),
decal.properties.get("StudsPerTileU"),
decal.properties.get("StudsPerTileV"),
)
{
let (size_u,size_v)=match cube_face{
@ -548,12 +543,12 @@ pub fn convert<'a>(
Some(rbx_dom_weak::types::Variant::Color3uint8(color3)),
Some(rbx_dom_weak::types::Variant::Bool(can_collide)),
) = (
object.properties.get(&static_ustr("CFrame")),
object.properties.get(&static_ustr("Size")),
object.properties.get(&static_ustr("Velocity")),
object.properties.get(&static_ustr("Transparency")),
object.properties.get(&static_ustr("Color")),
object.properties.get(&static_ustr("CanCollide")),
object.properties.get("CFrame"),
object.properties.get("Size"),
object.properties.get("Velocity"),
object.properties.get("Transparency"),
object.properties.get("Color"),
object.properties.get("CanCollide"),
)
{
let model_transform=planar64_affine3_from_roblox(cf,size);
@ -571,8 +566,8 @@ pub fn convert<'a>(
}
//TODO: also detect "CylinderMesh" etc here
let shape=match object.class.as_str(){
"Part"=>if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get(&static_ustr("Shape")){
let shape=match object.class{
"Part"=>if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get("Shape"){
Shape::Primitive(shape.to_u32().try_into().expect("Funky roblox PartType"))
}else{
panic!("Part has no Shape!");
@ -646,9 +641,9 @@ pub fn convert<'a>(
Some(rbx_dom_weak::types::Variant::Content(texture_content)),
)=(
// mesh must exist
object.properties.get(&static_ustr("MeshContent")),
object.properties.get("MeshContent"),
// texture is allowed to be none
object.properties.get(&static_ustr("TextureContent")),
object.properties.get("TextureContent"),
){
let mesh_asset_id=get_content_url(mesh_content).expect("MeshPart Mesh is not a Uri");
let texture_asset_id=get_content_url(texture_content);
@ -663,13 +658,13 @@ pub fn convert<'a>(
let mut content="";
let mut mesh_data:&[u8]=&[];
let mut physics_data:&[u8]=&[];
if let Some(rbx_dom_weak::types::Variant::ContentId(asset_id))=object.properties.get(&static_ustr("AssetId")){
if let Some(rbx_dom_weak::types::Variant::ContentId(asset_id))=object.properties.get("AssetId"){
content=asset_id.as_ref();
}
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(&static_ustr("MeshData")){
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get("MeshData"){
mesh_data=data.as_ref();
}
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(&static_ustr("PhysicsData")){
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get("PhysicsData"){
physics_data=data.as_ref();
}
let part_texture_description=get_texture_description(&mut temp_objects,render_config_deferred_loader,dom,object,size);

@ -8,14 +8,14 @@ description = "Run embedded Luau scripts which manipulate the DOM."
authors = ["Rhys Lloyd <krakow20@gmail.com>"]
[features]
default=["run-service"]
default=[]
run-service=[]
[dependencies]
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-sn4", registry = "strafesnet", features = ["instance-userdata"] }
rbx_reflection = "5.0.0"
rbx_reflection_database = "1.0.0"
rbx_types = "2.0.0"
rbx_dom_weak = { path = "../../../../git/rbx-dom/rbx_dom_weak", registry = "strafesnet" }
rbx_reflection = { path = "../../../../git/rbx-dom/rbx_reflection" }
rbx_reflection_database = { path = "../../../../git/rbx-dom/rbx_reflection_database" }
rbx_types = { path = "../../../../git/rbx-dom/rbx_types" }

@ -1,4 +1,3 @@
use crate::util::static_ustr;
use rbx_dom_weak::{types::Ref,InstanceBuilder,WeakDom};
#[derive(Debug)]
@ -28,18 +27,18 @@ impl Services{
}
}
pub type LuaAppData=&'static mut WeakDom;
pub struct Context{
pub(crate)dom:WeakDom,
pub type LuaAppData=&'static mut WeakDom<'static>;
pub struct Context<'a>{
pub(crate)dom:WeakDom<'a>,
pub(crate)services:Services,
}
impl Context{
pub fn from_place(dom:WeakDom)->Result<Context,ServicesError>{
impl<'a> Context<'a>{
pub fn from_place(dom:WeakDom<'a>)->Result<Context<'a>,ServicesError>{
let services=Services::find_services(&dom)?;
Ok(Self{dom,services})
}
pub fn script_singleton(source:String)->(Context,crate::runner::instance::Instance){
pub fn script_singleton(source:String)->(Context<'a>,crate::runner::instance::Instance){
let script=InstanceBuilder::new("Script")
.with_property("Source",rbx_types::Variant::String(source));
let script_ref=script.referent();
@ -51,13 +50,13 @@ impl Context{
(context,crate::runner::instance::Instance::new_unchecked(script_ref))
}
/// 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<'b>(&'b self,superclass:&'b str)->impl Iterator<Item=Ref>+'b{
let db=rbx_reflection_database::get();
let Some(superclass)=db.classes.get(superclass)else{
panic!("Invalid class");
};
self.dom.descendants().filter_map(|instance|{
let class=db.classes.get(instance.class.as_str())?;
let class=db.classes.get(instance.class)?;
db.has_superclass(class,superclass).then(||instance.referent())
})
}
@ -65,7 +64,7 @@ impl Context{
self.superclass_iter("Script")
.filter_map(|script_ref|{
let script=self.dom.get_by_ref(script_ref)?;
if let None|Some(rbx_dom_weak::types::Variant::Bool(false))=script.properties.get(&static_ustr("Disabled")){
if let None|Some(rbx_dom_weak::types::Variant::Bool(false))=script.properties.get("Disabled"){
return Some(crate::runner::instance::Instance::new_unchecked(script_ref));
}
None
@ -73,7 +72,7 @@ impl Context{
.collect()
}
pub fn from_model(mut dom:WeakDom)->Context{
pub fn from_model(mut dom:WeakDom<'a>)->Context<'a>{
//snapshot root instances
let children=dom.root().children().to_owned();
@ -96,8 +95,8 @@ impl Context{
//Lowercase and upper case workspace property!
let game=dom.root_mut();
// TODO: DELETE THIS!
game.properties.insert(static_ustr("workspace"),rbx_types::Variant::Ref(workspace));
game.properties.insert(static_ustr("Workspace"),rbx_types::Variant::Ref(workspace));
game.properties.insert("workspace",rbx_types::Variant::Ref(workspace));
game.properties.insert("Workspace",rbx_types::Variant::Ref(workspace));
}
dom.insert(game,InstanceBuilder::new("Lighting"));
@ -106,8 +105,8 @@ impl Context{
}
}
impl AsRef<WeakDom> for Context{
fn as_ref(&self)->&WeakDom{
impl<'a> AsRef<WeakDom<'a>> for Context<'a>{
fn as_ref(&self)->&WeakDom<'a>{
&self.dom
}
}

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

@ -4,8 +4,8 @@ pub struct EnumItem<'a>{
value:u32,
}
impl<'a> EnumItem<'a>{
fn known_name((name,&value):(&'a std::borrow::Cow<'a,str>,&u32))->Self{
Self{name:Some(name.as_ref()),value}
fn known_name((name,&value):(&&'a str,&u32))->Self{
Self{name:Some(name),value}
}
}
impl<'a> From<rbx_types::Enum> for EnumItem<'a>{

@ -2,9 +2,8 @@ use std::collections::{hash_map::Entry,HashMap};
use mlua::{FromLua,FromLuaMulti,IntoLua,IntoLuaMulti};
use rbx_types::Ref;
use rbx_dom_weak::{Ustr,InstanceBuilder,WeakDom};
use rbx_dom_weak::{InstanceBuilder,WeakDom};
use crate::util::static_ustr;
use crate::runner::vector3::Vector3;
use crate::runner::number::Number;
@ -19,8 +18,12 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
lua.create_function(|lua,(class_name,parent):(mlua::String,Option<Instance>)|{
let class_name_str=&*class_name.to_str()?;
let parent_ref=parent.map_or(Ref::none(),|instance|instance.referent);
let db=rbx_reflection_database::get();
let Some((known_class_name,_))=db.classes.get_key_value(class_name_str)else{
return Err(mlua::Error::runtime("Unknown ClassName"));
};
dom_mut(lua,|dom|{
Ok(Instance::new_unchecked(dom.insert(parent_ref,InstanceBuilder::new(class_name_str))))
Ok(Instance::new_unchecked(dom.insert(parent_ref,InstanceBuilder::new(known_class_name))))
})
})?
)?;
@ -57,7 +60,7 @@ fn get_full_name(dom:&rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance)->S
pub fn get_name_source(lua:&mlua::Lua,script:Instance)->Result<(String,String),mlua::Error>{
dom_mut(lua,|dom|{
let instance=script.get(dom)?;
let source=match instance.properties.get(&static_ustr("Source")){
let source=match instance.properties.get("Source"){
Some(rbx_dom_weak::types::Variant::String(s))=>s.clone(),
_=>Err(mlua::Error::external("Missing script.Source"))?,
};
@ -65,32 +68,32 @@ 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 rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,name:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.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 rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,name:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
dom.descendants_of(instance.referent()).find(|&inst|inst.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 rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,class:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|inst.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 rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,class:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
dom.descendants_of(instance.referent()).find(|&inst|inst.class==class)
}
pub fn find_first_child_which_is_a<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,superclass:&str)->Option<&'a rbx_dom_weak::Instance>{
pub fn find_first_child_which_is_a<'a>(dom:&'a rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,superclass:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
let db=rbx_reflection_database::get();
let superclass_descriptor=db.classes.get(superclass)?;
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|{
db.classes.get(inst.class.as_str()).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor))
db.classes.get(inst.class).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor))
})
}
pub fn find_first_descendant_which_is_a<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,superclass:&str)->Option<&'a rbx_dom_weak::Instance>{
pub fn find_first_descendant_which_is_a<'a>(dom:&'a rbx_dom_weak::WeakDom<'a>,instance:&rbx_dom_weak::Instance<'a>,superclass:&str)->Option<&'a rbx_dom_weak::Instance<'a>>{
let db=rbx_reflection_database::get();
let superclass_descriptor=db.classes.get(superclass)?;
dom.descendants_of(instance.referent()).find(|inst|{
db.classes.get(inst.class.as_str()).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor))
db.classes.get(inst.class).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor))
})
}
@ -105,10 +108,10 @@ impl Instance{
pub fn new(referent:Ref)->Option<Self>{
referent.is_some().then_some(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<'a>)->mlua::Result<&'a rbx_dom_weak::Instance<'a>>{
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,'b>(&self,dom:&'a mut WeakDom<'b>)->mlua::Result<&'a mut rbx_dom_weak::Instance<'b>>{
dom.get_by_ref_mut(self.referent).ok_or_else(||mlua::Error::runtime("Instance missing"))
}
}
@ -256,7 +259,7 @@ impl mlua::UserData for Instance{
fn is_a(lua:&mlua::Lua,this:&Instance,classname:mlua::String)->mlua::Result<bool>{
dom_mut(lua,|dom|{
let instance=this.get(dom)?;
Ok(class_is_a(instance.class.as_str(),&*classname.to_str()?))
Ok(class_is_a(instance.class,&*classname.to_str()?))
})
}
methods.add_method("isA",is_a);
@ -283,14 +286,9 @@ impl mlua::UserData for Instance{
let instance=this.get(dom)?;
//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.class).ok_or_else(||mlua::Error::runtime("Class missing"))?;
// Find existing property
// Interestingly, ustr can know ahead of time if
// a property does not exist in any runtime instance
match Ustr::from_existing(index_str)
.and_then(|index_ustr|
instance.properties.get(&index_ustr).cloned()
)
match instance.properties.get(index_str).cloned()
//Find default value
.or_else(||db.find_default_property(class,index_str).cloned())
//Find virtual property
@ -342,9 +340,9 @@ impl mlua::UserData for Instance{
dom_mut(lua,|dom|{
let instance=this.get_mut(dom)?;
let db=rbx_reflection_database::get();
let class=db.classes.get(instance.class.as_str()).ok_or_else(||mlua::Error::runtime("Class missing"))?;
let property=db.superclasses_iter(class).find_map(|cls|
cls.properties.get(index_str)
let class=db.classes.get(instance.class).ok_or_else(||mlua::Error::runtime("Class missing"))?;
let (prop_name,property)=db.superclasses_iter(class).find_map(|cls|
cls.properties.get_key_value(index_str)
).ok_or_else(||
mlua::Error::runtime(format!("Property '{index_str}' missing on class '{}'",class.name))
)?;
@ -429,10 +427,7 @@ impl mlua::UserData for Instance{
},
other=>return Err(mlua::Error::runtime(format!("Unimplemented property type: {other:?}"))),
};
// the index is known to be a real property at this point
// allow creating a permanent ustr (memory leak)
let index_ustr=rbx_dom_weak::ustr(index_str);
instance.properties.insert(index_ustr,value);
instance.properties.insert(prop_name,value);
Ok(())
})
});
@ -457,21 +452,27 @@ type CFD=phf::Map<&'static str,// Class name
ClassFunctionPointer
>
>;
const SERVICES:phf::Map<&'static str,()> =phf::phf_map!{
"Lighting"=>(),
"RunService"=>(),
"Players"=>(),
"Workspace"=>(),
"MaterialService"=>(),
"TweenService"=>(),
};
const GET_SERVICE:ClassFunctionPointer=cf!(|lua,_this,service:mlua::String|{
dom_mut(lua,|dom|{
//dom.root_ref()==this.referent ?
let service=&*service.to_str()?;
match service{
"Lighting"|"RunService"|"Players"|"Workspace"|"MaterialService"|"TweenService"=>{
let referent=find_first_child_of_class(dom,dom.root(),service)
.map(|instance|instance.referent())
.unwrap_or_else(||
dom.insert(dom.root_ref(),InstanceBuilder::new(service))
);
Ok(Instance::new_unchecked(referent))
},
other=>Err(mlua::Error::runtime(format!("Service '{other}' not supported"))),
}
let Some(known_service)=SERVICES.get_key(service)else{
return Err(mlua::Error::runtime(format!("Service '{service}' not supported")));
};
let referent=find_first_child_of_class(dom,dom.root(),known_service)
.map(|instance|instance.referent())
.unwrap_or_else(||
dom.insert(dom.root_ref(),InstanceBuilder::new(known_service))
);
Ok(Instance::new_unchecked(referent))
})
});
const GET_PLAYERS:ClassFunctionPointer=cf!(|_lua,_this,()|->mlua::Result<_>{
@ -590,7 +591,7 @@ static VIRTUAL_PROPERTY_DATABASE:VPD=phf::phf_map!{
};
fn find_virtual_property(
properties:&rbx_dom_weak::UstrMap<rbx_types::Variant>,
properties:&rbx_dom_weak::AHashMap<&str,rbx_types::Variant>,
class:&rbx_reflection::ClassDescriptor,
index:&str,
)->Option<rbx_types::Variant>{
@ -599,7 +600,7 @@ fn find_virtual_property(
let virtual_property=class_virtual_properties.get(index)?;
//Get source property
let variant=properties.get(&static_ustr(virtual_property.property))?;
let variant=properties.get(virtual_property.property)?;
//Transform Source property with provided function
(virtual_property.pointer)(variant)
@ -641,7 +642,7 @@ static LAZY_USER_DATA:LUD=phf::phf_map!{
fn get_or_create_userdata(instance:&mut rbx_dom_weak::Instance,lua:&mlua::Lua,index:&str)->mlua::Result<Option<mlua::AnyUserData>>{
use std::collections::hash_map::Entry;
let db=rbx_reflection_database::get();
let Some(class)=db.classes.get(instance.class.as_str())else{
let Some(class)=db.classes.get(instance.class)else{
return Ok(None)
};
if let Some((&static_str,create_userdata))=db.superclasses_iter(class).find_map(|superclass|
@ -649,11 +650,11 @@ fn get_or_create_userdata(instance:&mut rbx_dom_weak::Instance,lua:&mlua::Lua,in
LAZY_USER_DATA.get(&superclass.name)
.and_then(|map|map.get_entry(index))
){
let index_ustr=static_ustr(static_str);
return Ok(Some(match instance.userdata.entry(index_ustr){
Entry::Occupied(entry)=>entry.get().clone(),
Entry::Vacant(entry)=>entry.insert(create_userdata(lua)?).clone(),
}));
panic!();
// return Ok(Some(match instance.userdata.entry(static_str){
// Entry::Occupied(entry)=>entry.get().clone(),
// Entry::Vacant(entry)=>entry.insert(create_userdata(lua)?).clone(),
// }));
}
Ok(None)
}

@ -1,5 +1,4 @@
use crate::context::Context;
use crate::util::static_ustr;
#[cfg(feature="run-service")]
use crate::scheduler::scheduler_mut;
@ -68,8 +67,7 @@ impl Runner{
// SAFETY: This is not a &'static mut WeakDom,
// but as long as Runnable<'a> holds the lifetime of &'a mut Context
// it is a valid unique reference.
let ptr=&mut context.dom as *mut rbx_dom_weak::WeakDom;
self.lua.set_app_data::<crate::context::LuaAppData>(unsafe{&mut*ptr});
self.lua.set_app_data::<crate::context::LuaAppData>(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{

@ -1,3 +0,0 @@
pub fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
rbx_dom_weak::ustr(s)
}

@ -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-sn4", registry = "strafesnet" }
rbx_reflection_database = "1.0.0"
rbx_xml = { version = "1.1.0-sn4", registry = "strafesnet" }
rbx_binary = { path = "../../../git/rbx-dom/rbx_binary", registry = "strafesnet" }
rbx_dom_weak = { path = "../../../git/rbx-dom/rbx_dom_weak", registry = "strafesnet" }
rbx_reflection_database = { path = "../../../git/rbx-dom/rbx_reflection_database"}
rbx_xml = { path = "../../../git/rbx-dom/rbx_xml", 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" }

@ -8,11 +8,6 @@ use strafesnet_deferred_loader::deferred_loader::LoadFailureMode;
use rbxassetid::RobloxAssetId;
use tokio::io::AsyncReadExt;
// disallow non-static lifetimes
fn static_ustr(s:&'static str)->rbx_dom_weak::Ustr{
rbx_dom_weak::ustr(s)
}
const DOWNLOAD_LIMIT:usize=16;
#[derive(Subcommand)]
@ -56,13 +51,13 @@ enum LoadDomError{
Xml(rbx_xml::DecodeError),
UnknownFormat,
}
fn load_dom<R:Read+Seek>(mut input:R)->Result<rbx_dom_weak::WeakDom,LoadDomError>{
fn load_dom<R:Read+Seek>(mut input:R)->Result<rbx_dom_weak::WeakDom<'static>,LoadDomError>{
let mut first_8=[0u8;8];
input.read_exact(&mut first_8).map_err(LoadDomError::IO)?;
input.rewind().map_err(LoadDomError::IO)?;
match &first_8{
b"<roblox!"=>rbx_binary::from_reader(input).map_err(LoadDomError::Binary),
b"<roblox "=>rbx_xml::from_reader(input,rbx_xml::DecodeOptions::default()).map_err(LoadDomError::Xml),
b"<roblox!"=>rbx_binary::from_reader_default(input).map_err(LoadDomError::Binary),
b"<roblox "=>rbx_xml::from_reader_default(input).map_err(LoadDomError::Xml),
_=>Err(LoadDomError::UnknownFormat),
}
}
@ -108,8 +103,8 @@ WrapLayer.ReferenceMeshContent
*/
fn accumulate_content(content_list:&mut HashSet<RobloxAssetId>,object:&Instance,property:&'static str){
let Some(rbx_dom_weak::types::Variant::Content(content))=object.properties.get(&static_ustr(property))else{
println!("property={} does not exist for class={}",property,object.class.as_str());
let Some(rbx_dom_weak::types::Variant::Content(content))=object.properties.get(property)else{
println!("property={} does not exist for class={}",property,object.class);
return;
};
let rbx_dom_weak::types::ContentType::Uri(uri)=content.value()else{
@ -123,8 +118,8 @@ fn accumulate_content(content_list:&mut HashSet<RobloxAssetId>,object:&Instance,
content_list.insert(asset_id);
}
fn accumulate_content_id(content_list:&mut HashSet<RobloxAssetId>,object:&Instance,property:&'static str){
let Some(rbx_dom_weak::types::Variant::ContentId(content))=object.properties.get(&static_ustr(property))else{
println!("property={} does not exist for class={}",property,object.class.as_str());
let Some(rbx_dom_weak::types::Variant::ContentId(content))=object.properties.get(property)else{
println!("property={} does not exist for class={}",property,object.class);
return;
};
let Ok(asset_id)=content.as_str().parse()else{
@ -147,7 +142,7 @@ struct UniqueAssets{
}
impl UniqueAssets{
fn collect(&mut self,object:&Instance){
match object.class.as_str(){
match object.class{
"Beam"=>accumulate_content_id(&mut self.textures,object,"Texture"),
"Decal"=>accumulate_content(&mut self.textures,object,"TextureContent"),
"Texture"=>accumulate_content(&mut self.textures,object,"TextureContent"),

@ -28,7 +28,7 @@ impl std::error::Error for ReadError{}
pub enum ReadFormat{
#[cfg(feature="roblox")]
Roblox(strafesnet_rbx_loader::Model),
Roblox(strafesnet_rbx_loader::Model<'static>),
#[cfg(feature="source")]
Source(strafesnet_bsp_loader::Bsp),
#[cfg(feature="snf")]