From dd193e6e8fb098ce84db43b068b5cfe52a48e08d Mon Sep 17 00:00:00 2001 From: Quaternions Date: Tue, 3 Dec 2024 18:54:01 -0800 Subject: [PATCH] wip: write some script validation code --- validation/src/nats_types.rs | 1 + validation/src/validator.rs | 116 ++++++++++++++++++++++++++++++++++- 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/validation/src/nats_types.rs b/validation/src/nats_types.rs index edf269f..e23698c 100644 --- a/validation/src/nats_types.rs +++ b/validation/src/nats_types.rs @@ -10,6 +10,7 @@ pub struct ValidateRequest{ pub submission_id:u64, pub model_id:u64, pub model_version:u64, + pub validated_model_id:Option, } // Create a new map diff --git a/validation/src/validator.rs b/validation/src/validator.rs index ebb250f..7095ac0 100644 --- a/validation/src/validator.rs +++ b/validation/src/validator.rs @@ -17,6 +17,10 @@ enum ValidateError{ Get(rbx_asset::cookie::GetError), Json(serde_json::Error), ReadDom(ReadDomError), + ApiGetReplacements(api::GetReplacementsError), + WriteDom(rbx_binary::EncodeError), + Upload(rbx_asset::cookie::UploadError), + Create(rbx_asset::cookie::CreateError), } impl std::fmt::Display for ValidateError{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ @@ -72,11 +76,87 @@ impl Validator{ // decode dom (slow!) let mut dom=read_dom(&mut std::io::Cursor::new(data)).map_err(ValidateError::ReadDom)?; - // validate map - // validate(dom) + /* VALIDATE MAP */ + + // collect unique scripts + let script_refs=get_script_refs(&dom); + let mut script_map=std::collections::HashMap::>::new(); + for &script_ref in &script_refs{ + if let Some(script)=dom.get_by_ref(script_ref){ + if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get("Source"){ + script_map.insert(source.clone(),None); + } + } + } + // ["local a=true","local b=true"] + let script_list:Vec<&str>=script_map.keys().map(|s|s.as_str()).collect(); + let j=serde_json::to_string(&script_list).map_err(ValidateError::Json)?; + + // send all scripts to REST endpoint and receive the replacements + let replacements=self.api.get_replacements(j).await.map_err(ValidateError::ApiGetReplacements)?; + + // assume the iteration order will not change if no keys are inserted or removed + for (map_value,replacement) in script_map.values_mut().zip(replacements){ + *map_value=replacement; + } + + // make the replacements + let mut any_replaced=false; + for &script_ref in &script_refs{ + if let Some(script)=dom.get_by_ref_mut(script_ref){ + if let Some(rbx_dom_weak::types::Variant::String(source))=script.properties.get_mut("Source"){ + if let Some(Some(replacement))=script_map.get(source.as_str()){ + any_replaced=true; + *source=replacement.clone(); + } + } + } + } // reply with validity - Ok(Valid::Untouched) + Ok(if any_replaced{ + // serialize model (slow!) + let mut data=Vec::new(); + rbx_binary::to_writer(&mut data,&dom,&[dom.root_ref()]).map_err(ValidateError::WriteDom)?; + + // upload a model lol + let (model_id,model_version)=if let Some(model_id)=validate_info.validated_model_id{ + // upload to existing id + let response=self.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{ + assetid:model_id, + name:None, + description:None, + ispublic:None, + allowComments:None, + groupId:None, + },data).await.map_err(ValidateError::Upload)?; + + // response.AssetVersionId is not the right one + let model_version=get_model_version_number(); + (response.AssetId,model_version) + }else{ + // create new model + let response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{ + name:dom.root().name, + description:"".to_owned(), + ispublic:true, + allowComments:true, + groupId:None, + },data).await.map_err(ValidateError::Create)?; + + // response.AssetVersionId is not the right one + let model_version=get_model_version_number(); + (response.AssetId,model_version) + }; + + // tell the submission validate request to change the model + Valid::Modified(ModelVersion{ + model_id, + model_version, + }) + }else{ + Valid::Untouched + }) } } @@ -111,3 +191,33 @@ fn read_dom(input:&mut R)->ResultErr(ReadDomError::UnknownFormat(first_8)), } } + +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,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){ + if class_is_a(c.class.as_str(),superclass){ + objects.push(c.referent());//copy ref + } + recursive_collect_superclass(objects,dom,c,superclass); + } + } +} + +fn get_script_refs(dom:&rbx_dom_weak::WeakDom)->Vec{ + let mut scripts=std::vec::Vec::new(); + recursive_collect_superclass(&mut scripts,dom,dom.root(),"LuaSourceContainer"); + scripts +}