* basic push

* actual push

* JRE detection, and autosetting

* removed a println, retrying CI/CD

* new game version compare; preset java 7 and 8 using our jre

* 1.8 mislabeled

* working JRE changes

* fixed bugs with JRE setup

* fixed bugs with JRE setup

* manual merge

* prettier

* fixes + jre 17

* clippy, prettier

* typo

* forgot to hook up a function

* pr fix + comment fix

* added loader_version

* take 2
This commit is contained in:
Wyatt Verchere 2023-04-07 13:31:06 -07:00 committed by GitHub
parent 4b41ffbd8a
commit 34005dd2e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 542 additions and 83 deletions

137
theseus/src/api/jre.rs Normal file
View File

@ -0,0 +1,137 @@
//! Authentication flow interface
use crate::{
launcher::download,
prelude::Profile,
state::JavaGlobals,
util::jre::{self, extract_java_majorminor_version, JavaVersion},
State,
};
pub const JAVA_8_KEY: &str = "JAVA_8";
pub const JAVA_17_KEY: &str = "JAVA_17";
pub const JAVA_18PLUS_KEY: &str = "JAVA_18PLUS";
// Autodetect JavaSettings default
// Make a guess for what the default Java global settings should be
pub fn autodetect_java_globals() -> crate::Result<JavaGlobals> {
let mut java_8 = find_java8_jres()?;
let mut java_17 = find_java17_jres()?;
let mut java_18plus = find_java18plus_jres()?;
// Simply select last one found for initial guess
let mut java_globals = JavaGlobals::new();
if let Some(jre) = java_8.pop() {
java_globals.insert(JAVA_8_KEY.to_string(), jre);
}
if let Some(jre) = java_17.pop() {
java_globals.insert(JAVA_17_KEY.to_string(), jre);
}
if let Some(jre) = java_18plus.pop() {
java_globals.insert(JAVA_18PLUS_KEY.to_string(), jre);
}
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?;
// Fetch version info from stored profile game_version
let version = state
.metadata
.minecraft
.versions
.iter()
.find(|it| it.id == profile.metadata.game_version.as_ref())
.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()).await?;
let optimal_key = match version_info
.java_version
.as_ref()
.map(|it| it.major_version)
.unwrap_or(0)
{
0..=16 => JAVA_8_KEY.to_string(),
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 fn find_java18plus_jres() -> crate::Result<Vec<JavaVersion>> {
let version = extract_java_majorminor_version("1.18")?;
let jres = jre::get_all_jre()?;
// 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
} else {
false
}
})
.collect())
}
// Searches for jres on the system that are 1.8 exactly
pub fn find_java8_jres() -> crate::Result<Vec<JavaVersion>> {
let version = extract_java_majorminor_version("1.8")?;
let jres = jre::get_all_jre()?;
// Filter out JREs that are not 1.8
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
} else {
false
}
})
.collect())
}
// Searches for jres on the system that are 1.17 exactly
pub fn find_java17_jres() -> crate::Result<Vec<JavaVersion>> {
let version = extract_java_majorminor_version("1.17")?;
let jres = jre::get_all_jre()?;
// Filter out JREs that are not 1.8
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
} else {
false
}
})
.collect())
}
// Get all JREs that exist on the system
pub fn get_all_jre() -> crate::Result<Vec<JavaVersion>> {
Ok(jre::get_all_jre()?)
}
pub async fn validate_globals() -> crate::Result<bool> {
let state = State::get().await?;
let settings = state.settings.read().await;
Ok(settings.java_globals.is_all_valid())
}

View File

@ -1,5 +1,6 @@
//! API for interacting with Theseus
pub mod auth;
pub mod jre;
pub mod pack;
pub mod process;
pub mod profile;
@ -18,8 +19,11 @@ pub mod prelude {
pub use crate::{
auth::{self, Credentials},
data::*,
pack, process,
jre, pack, process,
profile::{self, Profile},
profile_create, settings, State,
profile_create, settings,
state::JavaGlobals,
util::jre::JavaVersion,
State,
};
}

View File

@ -1,10 +1,9 @@
//! Theseus profile management interface
use crate::state::MinecraftChild;
use crate::{launcher::download, state::MinecraftChild};
pub use crate::{
state::{JavaSettings, Profile},
State,
};
use daedalus as d;
use std::{
future::Future,
path::{Path, PathBuf},
@ -120,8 +119,8 @@ pub async fn run(
profile.metadata.game_version
))
})?;
let version_info = d::minecraft::fetch_version_info(version).await?;
let version_info =
download::download_version_info(&state, version, profile.metadata.loader_version.as_ref()).await?;
let pre_launch_hooks =
&profile.hooks.as_ref().unwrap_or(&settings.hooks).pre_launch;
for hook in pre_launch_hooks.iter() {
@ -145,29 +144,42 @@ pub async fn run(
}
}
let java_install = match profile.java {
let java_version = match profile.java {
// Load profile-specific Java implementation choice
// (This defaults to Daedalus-decided key on init, but can be changed by the user)
Some(JavaSettings {
install: Some(ref install),
jre_key: Some(ref jre_key),
..
}) => install,
_ => if version_info
.java_version
.as_ref()
.filter(|it| it.major_version >= 16)
.is_some()
{
settings.java_17_path.as_ref()
} else {
settings.java_8_path.as_ref()
}) => settings.java_globals.get(jre_key),
// Fall back to Daedalus-decided key if no profile-specific key is set
_ => {
match version_info
.java_version
.as_ref()
.map(|it| it.major_version)
.unwrap_or(0)
{
0..=16 => settings
.java_globals
.get(&crate::jre::JAVA_8_KEY.to_string()),
17 => settings
.java_globals
.get(&crate::jre::JAVA_17_KEY.to_string()),
_ => settings
.java_globals
.get(&crate::jre::JAVA_18PLUS_KEY.to_string()),
}
}
.ok_or_else(|| {
crate::ErrorKind::LauncherError(format!(
"No Java installed for version {}",
version_info.java_version.map_or(8, |it| it.major_version),
))
})?,
};
let java_version = java_version.as_ref().ok_or_else(|| {
crate::ErrorKind::LauncherError(format!(
"No Java stored for version {}",
version_info.java_version.map_or(8, |it| it.major_version),
))
})?;
// Get the path to the Java executable from the chosen Java implementation key
let java_install: &Path = &PathBuf::from(&java_version.path);
if !java_install.exists() {
return Err(crate::ErrorKind::LauncherError(format!(
"Could not find Java install: {}",
@ -175,7 +187,6 @@ pub async fn run(
))
.as_error());
}
let java_args = profile
.java
.as_ref()

View File

@ -1,5 +1,5 @@
//! Theseus profile management interface
use crate::prelude::ModLoader;
use crate::{jre, prelude::ModLoader};
pub use crate::{
state::{JavaSettings, Profile},
State,
@ -145,6 +145,20 @@ pub async fn profile_create(
}
profile.metadata.linked_project_id = linked_project_id;
// Attempts to find optimal JRE for the profile from the JavaGlobals
// Finds optimal key, and see if key has been set in JavaGlobals
let settings = state.settings.read().await;
let optimal_version_key = jre::get_optimal_jre_key(&profile).await?;
if settings.java_globals.get(&optimal_version_key).is_some() {
profile.set_java_settings(Some(JavaSettings {
jre_key: Some(optimal_version_key),
extra_arguments: None,
}))?;
} else {
println!("Could not detect optimal JRE: {optimal_version_key}, falling back to system default.");
}
{
let mut profiles = state.profiles.write().await;
profiles.insert(profile)?;

View File

@ -19,7 +19,6 @@ pub static REQWEST_CLIENT: Lazy<reqwest::Client> = Lazy::new(|| {
.unwrap();
headers.insert(reqwest::header::USER_AGENT, header);
reqwest::Client::builder()
.timeout(time::Duration::from_secs(15))
.tcp_keepalive(Some(time::Duration::from_secs(10)))
.default_headers(headers)
.build()

View File

@ -76,6 +76,12 @@ pub enum ErrorKind {
#[error("Could not create profile: {0}")]
ProfileCreationError(#[from] profile_create::ProfileCreationError),
#[error("JRE error: {0}")]
JREError(#[from] crate::util::jre::JREError),
#[error("Error parsing date: {0}")]
ChronoParseError(#[from] chrono::ParseError),
#[error("Zip error: {0}")]
ZipError(#[from] async_zip::error::ZipError),

View File

@ -8,8 +8,7 @@ use tokio::process::{Child, Command};
mod args;
pub mod auth;
mod download;
pub mod download;
#[tracing::instrument]
pub fn parse_rule(rule: &d::minecraft::Rule) -> bool {

View File

@ -42,7 +42,7 @@ impl AuthTask {
// Waits for the task to complete, and returns the credentials
let credentials = task
.ok_or_else(|| AuthTaskError::TaskMissing)?
.ok_or(AuthTaskError::TaskMissing)?
.await
.map_err(AuthTaskError::from)??;

View File

@ -0,0 +1,61 @@
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use crate::prelude::JavaVersion;
use crate::util::jre;
// All stored Java versions, chosen by the user
// A wrapper over a Hashmap connecting key -> java version
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct JavaGlobals(HashMap<String, JavaVersion>);
impl JavaGlobals {
pub fn new() -> JavaGlobals {
JavaGlobals(HashMap::new())
}
pub fn insert(&mut self, key: String, java: JavaVersion) {
self.0.insert(key, java);
}
pub fn remove(&mut self, key: &String) {
self.0.remove(key);
}
pub fn get(&self, key: &String) -> Option<&JavaVersion> {
self.0.get(key)
}
pub fn get_mut(&mut self, key: &String) -> Option<&mut JavaVersion> {
self.0.get_mut(key)
}
pub fn count(&self) -> usize {
self.0.len()
}
// Validates that every path here is a valid Java version and that the version matches the version stored here
// If false, when checked, the user should be prompted to reselect the Java version
pub fn is_all_valid(&self) -> bool {
for (_, java) in self.0.iter() {
let jre = jre::check_java_at_filepath(
PathBuf::from(&java.path).as_path(),
);
if let Some(jre) = jre {
if jre.version != java.version {
return false;
}
} else {
return false;
}
}
true
}
}
impl Default for JavaGlobals {
fn default() -> Self {
Self::new()
}
}

View File

@ -1,5 +1,6 @@
//! Theseus state management system
use crate::config::sled_config;
use crate::jre;
use std::sync::Arc;
use tokio::sync::{Mutex, OnceCell, RwLock, Semaphore};
@ -31,6 +32,9 @@ pub use self::auth_task::*;
mod tags;
pub use self::tags::*;
mod java_globals;
pub use self::java_globals::*;
// Global state
static LAUNCHER_STATE: OnceCell<Arc<State>> = OnceCell::const_new();
pub struct State {
@ -74,7 +78,7 @@ impl State {
.open()?;
// Settings
let settings =
let mut settings =
Settings::init(&directories.settings_file()).await?;
// Loose initializations
@ -101,6 +105,12 @@ impl State {
);
};
// On launcher initialization, if global java variables are unset, try to find and set them
// (they are required for the game to launch)
if settings.java_globals.count() == 0 {
settings.java_globals = jre::autodetect_java_globals()?;
}
Ok(Arc::new(Self {
database,
directories,

View File

@ -83,7 +83,7 @@ impl std::fmt::Display for ModLoader {
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct JavaSettings {
#[serde(skip_serializing_if = "Option::is_none")]
pub install: Option<PathBuf>,
pub jre_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extra_arguments: Option<Vec<String>>,
}
@ -146,6 +146,15 @@ impl Profile {
}
}
#[tracing::instrument]
pub fn set_java_settings(
&mut self,
java: Option<JavaSettings>,
) -> crate::Result<()> {
self.java = java;
Ok(())
}
pub fn get_profile_project_paths(&self) -> crate::Result<Vec<PathBuf>> {
let mut files = Vec::new();
let mut read_paths = |path: &str| {

View File

@ -322,7 +322,9 @@ pub async fn infer_data_from_files(
title: Some(
pack.display_name
.clone()
.unwrap_or(pack.mod_id.clone()),
.unwrap_or_else(|| {
pack.mod_id.clone()
}),
),
description: pack.description.clone(),
authors: pack

View File

@ -1,11 +1,10 @@
//! Theseus settings file
use serde::{Deserialize, Serialize};
use std::{
collections::HashSet,
path::{Path, PathBuf},
};
use std::{collections::HashSet, path::Path};
use tokio::fs;
use super::JavaGlobals;
// TODO: convert to semver?
const CURRENT_FORMAT_VERSION: u32 = 1;
@ -18,8 +17,7 @@ pub struct Settings {
pub game_resolution: WindowSize,
pub custom_java_args: Vec<String>,
pub custom_env_args: Vec<(String, String)>,
pub java_8_path: Option<PathBuf>,
pub java_17_path: Option<PathBuf>,
pub java_globals: JavaGlobals,
pub default_user: Option<uuid::Uuid>,
pub hooks: Hooks,
pub max_concurrent_downloads: usize,
@ -33,8 +31,7 @@ impl Default for Settings {
game_resolution: WindowSize::default(),
custom_java_args: Vec::new(),
custom_env_args: Vec::new(),
java_8_path: None,
java_17_path: None,
java_globals: JavaGlobals::new(),
default_user: None,
hooks: Hooks::default(),
max_concurrent_downloads: 64,

View File

@ -140,7 +140,6 @@ impl Tags {
let licenses = self.fetch_tag("license");
let donation_platforms = self.fetch_tag("donation_platform");
let report_types = self.fetch_tag("report_type");
let (
categories,
loaders,
@ -241,14 +240,6 @@ pub struct Loader {
pub supported_project_types: Vec<String>,
}
#[derive(Debug, Clone, Decode, Encode, Serialize, Deserialize)]
pub struct GameVersion {
pub version: String,
pub version_type: String,
pub date: String,
pub major: bool,
}
#[derive(Debug, Clone, Decode, Encode, Serialize, Deserialize)]
pub struct License {
pub short: String,
@ -260,3 +251,11 @@ pub struct DonationPlatform {
pub short: String,
pub name: String,
}
#[derive(Debug, Clone, Decode, Encode, Serialize, Deserialize)]
pub struct GameVersion {
pub version: String,
pub version_type: String,
pub date: String,
pub major: bool,
}

View File

@ -1,10 +1,11 @@
use dunce::canonicalize;
use lazy_static::lazy_static;
use regex::Regex;
use std::collections::HashSet;
use serde::{Deserialize, Serialize};
use std::env;
use std::path::PathBuf;
use std::process::Command;
use std::{collections::HashSet, path::Path};
#[cfg(target_os = "windows")]
use winreg::{
@ -12,7 +13,7 @@ use winreg::{
RegKey,
};
#[derive(Debug, PartialEq, Eq, Hash)]
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Clone)]
pub struct JavaVersion {
pub path: String,
pub version: String,
@ -35,11 +36,8 @@ pub fn get_all_jre() -> Result<Vec<JavaVersion>, JREError> {
let Ok(java_subpaths) = std::fs::read_dir(java_path) else {continue };
for java_subpath in java_subpaths {
let path = java_subpath?.path();
if let Some(j) =
check_java_at_filepath(PathBuf::from(path).join("bin"))
{
if let Some(j) = check_java_at_filepath(&path.join("bin")) {
jres.insert(j);
break;
}
}
}
@ -90,10 +88,9 @@ pub fn get_all_jre_winregkey(
subkey.get_value(subkey_value);
let Ok(path) = path else {continue};
if let Some(j) =
check_java_at_filepath(PathBuf::from(path).join("bin"))
check_java_at_filepath(&PathBuf::from(path).join("bin"))
{
jres.insert(j);
break;
}
}
}
@ -118,12 +115,23 @@ pub fn get_all_jre() -> Result<Vec<JavaVersion>, JREError> {
r"/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands",
];
for path in java_paths {
if let Some(j) = check_java_at_filepath(PathBuf::from(path).join("bin"))
if let Some(j) =
check_java_at_filepath(&PathBuf::from(path).join("bin"))
{
jres.insert(j);
break;
}
}
// Iterate over JavaVirtualMachines/(something)/Contents/Home/bin
let base_path = PathBuf::from("/Library/Java/JavaVirtualMachines/");
if base_path.is_dir() {
for entry in std::fs::read_dir(base_path)? {
let entry = entry?.path().join("Contents/Home/bin");
if let Some(j) = check_java_at_filepath(entry.as_path()) {
jres.insert(j);
}
}
}
Ok(jres.into_iter().collect())
}
@ -149,29 +157,29 @@ pub fn get_all_jre() -> Result<Vec<JavaVersion>, JREError> {
];
for path in java_paths {
if let Some(j) =
check_java_at_filepath(PathBuf::from(path).join("jre").join("bin"))
check_java_at_filepath(&PathBuf::from(path).join("jre").join("bin"))
{
jres.insert(j);
break;
}
if let Some(j) = check_java_at_filepath(PathBuf::from(path).join("bin"))
if let Some(j) =
check_java_at_filepath(&PathBuf::from(path).join("bin"))
{
jres.insert(j);
break;
}
}
Ok(jres.into_iter().collect())
}
// Gets all JREs from the PATH env variable
#[tracing::instrument]
pub fn get_all_jre_path() -> Result<HashSet<JavaVersion>, JREError> {
fn get_all_jre_path() -> Result<HashSet<JavaVersion>, JREError> {
// Iterate over values in PATH variable, where accessible JREs are referenced
let paths = env::var("PATH")?;
let paths = env::split_paths(&paths);
let mut jres = HashSet::new();
for path in paths {
if let Some(j) = check_java_at_filepath(path) {
if let Some(j) = check_java_at_filepath(&path) {
jres.insert(j);
}
}
@ -189,14 +197,19 @@ const JAVA_BIN: &str = "java";
// For example filepath 'path', attempt to resolve it and get a Java version at this path
// If no such path exists, or no such valid java at this path exists, returns None
#[tracing::instrument]
pub fn check_java_at_filepath(path: PathBuf) -> Option<JavaVersion> {
pub fn check_java_at_filepath(path: &Path) -> Option<JavaVersion> {
// Attempt to canonicalize the potential java filepath
// If it fails, this path does not exist and None is returned (no Java here)
let Ok(path) = canonicalize(path) else { return None };
let Some(path_str) = path.to_str() else { return None };
// Checks for existence of Java at this filepath
let java = path.join(JAVA_BIN);
// Adds JAVA_BIN to the end of the path if it is not already there
let java = if path.file_name()?.to_str()? != JAVA_BIN {
path.join(JAVA_BIN)
} else {
path
};
if !java.exists() {
return None;
};
@ -216,7 +229,8 @@ pub fn check_java_at_filepath(path: PathBuf) -> Option<JavaVersion> {
// Extract version info from it
if let Some(captures) = JAVA_VERSION_CAPTURE.captures(&stderr) {
if let Some(version) = captures.get(1) {
let path = path_str.to_string();
let Some(path) = java.to_str() else { return None };
let path = path.to_string();
return Some(JavaVersion {
path,
version: version.as_str().to_string(),
@ -226,6 +240,38 @@ pub fn check_java_at_filepath(path: PathBuf) -> Option<JavaVersion> {
None
}
/// Extract major/minor version from a java version string
/// Gets the minor version or an error, and assumes 1 for major version if it could not find
/// "1.8.0_361" -> (1, 8)
/// "20" -> (1, 20)
pub fn extract_java_majorminor_version(
version: &str,
) -> Result<(u32, u32), JREError> {
let mut split = version.split('.');
let major_opt = split.next();
let mut major;
// Try minor. If doesn't exist, in format like "20" so use major
let mut minor = if let Some(minor) = split.next() {
major = major_opt.unwrap_or("1").parse::<u32>()?;
minor.parse::<u32>()?
} else {
// Formatted like "20", only one value means that is minor version
major = 1;
major_opt
.ok_or_else(|| JREError::InvalidJREVersion(version.to_string()))?
.parse::<u32>()?
};
// Java start should always be 1. If more than 1, it is formatted like "17.0.1.2" and starts with minor version
if major > 1 {
minor = major;
major = 1;
}
Ok((major, minor))
}
#[derive(thiserror::Error, Debug)]
pub enum JREError {
#[error("Command error : {0}")]
@ -233,4 +279,32 @@ pub enum JREError {
#[error("Env error: {0}")]
EnvError(#[from] env::VarError),
#[error("No JRE found for required version: {0}")]
NoJREFound(String),
#[error("Invalid JRE version string: {0}")]
InvalidJREVersion(String),
#[error("Parsing error: {0}")]
ParseError(#[from] std::num::ParseIntError),
#[error("No stored tag for Minecraft Version {0}")]
NoMinecraftVersionFound(String),
}
#[cfg(test)]
mod tests {
use super::extract_java_majorminor_version;
#[test]
pub fn java_version_parsing() {
assert_eq!(extract_java_majorminor_version("1.8").unwrap(), (1, 8));
assert_eq!(extract_java_majorminor_version("17.0.6").unwrap(), (1, 17));
assert_eq!(extract_java_majorminor_version("20").unwrap(), (1, 20));
assert_eq!(
extract_java_majorminor_version("1.8.0_361").unwrap(),
(1, 8)
);
}
}

View File

@ -0,0 +1,62 @@
use std::path::Path;
use crate::api::Result;
use theseus::prelude::JavaVersion;
use theseus::prelude::*;
use super::TheseusSerializableError;
/// Get all JREs that exist on the system
#[tauri::command]
pub async fn jre_get_all_jre() -> Result<Vec<JavaVersion>> {
Ok(jre::get_all_jre()?)
}
// Finds the isntallation of Java 7, if it exists
#[tauri::command]
pub async fn jre_find_jre_8_jres() -> Result<Vec<JavaVersion>> {
Ok(jre::find_java8_jres()?)
}
// finds the installation of Java 17, if it exists
#[tauri::command]
pub async fn jre_find_jre_17_jres() -> Result<Vec<JavaVersion>> {
Ok(jre::find_java17_jres()?)
}
// Finds the highest version of Java 18+, if it exists
#[tauri::command]
pub async fn jre_find_jre_18plus_jres() -> Result<Vec<JavaVersion>> {
Ok(jre::find_java18plus_jres()?)
}
// Autodetect Java globals, by searching the users computer.
// Returns a *NEW* JavaGlobals that can be put into Settings
#[tauri::command]
pub async fn jre_autodetect_java_globals() -> Result<JavaGlobals> {
Ok(jre::autodetect_java_globals()?)
}
// Gets key for the optimal JRE to use, for a given profile Profile
// The key can be used in the hashmap contained by JavaGlobals in Settings (if it exists)
#[tauri::command]
pub async fn jre_get_optimal_jre_key(profile: Profile) -> Result<String> {
Ok(jre::get_optimal_jre_key(&profile).await?)
}
// Gets key for the optimal JRE to use, for a given profile path
// The key can be used in the hashmap contained by JavaGlobals in Settings (if it exists)
#[tauri::command]
pub async fn jre_get_optimal_jre_key_by_path(path: &Path) -> Result<String> {
let profile = profile::get(path).await?.ok_or_else(|| {
TheseusSerializableError::NoProfileFound(path.display().to_string())
})?;
Ok(jre::get_optimal_jre_key(&profile).await?)
}
// Validates java globals, by checking if the paths exist
// If false, recommend to direct them to reassign, or to re-guess
#[tauri::command]
pub async fn jre_validate_globals() -> Result<bool> {
Ok(jre::validate_globals().await?)
}

View File

@ -3,6 +3,7 @@ use serde::{Serialize, Serializer};
use thiserror::Error;
pub mod auth;
pub mod jre;
pub mod pack;
pub mod process;
@ -29,6 +30,9 @@ pub enum TheseusSerializableError {
#[error("IO error: {0}")]
IO(#[from] std::io::Error),
#[error("No profile found at {0}")]
NoProfileFound(String),
}
// Generic implementation of From<T> for ErrorTypeA
@ -69,4 +73,5 @@ macro_rules! impl_serialize {
impl_serialize! {
Theseus,
IO,
NoProfileFound,
}

View File

@ -43,6 +43,14 @@ fn main() {
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_optimal_jre_key,
api::jre::jre_get_optimal_jre_key_by_path,
api::process::process_get_all_pids,
api::process::process_get_all_running_pids,
api::process::process_get_pids_by_profile_path,

View File

@ -0,0 +1,63 @@
/**
* All theseus API calls return serialized values (both return values and errors);
* So, for example, addDefaultInstance creates a blank Profile object, where the Rust struct is serialized,
* and deserialized into a usable JS object.
*/
import { invoke } from '@tauri-apps/api/tauri'
/*
JavaVersion {
path: Path
version: String
}
*/
/// 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')
}
// 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')
}
// 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')
}
// 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')
}
// 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')
}
// Gets key for the optimal JRE to use, for a given profile path
// The key can be used in the hashmap contained by JavaGlobals in Settings (if it exists)
export async function get_optimal_jre_key_by_path(path) {
return await invoke('jre_get_optimal_jre_key_by_path', { path })
}
// Gets key for the optimal JRE to use, for a given profile
// The key can be used in the hashmap contained by JavaGlobals in Settings (if it exists)
export async function get_optimal_jre_ke(path) {
return await invoke('jre_get_optimal_jre_key', { 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(path) {
return await invoke('jre_autodetect_java_globals', { path })
}

View File

@ -13,8 +13,7 @@ Settings {
"game_resolution": [int int],
"custom_java_args": [String ...],
"custom_env_args" : [(string, string) ... ]>,
"java_8_path": Path (can be null),
"java_17_path": Path (can be null),
"java_globals": Hash of (string, Path),
"default_user": Uuid string (can be null),
"hooks": Hooks,
"max_concurrent_downloads": uint,

View File

@ -4,7 +4,6 @@
)]
use dunce::canonicalize;
use std::path::Path;
use theseus::prelude::*;
use tokio::time::{sleep, Duration};
@ -32,7 +31,7 @@ async fn main() -> theseus::Result<()> {
// Initialize state
let st = State::get().await?;
st.settings.write().await.max_concurrent_downloads = 100;
st.settings.write().await.max_concurrent_downloads = 1;
// Clear profiles
println!("Clearing profiles.");
@ -52,13 +51,14 @@ async fn main() -> theseus::Result<()> {
// async closure for testing any desired edits
// (ie: changing the java runtime of an added profile)
println!("Editing.");
profile::edit(&profile_path, |profile| {
// Eg: Java. TODO: hook up to jre.rs class to pick optimal java
profile.java = Some(JavaSettings {
install: Some(Path::new("/usr/bin/java").to_path_buf()),
extra_arguments: None,
});
// println!("Editing.");
profile::edit(&profile_path, |_profile| {
// Eg: Java- this would let you change the java runtime of the profile instead of using the default
// use theseus::prelude::jre::JAVA__KEY;
// profile.java = Some(JavaSettings {
// jre_key: Some(JAVA_17_KEY.to_string()),
// extra_arguments: None,
// });
async { Ok(()) }
})
.await?;
@ -107,8 +107,8 @@ async fn main() -> theseus::Result<()> {
println!("Minecraft PID: {}", pid);
// Wait 5 seconds
println!("Waiting 10 seconds to gather logs...");
sleep(Duration::from_secs(10)).await;
println!("Waiting 20 seconds to gather logs...");
sleep(Duration::from_secs(20)).await;
let stdout = process::get_stdout_by_pid(pid).await?;
println!("Logs after 5sec <<< {stdout} >>> end stdout");