diff --git a/.github/workflows/gui-build.yml b/.github/workflows/gui-build.yml index 520f03172..54cd43258 100644 --- a/.github/workflows/gui-build.yml +++ b/.github/workflows/gui-build.yml @@ -29,6 +29,6 @@ jobs: - name: Install dependencies run: yarn install --immutable --immutable-cache --check-cache - name: Run Lint - run: npm run lint + run: yarn run lint - name: Build - run: npm run build + run: yarn run build diff --git a/theseus/src/api/pack.rs b/theseus/src/api/pack.rs index 155e69849..796e3dc78 100644 --- a/theseus/src/api/pack.rs +++ b/theseus/src/api/pack.rs @@ -5,7 +5,9 @@ use crate::event::emit::{ loading_try_for_each_concurrent, }; use crate::event::{LoadingBarId, LoadingBarType}; -use crate::state::{LinkedData, ModrinthProject, ModrinthVersion, SideType}; +use crate::state::{ + LinkedData, ModrinthProject, ModrinthVersion, ProfileInstallStage, SideType, +}; use crate::util::fetch::{ fetch, fetch_advanced, fetch_json, fetch_mirrors, write, write_cached_icon, }; @@ -76,13 +78,28 @@ enum PackDependency { pub async fn install_pack_from_version_id( version_id: String, - title: Option, + title: String, + icon_url: Option, ) -> crate::Result { let state = State::get().await?; Box::pin(async move { + let profile = crate::api::profile_create::profile_create( + title.clone(), + "1.19.4".to_string(), + ModLoader::Vanilla, + None, + None, + icon_url.clone(), + None, + Some(true), + ) + .await?; + let loading_bar = init_loading( LoadingBarType::PackFileDownload { + profile_path: profile.clone(), pack_name: title, + icon: icon_url, pack_version: version_id.clone(), }, 100.0, @@ -171,6 +188,7 @@ pub async fn install_pack_from_version_id( Some(version.project_id), Some(version.id), Some(loading_bar), + profile, ) .await }) @@ -178,9 +196,36 @@ pub async fn install_pack_from_version_id( } pub async fn install_pack_from_file(path: PathBuf) -> crate::Result { - let file = fs::read(path).await?; + let file = fs::read(&path).await?; - install_pack(bytes::Bytes::from(file), None, None, None, None, None).await + let file_name = path + .file_name() + .unwrap_or_default() + .to_string_lossy() + .to_string(); + + let profile = crate::api::profile_create::profile_create( + file_name, + "1.19.4".to_string(), + ModLoader::Vanilla, + None, + None, + None, + None, + Some(true), + ) + .await?; + + install_pack( + bytes::Bytes::from(file), + None, + None, + None, + None, + None, + profile, + ) + .await } async fn install_pack( @@ -190,6 +235,7 @@ async fn install_pack( project_id: Option, version_id: Option, existing_loading_bar: Option, + profile: PathBuf, ) -> crate::Result { let state = &State::get().await?; @@ -261,25 +307,38 @@ async fn install_pack( .into()); }; - let profile_raw = crate::api::profile_create::profile_create( - override_title.unwrap_or_else(|| pack.name.clone()), - game_version.clone(), - mod_loader.unwrap_or(ModLoader::Vanilla), - loader_version.cloned(), - icon, - Some(LinkedData { + let loader_version = + crate::profile_create::get_loader_version_from_loader( + game_version.clone(), + mod_loader.unwrap_or(ModLoader::Vanilla), + loader_version.cloned(), + ) + .await?; + crate::api::profile::edit(&profile, |prof| { + prof.metadata.name = + override_title.clone().unwrap_or_else(|| pack.name.clone()); + prof.install_stage = ProfileInstallStage::PackInstalling; + prof.metadata.linked_data = Some(LinkedData { project_id: project_id.clone(), version_id: version_id.clone(), - }), - Some(true), - ) + }); + prof.metadata.icon = icon.clone(); + prof.metadata.game_version = game_version.clone(); + prof.metadata.loader_version = loader_version.clone(); + + async { Ok(()) } + }) .await?; - let profile = profile_raw.clone(); + State::sync().await?; + + let profile = profile.clone(); let result = async { let loading_bar = init_or_edit_loading( existing_loading_bar, LoadingBarType::PackDownload { + profile_path: profile.clone(), pack_name: pack.name.clone(), + icon, pack_id: project_id, pack_version: version_id, }, @@ -413,7 +472,7 @@ async fn install_pack( emit_loading( &loading_bar, 29.9, - Some("Done extacting overrides"), + Some("Done extracting overrides"), ) .await?; @@ -429,19 +488,21 @@ async fn install_pack( )?; } - Ok::(profile) + Ok::(profile.clone()) } .await; match result { Ok(profile) => Ok(profile), Err(err) => { - let _ = crate::api::profile::remove(&profile_raw).await; + let _ = crate::api::profile::remove(&profile).await; Err(err) } } } else { + let _ = crate::api::profile::remove(&profile).await; + Err(crate::Error::from(crate::ErrorKind::InputError( "No pack manifest found in mrpack".to_string(), ))) diff --git a/theseus/src/api/profile.rs b/theseus/src/api/profile.rs index 9ed210d02..7028b14bf 100644 --- a/theseus/src/api/profile.rs +++ b/theseus/src/api/profile.rs @@ -98,7 +98,7 @@ pub async fn sync(path: &Path) -> crate::Result<()> { > = state.profiles.write().await; if let Some(profile) = profiles.0.get_mut(path) { - profile.sync().await?; + profile.sync_projects().await?; Ok(()) } else { Err(crate::ErrorKind::UnmanagedProfileError( @@ -117,24 +117,18 @@ pub async fn sync(path: &Path) -> crate::Result<()> { /// Installs/Repairs a profile #[tracing::instrument] pub async fn install(path: &Path) -> crate::Result<()> { - let state = State::get().await?; - let result = { - let mut profiles: tokio::sync::RwLockWriteGuard< - crate::state::Profiles, - > = state.profiles.write().await; + let profile = get(path).await?; - if let Some(profile) = profiles.0.get_mut(path) { - crate::launcher::install_minecraft(profile, None).await?; - Ok(()) - } else { - Err(crate::ErrorKind::UnmanagedProfileError( - path.display().to_string(), - ) - .as_error()) - } - }; + if let Some(profile) = profile { + crate::launcher::install_minecraft(&profile, None).await?; + } else { + return Err(crate::ErrorKind::UnmanagedProfileError( + path.display().to_string(), + ) + .as_error()); + } State::sync().await?; - result + Ok(()) } pub async fn update_all(profile_path: &Path) -> crate::Result<()> { @@ -145,7 +139,7 @@ pub async fn update_all(profile_path: &Path) -> crate::Result<()> { if let Some(profile) = profiles.0.get_mut(profile_path) { let loading_bar = init_loading( LoadingBarType::ProfileUpdate { - profile_uuid: profile.uuid, + profile_path: profile.path.clone(), profile_name: profile.metadata.name.clone(), }, 100.0, @@ -162,11 +156,15 @@ pub async fn update_all(profile_path: &Path) -> crate::Result<()> { 100.0, profile.projects.keys().len(), None, - |project| update_project(profile_path, project, Some(true)), + |project| async move { + let _ = update_project(profile_path, project).await?; + + Ok(()) + }, ) .await?; - profile.sync().await?; + profile.sync_projects().await?; Ok(()) } else { @@ -182,8 +180,7 @@ pub async fn update_all(profile_path: &Path) -> crate::Result<()> { pub async fn update_project( profile_path: &Path, project_path: &Path, - should_not_sync: Option, -) -> crate::Result<()> { +) -> crate::Result { let state = State::get().await?; let mut profiles = state.profiles.write().await; @@ -194,21 +191,33 @@ pub async fn update_project( .. } = &project.metadata { - let path = profile + let (path, new_version) = profile .add_project_version(update_version.id.clone()) .await?; if path != project_path { - profile.remove_project(project_path).await?; + profile.remove_project(project_path, Some(true)).await?; } - if !should_not_sync.unwrap_or(false) { - profile.sync().await?; + let value = profile.projects.remove(project_path); + if let Some(mut project) = value { + if let ProjectMetadata::Modrinth { + ref mut version, .. + } = project.metadata + { + *version = Box::new(new_version); + } + profile.projects.insert(path.clone(), project); } + + return Ok(path); } } - Ok(()) + Err(crate::ErrorKind::InputError( + "This project cannot be updated!".to_string(), + ) + .as_error()) } else { Err(crate::ErrorKind::UnmanagedProfileError( profile_path.display().to_string(), @@ -227,13 +236,23 @@ pub async fn replace_project( let mut profiles = state.profiles.write().await; if let Some(profile) = profiles.0.get_mut(profile_path) { - let path = profile.add_project_version(version_id).await?; + let (path, new_version) = + profile.add_project_version(version_id).await?; if path != project { - profile.remove_project(project).await?; + profile.remove_project(project, Some(true)).await?; } - profile.sync().await?; + let value = profile.projects.remove(project); + if let Some(mut project) = value { + if let ProjectMetadata::Modrinth { + ref mut version, .. + } = project.metadata + { + *version = Box::new(new_version); + } + profile.projects.insert(path.clone(), project); + } Ok(path) } else { @@ -254,9 +273,9 @@ pub async fn add_project_from_version( let mut profiles = state.profiles.write().await; if let Some(profile) = profiles.0.get_mut(profile_path) { - let path = profile.add_project_version(version_id).await?; + let (path, _) = profile.add_project_version(version_id).await?; - profile.sync().await?; + Profile::sync_projects_task(profile.path.clone()); Ok(path) } else { @@ -293,7 +312,7 @@ pub async fn add_project_from_path( ) .await?; - profile.sync().await?; + Profile::sync_projects_task(profile.path.clone()); Ok(path) } else { @@ -335,7 +354,7 @@ pub async fn remove_project( let mut profiles = state.profiles.write().await; if let Some(profile) = profiles.0.get_mut(profile) { - profile.remove_project(project).await?; + profile.remove_project(project, None).await?; Ok(()) } else { diff --git a/theseus/src/api/profile_create.rs b/theseus/src/api/profile_create.rs index 62f6733f5..6784425b9 100644 --- a/theseus/src/api/profile_create.rs +++ b/theseus/src/api/profile_create.rs @@ -33,6 +33,7 @@ pub async fn profile_create_empty() -> crate::Result { None, // the icon for the profile None, None, + None, ) .await } @@ -40,20 +41,20 @@ pub async fn profile_create_empty() -> crate::Result { // Creates a profile at the given filepath and adds it to the in-memory state // Returns filepath at which it can be accessed in the State #[tracing::instrument] +#[allow(clippy::too_many_arguments)] pub async fn profile_create( name: String, // the name of the profile, and relative path game_version: String, // the game version of the profile modloader: ModLoader, // the modloader to use loader_version: Option, // the modloader version to use, set to "latest", "stable", or the ID of your chosen loader. defaults to latest icon: Option, // the icon for the profile + icon_url: Option, // the URL icon for a profile (ONLY USED FOR TEMPORARY PROFILES) linked_data: Option, // the linked project ID (mainly for modpacks)- used for updating skip_install_profile: Option, ) -> crate::Result { trace!("Creating new profile. {}", name); let state = State::get().await?; Box::pin(async move { - let metadata = state.metadata.read().await; - let uuid = Uuid::new_v4(); let path = state.directories.profiles_dir().join(uuid.to_string()); if path.exists() { @@ -82,66 +83,8 @@ pub async fn profile_create( "Creating profile at path {}", &canonicalize(&path)?.display() ); - - let loader = modloader; - let loader = if loader != ModLoader::Vanilla { - let version = loader_version.unwrap_or_else(|| "latest".to_string()); - - let filter = |it: &LoaderVersion| match version.as_str() { - "latest" => true, - "stable" => it.stable, - id => it.id == *id || format!("{}-{}", game_version, id) == it.id, - }; - - let loader_data = match loader { - ModLoader::Forge => &metadata.forge, - ModLoader::Fabric => &metadata.fabric, - ModLoader::Quilt => &metadata.quilt, - _ => { - return Err(ProfileCreationError::NoManifest( - loader.to_string(), - ) - .into()) - } - }; - - let loaders = &loader_data - .game_versions - .iter() - .find(|it| { - it.id.replace( - daedalus::modded::DUMMY_REPLACE_STRING, - &game_version, - ) == game_version - }) - .ok_or_else(|| { - ProfileCreationError::ModloaderUnsupported( - loader.to_string(), - game_version.clone(), - ) - })? - .loaders; - - let loader_version = loaders - .iter() - .cloned() - .find(filter) - .or( - // If stable was searched for but not found, return latest by default - if version == "stable" { - loaders.iter().next().cloned() - } else { - None - }, - ) - .ok_or_else(|| { - ProfileCreationError::InvalidVersionModloader( - version, - loader.to_string(), - ) - })?; - - Some((loader_version, loader)) + let loader = if modloader != ModLoader::Vanilla { + get_loader_version_from_loader(game_version.clone(), modloader, loader_version).await? } else { None }; @@ -161,8 +104,9 @@ pub async fn profile_create( ) .await?; } - if let Some((loader_version, loader)) = loader { - profile.metadata.loader = loader; + profile.metadata.icon_url = icon_url; + if let Some(loader_version) = loader { + profile.metadata.loader = modloader; profile.metadata.loader_version = Some(loader_version); } @@ -203,6 +147,71 @@ pub async fn profile_create( }).await } +pub(crate) async fn get_loader_version_from_loader( + game_version: String, + loader: ModLoader, + loader_version: Option, +) -> crate::Result> { + let state = State::get().await?; + let metadata = state.metadata.read().await; + + let version = loader_version.unwrap_or_else(|| "latest".to_string()); + + let filter = |it: &LoaderVersion| match version.as_str() { + "latest" => true, + "stable" => it.stable, + id => it.id == *id || format!("{}-{}", game_version, id) == it.id, + }; + + let loader_data = match loader { + ModLoader::Forge => &metadata.forge, + ModLoader::Fabric => &metadata.fabric, + ModLoader::Quilt => &metadata.quilt, + _ => { + return Err( + ProfileCreationError::NoManifest(loader.to_string()).into() + ) + } + }; + + let loaders = &loader_data + .game_versions + .iter() + .find(|it| { + it.id + .replace(daedalus::modded::DUMMY_REPLACE_STRING, &game_version) + == game_version + }) + .ok_or_else(|| { + ProfileCreationError::ModloaderUnsupported( + loader.to_string(), + game_version.clone(), + ) + })? + .loaders; + + let loader_version = loaders + .iter() + .cloned() + .find(filter) + .or( + // If stable was searched for but not found, return latest by default + if version == "stable" { + loaders.iter().next().cloned() + } else { + None + }, + ) + .ok_or_else(|| { + ProfileCreationError::InvalidVersionModloader( + version, + loader.to_string(), + ) + })?; + + Ok(Some(loader_version)) +} + #[derive(thiserror::Error, Debug)] pub enum ProfileCreationError { #[error("Profile .json exists: {0}")] diff --git a/theseus/src/event/mod.rs b/theseus/src/event/mod.rs index e05a4f6bf..1826c1f8f 100644 --- a/theseus/src/event/mod.rs +++ b/theseus/src/event/mod.rs @@ -148,20 +148,24 @@ impl Drop for LoadingBar { pub enum LoadingBarType { StateInit, PackFileDownload { - pack_name: Option, + profile_path: PathBuf, + pack_name: String, + icon: Option, pack_version: String, }, PackDownload { + profile_path: PathBuf, pack_name: String, + icon: Option, pack_id: Option, pack_version: Option, }, MinecraftDownload { - profile_uuid: Uuid, + profile_path: PathBuf, profile_name: String, }, ProfileUpdate { - profile_uuid: Uuid, + profile_path: PathBuf, profile_name: String, }, } @@ -187,6 +191,7 @@ pub struct ProcessPayload { pub message: String, } #[derive(Serialize, Clone, Debug)] +#[serde(rename_all = "snake_case")] pub enum ProcessPayloadType { Launched, Updated, // eg: if the MinecraftChild changes to its post-command process instead of the Minecraft process diff --git a/theseus/src/launcher/mod.rs b/theseus/src/launcher/mod.rs index ecfb2bdfe..be89f0f52 100644 --- a/theseus/src/launcher/mod.rs +++ b/theseus/src/launcher/mod.rs @@ -1,6 +1,7 @@ //! Logic for launching Minecraft use crate::event::emit::{emit_loading, init_or_edit_loading}; use crate::event::{LoadingBarId, LoadingBarType}; +use crate::state::ProfileInstallStage; use crate::{ process, state::{self as st, MinecraftChild}, @@ -59,9 +60,18 @@ pub async fn install_minecraft( existing_loading_bar: Option, ) -> crate::Result<()> { Box::pin(async move { + crate::api::profile::edit(&profile.path, |prof| { + prof.install_stage = ProfileInstallStage::Installing; + + async { Ok(()) } + }) + .await?; + State::sync().await?; + let state = State::get().await?; let instance_path = &canonicalize(&profile.path)?; let metadata = state.metadata.read().await; + let version = metadata .minecraft .versions @@ -85,7 +95,7 @@ pub async fn install_minecraft( LoadingBarType::MinecraftDownload { // If we are downloading minecraft for a profile, provide its name and uuid profile_name: profile.metadata.name.clone(), - profile_uuid: profile.uuid, + profile_path: profile.path.clone(), }, 100.0, "Downloading Minecraft", @@ -202,11 +212,11 @@ pub async fn install_minecraft( } crate::api::profile::edit(&profile.path, |prof| { - prof.installed = true; + prof.install_stage = ProfileInstallStage::Installed; async { Ok(()) } }) - .await?; + .await?; State::sync().await?; emit_loading( &loading_bar, @@ -232,7 +242,16 @@ pub async fn launch_minecraft( profile: &Profile, ) -> crate::Result>> { Box::pin(async move { - if !profile.installed { + if profile.install_stage == ProfileInstallStage::PackInstalling + || profile.install_stage == ProfileInstallStage::Installing + { + return Err(crate::ErrorKind::LauncherError( + "Profile is still installing".to_string(), + ) + .into()); + } + + if profile.install_stage != ProfileInstallStage::Installed { install_minecraft(profile, None).await?; } diff --git a/theseus/src/state/profiles.rs b/theseus/src/state/profiles.rs index 9597d336a..6148bd799 100644 --- a/theseus/src/state/profiles.rs +++ b/theseus/src/state/profiles.rs @@ -4,11 +4,12 @@ use crate::data::DirectoryInfo; use crate::event::emit::emit_profile; use crate::event::ProfilePayloadType; use crate::state::projects::Project; -use crate::state::{ModrinthVersion, ProjectType}; +use crate::state::{ModrinthVersion, ProjectMetadata, ProjectType}; use crate::util::fetch::{ fetch, fetch_json, write, write_cached_icon, IoSemaphore, }; use crate::State; +use daedalus::get_hash; use daedalus::modded::LoaderVersion; use dunce::canonicalize; use futures::prelude::*; @@ -29,12 +30,28 @@ pub(crate) struct Profiles(pub HashMap); // TODO: possibly add defaults to some of these values pub const CURRENT_FORMAT_VERSION: u32 = 1; +#[derive( + Serialize, Deserialize, Clone, Copy, Debug, Default, Eq, PartialEq, +)] +#[serde(rename_all = "snake_case")] +pub enum ProfileInstallStage { + /// Profile is installed + Installed, + /// Profile's minecraft game is still installing + Installing, + /// Profile created for pack, but the pack hasn't been fully installed yet + PackInstalling, + /// Profile is not installed + #[default] + NotInstalled, +} + // Represent a Minecraft instance. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Profile { pub uuid: Uuid, // todo: will be used in restructure to refer to profiles #[serde(default)] - pub installed: bool, + pub install_stage: ProfileInstallStage, pub path: PathBuf, pub metadata: ProfileMetadata, #[serde(skip_serializing_if = "Option::is_none")] @@ -53,6 +70,8 @@ pub struct ProfileMetadata { pub name: String, #[serde(skip_serializing_if = "Option::is_none")] pub icon: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub icon_url: Option, pub game_version: String, #[serde(default)] pub loader: ModLoader, @@ -127,11 +146,12 @@ impl Profile { Ok(Self { uuid, - installed: false, + install_stage: ProfileInstallStage::NotInstalled, path: canonicalize(path)?, metadata: ProfileMetadata { name, icon: None, + icon_url: None, game_version: version, loader: ModLoader::Vanilla, loader_version: None, @@ -160,7 +180,7 @@ impl Profile { Ok(self) } - pub async fn sync(&mut self) -> crate::Result<()> { + pub async fn sync_projects(&mut self) -> crate::Result<()> { let state = State::get().await?; let paths = self.get_profile_project_paths()?; @@ -186,6 +206,45 @@ impl Profile { Ok(()) } + pub fn sync_projects_task(path: PathBuf) { + tokio::task::spawn(async move { + let res = async { + let state = State::get().await?; + let profile = crate::api::profile::get(&path).await?; + + if let Some(profile) = profile { + let paths = profile.get_profile_project_paths()?; + + let projects = crate::state::infer_data_from_files( + profile, + paths, + state.directories.caches_dir(), + &state.io_semaphore, + &state.fetch_semaphore, + ) + .await?; + + let mut new_profiles = state.profiles.write().await; + if let Some(profile) = new_profiles.0.get_mut(&path) { + profile.projects = projects; + } + } + + Ok::<(), crate::Error>(()) + } + .await; + + match res { + Ok(()) => {} + Err(err) => { + tracing::warn!( + "Unable to fetch single profile projects: {err}" + ) + } + }; + }); + } + pub fn get_profile_project_paths(&self) -> crate::Result> { let mut files = Vec::new(); let mut read_paths = |path: &str| { @@ -212,7 +271,7 @@ impl Profile { pub async fn add_project_version( &mut self, version_id: String, - ) -> crate::Result { + ) -> crate::Result<(PathBuf, ModrinthVersion)> { let state = State::get().await?; let version = fetch_json::( @@ -247,11 +306,11 @@ impl Profile { .add_project_bytes( &file.filename, bytes, - ProjectType::get_from_loaders(version.loaders), + ProjectType::get_from_loaders(version.loaders.clone()), ) .await?; - Ok(path) + Ok((path, version)) } pub async fn add_project_bytes( @@ -294,6 +353,25 @@ impl Profile { let path = self.path.join(project_type.get_folder()).join(file_name); write(&path, &bytes, &state.io_semaphore).await?; + let hash = get_hash(bytes).await?; + + self.projects.insert( + path.clone(), + Project { + sha512: hash, + disabled: false, + metadata: ProjectMetadata::Unknown, + file_name: file_name.to_string(), + }, + ); + emit_profile( + self.uuid, + self.path.clone(), + &self.metadata.name, + ProfilePayloadType::Synced, + ) + .await?; + Ok(path) } @@ -329,10 +407,16 @@ impl Profile { Ok(()) } - pub async fn remove_project(&mut self, path: &Path) -> crate::Result<()> { + pub async fn remove_project( + &mut self, + path: &Path, + dont_remove_arr: Option, + ) -> crate::Result<()> { if self.projects.contains_key(path) { fs::remove_file(path).await?; - self.projects.remove(path); + if !dont_remove_arr.unwrap_or(false) { + self.projects.remove(path); + } } else { return Err(crate::ErrorKind::InputError(format!( "Project path does not exist: {:?}", diff --git a/theseus_cli/src/subcommands/profile.rs b/theseus_cli/src/subcommands/profile.rs index edcb678b8..6ecb4d303 100644 --- a/theseus_cli/src/subcommands/profile.rs +++ b/theseus_cli/src/subcommands/profile.rs @@ -195,6 +195,7 @@ impl ProfileInit { None, None, None, + None, ) .await?; diff --git a/theseus_gui/src-tauri/src/api/pack.rs b/theseus_gui/src-tauri/src/api/pack.rs index a745f7894..8872c8b9d 100644 --- a/theseus_gui/src-tauri/src/api/pack.rs +++ b/theseus_gui/src-tauri/src/api/pack.rs @@ -2,15 +2,15 @@ use crate::api::Result; use std::path::{Path, PathBuf}; 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, - pack_title: Option, + pack_title: String, + pack_icon: Option, ) -> Result { let res = - pack::install_pack_from_version_id(version_id, pack_title).await?; + pack::install_pack_from_version_id(version_id, pack_title, pack_icon) + .await?; Ok(res) } diff --git a/theseus_gui/src-tauri/src/api/profile.rs b/theseus_gui/src-tauri/src/api/profile.rs index b00a19821..d8ce08149 100644 --- a/theseus_gui/src-tauri/src/api/profile.rs +++ b/theseus_gui/src-tauri/src/api/profile.rs @@ -1,4 +1,6 @@ use crate::api::Result; +use daedalus::modded::LoaderVersion; +use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use theseus::prelude::*; use uuid::Uuid; @@ -59,7 +61,7 @@ pub async fn profile_update_project( path: &Path, project_path: &Path, ) -> Result<()> { - profile::update_project(path, project_path, None).await?; + profile::update_project(path, project_path).await?; Ok(()) } @@ -165,3 +167,53 @@ pub async fn profile_run_wait_credentials( let mut proc = proc_lock.write().await; Ok(process::wait_for(&mut proc).await?) } + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct EditProfile { + pub metadata: Option, + pub java: Option, + pub memory: Option, + pub resolution: Option, + pub hooks: Option, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct EditProfileMetadata { + pub name: Option, + pub game_version: Option, + pub loader: Option, + pub loader_version: Option, +} + +// Edits a profile +// invoke('profile_edit', {path, editProfile}) +#[tauri::command] +pub async fn profile_edit( + path: &Path, + edit_profile: EditProfile, +) -> Result<()> { + profile::edit(&path, |prof| { + if let Some(metadata) = edit_profile.metadata.clone() { + if let Some(name) = metadata.name { + prof.metadata.name = name + } + if let Some(game_version) = metadata.game_version { + prof.metadata.game_version = game_version + } + if let Some(loader) = metadata.loader { + prof.metadata.loader = loader + } + prof.metadata.loader_version = metadata.loader_version + } + + prof.java = edit_profile.java.clone(); + prof.memory = edit_profile.memory; + prof.resolution = edit_profile.resolution; + prof.hooks = edit_profile.hooks.clone(); + + async { Ok(()) } + }) + .await?; + + Ok(()) +} diff --git a/theseus_gui/src-tauri/src/api/profile_create.rs b/theseus_gui/src-tauri/src/api/profile_create.rs index 5cc865dda..fb49f3dd7 100644 --- a/theseus_gui/src-tauri/src/api/profile_create.rs +++ b/theseus_gui/src-tauri/src/api/profile_create.rs @@ -28,6 +28,7 @@ pub async fn profile_create( icon, None, None, + None, ) .await?; Ok(res) diff --git a/theseus_gui/src-tauri/src/main.rs b/theseus_gui/src-tauri/src/main.rs index e67a62a70..d51def999 100644 --- a/theseus_gui/src-tauri/src/main.rs +++ b/theseus_gui/src-tauri/src/main.rs @@ -92,6 +92,7 @@ fn main() { api::profile::profile_run_wait, api::profile::profile_run_credentials, api::profile::profile_run_wait_credentials, + api::profile::profile_edit, api::pack::pack_install_version_id, api::pack::pack_install_file, api::auth::auth_authenticate_begin_flow, diff --git a/theseus_gui/src/components/GridDisplay.vue b/theseus_gui/src/components/GridDisplay.vue index 2c7122d19..958952098 100644 --- a/theseus_gui/src/components/GridDisplay.vue +++ b/theseus_gui/src/components/GridDisplay.vue @@ -97,19 +97,13 @@ const modsRow = ref(null) .instances { display: grid; - grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)); width: 100%; gap: 1rem; margin-right: auto; margin-top: 0.8rem; scroll-behavior: smooth; - overflow-x: scroll; - overflow-y: hidden; - - &::-webkit-scrollbar { - width: 0px; - background: transparent; - } + overflow-y: scroll; } } diff --git a/theseus_gui/src/components/RowDisplay.vue b/theseus_gui/src/components/RowDisplay.vue index 0af6300f3..170fb5ae8 100644 --- a/theseus_gui/src/components/RowDisplay.vue +++ b/theseus_gui/src/components/RowDisplay.vue @@ -1,7 +1,6 @@ diff --git a/theseus_gui/src/helpers/pack.js b/theseus_gui/src/helpers/pack.js index 2418345ae..e49c84fac 100644 --- a/theseus_gui/src/helpers/pack.js +++ b/theseus_gui/src/helpers/pack.js @@ -6,8 +6,8 @@ import { invoke } from '@tauri-apps/api/tauri' // Installs pack from a version ID -export async function install(versionId, packTitle) { - return await invoke('pack_install_version_id', { versionId, packTitle }) +export async function install(versionId, packTitle, packIcon) { + return await invoke('pack_install_version_id', { versionId, packTitle, packIcon }) } // Installs pack from a path diff --git a/theseus_gui/src/helpers/profile.js b/theseus_gui/src/helpers/profile.js index 0ff91d5ce..b8db0b609 100644 --- a/theseus_gui/src/helpers/profile.js +++ b/theseus_gui/src/helpers/profile.js @@ -101,3 +101,8 @@ export async function run(path) { export async function run_wait(path) { return await invoke('profile_run_wait', { path }) } + +// Edits a profile +export async function edit(path, editProfile) { + return await invoke('profile_edit', { path, editProfile }) +} diff --git a/theseus_gui/src/pages/Library.vue b/theseus_gui/src/pages/Library.vue index 64bbfdf82..3a2e18bef 100644 --- a/theseus_gui/src/pages/Library.vue +++ b/theseus_gui/src/pages/Library.vue @@ -12,28 +12,28 @@ const breadcrumbs = useBreadcrumbs() breadcrumbs.setRootContext({ name: 'Library', link: route.path }) const profiles = await list() -const instances = shallowRef( - Object.values(profiles).filter((prof) => !prof.metadata.linked_project_id) -) -const modpacks = shallowRef( - Object.values(profiles).filter((prof) => prof.metadata.linked_project_id) -) +const instances = shallowRef(Object.values(profiles)) loading_listener(async (profile) => { - console.log(profile) if (profile.event === 'loaded') { const profiles = await list() - instances.value = Object.values(profiles).filter((prof) => !prof.metadata.linked_project_id) - modpacks.value = Object.values(profiles).filter((prof) => prof.metadata.linked_project_id) + instances.value = Object.values(profiles) } }) - + diff --git a/theseus_gui/src/pages/instance/Index.vue b/theseus_gui/src/pages/instance/Index.vue index 225b383ae..619fecca5 100644 --- a/theseus_gui/src/pages/instance/Index.vue +++ b/theseus_gui/src/pages/instance/Index.vue @@ -2,7 +2,14 @@
- +

{{ instance.metadata.name }}