You've already forked AstralRinth
forked from didirus/AstralRinth
Navbar wireup (#98)
* Navbar wireup * Fix height issue * Fix syncing * working branch * Added root directories to breadcrumbs * fix jre detect --------- Co-authored-by: Jai A <jaiagr+gpg@pm.me>
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -689,9 +689,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "daedalus"
|
name = "daedalus"
|
||||||
version = "0.1.18"
|
version = "0.1.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "83c9c34a2d4904bcaa4cfa5f62b38c915c106fdc92a6a66276ae2bd5ba1b2527"
|
checksum = "ab1ff8f873475996ff3d755659e5e0fbe5a2d02d6fc84ff2b625874a8c446973"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ zip = "0.5"
|
|||||||
async_zip = { version = "0.0.13", features = ["full"] }
|
async_zip = { version = "0.0.13", features = ["full"] }
|
||||||
|
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
daedalus = { version = "0.1.18" }
|
daedalus = { version = "0.1.20" }
|
||||||
dirs = "4.0"
|
dirs = "4.0"
|
||||||
|
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ pub async fn get_optimal_jre_key(profile: &Profile) -> crate::Result<String> {
|
|||||||
.map(|it| it.major_version)
|
.map(|it| it.major_version)
|
||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
{
|
{
|
||||||
0..=16 => JAVA_8_KEY.to_string(),
|
0..=15 => JAVA_8_KEY.to_string(),
|
||||||
17 => JAVA_17_KEY.to_string(),
|
16..=17 => JAVA_17_KEY.to_string(),
|
||||||
_ => JAVA_18PLUS_KEY.to_string(),
|
_ => JAVA_18PLUS_KEY.to_string(),
|
||||||
};
|
};
|
||||||
Ok(optimal_key)
|
Ok(optimal_key)
|
||||||
|
|||||||
@@ -136,18 +136,26 @@ pub async fn install_pack_from_version_id(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
install_pack(file, icon, Some(version.project_id), Some(version.id)).await
|
install_pack(
|
||||||
|
file,
|
||||||
|
icon,
|
||||||
|
Some(project.title),
|
||||||
|
Some(version.project_id),
|
||||||
|
Some(version.id),
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn install_pack_from_file(path: PathBuf) -> crate::Result<PathBuf> {
|
pub async fn install_pack_from_file(path: PathBuf) -> crate::Result<PathBuf> {
|
||||||
let file = fs::read(path).await?;
|
let file = fs::read(path).await?;
|
||||||
|
|
||||||
install_pack(bytes::Bytes::from(file), None, None, None).await
|
install_pack(bytes::Bytes::from(file), None, None, None, None).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn install_pack(
|
async fn install_pack(
|
||||||
file: bytes::Bytes,
|
file: bytes::Bytes,
|
||||||
icon: Option<PathBuf>,
|
icon: Option<PathBuf>,
|
||||||
|
override_title: Option<String>,
|
||||||
project_id: Option<String>,
|
project_id: Option<String>,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
) -> crate::Result<PathBuf> {
|
) -> crate::Result<PathBuf> {
|
||||||
@@ -192,7 +200,7 @@ async fn install_pack(
|
|||||||
let mut game_version = None;
|
let mut game_version = None;
|
||||||
let mut mod_loader = None;
|
let mut mod_loader = None;
|
||||||
let mut loader_version = None;
|
let mut loader_version = None;
|
||||||
for (key, value) in pack.dependencies {
|
for (key, value) in &pack.dependencies {
|
||||||
match key {
|
match key {
|
||||||
PackDependency::Forge => {
|
PackDependency::Forge => {
|
||||||
mod_loader = Some(ModLoader::Forge);
|
mod_loader = Some(ModLoader::Forge);
|
||||||
@@ -219,13 +227,11 @@ async fn install_pack(
|
|||||||
.into());
|
.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
let pack_name = pack.name.clone();
|
let profile_raw = crate::api::profile_create::profile_create(
|
||||||
|
override_title.unwrap_or_else(|| pack.name.clone()),
|
||||||
let profile = crate::api::profile_create::profile_create(
|
|
||||||
pack.name,
|
|
||||||
game_version.clone(),
|
game_version.clone(),
|
||||||
mod_loader.unwrap_or(ModLoader::Vanilla),
|
mod_loader.unwrap_or(ModLoader::Vanilla),
|
||||||
loader_version,
|
loader_version.cloned(),
|
||||||
icon,
|
icon,
|
||||||
Some(LinkedData {
|
Some(LinkedData {
|
||||||
project_id: project_id.clone(),
|
project_id: project_id.clone(),
|
||||||
@@ -234,141 +240,167 @@ async fn install_pack(
|
|||||||
Some(true),
|
Some(true),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
let profile = profile_raw.clone();
|
||||||
let loading_bar = init_loading(
|
let result = async {
|
||||||
LoadingBarType::PackDownload {
|
let loading_bar = init_loading(
|
||||||
pack_name,
|
LoadingBarType::PackDownload {
|
||||||
pack_id: project_id,
|
pack_name: pack.name.clone(),
|
||||||
pack_version: version_id,
|
pack_id: project_id,
|
||||||
},
|
pack_version: version_id,
|
||||||
100.0,
|
},
|
||||||
"Downloading modpack...",
|
100.0,
|
||||||
)
|
"Downloading modpack...",
|
||||||
.await?;
|
)
|
||||||
|
|
||||||
let num_files = pack.files.len();
|
|
||||||
use futures::StreamExt;
|
|
||||||
loading_try_for_each_concurrent(
|
|
||||||
futures::stream::iter(pack.files.into_iter())
|
|
||||||
.map(Ok::<PackFile, crate::Error>),
|
|
||||||
None,
|
|
||||||
Some(&loading_bar),
|
|
||||||
80.0,
|
|
||||||
num_files,
|
|
||||||
None,
|
|
||||||
|project| {
|
|
||||||
let profile = profile.clone();
|
|
||||||
async move {
|
|
||||||
//TODO: Future update: prompt user for optional files in a modpack
|
|
||||||
if let Some(env) = project.env {
|
|
||||||
if env
|
|
||||||
.get(&EnvType::Client)
|
|
||||||
.map(|x| x == &SideType::Unsupported)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = fetch_mirrors(
|
|
||||||
&project
|
|
||||||
.downloads
|
|
||||||
.iter()
|
|
||||||
.map(|x| &**x)
|
|
||||||
.collect::<Vec<&str>>(),
|
|
||||||
project.hashes.get(&PackFileHash::Sha1).map(|x| &**x),
|
|
||||||
&state.io_semaphore,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let path =
|
|
||||||
std::path::Path::new(&project.path).components().next();
|
|
||||||
if let Some(path) = path {
|
|
||||||
match path {
|
|
||||||
Component::CurDir | Component::Normal(_) => {
|
|
||||||
let path = profile.join(project.path);
|
|
||||||
write(&path, &file, &state.io_semaphore)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let extract_overrides = |overrides: String| async {
|
|
||||||
let reader = Cursor::new(&file);
|
|
||||||
|
|
||||||
let mut overrides_zip =
|
|
||||||
ZipFileReader::new(reader).await.map_err(|_| {
|
|
||||||
crate::Error::from(crate::ErrorKind::InputError(
|
|
||||||
"Failed extract overrides Zip".to_string(),
|
|
||||||
))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let profile = profile.clone();
|
|
||||||
async move {
|
|
||||||
for index in 0..overrides_zip.file().entries().len() {
|
|
||||||
let file = overrides_zip
|
|
||||||
.file()
|
|
||||||
.entries()
|
|
||||||
.get(index)
|
|
||||||
.unwrap()
|
|
||||||
.entry()
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
let file_path = PathBuf::from(file.filename());
|
|
||||||
if file.filename().starts_with(&overrides)
|
|
||||||
&& !file.filename().ends_with('/')
|
|
||||||
{
|
|
||||||
// Reads the file into the 'content' variable
|
|
||||||
let mut content = Vec::new();
|
|
||||||
let mut reader = overrides_zip.entry(index).await?;
|
|
||||||
reader.read_to_end_checked(&mut content, &file).await?;
|
|
||||||
|
|
||||||
let mut new_path = PathBuf::new();
|
|
||||||
let components = file_path.components().skip(1);
|
|
||||||
|
|
||||||
for component in components {
|
|
||||||
new_path.push(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
if new_path.file_name().is_some() {
|
|
||||||
write(
|
|
||||||
&profile.join(new_path),
|
|
||||||
&content,
|
|
||||||
&state.io_semaphore,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok::<(), crate::Error>(())
|
|
||||||
}
|
|
||||||
.await
|
|
||||||
};
|
|
||||||
|
|
||||||
emit_loading(&loading_bar, 0.05, 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"))
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
super::profile::sync(&profile).await?;
|
let num_files = pack.files.len();
|
||||||
|
use futures::StreamExt;
|
||||||
|
loading_try_for_each_concurrent(
|
||||||
|
futures::stream::iter(pack.files.into_iter())
|
||||||
|
.map(Ok::<PackFile, crate::Error>),
|
||||||
|
None,
|
||||||
|
Some(&loading_bar),
|
||||||
|
80.0,
|
||||||
|
num_files,
|
||||||
|
None,
|
||||||
|
|project| {
|
||||||
|
let profile = profile.clone();
|
||||||
|
async move {
|
||||||
|
//TODO: Future update: prompt user for optional files in a modpack
|
||||||
|
if let Some(env) = project.env {
|
||||||
|
if env
|
||||||
|
.get(&EnvType::Client)
|
||||||
|
.map(|x| x == &SideType::Unsupported)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(profile) = crate::api::profile::get(&profile).await? {
|
let file = fetch_mirrors(
|
||||||
crate::launcher::install_minecraft(&profile, Some(loading_bar))
|
&project
|
||||||
|
.downloads
|
||||||
|
.iter()
|
||||||
|
.map(|x| &**x)
|
||||||
|
.collect::<Vec<&str>>(),
|
||||||
|
project
|
||||||
|
.hashes
|
||||||
|
.get(&PackFileHash::Sha1)
|
||||||
|
.map(|x| &**x),
|
||||||
|
&state.io_semaphore,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let path = std::path::Path::new(&project.path)
|
||||||
|
.components()
|
||||||
|
.next();
|
||||||
|
if let Some(path) = path {
|
||||||
|
match path {
|
||||||
|
Component::CurDir | Component::Normal(_) => {
|
||||||
|
let path = profile.join(project.path);
|
||||||
|
write(&path, &file, &state.io_semaphore)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let extract_overrides = |overrides: String| async {
|
||||||
|
let reader = Cursor::new(&file);
|
||||||
|
|
||||||
|
let mut overrides_zip =
|
||||||
|
ZipFileReader::new(reader).await.map_err(|_| {
|
||||||
|
crate::Error::from(crate::ErrorKind::InputError(
|
||||||
|
"Failed extract overrides Zip".to_string(),
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let profile = profile.clone();
|
||||||
|
async move {
|
||||||
|
for index in 0..overrides_zip.file().entries().len() {
|
||||||
|
let file = overrides_zip
|
||||||
|
.file()
|
||||||
|
.entries()
|
||||||
|
.get(index)
|
||||||
|
.unwrap()
|
||||||
|
.entry()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let file_path = PathBuf::from(file.filename());
|
||||||
|
if file.filename().starts_with(&overrides)
|
||||||
|
&& !file.filename().ends_with('/')
|
||||||
|
{
|
||||||
|
// Reads the file into the 'content' variable
|
||||||
|
let mut content = Vec::new();
|
||||||
|
let mut reader = overrides_zip.entry(index).await?;
|
||||||
|
reader
|
||||||
|
.read_to_end_checked(&mut content, &file)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut new_path = PathBuf::new();
|
||||||
|
let components = file_path.components().skip(1);
|
||||||
|
|
||||||
|
for component in components {
|
||||||
|
new_path.push(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_path.file_name().is_some() {
|
||||||
|
write(
|
||||||
|
&profile.join(new_path),
|
||||||
|
&content,
|
||||||
|
&state.io_semaphore,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok::<(), crate::Error>(())
|
||||||
|
}
|
||||||
|
.await
|
||||||
|
};
|
||||||
|
|
||||||
|
emit_loading(&loading_bar, 0.05, Some("Extracting overrides"))
|
||||||
.await?;
|
.await?;
|
||||||
} else {
|
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, 0.1, Some("Done extacting overrides"))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
|
||||||
|
|
||||||
Ok(profile)
|
if let Some(profile) = crate::api::profile::get(&profile).await? {
|
||||||
|
tokio::try_join!(
|
||||||
|
super::profile::sync(&profile.path),
|
||||||
|
crate::launcher::install_minecraft(
|
||||||
|
&profile,
|
||||||
|
Some(loading_bar)
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
emit_loading(
|
||||||
|
&loading_bar,
|
||||||
|
0.1,
|
||||||
|
Some("Done extacting overrides"),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok::<PathBuf, crate::Error>(profile)
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(profile) => Ok(profile),
|
||||||
|
Err(err) => {
|
||||||
|
let _ = crate::api::profile::remove(&profile_raw).await;
|
||||||
|
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(crate::Error::from(crate::ErrorKind::InputError(
|
Err(crate::Error::from(crate::ErrorKind::InputError(
|
||||||
"No pack manifest found in mrpack".to_string(),
|
"No pack manifest found in mrpack".to_string(),
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ pub fn get_jvm_arguments(
|
|||||||
parsed_arguments.push(arg);
|
parsed_arguments.push(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
parsed_arguments.push("-Dorg.lwjgl.util.Debug=true".to_string());
|
||||||
|
|
||||||
Ok(parsed_arguments)
|
Ok(parsed_arguments)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ pub async fn download_libraries(
|
|||||||
library
|
library
|
||||||
.natives
|
.natives
|
||||||
.as_ref()?
|
.as_ref()?
|
||||||
.get(&Os::native())?,
|
.get(&Os::native_arch())?,
|
||||||
library
|
library
|
||||||
.downloads
|
.downloads
|
||||||
.as_ref()?
|
.as_ref()?
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use crate::event::LoadingBarType;
|
|||||||
use crate::{
|
use crate::{
|
||||||
process,
|
process,
|
||||||
state::{self as st, MinecraftChild},
|
state::{self as st, MinecraftChild},
|
||||||
|
State,
|
||||||
};
|
};
|
||||||
use daedalus as d;
|
use daedalus as d;
|
||||||
use dunce::canonicalize;
|
use dunce::canonicalize;
|
||||||
@@ -55,7 +56,7 @@ pub async fn install_minecraft(
|
|||||||
profile: &Profile,
|
profile: &Profile,
|
||||||
existing_loading_bar: Option<Uuid>,
|
existing_loading_bar: Option<Uuid>,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
let state = st::State::get().await?;
|
let state = State::get().await?;
|
||||||
let instance_path = &canonicalize(&profile.path)?;
|
let instance_path = &canonicalize(&profile.path)?;
|
||||||
let metadata = state.metadata.read().await;
|
let metadata = state.metadata.read().await;
|
||||||
|
|
||||||
@@ -98,7 +99,6 @@ pub async fn install_minecraft(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
download::download_minecraft(&state, &version_info, loading_bar).await?;
|
download::download_minecraft(&state, &version_info, loading_bar).await?;
|
||||||
st::State::sync().await?;
|
|
||||||
|
|
||||||
let client_path = state
|
let client_path = state
|
||||||
.directories
|
.directories
|
||||||
@@ -201,7 +201,7 @@ pub async fn install_minecraft(
|
|||||||
async { Ok(()) }
|
async { Ok(()) }
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
crate::api::profile::sync(&profile.path).await?;
|
State::sync().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -327,7 +327,7 @@ pub async fn launch_minecraft(
|
|||||||
let mut state_children = state.children.write().await;
|
let mut state_children = state.children.write().await;
|
||||||
state_children
|
state_children
|
||||||
.insert_process(
|
.insert_process(
|
||||||
uuid::Uuid::new_v4(),
|
Uuid::new_v4(),
|
||||||
instance_path.to_path_buf(),
|
instance_path.to_path_buf(),
|
||||||
command,
|
command,
|
||||||
post_exit_hook,
|
post_exit_hook,
|
||||||
@@ -337,9 +337,11 @@ pub async fn launch_minecraft(
|
|||||||
|
|
||||||
fn clear_cargo_env_vals(command: &mut Command) -> &mut Command {
|
fn clear_cargo_env_vals(command: &mut Command) -> &mut Command {
|
||||||
for (key, _) in std::env::vars() {
|
for (key, _) in std::env::vars() {
|
||||||
if key.starts_with("CARGO") {
|
command.env_remove(key);
|
||||||
command.env_remove(key);
|
|
||||||
}
|
// if key.starts_with("CARGO") {
|
||||||
|
// command.env_remove(key);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
command
|
command
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
//! Theseus metadata
|
//! Theseus metadata
|
||||||
use crate::data::DirectoryInfo;
|
use crate::data::DirectoryInfo;
|
||||||
use crate::util::fetch::{read_json, write};
|
use crate::util::fetch::{read_json, write};
|
||||||
|
use crate::State;
|
||||||
use daedalus::{
|
use daedalus::{
|
||||||
minecraft::{fetch_version_manifest, VersionManifest as MinecraftManifest},
|
minecraft::{fetch_version_manifest, VersionManifest as MinecraftManifest},
|
||||||
modded::{
|
modded::{
|
||||||
@@ -96,7 +97,7 @@ impl Metadata {
|
|||||||
pub async fn update() {
|
pub async fn update() {
|
||||||
let res = async {
|
let res = async {
|
||||||
let metadata_fetch = Metadata::fetch().await?;
|
let metadata_fetch = Metadata::fetch().await?;
|
||||||
let state = crate::State::get().await?;
|
let state = State::get().await?;
|
||||||
|
|
||||||
let metadata_path =
|
let metadata_path =
|
||||||
state.directories.caches_meta_dir().join("metadata.json");
|
state.directories.caches_meta_dir().join("metadata.json");
|
||||||
|
|||||||
@@ -164,7 +164,8 @@ impl Profile {
|
|||||||
|
|
||||||
let paths = self.get_profile_project_paths()?;
|
let paths = self.get_profile_project_paths()?;
|
||||||
let projects = crate::state::infer_data_from_files(
|
let projects = crate::state::infer_data_from_files(
|
||||||
&[(self.clone(), paths)],
|
self.clone(),
|
||||||
|
paths,
|
||||||
state.directories.caches_dir(),
|
state.directories.caches_dir(),
|
||||||
&state.io_semaphore,
|
&state.io_semaphore,
|
||||||
)
|
)
|
||||||
@@ -386,31 +387,28 @@ impl Profiles {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !files.is_empty() {
|
future::try_join_all(files.into_iter().map(
|
||||||
let inferred = super::projects::infer_data_from_files(
|
|(profile, files)| async {
|
||||||
&files,
|
let profile_path = profile.path.clone();
|
||||||
state.directories.caches_dir(),
|
let inferred = super::projects::infer_data_from_files(
|
||||||
&state.io_semaphore,
|
profile,
|
||||||
)
|
files,
|
||||||
.await?;
|
state.directories.caches_dir(),
|
||||||
let mut wipe_profiles = Vec::new();
|
&state.io_semaphore,
|
||||||
for (key, value) in inferred {
|
)
|
||||||
if let Some((profile, _)) =
|
.await?;
|
||||||
files.iter().find(|(_, files)| files.contains(&key))
|
|
||||||
|
let mut new_profiles = state.profiles.write().await;
|
||||||
|
if let Some(profile) = new_profiles.0.get_mut(&profile_path)
|
||||||
{
|
{
|
||||||
let mut new_profiles = state.profiles.write().await;
|
profile.projects = inferred;
|
||||||
if let Some(profile) =
|
|
||||||
new_profiles.0.get_mut(&profile.path)
|
|
||||||
{
|
|
||||||
if !wipe_profiles.contains(&profile.path) {
|
|
||||||
profile.projects = HashMap::new();
|
|
||||||
wipe_profiles.push(profile.path.clone());
|
|
||||||
}
|
|
||||||
profile.projects.insert(key, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
drop(new_profiles);
|
||||||
}
|
|
||||||
|
Ok::<(), crate::Error>(())
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok::<(), crate::Error>(())
|
Ok::<(), crate::Error>(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
//! Project management + inference
|
//! Project management + inference
|
||||||
|
|
||||||
use crate::config::MODRINTH_API_URL;
|
use crate::config::MODRINTH_API_URL;
|
||||||
use crate::data::ModLoader;
|
|
||||||
use crate::state::Profile;
|
use crate::state::Profile;
|
||||||
use crate::util::fetch::{fetch_json, write_cached_icon};
|
use crate::util::fetch::{fetch_json, write_cached_icon};
|
||||||
use async_zip::tokio::read::fs::ZipFileReader;
|
use async_zip::tokio::read::fs::ZipFileReader;
|
||||||
@@ -250,36 +249,51 @@ async fn read_icon_from_file(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn infer_data_from_files(
|
pub async fn infer_data_from_files(
|
||||||
paths: &[(Profile, Vec<PathBuf>)],
|
profile: Profile,
|
||||||
|
paths: Vec<PathBuf>,
|
||||||
cache_dir: PathBuf,
|
cache_dir: PathBuf,
|
||||||
io_semaphore: &RwLock<Semaphore>,
|
io_semaphore: &RwLock<Semaphore>,
|
||||||
) -> crate::Result<HashMap<PathBuf, Project>> {
|
) -> crate::Result<HashMap<PathBuf, Project>> {
|
||||||
let mut file_path_hashes = HashMap::new();
|
let mut file_path_hashes = HashMap::new();
|
||||||
|
|
||||||
// TODO: Make this concurrent and use progressive hashing to avoid loading each JAR in memory
|
// TODO: Make this concurrent and use progressive hashing to avoid loading each JAR in memory
|
||||||
for set in paths {
|
for path in paths {
|
||||||
for path in &set.1 {
|
let mut file = tokio::fs::File::open(path.clone()).await?;
|
||||||
let mut file = tokio::fs::File::open(path.clone()).await?;
|
|
||||||
|
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
file.read_to_end(&mut buffer).await?;
|
file.read_to_end(&mut buffer).await?;
|
||||||
|
|
||||||
let hash = format!("{:x}", sha2::Sha512::digest(&buffer));
|
let hash = format!("{:x}", sha2::Sha512::digest(&buffer));
|
||||||
file_path_hashes.insert(hash, path.clone());
|
file_path_hashes.insert(hash, path.clone());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let files: HashMap<String, ModrinthVersion> = fetch_json(
|
let files_url = format!("{}version_files", MODRINTH_API_URL);
|
||||||
Method::POST,
|
let updates_url = format!("{}version_files/update", MODRINTH_API_URL);
|
||||||
&format!("{}version_files", MODRINTH_API_URL),
|
let (files, update_versions) = tokio::try_join!(
|
||||||
None,
|
fetch_json::<HashMap<String, ModrinthVersion>>(
|
||||||
Some(json!({
|
Method::POST,
|
||||||
"hashes": file_path_hashes.keys().collect::<Vec<_>>(),
|
&files_url,
|
||||||
"algorithm": "sha512",
|
None,
|
||||||
})),
|
Some(json!({
|
||||||
io_semaphore,
|
"hashes": file_path_hashes.keys().collect::<Vec<_>>(),
|
||||||
)
|
"algorithm": "sha512",
|
||||||
.await?;
|
})),
|
||||||
|
io_semaphore,
|
||||||
|
),
|
||||||
|
fetch_json::<HashMap<String, ModrinthVersion>>(
|
||||||
|
Method::POST,
|
||||||
|
&updates_url,
|
||||||
|
None,
|
||||||
|
Some(json!({
|
||||||
|
"hashes": file_path_hashes.keys().collect::<Vec<_>>(),
|
||||||
|
"algorithm": "sha512",
|
||||||
|
"loaders": [profile.metadata.loader],
|
||||||
|
"game_versions": [profile.metadata.game_version]
|
||||||
|
})),
|
||||||
|
io_semaphore,
|
||||||
|
)
|
||||||
|
)?;
|
||||||
|
|
||||||
let projects: Vec<ModrinthProject> = fetch_json(
|
let projects: Vec<ModrinthProject> = fetch_json(
|
||||||
Method::GET,
|
Method::GET,
|
||||||
&format!(
|
&format!(
|
||||||
@@ -297,6 +311,7 @@ pub async fn infer_data_from_files(
|
|||||||
io_semaphore,
|
io_semaphore,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let teams: Vec<ModrinthTeamMember> = fetch_json::<
|
let teams: Vec<ModrinthTeamMember> = fetch_json::<
|
||||||
Vec<Vec<ModrinthTeamMember>>,
|
Vec<Vec<ModrinthTeamMember>>,
|
||||||
>(
|
>(
|
||||||
@@ -317,26 +332,6 @@ pub async fn infer_data_from_files(
|
|||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut update_versions: Vec<ModrinthVersion> = fetch_json(
|
|
||||||
Method::GET,
|
|
||||||
&format!(
|
|
||||||
"{}versions?ids={}",
|
|
||||||
MODRINTH_API_URL,
|
|
||||||
serde_json::to_string(
|
|
||||||
&projects
|
|
||||||
.iter()
|
|
||||||
.flat_map(|x| x.versions.clone())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
)?
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
io_semaphore,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
update_versions.sort_by(|a, b| b.date_published.cmp(&a.date_published));
|
|
||||||
|
|
||||||
let mut return_projects = HashMap::new();
|
let mut return_projects = HashMap::new();
|
||||||
let mut further_analyze_projects: Vec<(String, PathBuf)> = Vec::new();
|
let mut further_analyze_projects: Vec<(String, PathBuf)> = Vec::new();
|
||||||
|
|
||||||
@@ -345,8 +340,6 @@ pub async fn infer_data_from_files(
|
|||||||
if let Some(project) =
|
if let Some(project) =
|
||||||
projects.iter().find(|x| version.project_id == x.id)
|
projects.iter().find(|x| version.project_id == x.id)
|
||||||
{
|
{
|
||||||
let profile = paths.iter().find(|x| x.1.contains(&path));
|
|
||||||
|
|
||||||
let file_name = path
|
let file_name = path
|
||||||
.file_name()
|
.file_name()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
@@ -356,7 +349,6 @@ pub async fn infer_data_from_files(
|
|||||||
return_projects.insert(
|
return_projects.insert(
|
||||||
path,
|
path,
|
||||||
Project {
|
Project {
|
||||||
sha512: hash,
|
|
||||||
disabled: false,
|
disabled: false,
|
||||||
metadata: ProjectMetadata::Modrinth {
|
metadata: ProjectMetadata::Modrinth {
|
||||||
project: Box::new(project.clone()),
|
project: Box::new(project.clone()),
|
||||||
@@ -366,48 +358,25 @@ pub async fn infer_data_from_files(
|
|||||||
.filter(|x| x.team_id == project.team)
|
.filter(|x| x.team_id == project.team)
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
update_version: if let Some((profile, _)) = &profile
|
update_version: if let Some(val) =
|
||||||
|
update_versions.get(&hash)
|
||||||
{
|
{
|
||||||
update_versions
|
Some(Box::new(val.clone()))
|
||||||
.iter()
|
|
||||||
.find(|x| {
|
|
||||||
x.project_id == project.id
|
|
||||||
&& x.game_versions.contains(
|
|
||||||
&profile.metadata.game_version,
|
|
||||||
)
|
|
||||||
&& if profile.metadata.loader
|
|
||||||
== ModLoader::Vanilla
|
|
||||||
{
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
x.loaders.contains(
|
|
||||||
&profile
|
|
||||||
.metadata
|
|
||||||
.loader
|
|
||||||
.as_api_str()
|
|
||||||
.to_string(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.cloned()
|
|
||||||
.map(Box::new)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
incompatible: if let Some((profile, _)) = &profile {
|
|
||||||
!version.loaders.contains(
|
incompatible: !version.loaders.contains(
|
||||||
&profile
|
&profile
|
||||||
.metadata
|
.metadata
|
||||||
.loader
|
.loader
|
||||||
.as_api_str()
|
.as_api_str()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
) || version
|
) || version
|
||||||
.game_versions
|
.game_versions
|
||||||
.contains(&profile.metadata.game_version)
|
.contains(&profile.metadata.game_version),
|
||||||
} else {
|
|
||||||
false
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
sha512: hash,
|
||||||
file_name,
|
file_name,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,10 +6,63 @@ use regex::Regex;
|
|||||||
pub trait OsExt {
|
pub trait OsExt {
|
||||||
/// Get the OS of the current system
|
/// Get the OS of the current system
|
||||||
fn native() -> Self;
|
fn native() -> Self;
|
||||||
|
|
||||||
|
/// Gets the OS + Arch of the current system
|
||||||
|
fn native_arch() -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OsExt for Os {
|
impl OsExt for Os {
|
||||||
|
fn native_arch() -> Self {
|
||||||
|
if std::env::consts::OS == "windows" {
|
||||||
|
if std::env::consts::ARCH == "aarch64" {
|
||||||
|
Os::WindowsArm64
|
||||||
|
} else {
|
||||||
|
Os::Windows
|
||||||
|
}
|
||||||
|
} else if std::env::consts::OS == "linux" {
|
||||||
|
if std::env::consts::ARCH == "aarch64" {
|
||||||
|
Os::LinuxArm64
|
||||||
|
} else if std::env::consts::ARCH == "arm" {
|
||||||
|
Os::LinuxArm32
|
||||||
|
} else {
|
||||||
|
Os::Linux
|
||||||
|
}
|
||||||
|
} else if std::env::consts::OS == "macos" {
|
||||||
|
if std::env::consts::ARCH == "aarch64" {
|
||||||
|
Os::OsxArm64
|
||||||
|
} else {
|
||||||
|
Os::Osx
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Os::Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn native() -> Self {
|
fn native() -> Self {
|
||||||
|
if std::env::consts::OS == "windows" {
|
||||||
|
if std::env::consts::ARCH == "aarch64" {
|
||||||
|
Os::WindowsArm64
|
||||||
|
} else {
|
||||||
|
Os::Windows
|
||||||
|
}
|
||||||
|
} else if std::env::consts::OS == "linux" {
|
||||||
|
if std::env::consts::ARCH == "aarch64" {
|
||||||
|
Os::LinuxArm64
|
||||||
|
} else if std::env::consts::ARCH == "arm" {
|
||||||
|
Os::LinuxArm32
|
||||||
|
} else {
|
||||||
|
Os::Linux
|
||||||
|
}
|
||||||
|
} else if std::env::consts::OS == "macos" {
|
||||||
|
if std::env::consts::ARCH == "aarch64" {
|
||||||
|
Os::OsxArm64
|
||||||
|
} else {
|
||||||
|
Os::Osx
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Os::Unknown
|
||||||
|
};
|
||||||
|
|
||||||
match std::env::consts::OS {
|
match std::env::consts::OS {
|
||||||
"windows" => Self::Windows,
|
"windows" => Self::Windows,
|
||||||
"macos" => Self::Osx,
|
"macos" => Self::Osx,
|
||||||
@@ -35,7 +88,7 @@ pub fn os_rule(rule: &OsRule) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(name) = &rule.name {
|
if let Some(name) = &rule.name {
|
||||||
rule_match &= &Os::native() == name;
|
rule_match &= &Os::native() == name || &Os::native_arch() == name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(version) = &rule.version {
|
if let Some(version) = &rule.version {
|
||||||
@@ -49,8 +102,13 @@ pub fn os_rule(rule: &OsRule) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn classpath_separator() -> &'static str {
|
pub fn classpath_separator() -> &'static str {
|
||||||
match Os::native() {
|
match Os::native_arch() {
|
||||||
Os::Osx | Os::Linux | Os::Unknown => ":",
|
Os::Osx
|
||||||
Os::Windows => ";",
|
| Os::OsxArm64
|
||||||
|
| Os::Linux
|
||||||
|
| Os::LinuxArm32
|
||||||
|
| Os::LinuxArm64
|
||||||
|
| Os::Unknown => ":",
|
||||||
|
Os::Windows | Os::WindowsArm64 => ";",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,14 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
import { RouterView, RouterLink } from 'vue-router'
|
import { RouterView, RouterLink } from 'vue-router'
|
||||||
import {
|
import { HomeIcon, SearchIcon, LibraryIcon, PlusIcon, SettingsIcon } from 'omorphia'
|
||||||
ChevronLeftIcon,
|
|
||||||
ChevronRightIcon,
|
|
||||||
HomeIcon,
|
|
||||||
SearchIcon,
|
|
||||||
LibraryIcon,
|
|
||||||
PlusIcon,
|
|
||||||
SettingsIcon,
|
|
||||||
} from 'omorphia'
|
|
||||||
import { useTheming } from '@/store/state'
|
import { useTheming } from '@/store/state'
|
||||||
import AccountsCard from '@/components/ui/AccountsCard.vue'
|
import AccountsCard from '@/components/ui/AccountsCard.vue'
|
||||||
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
import InstanceCreationModal from '@/components/ui/InstanceCreationModal.vue'
|
||||||
import { list } from '@/helpers/profile'
|
import { list } from '@/helpers/profile'
|
||||||
import { get } from '@/helpers/settings'
|
import { get } from '@/helpers/settings'
|
||||||
|
import Breadcrumbs from '@/components/ui/Breadcrumbs.vue'
|
||||||
|
import RunningAppBar from '@/components/ui/RunningAppBar.vue'
|
||||||
|
|
||||||
const themeStore = useTheming()
|
const themeStore = useTheming()
|
||||||
|
|
||||||
@@ -65,12 +59,12 @@ list().then(
|
|||||||
<div class="view">
|
<div class="view">
|
||||||
<div class="appbar">
|
<div class="appbar">
|
||||||
<section class="navigation-controls">
|
<section class="navigation-controls">
|
||||||
<ChevronLeftIcon @click="$router.back()" />
|
<Breadcrumbs />
|
||||||
<ChevronRightIcon @click="$router.forward()" />
|
|
||||||
<p>{{ $route.name }}</p>
|
|
||||||
</section>
|
</section>
|
||||||
<section class="mod-stats">
|
<section class="mod-stats">
|
||||||
<p>{{ installedMods }} mods installed</p>
|
<Suspense>
|
||||||
|
<RunningAppBar />
|
||||||
|
</Suspense>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="router-view">
|
<div class="router-view">
|
||||||
@@ -90,7 +84,7 @@ list().then(
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.view {
|
.view {
|
||||||
width: 100%;
|
width: calc(100% - 5rem);
|
||||||
|
|
||||||
.appbar {
|
.appbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -98,7 +92,8 @@ list().then(
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
background: var(--color-super-raised-bg);
|
background: var(--color-super-raised-bg);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 0.5rem 1rem;
|
padding: 0 0 0 1rem;
|
||||||
|
height: 3.25rem;
|
||||||
z-index: 11;
|
z-index: 11;
|
||||||
|
|
||||||
.navigation-controls {
|
.navigation-controls {
|
||||||
@@ -132,6 +127,7 @@ list().then(
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mod-stats {
|
.mod-stats {
|
||||||
|
height: 100%;
|
||||||
display: inherit;
|
display: inherit;
|
||||||
align-items: inherit;
|
align-items: inherit;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
@@ -140,7 +136,7 @@ list().then(
|
|||||||
|
|
||||||
.router-view {
|
.router-view {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 2rem);
|
height: calc(100% - 3.125rem);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,3 +2,5 @@ export { default as PlayIcon } from './play.svg'
|
|||||||
export { default as OpenFolderIcon } from './folder-open.svg'
|
export { default as OpenFolderIcon } from './folder-open.svg'
|
||||||
export { default as BrowseIcon } from './folder-search.svg'
|
export { default as BrowseIcon } from './folder-search.svg'
|
||||||
export { default as LoginIcon } from './log-in.svg'
|
export { default as LoginIcon } from './log-in.svg'
|
||||||
|
export { default as StopIcon } from './stop-circle.svg'
|
||||||
|
export { default as TerminalIcon } from './terminal-square.svg'
|
||||||
|
|||||||
1
theseus_gui/src/assets/icons/stop-circle.svg
Normal file
1
theseus_gui/src/assets/icons/stop-circle.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-stop-circle"><circle cx="12" cy="12" r="10"></circle><rect width="6" height="6" x="9" y="9"></rect></svg>
|
||||||
|
After Width: | Height: | Size: 307 B |
1
theseus_gui/src/assets/icons/terminal-square.svg
Normal file
1
theseus_gui/src/assets/icons/terminal-square.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-terminal-square"><path d="m7 11 2-2-2-2"></path><path d="M11 13h4"></path><rect width="18" height="18" x="3" y="3" rx="2" ry="2"></rect></svg>
|
||||||
|
After Width: | Height: | Size: 344 B |
64
theseus_gui/src/components/ui/Breadcrumbs.vue
Normal file
64
theseus_gui/src/components/ui/Breadcrumbs.vue
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
<div v-for="breadcrumb in breadcrumbs" :key="breadcrumb.name" class="breadcrumbs__item">
|
||||||
|
<router-link
|
||||||
|
v-if="breadcrumb.link"
|
||||||
|
:to="breadcrumb.link.replace('{id}', encodeURIComponent($route.params.id))"
|
||||||
|
>{{
|
||||||
|
breadcrumb.name.charAt(0) === '?'
|
||||||
|
? breadcrumbData.getName(breadcrumb.name.slice(1))
|
||||||
|
: breadcrumb.name
|
||||||
|
}}
|
||||||
|
</router-link>
|
||||||
|
<span v-else class="selected">{{
|
||||||
|
breadcrumb.name.charAt(0) === '?'
|
||||||
|
? breadcrumbData.getName(breadcrumb.name.slice(1))
|
||||||
|
: breadcrumb.name
|
||||||
|
}}</span>
|
||||||
|
<ChevronRightIcon v-if="breadcrumb.link" class="chevron" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ChevronRightIcon } from 'omorphia'
|
||||||
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const breadcrumbData = useBreadcrumbs()
|
||||||
|
const breadcrumbs = computed(() => {
|
||||||
|
const additionalContext =
|
||||||
|
route.meta.useContext === true
|
||||||
|
? breadcrumbData.context
|
||||||
|
: route.meta.useRootContext === true
|
||||||
|
? breadcrumbData.rootContext
|
||||||
|
: null
|
||||||
|
return additionalContext ? [additionalContext, ...route.meta.breadcrumb] : route.meta.breadcrumb
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.breadcrumbs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.breadcrumbs__item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
vertical-align: center;
|
||||||
|
margin: auto 0;
|
||||||
|
|
||||||
|
.chevron,
|
||||||
|
a {
|
||||||
|
margin: auto 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
color: var(--color-contrast);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
103
theseus_gui/src/components/ui/RunningAppBar.vue
Normal file
103
theseus_gui/src/components/ui/RunningAppBar.vue
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="currentProcesses[0]" class="status">
|
||||||
|
<span class="circle running" />
|
||||||
|
<span class="running-text">
|
||||||
|
{{ currentProcesses[0].metadata.name }}
|
||||||
|
</span>
|
||||||
|
<Button icon-only class="icon-button stop" @click="stop()">
|
||||||
|
<StopIcon />
|
||||||
|
</Button>
|
||||||
|
<Button icon-only class="icon-button" @click="goToTerminal()">
|
||||||
|
<TerminalIcon />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div v-else class="status">
|
||||||
|
<span class="circle stopped" />
|
||||||
|
<span class="running-text"> No running profiles </span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { Button } from 'omorphia'
|
||||||
|
import { StopIcon, TerminalIcon } from '@/assets/icons'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import {
|
||||||
|
get_all_running_profiles as getRunningProfiles,
|
||||||
|
kill_by_uuid as killProfile,
|
||||||
|
get_uuids_by_profile_path as getProfileProcesses,
|
||||||
|
} from '@/helpers/process'
|
||||||
|
import { process_listener } from '@/helpers/events'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const currentProcesses = ref(await getRunningProfiles())
|
||||||
|
|
||||||
|
await process_listener(async () => {
|
||||||
|
await refresh()
|
||||||
|
})
|
||||||
|
|
||||||
|
const refresh = async () => {
|
||||||
|
currentProcesses.value = await getRunningProfiles()
|
||||||
|
}
|
||||||
|
|
||||||
|
const stop = async () => {
|
||||||
|
try {
|
||||||
|
const processes = await getProfileProcesses(currentProcesses.value[0].path)
|
||||||
|
await killProfile(processes[0])
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
await refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
const goToTerminal = () => {
|
||||||
|
router.push(`/instance/${encodeURIComponent(currentProcesses.value[0].path)}/logs`)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.status {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
background-color: var(--color-raised-bg);
|
||||||
|
padding: 0 1rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.running-text {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
|
||||||
|
&.running {
|
||||||
|
background-color: var(--color-brand);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.stopped {
|
||||||
|
background-color: var(--color-base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
box-shadow: none;
|
||||||
|
width: 1.25rem !important;
|
||||||
|
height: 1.25rem !important;
|
||||||
|
|
||||||
|
&.stop {
|
||||||
|
--text-color: var(--color-red) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -31,8 +31,8 @@ export async function get_all_running_uuids() {
|
|||||||
|
|
||||||
/// Gets all running process IDs with a given profile path
|
/// Gets all running process IDs with a given profile path
|
||||||
/// Returns [u32]
|
/// Returns [u32]
|
||||||
export async function get_uuids_by_profile_path(profile_path) {
|
export async function get_uuids_by_profile_path(profilePath) {
|
||||||
return await invoke('process_get_uuids_by_profile_path', { profile_path })
|
return await invoke('process_get_uuids_by_profile_path', { profilePath })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets all running process IDs with a given profile path
|
/// Gets all running process IDs with a given profile path
|
||||||
@@ -43,8 +43,8 @@ export async function get_all_running_profile_paths(profile_path) {
|
|||||||
|
|
||||||
/// Gets all running process IDs with a given profile path
|
/// Gets all running process IDs with a given profile path
|
||||||
/// Returns [u32]
|
/// Returns [u32]
|
||||||
export async function get_all_running_profiles(profile_path) {
|
export async function get_all_running_profiles() {
|
||||||
return await invoke('process_get_all_running_profiles', { profile_path })
|
return await invoke('process_get_all_running_profiles')
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets process stderr by UUID
|
/// Gets process stderr by UUID
|
||||||
|
|||||||
@@ -17,9 +17,14 @@ import {
|
|||||||
} from 'omorphia'
|
} from 'omorphia'
|
||||||
import Multiselect from 'vue-multiselect'
|
import Multiselect from 'vue-multiselect'
|
||||||
import { useSearch } from '@/store/state'
|
import { useSearch } from '@/store/state'
|
||||||
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
import { get_categories, get_loaders, get_game_versions } from '@/helpers/tags'
|
import { get_categories, get_loaders, get_game_versions } from '@/helpers/tags'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
const searchStore = useSearch()
|
const searchStore = useSearch()
|
||||||
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
const route = useRoute()
|
||||||
|
breadcrumbs.setContext({ name: 'Browse', link: route.path })
|
||||||
|
|
||||||
const showSnapshots = ref(false)
|
const showSnapshots = ref(false)
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
|
|||||||
@@ -2,9 +2,16 @@
|
|||||||
import RowDisplay from '@/components/RowDisplay.vue'
|
import RowDisplay from '@/components/RowDisplay.vue'
|
||||||
import { shallowRef } from 'vue'
|
import { shallowRef } from 'vue'
|
||||||
import { list } from '@/helpers/profile.js'
|
import { list } from '@/helpers/profile.js'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
|
||||||
const profiles = await list()
|
const profiles = await list()
|
||||||
const recentInstances = shallowRef(Object.values(profiles))
|
const recentInstances = shallowRef(Object.values(profiles))
|
||||||
|
|
||||||
|
breadcrumbs.setRootContext({ name: 'Home', link: route.path })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -2,9 +2,16 @@
|
|||||||
import GridDisplay from '@/components/GridDisplay.vue'
|
import GridDisplay from '@/components/GridDisplay.vue'
|
||||||
import { shallowRef } from 'vue'
|
import { shallowRef } from 'vue'
|
||||||
import { list } from '@/helpers/profile.js'
|
import { list } from '@/helpers/profile.js'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
|
||||||
const profiles = await list()
|
const profiles = await list()
|
||||||
const instances = shallowRef(Object.values(profiles))
|
const instances = shallowRef(Object.values(profiles))
|
||||||
|
|
||||||
|
breadcrumbs.setRootContext({ name: 'Library', link: route.path })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
<Avatar size="lg" :src="convertFileSrc(instance.metadata.icon)" />
|
<Avatar size="lg" :src="convertFileSrc(instance.metadata.icon)" />
|
||||||
<div class="instance-info">
|
<div class="instance-info">
|
||||||
<h2 class="name">{{ instance.metadata.name }}</h2>
|
<h2 class="name">{{ instance.metadata.name }}</h2>
|
||||||
<span class="metadata"
|
<span class="metadata">
|
||||||
>{{ instance.metadata.loader }} {{ instance.metadata.game_version }}</span
|
{{ instance.metadata.loader }} {{ instance.metadata.game_version }}
|
||||||
>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="button-group">
|
<span class="button-group">
|
||||||
<Button color="primary" class="instance-button" @click="run($route.params.id)">
|
<Button color="primary" class="instance-button" @click="run($route.params.id)">
|
||||||
@@ -47,9 +47,17 @@ import { get, run } from '@/helpers/profile'
|
|||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { shallowRef } from 'vue'
|
import { shallowRef } from 'vue'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
import { convertFileSrc } from '@tauri-apps/api/tauri'
|
||||||
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
|
|
||||||
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const instance = shallowRef(await get(route.params.id))
|
const instance = shallowRef(await get(route.params.id))
|
||||||
|
breadcrumbs.setName('Instance', instance.value.metadata.name)
|
||||||
|
breadcrumbs.setContext({
|
||||||
|
name: instance.value.metadata.name,
|
||||||
|
link: route.path,
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@@ -77,7 +85,7 @@ Button {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background: var(--color-raised-bg);
|
background: var(--color-raised-bg);
|
||||||
min-height: calc(100% - 2rem);
|
min-height: calc(100% - 3.25rem);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-for="mod in search" :key="mod.file_name" class="table-row">
|
<div v-for="mod in search" :key="mod.file_name" class="table-row">
|
||||||
<div class="table-cell table-text">
|
<div class="table-cell table-text">
|
||||||
<Button v-if="true" icon-only>
|
<Button v-if="mod.outdated" icon-only>
|
||||||
<UpdatedIcon />
|
<UpdatedIcon />
|
||||||
</Button>
|
</Button>
|
||||||
<Button v-else disabled icon-only>
|
<Button v-else disabled icon-only>
|
||||||
@@ -106,6 +106,7 @@ for (const project of Object.values(props.instance.projects)) {
|
|||||||
file_name: project.file_name,
|
file_name: project.file_name,
|
||||||
icon: project.metadata.project.icon_url,
|
icon: project.metadata.project.icon_url,
|
||||||
disabled: project.disabled,
|
disabled: project.disabled,
|
||||||
|
outdated: project.metadata.update_version,
|
||||||
})
|
})
|
||||||
} else if (project.metadata.type === 'inferred') {
|
} else if (project.metadata.type === 'inferred') {
|
||||||
projects.value.push({
|
projects.value.push({
|
||||||
@@ -115,6 +116,7 @@ for (const project of Object.values(props.instance.projects)) {
|
|||||||
file_name: project.file_name,
|
file_name: project.file_name,
|
||||||
icon: project.metadata.icon ? convertFileSrc(project.metadata.icon) : null,
|
icon: project.metadata.icon ? convertFileSrc(project.metadata.icon) : null,
|
||||||
disabled: project.disabled,
|
disabled: project.disabled,
|
||||||
|
outdated: false,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
projects.value.push({
|
projects.value.push({
|
||||||
@@ -124,6 +126,7 @@ for (const project of Object.values(props.instance.projects)) {
|
|||||||
file_name: project.file_name,
|
file_name: project.file_name,
|
||||||
icon: null,
|
icon: null,
|
||||||
disabled: project.disabled,
|
disabled: project.disabled,
|
||||||
|
outdated: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,9 +218,11 @@ import { ofetch } from 'ofetch'
|
|||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { ref, shallowRef, watch } from 'vue'
|
import { ref, shallowRef, watch } from 'vue'
|
||||||
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
|
import InstallConfirmModal from '@/components/ui/InstallConfirmModal.vue'
|
||||||
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
|
||||||
const confirmModal = ref(null)
|
const confirmModal = ref(null)
|
||||||
const loaders = ref(await get_loaders())
|
const loaders = ref(await get_loaders())
|
||||||
@@ -232,6 +234,8 @@ const [data, versions, members, dependencies] = await Promise.all([
|
|||||||
ofetch(`https://api.modrinth.com/v2/project/${route.params.id}/dependencies`).then(shallowRef),
|
ofetch(`https://api.modrinth.com/v2/project/${route.params.id}/dependencies`).then(shallowRef),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
breadcrumbs.setName('Project', data.value.title)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => route.params.id,
|
() => route.params.id,
|
||||||
() => {
|
() => {
|
||||||
@@ -246,7 +250,9 @@ async function install(version) {
|
|||||||
const packs = Object.values(await list())
|
const packs = Object.values(await list())
|
||||||
if (
|
if (
|
||||||
packs.length === 0 ||
|
packs.length === 0 ||
|
||||||
!packs.map((value) => value.metadata).find((pack) => pack.linked_project_id === data.value.id)
|
!packs
|
||||||
|
.map((value) => value.metadata)
|
||||||
|
.find((pack) => pack.linked_data?.project_id === data.value.id)
|
||||||
) {
|
) {
|
||||||
let id = await pack_install(version)
|
let id = await pack_install(version)
|
||||||
await router.push({ path: `/instance/${encodeURIComponent(id)}` })
|
await router.push({ path: `/instance/${encodeURIComponent(id)}` })
|
||||||
|
|||||||
@@ -183,6 +183,9 @@ import {
|
|||||||
import { releaseColor } from '@/helpers/utils'
|
import { releaseColor } from '@/helpers/utils'
|
||||||
import { ref, defineProps } from 'vue'
|
import { ref, defineProps } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
import { useBreadcrumbs } from '@/store/breadcrumbs'
|
||||||
|
|
||||||
|
const breadcrumbs = useBreadcrumbs()
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@@ -210,7 +213,10 @@ const props = defineProps({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const version = ref(props.versions.find((version) => version.id === route.params.version))
|
const version = ref(props.versions.find((version) => version.id === route.params.version))
|
||||||
|
breadcrumbs.setName('Version', version.value.name)
|
||||||
|
|
||||||
const author = ref(props.members.find((member) => member.user.id === version.value.author_id))
|
const author = ref(props.members.find((member) => member.user.id === version.value.author_id))
|
||||||
|
|
||||||
const displayDependencies = ref(
|
const displayDependencies = ref(
|
||||||
version.value.dependencies.map((dependency) => {
|
version.value.dependencies.map((dependency) => {
|
||||||
const version = props.dependencies.versions.find((obj) => obj.id === dependency.version_id)
|
const version = props.dependencies.versions.find((obj) => obj.id === dependency.version_id)
|
||||||
|
|||||||
@@ -13,31 +13,33 @@ export default new createRouter({
|
|||||||
path: '/',
|
path: '/',
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
component: Pages.Index,
|
component: Pages.Index,
|
||||||
|
meta: {
|
||||||
|
breadcrumb: [{ name: 'Home' }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/browse',
|
path: '/browse',
|
||||||
name: 'Browse',
|
name: 'Browse',
|
||||||
component: Pages.Browse,
|
component: Pages.Browse,
|
||||||
|
meta: {
|
||||||
|
breadcrumb: [{ name: 'Browse' }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/library',
|
path: '/library',
|
||||||
name: 'Library',
|
name: 'Library',
|
||||||
component: Pages.Library,
|
component: Pages.Library,
|
||||||
},
|
meta: {
|
||||||
{
|
breadcrumb: [{ name: 'Library' }],
|
||||||
path: '/add-instance',
|
},
|
||||||
name: 'Add Instance',
|
|
||||||
component: Pages.AddInstance,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/project',
|
|
||||||
name: 'Project',
|
|
||||||
component: Pages.Project,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/settings',
|
path: '/settings',
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
component: Pages.Settings,
|
component: Pages.Settings,
|
||||||
|
meta: {
|
||||||
|
breadcrumb: [{ name: 'Settings' }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/project/:id',
|
path: '/project/:id',
|
||||||
@@ -49,22 +51,42 @@ export default new createRouter({
|
|||||||
path: '',
|
path: '',
|
||||||
name: 'Description',
|
name: 'Description',
|
||||||
component: Project.Description,
|
component: Project.Description,
|
||||||
|
meta: {
|
||||||
|
useContext: true,
|
||||||
|
breadcrumb: [{ name: '?Project' }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'versions',
|
path: 'versions',
|
||||||
name: 'Versions',
|
name: 'Versions',
|
||||||
component: Project.Versions,
|
component: Project.Versions,
|
||||||
|
meta: {
|
||||||
|
useContext: true,
|
||||||
|
breadcrumb: [{ name: '?Project', link: '/project/{id}/' }, { name: 'Versions' }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'version/:version',
|
path: 'version/:version',
|
||||||
name: 'Version',
|
name: 'Version',
|
||||||
component: Project.Version,
|
component: Project.Version,
|
||||||
props: true,
|
props: true,
|
||||||
|
meta: {
|
||||||
|
useContext: true,
|
||||||
|
breadcrumb: [
|
||||||
|
{ name: '?Project', link: '/project/{id}/' },
|
||||||
|
{ name: 'Versions', link: '/project/{id}/versions' },
|
||||||
|
{ name: '?Version' },
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'gallery',
|
path: 'gallery',
|
||||||
name: 'Gallery',
|
name: 'Gallery',
|
||||||
component: Project.Gallery,
|
component: Project.Gallery,
|
||||||
|
meta: {
|
||||||
|
useContext: true,
|
||||||
|
breadcrumb: [{ name: '?Project', link: '/project/{id}/' }, { name: 'Gallery' }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -78,16 +100,28 @@ export default new createRouter({
|
|||||||
path: '',
|
path: '',
|
||||||
name: 'Mods',
|
name: 'Mods',
|
||||||
component: Instance.Mods,
|
component: Instance.Mods,
|
||||||
|
meta: {
|
||||||
|
useRootContext: true,
|
||||||
|
breadcrumb: [{ name: '?Instance' }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'options',
|
path: 'options',
|
||||||
name: 'Options',
|
name: 'Options',
|
||||||
component: Instance.Options,
|
component: Instance.Options,
|
||||||
|
meta: {
|
||||||
|
useRootContext: true,
|
||||||
|
breadcrumb: [{ name: '?Instance', link: '/instance/{id}/' }, { name: 'Options' }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'logs',
|
path: 'logs',
|
||||||
name: 'Logs',
|
name: 'Logs',
|
||||||
component: Instance.Logs,
|
component: Instance.Logs,
|
||||||
|
meta: {
|
||||||
|
useRootContext: true,
|
||||||
|
breadcrumb: [{ name: '?Instance', link: '/instance/{id}/' }, { name: 'Logs' }],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
23
theseus_gui/src/store/breadcrumbs.js
Normal file
23
theseus_gui/src/store/breadcrumbs.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
export const useBreadcrumbs = defineStore('breadcrumbsStore', {
|
||||||
|
state: () => ({
|
||||||
|
names: new Map(),
|
||||||
|
context: null,
|
||||||
|
rootContext: null,
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
getName(route) {
|
||||||
|
return this.names.get(route) ?? route
|
||||||
|
},
|
||||||
|
setName(route, title) {
|
||||||
|
this.names.set(route, title)
|
||||||
|
},
|
||||||
|
setContext(context) {
|
||||||
|
this.context = context
|
||||||
|
},
|
||||||
|
setRootContext(context) {
|
||||||
|
this.rootContext = context
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useSearch } from './search'
|
import { useSearch } from './search'
|
||||||
import { useTheming } from './theme'
|
import { useTheming } from './theme'
|
||||||
|
import { useBreadcrumbs } from './breadcrumbs'
|
||||||
|
|
||||||
export { useSearch, useTheming }
|
export { useSearch, useTheming, useBreadcrumbs }
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
use dunce::canonicalize;
|
use dunce::canonicalize;
|
||||||
|
use theseus::jre::autodetect_java_globals;
|
||||||
use theseus::prelude::*;
|
use theseus::prelude::*;
|
||||||
use theseus::profile_create::profile_create;
|
use theseus::profile_create::profile_create;
|
||||||
use tokio::time::{sleep, Duration};
|
use tokio::time::{sleep, Duration};
|
||||||
@@ -32,8 +33,9 @@ async fn main() -> theseus::Result<()> {
|
|||||||
|
|
||||||
// Initialize state
|
// Initialize state
|
||||||
let st = State::get().await?;
|
let st = State::get().await?;
|
||||||
State::update();
|
//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 = 5;
|
||||||
st.settings.write().await.hooks.post_exit =
|
st.settings.write().await.hooks.post_exit =
|
||||||
Some("echo This is after Minecraft runs- global setting!".to_string());
|
Some("echo This is after Minecraft runs- global setting!".to_string());
|
||||||
@@ -53,7 +55,7 @@ async fn main() -> theseus::Result<()> {
|
|||||||
|
|
||||||
let name = "Example".to_string();
|
let name = "Example".to_string();
|
||||||
let game_version = "1.19.2".to_string();
|
let game_version = "1.19.2".to_string();
|
||||||
let modloader = ModLoader::Fabric;
|
let modloader = ModLoader::Vanilla;
|
||||||
let loader_version = "stable".to_string();
|
let loader_version = "stable".to_string();
|
||||||
|
|
||||||
let profile_path = profile_create(
|
let profile_path = profile_create(
|
||||||
@@ -88,7 +90,7 @@ async fn main() -> theseus::Result<()> {
|
|||||||
//
|
//
|
||||||
// profile::remove_project(&profile_path, &mod_menu_path).await?;
|
// profile::remove_project(&profile_path, &mod_menu_path).await?;
|
||||||
// let profile_path =
|
// let profile_path =
|
||||||
// pack::install_pack_from_version_id("KxUUUFh5".to_string())
|
// pack::install_pack_from_version_id("zroFQG1k".to_string())
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
||||||
@@ -126,7 +128,9 @@ async fn main() -> theseus::Result<()> {
|
|||||||
println!("Waiting 20 seconds to gather logs...");
|
println!("Waiting 20 seconds to gather logs...");
|
||||||
sleep(Duration::from_secs(20)).await;
|
sleep(Duration::from_secs(20)).await;
|
||||||
let stdout = process::get_stdout_by_uuid(&uuid).await?;
|
let stdout = process::get_stdout_by_uuid(&uuid).await?;
|
||||||
|
let stderr = process::get_stderr_by_uuid(&uuid).await?;
|
||||||
println!("Logs after 5sec <<< {stdout} >>> end stdout");
|
println!("Logs after 5sec <<< {stdout} >>> end stdout");
|
||||||
|
println!("Logs after 5sec <<< {stderr} >>> end stderr");
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"All running process UUID {:?}",
|
"All running process UUID {:?}",
|
||||||
|
|||||||
Reference in New Issue
Block a user