From 6dfa46bc6783a19659bfaa90d76eea3bee63e98d Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Sun, 6 Oct 2024 17:14:46 -0700
Subject: [PATCH] VIRTUAL_PROPERTY_DATABASE (another goddamn superclass walk)

---
 src/runner/instance.rs | 75 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 67 insertions(+), 8 deletions(-)

diff --git a/src/runner/instance.rs b/src/runner/instance.rs
index 8ce90b8..222201a 100644
--- a/src/runner/instance.rs
+++ b/src/runner/instance.rs
@@ -237,16 +237,27 @@ impl mlua::UserData for Instance{
 				let class=db.classes.get(instance.class.as_str()).ok_or(mlua::Error::runtime("Class missing"))?;
 				//Find existing property
 				match instance.properties.get(index_str)
+					.cloned()
 					//Find default value
-					.or_else(||db.find_default_property(class,index_str))
+					.or_else(||db.find_default_property(class,index_str).cloned())
+					//Find virtual property
+					.or_else(||{
+						SuperClassIter{
+							database:db,
+							descriptor:Some(class),
+						}
+						.find_map(|class|
+							find_virtual_property(&instance.properties,class,index_str)
+						)
+					})
 				{
-					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::CFrame(cf))=>return Into::<super::cframe::CFrame>::into(cf).into_lua(lua),
-					Some(&rbx_types::Variant::Vector3(v))=>return Into::<super::vector3::Vector3>::into(v).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::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::CFrame(cf))=>return Into::<super::cframe::CFrame>::into(cf).into_lua(lua),
+					Some(rbx_types::Variant::Vector3(v))=>return Into::<super::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))),
 				}
@@ -415,3 +426,51 @@ impl ClassMethods<'_>{
 		})
 	}
 }
+
+/// A virtual property pointer definition shorthand.
+type VirtualPropertyFunctionPointer=fn(&rbx_types::Variant)->Option<rbx_types::Variant>;
+const fn vpp(
+	property:&'static str,
+	pointer:VirtualPropertyFunctionPointer,
+)->VirtualProperty{
+	VirtualProperty{
+		property,
+		pointer,
+	}
+}
+struct VirtualProperty{
+	property:&'static str,// Source property name
+	pointer:VirtualPropertyFunctionPointer,
+}
+type VPD=phf::Map<&'static str,// Class name
+	phf::Map<&'static str,// Virtual property name
+		VirtualProperty
+	>
+>;
+static VIRTUAL_PROPERTY_DATABASE:VPD=phf::phf_map!{
+	"BasePart"=>phf::phf_map!{
+		"Position"=>vpp("CFrame",|c:&rbx_types::Variant|{
+			let c=match c{
+				rbx_types::Variant::CFrame(c)=>c,
+				_=>return None,//fail silently and ungracefully
+			};
+			Some(rbx_types::Variant::Vector3(c.position))
+		}),
+	},
+};
+
+fn find_virtual_property(
+	properties:&HashMap<String,rbx_types::Variant>,
+	class:&rbx_reflection::ClassDescriptor,
+	index:&str
+)->Option<rbx_types::Variant>{
+	//Find virtual property
+	let class_virtual_properties=VIRTUAL_PROPERTY_DATABASE.get(&class.name)?;
+	let virtual_property=class_virtual_properties.get(index)?;
+
+	//Get source property
+	let variant=properties.get(virtual_property.property)?;
+
+	//Transform Source property with provided function
+	(virtual_property.pointer)(variant)
+}