Compare commits
97 Commits
Author | SHA1 | Date | |
---|---|---|---|
e832fccacb
|
|||
8ab910e18f
|
|||
b46fb04a5d
|
|||
6e6bafe719
|
|||
5f0ddc2f28
|
|||
ba1c1ec8c6
|
|||
67cafd8cbb
|
|||
ca88eb1cad
|
|||
2bf34fd04c
|
|||
b5da30fd9a
|
|||
95fffbbf42
|
|||
9a636d5b50
|
|||
2c8954c2b4
|
|||
985a703811
|
|||
37dbe35222
|
|||
f0de24d161
|
|||
7d5ff55803
|
|||
7fffe05751
|
|||
2ac38efef6
|
|||
5881e593b2
|
|||
64a4499544
|
|||
fca9e1c325
|
|||
6a88003b09
|
|||
08b358c192
|
|||
62d9bcff81
|
|||
08f9163605
|
|||
b507624d91
|
|||
d8358ec25c
|
|||
e673a12beb
|
|||
18269423a5
|
|||
1d82799400
|
|||
3a9fdebb4d
|
|||
f8ef17e3f5
|
|||
8b2f37d3d5
|
|||
16dfe7524f
|
|||
3d399635d7
|
|||
8bfc201d1f
|
|||
bb8e131464
|
|||
05bbe05979
|
|||
17a2199f36
|
|||
84db6503f9
|
|||
5b5f356863
|
|||
d07571519e
|
|||
6464343428
|
|||
534b45d7dd
|
|||
3ff8ccf58d
|
|||
67d2a3e398
|
|||
c1f8a13888
|
|||
e11af82443
|
|||
5b1d5502d8
|
|||
f48d86fe56
|
|||
877964be1f
|
|||
62d983ef2a
|
|||
8b7a4ee8c1
|
|||
7ebcfa7bc3
|
|||
c98f53a151
|
|||
94025b52e2
|
|||
213d2f3f6a
|
|||
438a22bd86
|
|||
e6fb8437b3
|
|||
8b6166f48e
|
|||
3f89f2c1c5
|
|||
c6ebb179a1
|
|||
14fa450e3e
|
|||
448f40a5d8
|
|||
4ecdcec17c
|
|||
261b88ada6
|
|||
5ea0a1b07d
|
|||
3b8d32913e
|
|||
3928431ac7
|
|||
6ecf403a4f
|
|||
1f7ee20c1e
|
|||
19c30c8701
|
|||
dabb25b3d3
|
|||
78593200eb
|
|||
7649d30b55
|
|||
6af8dd3c1f
|
|||
861a8afc47
|
|||
f0b2470039
|
|||
c0ad20d54c
|
|||
9aea73e134
|
|||
b34d3f89f9
|
|||
5600948d8a
|
|||
d722bcb46f
|
|||
081c95190f
|
|||
d1f4e2132f
|
|||
5317d96243
|
|||
8bb20ffe81
|
|||
03b16db10a
|
|||
f0d4915fba
|
|||
6e8da50b35
|
|||
615372aad5
|
|||
08d47b0f63
|
|||
08b5445838
|
|||
fa07d16cf4
|
|||
327688d79e
|
|||
1662d814ec
|
Cargo.lock
engine/physics/src
lib
map-tool
strafe-client/src
107
Cargo.lock
generated
107
Cargo.lock
generated
@ -40,12 +40,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom 0.2.15",
|
||||
"getrandom 0.2.16",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy 0.7.35",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash_macro"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e73c78bacfd5857793b9b6813ab94605bb1699de9915f3285472f6633748e773"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
@ -61,6 +73,12 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1"
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "android-activity"
|
||||
version = "0.6.0"
|
||||
@ -360,9 +378,9 @@ checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
|
||||
|
||||
[[package]]
|
||||
name = "blake3"
|
||||
version = "1.8.1"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "389a099b34312839e16420d499a9cad9650541715937ffbdd40d36f49e77eeb3"
|
||||
checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
@ -559,9 +577,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.36"
|
||||
version = "4.5.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04"
|
||||
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -569,9 +587,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.36"
|
||||
version = "4.5.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
|
||||
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -1094,9 +1112,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
@ -1251,12 +1269,27 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash_str"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "deb4f3e9ad2b19e6290ea4c648770a69ac4687c692a2a6175aaef2a398552da4"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ahash_macro",
|
||||
"bumpalo",
|
||||
"hashbrown",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
@ -1848,9 +1881,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.11"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||
checksum = "c9627da5196e5d8ed0b0495e61e518847578da83483c37288316d9b2e03a7f72"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
@ -2900,9 +2933,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.94"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -2992,7 +3025,7 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3099,10 +3132,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rbx_binary"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9573fee5e073d7b303f475c285197fdc8179468de66ca60ee115a58fbac99296"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"hash_str",
|
||||
"log",
|
||||
"lz4",
|
||||
"profiling",
|
||||
@ -3116,13 +3148,11 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rbx_dom_weak"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04425cf6e9376e5486f4fb35906c120d1b1b45618a490318cf563fab1fa230a9"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"hash_str",
|
||||
"rbx_types",
|
||||
"serde",
|
||||
"ustr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3138,9 +3168,8 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rbx_reflection"
|
||||
version = "5.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b6d0d62baa613556b058a5f94a53b01cf0ccde0ea327ce03056e335b982e77e"
|
||||
dependencies = [
|
||||
"hash_str",
|
||||
"rbx_types",
|
||||
"serde",
|
||||
"thiserror 1.0.69",
|
||||
@ -3148,9 +3177,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rbx_reflection_database"
|
||||
version = "1.0.1+roblox-666"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ea11f26cfddc57a136781baed2e68eda5a3aa0735152d1562d65b1909c8d65d"
|
||||
version = "1.0.3+roblox-670"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"rbx_reflection",
|
||||
@ -3161,8 +3188,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,11 +3201,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rbx_xml"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb623833c31cc43bbdaeb32f5e91db8ecd63fc46e438d0d268baf9e61539cf1c"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64 0.13.1",
|
||||
"hash_str",
|
||||
"log",
|
||||
"rbx_dom_weak",
|
||||
"rbx_reflection",
|
||||
@ -3228,7 +3252,7 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
|
||||
dependencies = [
|
||||
"getrandom 0.2.15",
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
@ -3333,7 +3357,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom 0.2.15",
|
||||
"getrandom 0.2.16",
|
||||
"libc",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
@ -3363,7 +3387,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "roblox_emulator"
|
||||
version = "0.4.7"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"mlua",
|
||||
@ -4152,9 +4176,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.14"
|
||||
version = "0.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034"
|
||||
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@ -4314,19 +4338,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"
|
||||
@ -4358,9 +4369,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "vbsp"
|
||||
version = "0.8.2"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75944cc43c7d4ebcb3bd97a875b4f3307d79328de16f8dcd57745cba926b784b"
|
||||
checksum = "fdab0507169ff47ea95acab2d08d91c7a31455e272b134879757d19c87075cb1"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"arrayvec",
|
||||
|
@ -1006,31 +1006,30 @@ impl PhysicsData{
|
||||
let mut used_meshes=Vec::new();
|
||||
let mut physics_mesh_id_from_model_mesh_id=HashMap::<MeshId,PhysicsMeshId>::new();
|
||||
for (model_id,model) in map.models.iter().enumerate(){
|
||||
//TODO: use .entry().or_insert_with(||{
|
||||
let attr_id=if let Some(&attr_id)=physics_attr_id_from_model_attr_id.get(&model.attributes){
|
||||
attr_id
|
||||
}else{
|
||||
//check if it's real
|
||||
match map.attributes.get(model.attributes.get() as usize).and_then(|m_attr|{
|
||||
PhysicsCollisionAttributes::try_from(m_attr).map_or(None,|p_attr|{
|
||||
let attr_id=match p_attr{
|
||||
PhysicsCollisionAttributes::Contact(attr)=>{
|
||||
let attr_id=ContactAttributesId::new(used_contact_attributes.len() as u32);
|
||||
used_contact_attributes.push(attr);
|
||||
PhysicsAttributesId::Contact(attr_id)
|
||||
},
|
||||
PhysicsCollisionAttributes::Intersect(attr)=>{
|
||||
let attr_id=IntersectAttributesId::new(used_intersect_attributes.len() as u32);
|
||||
used_intersect_attributes.push(attr);
|
||||
PhysicsAttributesId::Intersect(attr_id)
|
||||
},
|
||||
};
|
||||
physics_attr_id_from_model_attr_id.insert(model.attributes,attr_id);
|
||||
Some(attr_id)
|
||||
})
|
||||
}){
|
||||
Some(attr_id)=>attr_id,
|
||||
None=>continue,
|
||||
let attr_id=match physics_attr_id_from_model_attr_id.entry(model.attributes){
|
||||
std::collections::hash_map::Entry::Occupied(entry)=>*entry.get(),
|
||||
std::collections::hash_map::Entry::Vacant(entry)=>{
|
||||
//check if it's real
|
||||
match map.attributes.get(model.attributes.get() as usize).and_then(|m_attr|{
|
||||
PhysicsCollisionAttributes::try_from(m_attr).map_or(None,|p_attr|{
|
||||
let attr_id=match p_attr{
|
||||
PhysicsCollisionAttributes::Contact(attr)=>{
|
||||
let attr_id=ContactAttributesId::new(used_contact_attributes.len() as u32);
|
||||
used_contact_attributes.push(attr);
|
||||
PhysicsAttributesId::Contact(attr_id)
|
||||
},
|
||||
PhysicsCollisionAttributes::Intersect(attr)=>{
|
||||
let attr_id=IntersectAttributesId::new(used_intersect_attributes.len() as u32);
|
||||
used_intersect_attributes.push(attr);
|
||||
PhysicsAttributesId::Intersect(attr_id)
|
||||
},
|
||||
};
|
||||
Some(*entry.insert(attr_id))
|
||||
})
|
||||
}){
|
||||
Some(attr_id)=>attr_id,
|
||||
None=>continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
let mesh_id=if let Some(&mesh_id)=physics_mesh_id_from_model_mesh_id.get(&model.mesh){
|
||||
|
@ -13,7 +13,7 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
|
||||
glam = "0.30.0"
|
||||
strafesnet_common = { version = "0.6.0", path = "../common", registry = "strafesnet" }
|
||||
strafesnet_deferred_loader = { version = "0.5.0", path = "../deferred_loader", registry = "strafesnet" }
|
||||
vbsp = "0.8.0"
|
||||
vbsp = "0.9.1"
|
||||
vbsp-entities-css = "0.6.0"
|
||||
vmdl = "0.2.0"
|
||||
vpk = "0.3.0"
|
||||
|
@ -440,11 +440,11 @@ impl ModesBuilder{
|
||||
}
|
||||
NormalizedModes::new(modes.into_iter().map(|mode_builder|NormalizedMode(mode_builder.mode)).collect())
|
||||
}
|
||||
pub fn insert_mode(&mut self,mode_id:ModeId,mode:Mode){
|
||||
assert!(self.modes.insert(mode_id,mode).is_none(),"Cannot replace existing mode");
|
||||
pub fn insert_mode(&mut self,mode_id:ModeId,mode:Mode)->Result<(),ExistingEntryError>{
|
||||
error_if_exists(self.modes.insert(mode_id,mode))
|
||||
}
|
||||
pub fn insert_stage(&mut self,mode_id:ModeId,stage_id:StageId,stage:Stage){
|
||||
assert!(self.stages.entry(mode_id).or_insert(HashMap::new()).insert(stage_id,stage).is_none(),"Cannot replace existing stage");
|
||||
pub fn insert_stage(&mut self,mode_id:ModeId,stage_id:StageId,stage:Stage)->Result<(),ExistingEntryError>{
|
||||
error_if_exists(self.stages.entry(mode_id).or_insert(HashMap::new()).insert(stage_id,stage))
|
||||
}
|
||||
pub fn push_mode_update(&mut self,mode_id:ModeId,mode_update:ModeUpdate){
|
||||
self.mode_updates.push((mode_id,mode_update));
|
||||
@ -453,3 +453,12 @@ impl ModesBuilder{
|
||||
// self.stage_updates.push((mode_id,stage_id,stage_update));
|
||||
// }
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExistingEntryError;
|
||||
fn error_if_exists<T>(value:Option<T>)->Result<(),ExistingEntryError>{
|
||||
match value{
|
||||
Some(_)=>Err(ExistingEntryError),
|
||||
None=>Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -204,13 +204,19 @@ macro_rules! impl_matrix_named_fields_shape {
|
||||
type Target=$struct_outer<Vector<$size_inner,T>>;
|
||||
#[inline]
|
||||
fn deref(&self)->&Self::Target{
|
||||
unsafe{core::mem::transmute(&self.array)}
|
||||
// This cast is valid because Matrix has #[repr(transparent)]
|
||||
let ptr=&self.array as *const [[T;$size_inner];$size_outer] as *const Self::Target;
|
||||
// SAFETY: this pointer is non-null because it comes from a reference
|
||||
unsafe{&*ptr}
|
||||
}
|
||||
}
|
||||
impl<T> core::ops::DerefMut for Matrix<$size_outer,$size_inner,T>{
|
||||
#[inline]
|
||||
fn deref_mut(&mut self)->&mut Self::Target{
|
||||
unsafe{core::mem::transmute(&mut self.array)}
|
||||
// This cast is valid because Matrix has #[repr(transparent)]
|
||||
let ptr=&mut self.array as *mut [[T;$size_inner];$size_outer] as *mut Self::Target;
|
||||
// SAFETY: this pointer is non-null because it comes from a reference
|
||||
unsafe{&mut*ptr}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -330,13 +330,19 @@ macro_rules! impl_vector_named_fields {
|
||||
type Target=$struct<T>;
|
||||
#[inline]
|
||||
fn deref(&self)->&Self::Target{
|
||||
unsafe{core::mem::transmute(&self.array)}
|
||||
// This cast is valid because Vector has #[repr(transparent)]
|
||||
let ptr=&self.array as *const [T;$size] as *const Self::Target;
|
||||
// SAFETY: this pointer is non-null because it comes from a reference
|
||||
unsafe{&*ptr}
|
||||
}
|
||||
}
|
||||
impl<T> core::ops::DerefMut for Vector<$size,T>{
|
||||
#[inline]
|
||||
fn deref_mut(&mut self)->&mut Self::Target{
|
||||
unsafe{core::mem::transmute(&mut self.array)}
|
||||
// This cast is valid because Vector has #[repr(transparent)]
|
||||
let ptr=&mut self.array as *mut [T;$size] as *mut Self::Target;
|
||||
// SAFETY: this pointer is non-null because it comes from a reference
|
||||
unsafe{&mut*ptr}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 = "1.0.0"
|
||||
rbx_dom_weak = "3.0.0"
|
||||
rbx_mesh = "0.3.1"
|
||||
rbx_reflection_database = "1.0.0"
|
||||
rbx_xml = "1.0.0"
|
||||
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.4.7", path = "../roblox_emulator", 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" }
|
||||
strafesnet_deferred_loader = { version = "0.5.0", path = "../deferred_loader", registry = "strafesnet" }
|
||||
|
@ -1,5 +1,6 @@
|
||||
use std::io::Read;
|
||||
use rbx_dom_weak::WeakDom;
|
||||
use roblox_emulator::context::Context;
|
||||
use strafesnet_deferred_loader::deferred_loader::{LoadFailureMode,MeshDeferredLoader,RenderConfigDeferredLoader};
|
||||
|
||||
mod rbx;
|
||||
@ -20,47 +21,38 @@ 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 into_place(self)->Place{
|
||||
let Self{mut dom}=self;
|
||||
let context=roblox_emulator::context::Context::from_mut(&mut dom);
|
||||
let services=context.convert_into_place();
|
||||
Place{dom,services}
|
||||
}
|
||||
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{
|
||||
dom:WeakDom,
|
||||
services:roblox_emulator::context::Services,
|
||||
context:Context,
|
||||
}
|
||||
impl Place{
|
||||
pub fn new(dom:WeakDom)->Option<Self>{
|
||||
let context=roblox_emulator::context::Context::from_ref(&dom);
|
||||
Some(Self{
|
||||
services:context.find_services()?,
|
||||
dom,
|
||||
pub fn new(dom:WeakDom)->Result<Self,roblox_emulator::context::ServicesError>{
|
||||
let context=Context::from_place(dom)?;
|
||||
Ok(Self{
|
||||
context,
|
||||
})
|
||||
}
|
||||
pub fn run_scripts(&mut self){
|
||||
let Place{dom,services}=self;
|
||||
let Place{context}=self;
|
||||
let runner=roblox_emulator::runner::Runner::new().unwrap();
|
||||
let context=roblox_emulator::context::Context::from_mut(dom);
|
||||
let scripts=context.scripts();
|
||||
let runnable=runner.runnable_context_with_services(context,services).unwrap();
|
||||
let runnable=runner.runnable_context(context).unwrap();
|
||||
for script in scripts{
|
||||
if let Err(e)=runnable.run_script(script){
|
||||
println!("runner error: {e}");
|
||||
@ -73,7 +65,15 @@ impl Place{
|
||||
}
|
||||
impl AsRef<WeakDom> for Place{
|
||||
fn as_ref(&self)->&WeakDom{
|
||||
&self.dom
|
||||
self.context.as_ref()
|
||||
}
|
||||
}
|
||||
impl From<Model> for Place{
|
||||
fn from(model:Model)->Self{
|
||||
let context=Context::from_model(model.dom);
|
||||
Self{
|
||||
context,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,9 +94,9 @@ impl std::error::Error for ReadError{}
|
||||
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),
|
||||
match peek.get(0..8){
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use std::io::Read;
|
||||
use rbx_dom_weak::ustr;
|
||||
use rbxassetid::{RobloxAssetId,RobloxAssetIdParseErr};
|
||||
use strafesnet_common::model::Mesh;
|
||||
use strafesnet_deferred_loader::{loader::Loader,texture::Texture};
|
||||
@ -7,6 +6,8 @@ use strafesnet_deferred_loader::{loader::Loader,texture::Texture};
|
||||
use crate::data::RobloxMeshBytes;
|
||||
use crate::rbx::RobloxPartDescription;
|
||||
|
||||
use rbx_dom_weak::hstr;
|
||||
|
||||
fn read_entire_file(path:impl AsRef<std::path::Path>)->Result<Vec<u8>,std::io::Error>{
|
||||
let mut file=std::fs::File::open(path)?;
|
||||
let mut data=Vec::new();
|
||||
@ -164,7 +165,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);
|
||||
};
|
||||
@ -172,12 +173,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(&ustr("PhysicsData")){
|
||||
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(hstr!("PhysicsData")){
|
||||
physics_data=data.as_ref();
|
||||
}
|
||||
}
|
||||
if mesh_data.is_empty(){
|
||||
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(&ustr("MeshData")){
|
||||
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(hstr!("MeshData")){
|
||||
mesh_data=data.as_ref();
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use crate::loader::MeshIndex;
|
||||
use crate::primitives::{self,CubeFace,CubeFaceDescription,WedgeFaceDescription,CornerWedgeFaceDescription,FaceDescription,Primitives};
|
||||
use rbx_dom_weak::ustr;
|
||||
use strafesnet_common::aabb::Aabb;
|
||||
use strafesnet_common::map;
|
||||
use strafesnet_common::model;
|
||||
@ -14,31 +13,27 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
use rbx_dom_weak::{hstr,HashStr};
|
||||
|
||||
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:&HashStr,
|
||||
){
|
||||
let instance=instance;
|
||||
let db=rbx_reflection_database::get();
|
||||
let Some(superclass)=db.classes.get(superclass)else{
|
||||
return;
|
||||
};
|
||||
objects.extend(
|
||||
dom.descendants_of(instance.referent()).filter_map(|instance|{
|
||||
let class=db.classes.get(instance.class)?;
|
||||
db.has_superclass(class,superclass).then(||instance.referent())
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
fn planar64_affine3_from_roblox(cf:&rbx_dom_weak::types::CFrame,size:&rbx_dom_weak::types::Vector3)->Planar64Affine3{
|
||||
Planar64Affine3::new(
|
||||
Planar64Mat3::from_cols([
|
||||
@ -86,7 +81,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
|
||||
gameplay_style::StyleModifiers::roblox_bhop(),
|
||||
model_id
|
||||
)
|
||||
);
|
||||
).unwrap();
|
||||
},
|
||||
"MapFinish"=>{
|
||||
force_can_collide=false;
|
||||
@ -132,7 +127,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
|
||||
gameplay_style::StyleModifiers::roblox_bhop(),
|
||||
model_id
|
||||
)
|
||||
);
|
||||
).unwrap();
|
||||
},
|
||||
"WormholeOut"=>{
|
||||
//the PhysicsModelId has to exist for it to be teleported to!
|
||||
@ -161,7 +156,7 @@ fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:mode
|
||||
ModeId::MAIN,
|
||||
stage_id,
|
||||
Stage::empty(model_id),
|
||||
);
|
||||
).unwrap();
|
||||
//TODO: let denormalize handle this
|
||||
StageElementBehaviour::SpawnAt
|
||||
},
|
||||
@ -412,27 +407,30 @@ fn get_texture_description<'a>(
|
||||
//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,hstr!("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 (
|
||||
Some(rbx_dom_weak::types::Variant::ContentId(content)),
|
||||
Some(rbx_dom_weak::types::Variant::Content(content)),
|
||||
Some(rbx_dom_weak::types::Variant::Enum(normalid)),
|
||||
Some(rbx_dom_weak::types::Variant::Color3(decal_color3)),
|
||||
Some(rbx_dom_weak::types::Variant::Float32(decal_transparency)),
|
||||
)=(
|
||||
decal.properties.get(&ustr("Texture")),
|
||||
decal.properties.get(&ustr("Face")),
|
||||
decal.properties.get(&ustr("Color3")),
|
||||
decal.properties.get(&ustr("Transparency")),
|
||||
decal.properties.get(hstr!("TextureContent")),
|
||||
decal.properties.get(hstr!("Face")),
|
||||
decal.properties.get(hstr!("Color3")),
|
||||
decal.properties.get(hstr!("Transparency")),
|
||||
)else{
|
||||
println!("Decal is missing a required property");
|
||||
continue;
|
||||
};
|
||||
let texture_id=Some(content.as_str());
|
||||
let texture_id=match content.value(){
|
||||
rbx_dom_weak::types::ContentType::Uri(uri)=>Some(uri.as_str()),
|
||||
_=>None,
|
||||
};
|
||||
let render_id=render_config_deferred_loader.acquire_render_config_id(texture_id);
|
||||
let Ok(cube_face)=normalid.to_u32().try_into()else{
|
||||
println!("NormalId is invalid");
|
||||
@ -446,10 +444,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(&ustr("OffsetStudsU")),
|
||||
decal.properties.get(&ustr("OffsetStudsV")),
|
||||
decal.properties.get(&ustr("StudsPerTileU")),
|
||||
decal.properties.get(&ustr("StudsPerTileV")),
|
||||
decal.properties.get(hstr!("OffsetStudsU")),
|
||||
decal.properties.get(hstr!("OffsetStudsV")),
|
||||
decal.properties.get(hstr!("StudsPerTileU")),
|
||||
decal.properties.get(hstr!("StudsPerTileV")),
|
||||
)
|
||||
{
|
||||
let (size_u,size_v)=match cube_face{
|
||||
@ -536,7 +534,7 @@ 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");
|
||||
recursive_collect_superclass(&mut object_refs, &dom, dom.root(),hstr!("BasePart"));
|
||||
for object_ref in object_refs {
|
||||
if let Some(object)=dom.get_by_ref(object_ref){
|
||||
if let (
|
||||
@ -547,12 +545,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(&ustr("CFrame")),
|
||||
object.properties.get(&ustr("Size")),
|
||||
object.properties.get(&ustr("Velocity")),
|
||||
object.properties.get(&ustr("Transparency")),
|
||||
object.properties.get(&ustr("Color")),
|
||||
object.properties.get(&ustr("CanCollide")),
|
||||
object.properties.get(hstr!("CFrame")),
|
||||
object.properties.get(hstr!("Size")),
|
||||
object.properties.get(hstr!("Velocity")),
|
||||
object.properties.get(hstr!("Transparency")),
|
||||
object.properties.get(hstr!("Color")),
|
||||
object.properties.get(hstr!("CanCollide")),
|
||||
)
|
||||
{
|
||||
let model_transform=planar64_affine3_from_roblox(cf,size);
|
||||
@ -571,7 +569,7 @@ 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(&ustr("Shape")){
|
||||
"Part"=>if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get(hstr!("Shape")){
|
||||
Shape::Primitive(shape.to_u32().try_into().expect("Funky roblox PartType"))
|
||||
}else{
|
||||
panic!("Part has no Shape!");
|
||||
@ -645,9 +643,9 @@ pub fn convert<'a>(
|
||||
Some(rbx_dom_weak::types::Variant::Content(texture_content)),
|
||||
)=(
|
||||
// mesh must exist
|
||||
object.properties.get(&ustr("MeshContent")),
|
||||
object.properties.get(hstr!("MeshContent")),
|
||||
// texture is allowed to be none
|
||||
object.properties.get(&ustr("TextureContent")),
|
||||
object.properties.get(hstr!("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);
|
||||
@ -662,13 +660,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(&ustr("AssetId")){
|
||||
if let Some(rbx_dom_weak::types::Variant::ContentId(asset_id))=object.properties.get(hstr!("AssetId")){
|
||||
content=asset_id.as_ref();
|
||||
}
|
||||
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(&ustr("MeshData")){
|
||||
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(hstr!("MeshData")){
|
||||
mesh_data=data.as_ref();
|
||||
}
|
||||
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(&ustr("PhysicsData")){
|
||||
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get(hstr!("PhysicsData")){
|
||||
physics_data=data.as_ref();
|
||||
}
|
||||
let part_texture_description=get_texture_description(&mut temp_objects,render_config_deferred_loader,dom,object,size);
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "roblox_emulator"
|
||||
version = "0.4.7"
|
||||
version = "0.5.0"
|
||||
edition = "2024"
|
||||
repository = "https://git.itzana.me/StrafesNET/strafe-project"
|
||||
license = "MIT OR Apache-2.0"
|
||||
@ -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 = "3.0.0"
|
||||
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,93 +1,112 @@
|
||||
use rbx_dom_weak::{types::Ref,ustr,InstanceBuilder,WeakDom};
|
||||
use rbx_dom_weak::{hstr,types::Ref,InstanceBuilder,WeakDom,UnhashedStr};
|
||||
|
||||
pub fn class_is_a(class:&str,superclass:&str)->bool{
|
||||
class==superclass
|
||||
||rbx_reflection_database::get().classes.get(class)
|
||||
.is_some_and(|descriptor|
|
||||
descriptor.superclass.as_ref().is_some_and(|class_super|
|
||||
class_is_a(class_super,superclass)
|
||||
)
|
||||
)
|
||||
#[derive(Debug)]
|
||||
pub enum ServicesError{
|
||||
WorkspaceNotFound,
|
||||
}
|
||||
impl std::fmt::Display for ServicesError{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
write!(f,"{self:?}")
|
||||
}
|
||||
}
|
||||
impl std::error::Error for ServicesError{}
|
||||
|
||||
pub struct Services{
|
||||
pub(crate) game:Ref,
|
||||
pub(crate) workspace:Ref,
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Context{
|
||||
pub(crate)dom:WeakDom,
|
||||
}
|
||||
|
||||
impl Context{
|
||||
pub const fn new(dom:WeakDom)->Self{
|
||||
Self{dom}
|
||||
}
|
||||
pub fn script_singleton(source:String)->(Context,crate::runner::instance::Instance,Services){
|
||||
let script=InstanceBuilder::new("Script")
|
||||
.with_property("Source",rbx_types::Variant::String(source));
|
||||
let script_ref=script.referent();
|
||||
let mut context=Self::new(WeakDom::new(
|
||||
InstanceBuilder::new("DataModel")
|
||||
.with_child(script)
|
||||
));
|
||||
let services=context.convert_into_place();
|
||||
(context,crate::runner::instance::Instance::new(script_ref),services)
|
||||
}
|
||||
pub fn from_ref(dom:&WeakDom)->&Context{
|
||||
unsafe{&*(dom as *const WeakDom as *const Context)}
|
||||
}
|
||||
pub fn from_mut(dom:&mut WeakDom)->&mut Context{
|
||||
unsafe{&mut *(dom as *mut WeakDom as *mut 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())
|
||||
}
|
||||
pub fn scripts(&self)->Vec<crate::runner::instance::Instance>{
|
||||
self.superclass_iter("LuaSourceContainer").map(crate::runner::instance::Instance::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")
|
||||
)?,
|
||||
game:self.dom.root_ref(),
|
||||
impl Services{
|
||||
fn find_services(dom:&WeakDom)->Result<Services,ServicesError>{
|
||||
Ok(Services{
|
||||
workspace:*dom.root().children().iter().find(|&&r|
|
||||
dom.get_by_ref(r).is_some_and(|instance|instance.class=="Workspace")
|
||||
).ok_or(ServicesError::WorkspaceNotFound)?,
|
||||
game:dom.root_ref(),
|
||||
})
|
||||
}
|
||||
pub fn convert_into_place(&mut self)->Services{
|
||||
}
|
||||
|
||||
pub type LuaAppData=&'static mut WeakDom<'static>;
|
||||
pub struct Context<'a>{
|
||||
pub(crate)dom:WeakDom<'a>,
|
||||
pub(crate)services:Services,
|
||||
}
|
||||
|
||||
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<'a>,crate::runner::instance::Instance){
|
||||
let script=InstanceBuilder::new(hstr!("Script"))
|
||||
.with_property(hstr!("Source"),rbx_types::Variant::String(source));
|
||||
let script_ref=script.referent();
|
||||
let dom=WeakDom::new(
|
||||
InstanceBuilder::new(hstr!("DataModel"))
|
||||
.with_child(script)
|
||||
);
|
||||
let context=Self::from_model(dom);
|
||||
(context,crate::runner::instance::Instance::new_unchecked(script_ref))
|
||||
}
|
||||
/// Creates an iterator over all items of a particular class.
|
||||
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(UnhashedStr::from_ref(superclass))else{
|
||||
panic!("Invalid class");
|
||||
};
|
||||
self.dom.descendants().filter_map(|instance|{
|
||||
let class=db.classes.get(instance.class)?;
|
||||
db.has_superclass(class,superclass).then(||instance.referent())
|
||||
})
|
||||
}
|
||||
pub fn scripts(&self)->Vec<crate::runner::instance::Instance>{
|
||||
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(hstr!("Disabled")){
|
||||
return Some(crate::runner::instance::Instance::new_unchecked(script_ref));
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn from_model(mut dom:WeakDom<'a>)->Context<'a>{
|
||||
//snapshot root instances
|
||||
let children=self.dom.root().children().to_owned();
|
||||
let children=dom.root().children().to_owned();
|
||||
|
||||
//insert services
|
||||
let game=self.dom.root_ref();
|
||||
let terrain_bldr=InstanceBuilder::new("Terrain");
|
||||
let workspace=self.dom.insert(game,
|
||||
InstanceBuilder::new("Workspace")
|
||||
let game=dom.root_ref();
|
||||
let terrain_bldr=InstanceBuilder::new(hstr!("Terrain"));
|
||||
let workspace=dom.insert(game,
|
||||
InstanceBuilder::new(hstr!("Workspace"))
|
||||
//Set Workspace.Terrain property equal to Terrain
|
||||
.with_property("Terrain",terrain_bldr.referent())
|
||||
.with_property(hstr!("Terrain"),terrain_bldr.referent())
|
||||
.with_child(terrain_bldr)
|
||||
);
|
||||
{
|
||||
//Lowercase and upper case workspace property!
|
||||
let game=self.dom.root_mut();
|
||||
game.properties.insert(ustr("workspace"),rbx_types::Variant::Ref(workspace));
|
||||
game.properties.insert(ustr("Workspace"),rbx_types::Variant::Ref(workspace));
|
||||
}
|
||||
self.dom.insert(game,InstanceBuilder::new("Lighting"));
|
||||
|
||||
//transfer original root instances into workspace
|
||||
for instance in children{
|
||||
self.dom.transfer_within(instance,workspace);
|
||||
dom.transfer_within(instance,workspace);
|
||||
}
|
||||
|
||||
Services{
|
||||
game,
|
||||
workspace,
|
||||
{
|
||||
//Lowercase and upper case workspace property!
|
||||
let game=dom.root_mut();
|
||||
// TODO: DELETE THIS!
|
||||
game.properties.insert(hstr!("workspace"),rbx_types::Variant::Ref(workspace));
|
||||
game.properties.insert(hstr!("Workspace"),rbx_types::Variant::Ref(workspace));
|
||||
}
|
||||
dom.insert(game,InstanceBuilder::new(hstr!("Lighting")));
|
||||
|
||||
let services=Services{game,workspace};
|
||||
Self{dom,services}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Services{
|
||||
pub game:Ref,
|
||||
pub workspace:Ref,
|
||||
impl<'a> AsRef<WeakDom<'a>> for Context<'a>{
|
||||
fn as_ref(&self)->&WeakDom<'a>{
|
||||
&self.dom
|
||||
}
|
||||
}
|
||||
|
@ -5,3 +5,7 @@ pub(crate) mod scheduler;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub mod mlua{
|
||||
pub use mlua::{Result,Error};
|
||||
}
|
||||
|
59
lib/roblox_emulator/src/runner/brickcolor.rs
Normal file
59
lib/roblox_emulator/src/runner/brickcolor.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use super::color3::Color3;
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct BrickColor(rbx_types::BrickColor);
|
||||
impl BrickColor{
|
||||
pub fn from_name(name:&str)->Option<Self>{
|
||||
Some(BrickColor(rbx_types::BrickColor::from_name(name)?))
|
||||
}
|
||||
pub fn from_number(number:u16)->Option<Self>{
|
||||
Some(BrickColor(rbx_types::BrickColor::from_number(number)?))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let table=lua.create_table()?;
|
||||
|
||||
table.raw_set("new",
|
||||
lua.create_function(|_,r:mlua::Value|
|
||||
match r{
|
||||
mlua::Value::String(name)=>Ok(BrickColor::from_name(&*name.to_str()?)),
|
||||
mlua::Value::Integer(number)=>Ok(BrickColor::from_number(number as u16)),
|
||||
_=>Err(mlua::Error::runtime("Unsupported arguments"))
|
||||
}
|
||||
|
||||
)?
|
||||
)?;
|
||||
|
||||
macro_rules! brickcolor_constructor{
|
||||
($fname:expr,$internal:ident)=>{
|
||||
table.raw_set($fname,
|
||||
lua.create_function(|_,_:()|
|
||||
Ok(BrickColor(rbx_types::BrickColor::$internal))
|
||||
)?
|
||||
)?;
|
||||
};
|
||||
}
|
||||
brickcolor_constructor!("White",White);
|
||||
brickcolor_constructor!("Gray",MediumStoneGrey);
|
||||
brickcolor_constructor!("DarkGray",DarkStoneGrey);
|
||||
brickcolor_constructor!("Black",Black);
|
||||
brickcolor_constructor!("Red",BrightRed);
|
||||
brickcolor_constructor!("Yellow",BrightYellow);
|
||||
brickcolor_constructor!("Green",DarkGreen);
|
||||
brickcolor_constructor!("Blue",BrightBlue);
|
||||
|
||||
globals.set("BrickColor",table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl mlua::UserData for BrickColor{
|
||||
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
|
||||
fields.add_field_method_get("Color",|_,BrickColor(this)|{
|
||||
let rbx_types::Color3uint8{r,g,b}=this.to_color3uint8();
|
||||
Ok(Color3::from_rgb(r,g,b))
|
||||
});
|
||||
}
|
||||
}
|
||||
type_from_lua_userdata!(BrickColor);
|
@ -1,7 +1,10 @@
|
||||
use mlua::FromLua;
|
||||
|
||||
use super::number::Number;
|
||||
use super::vector3::Vector3;
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct CFrame(pub(crate)glam::Affine3A);
|
||||
pub struct CFrame(glam::Affine3A);
|
||||
|
||||
impl CFrame{
|
||||
pub fn new(
|
||||
@ -34,14 +37,14 @@ fn vec3_from_glam(v:rbx_types::Vector3)->glam::Vec3A{
|
||||
glam::vec3a(v.x,v.y,v.z)
|
||||
}
|
||||
|
||||
impl Into<rbx_types::CFrame> for CFrame{
|
||||
fn into(self)->rbx_types::CFrame{
|
||||
impl From<CFrame> for rbx_types::CFrame{
|
||||
fn from(CFrame(cf):CFrame)->rbx_types::CFrame{
|
||||
rbx_types::CFrame::new(
|
||||
vec3_to_glam(self.0.translation),
|
||||
vec3_to_glam(cf.translation),
|
||||
rbx_types::Matrix3::new(
|
||||
vec3_to_glam(self.0.matrix3.x_axis),
|
||||
vec3_to_glam(self.0.matrix3.y_axis),
|
||||
vec3_to_glam(self.0.matrix3.z_axis),
|
||||
vec3_to_glam(cf.matrix3.x_axis),
|
||||
vec3_to_glam(cf.matrix3.y_axis),
|
||||
vec3_to_glam(cf.matrix3.z_axis),
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -60,16 +63,25 @@ impl From<rbx_types::CFrame> for CFrame{
|
||||
}
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let cframe_table=lua.create_table()?;
|
||||
let table=lua.create_table()?;
|
||||
|
||||
//CFrame.new
|
||||
cframe_table.raw_set("new",
|
||||
lua.create_function(|_,tuple:(
|
||||
mlua::Value,mlua::Value,Option<f32>,
|
||||
Option<f32>,Option<f32>,Option<f32>,
|
||||
Option<f32>,Option<f32>,Option<f32>,
|
||||
Option<f32>,Option<f32>,Option<f32>,
|
||||
table.raw_set("new",
|
||||
lua.create_function(|lua,tuple:(
|
||||
mlua::Value,mlua::Value,Option<Number>,
|
||||
Option<Number>,Option<Number>,Option<Number>,
|
||||
Option<Number>,Option<Number>,Option<Number>,
|
||||
Option<Number>,Option<Number>,Option<Number>,
|
||||
)|match tuple{
|
||||
//CFrame.new()
|
||||
(
|
||||
mlua::Value::Nil,mlua::Value::Nil,None,
|
||||
None,None,None,
|
||||
None,None,None,
|
||||
None,None,None,
|
||||
)=>{
|
||||
Ok(CFrame(glam::Affine3A::IDENTITY))
|
||||
},
|
||||
//CFrame.new(pos)
|
||||
(
|
||||
mlua::Value::UserData(pos),mlua::Value::Nil,None,
|
||||
@ -77,8 +89,8 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
None,None,None,
|
||||
None,None,None,
|
||||
)=>{
|
||||
let pos:Vector3=pos.take()?;
|
||||
Ok(CFrame::point(pos.0.x,pos.0.y,pos.0.z))
|
||||
let Vector3(pos):&Vector3=&*pos.borrow()?;
|
||||
Ok(CFrame::point(pos.x,pos.y,pos.z))
|
||||
},
|
||||
//TODO: CFrame.new(pos,look)
|
||||
(
|
||||
@ -87,85 +99,99 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
None,None,None,
|
||||
None,None,None,
|
||||
)=>{
|
||||
let _pos:Vector3=pos.take()?;
|
||||
let _look:Vector3=look.take()?;
|
||||
let _pos:&Vector3=&*pos.borrow()?;
|
||||
let _look:&Vector3=&*look.borrow()?;
|
||||
Err(mlua::Error::runtime("Not yet implemented"))
|
||||
},
|
||||
//CFrame.new(x,y,z)
|
||||
(
|
||||
mlua::Value::Number(x),mlua::Value::Number(y),Some(z),
|
||||
x,y,Some(z),
|
||||
None,None,None,
|
||||
None,None,None,
|
||||
None,None,None,
|
||||
)=>Ok(CFrame::point(x as f32,y as f32,z)),
|
||||
)=>Ok(CFrame::point(Number::from_lua(x,lua)?.into(),Number::from_lua(y,lua)?.into(),z.into())),
|
||||
//CFrame.new(x,y,z,xx,yx,zx,xy,yy,zy,xz,yz,zz)
|
||||
(
|
||||
mlua::Value::Number(x),mlua::Value::Number(y),Some(z),
|
||||
x,y,Some(z),
|
||||
Some(xx),Some(yx),Some(zx),
|
||||
Some(xy),Some(yy),Some(zy),
|
||||
Some(xz),Some(yz),Some(zz),
|
||||
)=>Ok(CFrame::new(x as f32,y as f32,z,
|
||||
xx,yx,zx,
|
||||
xy,yy,zy,
|
||||
xz,yz,zz,
|
||||
)=>Ok(CFrame::new(Number::from_lua(x,lua)?.into(),Number::from_lua(y,lua)?.into(),z.into(),
|
||||
xx.into(),yx.into(),zx.into(),
|
||||
xy.into(),yy.into(),zy.into(),
|
||||
xz.into(),yz.into(),zz.into(),
|
||||
)),
|
||||
_=>Err(mlua::Error::runtime("Invalid arguments"))
|
||||
})?
|
||||
)?;
|
||||
|
||||
//CFrame.Angles
|
||||
cframe_table.raw_set("Angles",
|
||||
lua.create_function(|_,(x,y,z):(f32,f32,f32)|
|
||||
Ok(CFrame::angles(x,y,z))
|
||||
)?
|
||||
let from_euler_angles=lua.create_function(|_,(x,y,z):(Number,Number,Number)|
|
||||
Ok(CFrame::angles(x.into(),y.into(),z.into()))
|
||||
)?;
|
||||
table.raw_set("Angles",from_euler_angles.clone())?;
|
||||
table.raw_set("fromEulerAnglesXYZ",from_euler_angles.clone())?;
|
||||
table.raw_set("FromEulerAnglesXYZ",from_euler_angles)?;
|
||||
|
||||
globals.set("CFrame",cframe_table)?;
|
||||
globals.set("CFrame",table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl mlua::UserData for CFrame{
|
||||
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
|
||||
//CFrame.p
|
||||
fields.add_field_method_get("p",|_,this|Ok(Vector3(this.0.translation)));
|
||||
fields.add_field_method_get("p",|_,CFrame(this)|Ok(Vector3(this.translation)));
|
||||
fields.add_field_method_get("x",|_,CFrame(this)|Ok(this.translation.x));
|
||||
fields.add_field_method_get("X",|_,CFrame(this)|Ok(this.translation.x));
|
||||
fields.add_field_method_get("y",|_,CFrame(this)|Ok(this.translation.y));
|
||||
fields.add_field_method_get("Y",|_,CFrame(this)|Ok(this.translation.y));
|
||||
fields.add_field_method_get("z",|_,CFrame(this)|Ok(this.translation.z));
|
||||
fields.add_field_method_get("Z",|_,CFrame(this)|Ok(this.translation.z));
|
||||
fields.add_field_method_get("rightVector",|_,CFrame(this)|Ok(Vector3(this.matrix3.x_axis)));
|
||||
fields.add_field_method_get("RightVector",|_,CFrame(this)|Ok(Vector3(this.matrix3.x_axis)));
|
||||
fields.add_field_method_get("upVector",|_,CFrame(this)|Ok(Vector3(this.matrix3.y_axis)));
|
||||
fields.add_field_method_get("UpVector",|_,CFrame(this)|Ok(Vector3(this.matrix3.y_axis)));
|
||||
fields.add_field_method_get("lookVector",|_,CFrame(this)|Ok(Vector3(-this.matrix3.z_axis)));
|
||||
fields.add_field_method_get("LookVector",|_,CFrame(this)|Ok(Vector3(-this.matrix3.z_axis)));
|
||||
fields.add_field_method_get("XVector",|_,CFrame(this)|Ok(Vector3(this.matrix3.row(0))));
|
||||
fields.add_field_method_get("YVector",|_,CFrame(this)|Ok(Vector3(this.matrix3.row(1))));
|
||||
fields.add_field_method_get("ZVector",|_,CFrame(this)|Ok(Vector3(this.matrix3.row(2))));
|
||||
}
|
||||
|
||||
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
||||
methods.add_method("components",|_,this,()|Ok((
|
||||
this.0.translation.x,
|
||||
this.0.translation.y,
|
||||
this.0.translation.z,
|
||||
this.0.matrix3.x_axis.x,
|
||||
this.0.matrix3.y_axis.x,
|
||||
this.0.matrix3.z_axis.x,
|
||||
this.0.matrix3.x_axis.y,
|
||||
this.0.matrix3.y_axis.y,
|
||||
this.0.matrix3.z_axis.y,
|
||||
this.0.matrix3.x_axis.z,
|
||||
this.0.matrix3.y_axis.z,
|
||||
this.0.matrix3.z_axis.z,
|
||||
methods.add_method("components",|_,CFrame(this),()|Ok((
|
||||
this.translation.x,
|
||||
this.translation.y,
|
||||
this.translation.z,
|
||||
this.matrix3.x_axis.x,
|
||||
this.matrix3.y_axis.x,
|
||||
this.matrix3.z_axis.x,
|
||||
this.matrix3.x_axis.y,
|
||||
this.matrix3.y_axis.y,
|
||||
this.matrix3.z_axis.y,
|
||||
this.matrix3.x_axis.z,
|
||||
this.matrix3.y_axis.z,
|
||||
this.matrix3.z_axis.z,
|
||||
)));
|
||||
methods.add_method("VectorToWorldSpace",|_,this,v:Vector3|
|
||||
Ok(Vector3(this.0.transform_vector3a(v.0)))
|
||||
methods.add_method("VectorToWorldSpace",|_,CFrame(this),Vector3(v):Vector3|
|
||||
Ok(Vector3(this.transform_vector3a(v)))
|
||||
);
|
||||
|
||||
//methods.add_meta_method(mlua::MetaMethod::Mul,|_,this,val:&Vector3|Ok(Vector3(this.0.matrix3*val.0+this.0.translation)));
|
||||
methods.add_meta_function(mlua::MetaMethod::Mul,|_,(this,val):(Self,Self)|Ok(Self(this.0*val.0)));
|
||||
methods.add_meta_function(mlua::MetaMethod::ToString,|_,this:Self|
|
||||
methods.add_meta_function(mlua::MetaMethod::Mul,|_,(CFrame(this),CFrame(val)):(Self,Self)|Ok(Self(this*val)));
|
||||
methods.add_meta_function(mlua::MetaMethod::ToString,|_,CFrame(this):Self|
|
||||
Ok(format!("CFrame.new({},{},{},{},{},{},{},{},{},{},{},{})",
|
||||
this.0.translation.x,
|
||||
this.0.translation.y,
|
||||
this.0.translation.z,
|
||||
this.0.matrix3.x_axis.x,
|
||||
this.0.matrix3.y_axis.x,
|
||||
this.0.matrix3.z_axis.x,
|
||||
this.0.matrix3.x_axis.y,
|
||||
this.0.matrix3.y_axis.y,
|
||||
this.0.matrix3.z_axis.y,
|
||||
this.0.matrix3.x_axis.z,
|
||||
this.0.matrix3.y_axis.z,
|
||||
this.0.matrix3.z_axis.z,
|
||||
this.translation.x,
|
||||
this.translation.y,
|
||||
this.translation.z,
|
||||
this.matrix3.x_axis.x,
|
||||
this.matrix3.y_axis.x,
|
||||
this.matrix3.z_axis.x,
|
||||
this.matrix3.x_axis.y,
|
||||
this.matrix3.y_axis.y,
|
||||
this.matrix3.z_axis.y,
|
||||
this.matrix3.x_axis.z,
|
||||
this.matrix3.y_axis.z,
|
||||
this.matrix3.z_axis.z,
|
||||
))
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use super::number::Number;
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct Color3{
|
||||
r:f32,
|
||||
@ -8,28 +10,37 @@ impl Color3{
|
||||
pub const fn new(r:f32,g:f32,b:f32)->Self{
|
||||
Self{r,g,b}
|
||||
}
|
||||
pub const fn from_rgb(r:u8,g:u8,b:u8)->Self{
|
||||
Color3::new(r as f32/255.0,g as f32/255.0,b as f32/255.0)
|
||||
}
|
||||
}
|
||||
impl Into<rbx_types::Color3> for Color3{
|
||||
fn into(self)->rbx_types::Color3{
|
||||
rbx_types::Color3::new(self.r,self.g,self.b)
|
||||
impl From<rbx_types::Color3> for Color3{
|
||||
fn from(value:rbx_types::Color3)->Color3{
|
||||
Color3::new(value.r,value.g,value.b)
|
||||
}
|
||||
}
|
||||
impl From<Color3> for rbx_types::Color3{
|
||||
fn from(value:Color3)->rbx_types::Color3{
|
||||
rbx_types::Color3::new(value.r,value.g,value.b)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let color3_table=lua.create_table()?;
|
||||
let table=lua.create_table()?;
|
||||
|
||||
color3_table.raw_set("new",
|
||||
lua.create_function(|_,(r,g,b):(f32,f32,f32)|
|
||||
Ok(Color3::new(r,g,b))
|
||||
)?
|
||||
)?;
|
||||
color3_table.raw_set("fromRGB",
|
||||
lua.create_function(|_,(r,g,b):(u8,u8,u8)|
|
||||
Ok(Color3::new(r as f32/255.0,g as f32/255.0,b as f32/255.0))
|
||||
table.raw_set("new",
|
||||
lua.create_function(|_,(r,g,b):(Number,Number,Number)|
|
||||
Ok(Color3::new(r.into(),g.into(),b.into()))
|
||||
)?
|
||||
)?;
|
||||
|
||||
globals.set("Color3",color3_table)?;
|
||||
let from_rgb=lua.create_function(|_,(r,g,b):(u8,u8,u8)|
|
||||
Ok(Color3::from_rgb(r,g,b))
|
||||
)?;
|
||||
table.raw_set("fromRGB",from_rgb.clone())?;
|
||||
table.raw_set("FromRGB",from_rgb)?;
|
||||
|
||||
globals.set("Color3",table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,31 +1,29 @@
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct ColorSequence{}
|
||||
#[derive(Clone)]
|
||||
pub struct ColorSequence(rbx_types::ColorSequence);
|
||||
impl ColorSequence{
|
||||
pub const fn new()->Self{
|
||||
Self{}
|
||||
pub const fn new(keypoints:Vec<rbx_types::ColorSequenceKeypoint>)->Self{
|
||||
Self(rbx_types::ColorSequence{keypoints})
|
||||
}
|
||||
}
|
||||
impl Into<rbx_types::ColorSequence> for ColorSequence{
|
||||
fn into(self)->rbx_types::ColorSequence{
|
||||
rbx_types::ColorSequence{
|
||||
keypoints:Vec::new()
|
||||
}
|
||||
impl From<ColorSequence> for rbx_types::ColorSequence{
|
||||
fn from(ColorSequence(value):ColorSequence)->rbx_types::ColorSequence{
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let number_sequence_table=lua.create_table()?;
|
||||
let table=lua.create_table()?;
|
||||
|
||||
number_sequence_table.raw_set("new",
|
||||
table.raw_set("new",
|
||||
lua.create_function(|_,_:mlua::MultiValue|
|
||||
Ok(ColorSequence::new())
|
||||
Ok(ColorSequence::new(Vec::new()))
|
||||
)?
|
||||
)?;
|
||||
|
||||
globals.set("ColorSequence",number_sequence_table)?;
|
||||
globals.set("ColorSequence",table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl mlua::UserData for ColorSequence{}
|
||||
type_from_lua_userdata!(ColorSequence);
|
||||
type_from_lua_userdata_clone!(ColorSequence);
|
||||
|
@ -1,63 +1,138 @@
|
||||
use mlua::IntoLua;
|
||||
use rbx_dom_weak::{HashStr, UnhashedStr};
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct Enum(u32);
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct EnumItems;
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct EnumItem<'a>{
|
||||
ed:&'a rbx_reflection::EnumDescriptor<'a>,
|
||||
name:Option<&'a HashStr>,
|
||||
value:u32,
|
||||
}
|
||||
|
||||
impl Into<rbx_types::Enum> for Enum{
|
||||
fn into(self)->rbx_types::Enum{
|
||||
rbx_types::Enum::from_u32(self.0)
|
||||
impl<'a> EnumItem<'a>{
|
||||
fn known_name((name,&value):(&&'a HashStr,&u32))->Self{
|
||||
Self{name:Some(name),value}
|
||||
}
|
||||
}
|
||||
impl<'a> From<rbx_types::Enum> for EnumItem<'a>{
|
||||
fn from(e:rbx_types::Enum)->Self{
|
||||
EnumItem{
|
||||
name:None,
|
||||
value:e.to_u32(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<EnumItem<'_>> for rbx_types::Enum{
|
||||
fn from(e:EnumItem)->rbx_types::Enum{
|
||||
rbx_types::Enum::from_u32(e.value)
|
||||
}
|
||||
}
|
||||
impl PartialEq for EnumItem<'_>{
|
||||
fn eq(&self,other:&EnumItem<'_>)->bool{
|
||||
self.value==other.value&&{
|
||||
// if both names are known, they must match, otherwise whatever
|
||||
match (self.name,other.name){
|
||||
(Some(lhs),Some(rhs))=>lhs==rhs,
|
||||
_=>true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> EnumItem<'a>{
|
||||
const fn new(ed:&'a rbx_reflection::EnumDescriptor)->Self{
|
||||
Self{ed}
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct Enums;
|
||||
impl Enums{
|
||||
pub fn get(&self,index:&str)->Option<EnumItems<'static>>{
|
||||
let db=rbx_reflection_database::get();
|
||||
db.enums.get(UnhashedStr::from_ref(index)).map(|ed|EnumItems{ed})
|
||||
}
|
||||
}
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct EnumItems<'a>{
|
||||
ed:&'a rbx_reflection::EnumDescriptor<'a>,
|
||||
}
|
||||
|
||||
impl<'a> EnumItems<'a>{
|
||||
pub fn from_value(&self,value:u32)->Option<EnumItem<'a>>{
|
||||
self.ed.items.iter().find(|&(_,&v)|v==value).map(EnumItem::known_name)
|
||||
}
|
||||
pub fn from_name(&self,name:&str)->Option<EnumItem<'a>>{
|
||||
self.ed.items.get_key_value(UnhashedStr::from_ref(name)).map(EnumItem::known_name)
|
||||
}
|
||||
pub fn from_enum(&self,enum_item:EnumItem)->Option<EnumItem<'a>>{
|
||||
match enum_item.name{
|
||||
Some(s)=>{
|
||||
let got=self.from_name(s)?;
|
||||
(got.value==enum_item.value).then_some(got)
|
||||
},
|
||||
None=>self.from_value(enum_item.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum CoerceEnum<'a>{
|
||||
Integer(i32),
|
||||
String(mlua::String),
|
||||
Enum(EnumItem<'a>),
|
||||
}
|
||||
impl CoerceEnum<'_>{
|
||||
pub fn coerce_to<'a>(self,enum_items:EnumItems<'a>)->mlua::Result<EnumItem<'a>>{
|
||||
match self{
|
||||
CoerceEnum::Integer(int)=>enum_items.from_value(int as u32),
|
||||
CoerceEnum::String(s)=>enum_items.from_name(&*s.to_str()?),
|
||||
CoerceEnum::Enum(enum_item)=>enum_items.from_enum(enum_item),
|
||||
}.ok_or_else(||mlua::Error::runtime(format!("Bad {} EnumItem",enum_items.ed.name)))
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::FromLua for CoerceEnum<'_>{
|
||||
fn from_lua(value:mlua::Value,_lua:&mlua::Lua)->Result<Self,mlua::Error>{
|
||||
match value{
|
||||
mlua::Value::Integer(int)=>Ok(CoerceEnum::Integer(int)),
|
||||
mlua::Value::String(s)=>Ok(CoerceEnum::String(s)),
|
||||
mlua::Value::UserData(ud)=>Ok(CoerceEnum::Enum(*ud.borrow()?)),
|
||||
other=>Err(mlua::Error::runtime(format!("Expected {} got {:?}",stringify!(Enum),other))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_globals(_lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
globals.set("Enum",EnumItems)
|
||||
globals.set("Enum",Enums)
|
||||
}
|
||||
|
||||
impl mlua::UserData for EnumItem<'_>{
|
||||
impl mlua::UserData for EnumItems<'static>{
|
||||
fn add_fields<F:mlua::UserDataFields<Self>>(_fields:&mut F){
|
||||
}
|
||||
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
||||
methods.add_meta_function(mlua::MetaMethod::Index,|lua,(this,val):(EnumItem<'_>,mlua::String)|{
|
||||
match this.ed.items.get(&*val.to_str()?){
|
||||
Some(&id)=>Enum(id).into_lua(lua),
|
||||
None=>mlua::Value::Nil.into_lua(lua),
|
||||
}
|
||||
methods.add_method("FromName",|_,this:&EnumItems,name:mlua::String|Ok(this.from_name(&*name.to_str()?)));
|
||||
methods.add_method("FromValue",|_,this:&EnumItems,value:u32|Ok(this.from_value(value)));
|
||||
methods.add_method("GetEnumItems",|_,this:&EnumItems,()|->mlua::Result<Vec<EnumItem>>{
|
||||
Ok(this.ed.items.iter().map(EnumItem::known_name).collect())
|
||||
});
|
||||
methods.add_meta_function(mlua::MetaMethod::Index,|_,(this,val):(EnumItems,mlua::String)|{
|
||||
let index=&*val.to_str()?;
|
||||
Ok(this.ed.items.get_key_value(UnhashedStr::from_ref(index)).map(EnumItem::known_name))
|
||||
});
|
||||
}
|
||||
}
|
||||
type_from_lua_userdata_lua_lifetime!(EnumItems);
|
||||
|
||||
impl mlua::UserData for Enums{
|
||||
fn add_fields<F:mlua::UserDataFields<Self>>(_fields:&mut F){
|
||||
}
|
||||
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
||||
methods.add_meta_function(mlua::MetaMethod::Index,|_,(enums,val):(Self,mlua::String)|{
|
||||
Ok(enums.get(&*val.to_str()?))
|
||||
});
|
||||
}
|
||||
}
|
||||
type_from_lua_userdata!(Enums);
|
||||
|
||||
impl mlua::UserData for EnumItem<'_>{
|
||||
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
|
||||
fields.add_field_method_get("Name",|_,this|Ok(this.name.map(|s|s.as_str())));
|
||||
fields.add_field_method_get("Value",|_,this|Ok(this.value));
|
||||
}
|
||||
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
||||
methods.add_meta_function(mlua::MetaMethod::Eq,|_,(lhs,rhs):(EnumItem<'_>,EnumItem<'_>)|{
|
||||
Ok(lhs==rhs)
|
||||
});
|
||||
}
|
||||
}
|
||||
type_from_lua_userdata_lua_lifetime!(EnumItem);
|
||||
|
||||
impl mlua::UserData for EnumItems{
|
||||
fn add_fields<F:mlua::UserDataFields<Self>>(_fields:&mut F){
|
||||
}
|
||||
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
||||
methods.add_meta_function(mlua::MetaMethod::Index,|lua,(_,val):(Self,mlua::String)|{
|
||||
let db=rbx_reflection_database::get();
|
||||
match db.enums.get(&*val.to_str()?){
|
||||
Some(ed)=>EnumItem::new(ed).into_lua(lua),
|
||||
None=>mlua::Value::Nil.into_lua(lua),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
type_from_lua_userdata!(EnumItems);
|
||||
|
||||
impl mlua::UserData for Enum{
|
||||
fn add_fields<F:mlua::UserDataFields<Self>>(_fields:&mut F){
|
||||
}
|
||||
fn add_methods<M:mlua::UserDataMethods<Self>>(_methods:&mut M){
|
||||
}
|
||||
}
|
||||
type_from_lua_userdata!(Enum);
|
||||
|
@ -2,48 +2,46 @@ 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::{hstr,HashStr,UnhashedStr,InstanceBuilder,WeakDom};
|
||||
|
||||
use crate::runner::vector3::Vector3;
|
||||
use crate::runner::number::Number;
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
//class functions store
|
||||
lua.set_app_data(ClassMethodsStore::default());
|
||||
lua.set_app_data(InstanceValueStore::default());
|
||||
|
||||
let instance_table=lua.create_table()?;
|
||||
let table=lua.create_table()?;
|
||||
|
||||
//Instance.new
|
||||
instance_table.raw_set("new",
|
||||
table.raw_set("new",
|
||||
lua.create_function(|lua,(class_name,parent):(mlua::String,Option<Instance>)|{
|
||||
let class_name_str=&*class_name.to_str()?;
|
||||
let parent=parent.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?;
|
||||
let parent_ref=parent.map_or(Ref::none(),|instance|instance.referent);
|
||||
dom_mut(lua,|dom|{
|
||||
//TODO: Nil instances
|
||||
Ok(Instance::new(dom.insert(parent.referent,InstanceBuilder::new(class_name_str))))
|
||||
Ok(Instance::new_unchecked(dom.insert(parent_ref,InstanceBuilder::new(class_name_str))))
|
||||
})
|
||||
})?
|
||||
)?;
|
||||
|
||||
globals.set("Instance",instance_table)?;
|
||||
globals.set("Instance",table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// LMAO look at this function!
|
||||
pub fn dom_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut WeakDom)->mlua::Result<T>)->mlua::Result<T>{
|
||||
let mut dom=lua.app_data_mut::<&'static mut WeakDom>().ok_or_else(||mlua::Error::runtime("DataModel missing"))?;
|
||||
let mut dom=lua.app_data_mut::<crate::context::LuaAppData>().ok_or_else(||mlua::Error::runtime("DataModel missing"))?;
|
||||
f(*dom)
|
||||
}
|
||||
|
||||
fn coerce_float32(value:&mlua::Value)->Option<f32>{
|
||||
match value{
|
||||
&mlua::Value::Integer(i)=>Some(i as f32),
|
||||
&mlua::Value::Number(f)=>Some(f as f32),
|
||||
_=>None,
|
||||
}
|
||||
pub fn class_is_a(class:&str,superclass:&str)->bool{
|
||||
let db=rbx_reflection_database::get();
|
||||
let (Some(class),Some(superclass))=(db.classes.get(UnhashedStr::from_ref(class)),db.classes.get(UnhashedStr::from_ref(superclass)))else{
|
||||
return false;
|
||||
};
|
||||
db.has_superclass(class,superclass)
|
||||
}
|
||||
|
||||
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();
|
||||
@ -58,7 +56,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(&ustr("Source")){
|
||||
let source=match instance.properties.get(hstr!("Source")){
|
||||
Some(rbx_dom_weak::types::Variant::String(s))=>s.clone(),
|
||||
_=>Err(mlua::Error::external("Missing script.Source"))?,
|
||||
};
|
||||
@ -66,32 +64,50 @@ 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<'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(UnhashedStr::from_ref(superclass))?;
|
||||
instance.children().iter().filter_map(|&r|dom.get_by_ref(r)).find(|inst|{
|
||||
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<'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(UnhashedStr::from_ref(superclass))?;
|
||||
dom.descendants_of(instance.referent()).find(|inst|{
|
||||
db.classes.get(inst.class).is_some_and(|descriptor|db.has_superclass(descriptor,superclass_descriptor))
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct Instance{
|
||||
referent:Ref,
|
||||
}
|
||||
impl Instance{
|
||||
pub const fn new(referent:Ref)->Self{
|
||||
pub const fn new_unchecked(referent:Ref)->Self{
|
||||
Self{referent}
|
||||
}
|
||||
pub fn get<'a>(&self,dom:&'a WeakDom)->mlua::Result<&'a rbx_dom_weak::Instance>{
|
||||
pub fn new(referent:Ref)->Option<Self>{
|
||||
referent.is_some().then_some(Self{referent})
|
||||
}
|
||||
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"))
|
||||
}
|
||||
}
|
||||
@ -99,19 +115,23 @@ type_from_lua_userdata!(Instance);
|
||||
|
||||
impl mlua::UserData for Instance{
|
||||
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
|
||||
fields.add_field_method_get("Parent",|lua,this|{
|
||||
fn get_parent(lua:&mlua::Lua,this:&Instance)->mlua::Result<Option<Instance>>{
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
Ok(Instance::new(instance.parent()))
|
||||
})
|
||||
});
|
||||
fields.add_field_method_set("Parent",|lua,this,val:Option<Instance>|{
|
||||
let parent=val.ok_or_else(||mlua::Error::runtime("Nil Parent not yet supported"))?;
|
||||
}
|
||||
fields.add_field_method_get("parent",get_parent);
|
||||
fields.add_field_method_get("Parent",get_parent);
|
||||
fn set_parent(lua:&mlua::Lua,this:&mut Instance,new_parent:Option<Instance>)->mlua::Result<()>{
|
||||
let parent_ref=new_parent.map_or(Ref::none(),|instance|instance.referent);
|
||||
dom_mut(lua,|dom|{
|
||||
dom.transfer_within(this.referent,parent.referent);
|
||||
dom.transfer_within(this.referent,parent_ref);
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
}
|
||||
fields.add_field_method_set("parent",set_parent);
|
||||
fields.add_field_method_set("Parent",set_parent);
|
||||
fields.add_field_method_get("Name",|lua,this|{
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
@ -129,22 +149,38 @@ impl mlua::UserData for Instance{
|
||||
fields.add_field_method_get("ClassName",|lua,this|{
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
Ok(instance.class.to_owned())
|
||||
Ok(instance.class.as_str().to_owned())
|
||||
})
|
||||
});
|
||||
}
|
||||
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
||||
methods.add_method("GetChildren",|lua,this,_:()|
|
||||
fn clone(lua:&mlua::Lua,this:&Instance,_:())->mlua::Result<Instance>{
|
||||
dom_mut(lua,|dom|{
|
||||
let instance_ref=dom.clone_within(this.referent);
|
||||
Ok(Instance::new_unchecked(instance_ref))
|
||||
})
|
||||
}
|
||||
methods.add_method("clone",clone);
|
||||
methods.add_method("Clone",clone);
|
||||
fn get_children(lua:&mlua::Lua,this:&Instance,_:())->mlua::Result<Vec<Instance>>{
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
let children:Vec<_>=instance
|
||||
.children()
|
||||
.iter()
|
||||
.copied()
|
||||
.map(Instance::new)
|
||||
.map(Instance::new_unchecked)
|
||||
.collect();
|
||||
Ok(children)
|
||||
})
|
||||
}
|
||||
methods.add_method("children",get_children);
|
||||
methods.add_method("GetChildren",get_children);
|
||||
methods.add_method("GetFullName",|lua,this,()|
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
Ok(get_full_name(dom,instance))
|
||||
})
|
||||
);
|
||||
fn ffc(lua:&mlua::Lua,this:&Instance,(name,search_descendants):(mlua::String,Option<bool>))->mlua::Result<Option<Instance>>{
|
||||
let name_str=&*name.to_str()?;
|
||||
@ -156,7 +192,7 @@ impl mlua::UserData for Instance{
|
||||
false=>find_first_child(dom,instance,name_str),
|
||||
}
|
||||
.map(|instance|
|
||||
Instance::new(instance.referent())
|
||||
Instance::new_unchecked(instance.referent())
|
||||
)
|
||||
)
|
||||
})
|
||||
@ -166,13 +202,29 @@ impl mlua::UserData for Instance{
|
||||
methods.add_method("FindFirstChildOfClass",|lua,this,(class,search_descendants):(mlua::String,Option<bool>)|{
|
||||
let class_str=&*class.to_str()?;
|
||||
dom_mut(lua,|dom|{
|
||||
let inst=this.get(dom)?;
|
||||
Ok(
|
||||
match search_descendants.unwrap_or(false){
|
||||
true=>find_first_descendant_of_class(dom,this.get(dom)?,class_str),
|
||||
false=>find_first_child_of_class(dom,this.get(dom)?,class_str),
|
||||
true=>find_first_descendant_of_class(dom,inst,class_str),
|
||||
false=>find_first_child_of_class(dom,inst,class_str),
|
||||
}
|
||||
.map(|instance|
|
||||
Instance::new(instance.referent())
|
||||
Instance::new_unchecked(instance.referent())
|
||||
)
|
||||
)
|
||||
})
|
||||
});
|
||||
methods.add_method("FindFirstChildWhichIsA",|lua,this,(class,search_descendants):(mlua::String,Option<bool>)|{
|
||||
let class_str=&*class.to_str()?;
|
||||
dom_mut(lua,|dom|{
|
||||
let inst=this.get(dom)?;
|
||||
Ok(
|
||||
match search_descendants.unwrap_or(false){
|
||||
true=>find_first_descendant_which_is_a(dom,inst,class_str),
|
||||
false=>find_first_child_which_is_a(dom,inst,class_str),
|
||||
}
|
||||
.map(|instance|
|
||||
Instance::new_unchecked(instance.referent())
|
||||
)
|
||||
)
|
||||
})
|
||||
@ -182,24 +234,42 @@ impl mlua::UserData for Instance{
|
||||
let children:Vec<_>=dom
|
||||
.descendants_of(this.referent)
|
||||
.map(|instance|
|
||||
Instance::new(instance.referent())
|
||||
Instance::new_unchecked(instance.referent())
|
||||
)
|
||||
.collect();
|
||||
Ok(children)
|
||||
})
|
||||
);
|
||||
methods.add_method("IsA",|lua,this,classname:mlua::String|
|
||||
methods.add_method("IsAncestorOf",|lua,this,descendant:Instance|
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=descendant.get(dom)?;
|
||||
Ok(std::iter::successors(Some(instance),|inst|dom.get_by_ref(inst.parent())).any(|inst|inst.referent()==this.referent))
|
||||
})
|
||||
);
|
||||
methods.add_method("IsDescendantOf",|lua,this,ancestor:Instance|
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
Ok(crate::context::class_is_a(instance.class.as_str(),&*classname.to_str()?))
|
||||
Ok(std::iter::successors(Some(instance),|inst|dom.get_by_ref(inst.parent())).any(|inst|inst.referent()==ancestor.referent))
|
||||
})
|
||||
);
|
||||
methods.add_method("Destroy",|lua,this,()|
|
||||
fn is_a(lua:&mlua::Lua,this:&Instance,classname:mlua::String)->mlua::Result<bool>{
|
||||
dom_mut(lua,|dom|{
|
||||
dom.destroy(this.referent);
|
||||
let instance=this.get(dom)?;
|
||||
Ok(class_is_a(instance.class.as_str(),&*classname.to_str()?))
|
||||
})
|
||||
}
|
||||
methods.add_method("isA",is_a);
|
||||
methods.add_method("IsA",is_a);
|
||||
fn destroy(lua:&mlua::Lua,this:&Instance,_:())->mlua::Result<()>{
|
||||
dom_mut(lua,|dom|{
|
||||
dom.transfer_within(this.referent,Ref::none());
|
||||
Ok(())
|
||||
})
|
||||
);
|
||||
}
|
||||
methods.add_method("remove",destroy);
|
||||
methods.add_method("Remove",destroy);
|
||||
methods.add_method("destroy",destroy);
|
||||
methods.add_method("Destroy",destroy);
|
||||
methods.add_meta_function(mlua::MetaMethod::ToString,|lua,this:Instance|{
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get(dom)?;
|
||||
@ -208,29 +278,37 @@ impl mlua::UserData for Instance{
|
||||
});
|
||||
methods.add_meta_function(mlua::MetaMethod::Index,|lua,(this,index):(Instance,mlua::String)|{
|
||||
let index_str=&*index.to_str()?;
|
||||
let index_ustr=ustr(index_str);
|
||||
dom_mut(lua,|dom|{
|
||||
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"))?;
|
||||
//Find existing property
|
||||
match instance.properties.get(&index_ustr)
|
||||
.cloned()
|
||||
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()
|
||||
)
|
||||
//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.properties,class,index_str)
|
||||
))
|
||||
{
|
||||
Some(rbx_types::Variant::Bool(val))=>return val.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::<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),
|
||||
Some(rbx_types::Variant::String(val))=>return val.into_lua(lua),
|
||||
Some(rbx_types::Variant::Ref(val))=>return Instance::new_unchecked(val).into_lua(lua),
|
||||
Some(rbx_types::Variant::Enum(e))=>return crate::runner::r#enum::EnumItem::from(e).into_lua(lua),
|
||||
Some(rbx_types::Variant::Color3(c))=>return crate::runner::color3::Color3::from(c).into_lua(lua),
|
||||
Some(rbx_types::Variant::CFrame(cf))=>return crate::runner::cframe::CFrame::from(cf).into_lua(lua),
|
||||
Some(rbx_types::Variant::Vector2(v))=>return crate::runner::vector2::Vector2::from(v).into_lua(lua),
|
||||
Some(rbx_types::Variant::Vector3(v))=>return crate::runner::vector3::Vector3::from(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))),
|
||||
}
|
||||
@ -246,81 +324,114 @@ impl mlua::UserData for Instance{
|
||||
}
|
||||
|
||||
//find or create an associated userdata object
|
||||
if let Some(value)=instance_value_store_mut(lua,|ivs|{
|
||||
//TODO: walk class tree somehow
|
||||
match ivs.get_or_create_instance_values(&instance){
|
||||
Some(mut instance_values)=>instance_values.get_or_create_value(lua,index_str),
|
||||
None=>Ok(None)
|
||||
}
|
||||
})?{
|
||||
let instance=this.get_mut(dom)?;
|
||||
if let Some(value)=get_or_create_userdata(instance,lua,index_str)?{
|
||||
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|Instance::new_unchecked(instance.referent()))
|
||||
.into_lua(lua)
|
||||
})
|
||||
});
|
||||
methods.add_meta_function(mlua::MetaMethod::NewIndex,|lua,(this,index,value):(Instance,mlua::String,mlua::Value)|{
|
||||
let index_str=&*index.to_str()?;
|
||||
dom_mut(lua,|dom|{
|
||||
let instance=this.get_mut(dom)?;
|
||||
//println!("__newindex t={} i={index:?} v={value:?}",instance.name);
|
||||
let index_str=&*index.to_str()?;
|
||||
let index_ustr=ustr(index_str);
|
||||
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"))?;
|
||||
let property=db.superclasses_iter(class).find_map(|cls|
|
||||
cls.properties.get(index_str)
|
||||
).ok_or_else(||
|
||||
mlua::Error::runtime(format!("Property '{index_str}' missing on class '{}'",class.name))
|
||||
)?;
|
||||
match &property.data_type{
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::Vector3)=>{
|
||||
let typed_value:Vector3=*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected Userdata"))?.borrow()?;
|
||||
instance.properties.insert(index_ustr,rbx_types::Variant::Vector3(typed_value.into()));
|
||||
},
|
||||
let value=match &property.data_type{
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::Float32)=>{
|
||||
let typed_value:f32=coerce_float32(&value).ok_or_else(||mlua::Error::runtime("Expected f32"))?;
|
||||
instance.properties.insert(index_ustr,rbx_types::Variant::Float32(typed_value));
|
||||
let typed_value=Number::from_lua(value.clone(),lua)?.to_f32();
|
||||
rbx_types::Variant::Float32(typed_value)
|
||||
},
|
||||
rbx_reflection::DataType::Enum(enum_name)=>{
|
||||
let typed_value=match &value{
|
||||
&mlua::Value::Integer(int)=>Ok(rbx_types::Enum::from_u32(int as u32)),
|
||||
&mlua::Value::Number(num)=>Ok(rbx_types::Enum::from_u32(num as u32)),
|
||||
mlua::Value::String(s)=>{
|
||||
let e=db.enums.get(enum_name).ok_or_else(||mlua::Error::runtime("Database DataType Enum name does not exist"))?;
|
||||
let e=db.enums.get(enum_name).ok_or_else(||mlua::Error::runtime("Database DataType Enum name does not exist"))?;
|
||||
Ok(rbx_types::Enum::from_u32(*e.items.get(&*s.to_str()?).ok_or_else(||mlua::Error::runtime("Invalid enum item"))?))
|
||||
},
|
||||
mlua::Value::UserData(any_user_data)=>{
|
||||
let e:crate::runner::r#enum::Enum=*any_user_data.borrow()?;
|
||||
let e:crate::runner::r#enum::EnumItem=*any_user_data.borrow()?;
|
||||
Ok(e.into())
|
||||
},
|
||||
_=>Err(mlua::Error::runtime("Expected Enum")),
|
||||
}?;
|
||||
instance.properties.insert(index_ustr,rbx_types::Variant::Enum(typed_value));
|
||||
rbx_types::Variant::Enum(typed_value)
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::Color3)=>{
|
||||
let typed_value:crate::runner::color3::Color3=*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected Color3"))?.borrow()?;
|
||||
instance.properties.insert(index_ustr,rbx_types::Variant::Color3(typed_value.into()));
|
||||
rbx_types::Variant::Color3(typed_value.into())
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::Bool)=>{
|
||||
let typed_value=value.as_boolean().ok_or_else(||mlua::Error::runtime("Expected boolean"))?;
|
||||
instance.properties.insert(index_ustr,rbx_types::Variant::Bool(typed_value));
|
||||
rbx_types::Variant::Bool(typed_value)
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::Int32)=>{
|
||||
let typed_value=value.as_i32().ok_or_else(||mlua::Error::runtime("Expected Int32"))?;
|
||||
rbx_types::Variant::Int32(typed_value)
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::String)=>{
|
||||
let typed_value=value.as_str().ok_or_else(||mlua::Error::runtime("Expected boolean"))?;
|
||||
instance.properties.insert(index_ustr,rbx_types::Variant::String(typed_value.to_owned()));
|
||||
let typed_value=match &value{
|
||||
mlua::Value::Integer(i)=>i.to_string(),
|
||||
mlua::Value::Number(n)=>n.to_string(),
|
||||
mlua::Value::String(s)=>s.to_str()?.to_owned(),
|
||||
_=>return Err(mlua::Error::runtime("Expected string")),
|
||||
};
|
||||
rbx_types::Variant::String(typed_value)
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::UDim2)=>{
|
||||
let typed_value:&crate::runner::udim2::UDim2=&*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected UDim2"))?.borrow()?;
|
||||
rbx_types::Variant::UDim2(typed_value.clone().into())
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::NumberRange)=>{
|
||||
let typed_value:&crate::runner::number_range::NumberRange=&*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected NumberRange"))?.borrow()?;
|
||||
rbx_types::Variant::NumberRange(typed_value.clone().into())
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::NumberSequence)=>{
|
||||
let typed_value:crate::runner::number_sequence::NumberSequence=*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected NumberSequence"))?.borrow()?;
|
||||
instance.properties.insert(index_ustr,rbx_types::Variant::NumberSequence(typed_value.into()));
|
||||
let typed_value:&crate::runner::number_sequence::NumberSequence=&*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected NumberSequence"))?.borrow()?;
|
||||
rbx_types::Variant::NumberSequence(typed_value.clone().into())
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::ColorSequence)=>{
|
||||
let typed_value:crate::runner::color_sequence::ColorSequence=*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected ColorSequence"))?.borrow()?;
|
||||
instance.properties.insert(index_ustr,rbx_types::Variant::ColorSequence(typed_value.into()));
|
||||
let typed_value:&crate::runner::color_sequence::ColorSequence=&*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected ColorSequence"))?.borrow()?;
|
||||
rbx_types::Variant::ColorSequence(typed_value.clone().into())
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::Vector2)=>{
|
||||
let typed_value:crate::runner::vector2::Vector2=*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected Vector2"))?.borrow()?;
|
||||
rbx_types::Variant::Vector2(typed_value.clone().into())
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::Vector3)=>{
|
||||
let typed_value:crate::runner::vector3::Vector3=*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected Vector3"))?.borrow()?;
|
||||
rbx_types::Variant::Vector3(typed_value.clone().into())
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::CFrame)=>{
|
||||
let typed_value:crate::runner::cframe::CFrame=*value.as_userdata().ok_or_else(||mlua::Error::runtime("Expected CFrame"))?.borrow()?;
|
||||
rbx_types::Variant::CFrame(typed_value.clone().into())
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::ContentId)=>{
|
||||
let typed_value=value.as_str().ok_or_else(||mlua::Error::runtime("Expected string"))?.to_owned();
|
||||
rbx_types::Variant::ContentId(typed_value.into())
|
||||
},
|
||||
rbx_reflection::DataType::Value(rbx_types::VariantType::Ref)=>{
|
||||
// why clone?
|
||||
let typed_value=Option::<Instance>::from_lua(value.clone(),lua)?;
|
||||
rbx_types::Variant::Ref(typed_value.map_or(Ref::none(),|instance|instance.referent))
|
||||
},
|
||||
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);
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
@ -345,28 +456,53 @@ type CFD=phf::Map<&'static str,// Class name
|
||||
ClassFunctionPointer
|
||||
>
|
||||
>;
|
||||
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"))),
|
||||
}
|
||||
})
|
||||
});
|
||||
const GET_PLAYERS:ClassFunctionPointer=cf!(|_lua,_this,()|->mlua::Result<_>{
|
||||
Ok(Vec::<Instance>::new())
|
||||
});
|
||||
const NO_OP:ClassFunctionPointer=cf!(|_lua,_this,_:mlua::MultiValue|->mlua::Result<_>{Ok(())});
|
||||
static CLASS_FUNCTION_DATABASE:CFD=phf::phf_map!{
|
||||
"DataModel"=>phf::phf_map!{
|
||||
"GetService"=>cf!(|lua,_this,service:mlua::String|{
|
||||
dom_mut(lua,|dom|{
|
||||
//dom.root_ref()==this.referent ?
|
||||
let service=&*service.to_str()?;
|
||||
match service{
|
||||
"Lighting"|"RunService"=>{
|
||||
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(referent))
|
||||
},
|
||||
other=>Err::<Instance,_>(mlua::Error::runtime(format!("Service '{other}' not supported"))),
|
||||
}
|
||||
})
|
||||
}),
|
||||
"service"=>GET_SERVICE,
|
||||
"GetService"=>GET_SERVICE,
|
||||
},
|
||||
"Terrain"=>phf::phf_map!{
|
||||
"FillBlock"=>cf!(|_lua,_,_:(crate::runner::cframe::CFrame,Vector3,crate::runner::r#enum::Enum)|mlua::Result::Ok(()))
|
||||
"FillBall"=>cf!(|_lua,_,_:(Vector3,Number,crate::runner::r#enum::CoerceEnum)|mlua::Result::Ok(())),
|
||||
"FillBlock"=>cf!(|_lua,_,_:(crate::runner::cframe::CFrame,Vector3,crate::runner::r#enum::CoerceEnum)|mlua::Result::Ok(())),
|
||||
"FillCylinder"=>cf!(|_lua,_,_:(crate::runner::cframe::CFrame,Number,Number,crate::runner::r#enum::CoerceEnum)|mlua::Result::Ok(())),
|
||||
"SetMaterialColor"=>cf!(|_lua,_,_:(crate::runner::r#enum::CoerceEnum,crate::runner::color3::Color3)|mlua::Result::Ok(())),
|
||||
},
|
||||
"Players"=>phf::phf_map!{
|
||||
"players"=>GET_PLAYERS,
|
||||
"GetPlayers"=>GET_PLAYERS,
|
||||
},
|
||||
"Sound"=>phf::phf_map!{
|
||||
"Play"=>NO_OP,
|
||||
},
|
||||
"TweenService"=>phf::phf_map!{
|
||||
"Create"=>cf!(|_lua,_,(instance,tween_info,goal):(Instance,crate::runner::tween_info::TweenInfo,mlua::Table)|->mlua::Result<_>{
|
||||
Ok(crate::runner::tween::Tween::create(
|
||||
instance,
|
||||
tween_info,
|
||||
goal,
|
||||
))
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
@ -423,7 +559,7 @@ fn class_methods_store_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut ClassMethodsS
|
||||
/// A virtual property pointer definition shorthand.
|
||||
type VirtualPropertyFunctionPointer=fn(&rbx_types::Variant)->Option<rbx_types::Variant>;
|
||||
const fn vpp(
|
||||
property:&'static str,
|
||||
property:&'static HashStr,
|
||||
pointer:VirtualPropertyFunctionPointer,
|
||||
)->VirtualProperty{
|
||||
VirtualProperty{
|
||||
@ -432,7 +568,7 @@ const fn vpp(
|
||||
}
|
||||
}
|
||||
struct VirtualProperty{
|
||||
property:&'static str,// Source property name
|
||||
property:&'static HashStr,// Source property name
|
||||
pointer:VirtualPropertyFunctionPointer,
|
||||
}
|
||||
type VPD=phf::Map<&'static str,// Class name
|
||||
@ -440,9 +576,10 @@ type VPD=phf::Map<&'static str,// Class name
|
||||
VirtualProperty
|
||||
>
|
||||
>;
|
||||
static CFRAME:&HashStr=hstr!("CFrame");
|
||||
static VIRTUAL_PROPERTY_DATABASE:VPD=phf::phf_map!{
|
||||
"BasePart"=>phf::phf_map!{
|
||||
"Position"=>vpp("CFrame",|c:&rbx_types::Variant|{
|
||||
"Position"=>vpp(CFRAME,|c:&rbx_types::Variant|{
|
||||
let c=match c{
|
||||
rbx_types::Variant::CFrame(c)=>c,
|
||||
_=>return None,//fail silently and ungracefully
|
||||
@ -453,78 +590,69 @@ static VIRTUAL_PROPERTY_DATABASE:VPD=phf::phf_map!{
|
||||
};
|
||||
|
||||
fn find_virtual_property(
|
||||
properties:&rbx_dom_weak::UstrMap<rbx_types::Variant>,
|
||||
properties:&rbx_dom_weak::HashStrMap<rbx_types::Variant>,
|
||||
class:&rbx_reflection::ClassDescriptor,
|
||||
index:&Ustr,
|
||||
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(&ustr(virtual_property.property))?;
|
||||
let variant=properties.get(virtual_property.property)?;
|
||||
|
||||
//Transform Source property with provided function
|
||||
(virtual_property.pointer)(variant)
|
||||
}
|
||||
|
||||
// lazy-loaded per-instance userdata values
|
||||
// This whole thing is a bad idea and a garbage collection nightmare.
|
||||
// TODO: recreate rbx_dom_weak with my own instance type that owns this data.
|
||||
type CreateUserData=fn(&mlua::Lua)->mlua::Result<mlua::AnyUserData>;
|
||||
type LUD=phf::Map<&'static str,// Class name
|
||||
phf::Map<&'static str,// Value name
|
||||
CreateUserData
|
||||
>
|
||||
>;
|
||||
fn create_script_signal(lua:&mlua::Lua)->mlua::Result<mlua::AnyUserData>{
|
||||
lua.create_any_userdata(crate::runner::script_signal::ScriptSignal::new())
|
||||
}
|
||||
static LAZY_USER_DATA:LUD=phf::phf_map!{
|
||||
"RunService"=>phf::phf_map!{
|
||||
"RenderStepped"=>|lua|{
|
||||
lua.create_any_userdata(crate::runner::script_signal::ScriptSignal::new())
|
||||
},
|
||||
"Stepped"=>create_script_signal,
|
||||
"Heartbeat"=>create_script_signal,
|
||||
"RenderStepped"=>create_script_signal,
|
||||
},
|
||||
"Players"=>phf::phf_map!{
|
||||
"PlayerAdded"=>create_script_signal,
|
||||
},
|
||||
"BasePart"=>phf::phf_map!{
|
||||
"Touched"=>create_script_signal,
|
||||
"TouchEnded"=>create_script_signal,
|
||||
},
|
||||
"Instance"=>phf::phf_map!{
|
||||
"ChildAdded"=>create_script_signal,
|
||||
"ChildRemoved"=>create_script_signal,
|
||||
"DescendantAdded"=>create_script_signal,
|
||||
"DescendantRemoved"=>create_script_signal,
|
||||
},
|
||||
"ClickDetector"=>phf::phf_map!{
|
||||
"MouseClick"=>create_script_signal,
|
||||
},
|
||||
};
|
||||
#[derive(Default)]
|
||||
pub struct InstanceValueStore{
|
||||
values:HashMap<Ref,
|
||||
HashMap<&'static str,
|
||||
mlua::AnyUserData
|
||||
>
|
||||
>,
|
||||
}
|
||||
pub struct InstanceValues<'a>{
|
||||
named_values:&'static phf::Map<&'static str,CreateUserData>,
|
||||
values:&'a mut HashMap<&'static str,mlua::AnyUserData>,
|
||||
}
|
||||
impl InstanceValueStore{
|
||||
pub fn get_or_create_instance_values(&mut self,instance:&rbx_dom_weak::Instance)->Option<InstanceValues>{
|
||||
LAZY_USER_DATA.get(instance.class.as_str())
|
||||
.map(|named_values|
|
||||
InstanceValues{
|
||||
named_values,
|
||||
values:self.values.entry(instance.referent())
|
||||
.or_insert_with(||HashMap::new()),
|
||||
}
|
||||
)
|
||||
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)else{
|
||||
return Ok(None)
|
||||
};
|
||||
if let Some((hstr,create_userdata))=db.superclasses_iter(class).find_map(|superclass|
|
||||
// find pair (class,index)
|
||||
LAZY_USER_DATA.get(&superclass.name)
|
||||
.and_then(|map|map.get(index)).map(|f|(superclass.name,f))
|
||||
){
|
||||
return Ok(Some(match instance.userdata.entry(hstr){
|
||||
Entry::Occupied(entry)=>entry.get().clone(),
|
||||
Entry::Vacant(entry)=>entry.insert(create_userdata(lua)?).clone(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
impl InstanceValues<'_>{
|
||||
pub fn get_or_create_value(&mut self,lua:&mlua::Lua,index:&str)->mlua::Result<Option<mlua::AnyUserData>>{
|
||||
Ok(match self.named_values.get_entry(index){
|
||||
Some((&static_index_str,&function_pointer))=>Some(
|
||||
match self.values.entry(static_index_str){
|
||||
Entry::Occupied(entry)=>entry.get().clone(),
|
||||
Entry::Vacant(entry)=>entry.insert(
|
||||
function_pointer(lua)?
|
||||
).clone(),
|
||||
}
|
||||
),
|
||||
None=>None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn instance_value_store_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut InstanceValueStore)->mlua::Result<T>)->mlua::Result<T>{
|
||||
let mut cf=lua.app_data_mut::<InstanceValueStore>().ok_or_else(||mlua::Error::runtime("InstanceValueStore missing"))?;
|
||||
f(&mut *cf)
|
||||
Ok(None)
|
||||
}
|
||||
|
@ -10,6 +10,18 @@ macro_rules! type_from_lua_userdata{
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! type_from_lua_userdata_clone{
|
||||
($ty:ident)=>{
|
||||
impl mlua::FromLua for $ty{
|
||||
fn from_lua(value:mlua::Value,_lua:&mlua::Lua)->Result<Self,mlua::Error>{
|
||||
match value{
|
||||
mlua::Value::UserData(ud)=>Ok(ud.borrow::<Self>()?.clone()),
|
||||
other=>Err(mlua::Error::runtime(format!("Expected {} got {:?}",stringify!($ty),other))),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! type_from_lua_userdata_lua_lifetime{
|
||||
($ty:ident)=>{
|
||||
impl mlua::FromLua for $ty<'static>{
|
||||
|
@ -3,10 +3,19 @@ mod macros;
|
||||
mod runner;
|
||||
|
||||
mod r#enum;
|
||||
mod task;
|
||||
mod udim;
|
||||
mod tween;
|
||||
mod udim2;
|
||||
mod color3;
|
||||
mod cframe;
|
||||
mod number;
|
||||
mod vector2;
|
||||
mod vector3;
|
||||
mod brickcolor;
|
||||
mod tween_info;
|
||||
pub mod instance;
|
||||
mod number_range;
|
||||
mod script_signal;
|
||||
mod color_sequence;
|
||||
mod number_sequence;
|
||||
|
43
lib/roblox_emulator/src/runner/number.rs
Normal file
43
lib/roblox_emulator/src/runner/number.rs
Normal file
@ -0,0 +1,43 @@
|
||||
// the goal of this module is to provide an intermediate type
|
||||
// that is guaranteed to be some kind of number, and provide
|
||||
// methods to coerce it into various more specific types.
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
pub enum Number{
|
||||
Integer(i32),
|
||||
Number(f64),
|
||||
}
|
||||
macro_rules! impl_ty{
|
||||
($ident:ident,$ty:ty)=>{
|
||||
impl Number{
|
||||
#[inline]
|
||||
pub fn $ident(self)->$ty{
|
||||
match self{
|
||||
Self::Integer(int)=>int as $ty,
|
||||
Self::Number(num)=>num as $ty,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<Number> for $ty{
|
||||
fn from(value:Number)->$ty{
|
||||
value.$ident()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl_ty!(to_u32,u32);
|
||||
impl_ty!(to_i32,i32);
|
||||
impl_ty!(to_f32,f32);
|
||||
impl_ty!(to_u64,u64);
|
||||
impl_ty!(to_i64,i64);
|
||||
impl_ty!(to_f64,f64);
|
||||
|
||||
impl mlua::FromLua for Number{
|
||||
fn from_lua(value:mlua::Value,_lua:&mlua::Lua)->Result<Self,mlua::Error>{
|
||||
match value{
|
||||
mlua::Value::Integer(int)=>Ok(Number::Integer(int)),
|
||||
mlua::Value::Number(num)=>Ok(Number::Number(num)),
|
||||
other=>Err(mlua::Error::runtime(format!("Expected {} got {:?}",stringify!(Number),other))),
|
||||
}
|
||||
}
|
||||
}
|
34
lib/roblox_emulator/src/runner/number_range.rs
Normal file
34
lib/roblox_emulator/src/runner/number_range.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use super::number::Number;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NumberRange(rbx_types::NumberRange);
|
||||
impl NumberRange{
|
||||
pub const fn new(min:f32,max:f32)->Self{
|
||||
Self(rbx_types::NumberRange{min,max})
|
||||
}
|
||||
}
|
||||
impl From<NumberRange> for rbx_types::NumberRange{
|
||||
fn from(NumberRange(value):NumberRange)->rbx_types::NumberRange{
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let table=lua.create_table()?;
|
||||
|
||||
table.raw_set("new",
|
||||
lua.create_function(|_,(min,max):(Number,Option<Number>)|{
|
||||
Ok(match max{
|
||||
Some(max)=>NumberRange::new(min.into(),max.into()),
|
||||
None=>NumberRange::new(min.into(),min.into()),
|
||||
})
|
||||
})?
|
||||
)?;
|
||||
|
||||
globals.set("NumberRange",table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl mlua::UserData for NumberRange{}
|
||||
type_from_lua_userdata_clone!(NumberRange);
|
@ -1,31 +1,36 @@
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct NumberSequence{}
|
||||
#[derive(Clone)]
|
||||
pub struct NumberSequence(rbx_types::NumberSequence);
|
||||
impl NumberSequence{
|
||||
pub const fn new()->Self{
|
||||
Self{}
|
||||
pub const fn new(keypoints:Vec<rbx_types::NumberSequenceKeypoint>)->Self{
|
||||
Self(rbx_types::NumberSequence{keypoints})
|
||||
}
|
||||
}
|
||||
impl Into<rbx_types::NumberSequence> for NumberSequence{
|
||||
fn into(self)->rbx_types::NumberSequence{
|
||||
rbx_types::NumberSequence{
|
||||
keypoints:Vec::new()
|
||||
}
|
||||
impl From<NumberSequence> for rbx_types::NumberSequence{
|
||||
fn from(NumberSequence(value):NumberSequence)->rbx_types::NumberSequence{
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let number_sequence_table=lua.create_table()?;
|
||||
let table=lua.create_table()?;
|
||||
|
||||
number_sequence_table.raw_set("new",
|
||||
table.raw_set("new",
|
||||
lua.create_function(|_,_:mlua::MultiValue|
|
||||
Ok(NumberSequence::new())
|
||||
Ok(NumberSequence::new(Vec::new()))
|
||||
)?
|
||||
)?;
|
||||
|
||||
globals.set("NumberSequence",number_sequence_table)?;
|
||||
globals.set("NumberSequence",table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl mlua::UserData for NumberSequence{}
|
||||
type_from_lua_userdata!(NumberSequence);
|
||||
impl mlua::FromLua for NumberSequence{
|
||||
fn from_lua(value:mlua::Value,_lua:&mlua::Lua)->Result<Self,mlua::Error>{
|
||||
match value{
|
||||
mlua::Value::UserData(ud)=>Ok(ud.borrow::<Self>()?.clone()),
|
||||
other=>Err(mlua::Error::runtime(format!("Expected {} got {:?}",stringify!(NumberSequence),other))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ pub enum Error{
|
||||
error:mlua::Error
|
||||
},
|
||||
RustLua(mlua::Error),
|
||||
NoServices,
|
||||
Services(crate::context::ServicesError),
|
||||
}
|
||||
impl std::fmt::Display for Error{
|
||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
@ -31,14 +31,19 @@ fn init(lua:&mlua::Lua)->mlua::Result<()>{
|
||||
//global environment
|
||||
let globals=lua.globals();
|
||||
|
||||
#[cfg(feature="run-service")]
|
||||
crate::scheduler::set_globals(lua,&globals)?;
|
||||
super::task::set_globals(lua,&globals)?;
|
||||
super::script_signal::set_globals(lua,&globals)?;
|
||||
super::r#enum::set_globals(lua,&globals)?;
|
||||
super::udim::set_globals(lua,&globals)?;
|
||||
super::udim2::set_globals(lua,&globals)?;
|
||||
super::color3::set_globals(lua,&globals)?;
|
||||
super::brickcolor::set_globals(lua,&globals)?;
|
||||
super::vector2::set_globals(lua,&globals)?;
|
||||
super::vector3::set_globals(lua,&globals)?;
|
||||
super::cframe::set_globals(lua,&globals)?;
|
||||
super::instance::instance::set_globals(lua,&globals)?;
|
||||
super::tween_info::set_globals(lua,&globals)?;
|
||||
super::number_range::set_globals(lua,&globals)?;
|
||||
super::number_sequence::set_globals(lua,&globals)?;
|
||||
super::color_sequence::set_globals(lua,&globals)?;
|
||||
|
||||
@ -53,23 +58,21 @@ impl Runner{
|
||||
init(&runner.lua).map_err(Error::RustLua)?;
|
||||
Ok(runner)
|
||||
}
|
||||
pub fn runnable_context<'a>(self,context:&'a mut Context)->Result<Runnable<'a>,Error>{
|
||||
let services=context.find_services().ok_or(Error::NoServices)?;
|
||||
self.runnable_context_with_services(context,&services)
|
||||
}
|
||||
pub fn runnable_context_with_services<'a>(self,context:&'a mut Context,services:&crate::context::Services)->Result<Runnable<'a>,Error>{
|
||||
pub fn runnable_context<'a>(self,context:&'a mut Context<'a>)->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::Instance::new_unchecked(context.services.game)).map_err(Error::RustLua)?;
|
||||
globals.set("workspace",super::instance::Instance::new_unchecked(context.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)});
|
||||
// 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.
|
||||
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{
|
||||
lua:self.lua,
|
||||
_lifetime:&std::marker::PhantomData
|
||||
_lifetime:std::marker::PhantomData
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -77,11 +80,11 @@ impl Runner{
|
||||
//Runnable is the same thing but has context set, which it holds the lifetime for.
|
||||
pub struct Runnable<'a>{
|
||||
lua:mlua::Lua,
|
||||
_lifetime:&'a std::marker::PhantomData<()>
|
||||
_lifetime:std::marker::PhantomData<&'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::<crate::context::LuaAppData>();
|
||||
#[cfg(feature="run-service")]
|
||||
self.lua.remove_app_data::<crate::scheduler::Scheduler>();
|
||||
Runner{
|
||||
@ -123,20 +126,15 @@ impl Runnable<'_>{
|
||||
}
|
||||
#[cfg(feature="run-service")]
|
||||
pub fn run_service_step(&self)->Result<(),mlua::Error>{
|
||||
let render_stepped=super::instance::instance::dom_mut(&self.lua,|dom|{
|
||||
let render_stepped_signal=super::instance::instance::dom_mut(&self.lua,|dom|{
|
||||
let run_service=super::instance::instance::find_first_child_of_class(dom,dom.root(),"RunService").ok_or_else(||mlua::Error::runtime("RunService missing"))?;
|
||||
super::instance::instance::instance_value_store_mut(&self.lua,|instance_value_store|{
|
||||
//unwrap because I trust my find_first_child_of_class function to
|
||||
let mut instance_values=instance_value_store.get_or_create_instance_values(run_service).ok_or_else(||mlua::Error::runtime("RunService InstanceValues missing"))?;
|
||||
let render_stepped=instance_values.get_or_create_value(&self.lua,"RenderStepped")?;
|
||||
//let stepped=instance_values.get_or_create_value(&self.lua,"Stepped")?;
|
||||
//let heartbeat=instance_values.get_or_create_value(&self.lua,"Heartbeat")?;
|
||||
Ok(render_stepped)
|
||||
Ok(match run_service.userdata.get(&static_ustr("RenderStepped")){
|
||||
Some(render_stepped)=>Some(render_stepped.borrow::<super::script_signal::ScriptSignal>()?.clone()),
|
||||
None=>None
|
||||
})
|
||||
})?;
|
||||
if let Some(render_stepped)=render_stepped{
|
||||
let signal:&super::script_signal::ScriptSignal=&*render_stepped.borrow()?;
|
||||
signal.fire(&mlua::MultiValue::new());
|
||||
if let Some(render_stepped_signal)=render_stepped_signal{
|
||||
render_stepped_signal.fire(&mlua::MultiValue::new());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -98,12 +98,9 @@ impl ScriptConnection{
|
||||
|
||||
impl mlua::UserData for ScriptSignal{
|
||||
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
||||
methods.add_method("Connect",|_lua,this,f:mlua::Function|
|
||||
Ok(this.connect(f))
|
||||
);
|
||||
methods.add_method("Once",|_lua,this,f:mlua::Function|
|
||||
Ok(this.once(f))
|
||||
);
|
||||
methods.add_method("connect",|_lua,this,f:mlua::Function|Ok(this.connect(f)));
|
||||
methods.add_method("Connect",|_lua,this,f:mlua::Function|Ok(this.connect(f)));
|
||||
methods.add_method("Once",|_lua,this,f:mlua::Function|Ok(this.once(f)));
|
||||
// Fire is not allowed to be called from Lua
|
||||
// methods.add_method("Fire",|_lua,this,args:mlua::MultiValue|
|
||||
// Ok(this.fire(args))
|
||||
@ -164,6 +161,7 @@ pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
.call::<mlua::Function>(())?;
|
||||
|
||||
lua.register_userdata_type::<ScriptSignal>(|reg|{
|
||||
reg.add_field("wait",wait.clone());
|
||||
reg.add_field("Wait",wait);
|
||||
mlua::UserData::register(reg);
|
||||
})?;
|
||||
|
42
lib/roblox_emulator/src/runner/task.rs
Normal file
42
lib/roblox_emulator/src/runner/task.rs
Normal file
@ -0,0 +1,42 @@
|
||||
#[cfg(not(feature="run-service"))]
|
||||
fn no_op(_lua:&mlua::Lua,_time:Option<super::number::Number>)->mlua::Result<f64>{
|
||||
Ok(0.0)
|
||||
}
|
||||
fn tick(_lua:&mlua::Lua,_:())->mlua::Result<f64>{
|
||||
Ok(0.0)
|
||||
}
|
||||
// This is used to avoid calling coroutine.yield from the rust side.
|
||||
const LUA_WAIT:&str=
|
||||
"local coroutine_yield=coroutine.yield
|
||||
local schedule_thread=schedule_thread
|
||||
return function(dt)
|
||||
schedule_thread(dt)
|
||||
return coroutine_yield()
|
||||
end";
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let coroutine_table=globals.get::<mlua::Table>("coroutine")?;
|
||||
#[cfg(feature="run-service")]
|
||||
let schedule_thread=lua.create_function(crate::scheduler::schedule_thread)?;
|
||||
#[cfg(not(feature="run-service"))]
|
||||
let schedule_thread=lua.create_function(no_op)?;
|
||||
|
||||
//create wait function environment
|
||||
let wait_env=lua.create_table()?;
|
||||
wait_env.raw_set("coroutine",coroutine_table)?;
|
||||
wait_env.raw_set("schedule_thread",schedule_thread)?;
|
||||
|
||||
//construct wait function from Lua code
|
||||
let wait=lua.load(LUA_WAIT)
|
||||
.set_name("wait")
|
||||
.set_environment(wait_env)
|
||||
.call::<mlua::Function>(())?;
|
||||
|
||||
globals.raw_set("wait",wait)?;
|
||||
|
||||
// TODO: move this somewhere it belongs
|
||||
let tick=lua.create_function(tick)?;
|
||||
globals.raw_set("tick",tick)?;
|
||||
|
||||
Ok(())
|
||||
}
|
35
lib/roblox_emulator/src/runner/tween.rs
Normal file
35
lib/roblox_emulator/src/runner/tween.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use super::instance::Instance;
|
||||
use super::tween_info::TweenInfo;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone)]
|
||||
pub struct Tween{
|
||||
instance:Instance,
|
||||
tween_info:TweenInfo,
|
||||
goal:mlua::Table,
|
||||
playback_state:rbx_types::Enum,
|
||||
}
|
||||
impl Tween{
|
||||
pub fn create(
|
||||
instance:Instance,
|
||||
tween_info:TweenInfo,
|
||||
goal:mlua::Table,
|
||||
)->Self{
|
||||
Self{
|
||||
instance,
|
||||
tween_info,
|
||||
goal,
|
||||
// Enum.PlaybackState.Begin
|
||||
playback_state:rbx_types::Enum::from_u32(0),
|
||||
}
|
||||
}
|
||||
pub fn play(&mut self){
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::UserData for Tween{
|
||||
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
||||
methods.add_method_mut("Play",|_,this,()|Ok(this.play()))
|
||||
}
|
||||
}
|
||||
type_from_lua_userdata_clone!(Tween);
|
45
lib/roblox_emulator/src/runner/tween_info.rs
Normal file
45
lib/roblox_emulator/src/runner/tween_info.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use super::number::Number;
|
||||
use super::r#enum::{CoerceEnum,Enums};
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone)]
|
||||
pub struct TweenInfo{
|
||||
time:f64,
|
||||
easing_style:rbx_types::Enum,
|
||||
easing_direction:rbx_types::Enum,
|
||||
repeat_count:u32,
|
||||
reverses:bool,
|
||||
delay_time:f64,
|
||||
}
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let table=lua.create_table()?;
|
||||
|
||||
table.raw_set("new",
|
||||
lua.create_function(|_,(time,easing_style,easing_direction,repeat_count,reverses,delay_time):(Option<Number>,Option<CoerceEnum>,Option<CoerceEnum>,Option<u32>,Option<bool>,Option<Number>)|{
|
||||
Ok(TweenInfo{
|
||||
time:time.map_or(1.0,Number::to_f64),
|
||||
easing_style:match easing_style{
|
||||
// Enum.EasingStyle.Quad
|
||||
None=>rbx_types::Enum::from_u32(3),
|
||||
Some(e)=>e.coerce_to(Enums.get("EasingStyle").unwrap())?.into(),
|
||||
},
|
||||
easing_direction:match easing_direction{
|
||||
// Enum.EasingDirection.Out
|
||||
None=>rbx_types::Enum::from_u32(1),
|
||||
Some(e)=>e.coerce_to(Enums.get("EasingDirection").unwrap())?.into(),
|
||||
},
|
||||
repeat_count:repeat_count.unwrap_or(0),
|
||||
reverses:reverses.unwrap_or(false),
|
||||
delay_time:delay_time.map_or(0.0,Number::to_f64),
|
||||
})
|
||||
})?
|
||||
)?;
|
||||
|
||||
globals.set("TweenInfo",table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl mlua::UserData for TweenInfo{}
|
||||
type_from_lua_userdata_clone!(TweenInfo);
|
36
lib/roblox_emulator/src/runner/udim.rs
Normal file
36
lib/roblox_emulator/src/runner/udim.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use super::number::Number;
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct UDim(rbx_types::UDim);
|
||||
impl UDim{
|
||||
pub fn new(scale:f32,offset:i32)->Self{
|
||||
UDim(rbx_types::UDim::new(scale,offset))
|
||||
}
|
||||
}
|
||||
impl From<rbx_types::UDim> for UDim{
|
||||
fn from(value:rbx_types::UDim)->Self{
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let table=lua.create_table()?;
|
||||
|
||||
table.raw_set("new",
|
||||
lua.create_function(|_,(scale,offset):(Number,i32)|
|
||||
Ok(UDim::new(scale.into(),offset))
|
||||
)?
|
||||
)?;
|
||||
|
||||
globals.set("UDim",table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl mlua::UserData for UDim{
|
||||
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
|
||||
fields.add_field_method_get("Scale",|_,UDim(this)|Ok(this.scale));
|
||||
fields.add_field_method_get("Offset",|_,UDim(this)|Ok(this.offset));
|
||||
}
|
||||
}
|
||||
type_from_lua_userdata!(UDim);
|
40
lib/roblox_emulator/src/runner/udim2.rs
Normal file
40
lib/roblox_emulator/src/runner/udim2.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use super::udim::UDim;
|
||||
use super::number::Number;
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct UDim2(rbx_types::UDim2);
|
||||
impl UDim2{
|
||||
pub fn new(sx:f32,ox:i32,sy:f32,oy:i32)->Self{
|
||||
UDim2(rbx_types::UDim2::new(
|
||||
rbx_types::UDim::new(sx,ox),
|
||||
rbx_types::UDim::new(sy,oy),
|
||||
))
|
||||
}
|
||||
}
|
||||
impl From<UDim2> for rbx_types::UDim2{
|
||||
fn from(UDim2(value):UDim2)->rbx_types::UDim2{
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let table=lua.create_table()?;
|
||||
|
||||
table.raw_set("new",
|
||||
lua.create_function(|_,(sx,ox,sy,oy):(Number,i32,Number,i32)|
|
||||
Ok(UDim2::new(sx.into(),ox,sy.into(),oy))
|
||||
)?
|
||||
)?;
|
||||
|
||||
globals.set("UDim2",table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl mlua::UserData for UDim2{
|
||||
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
|
||||
fields.add_field_method_get("X",|_,UDim2(this)|Ok(UDim::from(this.x)));
|
||||
fields.add_field_method_get("Y",|_,UDim2(this)|Ok(UDim::from(this.y)));
|
||||
}
|
||||
}
|
||||
type_from_lua_userdata!(UDim2);
|
93
lib/roblox_emulator/src/runner/vector2.rs
Normal file
93
lib/roblox_emulator/src/runner/vector2.rs
Normal file
@ -0,0 +1,93 @@
|
||||
use mlua::FromLua;
|
||||
|
||||
use super::number::Number;
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct Vector2(glam::Vec2);
|
||||
|
||||
impl Vector2{
|
||||
pub const fn new(x:f32,y:f32)->Self{
|
||||
Self(glam::vec2(x,y))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let table=lua.create_table()?;
|
||||
|
||||
//Vector2.new
|
||||
table.raw_set("new",
|
||||
lua.create_function(|_,(x,y):(Option<Number>,Option<Number>)|
|
||||
match (x,y){
|
||||
(Some(x),Some(y))=>Ok(Vector2::new(x.into(),y.into())),
|
||||
(None,None)=>Ok(Vector2(glam::Vec2::ZERO)),
|
||||
_=>Err(mlua::Error::runtime("Unsupported arguments to Vector2.new")),
|
||||
}
|
||||
)?
|
||||
)?;
|
||||
|
||||
globals.set("Vector2",table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl From<Vector2> for rbx_types::Vector2{
|
||||
fn from(Vector2(v):Vector2)->rbx_types::Vector2{
|
||||
rbx_types::Vector2::new(v.x,v.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<rbx_types::Vector2> for Vector2{
|
||||
fn from(value:rbx_types::Vector2)->Vector2{
|
||||
Vector2::new(value.x,value.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::UserData for Vector2{
|
||||
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
|
||||
fields.add_field_method_get("magnitude",|_,Vector2(this)|Ok(this.length()));
|
||||
fields.add_field_method_get("Magnitude",|_,Vector2(this)|Ok(this.length()));
|
||||
fields.add_field_method_get("unit",|_,Vector2(this)|Ok(Vector2(this.normalize())));
|
||||
fields.add_field_method_get("Unit",|_,Vector2(this)|Ok(Vector2(this.normalize())));
|
||||
fields.add_field_method_get("x",|_,Vector2(this)|Ok(this.x));
|
||||
fields.add_field_method_get("X",|_,Vector2(this)|Ok(this.x));
|
||||
fields.add_field_method_get("y",|_,Vector2(this)|Ok(this.y));
|
||||
fields.add_field_method_get("Y",|_,Vector2(this)|Ok(this.y));
|
||||
}
|
||||
|
||||
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
||||
//methods.add_method("area",|_,this,()| Ok(this.length * this.width));
|
||||
|
||||
methods.add_meta_function(mlua::MetaMethod::Add,|_,(Vector2(this),Vector2(val)):(Self,Self)|Ok(Self(this+val)));
|
||||
methods.add_meta_function(mlua::MetaMethod::Sub,|_,(Vector2(this),Vector2(val)):(Self,Self)|Ok(Self(this-val)));
|
||||
methods.add_meta_function(mlua::MetaMethod::Mul,|lua,(lhs,rhs):(mlua::Value,mlua::Value)|{
|
||||
match (lhs,rhs){
|
||||
(mlua::Value::UserData(lhs),mlua::Value::UserData(rhs))=>lhs.borrow_scoped(|Vector2(lhs):&Vector2|rhs.borrow_scoped(|Vector2(rhs):&Vector2|Self(lhs*rhs)))?,
|
||||
(lhs,mlua::Value::UserData(rhs))=>{
|
||||
let lhs=Number::from_lua(lhs,lua)?;
|
||||
rhs.borrow_scoped(|Vector2(rhs):&Vector2|Self(lhs.to_f32()*rhs))
|
||||
},
|
||||
(mlua::Value::UserData(lhs),rhs)=>{
|
||||
let rhs=Number::from_lua(rhs,lua)?;
|
||||
lhs.borrow_scoped(|Vector2(lhs):&Vector2|Self(lhs*rhs.to_f32()))
|
||||
},
|
||||
_=>Err(mlua::Error::runtime(format!("Expected Vector2")))
|
||||
}
|
||||
});
|
||||
methods.add_meta_function(mlua::MetaMethod::Div,|_,(Vector2(this),val):(Self,mlua::Value)|{
|
||||
match val{
|
||||
mlua::Value::Integer(n)=>Ok(Self(this/(n as f32))),
|
||||
mlua::Value::Number(n)=>Ok(Self(this/(n as f32))),
|
||||
mlua::Value::UserData(ud)=>ud.borrow_scoped(|Vector2(rhs):&Vector2|Self(this/rhs)),
|
||||
other=>Err(mlua::Error::runtime(format!("Attempt to divide Vector2 by {other:?}"))),
|
||||
}
|
||||
});
|
||||
methods.add_meta_function(mlua::MetaMethod::ToString,|_,Vector2(this):Self|
|
||||
Ok(format!("Vector2.new({},{})",
|
||||
this.x,
|
||||
this.y,
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
type_from_lua_userdata!(Vector2);
|
@ -1,3 +1,7 @@
|
||||
use mlua::FromLua;
|
||||
|
||||
use super::number::Number;
|
||||
|
||||
#[derive(Clone,Copy)]
|
||||
pub struct Vector3(pub(crate)glam::Vec3A);
|
||||
|
||||
@ -8,23 +12,27 @@ impl Vector3{
|
||||
}
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let vector3_table=lua.create_table()?;
|
||||
let table=lua.create_table()?;
|
||||
|
||||
//Vector3.new
|
||||
vector3_table.raw_set("new",
|
||||
lua.create_function(|_,(x,y,z):(f32,f32,f32)|
|
||||
Ok(Vector3::new(x,y,z))
|
||||
table.raw_set("new",
|
||||
lua.create_function(|_,(x,y,z):(Option<Number>,Option<Number>,Option<Number>)|
|
||||
match (x,y,z){
|
||||
(Some(x),Some(y),Some(z))=>Ok(Vector3::new(x.into(),y.into(),z.into())),
|
||||
(None,None,None)=>Ok(Vector3(glam::Vec3A::ZERO)),
|
||||
_=>Err(mlua::Error::runtime("Unsupported arguments to Vector3.new")),
|
||||
}
|
||||
)?
|
||||
)?;
|
||||
|
||||
globals.set("Vector3",vector3_table)?;
|
||||
globals.set("Vector3",table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Into<rbx_types::Vector3> for Vector3{
|
||||
fn into(self)->rbx_types::Vector3{
|
||||
rbx_types::Vector3::new(self.0.x,self.0.y,self.0.z)
|
||||
impl From<Vector3> for rbx_types::Vector3{
|
||||
fn from(Vector3(v):Vector3)->rbx_types::Vector3{
|
||||
rbx_types::Vector3::new(v.x,v.y,v.z)
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,44 +44,50 @@ impl From<rbx_types::Vector3> for Vector3{
|
||||
|
||||
impl mlua::UserData for Vector3{
|
||||
fn add_fields<F:mlua::UserDataFields<Self>>(fields:&mut F){
|
||||
fields.add_field_method_get("magnitude",|_,this|Ok(this.0.length()));
|
||||
fields.add_field_method_get("x",|_,this|Ok(this.0.x));
|
||||
fields.add_field_method_set("x",|_,this,val|{
|
||||
this.0.x=val;
|
||||
Ok(())
|
||||
});
|
||||
fields.add_field_method_get("y",|_,this|Ok(this.0.y));
|
||||
fields.add_field_method_set("y",|_,this,val|{
|
||||
this.0.y=val;
|
||||
Ok(())
|
||||
});
|
||||
fields.add_field_method_get("z",|_,this|Ok(this.0.z));
|
||||
fields.add_field_method_set("z",|_,this,val|{
|
||||
this.0.z=val;
|
||||
Ok(())
|
||||
});
|
||||
fields.add_field_method_get("magnitude",|_,Vector3(this)|Ok(this.length()));
|
||||
fields.add_field_method_get("Magnitude",|_,Vector3(this)|Ok(this.length()));
|
||||
fields.add_field_method_get("unit",|_,Vector3(this)|Ok(Vector3(this.normalize())));
|
||||
fields.add_field_method_get("Unit",|_,Vector3(this)|Ok(Vector3(this.normalize())));
|
||||
fields.add_field_method_get("x",|_,Vector3(this)|Ok(this.x));
|
||||
fields.add_field_method_get("X",|_,Vector3(this)|Ok(this.x));
|
||||
fields.add_field_method_get("y",|_,Vector3(this)|Ok(this.y));
|
||||
fields.add_field_method_get("Y",|_,Vector3(this)|Ok(this.y));
|
||||
fields.add_field_method_get("z",|_,Vector3(this)|Ok(this.z));
|
||||
fields.add_field_method_get("Z",|_,Vector3(this)|Ok(this.z));
|
||||
}
|
||||
|
||||
fn add_methods<M:mlua::UserDataMethods<Self>>(methods:&mut M){
|
||||
//methods.add_method("area",|_,this,()| Ok(this.length * this.width));
|
||||
|
||||
methods.add_meta_function(mlua::MetaMethod::Add,|_,(this,val):(Self,Self)|Ok(Self(this.0+val.0)));
|
||||
methods.add_meta_function(mlua::MetaMethod::Div,|_,(this,val):(Self,mlua::Value)|{
|
||||
match val{
|
||||
mlua::Value::Integer(n)=>Ok(Self(this.0/(n as f32))),
|
||||
mlua::Value::Number(n)=>Ok(Self(this.0/(n as f32))),
|
||||
mlua::Value::UserData(ud)=>{
|
||||
let rhs:Vector3=ud.take()?;
|
||||
Ok(Self(this.0/rhs.0))
|
||||
methods.add_meta_function(mlua::MetaMethod::Add,|_,(Vector3(this),Vector3(val)):(Self,Self)|Ok(Self(this+val)));
|
||||
methods.add_meta_function(mlua::MetaMethod::Sub,|_,(Vector3(this),Vector3(val)):(Self,Self)|Ok(Self(this-val)));
|
||||
methods.add_meta_function(mlua::MetaMethod::Mul,|lua,(lhs,rhs):(mlua::Value,mlua::Value)|{
|
||||
match (lhs,rhs){
|
||||
(mlua::Value::UserData(lhs),mlua::Value::UserData(rhs))=>lhs.borrow_scoped(|Vector3(lhs):&Vector3|rhs.borrow_scoped(|Vector3(rhs):&Vector3|Self(lhs*rhs)))?,
|
||||
(lhs,mlua::Value::UserData(rhs))=>{
|
||||
let lhs=Number::from_lua(lhs,lua)?;
|
||||
rhs.borrow_scoped(|Vector3(rhs):&Vector3|Self(lhs.to_f32()*rhs))
|
||||
},
|
||||
(mlua::Value::UserData(lhs),rhs)=>{
|
||||
let rhs=Number::from_lua(rhs,lua)?;
|
||||
lhs.borrow_scoped(|Vector3(lhs):&Vector3|Self(lhs*rhs.to_f32()))
|
||||
},
|
||||
_=>Err(mlua::Error::runtime(format!("Expected Vector3")))
|
||||
}
|
||||
});
|
||||
methods.add_meta_function(mlua::MetaMethod::Div,|_,(Vector3(this),val):(Self,mlua::Value)|{
|
||||
match val{
|
||||
mlua::Value::Integer(n)=>Ok(Self(this/(n as f32))),
|
||||
mlua::Value::Number(n)=>Ok(Self(this/(n as f32))),
|
||||
mlua::Value::UserData(ud)=>ud.borrow_scoped(|Vector3(rhs):&Vector3|Self(this/rhs)),
|
||||
other=>Err(mlua::Error::runtime(format!("Attempt to divide Vector3 by {other:?}"))),
|
||||
}
|
||||
});
|
||||
methods.add_meta_function(mlua::MetaMethod::ToString,|_,this:Self|
|
||||
methods.add_meta_function(mlua::MetaMethod::ToString,|_,Vector3(this):Self|
|
||||
Ok(format!("Vector3.new({},{},{})",
|
||||
this.0.x,
|
||||
this.0.y,
|
||||
this.0.z,
|
||||
this.x,
|
||||
this.y,
|
||||
this.z,
|
||||
))
|
||||
);
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ pub fn scheduler_mut<T>(lua:&mlua::Lua,mut f:impl FnMut(&mut crate::scheduler::S
|
||||
f(&mut *scheduler)
|
||||
}
|
||||
|
||||
fn schedule_thread(lua:&mlua::Lua,dt:mlua::Value)->Result<(),mlua::Error>{
|
||||
pub fn schedule_thread(lua:&mlua::Lua,dt:mlua::Value)->Result<(),mlua::Error>{
|
||||
let delay=match dt{
|
||||
mlua::Value::Integer(i)=>i.max(0) as u64*60,
|
||||
mlua::Value::Number(f)=>{
|
||||
@ -75,32 +75,3 @@ fn schedule_thread(lua:&mlua::Lua,dt:mlua::Value)->Result<(),mlua::Error>{
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
// This is used to avoid calling coroutine.yield from the rust side.
|
||||
const LUA_WAIT:&str=
|
||||
"local coroutine_yield=coroutine.yield
|
||||
local schedule_thread=schedule_thread
|
||||
return function(dt)
|
||||
schedule_thread(dt)
|
||||
return coroutine_yield()
|
||||
end";
|
||||
|
||||
pub fn set_globals(lua:&mlua::Lua,globals:&mlua::Table)->Result<(),mlua::Error>{
|
||||
let coroutine_table=globals.get::<mlua::Table>("coroutine")?;
|
||||
let schedule_thread=lua.create_function(schedule_thread)?;
|
||||
|
||||
//create wait function environment
|
||||
let wait_env=lua.create_table()?;
|
||||
wait_env.raw_set("coroutine",coroutine_table)?;
|
||||
wait_env.raw_set("schedule_thread",schedule_thread)?;
|
||||
|
||||
//construct wait function from Lua code
|
||||
let wait=lua.load(LUA_WAIT)
|
||||
.set_name("wait")
|
||||
.set_environment(wait_env)
|
||||
.call::<mlua::Function>(())?;
|
||||
|
||||
globals.raw_set("wait",wait)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -56,8 +56,6 @@ impl From<strafesnet_common::integer::Ratio64Vec2> for Ratio64Vec2{
|
||||
}
|
||||
}
|
||||
|
||||
pub type Angle32=i32;
|
||||
pub type Planar64=i64;
|
||||
pub type Planar64Vec3=[i64;3];
|
||||
pub type Planar64Mat3=[i64;9];
|
||||
pub type Planar64Affine3=[i64;12];
|
||||
|
@ -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 = "1.0.0"
|
||||
rbx_dom_weak = "3.0.0"
|
||||
rbx_reflection_database = "1.0.0"
|
||||
rbx_xml = "1.0.0"
|
||||
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" }
|
||||
@ -25,7 +25,7 @@ strafesnet_rbx_loader = { version = "0.6.0", path = "../lib/rbx_loader", registr
|
||||
strafesnet_snf = { version = "0.3.0", path = "../lib/snf", registry = "strafesnet" }
|
||||
thiserror = "2.0.11"
|
||||
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread", "fs"] }
|
||||
vbsp = "0.8.0"
|
||||
vbsp = "0.9.1"
|
||||
vbsp-entities-css = "0.6.0"
|
||||
vmdl = "0.2.0"
|
||||
vmt-parser = "0.2.0"
|
||||
|
@ -3,11 +3,16 @@ use std::io::{Cursor,Read,Seek};
|
||||
use std::collections::HashSet;
|
||||
use clap::{Args,Subcommand};
|
||||
use anyhow::Result as AResult;
|
||||
use rbx_dom_weak::{ustr,Instance};
|
||||
use rbx_dom_weak::Instance;
|
||||
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)]
|
||||
@ -102,8 +107,8 @@ SurfaceAppearance.RoughnessMapContent
|
||||
WrapLayer.ReferenceMeshContent
|
||||
*/
|
||||
|
||||
fn accumulate_content(content_list:&mut HashSet<RobloxAssetId>,object:&Instance,property:&str){
|
||||
let Some(rbx_dom_weak::types::Variant::Content(content))=object.properties.get(&ustr(property))else{
|
||||
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());
|
||||
return;
|
||||
};
|
||||
@ -117,8 +122,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:&str){
|
||||
let Some(rbx_dom_weak::types::Variant::ContentId(content))=object.properties.get(&ustr(property))else{
|
||||
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());
|
||||
return;
|
||||
};
|
||||
@ -144,8 +149,8 @@ impl UniqueAssets{
|
||||
fn collect(&mut self,object:&Instance){
|
||||
match object.class.as_str(){
|
||||
"Beam"=>accumulate_content_id(&mut self.textures,object,"Texture"),
|
||||
"Decal"=>accumulate_content_id(&mut self.textures,object,"Texture"),
|
||||
"Texture"=>accumulate_content_id(&mut self.textures,object,"Texture"),
|
||||
"Decal"=>accumulate_content(&mut self.textures,object,"TextureContent"),
|
||||
"Texture"=>accumulate_content(&mut self.textures,object,"TextureContent"),
|
||||
"FileMesh"=>accumulate_content_id(&mut self.textures,object,"TextureId"),
|
||||
"MeshPart"=>{
|
||||
accumulate_content(&mut self.textures,object,"TextureContent");
|
||||
@ -418,7 +423,7 @@ async fn convert_to_snf(path:&Path,output_folder:PathBuf)->AResult<()>{
|
||||
std::io::Cursor::new(entire_file)
|
||||
).map_err(ConvertError::RobloxRead)?;
|
||||
|
||||
let mut place=model.into_place();
|
||||
let mut place=strafesnet_rbx_loader::Place::from(model);
|
||||
place.run_scripts();
|
||||
|
||||
let map=place.to_snf(LoadFailureMode::DefaultToNone).map_err(ConvertError::RobloxLoad)?;
|
||||
|
@ -97,7 +97,7 @@ pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<LoadFormat,LoadError>{
|
||||
ReadFormat::SNFM(map)=>Ok(LoadFormat::Map(map)),
|
||||
#[cfg(feature="roblox")]
|
||||
ReadFormat::Roblox(model)=>{
|
||||
let mut place=model.into_place();
|
||||
let mut place=strafesnet_rbx_loader::Place::from(model);
|
||||
place.run_scripts();
|
||||
Ok(LoadFormat::Map(
|
||||
place.to_snf(LoadFailureMode::DefaultToNone).map_err(LoadError::LoadRoblox)?
|
||||
|
Reference in New Issue
Block a user