forked from didirus/AstralRinth
Merge commit '7fa442fb28a2b9156690ff147206275163e7aec8' into beta
This commit is contained in:
@@ -14,8 +14,9 @@ use daedalus::{
|
||||
modded::SidedDataEntry,
|
||||
};
|
||||
use dunce::canonicalize;
|
||||
use hashlink::LinkedHashSet;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use itertools::Itertools;
|
||||
use std::io::{BufRead, BufReader, ErrorKind};
|
||||
use std::net::SocketAddr;
|
||||
use std::{collections::HashMap, path::Path};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -29,9 +30,21 @@ pub fn get_class_paths(
|
||||
java_arch: &str,
|
||||
minecraft_updated: bool,
|
||||
) -> crate::Result<String> {
|
||||
let mut cps = libraries
|
||||
launcher_class_path
|
||||
.iter()
|
||||
.filter_map(|library| {
|
||||
.map(|path| {
|
||||
Ok(canonicalize(path)
|
||||
.map_err(|_| {
|
||||
crate::ErrorKind::LauncherError(format!(
|
||||
"Specified class path {} does not exist",
|
||||
path.to_string_lossy()
|
||||
))
|
||||
.as_error()
|
||||
})?
|
||||
.to_string_lossy()
|
||||
.to_string())
|
||||
})
|
||||
.chain(libraries.iter().filter_map(|library| {
|
||||
if let Some(rules) = &library.rules
|
||||
&& !parse_rules(
|
||||
rules,
|
||||
@@ -47,29 +60,15 @@ 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))
|
||||
})
|
||||
.collect::<Result<LinkedHashSet<_>, _>>()?;
|
||||
|
||||
for launcher_path in launcher_class_path {
|
||||
cps.insert(
|
||||
canonicalize(launcher_path)
|
||||
.map_err(|_| {
|
||||
crate::ErrorKind::LauncherError(format!(
|
||||
"Specified class path {} does not exist",
|
||||
launcher_path.to_string_lossy()
|
||||
))
|
||||
.as_error()
|
||||
})?
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(cps
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>()
|
||||
.join(classpath_separator(java_arch)))
|
||||
}
|
||||
|
||||
pub fn get_class_paths_jar<T: AsRef<str>>(
|
||||
@@ -90,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)?);
|
||||
let path = libraries_path.join(get_path_from_artifact(lib)?);
|
||||
|
||||
if !path.exists() && allow_not_exist {
|
||||
return Ok(path.to_string_lossy().to_string());
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
@@ -124,6 +123,7 @@ pub fn get_jvm_arguments(
|
||||
quick_play_type: &QuickPlayType,
|
||||
quick_play_version: QuickPlayVersion,
|
||||
log_config: Option<&LoggingConfiguration>,
|
||||
ipc_addr: SocketAddr,
|
||||
) -> crate::Result<Vec<String>> {
|
||||
let mut parsed_arguments = Vec::new();
|
||||
|
||||
@@ -181,6 +181,11 @@ pub fn get_jvm_arguments(
|
||||
.to_string_lossy()
|
||||
));
|
||||
|
||||
parsed_arguments
|
||||
.push(format!("-Dmodrinth.internal.ipc.host={}", ipc_addr.ip()));
|
||||
parsed_arguments
|
||||
.push(format!("-Dmodrinth.internal.ipc.port={}", ipc_addr.port()));
|
||||
|
||||
parsed_arguments.push(format!(
|
||||
"-Dmodrinth.internal.quickPlay.serverVersion={}",
|
||||
serde_json::to_value(quick_play_version.server)?
|
||||
|
||||
@@ -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,
|
||||
@@ -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(());
|
||||
}
|
||||
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);
|
||||
if !library.downloadable {
|
||||
tracing::trace!(
|
||||
"Skipped non-downloadable library {}",
|
||||
&library.name
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 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
|
||||
),
|
||||
}
|
||||
} 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);
|
||||
|
||||
if path.exists() && !force {
|
||||
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);
|
||||
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?;
|
||||
|
||||
if path.exists() && !force {
|
||||
return Ok(());
|
||||
}
|
||||
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.
|
||||
|
||||
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 = format!(
|
||||
"{}{artifact_path}",
|
||||
library
|
||||
.url
|
||||
.as_deref()
|
||||
.unwrap_or("https://libraries.minecraft.net/")
|
||||
);
|
||||
|
||||
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?;
|
||||
|
||||
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,
|
||||
);
|
||||
write(&path, &bytes, &st.io_semaphore).await?;
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}?;
|
||||
|
||||
tracing::debug!("Loaded library {}", library.name);
|
||||
Ok(())
|
||||
tracing::trace!(
|
||||
"Fetched library {} to path {:?}",
|
||||
&library.name,
|
||||
&path
|
||||
);
|
||||
}
|
||||
}
|
||||
).await?;
|
||||
|
||||
tracing::debug!("Loaded library {}", library.name);
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
tracing::debug!("Done loading libraries!");
|
||||
Ok(())
|
||||
|
||||
@@ -12,6 +12,7 @@ use crate::state::{
|
||||
AccountType, Credentials, JavaVersion, ProcessMetadata, ProfileInstallStage,
|
||||
};
|
||||
use crate::util::{io, utils};
|
||||
use crate::util::rpc::RpcServerBuilder;
|
||||
use crate::{State, get_resource_file, process, state as st};
|
||||
use chrono::Utc;
|
||||
use daedalus as d;
|
||||
@@ -23,7 +24,6 @@ use serde::Deserialize;
|
||||
use st::Profile;
|
||||
use std::fmt::Write;
|
||||
use std::path::PathBuf;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command;
|
||||
|
||||
mod args;
|
||||
@@ -611,6 +611,8 @@ pub async fn launch_minecraft(
|
||||
let (main_class_keep_alive, main_class_path) =
|
||||
get_resource_file!(env "JAVA_JARS_DIR" / "theseus.jar")?;
|
||||
|
||||
let rpc_server = RpcServerBuilder::new().launch().await?;
|
||||
|
||||
command.args(
|
||||
args::get_jvm_arguments(
|
||||
args.get(&d::minecraft::ArgumentType::Jvm)
|
||||
@@ -636,6 +638,7 @@ pub async fn launch_minecraft(
|
||||
.logging
|
||||
.as_ref()
|
||||
.and_then(|x| x.get(&LoggingSide::Client)),
|
||||
rpc_server.address(),
|
||||
)?
|
||||
.into_iter(),
|
||||
);
|
||||
@@ -800,7 +803,8 @@ pub async fn launch_minecraft(
|
||||
state.directories.profile_logs_dir(&profile.path),
|
||||
version_info.logging.is_some(),
|
||||
main_class_keep_alive,
|
||||
async |process: &ProcessMetadata, stdin| {
|
||||
rpc_server,
|
||||
async |process: &ProcessMetadata, rpc_server| {
|
||||
let process_start_time = process.start_time.to_rfc3339();
|
||||
let profile_created_time = profile.created.to_rfc3339();
|
||||
let profile_modified_time = profile.modified.to_rfc3339();
|
||||
@@ -823,14 +827,11 @@ pub async fn launch_minecraft(
|
||||
let Some(value) = value else {
|
||||
continue;
|
||||
};
|
||||
stdin.write_all(b"property\t").await?;
|
||||
stdin.write_all(key.as_bytes()).await?;
|
||||
stdin.write_u8(b'\t').await?;
|
||||
stdin.write_all(value.as_bytes()).await?;
|
||||
stdin.write_u8(b'\n').await?;
|
||||
rpc_server
|
||||
.call_method_2::<()>("set_system_property", key, value)
|
||||
.await?;
|
||||
}
|
||||
stdin.write_all(b"launch\n").await?;
|
||||
stdin.flush().await?;
|
||||
rpc_server.call_method::<()>("launch").await?;
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user