From 04d7694da45643cb0f35cd607deaaf5ccba65bf2 Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Thu, 17 Apr 2025 16:46:51 -0700
Subject: [PATCH] rbx_loader: use extended instance

---
 lib/rbx_loader/src/lib.rs |  6 ++--
 lib/rbx_loader/src/rbx.rs | 63 +++++++++++++++++++--------------------
 2 files changed, 33 insertions(+), 36 deletions(-)

diff --git a/lib/rbx_loader/src/lib.rs b/lib/rbx_loader/src/lib.rs
index 4f320b1..97ca5913 100644
--- a/lib/rbx_loader/src/lib.rs
+++ b/lib/rbx_loader/src/lib.rs
@@ -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),
 	}
 }
diff --git a/lib/rbx_loader/src/rbx.rs b/lib/rbx_loader/src/rbx.rs
index 954edae..6413d8d 100644
--- a/lib/rbx_loader/src/rbx.rs
+++ b/lib/rbx_loader/src/rbx.rs
@@ -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)