2024-10-04 01:04:41 +00:00
use mlua ::IntoLua ;
2024-09-21 01:21:00 +00:00
use rbx_types ::Ref ;
2024-10-04 01:04:41 +00:00
use rbx_dom_weak ::{ InstanceBuilder , WeakDom } ;
2024-09-19 01:47:38 +00:00
use super ::vector3 ::Vector3 ;
// LMAO look at this function!
fn dom < T > ( lua :& mlua ::Lua , mut f :impl FnMut ( & mut WeakDom ) ->mlua ::Result < T > ) ->mlua ::Result < T > {
2024-09-20 20:51:17 +00:00
let mut dom = lua . app_data_mut ::< & 'static mut WeakDom > ( ) . ok_or ( mlua ::Error ::runtime ( " DataModel missing " ) ) ? ;
f ( & mut * dom )
2024-09-19 01:47:38 +00:00
}
2024-09-21 00:54:16 +00:00
fn coerce_float32 ( value :& mlua ::Value ) ->Option < f32 > {
2024-09-19 03:16:14 +00:00
match value {
& mlua ::Value ::Integer ( i ) = > Some ( i as f32 ) ,
& mlua ::Value ::Number ( f ) = > Some ( f as f32 ) ,
_ = > None ,
}
}
2024-09-21 20:26:20 +00:00
fn get_full_name ( dom :& rbx_dom_weak ::WeakDom , instance :& rbx_dom_weak ::Instance ) ->String {
let mut full_name = instance . name . clone ( ) ;
let mut pref = instance . parent ( ) ;
while let Some ( parent ) = dom . get_by_ref ( pref ) {
full_name . insert ( 0 , '.' ) ;
full_name . insert_str ( 0 , parent . name . as_str ( ) ) ;
pref = parent . parent ( ) ;
}
full_name
}
2024-10-04 01:55:50 +00:00
//workaround until I have an enum of classes
struct Dereferent ( Ref ) ;
impl mlua ::UserData for Dereferent { }
type_from_lua_userdata! ( Dereferent ) ;
2024-09-21 01:21:00 +00:00
trait Referent {
fn referent ( & self ) ->Ref ;
fn get < ' a > ( & self , dom :& ' a WeakDom ) ->mlua ::Result < & ' a rbx_dom_weak ::Instance > {
dom . get_by_ref ( self . referent ( ) ) . ok_or ( mlua ::Error ::runtime ( " Instance missing " ) )
}
fn get_mut < ' a > ( & self , dom :& ' a mut WeakDom ) ->mlua ::Result < & ' a mut rbx_dom_weak ::Instance > {
dom . get_by_ref_mut ( self . referent ( ) ) . ok_or ( mlua ::Error ::runtime ( " Instance missing " ) )
}
}
2024-09-21 20:26:20 +00:00
macro_rules ! class {
( $class :ident ) = > {
pub struct $class {
referent :Ref ,
}
impl $class {
pub const fn new ( referent :Ref ) ->Self {
Self { referent }
}
}
impl Referent for $class {
fn referent ( & self ) ->Ref {
self . referent
}
}
2024-10-04 01:53:17 +00:00
type_from_lua_userdata! ( $class ) ;
2024-09-21 20:26:20 +00:00
} ;
}
macro_rules ! class_composition {
( $class :ident , ( $( $superclass :ident ) , * ) ) = > {
impl mlua ::UserData for $class {
fn add_fields < ' lua , F :mlua ::UserDataFields < ' lua , Self > > ( fields :& mut F ) {
2024-10-04 01:55:50 +00:00
fields . add_field_method_get ( " Referent " , | _ , this | {
Ok ( Dereferent ( this . referent ( ) ) )
} ) ;
2024-09-21 20:26:20 +00:00
$(
$superclass ::composition_add_fields ( fields ) ;
) *
}
fn add_methods < ' lua , M :mlua ::UserDataMethods < ' lua , Self > > ( methods :& mut M ) {
$(
$superclass ::composition_add_methods ( methods ) ;
) *
}
}
} ;
2024-09-21 01:21:00 +00:00
}
2024-09-21 20:26:20 +00:00
class! ( Instance ) ;
class_composition! ( Instance , ( Instance ) ) ;
2024-09-21 01:21:00 +00:00
impl Instance {
2024-09-21 20:26:20 +00:00
fn composition_add_fields < ' lua , T :Referent , F :mlua ::UserDataFields < ' lua , T > > ( fields :& mut F ) {
2024-09-19 01:47:38 +00:00
fields . add_field_method_get ( " Parent " , | lua , this | {
dom ( lua , | dom | {
let instance = this . get ( dom ) ? ;
Ok ( Instance ::new ( instance . parent ( ) ) )
} )
} ) ;
2024-10-04 01:55:50 +00:00
fields . add_field_method_set ( " Parent " , | lua , this , val :mlua ::AnyUserData | {
let Dereferent ( referent ) = mlua ::AnyUserDataExt ::get ( & val , " Referent " ) ? ;
2024-09-19 01:47:38 +00:00
dom ( lua , | dom | {
2024-10-04 01:55:50 +00:00
dom . transfer_within ( this . referent ( ) , referent ) ;
2024-09-19 01:47:38 +00:00
Ok ( ( ) )
} )
} ) ;
2024-09-21 01:01:33 +00:00
fields . add_field_method_get ( " Name " , | lua , this | {
dom ( lua , | dom | {
let instance = this . get ( dom ) ? ;
Ok ( instance . name . clone ( ) )
} )
} ) ;
2024-09-21 21:21:23 +00:00
fields . add_field_method_set ( " Name " , | lua , this , val :mlua ::String | {
dom ( lua , | dom | {
2024-09-21 01:01:33 +00:00
let instance = this . get_mut ( dom ) ? ;
//Why does this need to be cloned?
2024-09-21 21:21:23 +00:00
instance . name = val . to_str ( ) ? . to_owned ( ) ;
2024-09-21 01:01:33 +00:00
Ok ( ( ) )
} )
} ) ;
2024-10-04 01:03:42 +00:00
fields . add_field_method_get ( " ClassName " , | lua , this | {
dom ( lua , | dom | {
let instance = this . get ( dom ) ? ;
Ok ( instance . class . clone ( ) )
} )
} ) ;
2024-09-19 01:47:38 +00:00
}
2024-09-21 20:26:20 +00:00
fn composition_add_methods < ' lua , T :Referent , M :mlua ::UserDataMethods < ' lua , T > > ( methods :& mut M ) {
2024-09-19 03:16:14 +00:00
methods . add_method ( " GetChildren " , | lua , this , _ :( ) |
dom ( lua , | dom | {
let instance = this . get ( dom ) ? ;
let children :Vec < _ > = instance
. children ( )
. iter ( )
. copied ( )
. map ( Instance ::new )
. collect ( ) ;
Ok ( children )
} )
) ;
2024-09-19 03:16:28 +00:00
methods . add_method ( " GetDescendants " , | lua , this , _ :( ) |
dom ( lua , | dom | {
let children :Vec < _ > = dom
2024-09-21 01:21:00 +00:00
. descendants_of ( this . referent ( ) )
2024-09-19 03:16:28 +00:00
. map ( | instance |
Instance ::new ( instance . referent ( ) )
)
. collect ( ) ;
Ok ( children )
} )
) ;
2024-09-19 03:16:14 +00:00
methods . add_method ( " IsA " , | lua , this , classname :mlua ::String |
dom ( lua , | dom | {
let instance = this . get ( dom ) ? ;
Ok ( crate ::context ::class_is_a ( instance . class . as_str ( ) , classname . to_str ( ) ? ) )
} )
) ;
2024-10-04 02:09:02 +00:00
methods . add_meta_function ( mlua ::MetaMethod ::Index , | lua , ( this , index ) :( Self , mlua ::String ) | {
let index_str = index . to_str ( ) ? ;
dom ( lua , | dom | {
let instance = this . get ( dom ) ? ;
//find a child with a matching name
let maybe_child = instance . children ( )
. iter ( )
. find ( | & & r |
dom . get_by_ref ( r )
. is_some_and ( | instance | instance . name = = index_str )
) ;
match maybe_child {
Some ( & referent ) = > Instance ::new ( referent ) . into_lua ( lua ) ,
None = > mlua ::Value ::Nil . into_lua ( lua ) ,
}
} )
} ) ;
2024-09-19 01:47:38 +00:00
methods . add_meta_function ( mlua ::MetaMethod ::NewIndex , | lua , ( this , index , value ) :( Self , mlua ::String , mlua ::Value ) |
dom ( lua , | dom | {
2024-09-21 01:21:00 +00:00
//println!("__newindex t={this:?} i={index:?} v={value:?}");
2024-09-19 01:47:38 +00:00
let instance = this . get_mut ( dom ) ? ;
let index_str = index . to_str ( ) ? ;
2024-09-21 00:54:43 +00:00
let db = rbx_reflection_database ::get ( ) ;
let class = db . classes . get ( instance . class . as_str ( ) ) . ok_or ( mlua ::Error ::runtime ( " Class missing " ) ) ? ;
let property = db . find_default_property ( class , index_str ) . ok_or ( mlua ::Error ::runtime ( format! ( " Property ' {index_str} ' missing on class ' {} ' " , class . name ) ) ) ? ;
2024-09-19 01:47:38 +00:00
match property {
rbx_types ::Variant ::Vector3 ( _ ) = > {
let typed_value :Vector3 = value . as_userdata ( ) . ok_or ( mlua ::Error ::runtime ( " Expected Userdata " ) ) ? . take ( ) ? ;
instance . properties . insert ( index_str . to_owned ( ) , rbx_types ::Variant ::Vector3 ( typed_value . into ( ) ) ) ;
} ,
2024-09-19 03:16:14 +00:00
rbx_types ::Variant ::Float32 ( _ ) = > {
2024-09-21 00:54:16 +00:00
let typed_value :f32 = coerce_float32 ( & value ) . ok_or ( mlua ::Error ::runtime ( " Expected f32 " ) ) ? ;
2024-09-19 03:16:14 +00:00
instance . properties . insert ( index_str . to_owned ( ) , rbx_types ::Variant ::Float32 ( typed_value ) ) ;
2024-09-19 01:47:38 +00:00
} ,
2024-10-04 01:05:44 +00:00
rbx_types ::Variant ::Enum ( _ ) = > {
let typed_value :super ::r#enum ::Enum = value . as_userdata ( ) . ok_or ( mlua ::Error ::runtime ( " Expected Userdata " ) ) ? . take ( ) ? ;
instance . properties . insert ( index_str . to_owned ( ) , rbx_types ::Variant ::Enum ( typed_value . into ( ) ) ) ;
} ,
2024-10-04 01:05:39 +00:00
rbx_types ::Variant ::Color3 ( _ ) = > {
let typed_value :super ::color3 ::Color3 = value . as_userdata ( ) . ok_or ( mlua ::Error ::runtime ( " Expected Userdata " ) ) ? . take ( ) ? ;
instance . properties . insert ( index_str . to_owned ( ) , rbx_types ::Variant ::Color3 ( typed_value . into ( ) ) ) ;
} ,
2024-09-21 21:25:56 +00:00
other = > return Err ( mlua ::Error ::runtime ( format! ( " Unimplemented property type: {other:?} " ) ) ) ,
2024-09-19 01:47:38 +00:00
}
Ok ( ( ) )
} )
) ;
}
}
2024-09-21 01:21:00 +00:00
2024-09-21 20:26:20 +00:00
class! ( DataModel ) ;
class_composition! ( DataModel , ( Instance , DataModel ) ) ;
2024-09-21 01:21:00 +00:00
impl DataModel {
2024-09-21 20:26:20 +00:00
fn composition_add_fields < ' lua , T :Referent , F :mlua ::UserDataFields < ' lua , T > > ( fields :& mut F ) {
2024-10-04 01:04:41 +00:00
fields . add_field_method_get ( " PlaceId " , | lua , this | {
Ok ( mlua ::Value ::Integer ( 0 ) )
} ) ;
2024-09-21 20:26:20 +00:00
}
fn composition_add_methods < ' lua , T , M :mlua ::UserDataMethods < ' lua , T > > ( methods :& mut M ) {
2024-09-21 01:21:00 +00:00
methods . add_method ( " GetService " , | lua , this , service :String |
dom ( lua , | dom | {
match service . as_str ( ) {
2024-10-04 01:04:41 +00:00
" Lighting " = > {
let referent = dom . root ( )
. children ( )
. iter ( )
. find ( | & & c |
dom . get_by_ref ( c ) . is_some_and ( | c | c . class = = " Lighting " )
) . map ( | r | * r )
. unwrap_or_else ( | |
dom . insert ( dom . root_ref ( ) , InstanceBuilder ::new ( " Lighting " ) )
) ;
Lighting ::new ( referent ) . into_lua ( lua )
} ,
other = > Err ::< mlua ::Value , _ > ( mlua ::Error ::runtime ( format! ( " Service ' {other} ' not supported " ) ) ) ,
2024-09-21 01:21:00 +00:00
}
} )
) ;
}
}
2024-10-04 00:19:47 +00:00
class! ( Workspace ) ;
class_composition! ( Workspace , ( Instance ) ) ;
2024-09-21 20:26:20 +00:00
class! ( Lighting ) ;
class_composition! ( Lighting , ( Instance ) ) ;
#[ derive(Debug) ]
pub enum GetScriptError {
NoScript ,
NoSource ,
2024-09-21 01:21:00 +00:00
}
2024-09-21 20:26:20 +00:00
class! ( Script ) ;
class_composition! ( Script , ( Instance ) ) ;
impl Script {
pub fn get_name_source ( & self , context :& crate ::context ::Context ) ->Result < ( String , String ) , GetScriptError > {
let instance = context . dom . get_by_ref ( self . referent ) . ok_or ( GetScriptError ::NoScript ) ? ;
let source = match instance . properties . get ( " Source " ) . ok_or ( GetScriptError ::NoSource ) ? {
rbx_dom_weak ::types ::Variant ::String ( s ) = > s . clone ( ) ,
_ = > Err ( GetScriptError ::NoSource ) ? ,
} ;
Ok ( ( get_full_name ( & context . dom , instance ) , source ) )
2024-09-21 01:21:00 +00:00
}
}
2024-10-04 01:04:52 +00:00
class! ( Terrain ) ;
class_composition! ( Terrain , ( Instance , Terrain ) ) ;
impl Terrain {
fn composition_add_fields < ' lua , T :Referent , F :mlua ::UserDataFields < ' lua , T > > ( fields :& mut F ) {
}
fn composition_add_methods < ' lua , T , M :mlua ::UserDataMethods < ' lua , T > > ( methods :& mut M ) {
methods . add_method ( " FillBlock " , | lua , this , _ :( super ::cframe ::CFrame , Vector3 , super ::r#enum ::Enum ) |
Ok ( ( ) ) //Ok(mlua::Value::Nil)
)
}
}