From 92fb8f54ceff56fb64d7b73d7cf398e09bd22f52 Mon Sep 17 00:00:00 2001 From: Quaternions <krakow20@gmail.com> Date: Wed, 2 Apr 2025 16:51:03 -0700 Subject: [PATCH] akfnjawef --- validation/src/create_mapfix.rs | 49 +++++++++++- validation/src/create_submission.rs | 81 +------------------ validation/src/main.rs | 1 + validation/src/rbx_util.rs | 119 ++++++++++++++++++++++++++++ validation/src/validator.rs | 46 +---------- 5 files changed, 171 insertions(+), 125 deletions(-) create mode 100644 validation/src/rbx_util.rs diff --git a/validation/src/create_mapfix.rs b/validation/src/create_mapfix.rs index dbfbc49..7c163bc 100644 --- a/validation/src/create_mapfix.rs +++ b/validation/src/create_mapfix.rs @@ -1,9 +1,16 @@ use crate::nats_types::CreateMapfixRequest; +use crate::rbx_util::{MapInfo,get_mapinfo,read_dom,ReadDomError,GetMapInfoError,ParseGameIDError}; #[allow(dead_code)] #[derive(Debug)] pub enum Error{ - Get(rbx_asset::cookie::GetError), + ModelVersionsPage(rbx_asset::cookie::PageError), + EmptyVersionsPage, + WrongCreatorType, + ModelFileDownload(rbx_asset::cookie::GetError), + ModelFileDecode(ReadDomError), + GetMapInfo(GetMapInfoError), + ParseGameID(ParseGameIDError), ApiActionMapfixCreate(submissions_api::Error), } impl std::fmt::Display for Error{ @@ -15,15 +22,51 @@ impl std::error::Error for Error{} impl crate::message_handler::MessageHandler{ pub async fn create_mapfix(&self,create_info:CreateMapfixRequest)->Result<(),Error>{ + // discover the latest asset version + let asset_versions_page=self.cookie_context.get_asset_versions_page(rbx_asset::cookie::AssetVersionsPageRequest{ + asset_id:create_info.ModelID, + cursor:None + }).await.map_err(Error::ModelVersionsPage)?; + + // grab version info + let first_version=asset_versions_page.data.first().ok_or(Error::EmptyVersionsPage)?; + + if first_version.creatorType!="User"{ + return Err(Error::WrongCreatorType); + } + + let asset_creator_id=first_version.creatorTargetId; + let asset_version=first_version.assetVersionNumber; + // download the map model version let model_data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{ asset_id:create_info.ModelID, - version:None, - }).await.map_err(Error::Get)?; + version:Some(asset_version), + }).await.map_err(Error::ModelFileDownload)?; + + // decode dom (slow!) + let dom=read_dom(&mut std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?; // parse create fields out of asset + let MapInfo{ + display_name, + creator, + game_id, + }=get_mapinfo(&dom).map_err(Error::GetMapInfo)?; + + let game_id=game_id.map_err(Error::ParseGameID)?; // call create on api + self.api.create_mapfix(submissions_api::types::CreateMapfixRequest{ + OperationID:create_info.OperationID, + AssetOwner:asset_creator_id as i64, + DisplayName:display_name.unwrap_or_default(), + Creator:creator.unwrap_or_default(), + GameID:game_id as i32, + AssetID:create_info.ModelID, + AssetVersion:asset_version, + TargetAssetID:create_info.TargetAssetID, + }).await.map_err(Error::ApiActionMapfixCreate)?; Ok(()) } diff --git a/validation/src/create_submission.rs b/validation/src/create_submission.rs index 4e5ea95..141338d 100644 --- a/validation/src/create_submission.rs +++ b/validation/src/create_submission.rs @@ -1,4 +1,5 @@ use crate::nats_types::CreateSubmissionRequest; +use crate::rbx_util::{MapInfo,get_mapinfo,read_dom,ReadDomError,GetMapInfoError,ParseGameIDError}; #[allow(dead_code)] #[derive(Debug)] @@ -7,7 +8,7 @@ pub enum Error{ EmptyVersionsPage, WrongCreatorType, ModelFileDownload(rbx_asset::cookie::GetError), - ModelFileDecode(crate::validator::ReadDomError), + ModelFileDecode(ReadDomError), GetMapInfo(GetMapInfoError), ParseGameID(ParseGameIDError), ApiActionSubmissionCreate(submissions_api::Error), @@ -40,11 +41,11 @@ impl crate::message_handler::MessageHandler{ // download the map model version let model_data=self.cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{ asset_id:create_info.ModelID, - version:None, + version:Some(asset_version), }).await.map_err(Error::ModelFileDownload)?; // decode dom (slow!) - let dom=crate::validator::read_dom(&mut std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?; + let dom=read_dom(&mut std::io::Cursor::new(model_data)).map_err(Error::ModelFileDecode)?; // parse create fields out of asset let MapInfo{ @@ -69,77 +70,3 @@ impl crate::message_handler::MessageHandler{ Ok(()) } } - -enum GameID{ - Bhop=1, - Surf=2, - FlyTrials=5, -} -#[derive(Debug)] -pub struct ParseGameIDError; -impl std::str::FromStr for GameID{ - type Err=ParseGameIDError; - fn from_str(s:&str)->Result<Self,Self::Err>{ - if s.starts_with("bhop_"){ - return Ok(GameID::Bhop); - } - if s.starts_with("surf_"){ - return Ok(GameID::Surf); - } - if s.starts_with("flytrials_"){ - return Ok(GameID::FlyTrials); - } - return Err(ParseGameIDError); - } -} - -struct MapInfo<'a>{ - display_name:Result<&'a str,StringValueError>, - creator:Result<&'a str,StringValueError>, - game_id:Result<GameID,ParseGameIDError>, -} - -enum StringValueError{ - ObjectNotFound, - ValueNotSet, - NonStringValue, -} - -fn string_value(instance:Option<&rbx_dom_weak::Instance>)->Result<&str,StringValueError>{ - let instance=instance.ok_or(StringValueError::ObjectNotFound)?; - let value=instance.properties.get("Value").ok_or(StringValueError::ValueNotSet)?; - match value{ - rbx_dom_weak::types::Variant::String(value)=>Ok(value), - _=>Err(StringValueError::NonStringValue), - } -} - -#[derive(Debug)] -pub enum GetMapInfoError{ - ModelFileRootMustHaveOneChild, - ModelFileChildRefIsNil, -} - -fn get_mapinfo(dom:&rbx_dom_weak::WeakDom)->Result<MapInfo,GetMapInfoError>{ - let &[map_ref]=dom.root().children()else{ - return Err(GetMapInfoError::ModelFileRootMustHaveOneChild); - }; - let model_instance=dom.get_by_ref(map_ref).ok_or(GetMapInfoError::ModelFileChildRefIsNil)?; - - Ok(MapInfo{ - display_name:string_value(find_first_child_class(dom,model_instance,"DisplayName","StringValue")), - creator:string_value(find_first_child_class(dom,model_instance,"Creator","StringValue")), - game_id:model_instance.name.parse(), - }) -} - -fn find_first_child_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&'a rbx_dom_weak::Instance,name:&'a str,class:&'a str)->Option<&'a rbx_dom_weak::Instance>{ - for &referent in instance.children(){ - if let Some(c)=dom.get_by_ref(referent){ - if c.name==name&&crate::validator::class_is_a(c.class.as_str(),class) { - return Some(c); - } - } - } - None -} diff --git a/validation/src/main.rs b/validation/src/main.rs index 5c7a458..e6fdc41 100644 --- a/validation/src/main.rs +++ b/validation/src/main.rs @@ -1,5 +1,6 @@ use futures::StreamExt; +mod rbx_util; mod message_handler; mod nats_types; mod types; diff --git a/validation/src/rbx_util.rs b/validation/src/rbx_util.rs new file mode 100644 index 0000000..947bb9c --- /dev/null +++ b/validation/src/rbx_util.rs @@ -0,0 +1,119 @@ + +#[allow(dead_code)] +#[derive(Debug)] +pub enum ReadDomError{ + Binary(rbx_binary::DecodeError), + Xml(rbx_xml::DecodeError), + Read(std::io::Error), + Seek(std::io::Error), + UnknownFormat([u8;8]), +} +impl std::fmt::Display for ReadDomError{ + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + write!(f,"{self:?}") + } +} +impl std::error::Error for ReadDomError{} + +pub fn read_dom<R:std::io::Read+std::io::Seek>(input:&mut R)->Result<rbx_dom_weak::WeakDom,ReadDomError>{ + let mut first_8=[0u8;8]; + std::io::Read::read_exact(input,&mut first_8).map_err(ReadDomError::Read)?; + std::io::Seek::rewind(input).map_err(ReadDomError::Seek)?; + match &first_8[0..4]{ + b"<rob"=>{ + match &first_8[4..8]{ + b"lox!"=>rbx_binary::from_reader(input).map_err(ReadDomError::Binary), + b"lox "=>rbx_xml::from_reader(input,rbx_xml::DecodeOptions::default()).map_err(ReadDomError::Xml), + _=>Err(ReadDomError::UnknownFormat(first_8)), + } + }, + _=>Err(ReadDomError::UnknownFormat(first_8)), + } +} + +pub 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 +} + +pub fn find_first_child_class<'a>(dom:&'a rbx_dom_weak::WeakDom,instance:&rbx_dom_weak::Instance,name:&str,class:&str)->Option<&'a rbx_dom_weak::Instance>{ + for &referent in instance.children(){ + if let Some(c)=dom.get_by_ref(referent){ + if c.name==name&&class_is_a(c.class.as_str(),class) { + return Some(c); + } + } + } + None +} + +pub enum GameID{ + Bhop=1, + Surf=2, + FlyTrials=5, +} +#[derive(Debug)] +pub struct ParseGameIDError; +impl std::str::FromStr for GameID{ + type Err=ParseGameIDError; + fn from_str(s:&str)->Result<Self,Self::Err>{ + if s.starts_with("bhop_"){ + return Ok(GameID::Bhop); + } + if s.starts_with("surf_"){ + return Ok(GameID::Surf); + } + if s.starts_with("flytrials_"){ + return Ok(GameID::FlyTrials); + } + return Err(ParseGameIDError); + } +} + +pub struct MapInfo<'a>{ + pub display_name:Result<&'a str,StringValueError>, + pub creator:Result<&'a str,StringValueError>, + pub game_id:Result<GameID,ParseGameIDError>, +} + +pub enum StringValueError{ + ObjectNotFound, + ValueNotSet, + NonStringValue, +} + +fn string_value(instance:Option<&rbx_dom_weak::Instance>)->Result<&str,StringValueError>{ + let instance=instance.ok_or(StringValueError::ObjectNotFound)?; + let value=instance.properties.get("Value").ok_or(StringValueError::ValueNotSet)?; + match value{ + rbx_dom_weak::types::Variant::String(value)=>Ok(value), + _=>Err(StringValueError::NonStringValue), + } +} + +#[derive(Debug)] +pub enum GetMapInfoError{ + ModelFileRootMustHaveOneChild, + ModelFileChildRefIsNil, +} + +pub fn get_mapinfo(dom:&rbx_dom_weak::WeakDom)->Result<MapInfo,GetMapInfoError>{ + let &[map_ref]=dom.root().children()else{ + return Err(GetMapInfoError::ModelFileRootMustHaveOneChild); + }; + let model_instance=dom.get_by_ref(map_ref).ok_or(GetMapInfoError::ModelFileChildRefIsNil)?; + + Ok(MapInfo{ + display_name:string_value(find_first_child_class(dom,model_instance,"DisplayName","StringValue")), + creator:string_value(find_first_child_class(dom,model_instance,"Creator","StringValue")), + game_id:model_instance.name.parse(), + }) +} diff --git a/validation/src/validator.rs b/validation/src/validator.rs index 5a0dc83..c114ca3 100644 --- a/validation/src/validator.rs +++ b/validation/src/validator.rs @@ -1,6 +1,7 @@ use futures::TryStreamExt; use submissions_api::types::ResourceType; +use crate::rbx_util::{class_is_a,read_dom,ReadDomError}; use crate::types::ResourceID; const SCRIPT_CONCURRENCY:usize=16; @@ -275,51 +276,6 @@ impl crate::message_handler::MessageHandler{ } } -#[allow(dead_code)] -#[derive(Debug)] -pub enum ReadDomError{ - Binary(rbx_binary::DecodeError), - Xml(rbx_xml::DecodeError), - Read(std::io::Error), - Seek(std::io::Error), - UnknownFormat([u8;8]), -} -impl std::fmt::Display for ReadDomError{ - fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ - write!(f,"{self:?}") - } -} -impl std::error::Error for ReadDomError{} - -pub fn read_dom<R:std::io::Read+std::io::Seek>(input:&mut R)->Result<rbx_dom_weak::WeakDom,ReadDomError>{ - let mut first_8=[0u8;8]; - std::io::Read::read_exact(input,&mut first_8).map_err(ReadDomError::Read)?; - std::io::Seek::rewind(input).map_err(ReadDomError::Seek)?; - match &first_8[0..4]{ - b"<rob"=>{ - match &first_8[4..8]{ - b"lox!"=>rbx_binary::from_reader(input).map_err(ReadDomError::Binary), - b"lox "=>rbx_xml::from_reader(input,rbx_xml::DecodeOptions::default()).map_err(ReadDomError::Xml), - _=>Err(ReadDomError::UnknownFormat(first_8)), - } - }, - _=>Err(ReadDomError::UnknownFormat(first_8)), - } -} - -pub 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){ for &referent in instance.children(){ if let Some(c)=dom.get_by_ref(referent){