Profile bindings (#55)

* basic framework. still has errors

* added functionality for main endpoints + some structuring

* formatting

* unused code

* mimicked CLI function with wait_for process

* made PR changes, added playground

* cargo fmt

* removed missed println

* misc tests fixes

* cargo fmt

* added windows support

* cargo fmt

* all OS use dunce

* restructured profile slightly; fixed mac bug

* profile changes, new main.rs

* fixed requested pr + canonicaliation bug

* fixed regressed bug in ui

* fixed regressed bugs

* fixed git error

* typo

* ran prettier

* clippy

* playground clippy

* ported profile loading fix

* profile change for real, url println and clippy

* PR changes

---------

Co-authored-by: Wyatt <wyatt@modrinth.com>
This commit is contained in:
Wyatt Verchere
2023-03-31 11:00:43 -07:00
committed by GitHub
parent 24ba986cf1
commit f48959a816
30 changed files with 857 additions and 80 deletions

View File

@@ -10,10 +10,14 @@ use daedalus::{
minecraft::{Argument, ArgumentValue, Library, VersionType},
modded::SidedDataEntry,
};
use dunce::canonicalize;
use std::io::{BufRead, BufReader};
use std::{collections::HashMap, path::Path};
use uuid::Uuid;
// Replaces the space separator with a newline character, as to not split the arguments
const TEMPORARY_REPLACE_CHAR: &str = "\n";
pub fn get_class_paths(
libraries_path: &Path,
libraries: &[Library],
@@ -37,8 +41,7 @@ pub fn get_class_paths(
.collect::<Result<Vec<_>, _>>()?;
cps.push(
client_path
.canonicalize()
canonicalize(client_path)
.map_err(|_| {
crate::ErrorKind::LauncherError(format!(
"Specified class path {} does not exist",
@@ -70,7 +73,7 @@ pub fn get_lib_path(libraries_path: &Path, lib: &str) -> crate::Result<String> {
path.push(get_path_from_artifact(lib)?);
let path = &path.canonicalize().map_err(|_| {
let path = &canonicalize(&path).map_err(|_| {
crate::ErrorKind::LauncherError(format!(
"Library file at path {} does not exist",
path.to_string_lossy()
@@ -104,15 +107,13 @@ pub fn get_jvm_arguments(
} else {
parsed_arguments.push(format!(
"-Djava.library.path={}",
&natives_path
.canonicalize()
canonicalize(natives_path)
.map_err(|_| crate::ErrorKind::LauncherError(format!(
"Specified natives path {} does not exist",
natives_path.to_string_lossy()
))
.as_error())?
.to_string_lossy()
.to_string()
));
parsed_arguments.push("-cp".to_string());
parsed_arguments.push(class_paths.to_string());
@@ -142,8 +143,7 @@ fn parse_jvm_argument(
Ok(argument
.replace(
"${natives_directory}",
&natives_path
.canonicalize()
&canonicalize(natives_path)
.map_err(|_| {
crate::ErrorKind::LauncherError(format!(
"Specified natives path {} does not exist",
@@ -155,8 +155,7 @@ fn parse_jvm_argument(
)
.replace(
"${library_directory}",
&libraries_path
.canonicalize()
&canonicalize(libraries_path)
.map_err(|_| {
crate::ErrorKind::LauncherError(format!(
"Specified libraries path {} does not exist",
@@ -206,7 +205,7 @@ pub fn get_minecraft_arguments(
Ok(parsed_arguments)
} else if let Some(legacy_arguments) = legacy_arguments {
Ok(parse_minecraft_argument(
legacy_arguments,
&legacy_arguments.replace(' ', TEMPORARY_REPLACE_CHAR),
&credentials.access_token,
&credentials.username,
&credentials.id,
@@ -249,8 +248,7 @@ fn parse_minecraft_argument(
.replace("${assets_index_name}", asset_index_name)
.replace(
"${game_directory}",
&game_directory
.canonicalize()
&canonicalize(game_directory)
.map_err(|_| {
crate::ErrorKind::LauncherError(format!(
"Specified game directory {} does not exist",
@@ -262,8 +260,7 @@ fn parse_minecraft_argument(
)
.replace(
"${assets_root}",
&assets_directory
.canonicalize()
&canonicalize(assets_directory)
.map_err(|_| {
crate::ErrorKind::LauncherError(format!(
"Specified assets directory {} does not exist",
@@ -275,8 +272,7 @@ fn parse_minecraft_argument(
)
.replace(
"${game_assets}",
&assets_directory
.canonicalize()
&canonicalize(assets_directory)
.map_err(|_| {
crate::ErrorKind::LauncherError(format!(
"Specified assets directory {} does not exist",
@@ -302,9 +298,9 @@ where
for argument in arguments {
match argument {
Argument::Normal(arg) => {
let parsed = parse_function(arg)?;
for arg in parsed.split(' ') {
let parsed =
parse_function(&arg.replace(' ', TEMPORARY_REPLACE_CHAR))?;
for arg in parsed.split(TEMPORARY_REPLACE_CHAR) {
parsed_arguments.push(arg.to_string());
}
}
@@ -312,11 +308,15 @@ where
if rules.iter().all(parse_rule) {
match value {
ArgumentValue::Single(arg) => {
parsed_arguments.push(parse_function(arg)?);
parsed_arguments.push(parse_function(
&arg.replace(' ', TEMPORARY_REPLACE_CHAR),
)?);
}
ArgumentValue::Many(args) => {
for arg in args {
parsed_arguments.push(parse_function(arg)?);
parsed_arguments.push(parse_function(
&arg.replace(' ', TEMPORARY_REPLACE_CHAR),
)?);
}
}
}

View File

@@ -4,7 +4,7 @@ use bincode::{Decode, Encode};
use chrono::{prelude::*, Duration};
use futures::prelude::*;
use lazy_static::lazy_static;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use url::Url;
lazy_static! {
@@ -47,7 +47,7 @@ struct ProfileInfoJSON {
}
// Login information
#[derive(Encode, Decode)]
#[derive(Encode, Decode, Serialize, Deserialize)]
pub struct Credentials {
#[bincode(with_serde)]
pub id: uuid::Uuid,

View File

@@ -139,10 +139,9 @@ pub async fn download_assets(
index: &AssetsIndex,
) -> crate::Result<()> {
log::debug!("Loading assets");
stream::iter(index.objects.iter())
.map(Ok::<(&String, &Asset), crate::Error>)
.try_for_each_concurrent(None, |(name, asset)| async move {
.try_for_each_concurrent(Some(st.settings.read().await.max_concurrent_downloads), |(name, asset)| async move {
let hash = &asset.hash;
let resource_path = st.directories.object_dir(hash);
let url = format!(
@@ -202,7 +201,7 @@ pub async fn download_libraries(
stream::iter(libraries.iter())
.map(Ok::<&Library, crate::Error>)
.try_for_each_concurrent(None, |library| async move {
.try_for_each_concurrent(Some(st.settings.read().await.max_concurrent_downloads), |library| async move {
if let Some(rules) = &library.rules {
if !rules.iter().all(super::parse_rule) {
return Ok(());

View File

@@ -1,6 +1,7 @@
//! Logic for launching Minecraft
use crate::state as st;
use daedalus as d;
use dunce::canonicalize;
use std::{path::Path, process::Stdio};
use tokio::process::{Child, Command};
@@ -58,7 +59,7 @@ pub async fn launch_minecraft(
credentials: &auth::Credentials,
) -> crate::Result<Child> {
let state = st::State::get().await?;
let instance_path = instance_path.canonicalize()?;
let instance_path = &canonicalize(instance_path)?;
let version = state
.metadata
@@ -173,34 +174,45 @@ pub async fn launch_minecraft(
};
command
.args(args::get_jvm_arguments(
args.get(&d::minecraft::ArgumentType::Jvm)
.map(|x| x.as_slice()),
&state.directories.version_natives_dir(&version.id),
&state.directories.libraries_dir(),
&args::get_class_paths(
.args(
args::get_jvm_arguments(
args.get(&d::minecraft::ArgumentType::Jvm)
.map(|x| x.as_slice()),
&state.directories.version_natives_dir(&version.id),
&state.directories.libraries_dir(),
version_info.libraries.as_slice(),
&client_path,
)?,
&version_jar,
*memory,
Vec::from(java_args),
)?)
&args::get_class_paths(
&state.directories.libraries_dir(),
version_info.libraries.as_slice(),
&client_path,
)?,
&version_jar,
*memory,
Vec::from(java_args),
)?
.into_iter()
.map(|r| r.replace(' ', r"\ "))
.collect::<Vec<_>>(),
)
.arg(version_info.main_class.clone())
.args(args::get_minecraft_arguments(
args.get(&d::minecraft::ArgumentType::Game)
.map(|x| x.as_slice()),
version_info.minecraft_arguments.as_deref(),
credentials,
&version.id,
&version_info.asset_index.id,
&instance_path,
&state.directories.assets_dir(),
&version.type_,
*resolution,
)?)
.args(
args::get_minecraft_arguments(
args.get(&d::minecraft::ArgumentType::Game)
.map(|x| x.as_slice()),
version_info.minecraft_arguments.as_deref(),
credentials,
&version.id,
&version_info.asset_index.id,
instance_path,
&state.directories.assets_dir(),
&version.type_,
*resolution,
)?
.into_iter()
.map(|r| r.replace(' ', r"\ "))
.collect::<Vec<_>>(),
)
.current_dir(instance_path.clone())
.env_clear()
.stdout(Stdio::inherit())
.stderr(Stdio::inherit());