1
0

Various final backend fixes (#117)

* Various final backend fixes

* Add FS watching

* run lint

* Autodetect installed jars
This commit is contained in:
Geometrically
2023-05-16 15:30:04 -07:00
committed by GitHub
parent 5cb54b44be
commit 3fa0e99de2
26 changed files with 941 additions and 529 deletions

View File

@@ -22,12 +22,13 @@ pub fn get_class_paths(
libraries_path: &Path,
libraries: &[Library],
client_path: &Path,
java_arch: &str,
) -> crate::Result<String> {
let mut cps = libraries
.iter()
.filter_map(|library| {
if let Some(rules) = &library.rules {
if !rules.iter().all(parse_rule) {
if !rules.iter().any(|x| parse_rule(x, java_arch)) {
return None;
}
}
@@ -53,19 +54,20 @@ pub fn get_class_paths(
.to_string(),
);
Ok(cps.join(classpath_separator()))
Ok(cps.join(classpath_separator(java_arch)))
}
pub fn get_class_paths_jar<T: AsRef<str>>(
libraries_path: &Path,
libraries: &[T],
java_arch: &str,
) -> crate::Result<String> {
let cps = libraries
.iter()
.map(|library| get_lib_path(libraries_path, library.as_ref(), false))
.collect::<Result<Vec<_>, _>>()?;
Ok(cps.join(classpath_separator()))
Ok(cps.join(classpath_separator(java_arch)))
}
pub fn get_lib_path(
@@ -91,6 +93,8 @@ pub fn get_lib_path(
Ok(path.to_string_lossy().to_string())
}
#[allow(clippy::too_many_arguments)]
pub fn get_jvm_arguments(
arguments: Option<&[Argument]>,
natives_path: &Path,
@@ -99,19 +103,26 @@ pub fn get_jvm_arguments(
version_name: &str,
memory: MemorySettings,
custom_args: Vec<String>,
java_arch: &str,
) -> crate::Result<Vec<String>> {
let mut parsed_arguments = Vec::new();
if let Some(args) = arguments {
parse_arguments(args, &mut parsed_arguments, |arg| {
parse_jvm_argument(
arg.to_string(),
natives_path,
libraries_path,
class_paths,
version_name,
)
})?;
parse_arguments(
args,
&mut parsed_arguments,
|arg| {
parse_jvm_argument(
arg.to_string(),
natives_path,
libraries_path,
class_paths,
version_name,
java_arch,
)
},
java_arch,
)?;
} else {
parsed_arguments.push(format!(
"-Djava.library.path={}",
@@ -147,6 +158,7 @@ fn parse_jvm_argument(
libraries_path: &Path,
class_paths: &str,
version_name: &str,
java_arch: &str,
) -> crate::Result<String> {
argument.retain(|c| !c.is_whitespace());
Ok(argument
@@ -174,7 +186,7 @@ fn parse_jvm_argument(
})?
.to_string_lossy(),
)
.replace("${classpath_separator}", classpath_separator())
.replace("${classpath_separator}", classpath_separator(java_arch))
.replace("${launcher_name}", "theseus")
.replace("${launcher_version}", env!("CARGO_PKG_VERSION"))
.replace("${version_name}", version_name)
@@ -192,13 +204,37 @@ pub fn get_minecraft_arguments(
assets_directory: &Path,
version_type: &VersionType,
resolution: WindowSize,
java_arch: &str,
) -> crate::Result<Vec<String>> {
if let Some(arguments) = arguments {
let mut parsed_arguments = Vec::new();
parse_arguments(arguments, &mut parsed_arguments, |arg| {
parse_minecraft_argument(
arg,
parse_arguments(
arguments,
&mut parsed_arguments,
|arg| {
parse_minecraft_argument(
arg,
&credentials.access_token,
&credentials.username,
&credentials.id,
version,
asset_index_name,
game_directory,
assets_directory,
version_type,
resolution,
)
},
java_arch,
)?;
Ok(parsed_arguments)
} else if let Some(legacy_arguments) = legacy_arguments {
let mut parsed_arguments = Vec::new();
for x in legacy_arguments.split(' ') {
parsed_arguments.push(parse_minecraft_argument(
&x.replace(' ', TEMPORARY_REPLACE_CHAR),
&credentials.access_token,
&credentials.username,
&credentials.id,
@@ -208,26 +244,9 @@ pub fn get_minecraft_arguments(
assets_directory,
version_type,
resolution,
)
})?;
)?);
}
Ok(parsed_arguments)
} else if let Some(legacy_arguments) = legacy_arguments {
Ok(parse_minecraft_argument(
&legacy_arguments.replace(' ', TEMPORARY_REPLACE_CHAR),
&credentials.access_token,
&credentials.username,
&credentials.id,
version,
asset_index_name,
game_directory,
assets_directory,
version_type,
resolution,
)?
.split(' ')
.map(|x| x.to_string())
.collect())
} else {
Ok(Vec::new())
}
@@ -300,6 +319,7 @@ fn parse_arguments<F>(
arguments: &[Argument],
parsed_arguments: &mut Vec<String>,
parse_function: F,
java_arch: &str,
) -> crate::Result<()>
where
F: Fn(&str) -> crate::Result<String>,
@@ -314,7 +334,7 @@ where
}
}
Argument::Ruled { rules, value } => {
if rules.iter().all(parse_rule) {
if rules.iter().any(|x| parse_rule(x, java_arch)) {
match value {
ArgumentValue::Single(arg) => {
parsed_arguments.push(parse_function(

View File

@@ -24,6 +24,7 @@ pub async fn download_minecraft(
st: &State,
version: &GameVersionInfo,
loading_bar: &LoadingBarId,
java_arch: &str,
) -> crate::Result<()> {
tracing::info!("Downloading Minecraft version {}", version.id);
// 5
@@ -45,7 +46,7 @@ pub async fn download_minecraft(
// Total loading sums to 90/60
download_client(st, version, Some(loading_bar)), // 10
download_assets(st, version.assets == "legacy", &assets_index, Some(loading_bar), amount), // 40
download_libraries(st, version.libraries.as_slice(), &version.id, Some(loading_bar), amount) // 40
download_libraries(st, version.libraries.as_slice(), &version.id, Some(loading_bar), amount, java_arch) // 40
}?;
tracing::info!("Done downloading Minecraft!");
@@ -247,6 +248,7 @@ pub async fn download_libraries(
version: &str,
loading_bar: Option<&LoadingBarId>,
loading_amount: f64,
java_arch: &str,
) -> crate::Result<()> {
Box::pin(async move {
tracing::debug!("Loading libraries");
@@ -260,10 +262,12 @@ pub async fn download_libraries(
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 {
if !rules.iter().all(super::parse_rule) {
if !rules.iter().any(|x| super::parse_rule(x, java_arch)) {
tracing::trace!("Skipped library {}", &library.name);
return Ok(());
}
}
tokio::try_join! {
async {
let artifact_path = d::get_path_from_artifact(&library.name)?;
@@ -278,24 +282,23 @@ pub async fn download_libraries(
let bytes = fetch(&artifact.url, Some(&artifact.sha1), &st.fetch_semaphore)
.await?;
write(&path, &bytes, &st.io_semaphore).await?;
tracing::trace!("Fetched library {}", &library.name);
tracing::trace!("Fetched library {} to path {:?}", &library.name, &path);
Ok::<_, crate::Error>(())
}
None => {
_ => {
let url = [
library
.url
.as_deref()
.unwrap_or("https://libraries.minecraft.net"),
.unwrap_or("https://libraries.minecraft.net/"),
&artifact_path
].concat();
let bytes = fetch(&url, None, &st.fetch_semaphore).await?;
write(&path, &bytes, &st.io_semaphore).await?;
tracing::trace!("Fetched library {}", &library.name);
tracing::trace!("Fetched library {} to path {:?}", &library.name, &path);
Ok::<_, crate::Error>(())
}
_ => Ok(())
}
},
async {
@@ -304,7 +307,7 @@ pub async fn download_libraries(
library
.natives
.as_ref()?
.get(&Os::native_arch())?,
.get(&Os::native_arch(java_arch))?,
library
.downloads
.as_ref()?

View File

@@ -1,6 +1,8 @@
//! Logic for launching Minecraft
use crate::event::emit::{emit_loading, init_or_edit_loading};
use crate::event::{LoadingBarId, LoadingBarType};
use crate::jre::{JAVA_17_KEY, JAVA_18PLUS_KEY, JAVA_8_KEY};
use crate::prelude::JavaVersion;
use crate::state::ProfileInstallStage;
use crate::{
process,
@@ -8,10 +10,11 @@ use crate::{
State,
};
use daedalus as d;
use daedalus::minecraft::VersionInfo;
use dunce::canonicalize;
use st::Profile;
use std::fs;
use std::{path::Path, process::Stdio, sync::Arc};
use std::{process::Stdio, sync::Arc};
use tokio::process::Command;
use uuid::Uuid;
@@ -21,18 +24,18 @@ pub mod auth;
pub mod download;
#[tracing::instrument]
pub fn parse_rule(rule: &d::minecraft::Rule) -> bool {
pub fn parse_rule(rule: &d::minecraft::Rule, java_version: &str) -> bool {
use d::minecraft::{Rule, RuleAction};
let res = match rule {
Rule {
os: Some(ref os), ..
} => crate::util::platform::os_rule(os),
} => crate::util::platform::os_rule(os, java_version),
Rule {
features: Some(ref features),
..
} => features.has_demo_resolution.unwrap_or(false),
_ => true,
_ => false,
};
match rule.action {
@@ -54,6 +57,40 @@ macro_rules! processor_rules {
}
}
async fn get_java_version_from_profile(
profile: &Profile,
version_info: &VersionInfo,
) -> crate::Result<JavaVersion> {
if let Some(java) = profile.java.clone().and_then(|x| x.override_version) {
Ok(java)
} else {
let optimal_keys = match version_info
.java_version
.as_ref()
.map(|it| it.major_version)
.unwrap_or(8)
{
0..=15 => vec![JAVA_8_KEY, JAVA_17_KEY, JAVA_18PLUS_KEY],
16..=17 => vec![JAVA_17_KEY, JAVA_18PLUS_KEY],
_ => vec![JAVA_18PLUS_KEY],
};
let state = State::get().await?;
let settings = state.settings.read().await;
for key in optimal_keys {
if let Some(java) = settings.java_globals.get(&key.to_string()) {
return Ok(java.clone());
}
}
Err(crate::ErrorKind::LauncherError(
"No available java installation".to_string(),
)
.into())
}
}
#[tracing::instrument(skip(profile))]
pub async fn install_minecraft(
profile: &Profile,
@@ -112,8 +149,10 @@ pub async fn install_minecraft(
)
.await?;
let java_version = get_java_version_from_profile(profile, &version_info).await?;
// Download minecraft (5-90)
download::download_minecraft(&state, &version_info, &loading_bar).await?;
download::download_minecraft(&state, &version_info, &loading_bar, &java_version.architecture).await?;
if let Some(processors) = &version_info.processors {
let client_path = state
@@ -157,11 +196,12 @@ pub async fn install_minecraft(
cp.push(processor.jar.clone())
});
let child = Command::new("java")
let child = Command::new(&java_version.path)
.arg("-cp")
.arg(args::get_class_paths_jar(
&state.directories.libraries_dir(),
&cp,
&java_version.architecture
)?)
.arg(
args::get_processor_main_class(args::get_lib_path(
@@ -231,7 +271,6 @@ pub async fn install_minecraft(
#[allow(clippy::too_many_arguments)]
pub async fn launch_minecraft(
java_install: &Path,
java_args: &[String],
env_args: &[(String, String)],
wrapper: &Option<String>,
@@ -286,6 +325,8 @@ pub async fn launch_minecraft(
)
.await?;
let java_version = get_java_version_from_profile(profile, &version_info).await?;
let client_path = state
.directories
.version_dir(&version_jar)
@@ -294,9 +335,9 @@ pub async fn launch_minecraft(
let args = version_info.arguments.clone().unwrap_or_default();
let mut command = match wrapper {
Some(hook) => {
wrap_ref_builder!(it = Command::new(hook) => {it.arg(java_install)})
wrap_ref_builder!(it = Command::new(hook) => {it.arg(&java_version.path)})
}
None => Command::new(String::from(java_install.to_string_lossy())),
None => Command::new(&java_version.path),
};
let env_args = Vec::from(env_args);
@@ -324,10 +365,12 @@ pub async fn launch_minecraft(
&state.directories.libraries_dir(),
version_info.libraries.as_slice(),
&client_path,
&java_version.architecture
)?,
&version_jar,
*memory,
Vec::from(java_args),
&java_version.architecture
)?
.into_iter()
.collect::<Vec<_>>(),
@@ -345,6 +388,7 @@ pub async fn launch_minecraft(
&state.directories.assets_dir(),
&version.type_,
*resolution,
&java_version.architecture
)?
.into_iter()
.collect::<Vec<_>>(),
@@ -362,7 +406,7 @@ pub async fn launch_minecraft(
// Get Modrinth logs directories
let datetime_string =
chrono::Local::now().format("%Y%m%y_%H%M%S").to_string();
chrono::Local::now().format("%Y%m%d_%H%M%S").to_string();
let logs_dir = {
let st = State::get().await?;
st.directories