Moved chapter to MinecraftClient struct
Creating of a new struct for fabric manifest because library resolution is different than minecraft official
This commit is contained in:
parent
2c84ebb87e
commit
5b8effd7c4
28
src-tauri/src/launcher/altarik/custom_version_manifest.rs
Normal file
28
src-tauri/src/launcher/altarik/custom_version_manifest.rs
Normal file
@ -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<CustomLibrary>,
|
||||
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<String>,
|
||||
game: Vec<String>,
|
||||
}
|
@ -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<CustomVersionManifest> {
|
||||
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<ProgressMessage>) -> 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<ProgressMessage>) -> Result<()> {
|
||||
pub async fn download_mods(&self, reqwest: &Client, modpack_dir: &PathBuf, root_dir: &PathBuf, log_channel: mpsc::Sender<ProgressMessage>) -> 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<ProgressMessage>) -> 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<ProgressMessage>) -> 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
|
||||
|
@ -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<CustomVersionManifest>,
|
||||
assets: AssetsManifest,
|
||||
reqwest_client: Client,
|
||||
chapter: Chapter,
|
||||
|
||||
}
|
||||
|
||||
impl<'a> MinecraftClient<'_> {
|
||||
pub async fn new(opts: &'a ClientOptions<'a>) -> Result<MinecraftClient<'a>> {
|
||||
pub async fn new(opts: &'a ClientOptions<'a>, chapter: Chapter) -> Result<MinecraftClient<'a>> {
|
||||
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::<Vec<&str>>();
|
||||
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<File> {
|
||||
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(())
|
||||
|
@ -114,10 +114,10 @@ async fn download(selected_chapter: usize, app: tauri::AppHandle, state: tauri::
|
||||
|
||||
|
||||
async fn download_libraries(opts: ClientOptions<'_>, chapter: Chapter) -> Result<String, String> {
|
||||
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())
|
||||
},
|
||||
|
Reference in New Issue
Block a user