diff --git a/Cargo.lock b/Cargo.lock index 49b44440..1bd9f004 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3818,7 +3818,6 @@ dependencies = [ "tauri", "tauri-build", "theseus", - "theseus_macros", "thiserror", "tokio", "tokio-stream", diff --git a/theseus/src/api/jre.rs b/theseus/src/api/jre.rs index d874a2ff..36c273c7 100644 --- a/theseus/src/api/jre.rs +++ b/theseus/src/api/jre.rs @@ -6,8 +6,6 @@ use std::path::PathBuf; use crate::event::emit::{emit_loading, init_loading}; use crate::util::fetch::{fetch_advanced, fetch_json}; use crate::{ - launcher::download, - prelude::Profile, state::JavaGlobals, util::jre::{self, extract_java_majorminor_version, JavaVersion}, LoadingBarType, State, @@ -39,48 +37,6 @@ pub async fn autodetect_java_globals() -> crate::Result { Ok(java_globals) } -// Gets the optimal JRE key for the given profile, using Daedalus -// Generally this would be used for profile_create, to get the optimal JRE key -// this can be overwritten by the user a profile-by-profile basis -pub async fn get_optimal_jre_key(profile: &Profile) -> crate::Result { - let state = State::get().await?; - let metadata = state.metadata.read().await; - - // Fetch version info from stored profile game_version - let version = metadata - .minecraft - .versions - .iter() - .find(|it| it.id == profile.metadata.game_version) - .ok_or_else(|| { - crate::ErrorKind::LauncherError(format!( - "Invalid or unknown Minecraft version: {}", - profile.metadata.game_version - )) - })?; - - // Get detailed manifest info from Daedalus - let version_info = download::download_version_info( - &state, - version, - profile.metadata.loader_version.as_ref(), - None, - None, - ) - .await?; - let optimal_key = match version_info - .java_version - .as_ref() - .map(|it| it.major_version) - .unwrap_or(0) - { - 0..=15 => JAVA_8_KEY.to_string(), - 16..=17 => JAVA_17_KEY.to_string(), - _ => JAVA_18PLUS_KEY.to_string(), - }; - Ok(optimal_key) -} - // Searches for jres on the system that are 1.18 or higher pub async fn find_java18plus_jres() -> crate::Result> { let version = extract_java_majorminor_version("1.18")?; diff --git a/theseus/src/api/pack.rs b/theseus/src/api/pack.rs index cba87f59..a60073a0 100644 --- a/theseus/src/api/pack.rs +++ b/theseus/src/api/pack.rs @@ -227,7 +227,7 @@ pub async fn install_pack_from_file(path: PathBuf) -> crate::Result { .await } -#[tracing::instrument] +#[tracing::instrument(skip(file))] #[theseus_macros::debug_pin] async fn install_pack( file: bytes::Bytes, diff --git a/theseus/src/api/process.rs b/theseus/src/api/process.rs index d350ddc3..848ef4e4 100644 --- a/theseus/src/api/process.rs +++ b/theseus/src/api/process.rs @@ -139,14 +139,14 @@ pub async fn wait_for_by_uuid(uuid: &Uuid) -> crate::Result<()> { } // Kill a running child process directly, and wait for it to be killed -#[tracing::instrument] +#[tracing::instrument(skip(running))] pub async fn kill(running: &mut MinecraftChild) -> crate::Result<()> { running.current_child.write().await.kill().await?; wait_for(running).await } // Await on the completion of a child process directly -#[tracing::instrument] +#[tracing::instrument(skip(running))] pub async fn wait_for(running: &mut MinecraftChild) -> crate::Result<()> { // We do not wait on the Child directly, but wait on the thread manager. // This way we can still run all cleanup hook functions that happen after. diff --git a/theseus/src/api/profile.rs b/theseus/src/api/profile.rs index 180fc72d..28b6da90 100644 --- a/theseus/src/api/profile.rs +++ b/theseus/src/api/profile.rs @@ -1,10 +1,12 @@ //! Theseus profile management interface use crate::event::emit::{init_loading, loading_try_for_each_concurrent}; use crate::event::LoadingBarType; +use crate::prelude::JavaVersion; use crate::state::ProjectMetadata; use crate::{ auth::{self, refresh}, event::{emit::emit_profile, ProfilePayloadType}, + profile, state::MinecraftChild, }; pub use crate::{ @@ -87,6 +89,100 @@ where } } +/// Edits a profile's icon +pub async fn edit_icon( + path: &Path, + icon_path: Option<&Path>, +) -> crate::Result<()> { + let state = State::get().await?; + + if let Some(icon) = icon_path { + let bytes = tokio::fs::read(icon).await?; + + let mut profiles = state.profiles.write().await; + + match profiles.0.get_mut(path) { + Some(ref mut profile) => { + emit_profile( + profile.uuid, + profile.path.clone(), + &profile.metadata.name, + ProfilePayloadType::Edited, + ) + .await?; + + profile + .set_icon( + &state.directories.caches_dir(), + &state.io_semaphore, + bytes::Bytes::from(bytes), + &icon.to_string_lossy(), + ) + .await + } + None => Err(crate::ErrorKind::UnmanagedProfileError( + path.display().to_string(), + ) + .as_error()), + } + } else { + edit(path, |profile| { + profile.metadata.icon = None; + async { Ok(()) } + }) + .await + } +} + +// Gets the optimal JRE key for the given profile, using Daedalus +// Generally this would be used for profile_create, to get the optimal JRE key +// this can be overwritten by the user a profile-by-profile basis +pub async fn get_optimal_jre_key( + path: &Path, +) -> crate::Result> { + let state = State::get().await?; + + if let Some(profile) = get(path, None).await? { + let metadata = state.metadata.read().await; + + // Fetch version info from stored profile game_version + let version = metadata + .minecraft + .versions + .iter() + .find(|it| it.id == profile.metadata.game_version) + .ok_or_else(|| { + crate::ErrorKind::LauncherError(format!( + "Invalid or unknown Minecraft version: {}", + profile.metadata.game_version + )) + })?; + + // Get detailed manifest info from Daedalus + let version_info = crate::launcher::download::download_version_info( + &state, + version, + profile.metadata.loader_version.as_ref(), + None, + None, + ) + .await?; + + let version = crate::launcher::get_java_version_from_profile( + &profile, + &version_info, + ) + .await?; + + Ok(version) + } else { + Err( + crate::ErrorKind::UnmanagedProfileError(path.display().to_string()) + .as_error(), + ) + } +} + /// Get a copy of the profile set #[tracing::instrument] pub async fn list( @@ -326,6 +422,7 @@ pub async fn remove_project( .as_error()) } } + /// Run Minecraft using a profile and the default credentials, logged in credentials, /// failing with an error if no credentials are available #[tracing::instrument] @@ -351,7 +448,7 @@ pub async fn run(path: &Path) -> crate::Result>> { /// Run Minecraft using a profile, and credentials for authentication /// Returns Arc pointer to RwLock to Child -#[tracing::instrument] +#[tracing::instrument(skip(credentials))] #[theseus_macros::debug_pin] pub async fn run_credentials( path: &Path, @@ -403,7 +500,11 @@ pub async fn run_credentials( let memory = profile.memory.unwrap_or(settings.memory); let resolution = profile.resolution.unwrap_or(settings.game_resolution); - let env_args = &settings.custom_env_args; + let env_args = profile + .java + .as_ref() + .and_then(|x| x.custom_env_args.as_ref()) + .unwrap_or(&settings.custom_env_args); // Post post exit hooks let post_exit_hook = diff --git a/theseus/src/launcher/download.rs b/theseus/src/launcher/download.rs index 2ab99866..67e32cfd 100644 --- a/theseus/src/launcher/download.rs +++ b/theseus/src/launcher/download.rs @@ -19,7 +19,7 @@ use daedalus::{ use futures::prelude::*; use tokio::{fs, sync::OnceCell}; -#[tracing::instrument(skip_all)] +#[tracing::instrument(skip(st, version))] pub async fn download_minecraft( st: &State, version: &GameVersionInfo, diff --git a/theseus/src/launcher/mod.rs b/theseus/src/launcher/mod.rs index da81f1d3..c052f09a 100644 --- a/theseus/src/launcher/mod.rs +++ b/theseus/src/launcher/mod.rs @@ -57,12 +57,12 @@ macro_rules! processor_rules { } } -async fn get_java_version_from_profile( +pub async fn get_java_version_from_profile( profile: &Profile, version_info: &VersionInfo, -) -> crate::Result { +) -> crate::Result> { if let Some(java) = profile.java.clone().and_then(|x| x.override_version) { - Ok(java) + Ok(Some(java)) } else { let optimal_keys = match version_info .java_version @@ -80,14 +80,11 @@ async fn get_java_version_from_profile( for key in optimal_keys { if let Some(java) = settings.java_globals.get(&key.to_string()) { - return Ok(java.clone()); + return Ok(Some(java.clone())); } } - Err(crate::ErrorKind::LauncherError( - "No available java installation".to_string(), - ) - .into()) + Ok(None) } } @@ -149,8 +146,13 @@ pub async fn install_minecraft( ) .await?; - let java_version = - get_java_version_from_profile(profile, &version_info).await?; + let java_version = get_java_version_from_profile(profile, &version_info) + .await? + .ok_or_else(|| { + crate::ErrorKind::LauncherError( + "No available java installation".to_string(), + ) + })?; // Download minecraft (5-90) download::download_minecraft( @@ -327,8 +329,13 @@ pub async fn launch_minecraft( ) .await?; - let java_version = - get_java_version_from_profile(profile, &version_info).await?; + let java_version = get_java_version_from_profile(profile, &version_info) + .await? + .ok_or_else(|| { + crate::ErrorKind::LauncherError( + "No available java installation".to_string(), + ) + })?; let client_path = state .directories diff --git a/theseus/src/state/children.rs b/theseus/src/state/children.rs index 0c957b04..33d04aa0 100644 --- a/theseus/src/state/children.rs +++ b/theseus/src/state/children.rs @@ -113,7 +113,7 @@ impl Children { // Spawns a new child process and inserts it into the hashmap // Also, as the process ends, it spawns the follow-up process if it exists // By convention, ExitStatus is last command's exit status, and we exit on the first non-zero exit status - #[tracing::instrument] + #[tracing::instrument(skip(current_child))] #[theseus_macros::debug_pin] async fn sequential_process_manager( uuid: Uuid, diff --git a/theseus/src/state/metadata.rs b/theseus/src/state/metadata.rs index 276d993c..38830088 100644 --- a/theseus/src/state/metadata.rs +++ b/theseus/src/state/metadata.rs @@ -54,7 +54,7 @@ impl Metadata { } // Attempt to fetch metadata and store in sled DB - #[tracing::instrument] + #[tracing::instrument(skip(io_semaphore))] #[theseus_macros::debug_pin] pub async fn init( dirs: &DirectoryInfo, diff --git a/theseus/src/state/profiles.rs b/theseus/src/state/profiles.rs index cbadcf7d..dcd80823 100644 --- a/theseus/src/state/profiles.rs +++ b/theseus/src/state/profiles.rs @@ -130,6 +130,8 @@ pub struct JavaSettings { pub override_version: Option, #[serde(skip_serializing_if = "Option::is_none")] pub extra_arguments: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub custom_env_args: Option>, } impl Profile { @@ -169,7 +171,7 @@ impl Profile { }) } - #[tracing::instrument] + #[tracing::instrument(skip(self, semaphore, icon))] pub async fn set_icon<'a>( &'a mut self, cache_dir: &Path, @@ -295,7 +297,7 @@ impl Profile { Ok(()) } - #[tracing::instrument] + #[tracing::instrument(skip(self))] #[theseus_macros::debug_pin] pub async fn add_project_version( &self, @@ -342,7 +344,7 @@ impl Profile { Ok((path, version)) } - #[tracing::instrument] + #[tracing::instrument(skip(self, bytes))] #[theseus_macros::debug_pin] pub async fn add_project_bytes( &self, @@ -412,7 +414,7 @@ impl Profile { Ok(path) } - #[tracing::instrument] + #[tracing::instrument(skip(self))] #[theseus_macros::debug_pin] pub async fn toggle_disable_project( &self, @@ -577,7 +579,7 @@ impl Profiles { }; } - #[tracing::instrument(skip(self))] + #[tracing::instrument(skip(self, profile))] #[theseus_macros::debug_pin] pub async fn insert(&mut self, profile: Profile) -> crate::Result<&Self> { emit_profile( diff --git a/theseus/src/state/projects.rs b/theseus/src/state/projects.rs index 105fa9f9..fa5f2e34 100644 --- a/theseus/src/state/projects.rs +++ b/theseus/src/state/projects.rs @@ -200,7 +200,7 @@ pub enum ProjectMetadata { Unknown, } -#[tracing::instrument] +#[tracing::instrument(skip(io_semaphore))] #[theseus_macros::debug_pin] async fn read_icon_from_file( icon_path: Option, @@ -251,7 +251,7 @@ async fn read_icon_from_file( Ok(None) } -#[tracing::instrument] +#[tracing::instrument(skip(profile, io_semaphore, fetch_semaphore))] #[theseus_macros::debug_pin] pub async fn infer_data_from_files( profile: Profile, diff --git a/theseus/src/state/tags.rs b/theseus/src/state/tags.rs index a03036fb..dab2762c 100644 --- a/theseus/src/state/tags.rs +++ b/theseus/src/state/tags.rs @@ -20,12 +20,12 @@ pub struct Tags { } impl Tags { - #[tracing::instrument] + #[tracing::instrument(skip(io_semaphore, fetch_semaphore))] #[theseus_macros::debug_pin] pub async fn init( dirs: &DirectoryInfo, io_semaphore: &IoSemaphore, - fetch_sempahore: &FetchSemaphore, + fetch_semaphore: &FetchSemaphore, ) -> crate::Result { let mut tags = None; let tags_path = dirs.caches_meta_dir().join("tags.json"); @@ -34,7 +34,7 @@ impl Tags { { tags = Some(tags_json); } else { - match Self::fetch(fetch_sempahore).await { + match Self::fetch(fetch_semaphore).await { Ok(tags_fetch) => tags = Some(tags_fetch), Err(err) => { tracing::warn!("Unable to fetch launcher tags: {err}") diff --git a/theseus_gui/package.json b/theseus_gui/package.json index a0946cef..198fc6b5 100644 --- a/theseus_gui/package.json +++ b/theseus_gui/package.json @@ -15,7 +15,7 @@ "dependencies": { "@tauri-apps/api": "^1.2.0", "ofetch": "^1.0.1", - "omorphia": "^0.4.13", + "omorphia": "^0.4.16", "pinia": "^2.0.33", "vite-svg-loader": "^4.0.0", "vue": "^3.2.45", diff --git a/theseus_gui/src-tauri/Cargo.toml b/theseus_gui/src-tauri/Cargo.toml index e01113db..7371495d 100644 --- a/theseus_gui/src-tauri/Cargo.toml +++ b/theseus_gui/src-tauri/Cargo.toml @@ -16,11 +16,10 @@ regex = "1.5" [dependencies] theseus = { path = "../../theseus", features = ["tauri"] } -theseus_macros = { path = "../../theseus_macros" } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.2", features = ["dialog", "dialog-open", "protocol-asset", "window-close", "window-create"] } +tauri = { version = "1.2", features = ["dialog", "dialog-open", "protocol-asset", "shell-open", "window-close", "window-create"] } tokio = { version = "1", features = ["full"] } thiserror = "1.0" tokio-stream = { version = "0.1", features = ["fs"] } diff --git a/theseus_gui/src-tauri/src/api/auth.rs b/theseus_gui/src-tauri/src/api/auth.rs index ffcedd89..52292e6b 100644 --- a/theseus_gui/src-tauri/src/api/auth.rs +++ b/theseus_gui/src-tauri/src/api/auth.rs @@ -4,7 +4,6 @@ use theseus::prelude::*; /// 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] -#[theseus_macros::debug_pin] pub async fn auth_authenticate_begin_flow() -> Result { Ok(auth::authenticate_begin_flow().await?) } @@ -13,7 +12,6 @@ pub async fn auth_authenticate_begin_flow() -> Result { /// This completes the authentication flow quasi-synchronously, returning the sign-in credentials /// (and also adding the credentials to the state) #[tauri::command] -#[theseus_macros::debug_pin] pub async fn auth_authenticate_await_completion() -> Result { Ok(auth::authenticate_await_complete_flow().await?) } @@ -21,13 +19,11 @@ pub async fn auth_authenticate_await_completion() -> Result { /// Refresh some credentials using Hydra, if needed // invoke('auth_refresh',user) #[tauri::command] -#[theseus_macros::debug_pin] pub async fn auth_refresh(user: uuid::Uuid) -> Result { Ok(auth::refresh(user).await?) } #[tauri::command] -#[theseus_macros::debug_pin] pub async fn auth_remove_user(user: uuid::Uuid) -> Result<()> { Ok(auth::remove_user(user).await?) } @@ -35,7 +31,6 @@ pub async fn auth_remove_user(user: uuid::Uuid) -> Result<()> { /// Check if a user exists in Theseus // invoke('auth_has_user',user) #[tauri::command] -#[theseus_macros::debug_pin] pub async fn auth_has_user(user: uuid::Uuid) -> Result { Ok(auth::has_user(user).await?) } @@ -43,7 +38,6 @@ pub async fn auth_has_user(user: uuid::Uuid) -> Result { /// Get a copy of the list of all user credentials // invoke('auth_users',user) #[tauri::command] -#[theseus_macros::debug_pin] pub async fn auth_users() -> Result> { Ok(auth::users().await?) } @@ -52,7 +46,6 @@ pub async fn auth_users() -> Result> { /// Prefer to use refresh instead, as it will refresh the credentials as well // invoke('auth_users',user) #[tauri::command] -#[theseus_macros::debug_pin] 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 4337e48f..e9bfe293 100644 --- a/theseus_gui/src-tauri/src/api/jre.rs +++ b/theseus_gui/src-tauri/src/api/jre.rs @@ -6,28 +6,24 @@ use theseus::prelude::*; /// Get all JREs that exist on the system #[tauri::command] -#[theseus_macros::debug_pin] pub async fn jre_get_all_jre() -> Result> { Ok(jre::get_all_jre().await?) } // Finds the isntallation of Java 7, if it exists #[tauri::command] -#[theseus_macros::debug_pin] pub async fn jre_find_jre_8_jres() -> Result> { Ok(jre::find_java8_jres().await?) } // finds the installation of Java 17, if it exists #[tauri::command] -#[theseus_macros::debug_pin] pub async fn jre_find_jre_17_jres() -> Result> { Ok(jre::find_java17_jres().await?) } // Finds the highest version of Java 18+, if it exists #[tauri::command] -#[theseus_macros::debug_pin] pub async fn jre_find_jre_18plus_jres() -> Result> { Ok(jre::find_java18plus_jres().await?) } @@ -35,7 +31,6 @@ pub async fn jre_find_jre_18plus_jres() -> Result> { // Autodetect Java globals, by searching the users computer. // Returns a *NEW* JavaGlobals that can be put into Settings #[tauri::command] -#[theseus_macros::debug_pin] pub async fn jre_autodetect_java_globals() -> Result { Ok(jre::autodetect_java_globals().await?) } @@ -43,7 +38,6 @@ pub async fn jre_autodetect_java_globals() -> Result { // Validates java globals, by checking if the paths exist // If false, recommend to direct them to reassign, or to re-guess #[tauri::command] -#[theseus_macros::debug_pin] pub async fn jre_validate_globals() -> Result { Ok(jre::validate_globals().await?) } @@ -51,21 +45,18 @@ pub async fn jre_validate_globals() -> Result { // Validates JRE at a given path // Returns None if the path is not a valid JRE #[tauri::command] -#[theseus_macros::debug_pin] pub async fn jre_get_jre(path: PathBuf) -> Result> { jre::check_jre(path).await.map_err(|e| e.into()) } // Auto installs java for the given java version #[tauri::command] -#[theseus_macros::debug_pin] pub async fn jre_auto_install_java(java_version: u32) -> Result { Ok(jre::auto_install_java(java_version).await?) } // Gets the maximum memory a system has available. #[tauri::command] -#[theseus_macros::debug_pin] pub async fn jre_get_max_memory() -> Result { Ok(jre::get_max_memory().await?) } diff --git a/theseus_gui/src-tauri/src/api/logs.rs b/theseus_gui/src-tauri/src/api/logs.rs index ba5f4f29..8e038a59 100644 --- a/theseus_gui/src-tauri/src/api/logs.rs +++ b/theseus_gui/src-tauri/src/api/logs.rs @@ -14,7 +14,6 @@ pub struct Logs { /// Get all Logs for a profile, sorted by datetime #[tauri::command] -#[theseus_macros::debug_pin] pub async fn logs_get_logs( profile_uuid: Uuid, clear_contents: Option, @@ -30,7 +29,6 @@ pub async fn logs_get_logs( /// Get a Log struct for a profile by profile id and datetime string #[tauri::command] -#[theseus_macros::debug_pin] pub async fn logs_get_logs_by_datetime( profile_uuid: Uuid, datetime_string: String, @@ -40,7 +38,6 @@ pub async fn logs_get_logs_by_datetime( /// Get the stdout for a profile by profile id and datetime string #[tauri::command] -#[theseus_macros::debug_pin] pub async fn logs_get_stdout_by_datetime( profile_uuid: Uuid, datetime_string: String, @@ -50,7 +47,6 @@ pub async fn logs_get_stdout_by_datetime( /// Get the stderr for a profile by profile id and datetime string #[tauri::command] -#[theseus_macros::debug_pin] pub async fn logs_get_stderr_by_datetime( profile_uuid: Uuid, datetime_string: String, @@ -60,14 +56,12 @@ pub async fn logs_get_stderr_by_datetime( /// Delete all logs for a profile by profile id #[tauri::command] -#[theseus_macros::debug_pin] pub async fn logs_delete_logs(profile_uuid: Uuid) -> Result<()> { Ok(logs::delete_logs(profile_uuid).await?) } /// Delete a log for a profile by profile id and datetime string #[tauri::command] -#[theseus_macros::debug_pin] pub async fn logs_delete_logs_by_datetime( profile_uuid: Uuid, datetime_string: String, diff --git a/theseus_gui/src-tauri/src/api/metadata.rs b/theseus_gui/src-tauri/src/api/metadata.rs index 9d1d2baf..807f5ec3 100644 --- a/theseus_gui/src-tauri/src/api/metadata.rs +++ b/theseus_gui/src-tauri/src/api/metadata.rs @@ -4,28 +4,24 @@ use daedalus::modded::Manifest; /// Gets the game versions from daedalus #[tauri::command] -#[theseus_macros::debug_pin] pub async fn metadata_get_game_versions() -> Result { Ok(theseus::metadata::get_minecraft_versions().await?) } /// Gets the fabric versions from daedalus #[tauri::command] -#[theseus_macros::debug_pin] pub async fn metadata_get_fabric_versions() -> Result { Ok(theseus::metadata::get_fabric_versions().await?) } /// Gets the forge versions from daedalus #[tauri::command] -#[theseus_macros::debug_pin] pub async fn metadata_get_forge_versions() -> Result { Ok(theseus::metadata::get_forge_versions().await?) } /// Gets the quilt versions from daedalus #[tauri::command] -#[theseus_macros::debug_pin] pub async fn metadata_get_quilt_versions() -> Result { Ok(theseus::metadata::get_quilt_versions().await?) } diff --git a/theseus_gui/src-tauri/src/api/mod.rs b/theseus_gui/src-tauri/src/api/mod.rs index 4e739ecb..3905de05 100644 --- a/theseus_gui/src-tauri/src/api/mod.rs +++ b/theseus_gui/src-tauri/src/api/mod.rs @@ -47,7 +47,6 @@ where // 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] -#[theseus_macros::debug_pin] pub async fn progress_bars_list( ) -> Result> { let res = theseus::EventState::list_progress_bars().await?; diff --git a/theseus_gui/src-tauri/src/api/pack.rs b/theseus_gui/src-tauri/src/api/pack.rs index 0bff85e9..8872c8b9 100644 --- a/theseus_gui/src-tauri/src/api/pack.rs +++ b/theseus_gui/src-tauri/src/api/pack.rs @@ -3,7 +3,6 @@ use std::path::{Path, PathBuf}; use theseus::prelude::*; #[tauri::command] -#[theseus_macros::debug_pin] pub async fn pack_install_version_id( version_id: String, pack_title: String, @@ -16,7 +15,6 @@ pub async fn pack_install_version_id( } #[tauri::command] -#[theseus_macros::debug_pin] pub async fn pack_install_file(path: &Path) -> Result { let res = pack::install_pack_from_file(path.to_path_buf()).await?; Ok(res) diff --git a/theseus_gui/src-tauri/src/api/process.rs b/theseus_gui/src-tauri/src/api/process.rs index 6dbf7634..87cd0813 100644 --- a/theseus_gui/src-tauri/src/api/process.rs +++ b/theseus_gui/src-tauri/src/api/process.rs @@ -6,14 +6,12 @@ use uuid::Uuid; // Checks if a process has finished by process UUID #[tauri::command] -#[theseus_macros::debug_pin] pub async fn process_has_finished_by_uuid(uuid: Uuid) -> Result { Ok(process::has_finished_by_uuid(&uuid).await?) } // Gets process exit status by process UUID #[tauri::command] -#[theseus_macros::debug_pin] pub async fn process_get_exit_status_by_uuid( uuid: Uuid, ) -> Result> { @@ -22,21 +20,18 @@ pub async fn process_get_exit_status_by_uuid( // Gets all process UUIDs #[tauri::command] -#[theseus_macros::debug_pin] pub async fn process_get_all_uuids() -> Result> { Ok(process::get_all_uuids().await?) } // Gets all running process UUIDs #[tauri::command] -#[theseus_macros::debug_pin] pub async fn process_get_all_running_uuids() -> Result> { Ok(process::get_all_running_uuids().await?) } // Gets all process UUIDs by profile path #[tauri::command] -#[theseus_macros::debug_pin] pub async fn process_get_uuids_by_profile_path( profile_path: &Path, ) -> Result> { @@ -45,42 +40,36 @@ pub async fn process_get_uuids_by_profile_path( // Gets the Profile paths of each *running* stored process in the state #[tauri::command] -#[theseus_macros::debug_pin] pub async fn process_get_all_running_profile_paths() -> Result> { Ok(process::get_all_running_profile_paths().await?) } // Gets the Profiles (cloned) of each *running* stored process in the state #[tauri::command] -#[theseus_macros::debug_pin] pub async fn process_get_all_running_profiles() -> Result> { Ok(process::get_all_running_profiles().await?) } // Gets process stderr by process UUID #[tauri::command] -#[theseus_macros::debug_pin] pub async fn process_get_stderr_by_uuid(uuid: Uuid) -> Result { Ok(process::get_stderr_by_uuid(&uuid).await?) } // Gets process stdout by process UUID #[tauri::command] -#[theseus_macros::debug_pin] pub async fn process_get_stdout_by_uuid(uuid: Uuid) -> Result { Ok(process::get_stdout_by_uuid(&uuid).await?) } // Kill a process by process UUID #[tauri::command] -#[theseus_macros::debug_pin] pub async fn process_kill_by_uuid(uuid: Uuid) -> Result<()> { Ok(process::kill_by_uuid(&uuid).await?) } // Wait for a process to finish by process UUID #[tauri::command] -#[theseus_macros::debug_pin] pub async fn process_wait_for_by_uuid(uuid: Uuid) -> Result<()> { Ok(process::wait_for_by_uuid(&uuid).await?) } diff --git a/theseus_gui/src-tauri/src/api/profile.rs b/theseus_gui/src-tauri/src/api/profile.rs index 991d4537..94233d7f 100644 --- a/theseus_gui/src-tauri/src/api/profile.rs +++ b/theseus_gui/src-tauri/src/api/profile.rs @@ -9,7 +9,6 @@ use uuid::Uuid; // Remove a profile // invoke('profile_add_path',path) #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_remove(path: &Path) -> Result<()> { profile::remove(path).await?; Ok(()) @@ -18,7 +17,6 @@ pub async fn profile_remove(path: &Path) -> Result<()> { // Get a profile by path // invoke('profile_add_path',path) #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_get( path: &Path, clear_projects: Option, @@ -27,10 +25,18 @@ pub async fn profile_get( Ok(res) } +// Get optimal java version from profile +#[tauri::command] +pub async fn profile_get_optimal_jre_key( + path: &Path, +) -> Result> { + let res = profile::get_optimal_jre_key(path).await?; + Ok(res) +} + // Get a copy of the profile set // invoke('profile_list') #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_list( clear_projects: Option, ) -> Result> { @@ -39,7 +45,6 @@ pub async fn profile_list( } #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_check_installed( path: &Path, project_id: String, @@ -62,7 +67,6 @@ pub async fn profile_check_installed( /// Installs/Repairs a profile /// invoke('profile_install') #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_install(path: &Path) -> Result<()> { profile::install(path).await?; Ok(()) @@ -71,7 +75,6 @@ pub async fn profile_install(path: &Path) -> Result<()> { /// Updates all of the profile's projects /// invoke('profile_update_all') #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_update_all( path: &Path, ) -> Result> { @@ -81,7 +84,6 @@ pub async fn profile_update_all( /// Updates a specified project /// invoke('profile_update_project') #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_update_project( path: &Path, project_path: &Path, @@ -92,7 +94,6 @@ pub async fn profile_update_project( // Adds a project to a profile from a version ID // invoke('profile_add_project_from_version') #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_add_project_from_version( path: &Path, version_id: String, @@ -103,7 +104,6 @@ pub async fn profile_add_project_from_version( // Adds a project to a profile from a path // invoke('profile_add_project_from_path') #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_add_project_from_path( path: &Path, project_path: &Path, @@ -117,7 +117,6 @@ pub async fn profile_add_project_from_path( // Toggles disabling a project from its path // invoke('profile_toggle_disable_project') #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_toggle_disable_project( path: &Path, project_path: &Path, @@ -128,7 +127,6 @@ pub async fn profile_toggle_disable_project( // Removes a project from a profile // invoke('profile_remove_project') #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_remove_project( path: &Path, project_path: &Path, @@ -141,7 +139,6 @@ pub async fn profile_remove_project( // for the actual Child in the state. // invoke('profile_run', path) #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_run(path: &Path) -> Result { let minecraft_child = profile::run(path).await?; let uuid = minecraft_child.read().await.uuid; @@ -151,7 +148,6 @@ 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) #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_run_wait(path: &Path) -> Result<()> { let proc_lock = profile::run(path).await?; let mut proc = proc_lock.write().await; @@ -163,7 +159,6 @@ pub async fn profile_run_wait(path: &Path) -> Result<()> { // for the actual Child in the state. // invoke('profile_run_credentials', {path, credentials})') #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_run_credentials( path: &Path, credentials: Credentials, @@ -176,7 +171,6 @@ 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) #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_run_wait_credentials( path: &Path, credentials: Credentials, @@ -206,7 +200,6 @@ pub struct EditProfileMetadata { // Edits a profile // invoke('profile_edit', {path, editProfile}) #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_edit( path: &Path, edit_profile: EditProfile, @@ -236,3 +229,14 @@ pub async fn profile_edit( Ok(()) } + +// Edits a profile's icon +// invoke('profile_edit_icon') +#[tauri::command] +pub async fn profile_edit_icon( + path: &Path, + icon_path: Option<&Path>, +) -> Result<()> { + profile::edit_icon(path, icon_path).await?; + Ok(()) +} diff --git a/theseus_gui/src-tauri/src/api/profile_create.rs b/theseus_gui/src-tauri/src/api/profile_create.rs index 70e0e592..24820dc8 100644 --- a/theseus_gui/src-tauri/src/api/profile_create.rs +++ b/theseus_gui/src-tauri/src/api/profile_create.rs @@ -5,7 +5,6 @@ use theseus::prelude::*; // Generic basic profile creation tool. // Creates an essentially empty dummy profile with profile_create #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_create_empty() -> Result { let res = profile_create::profile_create_empty().await?; Ok(res) @@ -14,19 +13,18 @@ pub async fn profile_create_empty() -> Result { // Creates a profile at the given filepath and adds it to the in-memory state // invoke('profile_add',profile) #[tauri::command] -#[theseus_macros::debug_pin] pub async fn profile_create( - name: String, // the name of the profile, and relative path - game_version: String, // the game version of the profile - modloader: ModLoader, // the modloader to use - loader_version: String, // the modloader version to use, set to "latest", "stable", or the ID of your chosen loader - icon: Option, // the icon for the profile + name: String, // the name of the profile, and relative path + game_version: String, // the game version of the profile + modloader: ModLoader, // the modloader to use + loader_version: Option, // the modloader version to use, set to "latest", "stable", or the ID of your chosen loader + icon: Option, // the icon for the profile ) -> Result { let res = profile_create::profile_create( name, game_version, modloader, - Some(loader_version), + loader_version, icon, None, None, diff --git a/theseus_gui/src-tauri/src/api/settings.rs b/theseus_gui/src-tauri/src/api/settings.rs index ffd9f3f9..4a8bb55d 100644 --- a/theseus_gui/src-tauri/src/api/settings.rs +++ b/theseus_gui/src-tauri/src/api/settings.rs @@ -23,68 +23,15 @@ pub struct FrontendSettings { // Get full settings // invoke('settings_get') #[tauri::command] -#[theseus_macros::debug_pin] -pub async fn settings_get() -> Result { - let backend_settings = settings::get().await?; - let frontend_settings = FrontendSettings { - theme: backend_settings.theme, - memory: backend_settings.memory, - game_resolution: backend_settings.game_resolution, - custom_java_args: backend_settings.custom_java_args.join(" "), - custom_env_args: backend_settings - .custom_env_args - .into_iter() - .map(|(s1, s2)| format!("{s1}={s2}")) - .collect::>() - .join(" "), - java_globals: backend_settings.java_globals, - default_user: backend_settings.default_user, - hooks: backend_settings.hooks, - max_concurrent_downloads: backend_settings.max_concurrent_downloads, - max_concurrent_writes: backend_settings.max_concurrent_writes, - version: backend_settings.version, - collapsed_navigation: backend_settings.collapsed_navigation, - }; - Ok(frontend_settings) +pub async fn settings_get() -> Result { + let res = settings::get().await?; + Ok(res) } // Set full settings // invoke('settings_set', settings) #[tauri::command] -#[theseus_macros::debug_pin] -pub async fn settings_set(settings: FrontendSettings) -> Result<()> { - let custom_env_args: Vec<(String, String)> = settings - .custom_env_args - .split_whitespace() - .map(|s| s.to_string()) - .flat_map(|f| { - let mut split = f.split('='); - if let (Some(name), Some(value)) = (split.next(), split.next()) { - Some((name.to_string(), value.to_string())) - } else { - None - } - }) - .collect::>(); - - let backend_settings = Settings { - theme: settings.theme, - memory: settings.memory, - game_resolution: settings.game_resolution, - custom_java_args: settings - .custom_java_args - .split_whitespace() - .map(|s| s.to_string()) - .collect(), - custom_env_args, - java_globals: settings.java_globals, - default_user: settings.default_user, - hooks: settings.hooks, - max_concurrent_downloads: settings.max_concurrent_downloads, - max_concurrent_writes: settings.max_concurrent_writes, - version: settings.version, - collapsed_navigation: settings.collapsed_navigation, - }; - settings::set(backend_settings).await?; +pub async fn settings_set(settings: Settings) -> Result<()> { + settings::set(settings).await?; Ok(()) } diff --git a/theseus_gui/src-tauri/src/api/tags.rs b/theseus_gui/src-tauri/src/api/tags.rs index 1d6b3b84..0ae2bad6 100644 --- a/theseus_gui/src-tauri/src/api/tags.rs +++ b/theseus_gui/src-tauri/src/api/tags.rs @@ -3,42 +3,36 @@ use theseus::tags::{Category, DonationPlatform, GameVersion, Loader, Tags}; /// Gets cached category tags from the database #[tauri::command] -#[theseus_macros::debug_pin] pub async fn tags_get_categories() -> Result> { Ok(theseus::tags::get_category_tags().await?) } /// Gets cached report type tags from the database #[tauri::command] -#[theseus_macros::debug_pin] pub async fn tags_get_report_types() -> Result> { Ok(theseus::tags::get_report_type_tags().await?) } /// Gets cached loader tags from the database #[tauri::command] -#[theseus_macros::debug_pin] pub async fn tags_get_loaders() -> Result> { Ok(theseus::tags::get_loader_tags().await?) } /// Gets cached game version tags from the database #[tauri::command] -#[theseus_macros::debug_pin] pub async fn tags_get_game_versions() -> Result> { Ok(theseus::tags::get_game_version_tags().await?) } /// Gets cached donation platform tags from the database #[tauri::command] -#[theseus_macros::debug_pin] pub async fn tags_get_donation_platforms() -> Result> { Ok(theseus::tags::get_donation_platform_tags().await?) } /// Gets cached tag bundle from the database #[tauri::command] -#[theseus_macros::debug_pin] pub async fn tags_get_tag_bundle() -> Result { Ok(theseus::tags::get_tag_bundle().await?) } diff --git a/theseus_gui/src-tauri/src/main.rs b/theseus_gui/src-tauri/src/main.rs index e4f38a7d..ce9e0504 100644 --- a/theseus_gui/src-tauri/src/main.rs +++ b/theseus_gui/src-tauri/src/main.rs @@ -13,7 +13,6 @@ mod error; // Should be called in launcher initialization #[tauri::command] -#[theseus_macros::debug_pin] async fn initialize_state(app: tauri::AppHandle) -> api::Result<()> { theseus::EventState::init(app).await?; State::get().await?; @@ -25,7 +24,6 @@ async fn initialize_state(app: tauri::AppHandle) -> api::Result<()> { // disables mouseover and fixes a random crash error only fixed by recent versions of macos #[cfg(target_os = "macos")] #[tauri::command] -#[theseus_macros::debug_pin] async fn should_disable_mouseover() -> bool { // We try to match version to 12.2 or higher. If unrecognizable to pattern or lower, we default to the css with disabled mouseover for safety let os = os_info::get(); @@ -39,7 +37,6 @@ async fn should_disable_mouseover() -> bool { } #[cfg(not(target_os = "macos"))] #[tauri::command] -#[theseus_macros::debug_pin] async fn should_disable_mouseover() -> bool { false } @@ -81,6 +78,7 @@ fn main() { 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, @@ -94,6 +92,7 @@ fn main() { 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, diff --git a/theseus_gui/src-tauri/tauri.conf.json b/theseus_gui/src-tauri/tauri.conf.json index f0dee441..d0b179d8 100644 --- a/theseus_gui/src-tauri/tauri.conf.json +++ b/theseus_gui/src-tauri/tauri.conf.json @@ -23,6 +23,9 @@ "$CONFIG/caches/icons/*" ] }, + "shell": { + "open": true + }, "window": { "create": true, "close": true diff --git a/theseus_gui/src/App.vue b/theseus_gui/src/App.vue index eea97419..fc910919 100644 --- a/theseus_gui/src/App.vue +++ b/theseus_gui/src/App.vue @@ -91,7 +91,7 @@ const loading = useLoading() @click="() => $refs.installationModal.show()" > - New Instance + New instance diff --git a/theseus_gui/src/assets/icons/index.js b/theseus_gui/src/assets/icons/index.js index 6f730ae4..580ee560 100644 --- a/theseus_gui/src/assets/icons/index.js +++ b/theseus_gui/src/assets/icons/index.js @@ -5,3 +5,4 @@ export { default as LoginIcon } from './log-in.svg' export { default as StopIcon } from './stop-circle.svg' export { default as TerminalIcon } from './terminal-square.svg' export { default as UsersIcon } from './users.svg' +export { default as HammerIcon } from './hammer.svg' diff --git a/theseus_gui/src/components/ui/InstallConfirmModal.vue b/theseus_gui/src/components/ui/InstallConfirmModal.vue index 065a521d..5e7a13f0 100644 --- a/theseus_gui/src/components/ui/InstallConfirmModal.vue +++ b/theseus_gui/src/components/ui/InstallConfirmModal.vue @@ -2,9 +2,6 @@ import { Button, Modal, XIcon, DownloadIcon } from 'omorphia' import { install as pack_install } from '@/helpers/pack' import { ref } from 'vue' -import { useRouter } from 'vue-router' - -const router = useRouter() const version = ref('') const title = ref('') @@ -23,9 +20,7 @@ defineExpose({ async function install() { installing.value = true - let id = await pack_install(version.value) await pack_install(version.value, title.value, icon.value ? icon.value : null) - await router.push({ path: `/instance/${encodeURIComponent(id)}` }) confirmModal.value.hide() } diff --git a/theseus_gui/src/components/ui/InstanceCreationModal.vue b/theseus_gui/src/components/ui/InstanceCreationModal.vue index 42d8ba8e..b40a317b 100644 --- a/theseus_gui/src/components/ui/InstanceCreationModal.vue +++ b/theseus_gui/src/components/ui/InstanceCreationModal.vue @@ -18,14 +18,14 @@

Name

+
+

Loader

+ +

Game Version

- +
-
-

Loader

- -
-
+

Loader Version

-
+

Select Version

{ return all_game_versions.value - .filter((item) => item.version_type === 'release' || showSnapshots.value) + .filter((item) => { + let defaultVal = item.version_type === 'release' || showSnapshots.value + if (loader.value === 'fabric') { + defaultVal &= fabric_versions.value.gameVersions.some((x) => item.version === x.id) + } else if (loader.value === 'forge') { + defaultVal &= forge_versions.value.gameVersions.some((x) => item.version === x.id) + } else if (loader.value === 'quilt') { + defaultVal &= quilt_versions.value.gameVersions.some((x) => item.version === x.id) + } + + return defaultVal + }) .map((item) => item.version) }) const modal = ref(null) const check_valid = computed(() => { - return profile_name.value && game_version.value + return ( + profile_name.value && game_version.value && game_versions.value.includes(game_version.value) + ) }) const create_instance = async () => { @@ -166,7 +176,7 @@ const create_instance = async () => { profile_name.value, game_version.value, loader.value, - loader_version_value ?? 'stable', + loader.value === 'vanilla' ? null : loader_version_value ?? 'stable', icon.value ) @@ -185,7 +195,7 @@ const upload_icon = async () => { filters: [ { name: 'Image', - extensions: ['png', 'jpeg'], + extensions: ['png', 'jpeg', 'svg', 'webp', 'gif', 'jpg'], }, ], }) diff --git a/theseus_gui/src/components/ui/JavaDetectionModal.vue b/theseus_gui/src/components/ui/JavaDetectionModal.vue new file mode 100644 index 00000000..122327a6 --- /dev/null +++ b/theseus_gui/src/components/ui/JavaDetectionModal.vue @@ -0,0 +1,109 @@ + + + diff --git a/theseus_gui/src/components/ui/JavaSelector.vue b/theseus_gui/src/components/ui/JavaSelector.vue new file mode 100644 index 00000000..21ac5d68 --- /dev/null +++ b/theseus_gui/src/components/ui/JavaSelector.vue @@ -0,0 +1,149 @@ + + + + + + diff --git a/theseus_gui/src/helpers/profile.js b/theseus_gui/src/helpers/profile.js index a9a92afe..85358eaa 100644 --- a/theseus_gui/src/helpers/profile.js +++ b/theseus_gui/src/helpers/profile.js @@ -36,6 +36,12 @@ export async function get(path, clearProjects) { return await invoke('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 }) +} + // Get a copy of the profile set // Returns hashmap of path -> Profile export async function list(clearProjects) { @@ -99,3 +105,8 @@ export async function run_wait(path) { export async function edit(path, editProfile) { return await invoke('profile_edit', { path, editProfile }) } + +// Edits a profile's icon +export async function edit_icon(path, iconPath) { + return await invoke('profile_edit_icon', { path, iconPath }) +} diff --git a/theseus_gui/src/helpers/settings.js b/theseus_gui/src/helpers/settings.js index c214dad5..8d027a33 100644 --- a/theseus_gui/src/helpers/settings.js +++ b/theseus_gui/src/helpers/settings.js @@ -28,20 +28,6 @@ Memorysettings { */ -// An example test function for getting/setting settings -export async function test() { - // First, print settings and store them to an object - let settings = await get() - console.log(JSON.stringify(settings)) - - // Then set some random settings in that object - settings.java_8_path = '/example/path' - - // Set the new settings object - await set(settings) - console.log(JSON.stringify(await get())) -} - // Get full settings object export async function get() { return await invoke('settings_get') diff --git a/theseus_gui/src/pages/Browse.vue b/theseus_gui/src/pages/Browse.vue index 4622bcff..996e10fe 100644 --- a/theseus_gui/src/pages/Browse.vue +++ b/theseus_gui/src/pages/Browse.vue @@ -15,6 +15,7 @@ import { NavRow, formatCategoryHeader, formatCategory, + Promotion, } from 'omorphia' import Multiselect from 'vue-multiselect' import { useSearch } from '@/store/state' @@ -271,6 +272,7 @@ const handleInstanceSwitch = async (value) => { - diff --git a/theseus_gui/src/pages/instance/Index.vue b/theseus_gui/src/pages/instance/Index.vue index 244945f3..37d0f857 100644 --- a/theseus_gui/src/pages/instance/Index.vue +++ b/theseus_gui/src/pages/instance/Index.vue @@ -18,8 +18,11 @@
+ - + Loading... +
@@ -371,12 +378,4 @@ Button { } } } - -.card-divider { - background-color: var(--color-button-bg); - border: none; - color: var(--color-button-bg); - height: 1px; - margin: var(--gap-xl) 0; -} diff --git a/theseus_gui/src/pages/instance/Options.vue b/theseus_gui/src/pages/instance/Options.vue index f5f9fce9..5fd0c434 100644 --- a/theseus_gui/src/pages/instance/Options.vue +++ b/theseus_gui/src/pages/instance/Options.vue @@ -1,139 +1,504 @@ diff --git a/theseus_gui/src/pages/project/Index.vue b/theseus_gui/src/pages/project/Index.vue index b539db65..07d75e74 100644 --- a/theseus_gui/src/pages/project/Index.vue +++ b/theseus_gui/src/pages/project/Index.vue @@ -81,11 +81,11 @@

- - @@ -96,7 +96,8 @@ v-if="data.issues_url" :href="data.issues_url" class="title" - rel="noopener nofollow ugc" + rel="noopener nofollow ugc external" + target="_blank" >