From 3ef28748cb6c361cfaad2bc6639871cfe4107ff7 Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Fri, 17 Jan 2025 01:36:33 -0600 Subject: building classpath --- src/launcher.rs | 28 ++++++++++++++++++++++----- src/launcher/assets.rs | 7 ++++--- src/launcher/download.rs | 50 +++++++++++++++++++++--------------------------- 3 files changed, 49 insertions(+), 36 deletions(-) diff --git a/src/launcher.rs b/src/launcher.rs index 0b1ea90..8a68a65 100644 --- a/src/launcher.rs +++ b/src/launcher.rs @@ -18,12 +18,14 @@ use std::fs::FileType; use std::io::ErrorKind; use std::io::ErrorKind::AlreadyExists; use std::path::{Component, Path, PathBuf}; -use std::process; +use std::{env, process}; +use std::env::JoinPathsError; use std::time::{SystemTime, UNIX_EPOCH}; use chrono::NaiveDateTime; use const_format::formatcp; use futures::{stream, FutureExt, StreamExt, TryStreamExt}; use log::{debug, info, warn}; +use reqwest::Client; use serde::{Deserialize, Serialize}; use sha1_smol::Sha1; use sysinfo::System; @@ -164,6 +166,7 @@ pub enum LaunchError { LibraryVerifyError(FileVerifyError), LibraryDownloadError, LibraryExtractError(extract::ZipExtractError), + LibraryClasspathError(JoinPathsError), // ensure file errors MissingURL, @@ -191,6 +194,7 @@ impl Display for LaunchError { LaunchError::LibraryVerifyError(e) => write!(f, "failed to verify library: {}", e), LaunchError::LibraryDownloadError => f.write_str("library download failed (see above logs for details)"), // TODO: booo this sucks LaunchError::LibraryExtractError(e) => write!(f, "library extract zip error: {e}"), + LaunchError::LibraryClasspathError(e) => write!(f, "error building classpath: {e}"), LaunchError::MissingURL => f.write_str("cannot download required file, URL is missing"), LaunchError::IO { what, error } => write!(f, "i/o error ({}): {}", what, error), LaunchError::Offline => f.write_str("cannot download file in offline mode"), @@ -213,6 +217,7 @@ impl Error for LaunchError { LaunchError::LibraryDirError(_, e) => Some(e), LaunchError::LibraryVerifyError(e) => Some(e), LaunchError::LibraryExtractError(e) => Some(e), + LaunchError::LibraryClasspathError(e) => Some(e), LaunchError::IO { error: e, .. } => Some(e), LaunchError::Download { error: e, .. } => Some(e), LaunchError::Integrity(e) => Some(e), @@ -389,7 +394,7 @@ impl Launcher { /* tasks 2 l;aunch the gayme!!!! :3 * - java runtime * - normal process (good research, past figboot :3) - * - libraries + * - (done) libraries * - (done) check which libraries we actually need (some have classifiers that don't apply to us) * - (done) of the libraries we need, check which have correct size and sha1 * - (done) redownload necessary libraries @@ -452,15 +457,15 @@ impl Launcher { if self.online { info!("Downloading {} libraries...", downloads.len()); - let mut multi = MultiDownloader::new(downloads); - multi.perform().await + let client = Client::new(); + MultiDownloader::new(downloads.iter_mut()).perform(&client).await .inspect_err(|e| warn!("library download failed: {e}")) .try_fold((), |_, _| async {Ok(())}) .await .map_err(|_| LaunchError::LibraryDownloadError)?; } else { info!("Verifying {} libraries...", downloads.len()); - download::verify_files(downloads).await.map_err(|e| { + download::verify_files(downloads.iter_mut()).await.map_err(|e| { warn!("A library could not be verified: {}", e); warn!("Since the launcher is in offline mode, libraries cannot be downloaded. Please try again in online mode."); LaunchError::LibraryVerifyError(e) @@ -511,6 +516,19 @@ impl Launcher { info!("Extracting natives."); let natives_dir = self.libraries.extract_natives(extract_jobs).await?; + info!("Building classpath."); + let classpath = env::join_paths(downloads.iter() + .map(|job| job.get_path()) + .chain(client_jar_path.iter().map(|p| p.as_path()))) + .map_err(|e| LaunchError::LibraryClasspathError(e))? + .into_string() + .unwrap_or_else(|os| { + warn!("Classpath contains invalid UTF-8. The game may not launch correctly."); + os.to_string_lossy().to_string() + }); + + info!("Classpath: {classpath}"); + //todo!() Ok(()) } diff --git a/src/launcher/assets.rs b/src/launcher/assets.rs index e732877..e540e50 100644 --- a/src/launcher/assets.rs +++ b/src/launcher/assets.rs @@ -6,6 +6,7 @@ use std::path::{Path, PathBuf}; use std::path::Component::Normal; use futures::TryStreamExt; use log::{debug, info, warn}; +use reqwest::Client; use sha1_smol::Sha1; use tokio::{fs, io}; use crate::assets::{Asset, AssetIndex}; @@ -215,15 +216,15 @@ impl AssetRepository { if self.online { info!("Downloading {} asset objects...", downloads.len()); - let mut multi = MultiDownloader::new(downloads); - multi.perform().await + let client = Client::new(); + MultiDownloader::with_concurrent(downloads.iter_mut(), 16).perform(&client).await .inspect_err(|e| warn!("asset download failed: {e}")) .try_fold((), |_, _| async {Ok(())}) .await .map_err(|_| AssetError::AssetObjectDownload)?; } else { info!("Verifying {} asset objects...", downloads.len()); - super::download::verify_files(downloads).await.map_err(|e| AssetError::AssetVerifyError(e))?; + super::download::verify_files(downloads.iter_mut()).await.map_err(|e| AssetError::AssetVerifyError(e))?; } Ok(()) diff --git a/src/launcher/download.rs b/src/launcher/download.rs index d8d05f7..f5469d8 100644 --- a/src/launcher/download.rs +++ b/src/launcher/download.rs @@ -22,9 +22,8 @@ pub trait Download: Debug + Display { async fn finish(&mut self) -> Result<(), Box>; } -pub struct MultiDownloader { - jobs: Vec, - client: Client, +pub struct MultiDownloader<'j, T: Download + 'j, I: Iterator> { + jobs: I, nconcurrent: usize } @@ -86,25 +85,22 @@ impl<'j, T: Download> PhaseDownloadError<'j, T> { } } -impl MultiDownloader { - pub fn new(jobs: impl IntoIterator) -> MultiDownloader { +impl<'j, T: Download + 'j, I: Iterator> MultiDownloader<'j, T, I> { + pub fn new(jobs: I) -> MultiDownloader<'j, T, I> { Self::with_concurrent(jobs, 8) } - pub fn with_concurrent(jobs: impl IntoIterator, n: usize) -> MultiDownloader { + pub fn with_concurrent(jobs: I, n: usize) -> MultiDownloader<'j, T, I> { assert!(n > 0); MultiDownloader { - jobs: jobs.into_iter().collect(), - client: Client::new(), + jobs, nconcurrent: n } } - pub async fn perform(&mut self) -> impl TryStream> { - stream::iter(self.jobs.iter_mut()).map(|job| { - let client = &self.client; - + pub async fn perform(self, client: &'j Client) -> impl TryStream> { + stream::iter(self.jobs.into_iter()).map(move |job| Ok(async move { macro_rules! map_err { ($result:expr, $phase:expr, $job:expr) => { match $result { @@ -114,26 +110,24 @@ impl MultiDownloader { } } - async move { - let Some(rq) = map_err!( - job.prepare(client.request(Method::GET, job.get_url()) - .header(reqwest::header::USER_AGENT, USER_AGENT)).await, Phase::Prepare, job) else { - return Ok(()) - }; + let Some(rq) = map_err!( + job.prepare(client.request(Method::GET, job.get_url()) + .header(reqwest::header::USER_AGENT, USER_AGENT)).await, Phase::Prepare, job) else { + return Ok(()) + }; - let mut data = map_err!(map_err!(rq.send().await, Phase::Send, job).error_for_status(), Phase::Send, job).bytes_stream(); + let mut data = map_err!(map_err!(rq.send().await, Phase::Send, job).error_for_status(), Phase::Send, job).bytes_stream(); - while let Some(bytes) = data.next().await { - let bytes = map_err!(bytes, Phase::Receive, job); + while let Some(bytes) = data.next().await { + let bytes = map_err!(bytes, Phase::Receive, job); - map_err!(job.handle_chunk(bytes.as_ref()).await, Phase::HandleChunk, job); - } + map_err!(job.handle_chunk(bytes.as_ref()).await, Phase::HandleChunk, job); + } - job.finish().await.map_err(|e| PhaseDownloadError::new(Phase::Finish, e.into(), job))?; + job.finish().await.map_err(|e| PhaseDownloadError::new(Phase::Finish, e.into(), job))?; - Ok(()) - } - }).buffer_unordered(self.nconcurrent) + Ok(()) + })).try_buffer_unordered(self.nconcurrent) } } @@ -282,7 +276,7 @@ impl Download for VerifiedDownload { } } -pub async fn verify_files(files: Vec) -> Result<(), FileVerifyError> { +pub async fn verify_files(files: impl Iterator) -> Result<(), FileVerifyError> { stream::iter(files) .map(|dl| Ok(async move { debug!("Verifying library {}", dl.get_path().display()); -- cgit v1.2.3-70-g09d2