cobble_core/minecraft/install/
libraries.rs1use crate::error::InstallationResult;
2use crate::minecraft::install::extract_native::extract_native;
3use crate::minecraft::install::InstallationUpdate;
4use crate::minecraft::models::Library;
5use crate::minecraft::InstallOptions;
6use crate::utils::{download, download_progress_channel, Download, DownloadProgress};
7use futures::future::try_join_all;
8use futures::join;
9use std::path::Path;
10use tokio::sync::mpsc::{Receiver, Sender};
11
12pub async fn install_libraries(
30 options: &InstallOptions,
31 update_sender: Sender<InstallationUpdate>,
32) -> InstallationResult<()> {
33 let needed_libraries = options.version_data.needed_libraries();
34
35 trace!("Building downloads for libraries");
36 let downloads = needed_libraries
37 .iter()
38 .map(|l| build_download(l, &options.libraries_path))
39 .collect::<Result<Vec<_>, _>>()?;
40
41 trace!("Preparing futures for downloading and channel translation");
42 let (tx, rx) = download_progress_channel(500);
43 let download_future = download(
44 downloads,
45 Some(tx),
46 options.parallel_downloads,
47 options.download_retries,
48 options.verify_downloads,
49 );
50 let map_future = map_progress(update_sender.clone(), rx);
51
52 trace!("Starting downloads");
53 join!(download_future, map_future).0?;
54
55 trace!("Preparing extraction of natives");
56 let extractions = needed_libraries
57 .iter()
58 .filter(|lib| lib.needs_extract())
59 .map(|lib| {
60 let excludes = lib
61 .extract
62 .as_ref()
63 .map(|e| e.exclude.clone())
64 .unwrap_or_default();
65 extract_native(
66 lib.jar_path(&options.libraries_path),
67 &options.natives_path,
68 excludes,
69 Some(update_sender.clone()),
70 )
71 });
72
73 trace!("Starting extraction");
74 try_join_all(extractions).await?;
75
76 Ok(())
77}
78
79async fn map_progress(
80 sender: Sender<InstallationUpdate>,
81 mut receiver: Receiver<DownloadProgress>,
82) {
83 while let Some(p) = receiver.recv().await {
84 let path = p
85 .file
86 .file_name()
87 .map(|s| s.to_string_lossy().to_string())
88 .unwrap_or_default();
89
90 let send_result = sender
91 .send(InstallationUpdate::Library((
92 path,
93 LibraryInstallationUpdate::Downloading(p),
94 )))
95 .await;
96
97 if send_result.is_err() {
98 debug!("Failed to translate DownloadProgress to InstallationUpdate");
99 break;
100 }
101 }
102}
103
104fn build_download(
105 library: &Library,
106 libraries_path: impl AsRef<Path>,
107) -> InstallationResult<Download> {
108 let (url, sha1, _size) = library.download_url();
109
110 let sha1 = sha1.as_deref().map(hex::decode).transpose()?;
111
112 Ok(Download {
113 url,
114 file: library.jar_path(libraries_path),
115 sha1,
116 })
117}
118
119#[derive(Clone, Debug)]
121pub enum LibraryInstallationUpdate {
122 Downloading(DownloadProgress),
124 Extracting,
126}