diff --git a/theseus/src/api/auth.rs b/theseus/src/api/auth.rs index 389d62c9b..362b8cd26 100644 --- a/theseus/src/api/auth.rs +++ b/theseus/src/api/auth.rs @@ -57,6 +57,7 @@ pub async fn authenticate( Ok(credentials) } + /// Refresh some credentials using Hydra, if needed /// This is the primary desired way to get credentials, as it will also refresh them. #[tracing::instrument] diff --git a/theseus/src/api/profile.rs b/theseus/src/api/profile.rs index a1b635ae5..bf22233af 100644 --- a/theseus/src/api/profile.rs +++ b/theseus/src/api/profile.rs @@ -1,5 +1,9 @@ //! Theseus profile management interface -use crate::{launcher::download, state::MinecraftChild}; +use crate::{ + auth::{self, refresh}, + launcher::download, + state::MinecraftChild, +}; pub use crate::{ state::{JavaSettings, Profile}, State, @@ -91,10 +95,33 @@ pub async fn sync(path: &Path) -> crate::Result<()> { } } -/// Run Minecraft using a profile +/// Run Minecraft using a profile and the default credentials, logged in credentials, +/// failing with an error if no credentials are available +#[tracing::instrument(skip_all)] +pub async fn run(path: &Path) -> crate::Result>> { + let state = State::get().await?; + + // Get default account and refresh credentials (preferred way to log in) + let default_account = state.settings.read().await.default_user; + let credentials = if let Some(default_account) = default_account { + refresh(default_account, false).await? + } else { + // If no default account, try to use a logged in account + let users = auth::users().await?; + let last_account = users.iter().next(); + if let Some(last_account) = last_account { + refresh(last_account.id, false).await? + } else { + return Err(crate::ErrorKind::NoCredentialsError.as_error()); + } + }; + run_credentials(path, &credentials).await +} + +/// Run Minecraft using a profile, and credentials for authentication /// Returns Arc pointer to RwLock to Child #[tracing::instrument(skip_all)] -pub async fn run( +pub async fn run_credentials( path: &Path, credentials: &crate::auth::Credentials, ) -> crate::Result>> { diff --git a/theseus/src/error.rs b/theseus/src/error.rs index 004610235..8d71dbcbc 100644 --- a/theseus/src/error.rs +++ b/theseus/src/error.rs @@ -76,6 +76,9 @@ pub enum ErrorKind { #[error("Could not create profile: {0}")] ProfileCreationError(#[from] profile_create::ProfileCreationError), + #[error("User is not logged in, no credentials available!")] + NoCredentialsError, + #[error("JRE error: {0}")] JREError(#[from] crate::util::jre::JREError), diff --git a/theseus/src/launcher/auth.rs b/theseus/src/launcher/auth.rs index a1d5ac3e8..ae7c557d1 100644 --- a/theseus/src/launcher/auth.rs +++ b/theseus/src/launcher/auth.rs @@ -47,7 +47,7 @@ struct ProfileInfoJSON { } // Login information -#[derive(Encode, Decode, Serialize, Deserialize)] +#[derive(Encode, Decode, Serialize, Deserialize, Clone, Debug)] pub struct Credentials { #[bincode(with_serde)] pub id: uuid::Uuid, diff --git a/theseus_cli/src/subcommands/profile.rs b/theseus_cli/src/subcommands/profile.rs index f6f6515ef..a46084bd6 100644 --- a/theseus_cli/src/subcommands/profile.rs +++ b/theseus_cli/src/subcommands/profile.rs @@ -332,7 +332,7 @@ impl ProfileRun { .await?; let credentials = auth::refresh(id, false).await?; - let proc_lock = profile::run(&path, &credentials).await?; + let proc_lock = profile::run_credentials(&path, &credentials).await?; let mut proc = proc_lock.write().await; process::wait_for(&mut proc).await?; diff --git a/theseus_gui/src-tauri/src/api/profile.rs b/theseus_gui/src-tauri/src/api/profile.rs index dd31c93a8..353bcf474 100644 --- a/theseus_gui/src-tauri/src/api/profile.rs +++ b/theseus_gui/src-tauri/src/api/profile.rs @@ -27,13 +27,13 @@ pub async fn profile_list( Ok(res) } -// Run Minecraft using a profile +// Run minecraft using a profile using the default credentials // Returns a u32 representing the PID, which can be used to poll // for the actual Child in the state. -// invoke('profile_run') +// invoke('profile_run', path) #[tauri::command] -pub async fn profile_run(path: &Path, credentials: Credentials) -> Result { - let proc_lock = profile::run(path, &credentials).await?; +pub async fn profile_run(path: &Path) -> Result { + let proc_lock = profile::run(path).await?; let pid = proc_lock.read().await.child.id().ok_or_else(|| { theseus::Error::from(theseus::ErrorKind::LauncherError( "Process failed to stay open.".to_string(), @@ -42,14 +42,38 @@ pub async fn profile_run(path: &Path, credentials: Credentials) -> Result { Ok(pid) } -// Run Minecraft using a profile, and wait for the result -// invoke('profile_run_wait', path, credentials) +// Run Minecraft using a profile using the default credentials, and wait for the result +// invoke('profile_run_wait', path) #[tauri::command] -pub async fn profile_run_wait( - path: &Path, - credentials: Credentials, -) -> Result<()> { - let proc_lock = profile::run(path, &credentials).await?; +pub async fn profile_run_wait(path: &Path) -> Result<()> { + let proc_lock = profile::run(path).await?; + let mut proc = proc_lock.write().await; + Ok(process::wait_for(&mut proc).await?) +} + +// Run Minecraft using a profile using chosen credentials +// Returns a u32 representing the PID, which can be used to poll +// for the actual Child in the state. +// invoke('profile_run_credentials', {path, credentials})') +#[tauri::command] +pub async fn profile_run_credentials(path: &Path, credentials: Credentials) -> Result { + let proc_lock = profile::run_credentials(path, &credentials).await?; + let pid = proc_lock.read().await.child.id().ok_or_else(|| { + theseus::Error::from(theseus::ErrorKind::LauncherError( + "Process failed to stay open.".to_string(), + )) + })?; + Ok(pid) +} + +// Run Minecraft using a profile using the chosen credentials, and wait for the result +// invoke('profile_run_wait', {path, credentials) +#[tauri::command] +pub async fn profile_run_wait_credentials( + path: &Path, + credentials: Credentials, +) -> Result<()> { + let proc_lock = profile::run_credentials(path, &credentials).await?; let mut proc = proc_lock.write().await; Ok(process::wait_for(&mut proc).await?) } diff --git a/theseus_gui/src-tauri/src/main.rs b/theseus_gui/src-tauri/src/main.rs index 78847df5f..a97634a82 100644 --- a/theseus_gui/src-tauri/src/main.rs +++ b/theseus_gui/src-tauri/src/main.rs @@ -25,6 +25,8 @@ fn main() { api::profile::profile_list, api::profile::profile_run, api::profile::profile_run_wait, + api::profile::profile_run_credentials, + api::profile::profile_run_wait_credentials, api::pack::pack_install_version_id, api::pack::pack_install_file, api::auth::auth_authenticate_begin_flow, diff --git a/theseus_playground/src/main.rs b/theseus_playground/src/main.rs index 03bda7763..4f13e8439 100644 --- a/theseus_playground/src/main.rs +++ b/theseus_playground/src/main.rs @@ -64,39 +64,16 @@ async fn main() -> theseus::Result<()> { .await?; State::sync().await?; - // Attempt to get the default user, if it exists, and refresh their credentials - let default_user_uuid = { - let settings = st.settings.read().await; - settings.default_user - }; - let credentials = if let Some(uuid) = default_user_uuid { - println!("Attempting to refresh existing authentication."); - auth::refresh(uuid, false).await - } else { - println!("Freshly authenticating."); - authenticate_run().await - }; - // Check attempt to get Credentials - // If successful, run the profile and store the RwLock to the process - let proc_lock = match credentials { - Ok(credentials) => { - println!("Preparing to run Minecraft."); - profile::run(&canonicalize(&profile_path)?, &credentials).await - } - Err(e) => { - // If Hydra could not be accessed, for testing, attempt to load credentials from disk and do the same - println!("Could not authenticate: {}.\nAttempting stored authentication.",e); - let users = auth::users().await.expect( - "Could not access any stored users- state was dropped.", - ); - let credentials = users - .first() - .expect("Hydra failed, and no stored users were found."); - println!("Preparing to run Minecraft."); - profile::run(&canonicalize(&profile_path)?, credentials).await - } - }?; + + // Attempt to run game + if auth::users().await?.len() == 0 { + println!("No users found, authenticating."); + authenticate_run().await?; // could take credentials from here direct, but also deposited in state users + } + + // Run a profile, running minecraft and store the RwLock to the process + let proc_lock = profile::run(&canonicalize(&profile_path)?).await?; let pid = proc_lock .read()