diff --git a/FCLauncher/src-tauri/Cargo.toml b/FCLauncher/src-tauri/Cargo.toml index 66df6fa..d42fef3 100644 --- a/FCLauncher/src-tauri/Cargo.toml +++ b/FCLauncher/src-tauri/Cargo.toml @@ -22,6 +22,8 @@ dirs = "5.0.1" gethostname = "0.4.3" self_update = "0.40.0" parking_lot = "0.12.3" +reqwest = { version = "0.12.5", features = ["stream"] } +futures-util = "0.3.30" [features] # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! diff --git a/FCLauncher/src-tauri/src/admin.rs b/FCLauncher/src-tauri/src/admin.rs index e3cf0e7..af9936f 100644 --- a/FCLauncher/src-tauri/src/admin.rs +++ b/FCLauncher/src-tauri/src/admin.rs @@ -1,15 +1,13 @@ -use crate::ftp::{self, test_cred}; - static USERNAME: parking_lot::Mutex = parking_lot::const_mutex(String::new()); static PASSWORD: parking_lot::Mutex = parking_lot::const_mutex(String::new()); #[tauri::command] pub fn login(username: String, password: String, window: tauri::Window) { - if(test_cred(username.as_str(), password.as_str())){ - *USERNAME.lock() = username; - *PASSWORD.lock() = password; - window.emit("Login_Success", {}); - }else{ + //if(test_cred(username.as_str(), password.as_str())){ + //*USERNAME.lock() = username; + //*PASSWORD.lock() = password; + //window.emit("Login_Success", {}); + //}else{ window.emit("Login_Failed", {}); - } + //} } \ No newline at end of file diff --git a/FCLauncher/src-tauri/src/https.rs b/FCLauncher/src-tauri/src/https.rs new file mode 100644 index 0000000..0b50b49 --- /dev/null +++ b/FCLauncher/src-tauri/src/https.rs @@ -0,0 +1,39 @@ +use std::io::Write; +use std::path::PathBuf; +use futures_util::StreamExt; +use std::cmp::min; +use serde::Serialize; + + +#[derive(Clone, Serialize)] +pub struct DownloadStatus { + downloaded: usize, + total: usize, + time_elapsed: usize, + download_name: String +} + + +pub async fn download(window: Option, url: String , mut writer: impl Write, downloadName: String) -> Result<(), String> { + let client = reqwest::Client::new(); + let res = client.get(url).send().await.or(Err(format!("Failed to fetch from URL!")))?; + let total_size = res.content_length().ok_or(format!("Failed to get content length"))?; + let mut downloaded: u64 = 0; + let mut stream = res.bytes_stream(); + while let Some(item) = stream.next().await { + let chunk = item.or(Err(format!("Error while downloading file!")))?; + writer.write_all(&chunk).or(Err("Error writing to stream!"))?; + let new = min(downloaded + (chunk.len() as u64), total_size); + downloaded = new; + println!("Downloading {}: {}MB / {}MB", downloadName.clone(), downloaded/(1024*1024), total_size/(1024*1024)); + if let Some(window) = window.clone() { + if downloaded != total_size { + window.emit("download_progress", DownloadStatus{downloaded: downloaded as usize, total: total_size as usize, time_elapsed: 0, download_name: downloadName.clone()}).or(Err(format!("Unable to signal window")))?; + } else { + window.emit("download_finished", true).or(Err(format!("Unable to signal window!")))?; + } + } + } + return Ok(()); +} + diff --git a/FCLauncher/src-tauri/src/java.rs b/FCLauncher/src-tauri/src/java.rs index 29a2e4c..56b1d04 100644 --- a/FCLauncher/src-tauri/src/java.rs +++ b/FCLauncher/src-tauri/src/java.rs @@ -6,7 +6,7 @@ use tar::Archive; use crate::system_dirs::get_local_data_directory; -use crate::ftp::{self, ftp_get_size}; +use crate::https; use crate::util; fn check_java(version: u8) -> bool { @@ -14,14 +14,14 @@ fn check_java(version: u8) -> bool { dir.exists() } -pub fn install_java(version: u8, window: tauri::Window) { +pub async fn install_java(version: u8, window: tauri::Window) { if check_java(version) { return; } - let ftp_dir = PathBuf::new().join("java").join(format!("java-{}-{}", version, if env::consts::OS == "windows" { "win.zip" } else {"lin.tar.gz"})); + //let ftp_dir = PathBuf::new().join("java").join(format!("java-{}-{}", version, if env::consts::OS == "windows" { "win.zip" } else {"lin.tar.gz"})); let mut buff = Cursor::new(vec![]); - ftp::ftp_retr(Some(window), ftp_dir, &mut buff, |window, data, size| util::download_callback(format!("Java {}", version), window,data, size)).unwrap(); - + //ftp::ftp_retr(Some(window), ftp_dir, &mut buff, |window, data, size| util::download_callback(format!("Java {}", version), window,data, size)).unwrap(); + https::download(Some(window.clone()), format!("https://gitea.piwalker.net/fclauncher/java/java-{}-{}", version, if env::consts::OS == "windows" { "win.zip" } else {"lin.tar.gz"}), &mut buff, format!("Java {}", version)).await; std::fs::create_dir_all(get_local_data_directory().join("java").join(format!("java-{}-{}", version, if env::consts::OS == "windows" { "win" } else {"lin"}))).unwrap(); buff.rewind().unwrap(); if env::consts::OS != "windows" { diff --git a/FCLauncher/src-tauri/src/main.rs b/FCLauncher/src-tauri/src/main.rs index 2f5b79c..3539a9d 100644 --- a/FCLauncher/src-tauri/src/main.rs +++ b/FCLauncher/src-tauri/src/main.rs @@ -9,13 +9,14 @@ use serde_json::{Map, Result, Value}; use serde::Serialize; use serde::Deserialize; -mod ftp; +//mod ftp; mod java; mod prism; mod system_dirs; mod util; mod modpack; mod admin; +mod https; #[derive(Serialize, Deserialize)] struct ModpackEntry{ @@ -31,7 +32,7 @@ fn greet(name: &str) -> String { fn main() { - modpack::get_modpacks(); + //modpack::get_modpacks(); //prism::install_prism(); tauri::Builder::default() .invoke_handler(tauri::generate_handler![greet, modpack::get_modpacks, modpack::launch_modpack, prism::launch_prism, prism::install_prism, admin::login]) diff --git a/FCLauncher/src-tauri/src/modpack.rs b/FCLauncher/src-tauri/src/modpack.rs index e1c1106..082331d 100644 --- a/FCLauncher/src-tauri/src/modpack.rs +++ b/FCLauncher/src-tauri/src/modpack.rs @@ -1,5 +1,5 @@ -use crate::{ftp, java}; +use crate::{java}; use crate::system_dirs::{get_data_directory, get_java_executable, get_local_data_directory, get_prism_executable}; use std::time::Duration; use std::{env, thread}; @@ -7,12 +7,14 @@ use std::fs::File; use std::process::Command; use std::{io::Cursor, path::PathBuf}; use std::io::{Read, Seek, Write}; +use reqwest::IntoUrl; use serde::de::value::Error; use serde_json::Value; use serde::Serialize; use serde::Deserialize; use crate::util; use std::fs; +use crate::https; @@ -29,8 +31,8 @@ pub struct VersionEntry{ date: String } -fn get_modpack_name(id: String) -> String { - let modpacks = get_modpacks(); +async fn get_modpack_name(id: String) -> String { + let modpacks = get_modpacks().await; let mut instance_name = String::new(); for pack in modpacks { if pack.id == id { @@ -42,13 +44,13 @@ fn get_modpack_name(id: String) -> String { -fn check_modpack_needs_update(id: String) -> bool{ - let mut instance_name = get_modpack_name(id.clone()); +async fn check_modpack_needs_update(id: String) -> bool{ + let mut instance_name = get_modpack_name(id.clone()).await; if !get_local_data_directory().join("prism").join("instances").join(&mut instance_name).exists() { return true; } - let versions = get_versions(id); + let versions = get_versions(id).await; if !versions.is_ok() { return false; } @@ -70,23 +72,24 @@ fn check_modpack_needs_update(id: String) -> bool{ #[tauri::command] pub async fn launch_modpack(window: tauri::Window, id: String){ - if check_modpack_needs_update(id.clone()) { - install_modpack(window, id.clone()); + if check_modpack_needs_update(id.clone()).await { + install_modpack(window, id.clone()).await; } // Launch - let mut child = Command::new(get_local_data_directory().join("prism").join(get_prism_executable())).arg("-l").arg(get_modpack_name(id)).spawn().unwrap(); + let mut child = Command::new(get_local_data_directory().join("prism").join(get_prism_executable())).arg("-l").arg(get_modpack_name(id).await).spawn().unwrap(); } -fn install_modpack(window: tauri::Window, id: String){ - let versions = get_versions(id.clone()).unwrap(); - let path = env::temp_dir().join(format!("{}.mrpack", get_modpack_name(id.clone()))); +async fn install_modpack(window: tauri::Window, id: String){ + let versions = get_versions(id.clone()).await.unwrap(); + let path = env::temp_dir().join(format!("{}.mrpack", get_modpack_name(id.clone()).await)); let mut file = File::create(path.clone()).unwrap(); let ftp_path = PathBuf::new().join(id.clone()).join(versions[versions.len()-1].file.clone().as_str()); println!("Downloading file {}", ftp_path.to_str().unwrap()); - ftp::ftp_retr(Some(window.clone()), ftp_path, &mut file, |window, data, size| util::download_callback(get_modpack_name(id.clone()), window, data, size)).unwrap(); + //ftp::ftp_retr(Some(window.clone()), ftp_path, &mut file, |window, data, size| util::download_callback(name.clone(), window, data, size)).unwrap(); + https::download(Some(window.clone()), format!("https://gitea.piwalker.net/fclauncher/{}/{}", id.clone(), versions[versions.len()-1].file.clone()), &mut file, get_modpack_name(id.clone()).await).await; let mut child = Command::new(get_local_data_directory().join("prism").join(get_prism_executable())).arg("-I").arg(path).spawn().unwrap(); loop { - let version_path = get_local_data_directory().join("prism").join("instances").join(get_modpack_name(id.clone())).join(".minecraft").join("version.txt"); + let version_path = get_local_data_directory().join("prism").join("instances").join(get_modpack_name(id.clone()).await).join(".minecraft").join("version.txt"); if version_path.clone().exists() { let mut ver_file = File::open(version_path).unwrap(); let mut buf = String::new(); @@ -99,7 +102,7 @@ fn install_modpack(window: tauri::Window, id: String){ } thread::sleep(Duration::from_secs(1)); child.kill(); - let info_path = get_local_data_directory().join("prism").join("instances").join(get_modpack_name(id.clone())).join("mmc-pack.json"); + let info_path = get_local_data_directory().join("prism").join("instances").join(get_modpack_name(id.clone()).await).join("mmc-pack.json"); let mut info_file = File::open(info_path.clone()).unwrap(); let info_json: Value = serde_json::from_reader(info_file).unwrap(); let mut mc_version = "0.0"; @@ -109,9 +112,9 @@ fn install_modpack(window: tauri::Window, id: String){ } } let java = get_java_version(mc_version); - java::install_java(java, window.clone()); + java::install_java(java, window.clone()).await; - let option_path = get_local_data_directory().join("prism").join("instances").join(get_modpack_name(id.clone())).join("instance.cfg"); + let option_path = get_local_data_directory().join("prism").join("instances").join(get_modpack_name(id.clone()).await).join("instance.cfg"); let mut option_file = File::open(option_path.clone()).unwrap(); let mut buf = String::new(); option_file.read_to_string(&mut buf); @@ -134,44 +137,46 @@ fn install_modpack(window: tauri::Window, id: String){ } #[tauri::command] -pub fn get_modpacks() -> Vec { - unsafe{ - static mut modpacks: Vec = Vec::new(); - if modpacks.is_empty() { - let mut buf = Cursor::new(vec![]); - ftp::ftp_retr(None, PathBuf::new().join("modpacks.json"), &mut buf, |_, _, _| return); - buf.rewind(); - let res = serde_json::from_reader(buf); - if !res.is_ok() { - let paths = fs::read_dir(get_local_data_directory().join("prism").join("instances")).unwrap(); - for path in paths { - let path = path.unwrap(); - if fs::metadata(path.path()).unwrap().is_file() { - continue; - } - let name = path.file_name().into_string().unwrap(); - if name.starts_with(".") { - continue; - } - - modpacks.push(ModpackEntry{name: name.clone(), id: name}) - } - return modpacks.clone() +pub async fn get_modpacks() -> Vec { + //unsafe{ + let mut modpacks: Vec = Vec::new(); + //if modpacks.is_empty() { + let mut buf = Cursor::new(vec![]); + //ftp::ftp_retr(None, PathBuf::new().join("modpacks.json"), &mut buf, |_, _, _| return); + https::download(None, format!("https://gitea.piwalker.net/fclauncher/modpacks.json"), &mut buf, format!("modpacks.json")).await; + buf.rewind(); + let res = serde_json::from_reader(buf); + if !res.is_ok() { + let paths = fs::read_dir(get_local_data_directory().join("prism").join("instances")).unwrap(); + for path in paths { + let path = path.unwrap(); + if fs::metadata(path.path()).unwrap().is_file() { + continue; } - let v: Value = res.unwrap(); - println!("{}", v[0]["name"]); - for pack in v.as_array().unwrap() { - modpacks.push(ModpackEntry{name: pack["name"].as_str().unwrap().to_string(), id: pack["id"].as_str().unwrap().to_string()}); + let name = path.file_name().into_string().unwrap(); + if name.starts_with(".") { + continue; } + + modpacks.push(ModpackEntry{name: name.clone(), id: name}) } - return modpacks.clone(); + return modpacks.clone() } + let v: Value = res.unwrap(); + println!("{}", v[0]["name"]); + for pack in v.as_array().unwrap() { + modpacks.push(ModpackEntry{name: pack["name"].as_str().unwrap().to_string(), id: pack["id"].as_str().unwrap().to_string()}); + } + //} + return modpacks.clone(); + //} } -fn get_versions(id: String) -> Result,Box> { +async fn get_versions(id: String) -> Result,Box> { let mut versions: Vec = Vec::new(); let mut buf = Cursor::new(vec![]); - ftp::ftp_retr(None, PathBuf::new().join(id).join("versions.json"), &mut buf, |_, _, _| return); + //ftp::ftp_retr(None, PathBuf::new().join(id).join("versions.json"), &mut buf, |_, _, _| return); + https::download(None, format!("https://gitea.piwalker.net/fclauncher/{}/versions.json", id.clone()), &mut buf, format!("{}/versions.json", id.clone())).await; buf.rewind(); let v: Value = serde_json::from_reader(buf)?; for version in v.as_array().unwrap() { diff --git a/FCLauncher/src-tauri/src/prism.rs b/FCLauncher/src-tauri/src/prism.rs index 6af00fc..29c006e 100644 --- a/FCLauncher/src-tauri/src/prism.rs +++ b/FCLauncher/src-tauri/src/prism.rs @@ -3,7 +3,7 @@ use flate2::read::GzDecoder; use tar::Archive; use tauri::api::file; -use crate::{ftp, java, system_dirs::{get_local_data_directory, get_prism_executable}, util}; +use crate::{https, java, system_dirs::{get_local_data_directory, get_prism_executable}, util}; pub fn check_prism() -> bool { @@ -16,12 +16,11 @@ pub async fn install_prism(window: tauri::Window){ if check_prism() { return; } - java::install_java(21, window.clone()); - let path = PathBuf::new().join("prism").join(format!("prism-{}",if env::consts::OS == "windows" {"win.zip"} else {"lin.tar.gz"})); - let size = ftp::ftp_get_size(path.clone()).unwrap(); + java::install_java(21, window.clone()).await; let mut buff = Cursor::new(vec![]); let mut total = 0; - ftp::ftp_retr(Some(window.clone()), path, &mut buff, |window: Option, data, size| util::download_callback("Prism Launcher".to_string(),window, data, size)).unwrap(); + //ftp::ftp_retr(Some(window.clone()), path, &mut buff, |window: Option, data, size| util::download_callback("Prism Launcher".to_string(),window, data, size)).unwrap(); + https::download(Some(window.clone()), format!("https://gitea.piwalker.net/fclauncher/prism/prism-{}", if env::consts::OS == "windows" {"win.zip"} else {"lin.tar.gz"}), &mut buff, format!("Prism Launcher")).await; std::fs::create_dir_all(get_local_data_directory().join("prism")).unwrap(); buff.rewind().unwrap(); if env::consts::OS != "windows" { @@ -37,7 +36,8 @@ pub async fn install_prism(window: tauri::Window){ } let mut buff = Cursor::new(vec![]); - ftp::ftp_retr(Some(window.clone()), PathBuf::new().join("prism").join("prismlauncher.cfg"), &mut buff, |_, _, _| return).unwrap(); + //ftp::ftp_retr(Some(window.clone()), PathBuf::new().join("prism").join("prismlauncher.cfg"), &mut buff, |_, _, _| return).unwrap(); + https::download(None, format!("https://gitea.piwalker.net/fclauncher/prism/prismlauncher.cfg"), &mut buff, format!("prismlauncher.cfg")).await; buff.rewind(); let mut file = File::create(get_local_data_directory().join("prism").join("prismlauncher.cfg")).unwrap(); loop {