Misc improvements and fixes (#109)
* now utilizing tracing better * better tracing * fix mac vs pc oppositional env var issue * modified loading package * added droppable loadingbarid that sends completion message * loading bar * regressed bug on mac * fixed non-updated loading bar on playground * Loading bar improvements --------- Co-authored-by: Jai A <jaiagr+gpg@pm.me>
This commit is contained in:
parent
c79d5c32a6
commit
65c1942037
154
Cargo.lock
generated
154
Cargo.lock
generated
@ -50,6 +50,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.70"
|
||||
@ -468,7 +477,7 @@ dependencies = [
|
||||
"indenter",
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"tracing-error",
|
||||
"tracing-error 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -480,7 +489,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"tracing-core",
|
||||
"tracing-error",
|
||||
"tracing-error 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1001,9 +1010,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac"
|
||||
checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@ -1011,9 +1020,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
@ -1028,38 +1037,38 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91"
|
||||
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6"
|
||||
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2"
|
||||
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879"
|
||||
checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.27"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab"
|
||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@ -1608,6 +1617,18 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.17.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cef509aa9bc73864d6756f0d34d35504af3cf0844373afe9b8669a5b8005a729"
|
||||
dependencies = [
|
||||
"console",
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "infer"
|
||||
version = "0.7.0"
|
||||
@ -1820,7 +1841,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-subscriber 0.3.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1863,6 +1884,15 @@ dependencies = [
|
||||
"tendril",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1"
|
||||
dependencies = [
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
@ -2077,6 +2107,12 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "number_prefix"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
||||
|
||||
[[package]]
|
||||
name = "objc"
|
||||
version = "0.2.7"
|
||||
@ -2458,6 +2494,12 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26f6a7b87c2e435a3241addceeeff740ff8b7e76b74c13bf9acb17fa454ea00b"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
@ -2699,10 +2741,12 @@ dependencies = [
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-util",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"web-sys",
|
||||
"winreg 0.10.1",
|
||||
]
|
||||
@ -3516,8 +3560,8 @@ dependencies = [
|
||||
"dirs",
|
||||
"dunce",
|
||||
"futures",
|
||||
"indicatif",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"paste",
|
||||
"regex",
|
||||
"reqwest",
|
||||
@ -3532,7 +3576,8 @@ dependencies = [
|
||||
"tokio-stream",
|
||||
"toml 0.7.3",
|
||||
"tracing",
|
||||
"tracing-error",
|
||||
"tracing-error 0.1.2",
|
||||
"tracing-subscriber 0.2.25",
|
||||
"url",
|
||||
"uuid 1.3.0",
|
||||
"winreg 0.11.0",
|
||||
@ -3557,9 +3602,9 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"tracing-error",
|
||||
"tracing-error 0.2.0",
|
||||
"tracing-futures",
|
||||
"tracing-subscriber",
|
||||
"tracing-subscriber 0.3.16",
|
||||
"url",
|
||||
"uuid 1.3.0",
|
||||
"webbrowser",
|
||||
@ -3582,6 +3627,9 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"tracing-error 0.1.2",
|
||||
"tracing-subscriber 0.2.25",
|
||||
"url",
|
||||
"uuid 1.3.0",
|
||||
]
|
||||
@ -3600,6 +3648,9 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"tracing-error 0.1.2",
|
||||
"tracing-subscriber 0.2.25",
|
||||
"url",
|
||||
"uuid 1.3.0",
|
||||
"webbrowser",
|
||||
@ -3842,6 +3893,16 @@ dependencies = [
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-error"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber 0.2.25",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-error"
|
||||
version = "0.2.0"
|
||||
@ -3849,7 +3910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"tracing-subscriber 0.3.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3873,13 +3934,45 @@ dependencies = [
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-serde"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"chrono",
|
||||
"lazy_static",
|
||||
"matchers 0.0.1",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
"tracing-serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-subscriber"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"matchers 0.1.0",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex",
|
||||
@ -4139,6 +4232,19 @@ version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-streams"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.61"
|
||||
|
||||
@ -22,19 +22,21 @@ chrono = { version = "0.4.19", features = ["serde"] }
|
||||
daedalus = { version = "0.1.20" }
|
||||
dirs = "4.0"
|
||||
|
||||
log = "0.4.14"
|
||||
regex = "1.5"
|
||||
sys-info = "0.9.0"
|
||||
thiserror = "1.0"
|
||||
tracing = "0.1"
|
||||
tracing-error = "0.2"
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = "0.2"
|
||||
tracing-error = "0.1"
|
||||
|
||||
paste = { version = "1.0"}
|
||||
|
||||
tauri = { version = "1.2", optional = true}
|
||||
paste = { version = "1.0", optional = true}
|
||||
indicatif = { version = "0.17.3", optional = true }
|
||||
|
||||
async-tungstenite = { version = "0.20.0", features = ["tokio-runtime", "tokio-native-tls"] }
|
||||
futures = "0.3"
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
reqwest = { version = "0.11", features = ["json", "stream"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-stream = { version = "0.1", features = ["fs"] }
|
||||
|
||||
@ -45,4 +47,5 @@ dunce = "1.0.3"
|
||||
winreg = "0.11.0"
|
||||
|
||||
[features]
|
||||
tauri = ["dep:tauri", "dep:paste"]
|
||||
tauri = ["dep:tauri"]
|
||||
cli = ["dep:indicatif"]
|
||||
@ -46,7 +46,7 @@ pub async fn authenticate(
|
||||
))
|
||||
})?;
|
||||
|
||||
let credentials = flow.extract_credentials(&state.io_semaphore).await?;
|
||||
let credentials = flow.extract_credentials(&state.fetch_semaphore).await?;
|
||||
users.insert(&credentials).await?;
|
||||
|
||||
if state.settings.read().await.default_user.is_none() {
|
||||
@ -64,7 +64,7 @@ pub async fn refresh(user: uuid::Uuid) -> crate::Result<Credentials> {
|
||||
let state = State::get().await?;
|
||||
let mut users = state.users.write().await;
|
||||
|
||||
let io_sempahore = &state.io_semaphore;
|
||||
let fetch_semaphore = &state.fetch_semaphore;
|
||||
futures::future::ready(users.get(user).ok_or_else(|| {
|
||||
crate::ErrorKind::OtherError(format!(
|
||||
"Tried to refresh nonexistent user with ID {user}"
|
||||
@ -73,7 +73,8 @@ pub async fn refresh(user: uuid::Uuid) -> crate::Result<Credentials> {
|
||||
}))
|
||||
.and_then(|mut credentials| async move {
|
||||
if chrono::offset::Utc::now() > credentials.expires {
|
||||
inner::refresh_credentials(&mut credentials, io_sempahore).await?;
|
||||
inner::refresh_credentials(&mut credentials, fetch_semaphore)
|
||||
.await?;
|
||||
}
|
||||
users.insert(&credentials).await?;
|
||||
Ok(credentials)
|
||||
|
||||
@ -61,6 +61,7 @@ pub async fn get_optimal_jre_key(profile: &Profile) -> crate::Result<String> {
|
||||
version,
|
||||
profile.metadata.loader_version.as_ref(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
let optimal_key = match version_info
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
use crate::config::MODRINTH_API_URL;
|
||||
use crate::data::ModLoader;
|
||||
use crate::event::emit::{
|
||||
emit_loading, init_loading, loading_try_for_each_concurrent,
|
||||
emit_loading, init_loading, init_or_edit_loading,
|
||||
loading_try_for_each_concurrent,
|
||||
};
|
||||
use crate::event::LoadingBarType;
|
||||
use crate::event::{LoadingBarId, LoadingBarType};
|
||||
use crate::state::{LinkedData, ModrinthProject, ModrinthVersion, SideType};
|
||||
use crate::util::fetch::{
|
||||
fetch, fetch_json, fetch_mirrors, write, write_cached_icon,
|
||||
fetch, fetch_advanced, fetch_json, fetch_mirrors, write, write_cached_icon,
|
||||
};
|
||||
use crate::State;
|
||||
use async_zip::tokio::read::seek::ZipFileReader;
|
||||
@ -75,17 +76,30 @@ enum PackDependency {
|
||||
|
||||
pub async fn install_pack_from_version_id(
|
||||
version_id: String,
|
||||
title: Option<String>,
|
||||
) -> crate::Result<PathBuf> {
|
||||
let state = State::get().await?;
|
||||
|
||||
let loading_bar = init_loading(
|
||||
LoadingBarType::PackFileDownload {
|
||||
pack_name: title,
|
||||
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.io_semaphore,
|
||||
&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) {
|
||||
@ -102,20 +116,31 @@ pub async fn install_pack_from_version_id(
|
||||
)
|
||||
})?;
|
||||
|
||||
let file = fetch(&url, hash.map(|x| &**x), &state.io_semaphore).await?;
|
||||
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.io_semaphore,
|
||||
&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.io_semaphore).await?;
|
||||
let icon_bytes = fetch(&icon_url, None, &state.fetch_semaphore).await?;
|
||||
|
||||
let filename = icon_url.rsplit('/').next();
|
||||
|
||||
@ -135,6 +160,7 @@ pub async fn install_pack_from_version_id(
|
||||
} else {
|
||||
None
|
||||
};
|
||||
emit_loading(&loading_bar, 10.0, None).await?;
|
||||
|
||||
install_pack(
|
||||
file,
|
||||
@ -142,6 +168,7 @@ pub async fn install_pack_from_version_id(
|
||||
Some(project.title),
|
||||
Some(version.project_id),
|
||||
Some(version.id),
|
||||
Some(loading_bar),
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -149,7 +176,7 @@ pub async fn install_pack_from_version_id(
|
||||
pub async fn install_pack_from_file(path: PathBuf) -> crate::Result<PathBuf> {
|
||||
let file = fs::read(path).await?;
|
||||
|
||||
install_pack(bytes::Bytes::from(file), None, None, None, None).await
|
||||
install_pack(bytes::Bytes::from(file), None, None, None, None, None).await
|
||||
}
|
||||
|
||||
async fn install_pack(
|
||||
@ -158,6 +185,7 @@ async fn install_pack(
|
||||
override_title: Option<String>,
|
||||
project_id: Option<String>,
|
||||
version_id: Option<String>,
|
||||
existing_loading_bar: Option<LoadingBarId>,
|
||||
) -> crate::Result<PathBuf> {
|
||||
let state = &State::get().await?;
|
||||
|
||||
@ -242,14 +270,15 @@ async fn install_pack(
|
||||
.await?;
|
||||
let profile = profile_raw.clone();
|
||||
let result = async {
|
||||
let loading_bar = init_loading(
|
||||
let loading_bar = init_or_edit_loading(
|
||||
existing_loading_bar,
|
||||
LoadingBarType::PackDownload {
|
||||
pack_name: pack.name.clone(),
|
||||
pack_id: project_id,
|
||||
pack_version: version_id,
|
||||
},
|
||||
100.0,
|
||||
"Downloading modpack...",
|
||||
"Downloading modpack",
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -260,7 +289,7 @@ async fn install_pack(
|
||||
.map(Ok::<PackFile, crate::Error>),
|
||||
None,
|
||||
Some(&loading_bar),
|
||||
80.0,
|
||||
70.0,
|
||||
num_files,
|
||||
None,
|
||||
|project| {
|
||||
@ -287,7 +316,7 @@ async fn install_pack(
|
||||
.hashes
|
||||
.get(&PackFileHash::Sha1)
|
||||
.map(|x| &**x),
|
||||
&state.io_semaphore,
|
||||
&state.fetch_semaphore,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -365,11 +394,11 @@ async fn install_pack(
|
||||
.await
|
||||
};
|
||||
|
||||
emit_loading(&loading_bar, 0.05, Some("Extracting overrides"))
|
||||
emit_loading(&loading_bar, 0.0, Some("Extracting overrides"))
|
||||
.await?;
|
||||
extract_overrides("overrides".to_string()).await?;
|
||||
extract_overrides("client_overrides".to_string()).await?;
|
||||
emit_loading(&loading_bar, 0.1, Some("Done extacting overrides"))
|
||||
emit_loading(&loading_bar, 29.9, Some("Done extacting overrides"))
|
||||
.await?;
|
||||
|
||||
if let Some(profile) = crate::api::profile::get(&profile).await? {
|
||||
@ -380,13 +409,6 @@ async fn install_pack(
|
||||
Some(loading_bar)
|
||||
),
|
||||
)?;
|
||||
} else {
|
||||
emit_loading(
|
||||
&loading_bar,
|
||||
0.1,
|
||||
Some("Done extacting overrides"),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok::<PathBuf, crate::Error>(profile)
|
||||
|
||||
@ -148,7 +148,7 @@ pub async fn update_all(profile_path: &Path) -> crate::Result<()> {
|
||||
profile_name: profile.metadata.name.clone(),
|
||||
},
|
||||
100.0,
|
||||
"Updating profile...",
|
||||
"Updating profile",
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -159,7 +159,7 @@ pub async fn update_all(profile_path: &Path) -> crate::Result<()> {
|
||||
None,
|
||||
Some(&loading_bar),
|
||||
100.0,
|
||||
profile.projects.len(),
|
||||
profile.projects.keys().len(),
|
||||
None,
|
||||
|project| update_project(profile_path, project, Some(true)),
|
||||
)
|
||||
@ -344,7 +344,7 @@ pub async fn remove_project(
|
||||
}
|
||||
/// Run Minecraft using a profile and the default credentials, logged in credentials,
|
||||
/// failing with an error if no credentials are available
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tracing::instrument]
|
||||
pub async fn run(path: &Path) -> crate::Result<Arc<RwLock<MinecraftChild>>> {
|
||||
let state = State::get().await?;
|
||||
|
||||
@ -367,7 +367,7 @@ pub async fn run(path: &Path) -> crate::Result<Arc<RwLock<MinecraftChild>>> {
|
||||
|
||||
/// Run Minecraft using a profile, and credentials for authentication
|
||||
/// Returns Arc pointer to RwLock to Child
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[tracing::instrument]
|
||||
pub async fn run_credentials(
|
||||
path: &Path,
|
||||
credentials: &auth::Credentials,
|
||||
@ -398,6 +398,7 @@ pub async fn run_credentials(
|
||||
version,
|
||||
profile.metadata.loader_version.as_ref(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
let pre_launch_hooks =
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
//! Theseus profile management interface
|
||||
use crate::event::emit::emit_warning;
|
||||
use crate::state::LinkedData;
|
||||
use crate::{
|
||||
event::{emit::emit_profile, ProfilePayloadType},
|
||||
@ -15,6 +16,7 @@ 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";
|
||||
@ -47,6 +49,7 @@ pub async fn profile_create(
|
||||
linked_data: Option<LinkedData>, // the linked project ID (mainly for modpacks)- used for updating
|
||||
skip_install_profile: Option<bool>,
|
||||
) -> crate::Result<PathBuf> {
|
||||
trace!("Creating new profile. {}", name);
|
||||
let state = State::get().await?;
|
||||
let metadata = state.metadata.read().await;
|
||||
|
||||
@ -74,7 +77,7 @@ pub async fn profile_create(
|
||||
fs::create_dir_all(&path).await?;
|
||||
}
|
||||
|
||||
println!(
|
||||
info!(
|
||||
"Creating profile at path {}",
|
||||
&canonicalize(&path)?.display()
|
||||
);
|
||||
@ -173,7 +176,7 @@ pub async fn profile_create(
|
||||
extra_arguments: None,
|
||||
});
|
||||
} else {
|
||||
println!("Could not detect optimal JRE: {optimal_version_key}, falling back to system default.");
|
||||
emit_warning(&format!("Could not detect optimal JRE: {optimal_version_key}, falling back to system default.")).await?;
|
||||
}
|
||||
|
||||
emit_profile(
|
||||
|
||||
@ -19,8 +19,22 @@ pub async fn get() -> crate::Result<Settings> {
|
||||
pub async fn set(settings: Settings) -> crate::Result<()> {
|
||||
let state = State::get().await?;
|
||||
// Replaces the settings struct in the RwLock with the passed argument
|
||||
let (reset_io, reset_fetch) = async {
|
||||
let read = state.settings.read().await;
|
||||
(
|
||||
settings.max_concurrent_writes != read.max_concurrent_writes,
|
||||
settings.max_concurrent_downloads != read.max_concurrent_downloads,
|
||||
)
|
||||
}
|
||||
.await;
|
||||
|
||||
*state.settings.write().await = settings;
|
||||
state.reset_semaphore().await; // reset semaphore to new max
|
||||
if reset_io {
|
||||
state.reset_io_semaphore().await;
|
||||
}
|
||||
if reset_fetch {
|
||||
state.reset_fetch_semaphore().await;
|
||||
}
|
||||
State::sync().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
use super::LoadingBarId;
|
||||
use crate::event::{
|
||||
EventError, LoadingBar, LoadingBarType, ProcessPayloadType,
|
||||
ProfilePayloadType,
|
||||
};
|
||||
use futures::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use tracing::warn;
|
||||
|
||||
#[cfg(feature = "tauri")]
|
||||
use crate::event::{
|
||||
@ -13,6 +15,9 @@ use crate::event::{
|
||||
use tauri::Manager;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
const CLI_PROGRESS_BAR_TOTAL: u64 = 1000;
|
||||
|
||||
/*
|
||||
Events are a way we can communciate with the Tauri frontend from the Rust backend.
|
||||
We include a feature flag for Tauri, so that we can compile this code without Tauri.
|
||||
@ -45,18 +50,34 @@ pub async fn init_loading(
|
||||
bar_type: LoadingBarType,
|
||||
total: f64,
|
||||
title: &str,
|
||||
) -> crate::Result<Uuid> {
|
||||
) -> crate::Result<LoadingBarId> {
|
||||
let event_state = crate::EventState::get().await?;
|
||||
let key = Uuid::new_v4();
|
||||
let key = LoadingBarId(Uuid::new_v4());
|
||||
|
||||
event_state.loading_bars.write().await.insert(
|
||||
key,
|
||||
key.0,
|
||||
LoadingBar {
|
||||
loading_bar_id: key,
|
||||
loading_bar_uuid: key.0,
|
||||
message: title.to_string(),
|
||||
total,
|
||||
current: 0.0,
|
||||
bar_type,
|
||||
#[cfg(feature = "cli")]
|
||||
cli_progress_bar: {
|
||||
let pb = indicatif::ProgressBar::new(CLI_PROGRESS_BAR_TOTAL);
|
||||
|
||||
pb.set_position(0);
|
||||
pb.set_style(
|
||||
indicatif::ProgressStyle::default_bar()
|
||||
.template(
|
||||
"{spinner:.green} [{elapsed_precise}] [{bar:.lime/green}] {pos}/{len} {msg}",
|
||||
).unwrap()
|
||||
.progress_chars("#>-"),
|
||||
);
|
||||
//pb.set_message(title);
|
||||
|
||||
pb
|
||||
},
|
||||
},
|
||||
);
|
||||
// attempt an initial loading_emit event to the frontend
|
||||
@ -65,13 +86,13 @@ pub async fn init_loading(
|
||||
}
|
||||
|
||||
pub async fn init_or_edit_loading(
|
||||
id: Option<Uuid>,
|
||||
id: Option<LoadingBarId>,
|
||||
bar_type: LoadingBarType,
|
||||
total: f64,
|
||||
title: &str,
|
||||
) -> crate::Result<Uuid> {
|
||||
) -> crate::Result<LoadingBarId> {
|
||||
if let Some(id) = id {
|
||||
edit_loading(id, bar_type, total, title).await?;
|
||||
edit_loading(&id, bar_type, total, title).await?;
|
||||
|
||||
Ok(id)
|
||||
} else {
|
||||
@ -80,21 +101,27 @@ pub async fn init_or_edit_loading(
|
||||
}
|
||||
|
||||
// Edits a loading bar's type
|
||||
// This also resets the bar's current progress to 0
|
||||
pub async fn edit_loading(
|
||||
id: Uuid,
|
||||
id: &LoadingBarId,
|
||||
bar_type: LoadingBarType,
|
||||
total: f64,
|
||||
title: &str,
|
||||
) -> crate::Result<()> {
|
||||
let event_state = crate::EventState::get().await?;
|
||||
|
||||
if let Some(bar) = event_state.loading_bars.write().await.get_mut(&id) {
|
||||
if let Some(bar) = event_state.loading_bars.write().await.get_mut(&id.0) {
|
||||
bar.bar_type = bar_type;
|
||||
bar.total = total;
|
||||
bar.message = title.to_string();
|
||||
bar.current = 0.0;
|
||||
#[cfg(feature = "cli")]
|
||||
{
|
||||
bar.cli_progress_bar.reset(); // indicatif::ProgressBar::new(CLI_PROGRESS_BAR_TOTAL as u64);
|
||||
}
|
||||
};
|
||||
|
||||
emit_loading(&id, 0.0, None).await?;
|
||||
emit_loading(id, 0.0, None).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -104,18 +131,19 @@ pub async fn edit_loading(
|
||||
// message is the message to display on the loading bar- if None, use the loading bar's default one
|
||||
// By convention, fraction is the fraction of the progress bar that is filled
|
||||
#[allow(unused_variables)]
|
||||
#[tracing::instrument(level = "debug")]
|
||||
pub async fn emit_loading(
|
||||
key: &Uuid,
|
||||
key: &LoadingBarId,
|
||||
increment_frac: f64,
|
||||
message: Option<&str>,
|
||||
) -> crate::Result<()> {
|
||||
let event_state = crate::EventState::get().await?;
|
||||
|
||||
let mut loading_bar = event_state.loading_bars.write().await;
|
||||
let loading_bar = match loading_bar.get_mut(key) {
|
||||
let loading_bar = match loading_bar.get_mut(&key.0) {
|
||||
Some(f) => f,
|
||||
None => {
|
||||
return Err(EventError::NoLoadingBar(*key).into());
|
||||
return Err(EventError::NoLoadingBar(key.0).into());
|
||||
}
|
||||
};
|
||||
|
||||
@ -128,6 +156,22 @@ pub async fn emit_loading(
|
||||
} else {
|
||||
Some(display_frac)
|
||||
};
|
||||
|
||||
// Emit event to indicatif progress bar
|
||||
#[cfg(feature = "cli")]
|
||||
{
|
||||
loading_bar.cli_progress_bar.set_message(
|
||||
message
|
||||
.map(|x| x.to_string())
|
||||
.unwrap_or(loading_bar.message.clone()),
|
||||
);
|
||||
loading_bar.cli_progress_bar.set_position(
|
||||
((loading_bar.current / loading_bar.total)
|
||||
* CLI_PROGRESS_BAR_TOTAL as f64)
|
||||
.round() as u64,
|
||||
);
|
||||
}
|
||||
|
||||
// Emit event to tauri
|
||||
#[cfg(feature = "tauri")]
|
||||
event_state
|
||||
@ -138,7 +182,7 @@ pub async fn emit_loading(
|
||||
fraction: display_frac,
|
||||
message: message.unwrap_or(&loading_bar.message).to_string(),
|
||||
event: loading_bar.bar_type.clone(),
|
||||
loader_uuid: loading_bar.loading_bar_id,
|
||||
loader_uuid: loading_bar.loading_bar_uuid,
|
||||
},
|
||||
)
|
||||
.map_err(EventError::from)?;
|
||||
@ -162,6 +206,7 @@ pub async fn emit_warning(message: &str) -> crate::Result<()> {
|
||||
)
|
||||
.map_err(EventError::from)?;
|
||||
}
|
||||
warn!("{}", message);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -235,7 +280,6 @@ macro_rules! count {
|
||||
() => (0usize);
|
||||
( $x:tt $($xs:tt)* ) => (1usize + $crate::count!($($xs)*));
|
||||
}
|
||||
#[cfg(feature = "tauri")]
|
||||
#[macro_export]
|
||||
macro_rules! loading_join {
|
||||
($key:expr, $total:expr, $message:expr; $($task:expr $(,)?)+) => {
|
||||
@ -272,24 +316,16 @@ macro_rules! loading_join {
|
||||
};
|
||||
|
||||
}
|
||||
#[cfg(not(feature = "tauri"))]
|
||||
#[macro_export]
|
||||
macro_rules! loading_join {
|
||||
($start:expr, $end:expr, $message:expr; $($future:expr $(,)?)+) => {{
|
||||
tokio::try_join!($($future),+)
|
||||
}};
|
||||
}
|
||||
|
||||
// A drop in replacement to try_for_each_concurrent that emits loading events as it goes
|
||||
// Key is the key to use for which loading bar- a LoadingBarId. If None, does nothing
|
||||
// Total is the total amount of progress that the loading bar should take up by all futures in this (will be split evenly amongst them).
|
||||
// If message is Some(t) you will overwrite this loading bar's message with a custom one
|
||||
// num_futs is the number of futures that will be run, which is needed as we allow Iterator to be passed in, which doesn't have a size
|
||||
#[cfg(feature = "tauri")]
|
||||
pub async fn loading_try_for_each_concurrent<I, F, Fut, T>(
|
||||
stream: I,
|
||||
limit: Option<usize>,
|
||||
key: Option<&Uuid>,
|
||||
key: Option<&LoadingBarId>,
|
||||
total: f64,
|
||||
num_futs: usize, // num is in here as we allow Iterator to be passed in, which doesn't have a size
|
||||
message: Option<&str>,
|
||||
@ -316,31 +352,3 @@ where
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tauri"))]
|
||||
pub async fn loading_try_for_each_concurrent<I, F, Fut, T>(
|
||||
stream: I,
|
||||
limit: Option<usize>,
|
||||
_key: Option<&Uuid>,
|
||||
_total: f64,
|
||||
_num_futs: usize, // num is in here as we allow Iterator to be passed in, which doesn't have a size
|
||||
_message: Option<&str>,
|
||||
f: F,
|
||||
) -> crate::Result<()>
|
||||
where
|
||||
I: futures::TryStreamExt<Error = crate::Error> + TryStream<Ok = T>,
|
||||
F: FnMut(T) -> Fut + Send,
|
||||
Fut: Future<Output = crate::Result<()>> + Send,
|
||||
T: Send,
|
||||
{
|
||||
let mut f = f;
|
||||
stream
|
||||
.try_for_each_concurrent(limit, |item| {
|
||||
let f = f(item);
|
||||
async move {
|
||||
f.await?;
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
@ -48,11 +48,18 @@ impl EventState {
|
||||
Ok(EVENT_STATE.get().ok_or(EventError::NotInitialized)?.clone())
|
||||
}
|
||||
|
||||
// Values provided should not be used directly, as they are clones and are not guaranteed to be up-to-date
|
||||
pub async fn list_progress_bars() -> crate::Result<HashMap<Uuid, LoadingBar>>
|
||||
{
|
||||
let value = Self::get().await?;
|
||||
let read = value.loading_bars.read().await;
|
||||
Ok(read.clone())
|
||||
|
||||
let mut display_list: HashMap<Uuid, LoadingBar> = HashMap::new();
|
||||
for (uuid, loading_bar) in read.iter() {
|
||||
display_list.insert(*uuid, loading_bar.clone());
|
||||
}
|
||||
|
||||
Ok(display_list)
|
||||
}
|
||||
|
||||
// Initialization requires no app handle in non-tauri mode, so we can just use the same function
|
||||
@ -64,16 +71,84 @@ impl EventState {
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
pub struct LoadingBar {
|
||||
pub loading_bar_id: Uuid,
|
||||
// loading_bar_uuid not be used directly by external functions as it may not reflect the current state of the loading bar/hashmap
|
||||
pub loading_bar_uuid: Uuid,
|
||||
pub message: String,
|
||||
pub total: f64,
|
||||
pub current: f64,
|
||||
pub bar_type: LoadingBarType,
|
||||
#[cfg(feature = "cli")]
|
||||
#[serde(skip)]
|
||||
pub cli_progress_bar: indicatif::ProgressBar,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct LoadingBarId(Uuid);
|
||||
|
||||
// When Loading bar id is dropped, we should remove it from the hashmap
|
||||
impl Drop for LoadingBarId {
|
||||
fn drop(&mut self) {
|
||||
let loader_uuid = self.0;
|
||||
let _event = LoadingBarType::StateInit;
|
||||
let _message = "finished".to_string();
|
||||
tokio::spawn(async move {
|
||||
if let Ok(event_state) = crate::EventState::get().await {
|
||||
{
|
||||
let mut bars = event_state.loading_bars.write().await;
|
||||
bars.remove(&loader_uuid);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// When Loading bar is dropped, should attempt to throw out one last event to indicate that the loading bar is done
|
||||
#[cfg(feature = "tauri")]
|
||||
impl Drop for LoadingBar {
|
||||
fn drop(&mut self) {
|
||||
let loader_uuid = self.loading_bar_uuid;
|
||||
let event = self.bar_type.clone();
|
||||
let fraction = self.current / self.total;
|
||||
let cli_progress_bar = self.cli_progress_bar.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
#[cfg(feature = "tauri")]
|
||||
{
|
||||
use tauri::Manager;
|
||||
if let Ok(event_state) = crate::EventState::get().await {
|
||||
let _ = event_state.app.emit_all(
|
||||
"loading",
|
||||
LoadingPayload {
|
||||
fraction: None,
|
||||
message: "Completed".to_string(),
|
||||
event,
|
||||
loader_uuid,
|
||||
},
|
||||
);
|
||||
tracing::debug!(
|
||||
"Exited at {fraction} for loading bar: {:?}",
|
||||
loader_uuid
|
||||
);
|
||||
}
|
||||
}
|
||||
// Emit event to indicatif progress bar arc
|
||||
#[cfg(feature = "cli")]
|
||||
{
|
||||
cli_progress_bar.finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
#[serde(tag = "type")]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum LoadingBarType {
|
||||
StateInit,
|
||||
PackFileDownload {
|
||||
pack_name: Option<String>,
|
||||
pack_version: String,
|
||||
},
|
||||
PackDownload {
|
||||
pack_name: String,
|
||||
pack_id: Option<String>,
|
||||
@ -124,6 +199,7 @@ pub struct ProfilePayload {
|
||||
pub event: ProfilePayloadType,
|
||||
}
|
||||
#[derive(Serialize, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ProfilePayloadType {
|
||||
Created,
|
||||
Added, // also triggered when Created
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
//! Authentication flow based on Hydra
|
||||
use crate::util::fetch::{fetch_advanced, fetch_json};
|
||||
use crate::util::fetch::{fetch_advanced, fetch_json, FetchSemaphore};
|
||||
use async_tungstenite as ws;
|
||||
use chrono::{prelude::*, Duration};
|
||||
use futures::prelude::*;
|
||||
use lazy_static::lazy_static;
|
||||
use reqwest::Method;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::{RwLock, Semaphore};
|
||||
use url::Url;
|
||||
|
||||
lazy_static! {
|
||||
@ -95,7 +94,7 @@ impl HydraAuthFlow<ws::tokio::ConnectStream> {
|
||||
|
||||
pub async fn extract_credentials(
|
||||
&mut self,
|
||||
semaphore: &RwLock<Semaphore>,
|
||||
semaphore: &FetchSemaphore,
|
||||
) -> crate::Result<Credentials> {
|
||||
// Minecraft bearer token
|
||||
let token_resp = self
|
||||
@ -130,7 +129,7 @@ impl HydraAuthFlow<ws::tokio::ConnectStream> {
|
||||
|
||||
pub async fn refresh_credentials(
|
||||
credentials: &mut Credentials,
|
||||
semaphore: &RwLock<Semaphore>,
|
||||
semaphore: &FetchSemaphore,
|
||||
) -> crate::Result<()> {
|
||||
let resp = fetch_json::<TokenJSON>(
|
||||
Method::POST,
|
||||
@ -152,7 +151,7 @@ pub async fn refresh_credentials(
|
||||
// Helpers
|
||||
async fn fetch_info(
|
||||
token: &str,
|
||||
semaphore: &RwLock<Semaphore>,
|
||||
semaphore: &FetchSemaphore,
|
||||
) -> crate::Result<ProfileInfoJSON> {
|
||||
let result = fetch_advanced(
|
||||
Method::GET,
|
||||
@ -160,6 +159,7 @@ async fn fetch_info(
|
||||
None,
|
||||
None,
|
||||
Some(("Authorization", &format!("Bearer {token}"))),
|
||||
None,
|
||||
semaphore,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
//! Downloader for Minecraft data
|
||||
|
||||
use crate::{
|
||||
event::emit::{emit_loading, loading_try_for_each_concurrent},
|
||||
event::{
|
||||
emit::{emit_loading, loading_try_for_each_concurrent},
|
||||
LoadingBarId,
|
||||
},
|
||||
state::State,
|
||||
util::{fetch::*, platform::OsExt},
|
||||
};
|
||||
@ -15,24 +18,26 @@ use daedalus::{
|
||||
};
|
||||
use futures::prelude::*;
|
||||
use tokio::{fs, sync::OnceCell};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn download_minecraft(
|
||||
st: &State,
|
||||
version: &GameVersionInfo,
|
||||
loading_bar: Uuid,
|
||||
loading_bar: &LoadingBarId,
|
||||
) -> crate::Result<()> {
|
||||
log::info!("Downloading Minecraft version {}", version.id);
|
||||
let assets_index = download_assets_index(st, version).await?;
|
||||
tracing::info!("Downloading Minecraft version {}", version.id);
|
||||
// 5
|
||||
let assets_index =
|
||||
download_assets_index(st, version, Some(loading_bar)).await?;
|
||||
|
||||
tokio::try_join! {
|
||||
download_client(st, version, Some(&loading_bar)),
|
||||
download_assets(st, version.assets == "legacy", &assets_index, Some(&loading_bar)),
|
||||
download_libraries(st, version.libraries.as_slice(), &version.id, Some(&loading_bar))
|
||||
// Total loading sums to 80
|
||||
download_client(st, version, Some(loading_bar)), // 10
|
||||
download_assets(st, version.assets == "legacy", &assets_index, Some(loading_bar)), // 35
|
||||
download_libraries(st, version.libraries.as_slice(), &version.id, Some(loading_bar)) // 35
|
||||
}?;
|
||||
|
||||
log::info!("Done downloading Minecraft!");
|
||||
tracing::info!("Done downloading Minecraft!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -42,10 +47,11 @@ pub async fn download_version_info(
|
||||
version: &GameVersion,
|
||||
loader: Option<&LoaderVersion>,
|
||||
force: Option<bool>,
|
||||
loading_bar: Option<&LoadingBarId>,
|
||||
) -> crate::Result<GameVersionInfo> {
|
||||
let version_id = loader
|
||||
.map_or(version.id.clone(), |it| format!("{}-{}", version.id, it.id));
|
||||
log::debug!("Loading version info for Minecraft {version_id}");
|
||||
tracing::debug!("Loading version info for Minecraft {version_id}");
|
||||
let path = st
|
||||
.directories
|
||||
.version_dir(&version_id)
|
||||
@ -57,7 +63,7 @@ pub async fn download_version_info(
|
||||
.await
|
||||
.and_then(|ref it| Ok(serde_json::from_slice(it)?))
|
||||
} else {
|
||||
log::info!("Downloading version info for version {}", &version.id);
|
||||
tracing::info!("Downloading version info for version {}", &version.id);
|
||||
let mut info = d::minecraft::fetch_version_info(version).await?;
|
||||
|
||||
if let Some(loader) = loader {
|
||||
@ -70,7 +76,25 @@ pub async fn download_version_info(
|
||||
Ok(info)
|
||||
}?;
|
||||
|
||||
log::debug!("Loaded version info for Minecraft {version_id}");
|
||||
if let Some(loading_bar) = loading_bar {
|
||||
emit_loading(
|
||||
loading_bar,
|
||||
if res
|
||||
.processors
|
||||
.as_ref()
|
||||
.map(|x| !x.is_empty())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
5.0
|
||||
} else {
|
||||
15.0
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
tracing::debug!("Loaded version info for Minecraft {version_id}");
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@ -78,10 +102,10 @@ pub async fn download_version_info(
|
||||
pub async fn download_client(
|
||||
st: &State,
|
||||
version_info: &GameVersionInfo,
|
||||
loading_bar: Option<&Uuid>,
|
||||
loading_bar: Option<&LoadingBarId>,
|
||||
) -> crate::Result<()> {
|
||||
let version = &version_info.id;
|
||||
log::debug!("Locating client for version {version}");
|
||||
tracing::debug!("Locating client for version {version}");
|
||||
let client_download = version_info
|
||||
.downloads
|
||||
.get(&d::minecraft::DownloadType::Client)
|
||||
@ -100,17 +124,17 @@ pub async fn download_client(
|
||||
let bytes = fetch(
|
||||
&client_download.url,
|
||||
Some(&client_download.sha1),
|
||||
&st.io_semaphore,
|
||||
&st.fetch_semaphore,
|
||||
)
|
||||
.await?;
|
||||
write(&path, &bytes, &st.io_semaphore).await?;
|
||||
log::info!("Fetched client version {version}");
|
||||
tracing::trace!("Fetched client version {version}");
|
||||
}
|
||||
if let Some(loading_bar) = loading_bar {
|
||||
emit_loading(loading_bar, 20.0, None).await?;
|
||||
emit_loading(loading_bar, 10.0, None).await?;
|
||||
}
|
||||
|
||||
log::debug!("Client loaded for version {version}!");
|
||||
tracing::debug!("Client loaded for version {version}!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -118,8 +142,9 @@ pub async fn download_client(
|
||||
pub async fn download_assets_index(
|
||||
st: &State,
|
||||
version: &GameVersionInfo,
|
||||
loading_bar: Option<&LoadingBarId>,
|
||||
) -> crate::Result<AssetsIndex> {
|
||||
log::debug!("Loading assets index");
|
||||
tracing::debug!("Loading assets index");
|
||||
let path = st
|
||||
.directories
|
||||
.assets_index_dir()
|
||||
@ -133,11 +158,14 @@ pub async fn download_assets_index(
|
||||
} else {
|
||||
let index = d::minecraft::fetch_assets_index(version).await?;
|
||||
write(&path, &serde_json::to_vec(&index)?, &st.io_semaphore).await?;
|
||||
log::info!("Fetched assets index");
|
||||
tracing::info!("Fetched assets index");
|
||||
Ok(index)
|
||||
}?;
|
||||
|
||||
log::debug!("Assets index successfully loaded!");
|
||||
if let Some(loading_bar) = loading_bar {
|
||||
emit_loading(loading_bar, 5.0, None).await?;
|
||||
}
|
||||
tracing::debug!("Assets index successfully loaded!");
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
@ -146,9 +174,9 @@ pub async fn download_assets(
|
||||
st: &State,
|
||||
with_legacy: bool,
|
||||
index: &AssetsIndex,
|
||||
loading_bar: Option<&Uuid>,
|
||||
loading_bar: Option<&LoadingBarId>,
|
||||
) -> crate::Result<()> {
|
||||
log::debug!("Loading assets");
|
||||
tracing::debug!("Loading assets");
|
||||
let num_futs = index.objects.len();
|
||||
let assets = stream::iter(index.objects.iter())
|
||||
.map(Ok::<(&String, &Asset), crate::Error>);
|
||||
@ -156,7 +184,7 @@ pub async fn download_assets(
|
||||
loading_try_for_each_concurrent(assets,
|
||||
None,
|
||||
loading_bar,
|
||||
50.0,
|
||||
35.0,
|
||||
num_futs,
|
||||
None,
|
||||
|(name, asset)| async move {
|
||||
@ -172,33 +200,33 @@ pub async fn download_assets(
|
||||
async {
|
||||
if !resource_path.exists() {
|
||||
let resource = fetch_cell
|
||||
.get_or_try_init(|| fetch(&url, Some(hash), &st.io_semaphore))
|
||||
.get_or_try_init(|| fetch(&url, Some(hash), &st.fetch_semaphore))
|
||||
.await?;
|
||||
write(&resource_path, resource, &st.io_semaphore).await?;
|
||||
log::info!("Fetched asset with hash {hash}");
|
||||
tracing::trace!("Fetched asset with hash {hash}");
|
||||
}
|
||||
Ok::<_, crate::Error>(())
|
||||
},
|
||||
async {
|
||||
if with_legacy {
|
||||
let resource = fetch_cell
|
||||
.get_or_try_init(|| fetch(&url, Some(hash), &st.io_semaphore))
|
||||
.get_or_try_init(|| fetch(&url, Some(hash), &st.fetch_semaphore))
|
||||
.await?;
|
||||
let resource_path = st.directories.legacy_assets_dir().join(
|
||||
name.replace('/', &String::from(std::path::MAIN_SEPARATOR))
|
||||
);
|
||||
write(&resource_path, resource, &st.io_semaphore).await?;
|
||||
log::info!("Fetched legacy asset with hash {hash}");
|
||||
tracing::trace!("Fetched legacy asset with hash {hash}");
|
||||
}
|
||||
Ok::<_, crate::Error>(())
|
||||
},
|
||||
}?;
|
||||
|
||||
log::debug!("Loaded asset with hash {hash}");
|
||||
tracing::trace!("Loaded asset with hash {hash}");
|
||||
Ok(())
|
||||
}).await?;
|
||||
|
||||
log::debug!("Done loading assets!");
|
||||
tracing::debug!("Done loading assets!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -207,9 +235,9 @@ pub async fn download_libraries(
|
||||
st: &State,
|
||||
libraries: &[Library],
|
||||
version: &str,
|
||||
loading_bar: Option<&Uuid>,
|
||||
loading_bar: Option<&LoadingBarId>,
|
||||
) -> crate::Result<()> {
|
||||
log::debug!("Loading libraries");
|
||||
tracing::debug!("Loading libraries");
|
||||
|
||||
tokio::try_join! {
|
||||
fs::create_dir_all(st.directories.libraries_dir()),
|
||||
@ -218,7 +246,7 @@ pub async fn download_libraries(
|
||||
let num_files = libraries.len();
|
||||
loading_try_for_each_concurrent(
|
||||
stream::iter(libraries.iter())
|
||||
.map(Ok::<&Library, crate::Error>), None, loading_bar,50.0,num_files, None,|library| async move {
|
||||
.map(Ok::<&Library, crate::Error>), None, loading_bar,35.0,num_files, None,|library| async move {
|
||||
if let Some(rules) = &library.rules {
|
||||
if !rules.iter().all(super::parse_rule) {
|
||||
return Ok(());
|
||||
@ -235,10 +263,10 @@ pub async fn download_libraries(
|
||||
artifact: Some(ref artifact),
|
||||
..
|
||||
}) => {
|
||||
let bytes = fetch(&artifact.url, Some(&artifact.sha1), &st.io_semaphore)
|
||||
let bytes = fetch(&artifact.url, Some(&artifact.sha1), &st.fetch_semaphore)
|
||||
.await?;
|
||||
write(&path, &bytes, &st.io_semaphore).await?;
|
||||
log::info!("Fetched library {}", &library.name);
|
||||
tracing::trace!("Fetched library {}", &library.name);
|
||||
Ok::<_, crate::Error>(())
|
||||
}
|
||||
None => {
|
||||
@ -250,9 +278,9 @@ pub async fn download_libraries(
|
||||
&artifact_path
|
||||
].concat();
|
||||
|
||||
let bytes = fetch(&url, None, &st.io_semaphore).await?;
|
||||
let bytes = fetch(&url, None, &st.fetch_semaphore).await?;
|
||||
write(&path, &bytes, &st.io_semaphore).await?;
|
||||
log::info!("Fetched library {}", &library.name);
|
||||
tracing::trace!("Fetched library {}", &library.name);
|
||||
Ok::<_, crate::Error>(())
|
||||
}
|
||||
_ => Ok(())
|
||||
@ -277,15 +305,15 @@ pub async fn download_libraries(
|
||||
);
|
||||
|
||||
if let Some(native) = classifiers.get(&parsed_key) {
|
||||
let data = fetch(&native.url, Some(&native.sha1), &st.io_semaphore).await?;
|
||||
let data = fetch(&native.url, Some(&native.sha1), &st.fetch_semaphore).await?;
|
||||
let reader = std::io::Cursor::new(&data);
|
||||
if let Ok(mut archive) = zip::ZipArchive::new(reader) {
|
||||
match archive.extract(&st.directories.version_natives_dir(version)) {
|
||||
Ok(_) => log::info!("Fetched native {}", &library.name),
|
||||
Err(err) => log::error!("Failed extracting native {}. err: {}", &library.name, err)
|
||||
Ok(_) => tracing::info!("Fetched native {}", &library.name),
|
||||
Err(err) => tracing::error!("Failed extracting native {}. err: {}", &library.name, err)
|
||||
}
|
||||
} else {
|
||||
log::error!("Failed extracting native {}", &library.name)
|
||||
tracing::error!("Failed extracting native {}", &library.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -294,11 +322,11 @@ pub async fn download_libraries(
|
||||
}
|
||||
}?;
|
||||
|
||||
log::debug!("Loaded library {}", library.name);
|
||||
tracing::debug!("Loaded library {}", library.name);
|
||||
Ok(())
|
||||
}
|
||||
).await?;
|
||||
|
||||
log::debug!("Done loading libraries!");
|
||||
tracing::debug!("Done loading libraries!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
//! Logic for launching Minecraft
|
||||
use crate::event::emit::{emit_loading, init_or_edit_loading};
|
||||
use crate::event::LoadingBarType;
|
||||
use crate::event::{LoadingBarId, LoadingBarType};
|
||||
use crate::{
|
||||
process,
|
||||
state::{self as st, MinecraftChild},
|
||||
@ -53,9 +53,10 @@ macro_rules! processor_rules {
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(profile))]
|
||||
pub async fn install_minecraft(
|
||||
profile: &Profile,
|
||||
existing_loading_bar: Option<Uuid>,
|
||||
existing_loading_bar: Option<LoadingBarId>,
|
||||
) -> crate::Result<()> {
|
||||
let state = State::get().await?;
|
||||
let instance_path = &canonicalize(&profile.path)?;
|
||||
@ -79,14 +80,6 @@ pub async fn install_minecraft(
|
||||
format!("{}-{}", version.id.clone(), it.id.clone())
|
||||
});
|
||||
|
||||
let mut version_info = download::download_version_info(
|
||||
&state,
|
||||
version,
|
||||
profile.metadata.loader_version.as_ref(),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let loading_bar = init_or_edit_loading(
|
||||
existing_loading_bar,
|
||||
LoadingBarType::MinecraftDownload {
|
||||
@ -95,11 +88,22 @@ pub async fn install_minecraft(
|
||||
profile_uuid: profile.uuid,
|
||||
},
|
||||
100.0,
|
||||
"Downloading Minecraft...",
|
||||
"Downloading Minecraft",
|
||||
)
|
||||
.await?;
|
||||
|
||||
download::download_minecraft(&state, &version_info, loading_bar).await?;
|
||||
// Download version info
|
||||
let mut version_info = download::download_version_info(
|
||||
&state,
|
||||
version,
|
||||
profile.metadata.loader_version.as_ref(),
|
||||
None,
|
||||
Some(&loading_bar),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Download minecraft (5-90)
|
||||
download::download_minecraft(&state, &version_info, &loading_bar).await?;
|
||||
|
||||
let client_path = state
|
||||
.directories
|
||||
@ -131,10 +135,11 @@ pub async fn install_minecraft(
|
||||
.await?;
|
||||
let total_length = processors.len();
|
||||
|
||||
// Forge processors (90-100)
|
||||
for (index, processor) in processors.iter().enumerate() {
|
||||
emit_loading(
|
||||
&loading_bar,
|
||||
index as f64 / total_length as f64,
|
||||
10.0 / total_length as f64,
|
||||
Some(&format!(
|
||||
"Running forge processor {}/{}",
|
||||
index, total_length
|
||||
@ -223,7 +228,7 @@ pub async fn launch_minecraft(
|
||||
install_minecraft(profile, None).await?;
|
||||
}
|
||||
|
||||
let state = st::State::get().await?;
|
||||
let state = State::get().await?;
|
||||
let metadata = state.metadata.read().await;
|
||||
let instance_path = &canonicalize(&profile.path)?;
|
||||
|
||||
@ -250,6 +255,7 @@ pub async fn launch_minecraft(
|
||||
version,
|
||||
profile.metadata.loader_version.as_ref(),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -320,8 +326,12 @@ pub async fn launch_minecraft(
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
|
||||
// Clear cargo-added env varaibles for debugging, and add settings env vars
|
||||
clear_cargo_env_vals(&mut command).envs(env_args);
|
||||
// CARGO-set DYLD_LIBRARY_PATH breaks Minecraft on macOS during testing on playground
|
||||
#[cfg(target_os = "macos")]
|
||||
if std::env::var("CARGO").is_ok() {
|
||||
command.env_remove("DYLD_FALLBACK_LIBRARY_PATH");
|
||||
}
|
||||
command.envs(env_args);
|
||||
|
||||
// Get Modrinth logs directories
|
||||
let datetime_string =
|
||||
@ -351,14 +361,3 @@ pub async fn launch_minecraft(
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn clear_cargo_env_vals(command: &mut Command) -> &mut Command {
|
||||
for (key, _) in std::env::vars() {
|
||||
command.env_remove(key);
|
||||
|
||||
// if key.starts_with("CARGO") {
|
||||
// command.env_remove(key);
|
||||
// }
|
||||
}
|
||||
command
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ use tokio::process::Child;
|
||||
use tokio::process::Command;
|
||||
use tokio::process::{ChildStderr, ChildStdout};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::error;
|
||||
|
||||
use crate::event::emit::emit_process;
|
||||
use crate::event::ProcessPayloadType;
|
||||
@ -55,7 +56,7 @@ impl Children {
|
||||
let stdout_clone = stdout.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = stdout_clone.read_stdout(child_stdout).await {
|
||||
eprintln!("Stdout process died with error: {}", e);
|
||||
error!("Stdout process died with error: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -64,7 +65,7 @@ impl Children {
|
||||
let stderr_clone = stderr.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = stderr_clone.read_stderr(child_stderr).await {
|
||||
eprintln!("Stderr process died with error: {}", e);
|
||||
error!("Stderr process died with error: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
//! Theseus metadata
|
||||
use crate::data::DirectoryInfo;
|
||||
use crate::util::fetch::{read_json, write};
|
||||
use crate::util::fetch::{read_json, write, IoSemaphore};
|
||||
use crate::State;
|
||||
use daedalus::{
|
||||
minecraft::{fetch_version_manifest, VersionManifest as MinecraftManifest},
|
||||
@ -9,7 +9,6 @@ use daedalus::{
|
||||
},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::{RwLock, Semaphore};
|
||||
|
||||
const METADATA_URL: &str = "https://meta.modrinth.com";
|
||||
|
||||
@ -51,7 +50,7 @@ impl Metadata {
|
||||
// Attempt to fetch metadata and store in sled DB
|
||||
pub async fn init(
|
||||
dirs: &DirectoryInfo,
|
||||
io_semaphore: &RwLock<Semaphore>,
|
||||
io_semaphore: &IoSemaphore,
|
||||
) -> crate::Result<Self> {
|
||||
let mut metadata = None;
|
||||
let metadata_path = dirs.caches_meta_dir().join("metadata.json");
|
||||
@ -79,7 +78,7 @@ impl Metadata {
|
||||
match res {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
log::warn!("Unable to fetch launcher metadata: {err}")
|
||||
tracing::warn!("Unable to fetch launcher metadata: {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -120,7 +119,7 @@ impl Metadata {
|
||||
match res {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
log::warn!("Unable to update launcher metadata: {err}")
|
||||
tracing::warn!("Unable to update launcher metadata: {err}")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ use crate::event::LoadingBarType;
|
||||
use crate::loading_join;
|
||||
|
||||
use crate::state::users::Users;
|
||||
use crate::util::fetch::{FetchSemaphore, IoSemaphore};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{OnceCell, RwLock, Semaphore};
|
||||
|
||||
@ -44,10 +45,16 @@ static LAUNCHER_STATE: OnceCell<Arc<State>> = OnceCell::const_new();
|
||||
pub struct State {
|
||||
/// Information on the location of files used in the launcher
|
||||
pub directories: DirectoryInfo,
|
||||
|
||||
/// Semaphore used to limit concurrent network requests and avoid errors
|
||||
pub fetch_semaphore: FetchSemaphore,
|
||||
/// Stored maximum number of sempahores of current fetch_semaphore
|
||||
pub fetch_semaphore_max: RwLock<u32>,
|
||||
/// Semaphore used to limit concurrent I/O and avoid errors
|
||||
pub io_semaphore: RwLock<Semaphore>,
|
||||
pub io_semaphore: IoSemaphore,
|
||||
/// Stored maximum number of sempahores of current io_semaphore
|
||||
pub io_semaphore_max: RwLock<u32>,
|
||||
|
||||
/// Launcher metadata
|
||||
pub metadata: RwLock<Metadata>,
|
||||
/// Launcher configuration
|
||||
@ -73,7 +80,7 @@ impl State {
|
||||
let loading_bar = init_loading(
|
||||
LoadingBarType::StateInit,
|
||||
100.0,
|
||||
"Initializing launcher...",
|
||||
"Initializing launcher",
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -83,20 +90,26 @@ impl State {
|
||||
// Settings
|
||||
let settings =
|
||||
Settings::init(&directories.settings_file()).await?;
|
||||
let io_semaphore = RwLock::new(Semaphore::new(
|
||||
settings.max_concurrent_downloads,
|
||||
let fetch_semaphore = FetchSemaphore(RwLock::new(
|
||||
Semaphore::new(settings.max_concurrent_downloads),
|
||||
));
|
||||
let io_semaphore = IoSemaphore(RwLock::new(
|
||||
Semaphore::new(settings.max_concurrent_writes),
|
||||
));
|
||||
emit_loading(&loading_bar, 10.0, None).await?;
|
||||
|
||||
let metadata_fut =
|
||||
Metadata::init(&directories, &io_semaphore);
|
||||
let profiles_fut =
|
||||
Profiles::init(&directories, &io_semaphore);
|
||||
let tags_fut = Tags::init(&directories, &io_semaphore);
|
||||
let profiles_fut = Profiles::init(&directories);
|
||||
let tags_fut = Tags::init(
|
||||
&directories,
|
||||
&io_semaphore,
|
||||
&fetch_semaphore,
|
||||
);
|
||||
let users_fut = Users::init(&directories, &io_semaphore);
|
||||
// Launcher data
|
||||
let (metadata, profiles, tags, users) = loading_join! {
|
||||
Some(&loading_bar), 70.0, Some("Initializing...");
|
||||
Some(&loading_bar), 70.0, Some("Loading metadata");
|
||||
metadata_fut,
|
||||
profiles_fut,
|
||||
tags_fut,
|
||||
@ -109,9 +122,13 @@ impl State {
|
||||
|
||||
Ok(Arc::new(Self {
|
||||
directories,
|
||||
fetch_semaphore,
|
||||
fetch_semaphore_max: RwLock::new(
|
||||
settings.max_concurrent_downloads as u32,
|
||||
),
|
||||
io_semaphore,
|
||||
io_semaphore_max: RwLock::new(
|
||||
settings.max_concurrent_downloads as u32,
|
||||
settings.max_concurrent_writes as u32,
|
||||
),
|
||||
metadata: RwLock::new(metadata),
|
||||
settings: RwLock::new(settings),
|
||||
@ -169,17 +186,34 @@ impl State {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Reset semaphores to default values
|
||||
/// Reset IO semaphore to default values
|
||||
/// This will block until all uses of the semaphore are complete, so it should only be called
|
||||
/// when we are not in the middle of downloading something (ie: changing the settings!)
|
||||
pub async fn reset_semaphore(&self) {
|
||||
pub async fn reset_io_semaphore(&self) {
|
||||
let settings = self.settings.read().await;
|
||||
let mut io_semaphore = self.io_semaphore.write().await;
|
||||
let mut io_semaphore = self.io_semaphore.0.write().await;
|
||||
let mut total_permits = self.io_semaphore_max.write().await;
|
||||
|
||||
// Wait to get all permits back
|
||||
let _ = io_semaphore.acquire_many(*total_permits).await;
|
||||
|
||||
// Reset the semaphore
|
||||
io_semaphore.close();
|
||||
*total_permits = settings.max_concurrent_writes as u32;
|
||||
*io_semaphore = Semaphore::new(settings.max_concurrent_writes);
|
||||
}
|
||||
|
||||
/// Reset IO semaphore to default values
|
||||
/// This will block until all uses of the semaphore are complete, so it should only be called
|
||||
/// when we are not in the middle of downloading something (ie: changing the settings!)
|
||||
pub async fn reset_fetch_semaphore(&self) {
|
||||
let settings = self.settings.read().await;
|
||||
let mut io_semaphore = self.fetch_semaphore.0.write().await;
|
||||
let mut total_permits = self.fetch_semaphore_max.write().await;
|
||||
|
||||
// Wait to get all permits back
|
||||
let _ = io_semaphore.acquire_many(*total_permits).await;
|
||||
|
||||
// Reset the semaphore
|
||||
io_semaphore.close();
|
||||
*total_permits = settings.max_concurrent_downloads as u32;
|
||||
|
||||
@ -5,7 +5,9 @@ use crate::event::emit::emit_profile;
|
||||
use crate::event::ProfilePayloadType;
|
||||
use crate::state::projects::Project;
|
||||
use crate::state::{ModrinthVersion, ProjectType};
|
||||
use crate::util::fetch::{fetch, fetch_json, write, write_cached_icon};
|
||||
use crate::util::fetch::{
|
||||
fetch, fetch_json, write, write_cached_icon, IoSemaphore,
|
||||
};
|
||||
use crate::State;
|
||||
use daedalus::modded::LoaderVersion;
|
||||
use dunce::canonicalize;
|
||||
@ -17,8 +19,7 @@ use std::{
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use tokio::sync::Semaphore;
|
||||
use tokio::{fs, sync::RwLock};
|
||||
use tokio::fs;
|
||||
use uuid::Uuid;
|
||||
|
||||
const PROFILE_JSON_PATH: &str = "profile.json";
|
||||
@ -149,7 +150,7 @@ impl Profile {
|
||||
pub async fn set_icon<'a>(
|
||||
&'a mut self,
|
||||
cache_dir: &Path,
|
||||
semaphore: &RwLock<Semaphore>,
|
||||
semaphore: &IoSemaphore,
|
||||
icon: bytes::Bytes,
|
||||
file_name: &str,
|
||||
) -> crate::Result<&'a mut Self> {
|
||||
@ -168,6 +169,7 @@ impl Profile {
|
||||
paths,
|
||||
state.directories.caches_dir(),
|
||||
&state.io_semaphore,
|
||||
&state.fetch_semaphore,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -218,7 +220,7 @@ impl Profile {
|
||||
&format!("{MODRINTH_API_URL}version/{version_id}"),
|
||||
None,
|
||||
None,
|
||||
&state.io_semaphore,
|
||||
&state.fetch_semaphore,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -237,7 +239,7 @@ impl Profile {
|
||||
let bytes = fetch(
|
||||
&file.url,
|
||||
file.hashes.get("sha1").map(|x| &**x),
|
||||
&state.io_semaphore,
|
||||
&state.fetch_semaphore,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -345,10 +347,7 @@ impl Profile {
|
||||
|
||||
impl Profiles {
|
||||
#[tracing::instrument]
|
||||
pub async fn init(
|
||||
dirs: &DirectoryInfo,
|
||||
io_sempahore: &RwLock<Semaphore>,
|
||||
) -> crate::Result<Self> {
|
||||
pub async fn init(dirs: &DirectoryInfo) -> crate::Result<Self> {
|
||||
let mut profiles = HashMap::new();
|
||||
fs::create_dir_all(dirs.profiles_dir()).await?;
|
||||
let mut entries = fs::read_dir(dirs.profiles_dir()).await?;
|
||||
@ -358,7 +357,9 @@ impl Profiles {
|
||||
let prof = match Self::read_profile_from_dir(&path).await {
|
||||
Ok(prof) => Some(prof),
|
||||
Err(err) => {
|
||||
log::warn!("Error loading profile: {err}. Skipping...");
|
||||
tracing::warn!(
|
||||
"Error loading profile: {err}. Skipping..."
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
@ -395,6 +396,7 @@ impl Profiles {
|
||||
files,
|
||||
state.directories.caches_dir(),
|
||||
&state.io_semaphore,
|
||||
&state.fetch_semaphore,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -417,7 +419,7 @@ impl Profiles {
|
||||
match res {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
log::warn!("Unable to fetch profile projects: {err}")
|
||||
tracing::warn!("Unable to fetch profile projects: {err}")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -2,7 +2,9 @@
|
||||
|
||||
use crate::config::MODRINTH_API_URL;
|
||||
use crate::state::Profile;
|
||||
use crate::util::fetch::{fetch_json, write_cached_icon};
|
||||
use crate::util::fetch::{
|
||||
fetch_json, write_cached_icon, FetchSemaphore, IoSemaphore,
|
||||
};
|
||||
use async_zip::tokio::read::fs::ZipFileReader;
|
||||
use chrono::{DateTime, Utc};
|
||||
use reqwest::Method;
|
||||
@ -12,7 +14,6 @@ use sha2::Digest;
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::sync::{RwLock, Semaphore};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
@ -203,7 +204,7 @@ async fn read_icon_from_file(
|
||||
icon_path: Option<String>,
|
||||
cache_dir: &Path,
|
||||
path: &PathBuf,
|
||||
io_semaphore: &RwLock<Semaphore>,
|
||||
io_semaphore: &IoSemaphore,
|
||||
) -> crate::Result<Option<PathBuf>> {
|
||||
if let Some(icon_path) = icon_path {
|
||||
// we have to repoen the zip twice here :(
|
||||
@ -252,7 +253,8 @@ pub async fn infer_data_from_files(
|
||||
profile: Profile,
|
||||
paths: Vec<PathBuf>,
|
||||
cache_dir: PathBuf,
|
||||
io_semaphore: &RwLock<Semaphore>,
|
||||
io_semaphore: &IoSemaphore,
|
||||
fetch_semaphore: &FetchSemaphore,
|
||||
) -> crate::Result<HashMap<PathBuf, Project>> {
|
||||
let mut file_path_hashes = HashMap::new();
|
||||
|
||||
@ -278,7 +280,7 @@ pub async fn infer_data_from_files(
|
||||
"hashes": file_path_hashes.keys().collect::<Vec<_>>(),
|
||||
"algorithm": "sha512",
|
||||
})),
|
||||
io_semaphore,
|
||||
fetch_semaphore,
|
||||
),
|
||||
fetch_json::<HashMap<String, ModrinthVersion>>(
|
||||
Method::POST,
|
||||
@ -290,7 +292,7 @@ pub async fn infer_data_from_files(
|
||||
"loaders": [profile.metadata.loader],
|
||||
"game_versions": [profile.metadata.game_version]
|
||||
})),
|
||||
io_semaphore,
|
||||
fetch_semaphore,
|
||||
)
|
||||
)?;
|
||||
|
||||
@ -308,7 +310,7 @@ pub async fn infer_data_from_files(
|
||||
),
|
||||
None,
|
||||
None,
|
||||
io_semaphore,
|
||||
fetch_semaphore,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -325,7 +327,7 @@ pub async fn infer_data_from_files(
|
||||
),
|
||||
None,
|
||||
None,
|
||||
io_semaphore,
|
||||
fetch_semaphore,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
|
||||
@ -23,6 +23,7 @@ pub struct Settings {
|
||||
pub default_user: Option<uuid::Uuid>,
|
||||
pub hooks: Hooks,
|
||||
pub max_concurrent_downloads: usize,
|
||||
pub max_concurrent_writes: usize,
|
||||
pub version: u32,
|
||||
pub collapsed_navigation: bool,
|
||||
}
|
||||
@ -39,6 +40,7 @@ impl Default for Settings {
|
||||
default_user: None,
|
||||
hooks: Hooks::default(),
|
||||
max_concurrent_downloads: 64,
|
||||
max_concurrent_writes: 100,
|
||||
version: CURRENT_FORMAT_VERSION,
|
||||
collapsed_navigation: false,
|
||||
}
|
||||
@ -84,7 +86,7 @@ impl Settings {
|
||||
match res {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
log::warn!("Unable to update launcher java: {err}")
|
||||
tracing::warn!("Unable to update launcher java: {err}")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -2,11 +2,12 @@ use std::path::PathBuf;
|
||||
|
||||
use reqwest::Method;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::{RwLock, Semaphore};
|
||||
|
||||
use crate::config::MODRINTH_API_URL;
|
||||
use crate::data::DirectoryInfo;
|
||||
use crate::util::fetch::{fetch_json, read_json, write};
|
||||
use crate::util::fetch::{
|
||||
fetch_json, read_json, write, FetchSemaphore, IoSemaphore,
|
||||
};
|
||||
|
||||
// Serializeable struct for all tags to be fetched together by the frontend
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@ -21,7 +22,8 @@ pub struct Tags {
|
||||
impl Tags {
|
||||
pub async fn init(
|
||||
dirs: &DirectoryInfo,
|
||||
io_semaphore: &RwLock<Semaphore>,
|
||||
io_semaphore: &IoSemaphore,
|
||||
fetch_sempahore: &FetchSemaphore,
|
||||
) -> crate::Result<Self> {
|
||||
let mut tags = None;
|
||||
let tags_path = dirs.caches_meta_dir().join("tags.json");
|
||||
@ -30,10 +32,10 @@ impl Tags {
|
||||
{
|
||||
tags = Some(tags_json);
|
||||
} else {
|
||||
match Self::fetch(io_semaphore).await {
|
||||
match Self::fetch(fetch_sempahore).await {
|
||||
Ok(tags_fetch) => tags = Some(tags_fetch),
|
||||
Err(err) => {
|
||||
log::warn!("Unable to fetch launcher tags: {err}")
|
||||
tracing::warn!("Unable to fetch launcher tags: {err}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -51,7 +53,7 @@ impl Tags {
|
||||
pub async fn update() {
|
||||
let res = async {
|
||||
let state = crate::State::get().await?;
|
||||
let tags_fetch = Tags::fetch(&state.io_semaphore).await?;
|
||||
let tags_fetch = Tags::fetch(&state.fetch_semaphore).await?;
|
||||
|
||||
let tags_path =
|
||||
state.directories.caches_meta_dir().join("tags.json");
|
||||
@ -74,7 +76,7 @@ impl Tags {
|
||||
match res {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
log::warn!("Unable to update launcher tags: {err}")
|
||||
tracing::warn!("Unable to update launcher tags: {err}")
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -116,7 +118,7 @@ impl Tags {
|
||||
}
|
||||
|
||||
// Fetches the tags from the Modrinth API and stores them in the database
|
||||
pub async fn fetch(semaphore: &RwLock<Semaphore>) -> crate::Result<Self> {
|
||||
pub async fn fetch(semaphore: &FetchSemaphore) -> crate::Result<Self> {
|
||||
let categories = format!("{MODRINTH_API_URL}tag/category");
|
||||
let loaders = format!("{MODRINTH_API_URL}tag/loader");
|
||||
let game_versions = format!("{MODRINTH_API_URL}tag/game_version");
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
//! User login info
|
||||
use crate::auth::Credentials;
|
||||
use crate::data::DirectoryInfo;
|
||||
use crate::util::fetch::{read_json, write};
|
||||
use crate::util::fetch::{read_json, write, IoSemaphore};
|
||||
use crate::State;
|
||||
use std::collections::HashMap;
|
||||
use tokio::sync::{RwLock, Semaphore};
|
||||
use uuid::Uuid;
|
||||
|
||||
const USERS_JSON: &str = "users.json";
|
||||
@ -16,7 +15,7 @@ pub(crate) struct Users(pub(crate) HashMap<Uuid, Credentials>);
|
||||
impl Users {
|
||||
pub async fn init(
|
||||
dirs: &DirectoryInfo,
|
||||
io_semaphore: &RwLock<Semaphore>,
|
||||
io_semaphore: &IoSemaphore,
|
||||
) -> crate::Result<Self> {
|
||||
let users_path = dirs.caches_meta_dir().join(USERS_JSON);
|
||||
let users = read_json(&users_path, io_semaphore).await.ok();
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
//! Functions for fetching infromation from the Internet
|
||||
use crate::event::emit::emit_loading;
|
||||
use crate::event::LoadingBarId;
|
||||
use bytes::Bytes;
|
||||
use lazy_static::lazy_static;
|
||||
use reqwest::Method;
|
||||
@ -12,6 +14,11 @@ use tokio::{
|
||||
io::AsyncWriteExt,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IoSemaphore(pub RwLock<Semaphore>);
|
||||
#[derive(Debug)]
|
||||
pub struct FetchSemaphore(pub RwLock<Semaphore>);
|
||||
|
||||
lazy_static! {
|
||||
static ref REQWEST_CLIENT: reqwest::Client = {
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
@ -34,9 +41,9 @@ const FETCH_ATTEMPTS: usize = 3;
|
||||
pub async fn fetch(
|
||||
url: &str,
|
||||
sha1: Option<&str>,
|
||||
semaphore: &RwLock<Semaphore>,
|
||||
semaphore: &FetchSemaphore,
|
||||
) -> crate::Result<Bytes> {
|
||||
fetch_advanced(Method::GET, url, sha1, None, None, semaphore).await
|
||||
fetch_advanced(Method::GET, url, sha1, None, None, None, semaphore).await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(json_body, semaphore))]
|
||||
@ -45,13 +52,14 @@ pub async fn fetch_json<T>(
|
||||
url: &str,
|
||||
sha1: Option<&str>,
|
||||
json_body: Option<serde_json::Value>,
|
||||
semaphore: &RwLock<Semaphore>,
|
||||
semaphore: &FetchSemaphore,
|
||||
) -> crate::Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let result =
|
||||
fetch_advanced(method, url, sha1, json_body, None, semaphore).await?;
|
||||
fetch_advanced(method, url, sha1, json_body, None, None, semaphore)
|
||||
.await?;
|
||||
let value = serde_json::from_slice(&result)?;
|
||||
Ok(value)
|
||||
}
|
||||
@ -64,9 +72,10 @@ pub async fn fetch_advanced(
|
||||
sha1: Option<&str>,
|
||||
json_body: Option<serde_json::Value>,
|
||||
header: Option<(&str, &str)>,
|
||||
semaphore: &RwLock<Semaphore>,
|
||||
loading_bar: Option<(&LoadingBarId, f64)>,
|
||||
semaphore: &FetchSemaphore,
|
||||
) -> crate::Result<Bytes> {
|
||||
let io_semaphore = semaphore.read().await;
|
||||
let io_semaphore = semaphore.0.read().await;
|
||||
let _permit = io_semaphore.acquire().await?;
|
||||
|
||||
for attempt in 1..=(FETCH_ATTEMPTS + 1) {
|
||||
@ -83,7 +92,35 @@ pub async fn fetch_advanced(
|
||||
let result = req.send().await;
|
||||
match result {
|
||||
Ok(x) => {
|
||||
let bytes = x.bytes().await;
|
||||
let bytes = if let Some((bar, total)) = &loading_bar {
|
||||
let length = x.content_length();
|
||||
if let Some(total_size) = length {
|
||||
use futures::StreamExt;
|
||||
let mut stream = x.bytes_stream();
|
||||
let mut bytes = Vec::new();
|
||||
while let Some(item) = stream.next().await {
|
||||
let chunk = item.or(Err(
|
||||
crate::error::ErrorKind::NoValueFor(
|
||||
"fetch bytes".to_string(),
|
||||
),
|
||||
))?;
|
||||
bytes.append(&mut chunk.to_vec());
|
||||
emit_loading(
|
||||
bar,
|
||||
(chunk.len() as f64 / total_size as f64)
|
||||
* total,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(bytes::Bytes::from(bytes))
|
||||
} else {
|
||||
x.bytes().await
|
||||
}
|
||||
} else {
|
||||
x.bytes().await
|
||||
};
|
||||
|
||||
if let Ok(bytes) = bytes {
|
||||
if let Some(sha1) = sha1 {
|
||||
@ -101,7 +138,7 @@ pub async fn fetch_advanced(
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("Done downloading URL {url}");
|
||||
tracing::trace!("Done downloading URL {url}");
|
||||
return Ok(bytes);
|
||||
} else if attempt <= 3 {
|
||||
continue;
|
||||
@ -124,7 +161,7 @@ pub async fn fetch_advanced(
|
||||
pub async fn fetch_mirrors(
|
||||
mirrors: &[&str],
|
||||
sha1: Option<&str>,
|
||||
semaphore: &RwLock<Semaphore>,
|
||||
semaphore: &FetchSemaphore,
|
||||
) -> crate::Result<Bytes> {
|
||||
if mirrors.is_empty() {
|
||||
return Err(crate::ErrorKind::InputError(
|
||||
@ -146,12 +183,12 @@ pub async fn fetch_mirrors(
|
||||
|
||||
pub async fn read_json<T>(
|
||||
path: &Path,
|
||||
semaphore: &RwLock<Semaphore>,
|
||||
semaphore: &IoSemaphore,
|
||||
) -> crate::Result<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let io_semaphore = semaphore.read().await;
|
||||
let io_semaphore = semaphore.0.read().await;
|
||||
let _permit = io_semaphore.acquire().await?;
|
||||
|
||||
let json = fs::read(path).await?;
|
||||
@ -164,9 +201,9 @@ where
|
||||
pub async fn write<'a>(
|
||||
path: &Path,
|
||||
bytes: &[u8],
|
||||
semaphore: &RwLock<Semaphore>,
|
||||
semaphore: &IoSemaphore,
|
||||
) -> crate::Result<()> {
|
||||
let io_semaphore = semaphore.read().await;
|
||||
let io_semaphore = semaphore.0.read().await;
|
||||
let _permit = io_semaphore.acquire().await?;
|
||||
|
||||
if let Some(parent) = path.parent() {
|
||||
@ -175,7 +212,7 @@ pub async fn write<'a>(
|
||||
|
||||
let mut file = File::create(path).await?;
|
||||
file.write_all(bytes).await?;
|
||||
log::debug!("Done writing file {}", path.display());
|
||||
tracing::trace!("Done writing file {}", path.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -184,7 +221,7 @@ pub async fn write_cached_icon(
|
||||
icon_path: &str,
|
||||
cache_dir: &Path,
|
||||
bytes: Bytes,
|
||||
semaphore: &RwLock<Semaphore>,
|
||||
semaphore: &IoSemaphore,
|
||||
) -> crate::Result<PathBuf> {
|
||||
let extension = Path::new(&icon_path).extension().and_then(OsStr::to_str);
|
||||
let hash = sha1_async(bytes.clone()).await?;
|
||||
|
||||
@ -7,7 +7,7 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
theseus = { path = "../theseus" }
|
||||
theseus = { path = "../theseus", features = ["cli"] }
|
||||
daedalus = {version = "0.1.15", features = ["bincode"]}
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-stream = { version = "0.1", features = ["fs"] }
|
||||
|
||||
@ -15,7 +15,7 @@ tauri-build = { version = "1.2", features = [] }
|
||||
regex = "1.5"
|
||||
|
||||
[dependencies]
|
||||
theseus = { path = "../../theseus", features = ["tauri"] }
|
||||
theseus = { path = "../../theseus", features = ["tauri", "cli"] }
|
||||
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
@ -30,6 +30,10 @@ url = "2.2"
|
||||
uuid = { version = "1.1", features = ["serde", "v4"] }
|
||||
os_info = "3.7.0"
|
||||
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = "0.2"
|
||||
tracing-error = "0.1"
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
||||
|
||||
@ -47,6 +47,8 @@ where
|
||||
}
|
||||
|
||||
// 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>> {
|
||||
@ -64,6 +66,16 @@ macro_rules! impl_serialize {
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
// For the Theseus variant, we add a special display for the error,
|
||||
// to view the spans if subscribed to them (which is information that is lost when serializing)
|
||||
TheseusSerializableError::Theseus(theseus_error) => {
|
||||
$crate::error::display_tracing_error(theseus_error);
|
||||
|
||||
let mut state = serializer.serialize_struct("Theseus", 2)?;
|
||||
state.serialize_field("field_name", "Theseus")?;
|
||||
state.serialize_field("message", &theseus_error.to_string())?;
|
||||
state.end()
|
||||
}
|
||||
$(
|
||||
TheseusSerializableError::$variant(message) => {
|
||||
let mut state = serializer.serialize_struct(stringify!($variant), 2)?;
|
||||
@ -80,7 +92,6 @@ macro_rules! impl_serialize {
|
||||
|
||||
// Use the macro to implement Serialize for TheseusSerializableError
|
||||
impl_serialize! {
|
||||
Theseus,
|
||||
IO,
|
||||
NoProfileFound,
|
||||
}
|
||||
|
||||
@ -5,8 +5,12 @@ use theseus::prelude::*;
|
||||
// Creates a pack from a version ID (returns a path to the created profile)
|
||||
// invoke('pack_install_version_id', version_id)
|
||||
#[tauri::command]
|
||||
pub async fn pack_install_version_id(version_id: String) -> Result<PathBuf> {
|
||||
let res = pack::install_pack_from_version_id(version_id).await?;
|
||||
pub async fn pack_install_version_id(
|
||||
version_id: String,
|
||||
pack_title: Option<String>,
|
||||
) -> Result<PathBuf> {
|
||||
let res =
|
||||
pack::install_pack_from_version_id(version_id, pack_title).await?;
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,7 @@ pub struct FrontendSettings {
|
||||
pub default_user: Option<uuid::Uuid>,
|
||||
pub hooks: Hooks,
|
||||
pub max_concurrent_downloads: usize,
|
||||
pub max_concurrent_writes: usize,
|
||||
pub version: u32,
|
||||
pub collapsed_navigation: bool,
|
||||
}
|
||||
@ -39,6 +40,7 @@ pub async fn settings_get() -> Result<FrontendSettings> {
|
||||
default_user: backend_settings.default_user,
|
||||
hooks: backend_settings.hooks,
|
||||
max_concurrent_downloads: backend_settings.max_concurrent_downloads,
|
||||
max_concurrent_writes: backend_settings.max_concurrent_writes,
|
||||
version: backend_settings.version,
|
||||
collapsed_navigation: backend_settings.collapsed_navigation,
|
||||
};
|
||||
@ -77,6 +79,7 @@ pub async fn settings_set(settings: FrontendSettings) -> Result<()> {
|
||||
default_user: settings.default_user,
|
||||
hooks: settings.hooks,
|
||||
max_concurrent_downloads: settings.max_concurrent_downloads,
|
||||
max_concurrent_writes: settings.max_concurrent_writes,
|
||||
version: settings.version,
|
||||
collapsed_navigation: settings.collapsed_navigation,
|
||||
};
|
||||
|
||||
18
theseus_gui/src-tauri/src/error.rs
Normal file
18
theseus_gui/src-tauri/src/error.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use tracing_error::ExtractSpanTrace;
|
||||
|
||||
pub fn display_tracing_error(err: &theseus::Error) {
|
||||
match get_span_trace(err) {
|
||||
Some(span_trace) => {
|
||||
tracing::error!(error = %err, span_trace = %span_trace);
|
||||
}
|
||||
None => {
|
||||
tracing::error!(error = %err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_span_trace<'a>(
|
||||
error: &'a (dyn std::error::Error + 'static),
|
||||
) -> Option<&'a tracing_error::SpanTrace> {
|
||||
error.source().and_then(|e| e.span_trace())
|
||||
}
|
||||
@ -5,7 +5,11 @@
|
||||
|
||||
use theseus::prelude::*;
|
||||
|
||||
use tracing_error::ErrorLayer;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
mod api;
|
||||
mod error;
|
||||
|
||||
// Should be called in launcher initialization
|
||||
#[tauri::command]
|
||||
@ -37,7 +41,34 @@ async fn should_disable_mouseover() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
fn main() {
|
||||
/*
|
||||
tracing is set basd on the environment variable RUST_LOG=xxx, depending on the amount of logs to show
|
||||
ERROR > WARN > INFO > DEBUG > TRACE
|
||||
eg. RUST_LOG=info will show info, warn, and error logs
|
||||
RUST_LOG="theseus=trace" will show *all* messages but from theseus only (and not dependencies using similar crates)
|
||||
RUST_LOG="theseus=trace" will show *all* messages but from theseus only (and not dependencies using similar crates)
|
||||
|
||||
Error messages returned to Tauri will display as traced error logs if they return an error.
|
||||
This will also include an attached span trace if the error is from a tracing error, and the level is set to info, debug, or trace
|
||||
|
||||
on unix:
|
||||
RUST_LOG="theseus=trace" {run command}
|
||||
|
||||
*/
|
||||
let filter = EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| EnvFilter::new("theseus=info"));
|
||||
|
||||
let subscriber = tracing_subscriber::registry()
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.with(filter)
|
||||
.with(ErrorLayer::default());
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber)
|
||||
.expect("setting default subscriber failed");
|
||||
|
||||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
initialize_state,
|
||||
|
||||
@ -25,15 +25,16 @@ import { listen } from '@tauri-apps/api/event'
|
||||
/// Payload for the 'loading' event
|
||||
/*
|
||||
LoadingPayload {
|
||||
event: "StateInit", "PackDownload", etc
|
||||
- Certain states have additional fields:
|
||||
- PackDownload: {
|
||||
event: {
|
||||
type: string, one of "StateInit", "PackDownload", etc
|
||||
(Optional fields depending on event type)
|
||||
pack_name: name of the pack
|
||||
pack_id, optional, the id of the modpack
|
||||
pack_version, optional, the version of the modpack
|
||||
- MinecraftDownload: {
|
||||
profile_name: name of the profile
|
||||
profile_uuid: unique identification of the profile
|
||||
|
||||
}
|
||||
loader_uuid: unique identification of the loading bar
|
||||
fraction: number, (as a fraction of 1, how much we'vel oaded so far). If null, by convention, loading is finished
|
||||
message: message to display to the user
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
|
||||
// Installs pack from a version ID
|
||||
export async function install(versionId) {
|
||||
return await invoke('pack_install_version_id', { versionId })
|
||||
export async function install(versionId, packTitle) {
|
||||
return await invoke('pack_install_version_id', { versionId, packTitle })
|
||||
}
|
||||
|
||||
// Installs pack from a path
|
||||
|
||||
@ -6,7 +6,7 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
theseus = { path = "../theseus" }
|
||||
theseus = { path = "../theseus", features = ["cli"] }
|
||||
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
@ -21,3 +21,7 @@ tokio-stream = { version = "0.1", features = ["fs"] }
|
||||
futures = "0.3"
|
||||
daedalus = {version = "0.1.15", features = ["bincode"] }
|
||||
uuid = { version = "1.1", features = ["serde", "v4"] }
|
||||
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = "0.2"
|
||||
tracing-error = "0.1"
|
||||
|
||||
@ -8,6 +8,9 @@ use theseus::jre::autodetect_java_globals;
|
||||
use theseus::prelude::*;
|
||||
use theseus::profile_create::profile_create;
|
||||
use tokio::time::{sleep, Duration};
|
||||
use tracing_error::ErrorLayer;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
// A simple Rust implementation of the authentication run
|
||||
// 1) call the authenticate_begin_flow() function to get the URL to open (like you would in the frontend)
|
||||
@ -31,16 +34,27 @@ pub async fn authenticate_run() -> theseus::Result<Credentials> {
|
||||
async fn main() -> theseus::Result<()> {
|
||||
println!("Starting.");
|
||||
|
||||
let filter = EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| EnvFilter::new("theseus=info"));
|
||||
|
||||
let subscriber = tracing_subscriber::registry()
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.with(filter)
|
||||
.with(ErrorLayer::default());
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber)
|
||||
.expect("setting default subscriber failed");
|
||||
|
||||
// Initialize state
|
||||
let st = State::get().await?;
|
||||
//State::update();
|
||||
|
||||
st.settings.write().await.java_globals = autodetect_java_globals().await?;
|
||||
st.settings.write().await.max_concurrent_downloads = 5;
|
||||
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());
|
||||
// Changed the settings, so need to reset the semaphore
|
||||
st.reset_semaphore().await;
|
||||
st.reset_fetch_semaphore().await;
|
||||
|
||||
// Clear profiles
|
||||
println!("Clearing profiles.");
|
||||
@ -53,21 +67,21 @@ async fn main() -> theseus::Result<()> {
|
||||
|
||||
println!("Creating/adding profile.");
|
||||
|
||||
let name = "Example".to_string();
|
||||
let game_version = "1.19.2".to_string();
|
||||
let modloader = ModLoader::Vanilla;
|
||||
let loader_version = "stable".to_string();
|
||||
|
||||
let profile_path = profile_create(
|
||||
name.clone(),
|
||||
game_version,
|
||||
modloader,
|
||||
Some(loader_version),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
// let name = "Example".to_string();
|
||||
// let game_version = "1.19.2".to_string();
|
||||
// let modloader = ModLoader::Vanilla;
|
||||
// let loader_version = "stable".to_string();
|
||||
//
|
||||
// let profile_path = profile_create(
|
||||
// name.clone(),
|
||||
// game_version,
|
||||
// modloader,
|
||||
// Some(loader_version),
|
||||
// None,
|
||||
// None,
|
||||
// None,
|
||||
// )
|
||||
// .await?;
|
||||
|
||||
// let mut value = list().await?;
|
||||
// let profile_path = value.iter().next().map(|x| x.0).unwrap();
|
||||
@ -89,10 +103,12 @@ async fn main() -> theseus::Result<()> {
|
||||
// profile::toggle_disable_project(&profile_path, &sodium_path).await?;
|
||||
//
|
||||
// profile::remove_project(&profile_path, &mod_menu_path).await?;
|
||||
// let profile_path =
|
||||
// pack::install_pack_from_version_id("zroFQG1k".to_string())
|
||||
// .await
|
||||
// .unwrap();
|
||||
let profile_path = pack::install_pack_from_version_id(
|
||||
"zroFQG1k".to_string(),
|
||||
Some("Technical Electrical".to_string()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// async closure for testing any desired edits
|
||||
// (ie: changing the java runtime of an added profile)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user