diff --git a/src/roblox.rs b/src/roblox.rs index 582458d..e43a11e 100644 --- a/src/roblox.rs +++ b/src/roblox.rs @@ -34,7 +34,7 @@ pub struct DownloadAssetsSubcommand{ impl Commands{ pub async fn run(self)->AResult<()>{ match self{ - Commands::RobloxToSNF(subcommand)=>roblox_to_snf(subcommand.input_files,subcommand.output_folder), + Commands::RobloxToSNF(subcommand)=>roblox_to_snf(subcommand.input_files,subcommand.output_folder).await, Commands::DownloadAssets(subcommand)=>download_assets( subcommand.roblox_files, rbx_asset::cookie::Cookie::new("".to_string()), @@ -400,51 +400,49 @@ impl std::fmt::Display for ConvertError{ } } impl std::error::Error for ConvertError{} +async fn convert_to_snf(path:&Path,output_folder:PathBuf)->AResult<()>{ + let entire_file=tokio::fs::read(path).await?; -type MapThread=std::thread::JoinHandle<Result<(),ConvertError>>; + let model=strafesnet_rbx_loader::read( + std::io::Cursor::new(entire_file) + ).map_err(ConvertError::RobloxRead)?; -fn roblox_to_snf(pathlist:Vec<std::path::PathBuf>,output_folder:PathBuf)->AResult<()>{ - let n_paths=pathlist.len(); - let start = std::time::Instant::now(); - let mut threads:std::collections::VecDeque<MapThread>=std::collections::VecDeque::new(); - let mut i=0; - let mut join_thread=|thread:MapThread|{ - i+=1; - if let Err(e)=thread.join(){ - println!("thread error: {:?}",e); - }else{ - println!("{}/{}",i,n_paths); - } - }; - for path in pathlist{ - if 32<=threads.len(){ - join_thread(threads.pop_front().unwrap()); - } - let output_folder=output_folder.clone(); - threads.push_back(std::thread::spawn(move ||{ - let model=strafesnet_rbx_loader::read( - std::fs::File::open(path.as_path()) - .map_err(ConvertError::IO)? - ).map_err(ConvertError::RobloxRead)?; + let mut place=model.into_place(); + place.run_scripts(); - let mut place=model.into_place(); - place.run_scripts(); + let map=place.to_snf(LoadFailureMode::DefaultToNone).map_err(ConvertError::RobloxLoad)?; - let map=place.to_snf(LoadFailureMode::DefaultToNone).map_err(ConvertError::RobloxLoad)?; + let mut dest=output_folder; + dest.push(path.file_stem().unwrap()); + dest.set_extension("snfm"); + let file=std::fs::File::create(dest).map_err(ConvertError::IO)?; - let mut dest=output_folder.clone(); - dest.push(path.file_stem().unwrap()); - dest.set_extension("snfm"); - let file=std::fs::File::create(dest).map_err(ConvertError::IO)?; + strafesnet_snf::map::write_map(file,map).map_err(ConvertError::SNFMap)?; - strafesnet_snf::map::write_map(file,map).map_err(ConvertError::SNFMap)?; - Ok(()) - })); - } - - for thread in threads{ - join_thread(thread); - } - println!("{:?}", start.elapsed()); + Ok(()) +} + +async fn roblox_to_snf(paths:Vec<std::path::PathBuf>,output_folder:PathBuf)->AResult<()>{ + let start=std::time::Instant::now(); + + let thread_limit=std::thread::available_parallelism()?.get(); + let mut it=paths.into_iter(); + static SEM:tokio::sync::Semaphore=tokio::sync::Semaphore::const_new(0); + SEM.add_permits(thread_limit); + + while let (Ok(permit),Some(path))=(SEM.acquire().await,it.next()){ + let output_folder=output_folder.clone(); + tokio::spawn(async move{ + let result=convert_to_snf(path.as_path(),output_folder).await; + drop(permit); + match result{ + Ok(())=>(), + Err(e)=>println!("Convert error: {e:?}"), + } + }); + } + _=SEM.acquire_many(thread_limit as u32).await.unwrap(); + + println!("elapsed={:?}", start.elapsed()); Ok(()) }