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 std::path::{PathBuf, Path};
|
||||||
|
|
||||||
use anyhow::{Result, bail, anyhow};
|
use anyhow::{Result, bail, anyhow};
|
||||||
@ -11,7 +13,9 @@ use tokio_stream::StreamExt;
|
|||||||
|
|
||||||
use crate::launcher::ProgressMessage;
|
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)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
@ -78,7 +82,13 @@ impl AltarikManifest {
|
|||||||
|
|
||||||
impl Chapter {
|
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(())
|
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 {
|
let (url, extension) = match ACTUAL_OS {
|
||||||
OSName::Linux => {
|
OSName::Linux => (self.linux.clone(), "tar.gz"),
|
||||||
(self.linux.clone(), "tar.gz")
|
OSName::Windows => (self.win32.clone(), "zip"),
|
||||||
},
|
_ => bail!("Your current is not supported")
|
||||||
OSName::Windows => {
|
|
||||||
(self.win32.clone(), "zip")
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
bail!("Your current is not supported")
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let url = match url {
|
let url = match url {
|
||||||
Some(url) => 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 archive_path = root_path.join("runtime").join("download").join(format!("{}.{}", url.x64.name, extension));
|
||||||
let extract_path = root_path.join("runtime");
|
let extract_path = root_path.join("runtime");
|
||||||
extract_zip(archive_path, extract_path, log_channel).await?;
|
extract_zip(&archive_path, &extract_path).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -171,7 +175,7 @@ impl JavaPlatform {
|
|||||||
impl ModsPack {
|
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() {
|
for index in 0..self.mods.len() {
|
||||||
log_channel.send(ProgressMessage { p_type: "mods".to_string(), current: index, total: self.mods.len() }).await?;
|
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"))?;
|
let mod_url = self.mods.get(index).ok_or(anyhow!("Cannot get mod download link"))?;
|
||||||
@ -187,12 +191,15 @@ impl ModsPack {
|
|||||||
.write(true)
|
.write(true)
|
||||||
.create_new(true)
|
.create_new(true)
|
||||||
.append(true)
|
.append(true)
|
||||||
.open(filepath)
|
.open(filepath.clone())
|
||||||
.await?;
|
.await?;
|
||||||
while let Some(chunk) = res.chunk().await? {
|
while let Some(chunk) = res.chunk().await? {
|
||||||
file.write_all(&chunk).await?;
|
file.write_all(&chunk).await?;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
println!("Mod {} already downloaded", sha1);
|
||||||
}
|
}
|
||||||
|
extract_zip(&filepath, &root_dir).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -202,11 +209,11 @@ impl ModsPack {
|
|||||||
let mut hasher = Sha1::new();
|
let mut hasher = Sha1::new();
|
||||||
let mut file = File::open(filepath).await?;
|
let mut file = File::open(filepath).await?;
|
||||||
let mut content = Vec::new();
|
let mut content = Vec::new();
|
||||||
file.read(&mut content).await?;
|
file.read_to_end(&mut content).await?;
|
||||||
hasher.update(content);
|
hasher.update(content);
|
||||||
let hash = hasher.finalize();
|
let hash = hasher.finalize();
|
||||||
// let b16 = base16ct::upper::encode_string(hash);
|
// 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);
|
println!("Correct: {:?}, current: {:X}", mod_sha1, hash);
|
||||||
fs::remove_file(filepath).await?;
|
fs::remove_file(filepath).await?;
|
||||||
Ok(true)
|
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 file = File::open(archive_path.clone()).await?;
|
||||||
let mut archive = Archive::new(file);
|
let mut archive = Archive::new(file);
|
||||||
let mut entries = archive.entries()?;
|
let mut entries = archive.entries()?;
|
||||||
@ -234,17 +241,15 @@ async fn extract_targz(archive_path: PathBuf, extract_path: PathBuf, log_channel
|
|||||||
Ok(())
|
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
|
// TODO add log_channel to send progression to user
|
||||||
let file = File::open(archive_path.clone()).await?;
|
let file = File::open(archive_path.clone()).await?;
|
||||||
let mut reader = ZipFileReader::with_tokio(file).await?;
|
let mut reader = ZipFileReader::with_tokio(file).await?;
|
||||||
let total = reader.file().entries().len();
|
|
||||||
for index in 0..reader.file().entries().len() {
|
for index in 0..reader.file().entries().len() {
|
||||||
if let Some(entry) = reader.file().entries().get(index) {
|
if let Some(entry) = reader.file().entries().get(index) {
|
||||||
let entry = entry.entry();
|
let entry = entry.entry();
|
||||||
let filename = entry.filename().as_str()?;
|
let filename = entry.filename().as_str()?;
|
||||||
let path = extract_path.join(filename);
|
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 entry.dir()? {
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
fs::remove_dir_all(path.clone()).await?; // clear folder before continue, avoid injection
|
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 std::path::{Path, self, PathBuf};
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail, anyhow};
|
||||||
use reqwest::{Client, StatusCode};
|
use reqwest::{Client, StatusCode};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use tokio::{fs::{self, File}, io::{AsyncWriteExt, AsyncSeekExt}, sync::mpsc};
|
use tokio::{fs::{self, File}, io::{AsyncWriteExt, AsyncSeekExt}, sync::mpsc};
|
||||||
|
|
||||||
use crate::authentification::GameProfile;
|
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")]
|
#[cfg(target_os="windows")]
|
||||||
@ -46,13 +46,15 @@ pub struct ClientOptions<'a> {
|
|||||||
pub struct MinecraftClient<'a> {
|
pub struct MinecraftClient<'a> {
|
||||||
opts: &'a ClientOptions<'a>,
|
opts: &'a ClientOptions<'a>,
|
||||||
details: VersionDetail,
|
details: VersionDetail,
|
||||||
|
custom_details: Option<CustomVersionManifest>,
|
||||||
assets: AssetsManifest,
|
assets: AssetsManifest,
|
||||||
reqwest_client: Client,
|
reqwest_client: Client,
|
||||||
|
chapter: Chapter,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MinecraftClient<'_> {
|
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 reqwest_client = Client::new();
|
||||||
let manifest = Self::load_manifest(&reqwest_client, &opts).await?;
|
let manifest = Self::load_manifest(&reqwest_client, &opts).await?;
|
||||||
let details = manifest.0;
|
let details = manifest.0;
|
||||||
@ -61,7 +63,9 @@ impl<'a> MinecraftClient<'_> {
|
|||||||
opts,
|
opts,
|
||||||
reqwest_client,
|
reqwest_client,
|
||||||
details,
|
details,
|
||||||
|
custom_details: None,
|
||||||
assets,
|
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
|
// create root folder if it doesn't exist
|
||||||
self.create_dirs().await?;
|
self.create_dirs().await?;
|
||||||
let lib = &self.opts.root_path.join("libraries");
|
let lib = &self.opts.root_path.join("libraries");
|
||||||
let asset = &self.opts.root_path.join("assets").join("objects");
|
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() {
|
if !modpack.exists() {
|
||||||
fs::create_dir_all(modpack).await?;
|
fs::create_dir_all(modpack).await?;
|
||||||
}
|
}
|
||||||
|
self.clear_folder().await?;
|
||||||
self.save_version_index().await?;
|
self.save_version_index().await?;
|
||||||
chapter.java.platform.download_java(self.opts.root_path, &self.reqwest_client, 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?;
|
||||||
chapter.java.platform.extract_java(self.opts.root_path, self.opts.log_channel.clone()).await?;
|
self.chapter.java.platform.extract_java(self.opts.root_path).await?;
|
||||||
chapter.modspack.download_mods(&self.reqwest_client, modpack.to_path_buf(), self.opts.log_channel.clone()).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_libraries(lib).await?;
|
||||||
|
self.download_custom_libraries(lib).await?;
|
||||||
self.download_assets(asset).await?;
|
self.download_assets(asset).await?;
|
||||||
self.opts.log_channel.send(ProgressMessage { p_type: "completed".to_string(), current: 0, total: 0 }).await?;
|
self.opts.log_channel.send(ProgressMessage { p_type: "completed".to_string(), current: 0, total: 0 }).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -131,6 +138,7 @@ impl<'a> MinecraftClient<'_> {
|
|||||||
self.filter_non_necessary_librairies();
|
self.filter_non_necessary_librairies();
|
||||||
let total = self.details.libraries.len();
|
let total = self.details.libraries.len();
|
||||||
for (progress, i) in self.details.libraries.iter().enumerate() {
|
for (progress, i) in self.details.libraries.iter().enumerate() {
|
||||||
|
|
||||||
let p = i.downloads.artifact.path.clone();
|
let p = i.downloads.artifact.path.clone();
|
||||||
let mut splited = p.split("/").collect::<Vec<&str>>();
|
let mut splited = p.split("/").collect::<Vec<&str>>();
|
||||||
let filename = splited.pop().ok_or(anyhow::anyhow!("Invalid filename"))?; // remove last element
|
let filename = splited.pop().ok_or(anyhow::anyhow!("Invalid filename"))?; // remove last element
|
||||||
@ -174,6 +182,24 @@ impl<'a> MinecraftClient<'_> {
|
|||||||
Ok(())
|
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> {
|
async fn select_file_option(file_path: &PathBuf, expected_size: u64) -> Result<File> {
|
||||||
if (&file_path).exists() {
|
if (&file_path).exists() {
|
||||||
let f = fs::File::open(&file_path).await;
|
let f = fs::File::open(&file_path).await;
|
||||||
@ -216,9 +242,9 @@ impl<'a> MinecraftClient<'_> {
|
|||||||
.await?;
|
.await?;
|
||||||
file.write_all(&received).await?;
|
file.write_all(&received).await?;
|
||||||
println!("{} downloaded", value.hash);
|
println!("{} downloaded", value.hash);
|
||||||
} else {
|
} // else {
|
||||||
println!("{} already downloaded", value.hash);
|
// println!("{} already downloaded", value.hash);
|
||||||
}
|
// }
|
||||||
self.opts.log_channel.send( ProgressMessage { p_type: "assets".to_string(), current: progress + 1, total }).await?;
|
self.opts.log_channel.send( ProgressMessage { p_type: "assets".to_string(), current: progress + 1, total }).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
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> {
|
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 {
|
let res = match client {
|
||||||
Ok(mut client) => {
|
Ok(mut client) => {
|
||||||
match client.download_requirements(chapter).await {
|
match client.download_requirements().await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
Ok("Content downloaded".to_string())
|
Ok("Content downloaded".to_string())
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user