Modpack support (#60)

* Modpack support

* Finish feature

* Tauri errors fix (#61)

* async impl

* working

* fmt and redundancy

* moved ? to if let Ok block

* Finish modpacks support

* remove generated file

* fix compile err

* fix lint

* Fix code review comments + forge support

---------

Co-authored-by: Wyatt Verchere <wverchere@gmail.com>
This commit is contained in:
Geometrically
2023-04-05 19:04:09 -07:00
committed by GitHub
parent 6965487b56
commit b9a3a6dc11
28 changed files with 1006 additions and 534 deletions

View File

@@ -36,7 +36,7 @@ pub fn get_class_paths(
return None;
}
Some(get_lib_path(libraries_path, &library.name))
Some(get_lib_path(libraries_path, &library.name, false))
})
.collect::<Result<Vec<_>, _>>()?;
@@ -62,17 +62,25 @@ pub fn get_class_paths_jar<T: AsRef<str>>(
) -> crate::Result<String> {
let cps = libraries
.iter()
.map(|library| get_lib_path(libraries_path, library.as_ref()))
.map(|library| get_lib_path(libraries_path, library.as_ref(), false))
.collect::<Result<Vec<_>, _>>()?;
Ok(cps.join(classpath_separator()))
}
pub fn get_lib_path(libraries_path: &Path, lib: &str) -> crate::Result<String> {
pub fn get_lib_path(
libraries_path: &Path,
lib: &str,
allow_not_exist: bool,
) -> crate::Result<String> {
let mut path = libraries_path.to_path_buf();
path.push(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",
@@ -343,13 +351,14 @@ pub fn get_processor_arguments<T: AsRef<str>>(
get_lib_path(
libraries_path,
&entry.client[1..entry.client.len() - 1],
true,
)?
} else {
entry.client.clone()
})
}
} else if argument.as_ref().starts_with('[') {
new_arguments.push(get_lib_path(libraries_path, trimmed_arg)?)
new_arguments.push(get_lib_path(libraries_path, trimmed_arg, true)?)
} else {
new_arguments.push(argument.as_ref().to_string())
}
@@ -361,7 +370,7 @@ pub fn get_processor_arguments<T: AsRef<str>>(
pub async fn get_processor_main_class(
path: String,
) -> crate::Result<Option<String>> {
tokio::task::spawn_blocking(move || {
let main_class = tokio::task::spawn_blocking(move || {
let zipfile = std::fs::File::open(&path)?;
let mut archive = zip::ZipArchive::new(zipfile).map_err(|_| {
crate::ErrorKind::LauncherError(format!(
@@ -394,6 +403,7 @@ pub async fn get_processor_main_class(
Ok::<Option<String>, crate::Error>(None)
})
.await
.unwrap()
.await??;
Ok(main_class)
}

View File

@@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
use url::Url;
lazy_static! {
static ref HYDRA_URL: Url =
Url::parse("https://hydra.modrinth.com").unwrap();
static ref HYDRA_URL: Url = Url::parse("https://hydra.modrinth.com")
.expect("Hydra URL parse failed");
}
// Socket messages

View File

@@ -39,11 +39,12 @@ pub async fn download_version_info(
version: &GameVersion,
loader: Option<&LoaderVersion>,
) -> crate::Result<GameVersionInfo> {
let version_id = loader.map_or(&version.id, |it| &it.id);
let version_id = loader
.map_or(version.id.clone(), |it| format!("{}-{}", version.id, it.id));
log::debug!("Loading version info for Minecraft {version_id}");
let path = st
.directories
.version_dir(version_id)
.version_dir(&version_id)
.join(format!("{version_id}.json"));
let res = if path.exists() {
@@ -58,10 +59,10 @@ pub async fn download_version_info(
if let Some(loader) = loader {
let partial = d::modded::fetch_partial_version(&loader.url).await?;
info = d::modded::merge_partial_version(partial, info);
info.id = loader.id.clone();
}
info.id = version_id.clone();
let permit = st.io_semaphore.acquire().await.unwrap();
let permit = st.io_semaphore.acquire().await?;
write(&path, &serde_json::to_vec(&info)?, &permit).await?;
Ok(info)
}?;
@@ -92,7 +93,7 @@ pub async fn download_client(
.join(format!("{version}.jar"));
if !path.exists() {
let permit = st.io_semaphore.acquire().await.unwrap();
let permit = st.io_semaphore.acquire().await?;
let bytes =
fetch(&client_download.url, Some(&client_download.sha1), &permit)
.await?;
@@ -122,7 +123,7 @@ pub async fn download_assets_index(
.and_then(|ref it| Ok(serde_json::from_slice(it)?))
} else {
let index = d::minecraft::fetch_assets_index(version).await?;
let permit = st.io_semaphore.acquire().await.unwrap();
let permit = st.io_semaphore.acquire().await?;
write(&path, &serde_json::to_vec(&index)?, &permit).await?;
log::info!("Fetched assets index");
Ok(index)
@@ -141,7 +142,7 @@ pub async fn download_assets(
log::debug!("Loading assets");
stream::iter(index.objects.iter())
.map(Ok::<(&String, &Asset), crate::Error>)
.try_for_each_concurrent(Some(st.settings.read().await.max_concurrent_downloads), |(name, asset)| async move {
.try_for_each_concurrent(None, |(name, asset)| async move {
let hash = &asset.hash;
let resource_path = st.directories.object_dir(hash);
let url = format!(
@@ -153,7 +154,7 @@ pub async fn download_assets(
tokio::try_join! {
async {
if !resource_path.exists() {
let permit = st.io_semaphore.acquire().await.unwrap();
let permit = st.io_semaphore.acquire().await?;
let resource = fetch_cell
.get_or_try_init(|| fetch(&url, Some(hash), &permit))
.await?;
@@ -164,7 +165,7 @@ pub async fn download_assets(
},
async {
if with_legacy {
let permit = st.io_semaphore.acquire().await.unwrap();
let permit = st.io_semaphore.acquire().await?;
let resource = fetch_cell
.get_or_try_init(|| fetch(&url, Some(hash), &permit))
.await?;
@@ -201,7 +202,7 @@ pub async fn download_libraries(
stream::iter(libraries.iter())
.map(Ok::<&Library, crate::Error>)
.try_for_each_concurrent(Some(st.settings.read().await.max_concurrent_downloads), |library| async move {
.try_for_each_concurrent(None, |library| async move {
if let Some(rules) = &library.rules {
if !rules.iter().all(super::parse_rule) {
return Ok(());
@@ -218,7 +219,7 @@ pub async fn download_libraries(
artifact: Some(ref artifact),
..
}) => {
let permit = st.io_semaphore.acquire().await.unwrap();
let permit = st.io_semaphore.acquire().await?;
let bytes = fetch(&artifact.url, Some(&artifact.sha1), &permit)
.await?;
write(&path, &bytes, &permit).await?;
@@ -234,7 +235,7 @@ pub async fn download_libraries(
&artifact_path
].concat();
let permit = st.io_semaphore.acquire().await.unwrap();
let permit = st.io_semaphore.acquire().await?;
let bytes = fetch(&url, None, &permit).await?;
write(&path, &bytes, &permit).await?;
log::info!("Fetched library {}", &library.name);
@@ -262,12 +263,17 @@ pub async fn download_libraries(
);
if let Some(native) = classifiers.get(&parsed_key) {
let permit = st.io_semaphore.acquire().await.unwrap();
let permit = st.io_semaphore.acquire().await?;
let data = fetch(&native.url, Some(&native.sha1), &permit).await?;
let reader = std::io::Cursor::new(&data);
let mut archive = zip::ZipArchive::new(reader).unwrap();
archive.extract(&st.directories.version_natives_dir(version)).unwrap();
log::info!("Fetched native {}", &library.name);
if let Ok(mut archive) = zip::ZipArchive::new(reader) {
match archive.extract(&st.directories.version_natives_dir(version)) {
Ok(_) => log::info!("Fetched native {}", &library.name),
Err(err) => log::error!("Failed extracting native {}. err: {}", &library.name, err)
}
} else {
log::error!("Failed extracting native {}", &library.name)
}
}
}

View File

@@ -72,9 +72,10 @@ pub async fn launch_minecraft(
"Invalid game version: {game_version}"
)))?;
let version_jar = loader_version
.as_ref()
.map_or(version.id.clone(), |it| it.id.clone());
let version_jar =
loader_version.as_ref().map_or(version.id.clone(), |it| {
format!("{}-{}", version.id.clone(), it.id.clone())
});
let mut version_info = download::download_version_info(
&state,
@@ -85,7 +86,7 @@ pub async fn launch_minecraft(
let client_path = state
.directories
.version_dir(&version.id)
.version_dir(&version_jar)
.join(format!("{version_jar}.jar"));
download::download_minecraft(&state, &version_info).await?;
@@ -133,6 +134,7 @@ pub async fn launch_minecraft(
args::get_processor_main_class(args::get_lib_path(
&state.directories.libraries_dir(),
&processor.jar,
false,
)?)
.await?
.ok_or_else(|| {
@@ -193,7 +195,7 @@ pub async fn launch_minecraft(
args::get_jvm_arguments(
args.get(&d::minecraft::ArgumentType::Jvm)
.map(|x| x.as_slice()),
&state.directories.version_natives_dir(&version.id),
&state.directories.version_natives_dir(&version_jar),
&state.directories.libraries_dir(),
&args::get_class_paths(
&state.directories.libraries_dir(),
@@ -205,7 +207,6 @@ pub async fn launch_minecraft(
Vec::from(java_args),
)?
.into_iter()
.map(|r| r.replace(' ', r"\ "))
.collect::<Vec<_>>(),
)
.arg(version_info.main_class.clone())
@@ -223,7 +224,6 @@ pub async fn launch_minecraft(
*resolution,
)?
.into_iter()
.map(|r| r.replace(' ', r"\ "))
.collect::<Vec<_>>(),
)
.current_dir(instance_path.clone())