Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions packages/app-lib/src/launcher/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use daedalus::{
};
use dunce::canonicalize;
use itertools::Itertools;
use std::io::{BufRead, BufReader};
use std::io::{BufRead, BufReader, ErrorKind};
use std::net::SocketAddr;
use std::{collections::HashMap, path::Path};
use uuid::Uuid;
Expand Down Expand Up @@ -60,7 +60,11 @@ pub fn get_class_paths(
return None;
}

Some(get_lib_path(libraries_path, &library.name, false))
Some(get_lib_path(
libraries_path,
&library.name,
library.natives_os_key_and_classifiers(java_arch).is_some(),
))
}))
.process_results(|iter| {
iter.unique().join(classpath_separator(java_arch))
Expand All @@ -85,21 +89,21 @@ pub fn get_lib_path(
lib: &str,
allow_not_exist: bool,
) -> crate::Result<String> {
let path = libraries_path
.to_path_buf()
.join(get_path_from_artifact(lib)?);

if !path.exists() && allow_not_exist {
return Ok(path.to_string_lossy().to_string());
}
let path = libraries_path.join(get_path_from_artifact(lib)?);

let path = &canonicalize(&path).map_err(|_| {
crate::ErrorKind::LauncherError(format!(
"Library file at path {} does not exist",
path.to_string_lossy()
))
.as_error()
})?;
let path = match canonicalize(&path) {
Ok(p) => p,
Err(err) if err.kind() == ErrorKind::NotFound && allow_not_exist => {
path
}
Err(err) => {
return Err(crate::ErrorKind::LauncherError(format!(
"Could not canonicalize library path {}: {err}",
path.display()
))
.as_error());
}
};

Ok(path.to_string_lossy().to_string())
}
Expand Down
202 changes: 122 additions & 80 deletions packages/app-lib/src/launcher/download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ use crate::{
emit::{emit_loading, loading_try_for_each_concurrent},
},
state::State,
util::{fetch::*, io, platform::OsExt},
util::{fetch::*, io},
};
use daedalus::minecraft::{LoggingConfiguration, LoggingSide};
use daedalus::{
self as d,
minecraft::{
Asset, AssetsIndex, Library, Os, Version as GameVersion,
Asset, AssetsIndex, Library, Version as GameVersion,
VersionInfo as GameVersionInfo,
},
modded::LoaderVersion,
Expand Down Expand Up @@ -288,90 +288,132 @@ pub async fn download_libraries(
}?;
let num_files = libraries.len();
loading_try_for_each_concurrent(
stream::iter(libraries.iter())
.map(Ok::<&Library, crate::Error>), None, loading_bar,loading_amount,num_files, None,|library| async move {
if let Some(rules) = &library.rules
&& !parse_rules(rules, java_arch, &QuickPlayType::None, minecraft_updated) {
tracing::trace!("Skipped library {}", &library.name);
return Ok(());
}

if !library.downloadable {
tracing::trace!("Skipped non-downloadable library {}", &library.name);
return Ok(());
}

tokio::try_join! {
async {
let artifact_path = d::get_path_from_artifact(&library.name)?;
let path = st.directories.libraries_dir().join(&artifact_path);
stream::iter(libraries.iter()).map(Ok::<&Library, crate::Error>),
None,
loading_bar,
loading_amount,
num_files,
None,
|library| async move {
if let Some(rules) = &library.rules
&& !parse_rules(
rules,
java_arch,
&QuickPlayType::None,
minecraft_updated,
)
{
tracing::trace!("Skipped library {}", &library.name);
return Ok(());
}

if path.exists() && !force {
return Ok(());
}
if !library.downloadable {
tracing::trace!(
"Skipped non-downloadable library {}",
&library.name
);
return Ok(());
}

if let Some(d::minecraft::LibraryDownloads { artifact: Some(ref artifact), ..}) = library.downloads
&& !artifact.url.is_empty(){
let bytes = fetch(&artifact.url, Some(&artifact.sha1), &st.fetch_semaphore, &st.pool)
.await?;
write(&path, &bytes, &st.io_semaphore).await?;
tracing::trace!("Fetched library {} to path {:?}", &library.name, &path);
return Ok::<_, crate::Error>(());
}

let url = [
library
.url
.as_deref()
.unwrap_or("https://libraries.minecraft.net/"),
&artifact_path
].concat();

let bytes = fetch(&url, None, &st.fetch_semaphore, &st.pool).await?;
write(&path, &bytes, &st.io_semaphore).await?;
tracing::trace!("Fetched library {} to path {:?}", &library.name, &path);
Ok::<_, crate::Error>(())
},
async {
// HACK: pseudo try block using or else
if let Some((os_key, classifiers)) = None.or_else(|| Some((
library
.natives
.as_ref()?
.get(&Os::native_arch(java_arch))?,
library
.downloads
.as_ref()?
.classifiers
.as_ref()?
))) {
let parsed_key = os_key.replace(
"${arch}",
crate::util::platform::ARCH_WIDTH,
);

if let Some(native) = classifiers.get(&parsed_key) {
let data = fetch(&native.url, Some(&native.sha1), &st.fetch_semaphore, &st.pool).await?;
let reader = std::io::Cursor::new(&data);
if let Ok(mut archive) = zip::ZipArchive::new(reader) {
match archive.extract(st.directories.version_natives_dir(version)) {
Ok(_) => tracing::debug!("Fetched native {}", &library.name),
Err(err) => tracing::error!("Failed extracting native {}. err: {}", &library.name, err)
}
} else {
tracing::error!("Failed extracting native {}", &library.name)
}
}
// When a library has natives, we only need to download such natives, as PrismLauncher does
if let Some((os_key, classifiers)) =
library.natives_os_key_and_classifiers(java_arch)
{
let parsed_key = os_key
.replace("${arch}", crate::util::platform::ARCH_WIDTH);

if let Some(native) = classifiers.get(&parsed_key) {
let data = fetch(
&native.url,
Some(&native.sha1),
&st.fetch_semaphore,
&st.pool,
)
.await?;

if let Ok(mut archive) =
zip::ZipArchive::new(std::io::Cursor::new(&data))
{
match archive.extract(
st.directories.version_natives_dir(version),
) {
Ok(_) => tracing::debug!(
"Fetched native {}",
&library.name
),
Err(err) => tracing::error!(
"Failed extracting native {}. err: {err}",
&library.name
),
}

Ok(())
} else {
tracing::error!(
"Failed extracting native {}",
&library.name
);
}
}?;
}
} else {
let artifact_path = d::get_path_from_artifact(&library.name)?;
let path = st.directories.libraries_dir().join(&artifact_path);

tracing::debug!("Loaded library {}", library.name);
Ok(())
if path.exists() && !force {
return Ok(());
}

if let Some(d::minecraft::LibraryDownloads {
artifact: Some(ref artifact),
..
}) = library.downloads
&& !artifact.url.is_empty()
{
let bytes = fetch(
&artifact.url,
Some(&artifact.sha1),
&st.fetch_semaphore,
&st.pool,
)
.await?;
write(&path, &bytes, &st.io_semaphore).await?;

tracing::trace!(
"Fetched library {} to path {:?}",
&library.name,
&path
);
} else {
// We lack an artifact URL, so fall back to constructing one ourselves.
// PrismLauncher just ignores the library if this is the case, so it's
// probably not needed, but previous code revisions of the Modrinth App
// intended to do this, so we keep that behavior for compatibility.

let url = format!(
"{}{artifact_path}",
library
.url
.as_deref()
.unwrap_or("https://libraries.minecraft.net/")
);

let bytes =
fetch(&url, None, &st.fetch_semaphore, &st.pool)
.await?;

write(&path, &bytes, &st.io_semaphore).await?;

tracing::trace!(
"Fetched library {} to path {:?}",
&library.name,
&path
);
}
}
).await?;

tracing::debug!("Loaded library {}", library.name);
Ok(())
},
)
.await?;

tracing::debug!("Done loading libraries!");
Ok(())
Expand Down
59 changes: 0 additions & 59 deletions packages/app-lib/src/util/platform.rs
Original file line number Diff line number Diff line change
@@ -1,65 +1,6 @@
//! Platform-related code
use daedalus::minecraft::{Os, OsRule};

// OS detection
pub trait OsExt {
/// Get the OS of the current system
fn native() -> Self;

/// Gets the OS + Arch of the current system
fn native_arch(java_arch: &str) -> Self;

/// Gets the OS from an OS + Arch
fn get_os(&self) -> Self;
}

impl OsExt for Os {
fn native() -> Self {
match std::env::consts::OS {
"windows" => Self::Windows,
"macos" => Self::Osx,
"linux" => Self::Linux,
_ => Self::Unknown,
}
}

fn native_arch(java_arch: &str) -> Self {
if std::env::consts::OS == "windows" {
if java_arch == "aarch64" {
Os::WindowsArm64
} else {
Os::Windows
}
} else if std::env::consts::OS == "linux" {
if java_arch == "aarch64" {
Os::LinuxArm64
} else if java_arch == "arm" {
Os::LinuxArm32
} else {
Os::Linux
}
} else if std::env::consts::OS == "macos" {
if java_arch == "aarch64" {
Os::OsxArm64
} else {
Os::Osx
}
} else {
Os::Unknown
}
}

fn get_os(&self) -> Self {
match self {
Os::OsxArm64 => Os::Osx,
Os::LinuxArm32 => Os::Linux,
Os::LinuxArm64 => Os::Linux,
Os::WindowsArm64 => Os::Windows,
_ => self.clone(),
}
}
}

// Bit width
#[cfg(target_pointer_width = "64")]
pub const ARCH_WIDTH: &str = "64";
Expand Down
Loading