Profile Options (#120)

* init profile settings

* more work

* finish everything

* Switch to index approach

* Fix settings str split

* Run lint
This commit is contained in:
Geometrically
2023-05-19 18:59:32 -07:00
committed by GitHub
parent 4df7605b8d
commit 6014172046
43 changed files with 1108 additions and 709 deletions

View File

@@ -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<JavaGlobals> {
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<String> {
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<Vec<JavaVersion>> {
let version = extract_java_majorminor_version("1.18")?;

View File

@@ -227,7 +227,7 @@ pub async fn install_pack_from_file(path: PathBuf) -> crate::Result<PathBuf> {
.await
}
#[tracing::instrument]
#[tracing::instrument(skip(file))]
#[theseus_macros::debug_pin]
async fn install_pack(
file: bytes::Bytes,

View File

@@ -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.

View File

@@ -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<Option<JavaVersion>> {
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<Arc<RwLock<MinecraftChild>>> {
/// 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 =

View File

@@ -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,

View File

@@ -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<JavaVersion> {
) -> crate::Result<Option<JavaVersion>> {
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

View File

@@ -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,

View File

@@ -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,

View File

@@ -130,6 +130,8 @@ pub struct JavaSettings {
pub override_version: Option<JavaVersion>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extra_arguments: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_env_args: Option<Vec<(String, String)>>,
}
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(

View File

@@ -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<String>,
@@ -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,

View File

@@ -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<Self> {
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}")