diff --git a/validation/src/check.rs b/validation/src/check.rs index 66d2a22..b4015c4 100644 --- a/validation/src/check.rs +++ b/validation/src/check.rs @@ -223,6 +223,7 @@ pub struct ModelInfo<'a>{ model_name:&'a str, map_info:MapInfo<'a>, counts:Counts<'a>, + unanchored_parts:Vec<&'a Instance>, } pub fn get_model_info<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&'a rbx_dom_weak::Instance)->ModelInfo<'a>{ @@ -232,6 +233,10 @@ pub fn get_model_info<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&'a rbx_d // count objects (default count is 0) let mut counts=Counts::default(); + // locate unanchored parts + let mut unanchored_parts=Vec::new(); + let anchored_ustr=rbx_dom_weak::ustr("Anchored"); + let db=rbx_reflection_database::get(); let base_part=&db.classes["BasePart"]; let base_parts=dom.descendants_of(model_instance.referent()).filter(|&instance| @@ -259,6 +264,10 @@ pub fn get_model_info<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&'a rbx_d Ok(WormholeElement{behaviour:WormholeBehaviour::Out,wormhole_id})=>*counts.wormhole_out_counts.entry(wormhole_id).or_insert(0)+=1, Err(_)=>(), } + // Unanchored parts + if let Some(rbx_dom_weak::types::Variant::Bool(false))=instance.properties.get(&anchored_ustr){ + unanchored_parts.push(instance); + } } ModelInfo{ @@ -266,6 +275,7 @@ pub fn get_model_info<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&'a rbx_d model_name:model_instance.name.as_str(), map_info, counts, + unanchored_parts, } } @@ -447,6 +457,9 @@ struct MapCheck<'a>{ // No duplicate WormholeOut# (duplicate WormholeIn# ok) // No dangling WormholeOut# wormhole_out_counts:DuplicateCheck, + + // === GENERAL CHECKS === + unanchored_parts:Result<(),Vec<&'a Instance>>, } impl<'a> ModelInfo<'a>{ @@ -520,6 +533,13 @@ impl<'a> ModelInfo<'a>{ // There must be exactly one of any perticular wormhole out id in the map. let wormhole_out_counts=DuplicateCheckContext(self.counts.wormhole_out_counts).check(|&c|1 ModelInfo<'a>{ spawn_counts, wormhole_in_counts, wormhole_out_counts, + unanchored_parts, } } } @@ -557,6 +578,7 @@ impl MapCheck<'_>{ spawn_counts:DuplicateCheck(Ok(())), wormhole_in_counts:SetDifferenceCheck(Ok(())), wormhole_out_counts:DuplicateCheck(Ok(())), + unanchored_parts:Ok(()), }=>{ Ok(MapInfoOwned{ display_name:display_name.to_owned(), @@ -780,6 +802,17 @@ impl MapCheck<'_>{ summary_format!("DuplicateWormholeOut","Duplicate WormholeOut: {context}") } }; + let unanchored_parts=match &self.unanchored_parts{ + Ok(())=>passed!("UnanchoredParts"), + Err(unanchored_parts)=>{ + let count=unanchored_parts.len(); + let plural=if count==1{"part"}else{"parts"}; + let context=Separated::new(", ",||unanchored_parts.iter().map(|&instance| + instance.name.as_str() + ).take(20)); + summary_format!("UnanchoredParts","{count} unanchored {plural}: {context}") + } + }; Ok(MapCheckList{checks:Box::new([ model_class, model_name, @@ -797,13 +830,14 @@ impl MapCheck<'_>{ extra_wormhole_in, missing_wormhole_in, duplicate_wormhole_out, + unanchored_parts, ])}) } } #[derive(serde::Serialize)] pub struct MapCheckList{ - pub checks:Box<[Check;16]>, + pub checks:Box<[Check;17]>, } pub struct CheckListAndVersion{