From f52e777379364db69cbfdd26613f04189ed9a8ee Mon Sep 17 00:00:00 2001 From: Wyatt Verchere Date: Wed, 28 Jun 2023 09:58:58 -0700 Subject: [PATCH] Compiler improvements (#145) * Initial bug fixes * fix compile error on non-mac * Fix even more bugs * Fix more * fix more * fix build * fix build * working basic * removed zip * working functions * merge fixes * fixed loadintg bar bug * changed to one layer deep * forge version numbers * improvements + refactoring * renamed things to fit plugin * fixed bugs * removed println * overrides dont include mrpack * merge * fixes * fixes * fixed deletion * merge errors * force sync before export * removed testing * missed line * removed console log * mac error reverted * incoreclty named helper * added to new register method * review changes * minor changes * moved create pack * renamed function --------- Co-authored-by: Jai A --- theseus/src/api/jre.rs | 28 +- theseus/src/api/{pack.rs => pack/install.rs} | 266 ++------------ theseus/src/api/pack/install_from.rs | 276 +++++++++++++++ theseus/src/api/pack/mod.rs | 2 + theseus/src/api/profile.rs | 312 +++++++++++++++- theseus/src/api/profile_create.rs | 20 +- theseus/src/state/auth_task.rs | 6 +- theseus/src/state/profiles.rs | 71 ++-- theseus/src/state/settings.rs | 16 +- theseus/src/util/export.rs | 335 ------------------ theseus/src/util/jre.rs | 44 +-- theseus/src/util/mod.rs | 1 - theseus_gui/src-tauri/src/api/auth.rs | 24 +- theseus_gui/src-tauri/src/api/jre.rs | 44 ++- theseus_gui/src-tauri/src/api/logs.rs | 12 + theseus_gui/src-tauri/src/api/metadata.rs | 11 + theseus_gui/src-tauri/src/api/mod.rs | 10 - theseus_gui/src-tauri/src/api/pack.rs | 40 ++- theseus_gui/src-tauri/src/api/process.rs | 17 + theseus_gui/src-tauri/src/api/profile.rs | 59 ++- .../src-tauri/src/api/profile_create.rs | 12 +- theseus_gui/src-tauri/src/api/settings.rs | 10 +- theseus_gui/src-tauri/src/api/tags.rs | 13 + theseus_gui/src-tauri/src/api/utils.rs | 20 ++ theseus_gui/src-tauri/src/main.rs | 89 +---- theseus_gui/src/helpers/auth.js | 16 +- theseus_gui/src/helpers/jre.js | 30 +- theseus_gui/src/helpers/logs.js | 10 +- theseus_gui/src/helpers/metadata.js | 8 +- theseus_gui/src/helpers/pack.js | 35 +- theseus_gui/src/helpers/process.js | 18 +- theseus_gui/src/helpers/profile.js | 49 +-- theseus_gui/src/helpers/settings.js | 4 +- theseus_gui/src/helpers/state.js | 2 +- theseus_gui/src/helpers/tags.js | 12 +- theseus_gui/src/helpers/utils.js | 2 +- theseus_gui/src/mixins/macCssFix.js | 2 +- theseus_playground/src/main.rs | 11 +- 38 files changed, 1044 insertions(+), 893 deletions(-) rename theseus/src/api/{pack.rs => pack/install.rs} (64%) create mode 100644 theseus/src/api/pack/install_from.rs create mode 100644 theseus/src/api/pack/mod.rs delete mode 100644 theseus/src/util/export.rs diff --git a/theseus/src/api/jre.rs b/theseus/src/api/jre.rs index 4729b75a0..1d1d3d37f 100644 --- a/theseus/src/api/jre.rs +++ b/theseus/src/api/jre.rs @@ -5,9 +5,10 @@ use std::path::PathBuf; use crate::event::emit::{emit_loading, init_loading}; use crate::util::fetch::{fetch_advanced, fetch_json}; +use crate::util::jre::extract_java_majorminor_version; use crate::{ state::JavaGlobals, - util::jre::{self, extract_java_majorminor_version, JavaVersion}, + util::jre::{self, JavaVersion}, LoadingBarType, State, }; @@ -16,12 +17,15 @@ pub const JAVA_17_KEY: &str = "JAVA_17"; pub const JAVA_18PLUS_KEY: &str = "JAVA_18PLUS"; // Autodetect JavaSettings default +// Using the supplied JavaVersions, autodetects the default JavaSettings // Make a guess for what the default Java global settings should be -pub async fn autodetect_java_globals() -> crate::Result { - let mut java_8 = find_filtered_jres("1.8").await?; - let mut java_17 = find_filtered_jres("1.17").await?; - let mut java_18plus = find_filtered_jres("1.18").await?; - +// Since the JRE paths are passed in as args, this handles the logic for selection. Currently this just pops the last one found +// TODO: When tauri compiler issue is fixed, this can be be improved (ie: getting JREs in-function) +pub async fn autodetect_java_globals( + mut java_8: Vec, + mut java_17: Vec, + mut java_18plus: Vec, +) -> crate::Result { // Simply select last one found for initial guess let mut java_globals = JavaGlobals::new(); if let Some(jre) = java_8.pop() { @@ -38,18 +42,24 @@ pub async fn autodetect_java_globals() -> crate::Result { } // Searches for jres on the system given a java version (ex: 1.8, 1.17, 1.18) +// Allow higher allows for versions higher than the given version to be returned ('at least') pub async fn find_filtered_jres( version: &str, + jres: Vec, + allow_higher: bool, ) -> crate::Result> { let version = extract_java_majorminor_version(version)?; - let jres = jre::get_all_jre().await?; - + // Filter out JREs that are not 1.17 or higher Ok(jres .into_iter() .filter(|jre| { let jre_version = extract_java_majorminor_version(&jre.version); if let Ok(jre_version) = jre_version { - jre_version >= version + if allow_higher { + jre_version >= version + } else { + jre_version == version + } } else { false } diff --git a/theseus/src/api/pack.rs b/theseus/src/api/pack/install.rs similarity index 64% rename from theseus/src/api/pack.rs rename to theseus/src/api/pack/install.rs index a9152ca5d..097e2ea9c 100644 --- a/theseus/src/api/pack.rs +++ b/theseus/src/api/pack/install.rs @@ -1,247 +1,53 @@ -use crate::config::MODRINTH_API_URL; use crate::data::ModLoader; use crate::event::emit::{ - emit_loading, init_loading, init_or_edit_loading, - loading_try_for_each_concurrent, -}; -use crate::event::{LoadingBarId, LoadingBarType}; -use crate::state::{ - LinkedData, ModrinthProject, ModrinthVersion, ProfileInstallStage, SideType, -}; -use crate::util::fetch::{ - fetch, fetch_advanced, fetch_json, fetch_mirrors, write, write_cached_icon, + emit_loading, init_or_edit_loading, loading_try_for_each_concurrent, }; +use crate::event::LoadingBarType; +use crate::pack::install_from::{EnvType, PackFile, PackFileHash}; +use crate::state::{LinkedData, ProfileInstallStage, SideType}; +use crate::util::fetch::{fetch_mirrors, write}; use crate::State; use async_zip::tokio::read::seek::ZipFileReader; -use reqwest::Method; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; + use std::io::Cursor; use std::path::{Component, PathBuf}; -use tokio::fs; - -#[derive(Serialize, Deserialize, Eq, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct PackFormat { - pub game: String, - pub format_version: i32, - pub version_id: String, - pub name: String, - pub summary: Option, - pub files: Vec, - pub dependencies: HashMap, -} - -#[derive(Serialize, Deserialize, Eq, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct PackFile { - pub path: String, - pub hashes: HashMap, - pub env: Option>, - pub downloads: Vec, - pub file_size: u32, -} - -#[derive(Serialize, Deserialize, Eq, PartialEq, Hash)] -#[serde(rename_all = "camelCase", from = "String")] -pub enum PackFileHash { - Sha1, - Sha512, - Unknown(String), -} -impl From for PackFileHash { - fn from(s: String) -> Self { - return match s.as_str() { - "sha1" => PackFileHash::Sha1, - "sha512" => PackFileHash::Sha512, - _ => PackFileHash::Unknown(s), - }; - } -} - -#[derive(Serialize, Deserialize, Eq, PartialEq, Hash)] -#[serde(rename_all = "camelCase")] -pub enum EnvType { - Client, - Server, -} - -#[derive(Serialize, Deserialize, Clone, Hash, PartialEq, Eq)] -#[serde(rename_all = "kebab-case")] -pub enum PackDependency { - Forge, - FabricLoader, - QuiltLoader, - Minecraft, -} +use super::install_from::{ + generate_pack_from_file, generate_pack_from_version_id, + CreatePackDescription, CreatePackLocation, PackDependency, PackFormat, +}; -#[tracing::instrument] #[theseus_macros::debug_pin] -pub async fn install_pack_from_version_id( - project_id: String, - version_id: String, - title: String, - icon_url: Option, +pub async fn install_pack( + location: CreatePackLocation, + profile: PathBuf, ) -> crate::Result { - let state = State::get().await?; - let profile = crate::api::profile_create::profile_create( - title.clone(), - "1.19.4".to_string(), - ModLoader::Vanilla, - None, - None, - icon_url.clone(), - Some(LinkedData { - project_id: Some(project_id), - version_id: Some(version_id.clone()), - }), - Some(true), - ) - .await?; - - let loading_bar = init_loading( - LoadingBarType::PackFileDownload { - profile_path: profile.clone(), - pack_name: title, - icon: icon_url, - pack_version: version_id.clone(), - }, - 100.0, - "Downloading pack file", - ) - .await?; - - emit_loading(&loading_bar, 0.0, Some("Fetching version")).await?; - let version: ModrinthVersion = fetch_json( - Method::GET, - &format!("{}version/{}", MODRINTH_API_URL, version_id), - None, - None, - &state.fetch_semaphore, - ) - .await?; - emit_loading(&loading_bar, 10.0, None).await?; - - let (url, hash) = - if let Some(file) = version.files.iter().find(|x| x.primary) { - Some((file.url.clone(), file.hashes.get("sha1"))) - } else { - version - .files - .first() - .map(|file| (file.url.clone(), file.hashes.get("sha1"))) - } - .ok_or_else(|| { - crate::ErrorKind::InputError( - "Specified version has no files".to_string(), + // Get file from description + let description: CreatePackDescription = match location { + CreatePackLocation::FromVersionId { + project_id, + version_id, + title, + icon_url, + } => { + generate_pack_from_version_id( + project_id, version_id, title, icon_url, profile, ) - })?; - - let file = fetch_advanced( - Method::GET, - &url, - hash.map(|x| &**x), - None, - None, - Some((&loading_bar, 70.0)), - &state.fetch_semaphore, - ) - .await?; - emit_loading(&loading_bar, 0.0, Some("Fetching project metadata")).await?; - - let project: ModrinthProject = fetch_json( - Method::GET, - &format!("{}project/{}", MODRINTH_API_URL, version.project_id), - None, - None, - &state.fetch_semaphore, - ) - .await?; - - emit_loading(&loading_bar, 10.0, Some("Retrieving icon")).await?; - let icon = if let Some(icon_url) = project.icon_url { - let state = State::get().await?; - let icon_bytes = fetch(&icon_url, None, &state.fetch_semaphore).await?; - - let filename = icon_url.rsplit('/').next(); - - if let Some(filename) = filename { - Some( - write_cached_icon( - filename, - &state.directories.caches_dir(), - icon_bytes, - &state.io_semaphore, - ) - .await?, - ) - } else { - None + .await? + } + CreatePackLocation::FromFile { path } => { + generate_pack_from_file(path, profile).await? } - } else { - None }; - emit_loading(&loading_bar, 10.0, None).await?; - - install_pack( - file, - icon, - Some(project.title), - Some(version.project_id), - Some(version.id), - Some(loading_bar), - profile, - ) - .await -} -#[tracing::instrument] -#[theseus_macros::debug_pin] -pub async fn install_pack_from_file(path: PathBuf) -> crate::Result { - let file = fs::read(&path).await?; - - let file_name = path - .file_name() - .unwrap_or_default() - .to_string_lossy() - .to_string(); - - let profile = crate::api::profile_create::profile_create( - file_name, - "1.19.4".to_string(), - ModLoader::Vanilla, - None, - None, - None, - None, - Some(true), - ) - .await?; - - install_pack( - bytes::Bytes::from(file), - None, - None, - None, - None, - None, - profile, - ) - .await -} + let file = description.file; + let icon = description.icon; + let override_title = description.override_title; + let project_id = description.project_id; + let version_id = description.version_id; + let existing_loading_bar = description.existing_loading_bar; + let profile = description.profile; -#[tracing::instrument(skip(file))] -#[theseus_macros::debug_pin] -async fn install_pack( - file: bytes::Bytes, - icon: Option, - override_title: Option, - project_id: Option, - version_id: Option, - existing_loading_bar: Option, - profile: PathBuf, -) -> crate::Result { let state = &State::get().await?; let result = async { @@ -299,9 +105,7 @@ async fn install_pack( mod_loader = Some(ModLoader::Quilt); loader_version = Some(value); } - PackDependency::Minecraft => { - game_version = Some(value.clone()) - } + PackDependency::Minecraft => game_version = Some(value), } } diff --git a/theseus/src/api/pack/install_from.rs b/theseus/src/api/pack/install_from.rs new file mode 100644 index 000000000..419bbf1f6 --- /dev/null +++ b/theseus/src/api/pack/install_from.rs @@ -0,0 +1,276 @@ +use crate::config::MODRINTH_API_URL; +use crate::data::ModLoader; +use crate::event::emit::{emit_loading, init_loading}; +use crate::event::{LoadingBarId, LoadingBarType}; +use crate::state::{LinkedData, ModrinthProject, ModrinthVersion, SideType}; +use crate::util::fetch::{ + fetch, fetch_advanced, fetch_json, write_cached_icon, +}; +use crate::State; + +use reqwest::Method; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use std::path::PathBuf; +use tokio::fs; + +#[derive(Serialize, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct PackFormat { + pub game: String, + pub format_version: i32, + pub version_id: String, + pub name: String, + pub summary: Option, + pub files: Vec, + pub dependencies: HashMap, +} + +#[derive(Serialize, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct PackFile { + pub path: String, + pub hashes: HashMap, + pub env: Option>, + pub downloads: Vec, + pub file_size: u32, +} + +#[derive(Serialize, Deserialize, Eq, PartialEq, Hash)] +#[serde(rename_all = "camelCase", from = "String")] +pub enum PackFileHash { + Sha1, + Sha512, + Unknown(String), +} + +impl From for PackFileHash { + fn from(s: String) -> Self { + return match s.as_str() { + "sha1" => PackFileHash::Sha1, + "sha512" => PackFileHash::Sha512, + _ => PackFileHash::Unknown(s), + }; + } +} + +#[derive(Serialize, Deserialize, Eq, PartialEq, Hash)] +#[serde(rename_all = "camelCase")] +pub enum EnvType { + Client, + Server, +} + +#[derive(Serialize, Deserialize, Clone, Hash, PartialEq, Eq)] +#[serde(rename_all = "kebab-case")] +pub enum PackDependency { + Forge, + FabricLoader, + QuiltLoader, + Minecraft, +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase", tag = "type")] +pub enum CreatePackLocation { + FromVersionId { + project_id: String, + version_id: String, + title: String, + icon_url: Option, + }, + FromFile { + path: PathBuf, + }, +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreatePackProfile { + pub name: String, // the name of the profile, and relative path + pub game_version: String, // the game version of the profile + pub modloader: ModLoader, // the modloader to use + pub loader_version: Option, // the modloader version to use, set to "latest", "stable", or the ID of your chosen loader. defaults to latest + pub icon: Option, // the icon for the profile + pub icon_url: Option, // the URL icon for a profile (ONLY USED FOR TEMPORARY PROFILES) + pub linked_data: Option, // the linked project ID (mainly for modpacks)- used for updating + pub skip_install_profile: Option, +} + +pub struct CreatePackDescription { + pub file: bytes::Bytes, + pub icon: Option, + pub override_title: Option, + pub project_id: Option, + pub version_id: Option, + pub existing_loading_bar: Option, + pub profile: PathBuf, +} + +pub fn get_profile_from_pack( + location: CreatePackLocation, +) -> CreatePackProfile { + match location { + CreatePackLocation::FromVersionId { + project_id, + version_id, + title, + icon_url, + } => CreatePackProfile { + name: title, + game_version: "1.19.4".to_string(), + modloader: ModLoader::Vanilla, + loader_version: None, + icon: None, + icon_url, + linked_data: Some(LinkedData { + project_id: Some(project_id), + version_id: Some(version_id), + }), + skip_install_profile: Some(true), + }, + CreatePackLocation::FromFile { path } => { + let file_name = path + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(); + + CreatePackProfile { + name: file_name, + game_version: "1.19.4".to_string(), + modloader: ModLoader::Vanilla, + loader_version: None, + icon: None, + icon_url: None, + linked_data: None, + skip_install_profile: Some(true), + } + } + } +} + +#[tracing::instrument] +#[theseus_macros::debug_pin] +pub async fn generate_pack_from_version_id( + project_id: String, + version_id: String, + title: String, + icon_url: Option, + profile: PathBuf, +) -> crate::Result { + let state = State::get().await?; + + let loading_bar = init_loading( + LoadingBarType::PackFileDownload { + profile_path: profile.clone(), + pack_name: title, + icon: icon_url, + pack_version: version_id.clone(), + }, + 100.0, + "Downloading pack file", + ) + .await?; + + emit_loading(&loading_bar, 0.0, Some("Fetching version")).await?; + let version: ModrinthVersion = fetch_json( + Method::GET, + &format!("{}version/{}", MODRINTH_API_URL, version_id), + None, + None, + &state.fetch_semaphore, + ) + .await?; + emit_loading(&loading_bar, 10.0, None).await?; + + let (url, hash) = + if let Some(file) = version.files.iter().find(|x| x.primary) { + Some((file.url.clone(), file.hashes.get("sha1"))) + } else { + version + .files + .first() + .map(|file| (file.url.clone(), file.hashes.get("sha1"))) + } + .ok_or_else(|| { + crate::ErrorKind::InputError( + "Specified version has no files".to_string(), + ) + })?; + + let file = fetch_advanced( + Method::GET, + &url, + hash.map(|x| &**x), + None, + None, + Some((&loading_bar, 70.0)), + &state.fetch_semaphore, + ) + .await?; + emit_loading(&loading_bar, 0.0, Some("Fetching project metadata")).await?; + + let project: ModrinthProject = fetch_json( + Method::GET, + &format!("{}project/{}", MODRINTH_API_URL, version.project_id), + None, + None, + &state.fetch_semaphore, + ) + .await?; + + emit_loading(&loading_bar, 10.0, Some("Retrieving icon")).await?; + let icon = if let Some(icon_url) = project.icon_url { + let state = State::get().await?; + let icon_bytes = fetch(&icon_url, None, &state.fetch_semaphore).await?; + + let filename = icon_url.rsplit('/').next(); + + if let Some(filename) = filename { + Some( + write_cached_icon( + filename, + &state.directories.caches_dir(), + icon_bytes, + &state.io_semaphore, + ) + .await?, + ) + } else { + None + } + } else { + None + }; + emit_loading(&loading_bar, 10.0, None).await?; + + Ok(CreatePackDescription { + file, + icon, + override_title: None, + project_id: Some(project_id), + version_id: Some(version_id), + existing_loading_bar: Some(loading_bar), + profile, + }) +} + +#[tracing::instrument] +#[theseus_macros::debug_pin] +pub async fn generate_pack_from_file( + path: PathBuf, + profile: PathBuf, +) -> crate::Result { + let file = fs::read(&path).await?; + Ok(CreatePackDescription { + file: bytes::Bytes::from(file), + icon: None, + override_title: None, + project_id: None, + version_id: None, + existing_loading_bar: None, + profile, + }) +} diff --git a/theseus/src/api/pack/mod.rs b/theseus/src/api/pack/mod.rs new file mode 100644 index 000000000..9a7c1d341 --- /dev/null +++ b/theseus/src/api/pack/mod.rs @@ -0,0 +1,2 @@ +pub mod install; +pub mod install_from; diff --git a/theseus/src/api/profile.rs b/theseus/src/api/profile.rs index 196f0b636..e4de2b372 100644 --- a/theseus/src/api/profile.rs +++ b/theseus/src/api/profile.rs @@ -1,9 +1,14 @@ //! Theseus profile management interface -use crate::event::emit::{init_loading, loading_try_for_each_concurrent}; +use crate::event::emit::{ + emit_loading, init_loading, loading_try_for_each_concurrent, +}; use crate::event::LoadingBarType; +use crate::pack::install_from::{ + EnvType, PackDependency, PackFile, PackFileHash, PackFormat, +}; use crate::prelude::JavaVersion; use crate::state::ProjectMetadata; -use crate::util::export; + use crate::{ auth::{self, refresh}, event::{emit::emit_profile, ProfilePayloadType}, @@ -13,13 +18,20 @@ pub use crate::{ state::{JavaSettings, Profile}, State, }; +use async_zip::tokio::write::ZipFileWriter; +use async_zip::{Compression, ZipEntryBuilder}; use std::collections::HashMap; use std::{ future::Future, path::{Path, PathBuf}, sync::Arc, }; -use tokio::{fs, process::Command, sync::RwLock}; +use tokio::io::AsyncReadExt; +use tokio::{ + fs::{self, File}, + process::Command, + sync::RwLock, +}; /// Remove a profile #[tracing::instrument] @@ -494,6 +506,7 @@ pub async fn remove_project( /// Exports the profile to a Modrinth-formatted .mrpack file // Version ID of uploaded version (ie 1.1.5), not the unique identifying ID of the version (nvrqJg44) #[tracing::instrument(skip_all)] +#[theseus_macros::debug_pin] pub async fn export_mrpack( profile_path: &Path, export_path: PathBuf, @@ -502,22 +515,104 @@ pub async fn export_mrpack( ) -> crate::Result<()> { let state = State::get().await?; let io_semaphore = state.io_semaphore.0.read().await; - let permit: tokio::sync::SemaphorePermit = io_semaphore.acquire().await?; + let _permit: tokio::sync::SemaphorePermit = io_semaphore.acquire().await?; let profile = get(profile_path, None).await?.ok_or_else(|| { crate::ErrorKind::OtherError(format!( "Tried to export a nonexistent or unloaded profile at path {}!", profile_path.display() )) })?; - export::export_mrpack( - &profile, - &export_path, - version_id.unwrap_or("1.0.0".to_string()), - included_overrides, - true, - &permit, + + let profile_base_path = &profile.path; + + let mut file = File::create(export_path).await?; + let mut writer = ZipFileWriter::new(&mut file); + + // Create mrpack json configuration file + let version_id = version_id.unwrap_or("1.0.0".to_string()); + let packfile = create_mrpack_json(&profile, version_id)?; + let modrinth_path_list = get_modrinth_pack_list(&packfile); + + // Build vec of all files in the folder + let mut path_list = Vec::new(); + build_folder(profile_base_path, &mut path_list).await?; + + // Initialize loading bar + let loading_bar = init_loading( + LoadingBarType::ZipExtract { + profile_path: profile.path.to_path_buf(), + profile_name: profile.metadata.name.clone(), + }, + path_list.len() as f64, + "Exporting profile to .mrpack", ) .await?; + + // Iterate over every file in the folder + // Every file that is NOT in the config file is added to the zip, in overrides + for path in path_list { + emit_loading(&loading_bar, 1.0, None).await?; + + // Get local path of file, relative to profile folder + let relative_path = path.strip_prefix(profile_base_path)?; + + // Get highest level folder pair ('a/b' in 'a/b/c', 'a' in 'a') + // We only go one layer deep for the sake of not having a huge list of overrides + let topmost_two = relative_path + .iter() + .take(2) + .map(|os| os.to_string_lossy().to_string()) + .collect::>(); + + // a,b => a/b + // a => a + let topmost = match topmost_two.len() { + 2 => topmost_two.join("/"), + 1 => topmost_two[0].clone(), + _ => { + return Err(crate::ErrorKind::OtherError( + "No topmost folder found".to_string(), + ) + .into()) + } + }; + + if !included_overrides.contains(&topmost) { + continue; + } + + let relative_path: std::borrow::Cow = + relative_path.to_string_lossy(); + let relative_path = relative_path.replace('\\', "/"); + let relative_path = relative_path.trim_start_matches('/').to_string(); + + if modrinth_path_list.contains(&relative_path) { + continue; + } + + // File is not in the config file, add it to the .mrpack zip + if path.is_file() { + let mut file = File::open(&path).await?; + let mut data = Vec::new(); + file.read_to_end(&mut data).await?; + let builder = ZipEntryBuilder::new( + format!("overrides/{relative_path}"), + Compression::Deflate, + ); + writer.write_entry_whole(builder, &data).await?; + } + } + + // Add modrinth json to the zip + let data = serde_json::to_vec_pretty(&packfile)?; + let builder = ZipEntryBuilder::new( + "modrinth.index.json".to_string(), + Compression::Deflate, + ); + writer.write_entry_whole(builder, &data).await?; + + writer.close().await?; + Ok(()) } @@ -532,7 +627,40 @@ pub async fn export_mrpack( pub async fn get_potential_override_folders( profile_path: PathBuf, ) -> crate::Result> { - export::get_potential_override_folders(profile_path).await + // First, get a dummy mrpack json for the files within + let profile: Profile = + get(&profile_path, None).await?.ok_or_else(|| { + crate::ErrorKind::OtherError(format!( + "Tried to export a nonexistent or unloaded profile at path {}!", + profile_path.display() + )) + })?; + let mrpack = create_mrpack_json(&profile, "0".to_string())?; + let mrpack_files = get_modrinth_pack_list(&mrpack); + + let mut path_list: Vec = Vec::new(); + let mut read_dir = fs::read_dir(&profile_path).await?; + while let Some(entry) = read_dir.next_entry().await? { + let path: PathBuf = entry.path(); + if path.is_dir() { + // Two layers of files/folders if its a folder + let mut read_dir = fs::read_dir(&path).await?; + while let Some(entry) = read_dir.next_entry().await? { + let path: PathBuf = entry.path(); + let name = path.strip_prefix(&profile_path)?.to_path_buf(); + if !mrpack_files.contains(&name.to_string_lossy().to_string()) { + path_list.push(name); + } + } + } else { + // One layer of files/folders if its a file + let name = path.strip_prefix(&profile_path)?.to_path_buf(); + if !mrpack_files.contains(&name.to_string_lossy().to_string()) { + path_list.push(name); + } + } + } + Ok(path_list) } /// Run Minecraft using a profile and the default credentials, logged in credentials, @@ -648,3 +776,163 @@ pub async fn run_credentials( .await?; Ok(mc_process) } + +fn get_modrinth_pack_list(packfile: &PackFormat) -> Vec { + packfile + .files + .iter() + .map(|f| { + let path = PathBuf::from(f.path.clone()); + let name = path.to_string_lossy(); + let name = name.replace('\\', "/"); + name.trim_start_matches('/').to_string() + }) + .collect::>() +} + +/// Creates a json configuration for a .mrpack zipped file +// Version ID of uploaded version (ie 1.1.5), not the unique identifying ID of the version (nvrqJg44) +#[tracing::instrument(skip_all)] +pub fn create_mrpack_json( + profile: &Profile, + version_id: String, +) -> crate::Result { + // Add loader version to dependencies + let mut dependencies = HashMap::new(); + match ( + profile.metadata.loader, + profile.metadata.loader_version.clone(), + ) { + (crate::prelude::ModLoader::Forge, Some(v)) => { + dependencies.insert(PackDependency::Forge, v.id) + } + (crate::prelude::ModLoader::Fabric, Some(v)) => { + dependencies.insert(PackDependency::FabricLoader, v.id) + } + (crate::prelude::ModLoader::Quilt, Some(v)) => { + dependencies.insert(PackDependency::QuiltLoader, v.id) + } + (crate::prelude::ModLoader::Vanilla, _) => None, + _ => { + return Err(crate::ErrorKind::OtherError( + "Loader version mismatch".to_string(), + ) + .into()) + } + }; + dependencies.insert( + PackDependency::Minecraft, + profile.metadata.game_version.clone(), + ); + + // Converts a HashMap to a HashMap + // But the values are sanitized to only include the version number + let dependencies = dependencies + .into_iter() + .map(|(k, v)| (k, sanitize_loader_version_string(&v).to_string())) + .collect::>(); + + let base_path = &profile.path; + let files: Result, crate::ErrorKind> = profile + .projects + .iter() + .filter_map(|(mod_path, project)| { + let path = match mod_path.strip_prefix(base_path) { + Ok(path) => path.to_string_lossy().to_string(), + Err(e) => { + return Some(Err(e.into())); + } + }; + + // Only Modrinth projects have a modrinth metadata field for the modrinth.json + Some(Ok(match project.metadata { + crate::prelude::ProjectMetadata::Modrinth { + ref project, + ref version, + .. + } => { + let mut env = HashMap::new(); + env.insert(EnvType::Client, project.client_side.clone()); + env.insert(EnvType::Server, project.server_side.clone()); + + let primary_file = if let Some(primary_file) = + version.files.first() + { + primary_file + } else { + return Some(Err(crate::ErrorKind::OtherError( + format!("No primary file found for mod at: {path}"), + ))); + }; + + let file_size = primary_file.size; + let downloads = vec![primary_file.url.clone()]; + let hashes = primary_file + .hashes + .clone() + .into_iter() + .map(|(h1, h2)| (PackFileHash::from(h1), h2)) + .collect(); + + PackFile { + path, + hashes, + env: Some(env), + downloads, + file_size, + } + } + // Inferred files are skipped for the modrinth.json + crate::prelude::ProjectMetadata::Inferred { .. } => { + return None + } + // Unknown projects are skipped for the modrinth.json + crate::prelude::ProjectMetadata::Unknown => return None, + })) + }) + .collect(); + let files = files?; + + Ok(PackFormat { + game: "minecraft".to_string(), + format_version: 1, + version_id, + name: profile.metadata.name.clone(), + summary: None, + files, + dependencies, + }) +} + +fn sanitize_loader_version_string(s: &str) -> &str { + // Split on '-' + // If two or more, take the second + // If one, take the first + // If none, take the whole thing + let mut split: std::str::Split<'_, char> = s.split('-'); + match split.next() { + Some(first) => match split.next() { + Some(second) => second, + None => first, + }, + None => s, + } +} + +// Given a folder path, populate a Vec of all the files in the folder, recursively +#[async_recursion::async_recursion] +pub async fn build_folder( + path: &Path, + path_list: &mut Vec, +) -> crate::Result<()> { + let mut read_dir = fs::read_dir(path).await?; + while let Some(entry) = read_dir.next_entry().await? { + let path = entry.path(); + if path.is_dir() { + build_folder(&path, path_list).await?; + } else { + path_list.push(path); + } + } + Ok(()) +} diff --git a/theseus/src/api/profile_create.rs b/theseus/src/api/profile_create.rs index 87840fdf7..88a802b61 100644 --- a/theseus/src/api/profile_create.rs +++ b/theseus/src/api/profile_create.rs @@ -11,31 +11,13 @@ pub use crate::{ use daedalus::modded::LoaderVersion; use dunce::canonicalize; use futures::prelude::*; + use std::path::PathBuf; use tokio::fs; use tokio_stream::wrappers::ReadDirStream; use tracing::{info, trace}; use uuid::Uuid; -const DEFAULT_NAME: &str = "Untitled Instance"; - -// Generic basic profile creation tool. -// Creates an essentially empty dummy profile with profile_create -#[tracing::instrument] -pub async fn profile_create_empty() -> crate::Result { - profile_create( - String::from(DEFAULT_NAME), // the name/path of the profile - String::from("1.19.2"), // the game version of the profile - ModLoader::Vanilla, // the modloader to use - None, // the modloader version to use, set to "latest", "stable", or the ID of your chosen loader - None, // the icon for the profile - None, - None, - None, - ) - .await -} - // Creates a profile at the given filepath and adds it to the in-memory state // Returns filepath at which it can be accessed in the State #[tracing::instrument] diff --git a/theseus/src/state/auth_task.rs b/theseus/src/state/auth_task.rs index 7c3250cd3..a87509fb4 100644 --- a/theseus/src/state/auth_task.rs +++ b/theseus/src/state/auth_task.rs @@ -1,5 +1,5 @@ use crate::launcher::auth::Credentials; -use std::mem; + use tokio::task::JoinHandle; // Authentication task @@ -43,7 +43,7 @@ impl AuthTask { let state = crate::State::get().await?; let mut write = state.auth_flow.write().await; - mem::replace(&mut write.0, None) + write.0.take() }; // Waits for the task to complete, and returns the credentials @@ -61,7 +61,7 @@ impl AuthTask { let state = crate::State::get().await?; let mut write = state.auth_flow.write().await; - mem::replace(&mut write.0, None) + write.0.take() }; if let Some(task) = task { // Cancels the task diff --git a/theseus/src/state/profiles.rs b/theseus/src/state/profiles.rs index 6136c4f30..97001fc8f 100644 --- a/theseus/src/state/profiles.rs +++ b/theseus/src/state/profiles.rs @@ -224,7 +224,40 @@ impl Profile { pub fn sync_projects_task(path: PathBuf) { tokio::task::spawn(async move { - let res = Self::sync_projects_inner(path).await; + let res = async { + let state = State::get().await?; + let profile = crate::api::profile::get(&path, None).await?; + + if let Some(profile) = profile { + let paths = profile.get_profile_project_paths()?; + + let projects = crate::state::infer_data_from_files( + profile.clone(), + paths, + state.directories.caches_dir(), + &state.io_semaphore, + &state.fetch_semaphore, + ) + .await?; + + let mut new_profiles = state.profiles.write().await; + if let Some(profile) = new_profiles.0.get_mut(&path) { + profile.projects = projects; + } + emit_profile( + profile.uuid, + profile.path, + &profile.metadata.name, + ProfilePayloadType::Synced, + ) + .await?; + } else { + tracing::warn!( + "Unable to fetch single profile projects: path {path:?} invalid", + ); + } + Ok::<(), crate::Error>(()) + }.await; match res { Ok(()) => {} Err(err) => { @@ -236,42 +269,6 @@ impl Profile { }); } - pub async fn sync_projects_inner(path: PathBuf) -> crate::Result<()> { - let state = State::get().await?; - let profile = crate::api::profile::get(&path, None).await?; - - if let Some(profile) = profile { - let paths = profile.get_profile_project_paths()?; - - let projects = crate::state::infer_data_from_files( - profile.clone(), - paths, - state.directories.caches_dir(), - &state.io_semaphore, - &state.fetch_semaphore, - ) - .await?; - - let mut new_profiles = state.profiles.write().await; - if let Some(profile) = new_profiles.0.get_mut(&path) { - profile.projects = projects; - } - - emit_profile( - profile.uuid, - profile.path, - &profile.metadata.name, - ProfilePayloadType::Synced, - ) - .await?; - } else { - tracing::warn!( - "Unable to fetch single profile projects: path {path:?} invalid", - ); - } - Ok::<(), crate::Error>(()) - } - pub fn get_profile_project_paths(&self) -> crate::Result> { let mut files = Vec::new(); let mut read_paths = |path: &str| { diff --git a/theseus/src/state/settings.rs b/theseus/src/state/settings.rs index b99720bcf..eaa295b1e 100644 --- a/theseus/src/state/settings.rs +++ b/theseus/src/state/settings.rs @@ -1,5 +1,8 @@ //! Theseus settings file -use crate::{jre, State}; +use crate::{ + jre::{self, autodetect_java_globals, find_filtered_jres}, + State, +}; use serde::{Deserialize, Serialize}; use std::path::Path; use tokio::fs; @@ -89,7 +92,16 @@ impl Settings { if settings_read.java_globals.count() == 0 { drop(settings_read); - let java_globals = jre::autodetect_java_globals().await?; + let jres = jre::get_all_jre().await?; + let java_8 = + find_filtered_jres("1.8", jres.clone(), false).await?; + let java_17 = + find_filtered_jres("1.17", jres.clone(), false).await?; + let java_18plus = + find_filtered_jres("1.18", jres.clone(), true).await?; + let java_globals = + autodetect_java_globals(java_8, java_17, java_18plus) + .await?; state.settings.write().await.java_globals = java_globals; } diff --git a/theseus/src/util/export.rs b/theseus/src/util/export.rs deleted file mode 100644 index 7fc0a6cde..000000000 --- a/theseus/src/util/export.rs +++ /dev/null @@ -1,335 +0,0 @@ -//! Functions for fetching infromation from the Internet -use crate::event::emit::{emit_loading, init_loading}; -use crate::pack::{ - EnvType, PackDependency, PackFile, PackFileHash, PackFormat, -}; -use crate::process::Profile; -use crate::profile::get; -use crate::LoadingBarType; -use async_zip::tokio::write::ZipFileWriter; -use async_zip::{Compression, ZipEntryBuilder}; -use std::collections::HashMap; -use std::path::{Path, PathBuf}; -use tokio::fs::{self, File}; -use tokio::io::AsyncReadExt; -use tokio::sync::SemaphorePermit; - -/// Creates a .mrpack (Modrinth zip file) for a given modpack -// Version ID of uploaded version (ie 1.1.5), not the unique identifying ID of the version (nvrqJg44) -#[tracing::instrument(skip_all)] -#[theseus_macros::debug_pin] -pub async fn export_mrpack( - profile: &Profile, - export_location: &Path, - version_id: String, - included_overrides: Vec, // which folders to include in the overrides - loading_bar: bool, - _semaphore: &SemaphorePermit<'_>, -) -> crate::Result<()> { - let profile_base_path = &profile.path; - - let mut file = File::create(export_location).await?; - let mut writer = ZipFileWriter::new(&mut file); - - // Create mrpack json configuration file - let packfile = create_mrpack_json(profile, version_id)?; - let modrinth_path_list = get_modrinth_pack_list(&packfile); - - // Build vec of all files in the folder - let mut path_list = Vec::new(); - build_folder(profile_base_path, &mut path_list).await?; - - // Initialize loading bar - let loading_bar = if loading_bar { - Some( - init_loading( - LoadingBarType::ZipExtract { - profile_path: profile.path.to_path_buf(), - profile_name: profile.metadata.name.clone(), - }, - path_list.len() as f64, - "Exporting profile to .mrpack", - ) - .await?, - ) - } else { - None - }; - - // Iterate over every file in the folder - // Every file that is NOT in the config file is added to the zip, in overrides - for path in path_list { - if let Some(ref loading_bar) = loading_bar { - emit_loading(loading_bar, 1.0, None).await?; - } - - // Get local path of file, relative to profile folder - let relative_path = path.strip_prefix(profile_base_path)?; - - // Get highest level folder pair ('a/b' in 'a/b/c', 'a' in 'a') - // We only go one layer deep for the sake of not having a huge list of overrides - let topmost_two = relative_path - .iter() - .take(2) - .map(|os| os.to_string_lossy().to_string()) - .collect::>(); - - // a,b => a/b - // a => a - let topmost = match topmost_two.len() { - 2 => topmost_two.join("/"), - 1 => topmost_two[0].clone(), - _ => { - return Err(crate::ErrorKind::OtherError( - "No topmost folder found".to_string(), - ) - .into()) - } - }; - - if !included_overrides.contains(&topmost) { - continue; - } - - let relative_path: std::borrow::Cow = - relative_path.to_string_lossy(); - let relative_path = relative_path.replace('\\', "/"); - let relative_path = relative_path.trim_start_matches('/').to_string(); - - if modrinth_path_list.contains(&relative_path) { - continue; - } - - // File is not in the config file, add it to the .mrpack zip - if path.is_file() { - let mut file = File::open(&path).await?; - let mut data = Vec::new(); - file.read_to_end(&mut data).await?; - let builder = ZipEntryBuilder::new( - format!("overrides/{relative_path}"), - Compression::Deflate, - ); - writer.write_entry_whole(builder, &data).await?; - } - } - - // Add modrinth json to the zip - let data = serde_json::to_vec_pretty(&packfile)?; - let builder = ZipEntryBuilder::new( - "modrinth.index.json".to_string(), - Compression::Deflate, - ); - writer.write_entry_whole(builder, &data).await?; - - writer.close().await?; - Ok(()) -} - -fn get_modrinth_pack_list(packfile: &PackFormat) -> Vec { - packfile - .files - .iter() - .map(|f| { - let path = PathBuf::from(f.path.clone()); - let name = path.to_string_lossy(); - let name = name.replace('\\', "/"); - name.trim_start_matches('/').to_string() - }) - .collect::>() -} - -/// Creates a json configuration for a .mrpack zipped file -// Version ID of uploaded version (ie 1.1.5), not the unique identifying ID of the version (nvrqJg44) -#[tracing::instrument(skip_all)] -pub fn create_mrpack_json( - profile: &Profile, - version_id: String, -) -> crate::Result { - // Add loader version to dependencies - let mut dependencies = HashMap::new(); - match ( - profile.metadata.loader, - profile.metadata.loader_version.clone(), - ) { - (crate::prelude::ModLoader::Forge, Some(v)) => { - dependencies.insert(PackDependency::Forge, v.id) - } - (crate::prelude::ModLoader::Fabric, Some(v)) => { - dependencies.insert(PackDependency::FabricLoader, v.id) - } - (crate::prelude::ModLoader::Quilt, Some(v)) => { - dependencies.insert(PackDependency::QuiltLoader, v.id) - } - (crate::prelude::ModLoader::Vanilla, _) => None, - _ => { - return Err(crate::ErrorKind::OtherError( - "Loader version mismatch".to_string(), - ) - .into()) - } - }; - dependencies.insert( - PackDependency::Minecraft, - profile.metadata.game_version.clone(), - ); - - // Converts a HashMap to a HashMap - // But the values are sanitized to only include the version number - let dependencies = dependencies - .into_iter() - .map(|(k, v)| (k, sanitize_loader_version_string(&v).to_string())) - .collect::>(); - - let base_path = &profile.path; - let files: Result, crate::ErrorKind> = profile - .projects - .iter() - .filter_map(|(mod_path, project)| { - let path = match mod_path.strip_prefix(base_path) { - Ok(path) => path.to_string_lossy().to_string(), - Err(e) => { - return Some(Err(e.into())); - } - }; - - // Only Modrinth projects have a modrinth metadata field for the modrinth.json - Some(Ok(match project.metadata { - crate::prelude::ProjectMetadata::Modrinth { - ref project, - ref version, - .. - } => { - let mut env = HashMap::new(); - env.insert(EnvType::Client, project.client_side.clone()); - env.insert(EnvType::Server, project.server_side.clone()); - - let primary_file = if let Some(primary_file) = - version.files.first() - { - primary_file - } else { - return Some(Err(crate::ErrorKind::OtherError( - format!("No primary file found for mod at: {path}"), - ))); - }; - - let file_size = primary_file.size; - let downloads = vec![primary_file.url.clone()]; - let hashes = primary_file - .hashes - .clone() - .into_iter() - .map(|(h1, h2)| (PackFileHash::from(h1), h2)) - .collect(); - - PackFile { - path, - hashes, - env: Some(env), - downloads, - file_size, - } - } - // Inferred files are skipped for the modrinth.json - crate::prelude::ProjectMetadata::Inferred { .. } => { - return None - } - // Unknown projects are skipped for the modrinth.json - crate::prelude::ProjectMetadata::Unknown => return None, - })) - }) - .collect(); - let files = files?; - - Ok(PackFormat { - game: "minecraft".to_string(), - format_version: 1, - version_id, - name: profile.metadata.name.clone(), - summary: None, - files, - dependencies, - }) -} - -fn sanitize_loader_version_string(s: &str) -> &str { - // Split on '-' - // If two or more, take the second - // If one, take the first - // If none, take the whole thing - let mut split: std::str::Split<'_, char> = s.split('-'); - match split.next() { - Some(first) => match split.next() { - Some(second) => second, - None => first, - }, - None => s, - } -} - -// Given a folder path, populate a Vec of all the files in the folder, recursively -#[async_recursion::async_recursion] -pub async fn build_folder( - path: &Path, - path_list: &mut Vec, -) -> crate::Result<()> { - let mut read_dir = fs::read_dir(path).await?; - while let Some(entry) = read_dir.next_entry().await? { - let path = entry.path(); - if path.is_dir() { - build_folder(&path, path_list).await?; - } else { - path_list.push(path); - } - } - Ok(()) -} - -// Given a folder path, populate a Vec of all the subfolders -// Intended to be used for finding potential override folders -// profile -// -- folder1 -// -- folder2 -// ----- file2 -// ----- folder3 -// ------- folder4 -// -- file1 -// => [folder1, folder2, fil2, folder3, file1] -pub async fn get_potential_override_folders( - profile_path: PathBuf, -) -> crate::Result> { - // First, get a dummy mrpack json for the files within - let profile: Profile = - get(&profile_path, None).await?.ok_or_else(|| { - crate::ErrorKind::OtherError(format!( - "Tried to export a nonexistent or unloaded profile at path {}!", - profile_path.display() - )) - })?; - let mrpack = create_mrpack_json(&profile, "0".to_string())?; - let mrpack_files = get_modrinth_pack_list(&mrpack); - - let mut path_list: Vec = Vec::new(); - let mut read_dir = fs::read_dir(&profile_path).await?; - while let Some(entry) = read_dir.next_entry().await? { - let path: PathBuf = entry.path(); - if path.is_dir() { - // Two layers of files/folders if its a folder - let mut read_dir = fs::read_dir(&path).await?; - while let Some(entry) = read_dir.next_entry().await? { - let path: PathBuf = entry.path(); - let name = path.strip_prefix(&profile_path)?.to_path_buf(); - if !mrpack_files.contains(&name.to_string_lossy().to_string()) { - path_list.push(name); - } - } - } else { - // One layer of files/folders if its a file - let name = path.strip_prefix(&profile_path)?.to_path_buf(); - if !mrpack_files.contains(&name.to_string_lossy().to_string()) { - path_list.push(name); - } - } - } - Ok(path_list) -} diff --git a/theseus/src/util/jre.rs b/theseus/src/util/jre.rs index d7318c516..05e57d86c 100644 --- a/theseus/src/util/jre.rs +++ b/theseus/src/util/jre.rs @@ -136,11 +136,9 @@ pub async fn get_all_jre() -> Result, JREError> { // Iterate over JavaVirtualMachines/(something)/Contents/Home/bin let base_path = PathBuf::from("/Library/Java/JavaVirtualMachines/"); if let Ok(dir) = std::fs::read_dir(base_path) { - for entry in dir { - if let Ok(entry) = entry { - let entry = entry.path().join("Contents/Home/bin"); - jre_paths.insert(entry); - } + for entry in dir.flatten() { + let entry = entry.path().join("Contents/Home/bin"); + jre_paths.insert(entry); } } @@ -178,12 +176,10 @@ pub async fn get_all_jre() -> Result, JREError> { jre_paths.insert(PathBuf::from(&path).join("jre").join("bin")); jre_paths.insert(PathBuf::from(&path).join("bin")); if let Ok(dir) = std::fs::read_dir(path) { - for entry in dir { - if let Ok(entry) = entry { - let entry_path = entry.path(); - jre_paths.insert(entry_path.join("jre").join("bin")); - jre_paths.insert(entry_path.join("bin")); - } + for entry in dir.flatten() { + let entry_path = entry.path(); + jre_paths.insert(entry_path.join("jre").join("bin")); + jre_paths.insert(entry_path.join("bin")); } } } @@ -209,21 +205,19 @@ async fn get_all_autoinstalled_jre_path() -> Result, JREError> if base_path.is_dir() { if let Ok(dir) = std::fs::read_dir(base_path) { - for entry in dir { - if let Ok(entry) = entry { - let file_path = entry.path().join("bin"); - - if let Ok(contents) = - std::fs::read_to_string(file_path.clone()) + for entry in dir.flatten() { + let file_path = entry.path().join("bin"); + + if let Ok(contents) = + std::fs::read_to_string(file_path.clone()) + { + let entry = entry.path().join(contents); + jre_paths.insert(entry); + } else { + #[cfg(not(target_os = "macos"))] { - let entry = entry.path().join(contents); - jre_paths.insert(entry); - } else { - #[cfg(not(target_os = "macos"))] - { - let file_path = file_path.join(JAVA_BIN); - jre_paths.insert(file_path); - } + let file_path = file_path.join(JAVA_BIN); + jre_paths.insert(file_path); } } } diff --git a/theseus/src/util/mod.rs b/theseus/src/util/mod.rs index dfc405165..a3a4c7937 100644 --- a/theseus/src/util/mod.rs +++ b/theseus/src/util/mod.rs @@ -1,5 +1,4 @@ //! Theseus utility functions -pub mod export; pub mod fetch; pub mod jre; pub mod platform; diff --git a/theseus_gui/src-tauri/src/api/auth.rs b/theseus_gui/src-tauri/src/api/auth.rs index 2b39aaded..0b6ceef4b 100644 --- a/theseus_gui/src-tauri/src/api/auth.rs +++ b/theseus_gui/src-tauri/src/api/auth.rs @@ -1,6 +1,22 @@ use crate::api::Result; +use tauri::plugin::TauriPlugin; use theseus::prelude::*; +pub fn init() -> TauriPlugin { + tauri::plugin::Builder::new("auth") + .invoke_handler(tauri::generate_handler![ + auth_authenticate_begin_flow, + auth_authenticate_await_completion, + auth_cancel_flow, + auth_refresh, + auth_remove_user, + auth_has_user, + auth_users, + auth_get_user, + ]) + .build() +} + /// Authenticate a user with Hydra - part 1 /// This begins the authentication flow quasi-synchronously, returning a URL to visit (that the user will sign in at) #[tauri::command] @@ -22,7 +38,7 @@ pub async fn auth_cancel_flow() -> Result<()> { } /// Refresh some credentials using Hydra, if needed -// invoke('auth_refresh',user) +// invoke('plugin:auth|auth_refresh',user) #[tauri::command] pub async fn auth_refresh(user: uuid::Uuid) -> Result { Ok(auth::refresh(user).await?) @@ -34,14 +50,14 @@ pub async fn auth_remove_user(user: uuid::Uuid) -> Result<()> { } /// Check if a user exists in Theseus -// invoke('auth_has_user',user) +// invoke('plugin:auth|auth_has_user',user) #[tauri::command] pub async fn auth_has_user(user: uuid::Uuid) -> Result { Ok(auth::has_user(user).await?) } /// Get a copy of the list of all user credentials -// invoke('auth_users',user) +// invoke('plugin:auth|auth_users',user) #[tauri::command] pub async fn auth_users() -> Result> { Ok(auth::users().await?) @@ -49,7 +65,7 @@ pub async fn auth_users() -> Result> { /// Get a user from the UUID /// Prefer to use refresh instead, as it will refresh the credentials as well -// invoke('auth_users',user) +// invoke('plugin:auth|auth_users',user) #[tauri::command] pub async fn auth_get_user(user: uuid::Uuid) -> Result { Ok(auth::get_user(user).await?) diff --git a/theseus_gui/src-tauri/src/api/jre.rs b/theseus_gui/src-tauri/src/api/jre.rs index e56486d3d..0d1999bf8 100644 --- a/theseus_gui/src-tauri/src/api/jre.rs +++ b/theseus_gui/src-tauri/src/api/jre.rs @@ -1,9 +1,24 @@ use std::path::PathBuf; use crate::api::Result; +use tauri::plugin::TauriPlugin; use theseus::prelude::JavaVersion; use theseus::prelude::*; +pub fn init() -> TauriPlugin { + tauri::plugin::Builder::new("jre") + .invoke_handler(tauri::generate_handler![ + jre_get_all_jre, + jre_find_filtered_jres, + jre_autodetect_java_globals, + jre_validate_globals, + jre_get_jre, + jre_auto_install_java, + jre_get_max_memory, + ]) + .build() +} + /// Get all JREs that exist on the system #[tauri::command] pub async fn jre_get_all_jre() -> Result> { @@ -12,27 +27,24 @@ pub async fn jre_get_all_jre() -> Result> { // Finds the installation of Java 8, if it exists #[tauri::command] -pub async fn jre_find_jre_8_jres() -> Result> { - Ok(jre::find_filtered_jres("1.8").await?) -} - -// finds the installation of Java 17, if it exists -#[tauri::command] -pub async fn jre_find_jre_17_jres() -> Result> { - Ok(jre::find_filtered_jres("1.17").await?) -} - -// Finds the highest version of Java 18+, if it exists -#[tauri::command] -pub async fn jre_find_jre_18plus_jres() -> Result> { - Ok(jre::find_filtered_jres("1.18").await?) +pub async fn jre_find_filtered_jres( + jres: Vec, + version: String, + allow_higher: bool, +) -> Result> { + Ok(jre::find_filtered_jres(&version, jres, allow_higher).await?) } // Autodetect Java globals, by searching the users computer. +// Selects from the given JREs, and returns a new JavaGlobals // Returns a *NEW* JavaGlobals that can be put into Settings #[tauri::command] -pub async fn jre_autodetect_java_globals() -> Result { - Ok(jre::autodetect_java_globals().await?) +pub async fn jre_autodetect_java_globals( + java_8: Vec, + java_17: Vec, + java_18plus: Vec, +) -> Result { + Ok(jre::autodetect_java_globals(java_8, java_17, java_18plus).await?) } // Validates java globals, by checking if the paths exist diff --git a/theseus_gui/src-tauri/src/api/logs.rs b/theseus_gui/src-tauri/src/api/logs.rs index f9b8fe542..c81dbe039 100644 --- a/theseus_gui/src-tauri/src/api/logs.rs +++ b/theseus_gui/src-tauri/src/api/logs.rs @@ -12,6 +12,18 @@ pub struct Logs { } */ +pub fn init() -> tauri::plugin::TauriPlugin { + tauri::plugin::Builder::new("logs") + .invoke_handler(tauri::generate_handler![ + logs_get_logs, + logs_get_logs_by_datetime, + logs_get_output_by_datetime, + logs_delete_logs, + logs_delete_logs_by_datetime, + ]) + .build() +} + /// Get all Logs for a profile, sorted by datetime #[tauri::command] pub async fn logs_get_logs( diff --git a/theseus_gui/src-tauri/src/api/metadata.rs b/theseus_gui/src-tauri/src/api/metadata.rs index 807f5ec3c..dd2246b7e 100644 --- a/theseus_gui/src-tauri/src/api/metadata.rs +++ b/theseus_gui/src-tauri/src/api/metadata.rs @@ -2,6 +2,17 @@ use crate::api::Result; use daedalus::minecraft::VersionManifest; use daedalus::modded::Manifest; +pub fn init() -> tauri::plugin::TauriPlugin { + tauri::plugin::Builder::new("metadata") + .invoke_handler(tauri::generate_handler![ + metadata_get_game_versions, + metadata_get_fabric_versions, + metadata_get_forge_versions, + metadata_get_quilt_versions, + ]) + .build() +} + /// Gets the game versions from daedalus #[tauri::command] pub async fn metadata_get_game_versions() -> Result { diff --git a/theseus_gui/src-tauri/src/api/mod.rs b/theseus_gui/src-tauri/src/api/mod.rs index 2594d9b53..de1d61d07 100644 --- a/theseus_gui/src-tauri/src/api/mod.rs +++ b/theseus_gui/src-tauri/src/api/mod.rs @@ -45,16 +45,6 @@ pub enum TheseusSerializableError { // } // } -// Lists active progress bars -// Create a new HashMap with the same keys -// Values provided should not be used directly, as they are not guaranteed to be up-to-date -#[tauri::command] -pub async fn progress_bars_list( -) -> Result> { - let res = theseus::EventState::list_progress_bars().await?; - Ok(res) -} - // This is a very simple macro that implements a very basic Serializable for each variant of TheseusSerializableError, // where the field is the string. (This allows easy extension to errors without many match arms) macro_rules! impl_serialize { diff --git a/theseus_gui/src-tauri/src/api/pack.rs b/theseus_gui/src-tauri/src/api/pack.rs index 2c56331b2..1d13d8a18 100644 --- a/theseus_gui/src-tauri/src/api/pack.rs +++ b/theseus_gui/src-tauri/src/api/pack.rs @@ -1,23 +1,33 @@ use crate::api::Result; -use std::path::{Path, PathBuf}; -use theseus::prelude::*; +use std::path::PathBuf; +use theseus::{ + pack::{ + install::install_pack, + install_from::{CreatePackLocation, CreatePackProfile}, + }, + prelude::*, +}; + +pub fn init() -> tauri::plugin::TauriPlugin { + tauri::plugin::Builder::new("pack") + .invoke_handler(tauri::generate_handler![ + pack_install, + pack_get_profile_from_pack, + ]) + .build() +} #[tauri::command] -pub async fn pack_install_version_id( - project_id: String, - version_id: String, - pack_title: String, - pack_icon: Option, +pub async fn pack_install( + location: CreatePackLocation, + profile: PathBuf, ) -> Result { - let res = pack::install_pack_from_version_id( - project_id, version_id, pack_title, pack_icon, - ) - .await?; - Ok(res) + Ok(install_pack(location, profile).await?) } #[tauri::command] -pub async fn pack_install_file(path: &Path) -> Result { - let res = pack::install_pack_from_file(path.to_path_buf()).await?; - Ok(res) +pub fn pack_get_profile_from_pack( + location: CreatePackLocation, +) -> Result { + Ok(pack::install_from::get_profile_from_pack(location)) } diff --git a/theseus_gui/src-tauri/src/api/process.rs b/theseus_gui/src-tauri/src/api/process.rs index 1c1065988..58ffb8e3e 100644 --- a/theseus_gui/src-tauri/src/api/process.rs +++ b/theseus_gui/src-tauri/src/api/process.rs @@ -4,6 +4,23 @@ use crate::api::Result; use theseus::prelude::*; use uuid::Uuid; +pub fn init() -> tauri::plugin::TauriPlugin { + tauri::plugin::Builder::new("process") + .invoke_handler(tauri::generate_handler![ + process_has_finished_by_uuid, + process_get_exit_status_by_uuid, + process_get_all_uuids, + process_get_all_running_uuids, + process_get_uuids_by_profile_path, + process_get_all_running_profile_paths, + process_get_all_running_profiles, + process_get_output_by_uuid, + process_kill_by_uuid, + process_wait_for_by_uuid, + ]) + .build() +} + // Checks if a process has finished by process UUID #[tauri::command] pub async fn process_has_finished_by_uuid(uuid: Uuid) -> Result { diff --git a/theseus_gui/src-tauri/src/api/profile.rs b/theseus_gui/src-tauri/src/api/profile.rs index 864bfcf78..c2650932b 100644 --- a/theseus_gui/src-tauri/src/api/profile.rs +++ b/theseus_gui/src-tauri/src/api/profile.rs @@ -6,8 +6,35 @@ use std::path::{Path, PathBuf}; use theseus::prelude::*; use uuid::Uuid; +pub fn init() -> tauri::plugin::TauriPlugin { + tauri::plugin::Builder::new("profile") + .invoke_handler(tauri::generate_handler![ + profile_remove, + profile_get, + profile_get_optimal_jre_key, + profile_list, + profile_check_installed, + profile_install, + profile_update_all, + profile_update_project, + profile_add_project_from_version, + profile_add_project_from_path, + profile_toggle_disable_project, + profile_remove_project, + profile_run, + profile_run_wait, + profile_run_credentials, + profile_run_wait_credentials, + profile_edit, + profile_edit_icon, + profile_export_mrpack, + profile_get_potential_override_folders, + ]) + .build() +} + // Remove a profile -// invoke('profile_add_path',path) +// invoke('plugin:profile|profile_add_path',path) #[tauri::command] pub async fn profile_remove(path: &Path) -> Result<()> { profile::remove(path).await?; @@ -15,7 +42,7 @@ pub async fn profile_remove(path: &Path) -> Result<()> { } // Get a profile by path -// invoke('profile_add_path',path) +// invoke('plugin:profile|profile_add_path',path) #[tauri::command] pub async fn profile_get( path: &Path, @@ -35,7 +62,7 @@ pub async fn profile_get_optimal_jre_key( } // Get a copy of the profile set -// invoke('profile_list') +// invoke('plugin:profile|profile_list') #[tauri::command] pub async fn profile_list( clear_projects: Option, @@ -65,7 +92,7 @@ pub async fn profile_check_installed( } /// Installs/Repairs a profile -/// invoke('profile_install') +/// invoke('plugin:profile|profile_install') #[tauri::command] pub async fn profile_install(path: &Path) -> Result<()> { profile::install(path).await?; @@ -73,7 +100,7 @@ pub async fn profile_install(path: &Path) -> Result<()> { } /// Updates all of the profile's projects -/// invoke('profile_update_all') +/// invoke('plugin:profile|profile_update_all') #[tauri::command] pub async fn profile_update_all( path: &Path, @@ -82,7 +109,7 @@ pub async fn profile_update_all( } /// Updates a specified project -/// invoke('profile_update_project') +/// invoke('plugin:profile|profile_update_project') #[tauri::command] pub async fn profile_update_project( path: &Path, @@ -92,7 +119,7 @@ pub async fn profile_update_project( } // Adds a project to a profile from a version ID -// invoke('profile_add_project_from_version') +// invoke('plugin:profile|profile_add_project_from_version') #[tauri::command] pub async fn profile_add_project_from_version( path: &Path, @@ -102,7 +129,7 @@ pub async fn profile_add_project_from_version( } // Adds a project to a profile from a path -// invoke('profile_add_project_from_path') +// invoke('plugin:profile|profile_add_project_from_path') #[tauri::command] pub async fn profile_add_project_from_path( path: &Path, @@ -115,7 +142,7 @@ pub async fn profile_add_project_from_path( } // Toggles disabling a project from its path -// invoke('profile_toggle_disable_project') +// invoke('plugin:profile|profile_toggle_disable_project') #[tauri::command] pub async fn profile_toggle_disable_project( path: &Path, @@ -125,7 +152,7 @@ pub async fn profile_toggle_disable_project( } // Removes a project from a profile -// invoke('profile_remove_project') +// invoke('plugin:profile|profile_remove_project') #[tauri::command] pub async fn profile_remove_project( path: &Path, @@ -173,7 +200,7 @@ pub async fn profile_get_potential_override_folders( // Run minecraft using a profile using the default credentials // Returns the UUID, which can be used to poll // for the actual Child in the state. -// invoke('profile_run', path) +// invoke('plugin:profile|profile_run', path) #[tauri::command] pub async fn profile_run(path: &Path) -> Result { let minecraft_child = profile::run(path).await?; @@ -182,7 +209,7 @@ pub async fn profile_run(path: &Path) -> Result { } // Run Minecraft using a profile using the default credentials, and wait for the result -// invoke('profile_run_wait', path) +// invoke('plugin:profile|profile_run_wait', path) #[tauri::command] pub async fn profile_run_wait(path: &Path) -> Result<()> { let proc_lock = profile::run(path).await?; @@ -193,7 +220,7 @@ pub async fn profile_run_wait(path: &Path) -> Result<()> { // Run Minecraft using a profile using chosen credentials // Returns the UUID, which can be used to poll // for the actual Child in the state. -// invoke('profile_run_credentials', {path, credentials})') +// invoke('plugin:profile|profile_run_credentials', {path, credentials})') #[tauri::command] pub async fn profile_run_credentials( path: &Path, @@ -205,7 +232,7 @@ pub async fn profile_run_credentials( } // Run Minecraft using a profile using the chosen credentials, and wait for the result -// invoke('profile_run_wait', {path, credentials) +// invoke('plugin:profile|profile_run_wait', {path, credentials) #[tauri::command] pub async fn profile_run_wait_credentials( path: &Path, @@ -235,7 +262,7 @@ pub struct EditProfileMetadata { } // Edits a profile -// invoke('profile_edit', {path, editProfile}) +// invoke('plugin:profile|profile_edit', {path, editProfile}) #[tauri::command] pub async fn profile_edit( path: &Path, @@ -275,7 +302,7 @@ pub async fn profile_edit( } // Edits a profile's icon -// invoke('profile_edit_icon') +// invoke('plugin:profile|profile_edit_icon') #[tauri::command] pub async fn profile_edit_icon( path: &Path, diff --git a/theseus_gui/src-tauri/src/api/profile_create.rs b/theseus_gui/src-tauri/src/api/profile_create.rs index 24820dc89..8afb4f77f 100644 --- a/theseus_gui/src-tauri/src/api/profile_create.rs +++ b/theseus_gui/src-tauri/src/api/profile_create.rs @@ -2,16 +2,14 @@ use crate::api::Result; use std::path::PathBuf; use theseus::prelude::*; -// Generic basic profile creation tool. -// Creates an essentially empty dummy profile with profile_create -#[tauri::command] -pub async fn profile_create_empty() -> Result { - let res = profile_create::profile_create_empty().await?; - Ok(res) +pub fn init() -> tauri::plugin::TauriPlugin { + tauri::plugin::Builder::new("profile_create") + .invoke_handler(tauri::generate_handler![profile_create,]) + .build() } // Creates a profile at the given filepath and adds it to the in-memory state -// invoke('profile_add',profile) +// invoke('plugin:profile|profile_add',profile) #[tauri::command] pub async fn profile_create( name: String, // the name of the profile, and relative path diff --git a/theseus_gui/src-tauri/src/api/settings.rs b/theseus_gui/src-tauri/src/api/settings.rs index 4a8bb55df..9b90cf80d 100644 --- a/theseus_gui/src-tauri/src/api/settings.rs +++ b/theseus_gui/src-tauri/src/api/settings.rs @@ -20,8 +20,14 @@ pub struct FrontendSettings { pub collapsed_navigation: bool, } +pub fn init() -> tauri::plugin::TauriPlugin { + tauri::plugin::Builder::new("settings") + .invoke_handler(tauri::generate_handler![settings_get, settings_set,]) + .build() +} + // Get full settings -// invoke('settings_get') +// invoke('plugin:settings|settings_get') #[tauri::command] pub async fn settings_get() -> Result { let res = settings::get().await?; @@ -29,7 +35,7 @@ pub async fn settings_get() -> Result { } // Set full settings -// invoke('settings_set', settings) +// invoke('plugin:settings|settings_set', settings) #[tauri::command] pub async fn settings_set(settings: Settings) -> Result<()> { settings::set(settings).await?; diff --git a/theseus_gui/src-tauri/src/api/tags.rs b/theseus_gui/src-tauri/src/api/tags.rs index 0ae2bad65..cecaba24f 100644 --- a/theseus_gui/src-tauri/src/api/tags.rs +++ b/theseus_gui/src-tauri/src/api/tags.rs @@ -1,6 +1,19 @@ use crate::api::Result; use theseus::tags::{Category, DonationPlatform, GameVersion, Loader, Tags}; +pub fn init() -> tauri::plugin::TauriPlugin { + tauri::plugin::Builder::new("tags") + .invoke_handler(tauri::generate_handler![ + tags_get_categories, + tags_get_report_types, + tags_get_loaders, + tags_get_game_versions, + tags_get_donation_platforms, + tags_get_tag_bundle, + ]) + .build() +} + /// Gets cached category tags from the database #[tauri::command] pub async fn tags_get_categories() -> Result> { diff --git a/theseus_gui/src-tauri/src/api/utils.rs b/theseus_gui/src-tauri/src/api/utils.rs index 68bd47fb8..edf8463a4 100644 --- a/theseus_gui/src-tauri/src/api/utils.rs +++ b/theseus_gui/src-tauri/src/api/utils.rs @@ -1,6 +1,26 @@ use crate::api::Result; use std::process::Command; +pub fn init() -> tauri::plugin::TauriPlugin { + tauri::plugin::Builder::new("utils") + .invoke_handler(tauri::generate_handler![ + should_disable_mouseover, + show_in_folder, + progress_bars_list, + ]) + .build() +} + +// Lists active progress bars +// Create a new HashMap with the same keys +// Values provided should not be used directly, as they are not guaranteed to be up-to-date +#[tauri::command] +pub async fn progress_bars_list( +) -> Result> { + let res = theseus::EventState::list_progress_bars().await?; + Ok(res) +} + // cfg only on mac os // disables mouseover and fixes a random crash error only fixed by recent versions of macos #[cfg(target_os = "macos")] diff --git a/theseus_gui/src-tauri/src/main.rs b/theseus_gui/src-tauri/src/main.rs index d9d0735a4..99fbb6ba0 100644 --- a/theseus_gui/src-tauri/src/main.rs +++ b/theseus_gui/src-tauri/src/main.rs @@ -111,82 +111,19 @@ fn main() { } }) } - - builder = builder.invoke_handler(tauri::generate_handler![ - initialize_state, - is_dev, - api::progress_bars_list, - api::profile_create::profile_create_empty, - api::profile_create::profile_create, - api::profile::profile_remove, - api::profile::profile_get, - api::profile::profile_get_optimal_jre_key, - api::profile::profile_list, - api::profile::profile_install, - api::profile::profile_update_all, - api::profile::profile_update_project, - api::profile::profile_add_project_from_version, - api::profile::profile_add_project_from_path, - api::profile::profile_toggle_disable_project, - api::profile::profile_remove_project, - api::profile::profile_run, - api::profile::profile_run_wait, - api::profile::profile_run_credentials, - api::profile::profile_run_wait_credentials, - api::profile::profile_edit, - api::profile::profile_edit_icon, - api::profile::profile_check_installed, - api::pack::pack_install_version_id, - api::pack::pack_install_file, - api::auth::auth_authenticate_begin_flow, - api::auth::auth_authenticate_await_completion, - api::auth::auth_cancel_flow, - api::auth::auth_refresh, - api::auth::auth_remove_user, - api::auth::auth_has_user, - api::auth::auth_users, - api::auth::auth_get_user, - api::tags::tags_get_categories, - api::tags::tags_get_donation_platforms, - api::tags::tags_get_game_versions, - api::tags::tags_get_loaders, - api::tags::tags_get_report_types, - api::tags::tags_get_tag_bundle, - api::settings::settings_get, - api::settings::settings_set, - api::jre::jre_get_all_jre, - api::jre::jre_autodetect_java_globals, - api::jre::jre_find_jre_18plus_jres, - api::jre::jre_find_jre_17_jres, - api::jre::jre_find_jre_8_jres, - api::jre::jre_validate_globals, - api::jre::jre_get_jre, - api::jre::jre_auto_install_java, - api::jre::jre_get_max_memory, - api::profile::profile_export_mrpack, - api::profile::profile_get_potential_override_folders, - api::process::process_get_all_uuids, - api::process::process_get_all_running_uuids, - api::process::process_get_uuids_by_profile_path, - api::process::process_get_all_running_profile_paths, - api::process::process_get_all_running_profiles, - api::process::process_get_exit_status_by_uuid, - api::process::process_has_finished_by_uuid, - api::process::process_get_output_by_uuid, - api::process::process_kill_by_uuid, - api::process::process_wait_for_by_uuid, - api::metadata::metadata_get_game_versions, - api::metadata::metadata_get_fabric_versions, - api::metadata::metadata_get_forge_versions, - api::metadata::metadata_get_quilt_versions, - api::logs::logs_get_logs, - api::logs::logs_get_logs_by_datetime, - api::logs::logs_get_output_by_datetime, - api::logs::logs_delete_logs, - api::logs::logs_delete_logs_by_datetime, - api::utils::show_in_folder, - api::utils::should_disable_mouseover, - ]); + let builder = builder + .plugin(api::auth::init()) + .plugin(api::logs::init()) + .plugin(api::jre::init()) + .plugin(api::metadata::init()) + .plugin(api::pack::init()) + .plugin(api::process::init()) + .plugin(api::profile::init()) + .plugin(api::profile_create::init()) + .plugin(api::settings::init()) + .plugin(api::tags::init()) + .plugin(api::utils::init()) + .invoke_handler(tauri::generate_handler![initialize_state, is_dev]); builder .run(tauri::generate_context!()) diff --git a/theseus_gui/src/helpers/auth.js b/theseus_gui/src/helpers/auth.js index 4ffbf6c45..fa6798df6 100644 --- a/theseus_gui/src/helpers/auth.js +++ b/theseus_gui/src/helpers/auth.js @@ -17,7 +17,7 @@ import { invoke } from '@tauri-apps/api/tauri' /// This begins the authentication flow quasi-synchronously /// This returns a URL to be opened in a browser export async function authenticate_begin_flow() { - return await invoke('auth_authenticate_begin_flow') + return await invoke('plugin:auth|auth_authenticate_begin_flow') } /// Authenticate a user with Hydra - part 2 @@ -25,11 +25,11 @@ export async function authenticate_begin_flow() { /// (and also adding the credentials to the state) /// This returns a Credentials object export async function authenticate_await_completion() { - return await invoke('auth_authenticate_await_completion') + return await invoke('plugin:auth|auth_authenticate_await_completion') } export async function cancel_flow() { - return await invoke('auth_cancel_flow') + return await invoke('plugin:auth|auth_cancel_flow') } /// Refresh some credentials using Hydra, if needed @@ -37,26 +37,26 @@ export async function cancel_flow() { /// update_name is bool /// Returns a Credentials object export async function refresh(user, update_name) { - return await invoke('auth_refresh', { user, update_name }) + return await invoke('plugin:auth|auth_refresh', { user, update_name }) } /// Remove a user account from the database /// user is UUID export async function remove_user(user) { - return await invoke('auth_remove_user', { user }) + return await invoke('plugin:auth|auth_remove_user', { user }) } // Add a path as a profile in-memory // user is UUID /// Returns a bool export async function has_user(user) { - return await invoke('auth_has_user', { user }) + return await invoke('plugin:auth|auth_has_user', { user }) } /// Returns a list of users /// Returns an Array of Credentials export async function users() { - return await invoke('auth_users') + return await invoke('plugin:auth|auth_users') } // Get a user by UUID @@ -64,5 +64,5 @@ export async function users() { // user is UUID // Returns Credentials (of user) export async function get_user(user) { - return await invoke('auth_get_user', { user }) + return await invoke('plugin:auth|auth_get_user', { user }) } diff --git a/theseus_gui/src/helpers/jre.js b/theseus_gui/src/helpers/jre.js index 176aac2d4..78dce79ed 100644 --- a/theseus_gui/src/helpers/jre.js +++ b/theseus_gui/src/helpers/jre.js @@ -17,51 +17,63 @@ JavaVersion { /// Get all JREs that exist on the system // Returns an array of JavaVersion export async function get_all_jre() { - return await invoke('jre_get_all_jre') + return await invoke('plugin:jre|jre_get_all_jre') } // Finds all the installation of Java 7, if it exists // Returns [JavaVersion] export async function find_jre_8_jres() { - return await invoke('jre_find_jre_8_jres') + const jres = await invoke('plugin:jre|jre_get_all_jre') + const version = '1.8' + const allowHigher = false + return await invoke('plugin:jre|jre_find_filtered_jres', { jres, version, allowHigher }) } // Finds the installation of Java 17, if it exists // Returns [JavaVersion] export async function find_jre_17_jres() { - return await invoke('jre_find_jre_17_jres') + const jres = await invoke('plugin:jre|jre_get_all_jre') + const version = '1.17' + const allowHigher = false + return await invoke('plugin:jre|jre_find_filtered_jres', { jres, version, allowHigher }) } // Finds the highest version of Java 18+, if it exists // Returns [JavaVersion] export async function find_jre_18plus_jres() { - return await invoke('jre_find_jre_18plus_jres') + const jres = await invoke('plugin:jre|jre_get_all_jre') + const version = '1.18' + const allowHigher = true + return await invoke('plugin:jre|jre_find_filtered_jres', { jres, version, allowHigher }) } // Validates globals. Recommend directing the user to reassigned the globals if this returns false // Returns [JavaVersion] export async function validate_globals() { - return await invoke('jre_validate_globals') + return await invoke('plugin:jre|jre_validate_globals') } // Gets java version from a specific path by trying to run 'java -version' on it. // This also validates it, as it returns null if no valid java version is found at the path export async function get_jre(path) { - return await invoke('jre_get_jre', { path }) + return await invoke('plugin:jre|jre_get_jre', { path }) } // Autodetect Java globals, by searching the users computer. // Returns a *NEW* JavaGlobals that can be put into Settings export async function autodetect_java_globals() { - return await invoke('jre_autodetect_java_globals') + const java8 = await find_jre_8_jres() + const java17 = await find_jre_17_jres() + const java18plus = await find_jre_18plus_jres() + return await invoke('plugin:jre|jre_autodetect_java_globals', { java8, java17, java18plus }) } // Automatically installs specified java version export async function auto_install_java(javaVersion) { - return await invoke('jre_auto_install_java', { javaVersion }) + return await invoke('plugin:jre|jre_auto_install_java', { javaVersion }) } // Get max memory in KiB export async function get_max_memory() { - return await invoke('jre_get_max_memory') + return await invoke('plugin:jre|jre_get_max_memory') } diff --git a/theseus_gui/src/helpers/logs.js b/theseus_gui/src/helpers/logs.js index 8b3e01e96..d17829451 100644 --- a/theseus_gui/src/helpers/logs.js +++ b/theseus_gui/src/helpers/logs.js @@ -18,25 +18,25 @@ pub struct Logs { /// Get all logs that exist for a given profile /// This is returned as an array of Log objects, sorted by datetime_string (the folder name, when the log was created) export async function get_logs(profileUuid, clearContents) { - return await invoke('logs_get_logs', { profileUuid, clearContents }) + return await invoke('plugin:logs|logs_get_logs', { profileUuid, clearContents }) } /// Get a profile's log by datetime_string (the folder name, when the log was created) export async function get_logs_by_datetime(profileUuid, datetimeString) { - return await invoke('logs_get_logs_by_datetime', { profileUuid, datetimeString }) + return await invoke('plugin:logs|logs_get_logs_by_datetime', { profileUuid, datetimeString }) } /// Get a profile's stdout only by datetime_string (the folder name, when the log was created) export async function get_output_by_datetime(profileUuid, datetimeString) { - return await invoke('logs_get_output_by_datetime', { profileUuid, datetimeString }) + return await invoke('plugin:logs|logs_get_output_by_datetime', { profileUuid, datetimeString }) } /// Delete a profile's log by datetime_string (the folder name, when the log was created) export async function delete_logs_by_datetime(profileUuid, datetimeString) { - return await invoke('logs_delete_logs_by_datetime', { profileUuid, datetimeString }) + return await invoke('plugin:logs|logs_delete_logs_by_datetime', { profileUuid, datetimeString }) } /// Delete all logs for a given profile export async function delete_logs(profileUuid) { - return await invoke('logs_delete_logs', { profileUuid }) + return await invoke('plugin:logs|logs_delete_logs', { profileUuid }) } diff --git a/theseus_gui/src/helpers/metadata.js b/theseus_gui/src/helpers/metadata.js index 0a0a402cd..a8d85c3d1 100644 --- a/theseus_gui/src/helpers/metadata.js +++ b/theseus_gui/src/helpers/metadata.js @@ -3,23 +3,23 @@ import { invoke } from '@tauri-apps/api/tauri' /// Gets the game versions from daedalus // Returns a VersionManifest export async function get_game_versions() { - return await invoke('metadata_get_game_versions') + return await invoke('plugin:metadata|metadata_get_game_versions') } // Gets the fabric versions from daedalus // Returns Manifest export async function get_fabric_versions() { - return await invoke('metadata_get_fabric_versions') + return await invoke('plugin:metadata|metadata_get_fabric_versions') } // Gets the forge versions from daedalus // Returns Manifest export async function get_forge_versions() { - return await invoke('metadata_get_forge_versions') + return await invoke('plugin:metadata|metadata_get_forge_versions') } // Gets the quilt versions from daedalus // Returns Manifest export async function get_quilt_versions() { - return await invoke('metadata_get_quilt_versions') + return await invoke('plugin:metadata|metadata_get_quilt_versions') } diff --git a/theseus_gui/src/helpers/pack.js b/theseus_gui/src/helpers/pack.js index 9396d31d1..c7e597270 100644 --- a/theseus_gui/src/helpers/pack.js +++ b/theseus_gui/src/helpers/pack.js @@ -4,13 +4,42 @@ * and deserialized into a usable JS object. */ import { invoke } from '@tauri-apps/api/tauri' +import { create } from './profile' // Installs pack from a version ID -export async function install(projectId, versionId, packTitle, packIcon) { - return await invoke('pack_install_version_id', { projectId, versionId, packTitle, packIcon }) +export async function install(projectId, versionId, packTitle, iconUrl) { + const location = { + type: 'fromVersionId', + project_id: projectId, + version_id: versionId, + title: packTitle, + icon_url: iconUrl, + } + const profile_creator = await invoke('plugin:pack|pack_get_profile_from_pack', { location }) + const profile = await create( + profile_creator.name, + profile_creator.gameVersion, + profile_creator.modloader, + profile_creator.loaderVersion, + profile_creator.icon + ) + + return await invoke('plugin:pack|pack_install', { location, profile }) } // Installs pack from a path export async function install_from_file(path) { - return await invoke('pack_install_file', { path }) + const location = { + type: 'fromFile', + path: path, + } + const profile_creator = await invoke('plugin:pack|pack_get_profile_from_pack', { location }) + const profile = await create( + profile_creator.name, + profile_creator.gameVersion, + profile_creator.modloader, + profile_creator.loaderVersion, + profile_creator.icon + ) + return await invoke('plugin:pack|pack_install', { location, profile }) } diff --git a/theseus_gui/src/helpers/process.js b/theseus_gui/src/helpers/process.js index 50ecd5539..750f915db 100644 --- a/theseus_gui/src/helpers/process.js +++ b/theseus_gui/src/helpers/process.js @@ -8,52 +8,52 @@ import { invoke } from '@tauri-apps/api/tauri' /// Gets if a process has finished by UUID /// Returns bool export async function has_finished_by_uuid(uuid) { - return await invoke('process_has_finished_by_uuid', { uuid }) + return await invoke('plugin:process|process_has_finished_by_uuid', { uuid }) } /// Gets process exit status by UUID /// Returns u32 export async function get_exit_status_by_uuid(uuid) { - return await invoke('process_get_exit_status_by_uuid', { uuid }) + return await invoke('plugin:process|process_get_exit_status_by_uuid', { uuid }) } /// Gets all process IDs /// Returns [u32] export async function get_all_uuids() { - return await invoke('process_get_all_uuids') + return await invoke('plugin:process|process_get_all_uuids') } /// Gets all running process IDs /// Returns [u32] export async function get_all_running_uuids() { - return await invoke('process_get_all_running_uuids') + return await invoke('plugin:process|process_get_all_running_uuids') } /// Gets all running process IDs with a given profile path /// Returns [u32] export async function get_uuids_by_profile_path(profilePath) { - return await invoke('process_get_uuids_by_profile_path', { profilePath }) + return await invoke('plugin:process|process_get_uuids_by_profile_path', { profilePath }) } /// Gets all running process IDs with a given profile path /// Returns [u32] export async function get_all_running_profile_paths(profilePath) { - return await invoke('process_get_all_running_profile_paths', { profilePath }) + return await invoke('plugin:process|process_get_all_running_profile_paths', { profilePath }) } /// Gets all running process IDs with a given profile path /// Returns [u32] export async function get_all_running_profiles() { - return await invoke('process_get_all_running_profiles') + return await invoke('plugin:process|process_get_all_running_profiles') } /// Gets process stdout by UUID /// Returns String export async function get_output_by_uuid(uuid) { - return await invoke('process_get_output_by_uuid', { uuid }) + return await invoke('plugin:process|process_get_output_by_uuid', { uuid }) } /// Kills a process by UUID export async function kill_by_uuid(uuid) { - return await invoke('process_kill_by_uuid', { uuid }) + return await invoke('plugin:process|process_kill_by_uuid', { uuid }) } diff --git a/theseus_gui/src/helpers/profile.js b/theseus_gui/src/helpers/profile.js index 63ba1a6b6..a23f3fd07 100644 --- a/theseus_gui/src/helpers/profile.js +++ b/theseus_gui/src/helpers/profile.js @@ -5,11 +5,6 @@ */ import { invoke } from '@tauri-apps/api/tauri' -// Add empty default instance -export async function create_empty() { - return await invoke('profile_create_empty') -} - /// Add instance /* name: String, // the name of the profile, and relative path to create @@ -22,71 +17,81 @@ export async function create_empty() { */ export async function create(name, gameVersion, modloader, loaderVersion, icon) { - return await invoke('profile_create', { name, gameVersion, modloader, loaderVersion, icon }) + return await invoke('plugin:profile_create|profile_create', { + name, + gameVersion, + modloader, + loaderVersion, + icon, + }) } // Remove a profile export async function remove(path) { - return await invoke('profile_remove', { path }) + return await invoke('plugin:profile|profile_remove', { path }) } // Get a profile by path // Returns a Profile export async function get(path, clearProjects) { - return await invoke('profile_get', { path, clearProjects }) + return await invoke('plugin:profile|profile_get', { path, clearProjects }) } // Get optimal java version from profile // Returns a java version export async function get_optimal_jre_key(path) { - return await invoke('profile_get_optimal_jre_key', { path }) + return await invoke('plugin:profile|profile_get_optimal_jre_key', { path }) } // Get a copy of the profile set // Returns hashmap of path -> Profile export async function list(clearProjects) { - return await invoke('profile_list', { clearProjects }) + return await invoke('plugin:profile|profile_list', { clearProjects }) } export async function check_installed(path, projectId) { - return await invoke('profile_check_installed', { path, projectId }) + return await invoke('plugin:profile|profile_check_installed', { path, projectId }) } // Installs/Repairs a profile export async function install(path) { - return await invoke('profile_install', { path }) + return await invoke('plugin:profile|profile_install', { path }) } // Updates all of a profile's projects export async function update_all(path) { - return await invoke('profile_update_all', { path }) + return await invoke('plugin:profile|profile_update_all', { path }) } // Updates a specified project export async function update_project(path, projectPath) { - return await invoke('profile_update_project', { path, projectPath }) + return await invoke('plugin:profile|profile_update_project', { path, projectPath }) } // Add a project to a profile from a version // Returns a path to the new project file export async function add_project_from_version(path, versionId) { - return await invoke('profile_add_project_from_version', { path, versionId }) + return await invoke('plugin:profile|profile_add_project_from_version', { path, versionId }) } // Add a project to a profile from a path + project_type // Returns a path to the new project file export async function add_project_from_path(path, projectPath, projectType) { - return await invoke('profile_add_project_from_path', { path, projectPath, projectType }) + return await invoke('plugin:profile|profile_add_project_from_path', { + path, + projectPath, + projectType, + }) } // Toggle disabling a project export async function toggle_disable_project(path, projectPath) { - return await invoke('profile_toggle_disable_project', { path, projectPath }) + return await invoke('plugin:profile|profile_toggle_disable_project', { path, projectPath }) } // Remove a project export async function remove_project(path, projectPath) { - return await invoke('profile_remove_project', { path, projectPath }) + return await invoke('plugin:profile|profile_remove_project', { path, projectPath }) } // Export a profile to .mrpack @@ -116,21 +121,21 @@ export async function get_potential_override_folders(profilePath) { // Run Minecraft using a pathed profile // Returns PID of child export async function run(path) { - return await invoke('profile_run', { path }) + return await invoke('plugin:profile|profile_run', { path }) } // Run Minecraft using a pathed profile // Waits for end export async function run_wait(path) { - return await invoke('profile_run_wait', { path }) + return await invoke('plugin:profile|profile_run_wait', { path }) } // Edits a profile export async function edit(path, editProfile) { - return await invoke('profile_edit', { path, editProfile }) + return await invoke('plugin:profile|profile_edit', { path, editProfile }) } // Edits a profile's icon export async function edit_icon(path, iconPath) { - return await invoke('profile_edit_icon', { path, iconPath }) + return await invoke('plugin:profile|profile_edit_icon', { path, iconPath }) } diff --git a/theseus_gui/src/helpers/settings.js b/theseus_gui/src/helpers/settings.js index 8d027a332..44495cb20 100644 --- a/theseus_gui/src/helpers/settings.js +++ b/theseus_gui/src/helpers/settings.js @@ -30,10 +30,10 @@ Memorysettings { // Get full settings object export async function get() { - return await invoke('settings_get') + return await invoke('plugin:settings|settings_get') } // Set full settings object export async function set(settings) { - return await invoke('settings_set', { settings }) + return await invoke('plugin:settings|settings_set', { settings }) } diff --git a/theseus_gui/src/helpers/state.js b/theseus_gui/src/helpers/state.js index c4d768c2e..4774544ab 100644 --- a/theseus_gui/src/helpers/state.js +++ b/theseus_gui/src/helpers/state.js @@ -13,5 +13,5 @@ export async function initialize_state() { // Gets active progress bars export async function progress_bars_list() { - return await invoke('progress_bars_list') + return await invoke('plugin:utils|progress_bars_list') } diff --git a/theseus_gui/src/helpers/tags.js b/theseus_gui/src/helpers/tags.js index 2ce3f234b..aa9955c48 100644 --- a/theseus_gui/src/helpers/tags.js +++ b/theseus_gui/src/helpers/tags.js @@ -7,30 +7,30 @@ import { invoke } from '@tauri-apps/api/tauri' // Gets tag bundle of all tags export async function get_tag_bundle() { - return await invoke('tags_get_tag_bundle') + return await invoke('plugin:tags|tags_get_tag_bundle') } // Gets cached category tags export async function get_categories() { - return await invoke('tags_get_categories') + return await invoke('plugin:tags|tags_get_categories') } // Gets cached loaders tags export async function get_loaders() { - return await invoke('tags_get_loaders') + return await invoke('plugin:tags|tags_get_loaders') } // Gets cached game_versions tags export async function get_game_versions() { - return await invoke('tags_get_game_versions') + return await invoke('plugin:tags|tags_get_game_versions') } // Gets cached donation_platforms tags export async function get_donation_platforms() { - return await invoke('tags_get_donation_platforms') + return await invoke('plugin:tags|tags_get_donation_platforms') } // Gets cached licenses tags export async function get_report_types() { - return await invoke('tags_get_report_types') + return await invoke('plugin:tags|tags_get_report_types') } diff --git a/theseus_gui/src/helpers/utils.js b/theseus_gui/src/helpers/utils.js index bdd4ebeec..ea8d61412 100644 --- a/theseus_gui/src/helpers/utils.js +++ b/theseus_gui/src/helpers/utils.js @@ -8,7 +8,7 @@ export async function isDev() { } export async function showInFolder(path) { - return await invoke('show_in_folder', { path }) + return await invoke('plugin:utils|show_in_folder', { path }) } export const releaseColor = (releaseType) => { diff --git a/theseus_gui/src/mixins/macCssFix.js b/theseus_gui/src/mixins/macCssFix.js index cb176dfe9..9d8b6ce45 100644 --- a/theseus_gui/src/mixins/macCssFix.js +++ b/theseus_gui/src/mixins/macCssFix.js @@ -9,7 +9,7 @@ export default { async checkDisableMouseover() { try { // Fetch the CSS content from the Rust backend - const should_disable_mouseover = await invoke('should_disable_mouseover') + const should_disable_mouseover = await invoke('plugin:utils|should_disable_mouseover') if (should_disable_mouseover) { // Create a style element and set its content diff --git a/theseus_playground/src/main.rs b/theseus_playground/src/main.rs index c537028a1..fcef62807 100644 --- a/theseus_playground/src/main.rs +++ b/theseus_playground/src/main.rs @@ -50,9 +50,16 @@ async fn main() -> theseus::Result<()> { let st = State::get().await?; //State::update(); - // let path = jre::auto_install_java(8).await.unwrap(); + // Autodetect java globals + let jres = jre::get_all_jre().await?; + let java_8 = jre::find_filtered_jres("1.8", jres.clone(), false).await?; + let java_17 = jre::find_filtered_jres("1.78", jres.clone(), false).await?; + let java_18plus = + jre::find_filtered_jres("1.18", jres.clone(), true).await?; + let java_globals = + autodetect_java_globals(java_8, java_17, java_18plus).await?; + st.settings.write().await.java_globals = java_globals; - st.settings.write().await.java_globals = autodetect_java_globals().await?; st.settings.write().await.max_concurrent_downloads = 50; st.settings.write().await.hooks.post_exit = Some("echo This is after Minecraft runs- global setting!".to_string());