parent
ea58fcedc9
commit
c63997d161
@ -43,27 +43,28 @@ impl From<crate::nats_types::CheckSubmissionRequest> for CheckRequest{
|
||||
enum Zone{
|
||||
Start(ModeID),
|
||||
Finish(ModeID),
|
||||
Anticheat(ModeID),
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
||||
struct ModeID(u64);
|
||||
macro_rules! write_zone{
|
||||
($fname:ident,$zone:expr)=>{
|
||||
fn $fname(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
match self{
|
||||
ModeID(0)=>write!(f,concat!("Map",$zone)),
|
||||
ModeID(1)=>write!(f,concat!("Bonus",$zone)),
|
||||
ModeID(other)=>write!(f,concat!("Bonus{}",$zone),other),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl ModeID{
|
||||
const MAIN:Self=Self(0);
|
||||
const BONUS:Self=Self(1);
|
||||
fn write_start_zone(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
match self{
|
||||
ModeID(0)=>write!(f,"MapStart"),
|
||||
ModeID(1)=>write!(f,"BonusStart"),
|
||||
ModeID(other)=>write!(f,"Bonus{other}Start"),
|
||||
}
|
||||
}
|
||||
fn write_finish_zone(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||
match self{
|
||||
ModeID(0)=>write!(f,"MapFinish"),
|
||||
ModeID(1)=>write!(f,"BonusFinish"),
|
||||
ModeID(other)=>write!(f,"Bonus{other}Finish"),
|
||||
}
|
||||
}
|
||||
write_zone!(write_start_zone,"Start");
|
||||
write_zone!(write_finish_zone,"Finish");
|
||||
write_zone!(write_anticheat_zone,"Anticheat");
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub enum ZoneParseError{
|
||||
@ -76,8 +77,10 @@ impl std::str::FromStr for Zone{
|
||||
match s{
|
||||
"MapStart"=>Ok(Self::Start(ModeID::MAIN)),
|
||||
"MapFinish"=>Ok(Self::Finish(ModeID::MAIN)),
|
||||
"MapAnticheat"=>Ok(Self::Anticheat(ModeID::MAIN)),
|
||||
"BonusStart"=>Ok(Self::Start(ModeID::BONUS)),
|
||||
"BonusFinish"=>Ok(Self::Finish(ModeID::BONUS)),
|
||||
"BonusAnticheat"=>Ok(Self::Anticheat(ModeID::BONUS)),
|
||||
other=>{
|
||||
let bonus_start_pattern=lazy_regex::lazy_regex!(r"^Bonus(\d+)Start$|^BonusStart(\d+)$");
|
||||
if let Some(captures)=bonus_start_pattern.captures(other){
|
||||
@ -87,6 +90,10 @@ impl std::str::FromStr for Zone{
|
||||
if let Some(captures)=bonus_finish_pattern.captures(other){
|
||||
return Ok(Self::Finish(ModeID(captures[1].parse().map_err(ZoneParseError::ParseInt)?)));
|
||||
}
|
||||
let bonus_finish_pattern=lazy_regex::lazy_regex!(r"^Bonus(\d+)Anticheat$|^BonusAnticheat(\d+)$");
|
||||
if let Some(captures)=bonus_finish_pattern.captures(other){
|
||||
return Ok(Self::Anticheat(ModeID(captures[1].parse().map_err(ZoneParseError::ParseInt)?)));
|
||||
}
|
||||
Err(ZoneParseError::NoCaptures)
|
||||
}
|
||||
}
|
||||
@ -106,6 +113,7 @@ struct WormholeOutID(u64);
|
||||
struct Counts{
|
||||
mode_start_counts:HashMap<ModeID,u64>,
|
||||
mode_finish_counts:HashMap<ModeID,u64>,
|
||||
mode_anticheat_counts:HashMap<ModeID,u64>,
|
||||
spawn_counts:HashMap<SpawnID,u64>,
|
||||
wormhole_out_counts:HashMap<WormholeOutID,u64>,
|
||||
}
|
||||
@ -129,7 +137,8 @@ pub fn get_model_info<'a>(dom:&'a rbx_dom_weak::WeakDom,model_instance:&'a rbx_d
|
||||
match instance.name.parse(){
|
||||
Ok(Zone::Start(mode_id))=>*counts.mode_start_counts.entry(mode_id).or_insert(0)+=1,
|
||||
Ok(Zone::Finish(mode_id))=>*counts.mode_finish_counts.entry(mode_id).or_insert(0)+=1,
|
||||
_=>(),
|
||||
Ok(Zone::Anticheat(mode_id))=>*counts.mode_anticheat_counts.entry(mode_id).or_insert(0)+=1,
|
||||
Err(_)=>(),
|
||||
}
|
||||
// Spawns
|
||||
let spawn_pattern=lazy_regex::lazy_regex!(r"^Spawn(\d+)$");
|
||||
@ -217,12 +226,36 @@ impl<ID> DuplicateCheckContext<ID>{
|
||||
}
|
||||
|
||||
// check that there is at least one matching item for each item in a reference set, and no extra items
|
||||
pub struct SetDifferenceCheckContext<ID>{
|
||||
pub struct SetDifferenceCheckContextAllowNone<ID>{
|
||||
extra:HashMap<ID,u64>,
|
||||
}
|
||||
pub struct SetDifferenceCheckContextAtLeastOne<ID>{
|
||||
extra:HashMap<ID,u64>,
|
||||
missing:HashSet<ID>,
|
||||
}
|
||||
pub struct SetDifferenceCheck<ID>(Result<(),SetDifferenceCheckContext<ID>>);
|
||||
impl<ID> SetDifferenceCheckContext<ID>{
|
||||
pub struct SetDifferenceCheck<Context>(Result<(),Context>);
|
||||
impl<ID> SetDifferenceCheckContextAllowNone<ID>{
|
||||
fn new(initial_set:HashMap<ID,u64>)->Self{
|
||||
Self{
|
||||
extra:initial_set,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<ID:Eq+std::hash::Hash> SetDifferenceCheckContextAllowNone<ID>{
|
||||
fn check<T>(mut self,reference_set:&HashMap<ID,T>)->SetDifferenceCheck<Self>{
|
||||
// remove correct entries
|
||||
for (id,_) in reference_set{
|
||||
self.extra.remove(id);
|
||||
}
|
||||
// if any entries remain, they are incorrect
|
||||
if self.extra.is_empty(){
|
||||
SetDifferenceCheck(Ok(()))
|
||||
}else{
|
||||
SetDifferenceCheck(Err(self))
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<ID> SetDifferenceCheckContextAtLeastOne<ID>{
|
||||
fn new(initial_set:HashMap<ID,u64>)->Self{
|
||||
Self{
|
||||
extra:initial_set,
|
||||
@ -230,21 +263,20 @@ impl<ID> SetDifferenceCheckContext<ID>{
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<ID:Copy+Eq+std::hash::Hash> SetDifferenceCheckContext<ID>{
|
||||
fn check<T>(self,reference_set:&HashMap<ID,T>)->SetDifferenceCheck<ID>{
|
||||
let Self{mut extra,mut missing}=self;
|
||||
impl<ID:Copy+Eq+std::hash::Hash> SetDifferenceCheckContextAtLeastOne<ID>{
|
||||
fn check<T>(mut self,reference_set:&HashMap<ID,T>)->SetDifferenceCheck<Self>{
|
||||
// remove correct entries
|
||||
for (id,_) in reference_set{
|
||||
if extra.remove(id).is_none(){
|
||||
if self.extra.remove(id).is_none(){
|
||||
// the set did not contain a required item. This is a fail
|
||||
missing.insert(*id);
|
||||
self.missing.insert(*id);
|
||||
}
|
||||
}
|
||||
// if any entries remain, they are incorrect
|
||||
if extra.is_empty()&&missing.is_empty(){
|
||||
if self.extra.is_empty()&&self.missing.is_empty(){
|
||||
SetDifferenceCheck(Ok(()))
|
||||
}else{
|
||||
SetDifferenceCheck(Err(Self{extra,missing}))
|
||||
SetDifferenceCheck(Err(self))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -278,8 +310,9 @@ pub struct MapCheck<'a>{
|
||||
// No duplicate map starts (including bonuses)
|
||||
mode_start_counts:DuplicateCheck<ModeID>,
|
||||
// At least one finish zone for each start zone, and no finishes with no start
|
||||
mode_finish_counts:SetDifferenceCheck<ModeID>,
|
||||
// TODO: check for dangling MapAnticheat zones (no associated MapStart)
|
||||
mode_finish_counts:SetDifferenceCheck<SetDifferenceCheckContextAtLeastOne<ModeID>>,
|
||||
// check for dangling MapAnticheat zones (no associated MapStart)
|
||||
mode_anticheat_counts:SetDifferenceCheck<SetDifferenceCheckContextAllowNone<ModeID>>,
|
||||
// Spawn1 must exist
|
||||
spawn1:Result<(),()>,
|
||||
// No duplicate Spawn#
|
||||
@ -331,7 +364,12 @@ impl<'a> ModelInfo<'a>{
|
||||
};
|
||||
|
||||
// check that at least one end zone exists for each start zone.
|
||||
let mode_finish_counts=SetDifferenceCheckContext::new(self.counts.mode_finish_counts)
|
||||
let mode_finish_counts=SetDifferenceCheckContextAtLeastOne::new(self.counts.mode_finish_counts)
|
||||
.check(&self.counts.mode_start_counts);
|
||||
|
||||
// check that there are no anticheat zones that have no corresponding start zone.
|
||||
// modes are allowed to have 0 anticheat zones.
|
||||
let mode_anticheat_counts=SetDifferenceCheckContextAllowNone::new(self.counts.mode_anticheat_counts)
|
||||
.check(&self.counts.mode_start_counts);
|
||||
|
||||
// there must be exactly one start zone for every mode in the map.
|
||||
@ -352,6 +390,7 @@ impl<'a> ModelInfo<'a>{
|
||||
mapstart,
|
||||
mode_start_counts,
|
||||
mode_finish_counts,
|
||||
mode_anticheat_counts,
|
||||
spawn1,
|
||||
spawn_counts,
|
||||
wormhole_out_counts,
|
||||
@ -371,6 +410,7 @@ impl<'a> MapCheck<'a>{
|
||||
mapstart:Ok(()),
|
||||
mode_start_counts:DuplicateCheck(Ok(())),
|
||||
mode_finish_counts:SetDifferenceCheck(Ok(())),
|
||||
mode_anticheat_counts:SetDifferenceCheck(Ok(())),
|
||||
spawn1:Ok(()),
|
||||
spawn_counts:DuplicateCheck(Ok(())),
|
||||
wormhole_out_counts:DuplicateCheck(Ok(())),
|
||||
@ -443,7 +483,8 @@ impl<'a> std::fmt::Display for MapCheck<'a>{
|
||||
if !context.extra.is_empty(){
|
||||
write!(f,"Extra finish zones with no matching start zone: ")?;
|
||||
comma_separated(f,context.extra.iter(),|f,(mode_id,_count)|
|
||||
mode_id.write_finish_zone(f))?;
|
||||
mode_id.write_finish_zone(f)
|
||||
)?;
|
||||
writeln!(f,"")?;
|
||||
}
|
||||
// perhaps there are missing end zones (context.missing)
|
||||
@ -455,6 +496,16 @@ impl<'a> std::fmt::Display for MapCheck<'a>{
|
||||
writeln!(f,"")?;
|
||||
}
|
||||
}
|
||||
if let SetDifferenceCheck(Err(context))=&self.mode_anticheat_counts{
|
||||
// perhaps there are extra end zones (context.extra)
|
||||
if !context.extra.is_empty(){
|
||||
write!(f,"Extra anticheat zones with no matching start zone: ")?;
|
||||
comma_separated(f,context.extra.iter(),|f,(mode_id,_count)|
|
||||
mode_id.write_anticheat_zone(f)
|
||||
)?;
|
||||
writeln!(f,"")?;
|
||||
}
|
||||
}
|
||||
if let Err(())=&self.spawn1{
|
||||
writeln!(f,"Model has no Spawn1")?;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user