diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index a40966e..b8068fe 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -68,6 +68,17 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "atk" version = "0.15.1" @@ -1593,6 +1604,7 @@ dependencies = [ "reqwest", "serde", "serde_json", + "sha256", "tauri", "tauri-build", "tokio", @@ -2890,6 +2902,19 @@ dependencies = [ "digest", ] +[[package]] +name = "sha256" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7895c8ae88588ccead14ff438b939b0c569cd619116f14b4d13fdff7b8333386" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2", + "tokio", +] + [[package]] name = "sharded-slab" version = "0.1.6" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 1997c33..f1ad555 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -26,6 +26,7 @@ warp = "0.3.3" anyhow = "1.0.66" rand = "0.8.5" directories = "5.0.0" +sha256 = "1.4.0" [features] # by default Tauri runs in production mode diff --git a/src-tauri/src/launcher/manifest.rs b/src-tauri/src/launcher/manifest.rs index 74fc503..90d099c 100644 --- a/src-tauri/src/launcher/manifest.rs +++ b/src-tauri/src/launcher/manifest.rs @@ -106,6 +106,7 @@ pub enum OSName { Windows } + #[derive(Serialize, Deserialize)] pub struct LibraryArtifact { pub path: String, diff --git a/src-tauri/src/launcher/mod.rs b/src-tauri/src/launcher/mod.rs index c93a93f..46bc33f 100644 --- a/src-tauri/src/launcher/mod.rs +++ b/src-tauri/src/launcher/mod.rs @@ -6,11 +6,11 @@ use std::path::{Path, self, PathBuf}; use anyhow::{Result, bail}; use reqwest::{Client, StatusCode}; use serde::{Serialize, Deserialize}; -use tokio::{fs::{self, File}, io::{AsyncWriteExt, AsyncSeekExt}, sync::mpsc}; +use tokio::{fs::{self, File, OpenOptions}, 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}; +use self::{manifest::{VersionDetail, get_version_manifest, get_version_from_manifest, get_version_detail, Library, OSName, get_version_assets, AssetsManifest}, altarik::Chapter}; #[cfg(target_os="windows")] @@ -20,6 +20,9 @@ const ACTUAL_OS: OSName = OSName::Linux; #[cfg(target_os="macos")] const ACTUAL_OS: OSName = OSName::MacOsX; +#[cfg(not(any(target_arch="x86_64", target_arch="x86")))] +compile_error!("Your architecture is not supported"); + #[derive(Clone, serde::Serialize, Debug)] pub struct ProgressMessage { p_type: String, @@ -32,6 +35,7 @@ pub struct ClientOptions<'a> { pub log_channel: mpsc::Sender, pub root_path: &'a Path, pub java_path: &'a Path, + /// deprecated, will be remove pub version_number: String, pub version_type: VersionType, // version_custom: String, // for a next update @@ -73,7 +77,8 @@ impl<'a> MinecraftClient<'_> { let folders = vec![ self.opts.root_path.join("libraries"), self.opts.root_path.join("assets").join("objects"), - self.opts.root_path.join("assets").join("indexes") + self.opts.root_path.join("assets").join("indexes"), + self.opts.root_path.join("runtime").join("download"), ]; let mut tasks = Vec::with_capacity(folders.len()); for folder in folders { @@ -102,18 +107,73 @@ impl<'a> MinecraftClient<'_> { } } - pub async fn download_requirements(&mut self) -> Result<()> { + pub async fn download_requirements(&mut self, chapter: Chapter) -> 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"); self.save_version_index().await?; + self.download_java(chapter).await?; self.download_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(()) } + async fn download_java(&mut self, chapter: Chapter) -> Result<()> { + let download_path = self.opts.root_path.join("runtime").join("download"); + // let extract_path = self.opts.root_path.join("runtime"); + let (url, extension) = match ACTUAL_OS { + OSName::Linux => { + (chapter.java.platform.linux, "tar.gz") + }, + OSName::Windows => { + (chapter.java.platform.win32, "zip") + }, + _ => { + bail!("Your current is not supported") + } + }; + let url = match url { + Some(url) => url, + None => bail!("No available executable available for your platform") + }; + + let filepath = download_path.join(format!("{}.{}", url.x64.name.clone(), extension)); + let mut should_download = false; + if !filepath.exists() { + should_download = true; + } else { + let hash = sha256::try_digest(filepath.clone()); + match hash { + Ok(hash) => { + if hash != url.x64.sha256sum { + println!("Hash of java archive is not correct, redownloading"); + should_download = true; + } + }, + Err(_) => should_download = true + } + } + if should_download { + println!("Downloading java"); + if filepath.exists() { + fs::remove_file(filepath.clone()).await?; // remove content before writing and appending to it + } + let mut file = OpenOptions::new() + .write(true) + .create(true) + .open(filepath) + .await?; + let b = self.reqwest_client.get(url.x64.link.clone()).send().await?.bytes().await?; + file.write(&b).await?; + println!("{} downloaded", url.x64.name) + } else { + println!("{} already downloaded", url.x64.name) + } + Ok(()) + } + async fn download_libraries(&mut self, lib: &PathBuf) -> Result<()> { self.filter_non_necessary_librairies(); let total = self.details.libraries.len(); diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 593eb7a..4411450 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -11,7 +11,7 @@ use std::sync::Mutex; use authentification::{Authentification, Prompt, GameProfile}; use anyhow::Result; use directories::BaseDirs; -use launcher::{MinecraftClient, ClientOptions, ProgressMessage, altarik::AltarikManifest}; +use launcher::{MinecraftClient, ClientOptions, ProgressMessage, altarik::{AltarikManifest, Chapter}}; use reqwest::Client; use tauri::Manager; use tokio::sync::mpsc; @@ -62,7 +62,23 @@ async fn load_altarik_manifest(state: tauri::State<'_, Mutex>) -> R } #[tauri::command] -async fn download(game_version: String, app: tauri::AppHandle, state: tauri::State<'_, Mutex>) -> Result { +async fn download(selected_chapter: usize, app: tauri::AppHandle, state: tauri::State<'_, Mutex>) -> Result { + let chapter = match state.lock() { + Ok(lock) => { + match &lock.1 { + Some(manifest) => { + match manifest.chapters.get(selected_chapter) { + Some(val) => { + val.clone() + }, + None => return Err("Selected chapter doesn't exist".to_string()) + } + }, + None => return Err("Cannot load altarik manifest".to_string()) + } + }, + Err(err) => return Err(err.to_string()) + }; if let Some(base_dir) = BaseDirs::new() { let data_folder = base_dir.data_dir().join(".altarik_test"); let root_path = data_folder.as_path(); @@ -80,14 +96,14 @@ async fn download(game_version: String, app: tauri::AppHandle, state: tauri::Sta log_channel: sender.clone(), root_path, java_path: &java_path.as_path(), - version_number: game_version, + version_number: chapter.minecraft_version.clone(), version_type: launcher::VersionType::Release, memory_min: "2G".to_string(), memory_max: "4G".to_string(), }; drop(sender); let res = tokio::join!( - download_libraries(opts), + download_libraries(opts, chapter), read_channel(receiver, app), ); res.0 @@ -97,11 +113,11 @@ async fn download(game_version: String, app: tauri::AppHandle, state: tauri::Sta } -async fn download_libraries(opts: ClientOptions<'_>) -> Result { +async fn download_libraries(opts: ClientOptions<'_>, chapter: Chapter) -> Result { let client = MinecraftClient::new(&opts).await; let res = match client { Ok(mut client) => { - match client.download_requirements().await { + match client.download_requirements(chapter).await { Ok(_) => { Ok("Content downloaded".to_string()) }, diff --git a/src/components/LoginPage.tsx b/src/components/LoginPage.tsx index c925bdb..792c966 100644 --- a/src/components/LoginPage.tsx +++ b/src/components/LoginPage.tsx @@ -55,7 +55,7 @@ export default function LoginPage() { async function download() { if(isLogged) { if(selectedChapter !== -1 && altarikManifest !== undefined) { - invoke("download", { gameVersion: altarikManifest?.chapters[selectedChapter].minecraftVersion }).then(value => { + invoke("download", { selectedChapter: selectedChapter }).then(value => { setGreetMessage(String(value)) }).catch(err => { console.log("An error occured")