use crate::error::InstallationResult;
use crate::minecraft::install::extract_native::extract_native;
use crate::minecraft::install::InstallationUpdate;
use crate::minecraft::models::Library;
use crate::minecraft::InstallOptions;
use crate::utils::{download, download_progress_channel, Download, DownloadProgress};
use futures::future::try_join_all;
use futures::join;
use std::path::Path;
use tokio::sync::mpsc::{Receiver, Sender};
pub async fn install_libraries(
options: &InstallOptions,
update_sender: Sender<InstallationUpdate>,
) -> InstallationResult<()> {
let needed_libraries = options.version_data.needed_libraries();
trace!("Building downloads for libraries");
let downloads = needed_libraries
.iter()
.map(|l| build_download(l, &options.libraries_path))
.collect::<Result<Vec<_>, _>>()?;
trace!("Preparing futures for downloading and channel translation");
let (tx, rx) = download_progress_channel(500);
let download_future = download(
downloads,
Some(tx),
options.parallel_downloads,
options.download_retries,
options.verify_downloads,
);
let map_future = map_progress(update_sender.clone(), rx);
trace!("Starting downloads");
join!(download_future, map_future).0?;
trace!("Preparing extraction of natives");
let extractions = needed_libraries
.iter()
.filter(|lib| lib.needs_extract())
.map(|lib| {
let excludes = lib
.extract
.as_ref()
.map(|e| e.exclude.clone())
.unwrap_or_default();
extract_native(
lib.jar_path(&options.libraries_path),
&options.natives_path,
excludes,
Some(update_sender.clone()),
)
});
trace!("Starting extraction");
try_join_all(extractions).await?;
Ok(())
}
async fn map_progress(
sender: Sender<InstallationUpdate>,
mut receiver: Receiver<DownloadProgress>,
) {
while let Some(p) = receiver.recv().await {
let path = p
.file
.file_name()
.map(|s| s.to_string_lossy().to_string())
.unwrap_or_default();
let send_result = sender
.send(InstallationUpdate::Library((
path,
LibraryInstallationUpdate::Downloading(p),
)))
.await;
if send_result.is_err() {
debug!("Failed to translate DownloadProgress to InstallationUpdate");
break;
}
}
}
fn build_download(
library: &Library,
libraries_path: impl AsRef<Path>,
) -> InstallationResult<Download> {
let (url, sha1, _size) = library.download_url();
let sha1 = sha1.as_deref().map(hex::decode).transpose()?;
Ok(Download {
url,
file: library.jar_path(libraries_path),
sha1,
})
}
#[derive(Clone, Debug)]
pub enum LibraryInstallationUpdate {
Downloading(DownloadProgress),
Extracting,
}