From 5b8effd7c42c4448d5694e8a761162600d4866e7 Mon Sep 17 00:00:00 2001 From: Quentin Legot Date: Sat, 7 Oct 2023 01:33:26 +0200 Subject: [PATCH] Moved chapter to MinecraftClient struct Creating of a new struct for fabric manifest because library resolution is different than minecraft official --- .../altarik/custom_version_manifest.rs | 28 +++++++++++ src-tauri/src/launcher/altarik/mod.rs | 47 +++++++++-------- src-tauri/src/launcher/mod.rs | 50 ++++++++++++++----- src-tauri/src/main.rs | 4 +- 4 files changed, 94 insertions(+), 35 deletions(-) create mode 100644 src-tauri/src/launcher/altarik/custom_version_manifest.rs diff --git a/src-tauri/src/launcher/altarik/custom_version_manifest.rs b/src-tauri/src/launcher/altarik/custom_version_manifest.rs new file mode 100644 index 0000000..28c37a7 --- /dev/null +++ b/src-tauri/src/launcher/altarik/custom_version_manifest.rs @@ -0,0 +1,28 @@ +//! module for fabric version detail manifest + +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize)] +pub struct CustomVersionManifest { + #[serde(rename(serialize = "inheritsFrom", deserialize = "inheritsFrom"))] + pub inherits_from: String, + #[serde(rename(serialize = "mainClass", deserialize = "mainClass"))] + pub main_class: String, + pub libraries: Vec, + pub arguments: CustomArguments, + pub id: String, + #[serde(rename(serialize = "type", deserialize = "type"))] + pub t_type: String, +} + +#[derive(Serialize, Deserialize)] +pub struct CustomLibrary { + pub name: String, + pub url: String, +} + +#[derive(Serialize, Deserialize)] +pub struct CustomArguments { + jvm: Vec, + game: Vec, +} \ No newline at end of file diff --git a/src-tauri/src/launcher/altarik/mod.rs b/src-tauri/src/launcher/altarik/mod.rs index 2f2d6fb..0045524 100644 --- a/src-tauri/src/launcher/altarik/mod.rs +++ b/src-tauri/src/launcher/altarik/mod.rs @@ -1,3 +1,5 @@ +pub mod custom_version_manifest; + use std::path::{PathBuf, Path}; use anyhow::{Result, bail, anyhow}; @@ -11,7 +13,9 @@ use tokio_stream::StreamExt; use crate::launcher::ProgressMessage; -use super::{ACTUAL_OS, manifest::OSName}; +use self::custom_version_manifest::CustomVersionManifest; + +use super::{ACTUAL_OS, manifest::{OSName, VersionDetail}}; #[derive(Serialize, Deserialize, Clone)] @@ -78,7 +82,13 @@ impl AltarikManifest { impl Chapter { - + pub async fn get_custom_version_detail_manifest(&self, version_dir: &PathBuf) -> Result { + let filepath = version_dir.join(self.custom_version.to_string()).join(format!("{}.json", self.custom_version.to_string())); + let mut file = File::open(filepath).await?; + let mut content = String::new(); + file.read_to_string(&mut content).await?; + Ok(serde_json::from_str(&content)?) + } } @@ -143,17 +153,11 @@ impl JavaPlatform { Ok(()) } - pub async fn extract_java(&self, root_path: &Path, log_channel: mpsc::Sender) -> Result<()> { + pub async fn extract_java(&self, root_path: &Path) -> Result<()> { let (url, extension) = match ACTUAL_OS { - OSName::Linux => { - (self.linux.clone(), "tar.gz") - }, - OSName::Windows => { - (self.win32.clone(), "zip") - }, - _ => { - bail!("Your current is not supported") - } + OSName::Linux => (self.linux.clone(), "tar.gz"), + OSName::Windows => (self.win32.clone(), "zip"), + _ => bail!("Your current is not supported") }; let url = match url { Some(url) => url, @@ -161,7 +165,7 @@ impl JavaPlatform { }; let archive_path = root_path.join("runtime").join("download").join(format!("{}.{}", url.x64.name, extension)); let extract_path = root_path.join("runtime"); - extract_zip(archive_path, extract_path, log_channel).await?; + extract_zip(&archive_path, &extract_path).await?; Ok(()) } @@ -171,7 +175,7 @@ impl JavaPlatform { impl ModsPack { - pub async fn download_mods(&self, reqwest: &Client, modpack_dir: PathBuf, log_channel: mpsc::Sender) -> Result<()> { + pub async fn download_mods(&self, reqwest: &Client, modpack_dir: &PathBuf, root_dir: &PathBuf, log_channel: mpsc::Sender) -> Result<()> { for index in 0..self.mods.len() { log_channel.send(ProgressMessage { p_type: "mods".to_string(), current: index, total: self.mods.len() }).await?; let mod_url = self.mods.get(index).ok_or(anyhow!("Cannot get mod download link"))?; @@ -187,12 +191,15 @@ impl ModsPack { .write(true) .create_new(true) .append(true) - .open(filepath) + .open(filepath.clone()) .await?; while let Some(chunk) = res.chunk().await? { file.write_all(&chunk).await?; } + } else { + println!("Mod {} already downloaded", sha1); } + extract_zip(&filepath, &root_dir).await?; } Ok(()) } @@ -202,11 +209,11 @@ impl ModsPack { let mut hasher = Sha1::new(); let mut file = File::open(filepath).await?; let mut content = Vec::new(); - file.read(&mut content).await?; + file.read_to_end(&mut content).await?; hasher.update(content); let hash = hasher.finalize(); // let b16 = base16ct::upper::encode_string(hash); - if &&format!("{:x}", &hash.clone()) != mod_sha1 { + if format!("{:x}", hash.clone()) != mod_sha1.to_lowercase() { println!("Correct: {:?}, current: {:X}", mod_sha1, hash); fs::remove_file(filepath).await?; Ok(true) @@ -221,7 +228,7 @@ impl ModsPack { } -async fn extract_targz(archive_path: PathBuf, extract_path: PathBuf, log_channel: mpsc::Sender) -> Result<()> { +async fn extract_targz(archive_path: PathBuf, extract_path: PathBuf) -> Result<()> { let file = File::open(archive_path.clone()).await?; let mut archive = Archive::new(file); let mut entries = archive.entries()?; @@ -234,17 +241,15 @@ async fn extract_targz(archive_path: PathBuf, extract_path: PathBuf, log_channel Ok(()) } -async fn extract_zip(archive_path: PathBuf, extract_path: PathBuf, log_channel: mpsc::Sender) -> Result<()> { +async fn extract_zip(archive_path: &PathBuf, extract_path: &PathBuf) -> Result<()> { // TODO add log_channel to send progression to user let file = File::open(archive_path.clone()).await?; let mut reader = ZipFileReader::with_tokio(file).await?; - let total = reader.file().entries().len(); for index in 0..reader.file().entries().len() { if let Some(entry) = reader.file().entries().get(index) { let entry = entry.entry(); let filename = entry.filename().as_str()?; let path = extract_path.join(filename); - log_channel.send(ProgressMessage { p_type: "extract".to_string(), current: index + 1, total: total }).await?; if entry.dir()? { if path.exists() { fs::remove_dir_all(path.clone()).await?; // clear folder before continue, avoid injection diff --git a/src-tauri/src/launcher/mod.rs b/src-tauri/src/launcher/mod.rs index 7efcaf7..5959ced 100644 --- a/src-tauri/src/launcher/mod.rs +++ b/src-tauri/src/launcher/mod.rs @@ -3,14 +3,14 @@ pub mod altarik; use std::path::{Path, self, PathBuf}; -use anyhow::{Result, bail}; +use anyhow::{Result, bail, anyhow}; use reqwest::{Client, StatusCode}; use serde::{Serialize, Deserialize}; use tokio::{fs::{self, File}, io::{AsyncWriteExt, AsyncSeekExt}, sync::mpsc}; use crate::authentification::GameProfile; -use self::{manifest::{VersionDetail, get_version_manifest, get_version_from_manifest, get_version_detail, Library, OSName, get_version_assets, AssetsManifest}, altarik::Chapter}; +use self::{manifest::{VersionDetail, get_version_manifest, get_version_from_manifest, get_version_detail, Library, OSName, get_version_assets, AssetsManifest}, altarik::{Chapter, custom_version_manifest::CustomVersionManifest}}; #[cfg(target_os="windows")] @@ -46,13 +46,15 @@ pub struct ClientOptions<'a> { pub struct MinecraftClient<'a> { opts: &'a ClientOptions<'a>, details: VersionDetail, + custom_details: Option, assets: AssetsManifest, reqwest_client: Client, + chapter: Chapter, } impl<'a> MinecraftClient<'_> { - pub async fn new(opts: &'a ClientOptions<'a>) -> Result> { + pub async fn new(opts: &'a ClientOptions<'a>, chapter: Chapter) -> Result> { let reqwest_client = Client::new(); let manifest = Self::load_manifest(&reqwest_client, &opts).await?; let details = manifest.0; @@ -61,7 +63,9 @@ impl<'a> MinecraftClient<'_> { opts, reqwest_client, details, + custom_details: None, assets, + chapter }) } @@ -108,20 +112,23 @@ impl<'a> MinecraftClient<'_> { } } - pub async fn download_requirements(&mut self, chapter: Chapter) -> Result<()> { + pub async fn download_requirements(&mut self) -> Result<()> { // create root folder if it doesn't exist self.create_dirs().await?; let lib = &self.opts.root_path.join("libraries"); let asset = &self.opts.root_path.join("assets").join("objects"); - let modpack = &self.opts.root_path.join("modpack").join(chapter.title); + let modpack = &self.opts.root_path.join("modpack").join(self.chapter.title.clone()); if !modpack.exists() { fs::create_dir_all(modpack).await?; } + self.clear_folder().await?; self.save_version_index().await?; - chapter.java.platform.download_java(self.opts.root_path, &self.reqwest_client, self.opts.log_channel.clone()).await?; - chapter.java.platform.extract_java(self.opts.root_path, self.opts.log_channel.clone()).await?; - chapter.modspack.download_mods(&self.reqwest_client, modpack.to_path_buf(), self.opts.log_channel.clone()).await?; + self.chapter.java.platform.download_java(self.opts.root_path, &self.reqwest_client, self.opts.log_channel.clone()).await?; + self.chapter.java.platform.extract_java(self.opts.root_path).await?; + self.chapter.modspack.download_mods(&self.reqwest_client, modpack, &self.opts.root_path.to_path_buf(), self.opts.log_channel.clone()).await?; + self.custom_details = Some(self.chapter.get_custom_version_detail_manifest(&self.opts.root_path.join("versions")).await?); self.download_libraries(lib).await?; + self.download_custom_libraries(lib).await?; self.download_assets(asset).await?; self.opts.log_channel.send(ProgressMessage { p_type: "completed".to_string(), current: 0, total: 0 }).await?; Ok(()) @@ -131,6 +138,7 @@ impl<'a> MinecraftClient<'_> { self.filter_non_necessary_librairies(); let total = self.details.libraries.len(); for (progress, i) in self.details.libraries.iter().enumerate() { + let p = i.downloads.artifact.path.clone(); let mut splited = p.split("/").collect::>(); let filename = splited.pop().ok_or(anyhow::anyhow!("Invalid filename"))?; // remove last element @@ -174,6 +182,24 @@ impl<'a> MinecraftClient<'_> { Ok(()) } + async fn download_custom_libraries(&self, lib_dir: &PathBuf) -> Result<()> { + if let Some(custom) = &self.custom_details { + for i in &custom.libraries { + + } + } + Ok(()) + } + + /// delete and recreate some folder, in particular mods and version folder + async fn clear_folder(&self) -> Result<()> { + for i in [self.opts.root_path.join("mods"), self.opts.root_path.join("versions")] { + fs::remove_dir_all(&i).await?; + fs::create_dir_all(&i).await?; + } + Ok(()) + } + async fn select_file_option(file_path: &PathBuf, expected_size: u64) -> Result { if (&file_path).exists() { let f = fs::File::open(&file_path).await; @@ -188,7 +214,7 @@ impl<'a> MinecraftClient<'_> { /// Filter non necessary librairies for the current OS fn filter_non_necessary_librairies(&mut self) { - self.details.libraries.retain(|e| { Self::should_use_library(e) }); + self.details.libraries.retain(|e| { Self::should_use_library(e) }); } async fn download_assets(&mut self, object_folder: &PathBuf) -> Result<()> { @@ -216,9 +242,9 @@ impl<'a> MinecraftClient<'_> { .await?; file.write_all(&received).await?; println!("{} downloaded", value.hash); - } else { - println!("{} already downloaded", value.hash); - } + } // else { + // println!("{} already downloaded", value.hash); + // } self.opts.log_channel.send( ProgressMessage { p_type: "assets".to_string(), current: progress + 1, total }).await?; } Ok(()) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 4411450..a7ebcbf 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -114,10 +114,10 @@ async fn download(selected_chapter: usize, app: tauri::AppHandle, state: tauri:: async fn download_libraries(opts: ClientOptions<'_>, chapter: Chapter) -> Result { - let client = MinecraftClient::new(&opts).await; + let client = MinecraftClient::new(&opts, chapter).await; let res = match client { Ok(mut client) => { - match client.download_requirements(chapter).await { + match client.download_requirements().await { Ok(_) => { Ok("Content downloaded".to_string()) },