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 <jaiagr+gpg@pm.me>
This commit is contained in:
parent
47970d932b
commit
f52e777379
@ -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<JavaGlobals> {
|
||||
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<JavaVersion>,
|
||||
mut java_17: Vec<JavaVersion>,
|
||||
mut java_18plus: Vec<JavaVersion>,
|
||||
) -> crate::Result<JavaGlobals> {
|
||||
// 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<JavaGlobals> {
|
||||
}
|
||||
|
||||
// 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<JavaVersion>,
|
||||
allow_higher: bool,
|
||||
) -> crate::Result<Vec<JavaVersion>> {
|
||||
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
|
||||
}
|
||||
|
||||
@ -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<String>,
|
||||
pub files: Vec<PackFile>,
|
||||
pub dependencies: HashMap<PackDependency, String>,
|
||||
}
|
||||
use super::install_from::{
|
||||
generate_pack_from_file, generate_pack_from_version_id,
|
||||
CreatePackDescription, CreatePackLocation, PackDependency, PackFormat,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PackFile {
|
||||
pub path: String,
|
||||
pub hashes: HashMap<PackFileHash, String>,
|
||||
pub env: Option<HashMap<EnvType, SideType>>,
|
||||
pub downloads: Vec<String>,
|
||||
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<String> 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,
|
||||
}
|
||||
|
||||
#[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<String>,
|
||||
) -> crate::Result<PathBuf> {
|
||||
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(),
|
||||
)
|
||||
})?;
|
||||
|
||||
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?;
|
||||
|
||||
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<PathBuf> {
|
||||
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
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(file))]
|
||||
#[theseus_macros::debug_pin]
|
||||
async fn install_pack(
|
||||
file: bytes::Bytes,
|
||||
icon: Option<PathBuf>,
|
||||
override_title: Option<String>,
|
||||
project_id: Option<String>,
|
||||
version_id: Option<String>,
|
||||
existing_loading_bar: Option<LoadingBarId>,
|
||||
pub async fn install_pack(
|
||||
location: CreatePackLocation,
|
||||
profile: PathBuf,
|
||||
) -> crate::Result<PathBuf> {
|
||||
// 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,
|
||||
)
|
||||
.await?
|
||||
}
|
||||
CreatePackLocation::FromFile { path } => {
|
||||
generate_pack_from_file(path, 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;
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
276
theseus/src/api/pack/install_from.rs
Normal file
276
theseus/src/api/pack/install_from.rs
Normal file
@ -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<String>,
|
||||
pub files: Vec<PackFile>,
|
||||
pub dependencies: HashMap<PackDependency, String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PackFile {
|
||||
pub path: String,
|
||||
pub hashes: HashMap<PackFileHash, String>,
|
||||
pub env: Option<HashMap<EnvType, SideType>>,
|
||||
pub downloads: Vec<String>,
|
||||
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<String> 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<String>,
|
||||
},
|
||||
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<String>, // the modloader version to use, set to "latest", "stable", or the ID of your chosen loader. defaults to latest
|
||||
pub icon: Option<PathBuf>, // the icon for the profile
|
||||
pub icon_url: Option<String>, // the URL icon for a profile (ONLY USED FOR TEMPORARY PROFILES)
|
||||
pub linked_data: Option<LinkedData>, // the linked project ID (mainly for modpacks)- used for updating
|
||||
pub skip_install_profile: Option<bool>,
|
||||
}
|
||||
|
||||
pub struct CreatePackDescription {
|
||||
pub file: bytes::Bytes,
|
||||
pub icon: Option<PathBuf>,
|
||||
pub override_title: Option<String>,
|
||||
pub project_id: Option<String>,
|
||||
pub version_id: Option<String>,
|
||||
pub existing_loading_bar: Option<LoadingBarId>,
|
||||
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<String>,
|
||||
profile: PathBuf,
|
||||
) -> crate::Result<CreatePackDescription> {
|
||||
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<CreatePackDescription> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
2
theseus/src/api/pack/mod.rs
Normal file
2
theseus/src/api/pack/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod install;
|
||||
pub mod install_from;
|
||||
@ -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::<Vec<_>>();
|
||||
|
||||
// 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<str> =
|
||||
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<Vec<PathBuf>> {
|
||||
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<PathBuf> = 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<String> {
|
||||
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::<Vec<String>>()
|
||||
}
|
||||
|
||||
/// 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<PackFormat> {
|
||||
// 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<String, String> to a HashMap<String, String>
|
||||
// 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::<HashMap<_, _>>();
|
||||
|
||||
let base_path = &profile.path;
|
||||
let files: Result<Vec<PackFile>, 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<PathBuf>,
|
||||
) -> 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(())
|
||||
}
|
||||
|
||||
@ -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<PathBuf> {
|
||||
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]
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<Vec<PathBuf>> {
|
||||
let mut files = Vec::new();
|
||||
let mut read_paths = |path: &str| {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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<String>, // 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::<Vec<_>>();
|
||||
|
||||
// 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<str> =
|
||||
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<String> {
|
||||
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::<Vec<String>>()
|
||||
}
|
||||
|
||||
/// 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<PackFormat> {
|
||||
// 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<String, String> to a HashMap<String, String>
|
||||
// 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::<HashMap<_, _>>();
|
||||
|
||||
let base_path = &profile.path;
|
||||
let files: Result<Vec<PackFile>, 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<PathBuf>,
|
||||
) -> 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<Vec<PathBuf>> {
|
||||
// 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<PathBuf> = 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)
|
||||
}
|
||||
@ -136,11 +136,9 @@ pub async fn get_all_jre() -> Result<Vec<JavaVersion>, 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<Vec<JavaVersion>, 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<HashSet<PathBuf>, 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");
|
||||
for entry in dir.flatten() {
|
||||
let file_path = entry.path().join("bin");
|
||||
|
||||
if let Ok(contents) =
|
||||
std::fs::read_to_string(file_path.clone())
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
//! Theseus utility functions
|
||||
pub mod export;
|
||||
pub mod fetch;
|
||||
pub mod jre;
|
||||
pub mod platform;
|
||||
|
||||
@ -1,6 +1,22 @@
|
||||
use crate::api::Result;
|
||||
use tauri::plugin::TauriPlugin;
|
||||
use theseus::prelude::*;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
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<Credentials> {
|
||||
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<bool> {
|
||||
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<Vec<Credentials>> {
|
||||
Ok(auth::users().await?)
|
||||
@ -49,7 +65,7 @@ pub async fn auth_users() -> Result<Vec<Credentials>> {
|
||||
|
||||
/// 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<Credentials> {
|
||||
Ok(auth::get_user(user).await?)
|
||||
|
||||
@ -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<R: tauri::Runtime>() -> TauriPlugin<R> {
|
||||
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<Vec<JavaVersion>> {
|
||||
@ -12,27 +27,24 @@ pub async fn jre_get_all_jre() -> Result<Vec<JavaVersion>> {
|
||||
|
||||
// Finds the installation of Java 8, if it exists
|
||||
#[tauri::command]
|
||||
pub async fn jre_find_jre_8_jres() -> Result<Vec<JavaVersion>> {
|
||||
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<Vec<JavaVersion>> {
|
||||
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<Vec<JavaVersion>> {
|
||||
Ok(jre::find_filtered_jres("1.18").await?)
|
||||
pub async fn jre_find_filtered_jres(
|
||||
jres: Vec<JavaVersion>,
|
||||
version: String,
|
||||
allow_higher: bool,
|
||||
) -> Result<Vec<JavaVersion>> {
|
||||
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<JavaGlobals> {
|
||||
Ok(jre::autodetect_java_globals().await?)
|
||||
pub async fn jre_autodetect_java_globals(
|
||||
java_8: Vec<JavaVersion>,
|
||||
java_17: Vec<JavaVersion>,
|
||||
java_18plus: Vec<JavaVersion>,
|
||||
) -> Result<JavaGlobals> {
|
||||
Ok(jre::autodetect_java_globals(java_8, java_17, java_18plus).await?)
|
||||
}
|
||||
|
||||
// Validates java globals, by checking if the paths exist
|
||||
|
||||
@ -12,6 +12,18 @@ pub struct Logs {
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
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(
|
||||
|
||||
@ -2,6 +2,17 @@ use crate::api::Result;
|
||||
use daedalus::minecraft::VersionManifest;
|
||||
use daedalus::modded::Manifest;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
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<VersionManifest> {
|
||||
|
||||
@ -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<std::collections::HashMap<uuid::Uuid, theseus::LoadingBar>> {
|
||||
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 {
|
||||
|
||||
@ -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<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
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<String>,
|
||||
pub async fn pack_install(
|
||||
location: CreatePackLocation,
|
||||
profile: PathBuf,
|
||||
) -> Result<PathBuf> {
|
||||
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<PathBuf> {
|
||||
let res = pack::install_pack_from_file(path.to_path_buf()).await?;
|
||||
Ok(res)
|
||||
pub fn pack_get_profile_from_pack(
|
||||
location: CreatePackLocation,
|
||||
) -> Result<CreatePackProfile> {
|
||||
Ok(pack::install_from::get_profile_from_pack(location))
|
||||
}
|
||||
|
||||
@ -4,6 +4,23 @@ use crate::api::Result;
|
||||
use theseus::prelude::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
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<bool> {
|
||||
|
||||
@ -6,8 +6,35 @@ use std::path::{Path, PathBuf};
|
||||
use theseus::prelude::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
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<bool>,
|
||||
@ -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<Uuid> {
|
||||
let minecraft_child = profile::run(path).await?;
|
||||
@ -182,7 +209,7 @@ pub async fn profile_run(path: &Path) -> Result<Uuid> {
|
||||
}
|
||||
|
||||
// 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,
|
||||
|
||||
@ -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<PathBuf> {
|
||||
let res = profile_create::profile_create_empty().await?;
|
||||
Ok(res)
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
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
|
||||
|
||||
@ -20,8 +20,14 @@ pub struct FrontendSettings {
|
||||
pub collapsed_navigation: bool,
|
||||
}
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
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<Settings> {
|
||||
let res = settings::get().await?;
|
||||
@ -29,7 +35,7 @@ pub async fn settings_get() -> Result<Settings> {
|
||||
}
|
||||
|
||||
// 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?;
|
||||
|
||||
@ -1,6 +1,19 @@
|
||||
use crate::api::Result;
|
||||
use theseus::tags::{Category, DonationPlatform, GameVersion, Loader, Tags};
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
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<Vec<Category>> {
|
||||
|
||||
@ -1,6 +1,26 @@
|
||||
use crate::api::Result;
|
||||
use std::process::Command;
|
||||
|
||||
pub fn init<R: tauri::Runtime>() -> tauri::plugin::TauriPlugin<R> {
|
||||
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<std::collections::HashMap<uuid::Uuid, theseus::LoadingBar>> {
|
||||
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")]
|
||||
|
||||
@ -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!())
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
@ -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')
|
||||
}
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
@ -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')
|
||||
}
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
@ -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 })
|
||||
}
|
||||
|
||||
@ -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')
|
||||
}
|
||||
|
||||
@ -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')
|
||||
}
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user