use anyhow::Context;
use async_trait::async_trait;
use serde::Deserialize;
use super::Downloader;
use crate::{package::PackageName, prelude::*, version::structs::VersionMeta};
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
pub struct LoaderMetaItem {
pub loader: LoaderStruct,
pub intermediary: IntermediaryStruct,
}
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
pub struct IntermediaryStruct {
pub maven: String,
pub version: String,
}
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
pub struct LoaderStruct {
pub maven: String,
pub version: String,
}
#[async_trait]
pub trait QuiltMCDownloadExt: Sync {
async fn get_avaliable_loaders(&self, vanilla_version: &str) -> DynResult<Vec<LoaderMetaItem>>;
async fn download_library(&self, name: &str, url: String) -> DynResult;
async fn download_quiltmc_pre(
&self,
version_name: &str,
version_id: &str,
loader_version: &str,
) -> DynResult;
async fn download_quiltmc_post(&self, version_name: &str) -> DynResult;
}
#[async_trait]
impl<R: Reporter> QuiltMCDownloadExt for Downloader<R> {
async fn get_avaliable_loaders(&self, vanilla_version: &str) -> DynResult<Vec<LoaderMetaItem>> {
let mut result = crate::http::retry_get(format!(
"https://meta.quiltmc.org/v3/versions/loader/{vanilla_version}"
))
.await
.map_err(|e| {
anyhow::anyhow!(
"获取为原版 {} 可用的 QuiltMC Loader 版本失败 {:?}",
vanilla_version,
e
)
})?;
if result.status().is_success() {
let result = result.body_json().await.map_err(|e| anyhow::anyhow!(e))?;
Ok(result)
} else {
Ok(vec![])
}
}
async fn download_library(&self, name: &str, url: String) -> DynResult {
let url = if url.is_empty() {
"https://maven.quiltmc.org/repository/release"
} else {
url.trim_end_matches('/')
};
let package_name = name.parse::<PackageName>().unwrap();
let full_path = package_name.to_maven_jar_path(self.minecraft_library_path.as_str());
let r = self.reporter.sub();
inner_future::fs::create_dir_all(
&full_path[..full_path.rfind('/').unwrap_or(full_path.len())],
)
.await
.unwrap_or_default();
r.set_message(format!("正在下载 QuiltMC 支持库 {name}"));
r.add_max_progress(1.);
if std::path::Path::new(&full_path).is_file() {
if self.verify_data {
let mut file = inner_future::fs::OpenOptions::new()
.read(true)
.open(&full_path)
.await?;
r.set_message(format!("正在获取数据摘要以验证完整性 {name}"));
r.add_max_progress(1.);
let sha1 = crate::http::retry_get_string(format!(
"{}.sha1",
package_name.to_maven_jar_path(url)
))
.await?;
let current_sha1 = crate::utils::get_data_sha1(&mut file).await?;
if sha1 == current_sha1 {
r.add_progress(1.);
return Ok(());
}
} else {
r.add_progress(1.);
return Ok(());
}
}
let uris = [package_name.to_maven_jar_path(url)];
crate::http::download(&uris, &full_path, 0)
.await
.map_err(|e| anyhow::anyhow!("下载 QuiltMC 依赖库失败:{:?}", e))?;
Ok(())
}
async fn download_quiltmc_pre(
&self,
version_name: &str,
version_id: &str,
loader_version: &str,
) -> DynResult {
let mut loader_meta_res = crate::http::retry_get(format!(
"https://meta.quiltmc.org/v3/versions/loader/{version_id}/{loader_version}/profile/json"
))
.await
.map_err(|e| anyhow::anyhow!("获取 QuiltMC 版本元数据失败:{:?}", e))?;
let res = loader_meta_res
.body_bytes()
.await
.map_err(|e| anyhow::anyhow!(e))?;
inner_future::fs::write(
format!(
"{}/{}/{}-quiltmc-loader.tmp.json",
self.minecraft_version_path.as_str(),
version_name,
version_name
),
&res,
)
.await?;
let meta: VersionMeta =
serde_json::from_slice(&res).context("无法解析 QuiltMC 版本元数据")?;
let mut libraries_threads = Vec::with_capacity(meta.libraries.len());
for lib in &meta.libraries {
if !lib.name.is_empty() {
let url = lib.url.as_ref().cloned().unwrap_or_default();
libraries_threads.push(self.download_library(&lib.name, url));
}
}
futures::future::try_join_all(libraries_threads).await?;
Ok(())
}
async fn download_quiltmc_post(&self, version_name: &str) -> DynResult {
tracing::trace!("合并元数据中");
let vanilla_path = format!(
"{}/{}/{}.json",
self.minecraft_version_path.as_str(),
version_name,
version_name
);
let vanilla_meta = crate::prelude::inner_future::fs::read(&vanilla_path).await?;
let loader_path = format!(
"{}/{}/{}-quiltmc-loader.tmp.json",
self.minecraft_version_path.as_str(),
version_name,
version_name
);
let loader_meta = crate::prelude::inner_future::fs::read(&loader_path).await?;
inner_future::fs::remove_file(loader_path).await?;
let mut vanilla_meta: VersionMeta =
serde_json::from_slice(&vanilla_meta).context("无法解析原版版本元数据")?;
let loader_meta: VersionMeta =
serde_json::from_slice(&loader_meta).context("无法解析 QuiltMC 版本元数据")?;
vanilla_meta += loader_meta;
inner_future::fs::write(&vanilla_path, serde_json::to_vec(&vanilla_meta)?).await?;
Ok(())
}
}