validator: marginally improve map check clarity #123

Merged
Quaternions merged 1 commits from check into staging 2025-04-10 04:07:26 +00:00

@ -40,51 +40,74 @@ impl From<crate::nats_types::CheckSubmissionRequest> for CheckRequest{
} }
} }
#[derive(Default)]
pub enum Check{
Pass,
#[default]
Fail,
}
impl Check{
fn pass(&self)->bool{
match self{
Check::Pass=>true,
Check::Fail=>false,
}
}
}
impl std::fmt::Display for Check{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
match self{
Check::Pass=>write!(f,"passed"),
Check::Fail=>write!(f,"failed"),
}
}
}
#[derive(Default)] #[derive(Default)]
pub struct CheckReport{ pub struct CheckReport{
// === METADATA CHECKS === // === METADATA CHECKS ===
// the model must have exactly 1 root part (models uploaded to roblox can have multiple roots) // the model must have exactly 1 root part (models uploaded to roblox can have multiple roots)
exactly_one_root:bool, exactly_one_root:Check,
// the root must be of class Model // the root must be of class Model
root_is_model:bool, root_is_model:Check,
// the prefix of the model's name must match the game it was submitted for. bhop_ for bhop, and surf_ for surf // the prefix of the model's name must match the game it was submitted for. bhop_ for bhop, and surf_ for surf
model_name_prefix_is_valid:bool, model_name_prefix_is_valid:Check,
// your model's name must match this regex: ^[a-z0-9_] // your model's name must match this regex: ^[a-z0-9_]
model_name_is_snake_case:bool, model_name_is_snake_case:Check,
// map must have a StringValue named Creator and DisplayName. additionally, they must both have a value // map must have a StringValue named Creator and DisplayName. additionally, they must both have a value
has_display_name:bool, has_display_name:Check,
has_creator:bool, has_creator:Check,
// the display name must be capitalized // the display name must be capitalized
display_name_is_title_case:bool, display_name_is_title_case:Check,
// you cannot have any Scripts or ModuleScripts that have the keyword 'getfenv" or 'require' // you cannot have any Scripts or ModuleScripts that have the keyword 'getfenv" or 'require'
// you cannot have more than 50 duplicate scripts // you cannot have more than 50 duplicate scripts
// === MODE CHECKS === // === MODE CHECKS ===
// Exactly one MapStart // Exactly one MapStart
exactly_one_mapstart:bool, exactly_one_mapstart:Check,
// At least one MapFinish // At least one MapFinish
at_least_one_mapfinish:bool, at_least_one_mapfinish:Check,
// Spawn0 or Spawn1 must exist // Spawn0 or Spawn1 must exist
spawn1_exists:bool, spawn1_exists:Check,
// No duplicate Spawn# // No duplicate Spawn#
no_duplicate_spawns:bool, no_duplicate_spawns:Check,
// No duplicate WormholeOut# (duplicate WormholeIn# ok) // No duplicate WormholeOut# (duplicate WormholeIn# ok)
no_duplicate_wormhole_out:bool, no_duplicate_wormhole_out:Check,
} }
impl CheckReport{ impl CheckReport{
pub fn pass(&self)->bool{ pub fn pass(&self)->bool{
return self.exactly_one_root return self.exactly_one_root.pass()
&&self.root_is_model &&self.root_is_model.pass()
&&self.model_name_prefix_is_valid &&self.model_name_prefix_is_valid.pass()
&&self.model_name_is_snake_case &&self.model_name_is_snake_case.pass()
&&self.has_display_name &&self.has_display_name.pass()
&&self.has_creator &&self.has_creator.pass()
&&self.display_name_is_title_case &&self.display_name_is_title_case.pass()
&&self.exactly_one_mapstart &&self.exactly_one_mapstart.pass()
&&self.at_least_one_mapfinish &&self.at_least_one_mapfinish.pass()
&&self.spawn1_exists &&self.spawn1_exists.pass()
&&self.no_duplicate_spawns &&self.no_duplicate_spawns.pass()
&&self.no_duplicate_wormhole_out &&self.no_duplicate_wormhole_out.pass()
} }
} }
impl std::fmt::Display for CheckReport{ impl std::fmt::Display for CheckReport{
@ -192,13 +215,13 @@ pub fn check(dom:&rbx_dom_weak::WeakDom)->CheckReport{
return report; return report;
}; };
report.exactly_one_root=true; report.exactly_one_root=Check::Pass;
if model_instance.class=="Model"{ if model_instance.class=="Model"{
report.root_is_model=true; report.root_is_model=Check::Pass;
} }
if model_instance.name==model_instance.name.to_snake_case(){ if model_instance.name==model_instance.name.to_snake_case(){
report.model_name_is_snake_case=true; report.model_name_is_snake_case=Check::Pass;
} }
// extract model info // extract model info
@ -207,9 +230,9 @@ pub fn check(dom:&rbx_dom_weak::WeakDom)->CheckReport{
// check DisplayName // check DisplayName
if let Ok(display_name)=display_name{ if let Ok(display_name)=display_name{
if !display_name.is_empty(){ if !display_name.is_empty(){
report.has_display_name=true; report.has_display_name=Check::Pass;
if display_name==display_name.to_title_case(){ if display_name==display_name.to_title_case(){
report.display_name_is_title_case=true; report.display_name_is_title_case=Check::Pass;
} }
} }
} }
@ -217,13 +240,13 @@ pub fn check(dom:&rbx_dom_weak::WeakDom)->CheckReport{
// check Creator // check Creator
if let Ok(creator)=creator{ if let Ok(creator)=creator{
if !creator.is_empty(){ if !creator.is_empty(){
report.has_creator=true; report.has_creator=Check::Pass;
} }
} }
// check GameID // check GameID
if game_id.is_ok(){ if game_id.is_ok(){
report.model_name_prefix_is_valid=true; report.model_name_prefix_is_valid=Check::Pass;
} }
// === MODE CHECKS === // === MODE CHECKS ===
@ -257,24 +280,24 @@ pub fn check(dom:&rbx_dom_weak::WeakDom)->CheckReport{
// MapStart must exist && there must be exactly one of any bonus start zones. // MapStart must exist && there must be exactly one of any bonus start zones.
if counts.mode_start_counts.get(&ModeID::MAIN)==Some(&1) if counts.mode_start_counts.get(&ModeID::MAIN)==Some(&1)
&&counts.mode_start_counts.iter().all(|(_,&c)|c==1){ &&counts.mode_start_counts.iter().all(|(_,&c)|c==1){
report.exactly_one_mapstart=true; report.exactly_one_mapstart=Check::Pass;
} }
// iterate over start zones // iterate over start zones
if counts.mode_start_counts.iter().all(|(mode_id,_)| if counts.mode_start_counts.iter().all(|(mode_id,_)|
// ensure that at least one end zone exists with the same mode id // ensure that at least one end zone exists with the same mode id
counts.mode_finish_counts.get(mode_id).is_some_and(|&num|0<num) counts.mode_finish_counts.get(mode_id).is_some_and(|&num|0<num)
){ ){
report.at_least_one_mapfinish=true; report.at_least_one_mapfinish=Check::Pass;
} }
// Spawn1 must exist // Spawn1 must exist
if counts.spawn_counts.get(&SpawnID::FIRST).is_some(){ if counts.spawn_counts.get(&SpawnID::FIRST).is_some(){
report.spawn1_exists=true; report.spawn1_exists=Check::Pass;
} }
if counts.spawn_counts.iter().all(|(_,&c)|c==1){ if counts.spawn_counts.iter().all(|(_,&c)|c==1){
report.no_duplicate_spawns=true; report.no_duplicate_spawns=Check::Pass;
} }
if counts.wormhole_out_counts.iter().all(|(_,&c)|c==1){ if counts.wormhole_out_counts.iter().all(|(_,&c)|c==1){
report.no_duplicate_wormhole_out=true; report.no_duplicate_wormhole_out=Check::Pass;
} }
report report