diff --git a/apps/app-frontend/src/components/ui/instance_settings/GeneralSettings.vue b/apps/app-frontend/src/components/ui/instance_settings/GeneralSettings.vue index e30bead729..85a2df3500 100644 --- a/apps/app-frontend/src/components/ui/instance_settings/GeneralSettings.vue +++ b/apps/app-frontend/src/components/ui/instance_settings/GeneralSettings.vue @@ -9,6 +9,7 @@ import { injectNotificationManager, OverflowMenu, StyledInput, + Toggle, useVIntl, } from '@modrinth/ui' import { useQueryClient } from '@tanstack/vue-query' @@ -44,6 +45,21 @@ const releaseChannelDisabledItems = computed(() => savingReleaseChannel.value ? [...releaseChannelOptions] : [], ) +const autoUpdateModpack = ref(instance.value.auto_update_modpack ?? false) + +const hasLinkedModpack = computed(() => !!instance.value.linked_data) + +watch(autoUpdateModpack, async (value) => { + await edit(instance.value.path, { auto_update_modpack: value }).catch(handleError) +}) + +watch( + () => instance.value.auto_update_modpack, + (val) => { + autoUpdateModpack.value = val ?? false + }, +) + const newCategoryInput = ref('') const installing = computed(() => instance.value.install_stage !== 'installed') @@ -263,6 +279,15 @@ const messages = defineMessages({ id: 'instance.settings.tabs.general.update-channel.select', defaultMessage: 'Select update channel', }, + autoUpdateModpack: { + id: 'instance.settings.tabs.general.auto-update-modpack', + defaultMessage: 'Auto-update modpack', + }, + autoUpdateModpackDescription: { + id: 'instance.settings.tabs.general.auto-update-modpack.description', + defaultMessage: + 'Automatically update this modpack to the latest version. Updates are checked before launching and periodically in the background.', + }, deleteInstance: { id: 'instance.settings.tabs.general.delete', defaultMessage: 'Delete instance', @@ -409,6 +434,18 @@ const messages = defineMessages({

+
+
+

+ {{ formatMessage(messages.autoUpdateModpack) }} +

+

+ {{ formatMessage(messages.autoUpdateModpackDescription) }} +

+
+ +
+

{{ formatMessage(messages.deleteInstance) }} diff --git a/apps/app-frontend/src/helpers/profile.ts b/apps/app-frontend/src/helpers/profile.ts index be3b62c6ef..d4ffcd4707 100644 --- a/apps/app-frontend/src/helpers/profile.ts +++ b/apps/app-frontend/src/helpers/profile.ts @@ -242,6 +242,11 @@ export async function update_managed_modrinth_version( }) } +// Auto-update all modpacks with auto_update_modpack enabled +export async function auto_update_all_modpacks(): Promise { + return await invoke('plugin:profile|profile_auto_update_all_modpacks') +} + // Repair a managed Modrinth profile export async function update_repair_modrinth(path: string): Promise { return await invoke('plugin:profile|profile_repair_managed_modrinth', { path }) diff --git a/apps/app-frontend/src/helpers/types.d.ts b/apps/app-frontend/src/helpers/types.d.ts index 143997702f..f659f2d9cc 100644 --- a/apps/app-frontend/src/helpers/types.d.ts +++ b/apps/app-frontend/src/helpers/types.d.ts @@ -31,6 +31,7 @@ export type GameInstance = { force_fullscreen?: boolean game_resolution?: [number, number] hooks: Hooks + auto_update_modpack?: boolean } type InstallStage = diff --git a/apps/app-frontend/src/locales/en-US/index.json b/apps/app-frontend/src/locales/en-US/index.json index cf98dc3a76..cb3cb7e424 100644 --- a/apps/app-frontend/src/locales/en-US/index.json +++ b/apps/app-frontend/src/locales/en-US/index.json @@ -665,6 +665,12 @@ "instance.settings.tabs.general": { "message": "General" }, + "instance.settings.tabs.general.auto-update-modpack": { + "message": "Auto-update modpack" + }, + "instance.settings.tabs.general.auto-update-modpack.description": { + "message": "Automatically update this modpack to the latest version. Updates are checked before launching and periodically in the background." + }, "instance.settings.tabs.general.delete": { "message": "Delete instance" }, diff --git a/apps/app/build.rs b/apps/app/build.rs index 1f78fcce47..58f683bdff 100644 --- a/apps/app/build.rs +++ b/apps/app/build.rs @@ -196,6 +196,7 @@ fn main() { "profile_edit_icon", "profile_export_mrpack", "profile_get_pack_export_candidates", + "profile_auto_update_all_modpacks", ]) .default_permission( DefaultPermissionRule::AllowAllCommands, diff --git a/apps/app/src/api/profile.rs b/apps/app/src/api/profile.rs index ab41af5887..cf8ab54e0e 100644 --- a/apps/app/src/api/profile.rs +++ b/apps/app/src/api/profile.rs @@ -43,6 +43,7 @@ pub fn init() -> tauri::plugin::TauriPlugin { profile_edit_icon, profile_export_mrpack, profile_get_pack_export_candidates, + profile_auto_update_all_modpacks, ]) .build() } @@ -432,6 +433,20 @@ pub struct EditProfile { )] pub game_resolution: Option>, pub hooks: Option, + + #[serde( + default, + skip_serializing_if = "Option::is_none", + with = "serde_with::rust::double_option" + )] + pub auto_update_modpack: Option>, +} + +// Auto-update all modpacks with auto_update_modpack enabled +// invoke('plugin:profile|profile_auto_update_all_modpacks') +#[tauri::command] +pub async fn profile_auto_update_all_modpacks() -> Result<()> { + Ok(profile::auto_update_all_modpacks().await?) } // Edits a profile @@ -480,6 +495,9 @@ pub async fn profile_edit(path: &str, edit_profile: EditProfile) -> Result<()> { if let Some(hooks) = edit_profile.hooks.clone() { prof.hooks = hooks; } + if let Some(auto_update_modpack) = edit_profile.auto_update_modpack { + prof.auto_update_modpack = auto_update_modpack; + } prof.modified = chrono::Utc::now(); diff --git a/apps/app/src/main.rs b/apps/app/src/main.rs index 33d2da8796..8674e87da6 100644 --- a/apps/app/src/main.rs +++ b/apps/app/src/main.rs @@ -226,6 +226,19 @@ fn main() { tracing::warn!("Failed to set window shadow: {e}"); } + // Background task: auto-update modpacks every 60 minutes + tauri::async_runtime::spawn(async move { + // Wait for state to initialize before first check + tokio::time::sleep(std::time::Duration::from_secs(30)).await; + loop { + tracing::debug!("Running background modpack auto-update check"); + if let Err(e) = theseus::profile::auto_update_all_modpacks().await { + tracing::warn!("Background modpack auto-update failed: {e}"); + } + tokio::time::sleep(std::time::Duration::from_secs(3600)).await; + } + }); + Ok(()) }); diff --git a/apps/frontend/AGENTS.md b/apps/frontend/AGENTS.md index 681311eb9c..ceb2b988dc 120000 --- a/apps/frontend/AGENTS.md +++ b/apps/frontend/AGENTS.md @@ -1 +1 @@ -CLAUDE.md \ No newline at end of file +CLAUDE.md diff --git a/packages/api-client/AGENTS.md b/packages/api-client/AGENTS.md index 681311eb9c..ceb2b988dc 120000 --- a/packages/api-client/AGENTS.md +++ b/packages/api-client/AGENTS.md @@ -1 +1 @@ -CLAUDE.md \ No newline at end of file +CLAUDE.md diff --git a/packages/app-lib/.sqlx/query-22202dacf6b5bade90c909cb4a663a1388ef538e4b80680bce4b668b182b4f42.json b/packages/app-lib/.sqlx/query-22202dacf6b5bade90c909cb4a663a1388ef538e4b80680bce4b668b182b4f42.json deleted file mode 100644 index acc106f94d..0000000000 --- a/packages/app-lib/.sqlx/query-22202dacf6b5bade90c909cb4a663a1388ef538e4b80680bce4b668b182b4f42.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "\n INSERT INTO profiles (\n path, install_stage, name, icon_path,\n game_version, mod_loader, mod_loader_version,\n groups,\n linked_project_id, linked_version_id, locked, preferred_update_channel,\n created, modified, last_played,\n submitted_time_played, recent_time_played,\n override_java_path, override_extra_launch_args, override_custom_env_vars,\n override_mc_memory_max, override_mc_force_fullscreen, override_mc_game_resolution_x, override_mc_game_resolution_y,\n override_hook_pre_launch, override_hook_wrapper, override_hook_post_exit,\n protocol_version, launcher_feature_version\n )\n VALUES (\n $1, $2, $3, $4,\n $5, $6, $7,\n jsonb($8),\n $9, $10, $11, $12,\n $13, $14, $15,\n $16, $17,\n $18, jsonb($19), jsonb($20),\n $21, $22, $23, $24,\n $25, $26, $27,\n $28, $29\n )\n ON CONFLICT (path) DO UPDATE SET\n install_stage = $2,\n name = $3,\n icon_path = $4,\n\n game_version = $5,\n mod_loader = $6,\n mod_loader_version = $7,\n\n groups = jsonb($8),\n\n linked_project_id = $9,\n linked_version_id = $10,\n locked = $11,\n preferred_update_channel = $12,\n\n created = $13,\n modified = $14,\n last_played = $15,\n\n submitted_time_played = $16,\n recent_time_played = $17,\n\n override_java_path = $18,\n override_extra_launch_args = jsonb($19),\n override_custom_env_vars = jsonb($20),\n override_mc_memory_max = $21,\n override_mc_force_fullscreen = $22,\n override_mc_game_resolution_x = $23,\n override_mc_game_resolution_y = $24,\n\n override_hook_pre_launch = $25,\n override_hook_wrapper = $26,\n override_hook_post_exit = $27,\n\n protocol_version = $28,\n launcher_feature_version = $29\n ", - "describe": { - "columns": [], - "parameters": { - "Right": 29 - }, - "nullable": [] - }, - "hash": "22202dacf6b5bade90c909cb4a663a1388ef538e4b80680bce4b668b182b4f42" -} diff --git a/packages/app-lib/.sqlx/query-be21bfd7c36fd8c69dfffe0299eeb7e57590dc75b721dd823d00572f428d0bc5.json b/packages/app-lib/.sqlx/query-6641176d0102e8219a42d2207a06d0494058373b484ca8ac44a02edb7a4cc444.json similarity index 93% rename from packages/app-lib/.sqlx/query-be21bfd7c36fd8c69dfffe0299eeb7e57590dc75b721dd823d00572f428d0bc5.json rename to packages/app-lib/.sqlx/query-6641176d0102e8219a42d2207a06d0494058373b484ca8ac44a02edb7a4cc444.json index 6f16a301a4..47a2543c4f 100644 --- a/packages/app-lib/.sqlx/query-be21bfd7c36fd8c69dfffe0299eeb7e57590dc75b721dd823d00572f428d0bc5.json +++ b/packages/app-lib/.sqlx/query-6641176d0102e8219a42d2207a06d0494058373b484ca8ac44a02edb7a4cc444.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n SELECT\n path, install_stage, launcher_feature_version, name, icon_path,\n game_version, protocol_version, mod_loader, mod_loader_version,\n json(groups) as \"groups!: serde_json::Value\",\n linked_project_id, linked_version_id, locked, preferred_update_channel,\n created, modified, last_played,\n submitted_time_played, recent_time_played,\n override_java_path,\n json(override_extra_launch_args) as \"override_extra_launch_args!: serde_json::Value\", json(override_custom_env_vars) as \"override_custom_env_vars!: serde_json::Value\",\n override_mc_memory_max, override_mc_force_fullscreen, override_mc_game_resolution_x, override_mc_game_resolution_y,\n override_hook_pre_launch, override_hook_wrapper, override_hook_post_exit\n FROM profiles\n WHERE 1=$1", + "query": "\n SELECT\n path, install_stage, launcher_feature_version, name, icon_path,\n game_version, protocol_version, mod_loader, mod_loader_version,\n json(groups) as \"groups!: serde_json::Value\",\n linked_project_id, linked_version_id, locked, preferred_update_channel,\n created, modified, last_played,\n submitted_time_played, recent_time_played,\n override_java_path,\n json(override_extra_launch_args) as \"override_extra_launch_args!: serde_json::Value\", json(override_custom_env_vars) as \"override_custom_env_vars!: serde_json::Value\",\n override_mc_memory_max, override_mc_force_fullscreen, override_mc_game_resolution_x, override_mc_game_resolution_y,\n override_hook_pre_launch, override_hook_wrapper, override_hook_post_exit,\n auto_update_modpack\n FROM profiles\n WHERE 1=$1", "describe": { "columns": [ { @@ -147,6 +147,11 @@ "name": "override_hook_post_exit", "ordinal": 28, "type_info": "Text" + }, + { + "name": "auto_update_modpack", + "ordinal": 29, + "type_info": "Integer" } ], "parameters": { @@ -181,8 +186,9 @@ true, true, true, + true, true ] }, - "hash": "be21bfd7c36fd8c69dfffe0299eeb7e57590dc75b721dd823d00572f428d0bc5" + "hash": "6641176d0102e8219a42d2207a06d0494058373b484ca8ac44a02edb7a4cc444" } diff --git a/packages/app-lib/.sqlx/query-de1887a95766303d16ddcad7fe7f34e0a64101ead329bcc88e55e31b32578a97.json b/packages/app-lib/.sqlx/query-df575bd90e6725b75cce9366ff27fa00083022e0a4b0437a3e613c84e76f2d5c.json similarity index 93% rename from packages/app-lib/.sqlx/query-de1887a95766303d16ddcad7fe7f34e0a64101ead329bcc88e55e31b32578a97.json rename to packages/app-lib/.sqlx/query-df575bd90e6725b75cce9366ff27fa00083022e0a4b0437a3e613c84e76f2d5c.json index 775055ea41..2e1bf890b2 100644 --- a/packages/app-lib/.sqlx/query-de1887a95766303d16ddcad7fe7f34e0a64101ead329bcc88e55e31b32578a97.json +++ b/packages/app-lib/.sqlx/query-df575bd90e6725b75cce9366ff27fa00083022e0a4b0437a3e613c84e76f2d5c.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "\n SELECT\n path, install_stage, launcher_feature_version, name, icon_path,\n game_version, protocol_version, mod_loader, mod_loader_version,\n json(groups) as \"groups!: serde_json::Value\",\n linked_project_id, linked_version_id, locked, preferred_update_channel,\n created, modified, last_played,\n submitted_time_played, recent_time_played,\n override_java_path,\n json(override_extra_launch_args) as \"override_extra_launch_args!: serde_json::Value\", json(override_custom_env_vars) as \"override_custom_env_vars!: serde_json::Value\",\n override_mc_memory_max, override_mc_force_fullscreen, override_mc_game_resolution_x, override_mc_game_resolution_y,\n override_hook_pre_launch, override_hook_wrapper, override_hook_post_exit\n FROM profiles\n WHERE path IN (SELECT value FROM json_each($1))", + "query": "\n SELECT\n path, install_stage, launcher_feature_version, name, icon_path,\n game_version, protocol_version, mod_loader, mod_loader_version,\n json(groups) as \"groups!: serde_json::Value\",\n linked_project_id, linked_version_id, locked, preferred_update_channel,\n created, modified, last_played,\n submitted_time_played, recent_time_played,\n override_java_path,\n json(override_extra_launch_args) as \"override_extra_launch_args!: serde_json::Value\", json(override_custom_env_vars) as \"override_custom_env_vars!: serde_json::Value\",\n override_mc_memory_max, override_mc_force_fullscreen, override_mc_game_resolution_x, override_mc_game_resolution_y,\n override_hook_pre_launch, override_hook_wrapper, override_hook_post_exit,\n auto_update_modpack\n FROM profiles\n WHERE path IN (SELECT value FROM json_each($1))", "describe": { "columns": [ { @@ -147,6 +147,11 @@ "name": "override_hook_post_exit", "ordinal": 28, "type_info": "Text" + }, + { + "name": "auto_update_modpack", + "ordinal": 29, + "type_info": "Integer" } ], "parameters": { @@ -181,8 +186,9 @@ true, true, true, + true, true ] }, - "hash": "de1887a95766303d16ddcad7fe7f34e0a64101ead329bcc88e55e31b32578a97" + "hash": "df575bd90e6725b75cce9366ff27fa00083022e0a4b0437a3e613c84e76f2d5c" } diff --git a/packages/app-lib/.sqlx/query-f3fd719bd016b84c314631fd256b207c848c1d4cf7c30243b5c2cfd6a6f11597.json b/packages/app-lib/.sqlx/query-f3fd719bd016b84c314631fd256b207c848c1d4cf7c30243b5c2cfd6a6f11597.json new file mode 100644 index 0000000000..30621991bc --- /dev/null +++ b/packages/app-lib/.sqlx/query-f3fd719bd016b84c314631fd256b207c848c1d4cf7c30243b5c2cfd6a6f11597.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "\n INSERT INTO profiles (\n path, install_stage, name, icon_path,\n game_version, mod_loader, mod_loader_version,\n groups,\n linked_project_id, linked_version_id, locked, preferred_update_channel,\n created, modified, last_played,\n submitted_time_played, recent_time_played,\n override_java_path, override_extra_launch_args, override_custom_env_vars,\n override_mc_memory_max, override_mc_force_fullscreen, override_mc_game_resolution_x, override_mc_game_resolution_y,\n override_hook_pre_launch, override_hook_wrapper, override_hook_post_exit,\n protocol_version, launcher_feature_version,\n auto_update_modpack\n )\n VALUES (\n $1, $2, $3, $4,\n $5, $6, $7,\n jsonb($8),\n $9, $10, $11, $12,\n $13, $14, $15,\n $16, $17,\n $18, jsonb($19), jsonb($20),\n $21, $22, $23, $24,\n $25, $26, $27,\n $28, $29,\n $30\n )\n ON CONFLICT (path) DO UPDATE SET\n install_stage = $2,\n name = $3,\n icon_path = $4,\n\n game_version = $5,\n mod_loader = $6,\n mod_loader_version = $7,\n\n groups = jsonb($8),\n\n linked_project_id = $9,\n linked_version_id = $10,\n locked = $11,\n preferred_update_channel = $12,\n\n created = $13,\n modified = $14,\n last_played = $15,\n\n submitted_time_played = $16,\n recent_time_played = $17,\n\n override_java_path = $18,\n override_extra_launch_args = jsonb($19),\n override_custom_env_vars = jsonb($20),\n override_mc_memory_max = $21,\n override_mc_force_fullscreen = $22,\n override_mc_game_resolution_x = $23,\n override_mc_game_resolution_y = $24,\n\n override_hook_pre_launch = $25,\n override_hook_wrapper = $26,\n override_hook_post_exit = $27,\n\n protocol_version = $28,\n launcher_feature_version = $29,\n auto_update_modpack = $30\n ", + "describe": { + "columns": [], + "parameters": { + "Right": 30 + }, + "nullable": [] + }, + "hash": "f3fd719bd016b84c314631fd256b207c848c1d4cf7c30243b5c2cfd6a6f11597" +} diff --git a/packages/app-lib/migrations/20260618000000_add-auto-update-modpack.sql b/packages/app-lib/migrations/20260618000000_add-auto-update-modpack.sql new file mode 100644 index 0000000000..a6835f5056 --- /dev/null +++ b/packages/app-lib/migrations/20260618000000_add-auto-update-modpack.sql @@ -0,0 +1 @@ +ALTER TABLE profiles ADD COLUMN auto_update_modpack INTEGER NULL; diff --git a/packages/app-lib/src/api/profile/create.rs b/packages/app-lib/src/api/profile/create.rs index c1a1491c1b..21d8f31548 100644 --- a/packages/app-lib/src/api/profile/create.rs +++ b/packages/app-lib/src/api/profile/create.rs @@ -102,6 +102,7 @@ pub async fn profile_create( wrapper: None, post_exit: None, }, + auto_update_modpack: None, }; let result = async { diff --git a/packages/app-lib/src/api/profile/mod.rs b/packages/app-lib/src/api/profile/mod.rs index a7f2f113ac..b58ba3d537 100644 --- a/packages/app-lib/src/api/profile/mod.rs +++ b/packages/app-lib/src/api/profile/mod.rs @@ -819,6 +819,37 @@ async fn run_credentials( )) })?; + // Auto-update modpack if enabled and an update is available + if profile.auto_update_modpack == Some(true) + && profile.linked_data.is_some() + && let Ok(Some(info)) = crate::state::get_linked_modpack_info( + &profile, + None, + &state.pool, + &state.api_semaphore, + ) + .await + && info.has_update + && let Some(update_version_id) = &info.update_version_id + { + tracing::info!( + "Auto-updating modpack '{}' to version {}", + profile.name, + update_version_id, + ); + if let Err(e) = update::update_managed_modrinth_version( + &profile.path, + update_version_id, + ) + .await + { + tracing::warn!( + "Auto-update of modpack '{}' failed: {e}", + profile.name, + ); + } + } + let pre_launch_hooks = profile .hooks .pre_launch @@ -1178,6 +1209,117 @@ pub async fn add_all_recursive_folder_paths( Ok(()) } +/// Check for a modpack update for a single profile and apply it if auto-update is enabled. +/// Returns `true` if an update was applied. +#[tracing::instrument] +pub async fn auto_update_modpack_if_needed(path: &str) -> crate::Result { + let state = State::get().await?; + + let Some(profile) = get(path).await? else { + return Ok(false); + }; + + if profile.auto_update_modpack != Some(true) + || profile.linked_data.is_none() + { + return Ok(false); + } + + let Some(info) = crate::state::get_linked_modpack_info( + &profile, + None, + &state.pool, + &state.api_semaphore, + ) + .await? + else { + return Ok(false); + }; + + if info.has_update + && let Some(update_version_id) = &info.update_version_id + { + tracing::info!( + "Auto-updating modpack '{}' to version {}", + profile.name, + update_version_id, + ); + update::update_managed_modrinth_version( + &profile.path, + update_version_id, + ) + .await?; + return Ok(true); + } + + Ok(false) +} + +/// Check all profiles for modpack updates and apply them automatically +/// where `auto_update_modpack` is enabled. +#[tracing::instrument] +pub async fn auto_update_all_modpacks() -> crate::Result<()> { + let state = State::get().await?; + let profiles = Profile::get_all(&state.pool).await?; + + for profile in &profiles { + if profile.auto_update_modpack != Some(true) + || profile.linked_data.is_none() + { + continue; + } + + // Skip if the profile is currently running + let is_running = state + .process_manager + .get_all() + .iter() + .any(|p| p.profile_path == profile.path); + + if is_running { + tracing::debug!( + "Skipping auto-update for '{}' — profile is running", + profile.name, + ); + continue; + } + + let Ok(Some(info)) = crate::state::get_linked_modpack_info( + profile, + None, + &state.pool, + &state.api_semaphore, + ) + .await + else { + continue; + }; + + if info.has_update + && let Some(update_version_id) = &info.update_version_id + { + tracing::info!( + "Background auto-updating modpack '{}' to version {}", + profile.name, + update_version_id, + ); + if let Err(e) = update::update_managed_modrinth_version( + &profile.path, + update_version_id, + ) + .await + { + tracing::warn!( + "Background auto-update of modpack '{}' failed: {e}", + profile.name, + ); + } + } + } + + Ok(()) +} + pub fn sanitize_profile_name(input: &str) -> String { input.replace( ['/', '\\', '?', '*', ':', '\'', '\"', '|', '<', '>', '!'], diff --git a/packages/app-lib/src/state/legacy_converter.rs b/packages/app-lib/src/state/legacy_converter.rs index 9404c6ae0f..e765cf267c 100644 --- a/packages/app-lib/src/state/legacy_converter.rs +++ b/packages/app-lib/src/state/legacy_converter.rs @@ -376,6 +376,7 @@ where .and_then(|x| x.wrapper.clone()), post_exit: profile.hooks.and_then(|x| x.post_exit), }, + auto_update_modpack: None, } .upsert(exec) .await?; diff --git a/packages/app-lib/src/state/profiles.rs b/packages/app-lib/src/state/profiles.rs index c30412af22..c2e5bd11a4 100644 --- a/packages/app-lib/src/state/profiles.rs +++ b/packages/app-lib/src/state/profiles.rs @@ -57,6 +57,7 @@ pub struct Profile { pub force_fullscreen: Option, pub game_resolution: Option, pub hooks: Hooks, + pub auto_update_modpack: Option, } #[derive(Serialize, Deserialize, Clone, Copy, Debug, Eq, PartialEq)] @@ -315,6 +316,7 @@ struct ProfileQueryResult { override_hook_post_exit: Option, protocol_version: Option, launcher_feature_version: String, + auto_update_modpack: Option, } impl TryFrom for Profile { @@ -387,6 +389,7 @@ impl TryFrom for Profile { wrapper: x.override_hook_wrapper, post_exit: x.override_hook_post_exit, }, + auto_update_modpack: x.auto_update_modpack.map(|x| x == 1), }) } } @@ -406,7 +409,8 @@ macro_rules! select_profiles_with_predicate { override_java_path, json(override_extra_launch_args) as "override_extra_launch_args!: serde_json::Value", json(override_custom_env_vars) as "override_custom_env_vars!: serde_json::Value", override_mc_memory_max, override_mc_force_fullscreen, override_mc_game_resolution_x, override_mc_game_resolution_y, - override_hook_pre_launch, override_hook_wrapper, override_hook_post_exit + override_hook_pre_launch, override_hook_wrapper, override_hook_post_exit, + auto_update_modpack FROM profiles "# + $predicate, @@ -527,7 +531,8 @@ impl Profile { override_java_path, override_extra_launch_args, override_custom_env_vars, override_mc_memory_max, override_mc_force_fullscreen, override_mc_game_resolution_x, override_mc_game_resolution_y, override_hook_pre_launch, override_hook_wrapper, override_hook_post_exit, - protocol_version, launcher_feature_version + protocol_version, launcher_feature_version, + auto_update_modpack ) VALUES ( $1, $2, $3, $4, @@ -539,7 +544,8 @@ impl Profile { $18, jsonb($19), jsonb($20), $21, $22, $23, $24, $25, $26, $27, - $28, $29 + $28, $29, + $30 ) ON CONFLICT (path) DO UPDATE SET install_stage = $2, @@ -577,7 +583,8 @@ impl Profile { override_hook_post_exit = $27, protocol_version = $28, - launcher_feature_version = $29 + launcher_feature_version = $29, + auto_update_modpack = $30 ", self.path, install_stage, @@ -607,7 +614,8 @@ impl Profile { self.hooks.wrapper, self.hooks.post_exit, self.protocol_version, - launcher_feature_version + launcher_feature_version, + self.auto_update_modpack ) .execute(exec) .await?; diff --git a/packages/assets/generated-icons.ts b/packages/assets/generated-icons.ts index 17c0b991ee..b7f340b38f 100644 --- a/packages/assets/generated-icons.ts +++ b/packages/assets/generated-icons.ts @@ -851,140 +851,141 @@ export const XCircleIcon = _XCircleIcon export const ZoomInIcon = _ZoomInIcon export const ZoomOutIcon = _ZoomOutIcon + export const categoryIconMap: Record = { - adventure: TagCategoryAdventureIcon, - atmosphere: TagCategoryAtmosphereIcon, - audio: TagCategoryAudioIcon, - backpack: TagCategoryBackpackIcon, - badge: TagCategoryBadgeIcon, + 'adventure': TagCategoryAdventureIcon, + 'atmosphere': TagCategoryAtmosphereIcon, + 'audio': TagCategoryAudioIcon, + 'backpack': TagCategoryBackpackIcon, + 'badge': TagCategoryBadgeIcon, 'badge-check': TagCategoryBadgeCheckIcon, 'bed-double': TagCategoryBedDoubleIcon, - blocks: TagCategoryBlocksIcon, - bloom: TagCategoryBloomIcon, + 'blocks': TagCategoryBlocksIcon, + 'bloom': TagCategoryBloomIcon, 'building-2': TagCategoryBuilding2Icon, - camera: TagCategoryCameraIcon, - cartoon: TagCategoryCartoonIcon, - castle: TagCategoryCastleIcon, - challenging: TagCategoryChallengingIcon, - clapperboard: TagCategoryClapperboardIcon, - cloud: TagCategoryCloudIcon, + 'camera': TagCategoryCameraIcon, + 'cartoon': TagCategoryCartoonIcon, + 'castle': TagCategoryCastleIcon, + 'challenging': TagCategoryChallengingIcon, + 'clapperboard': TagCategoryClapperboardIcon, + 'cloud': TagCategoryCloudIcon, 'colored-lighting': TagCategoryColoredLightingIcon, - combat: TagCategoryCombatIcon, - compass: TagCategoryCompassIcon, + 'combat': TagCategoryCombatIcon, + 'compass': TagCategoryCompassIcon, 'core-shaders': TagCategoryCoreShadersIcon, - crown: TagCategoryCrownIcon, - cursed: TagCategoryCursedIcon, - decoration: TagCategoryDecorationIcon, - dices: TagCategoryDicesIcon, - economy: TagCategoryEconomyIcon, - entities: TagCategoryEntitiesIcon, - environment: TagCategoryEnvironmentIcon, - equipment: TagCategoryEquipmentIcon, - fantasy: TagCategoryFantasyIcon, - film: TagCategoryFilmIcon, - flag: TagCategoryFlagIcon, - foliage: TagCategoryFoliageIcon, - fonts: TagCategoryFontsIcon, - food: TagCategoryFoodIcon, - footprints: TagCategoryFootprintsIcon, + 'crown': TagCategoryCrownIcon, + 'cursed': TagCategoryCursedIcon, + 'decoration': TagCategoryDecorationIcon, + 'dices': TagCategoryDicesIcon, + 'economy': TagCategoryEconomyIcon, + 'entities': TagCategoryEntitiesIcon, + 'environment': TagCategoryEnvironmentIcon, + 'equipment': TagCategoryEquipmentIcon, + 'fantasy': TagCategoryFantasyIcon, + 'film': TagCategoryFilmIcon, + 'flag': TagCategoryFlagIcon, + 'foliage': TagCategoryFoliageIcon, + 'fonts': TagCategoryFontsIcon, + 'food': TagCategoryFoodIcon, + 'footprints': TagCategoryFootprintsIcon, 'game-mechanics': TagCategoryGameMechanicsIcon, 'gamepad-2': TagCategoryGamepad2Icon, - gauge: TagCategoryGaugeIcon, - globe: TagCategoryGlobeIcon, + 'gauge': TagCategoryGaugeIcon, + 'globe': TagCategoryGlobeIcon, 'grid-3x3': TagCategoryGrid3x3Icon, - gui: TagCategoryGuiIcon, - handshake: TagCategoryHandshakeIcon, + 'gui': TagCategoryGuiIcon, + 'handshake': TagCategoryHandshakeIcon, 'heart-crack': TagCategoryHeartCrackIcon, 'heart-pulse': TagCategoryHeartPulseIcon, - high: TagCategoryHighIcon, - house: TagCategoryHouseIcon, - items: TagCategoryItemsIcon, + 'high': TagCategoryHighIcon, + 'house': TagCategoryHouseIcon, + 'items': TagCategoryItemsIcon, 'kitchen-sink': TagCategoryKitchenSinkIcon, - library: TagCategoryLibraryIcon, - lightweight: TagCategoryLightweightIcon, - locale: TagCategoryLocaleIcon, - lock: TagCategoryLockIcon, - low: TagCategoryLowIcon, - magic: TagCategoryMagicIcon, - management: TagCategoryManagementIcon, + 'library': TagCategoryLibraryIcon, + 'lightweight': TagCategoryLightweightIcon, + 'locale': TagCategoryLocaleIcon, + 'lock': TagCategoryLockIcon, + 'low': TagCategoryLowIcon, + 'magic': TagCategoryMagicIcon, + 'management': TagCategoryManagementIcon, 'map-pinned': TagCategoryMapPinnedIcon, - medium: TagCategoryMediumIcon, - minigame: TagCategoryMinigameIcon, - mobs: TagCategoryMobsIcon, - modded: TagCategoryModdedIcon, - models: TagCategoryModelsIcon, - multiplayer: TagCategoryMultiplayerIcon, - network: TagCategoryNetworkIcon, - optimization: TagCategoryOptimizationIcon, - palette: TagCategoryPaletteIcon, + 'medium': TagCategoryMediumIcon, + 'minigame': TagCategoryMinigameIcon, + 'mobs': TagCategoryMobsIcon, + 'modded': TagCategoryModdedIcon, + 'models': TagCategoryModelsIcon, + 'multiplayer': TagCategoryMultiplayerIcon, + 'network': TagCategoryNetworkIcon, + 'optimization': TagCategoryOptimizationIcon, + 'palette': TagCategoryPaletteIcon, 'path-tracing': TagCategoryPathTracingIcon, 'paw-print': TagCategoryPawPrintIcon, - pbr: TagCategoryPbrIcon, - pickaxe: TagCategoryPickaxeIcon, - potato: TagCategoryPotatoIcon, - quests: TagCategoryQuestsIcon, - realistic: TagCategoryRealisticIcon, - reflections: TagCategoryReflectionsIcon, + 'pbr': TagCategoryPbrIcon, + 'pickaxe': TagCategoryPickaxeIcon, + 'potato': TagCategoryPotatoIcon, + 'quests': TagCategoryQuestsIcon, + 'realistic': TagCategoryRealisticIcon, + 'reflections': TagCategoryReflectionsIcon, 'refresh-ccw': TagCategoryRefreshCcwIcon, - screenshot: TagCategoryScreenshotIcon, + 'screenshot': TagCategoryScreenshotIcon, 'scroll-text': TagCategoryScrollTextIcon, 'semi-realistic': TagCategorySemiRealisticIcon, - shadows: TagCategoryShadowsIcon, - shield: TagCategoryShieldIcon, - simplistic: TagCategorySimplisticIcon, - skull: TagCategorySkullIcon, - social: TagCategorySocialIcon, - square: TagCategorySquareIcon, - storage: TagCategoryStorageIcon, - sword: TagCategorySwordIcon, - swords: TagCategorySwordsIcon, - target: TagCategoryTargetIcon, - technology: TagCategoryTechnologyIcon, - terminal: TagCategoryTerminalIcon, - theater: TagCategoryTheaterIcon, - themed: TagCategoryThemedIcon, - transportation: TagCategoryTransportationIcon, + 'shadows': TagCategoryShadowsIcon, + 'shield': TagCategoryShieldIcon, + 'simplistic': TagCategorySimplisticIcon, + 'skull': TagCategorySkullIcon, + 'social': TagCategorySocialIcon, + 'square': TagCategorySquareIcon, + 'storage': TagCategoryStorageIcon, + 'sword': TagCategorySwordIcon, + 'swords': TagCategorySwordsIcon, + 'target': TagCategoryTargetIcon, + 'technology': TagCategoryTechnologyIcon, + 'terminal': TagCategoryTerminalIcon, + 'theater': TagCategoryTheaterIcon, + 'themed': TagCategoryThemedIcon, + 'transportation': TagCategoryTransportationIcon, 'tree-pine': TagCategoryTreePineIcon, - trophy: TagCategoryTrophyIcon, - tweaks: TagCategoryTweaksIcon, - users: TagCategoryUsersIcon, - utility: TagCategoryUtilityIcon, + 'trophy': TagCategoryTrophyIcon, + 'tweaks': TagCategoryTweaksIcon, + 'users': TagCategoryUsersIcon, + 'utility': TagCategoryUtilityIcon, 'vanilla-like': TagCategoryVanillaLikeIcon, 'wand-sparkles': TagCategoryWandSparklesIcon, 'wifi-off': TagCategoryWifiOffIcon, - worldgen: TagCategoryWorldgenIcon, - zap: TagCategoryZapIcon, + 'worldgen': TagCategoryWorldgenIcon, + 'zap': TagCategoryZapIcon, } export const loaderIconMap: Record = { - babric: TagLoaderBabricIcon, + 'babric': TagLoaderBabricIcon, 'bta-babric': TagLoaderBtaBabricIcon, - bukkit: TagLoaderBukkitIcon, - bungeecord: TagLoaderBungeecordIcon, - canvas: TagLoaderCanvasIcon, - datapack: TagLoaderDatapackIcon, - fabric: TagLoaderFabricIcon, - folia: TagLoaderFoliaIcon, - forge: TagLoaderForgeIcon, - geyser: TagLoaderGeyserIcon, - iris: TagLoaderIrisIcon, + 'bukkit': TagLoaderBukkitIcon, + 'bungeecord': TagLoaderBungeecordIcon, + 'canvas': TagLoaderCanvasIcon, + 'datapack': TagLoaderDatapackIcon, + 'fabric': TagLoaderFabricIcon, + 'folia': TagLoaderFoliaIcon, + 'forge': TagLoaderForgeIcon, + 'geyser': TagLoaderGeyserIcon, + 'iris': TagLoaderIrisIcon, 'java-agent': TagLoaderJavaAgentIcon, 'legacy-fabric': TagLoaderLegacyFabricIcon, - liteloader: TagLoaderLiteloaderIcon, - minecraft: TagLoaderMinecraftIcon, - modloader: TagLoaderModloaderIcon, - mrpack: TagLoaderMrpackIcon, - neoforge: TagLoaderNeoforgeIcon, - nilloader: TagLoaderNilloaderIcon, - optifine: TagLoaderOptifineIcon, - ornithe: TagLoaderOrnitheIcon, - paper: TagLoaderPaperIcon, - purpur: TagLoaderPurpurIcon, - quilt: TagLoaderQuiltIcon, - rift: TagLoaderRiftIcon, - spigot: TagLoaderSpigotIcon, - sponge: TagLoaderSpongeIcon, - vanilla: TagLoaderVanillaIcon, - velocity: TagLoaderVelocityIcon, - waterfall: TagLoaderWaterfallIcon, + 'liteloader': TagLoaderLiteloaderIcon, + 'minecraft': TagLoaderMinecraftIcon, + 'modloader': TagLoaderModloaderIcon, + 'mrpack': TagLoaderMrpackIcon, + 'neoforge': TagLoaderNeoforgeIcon, + 'nilloader': TagLoaderNilloaderIcon, + 'optifine': TagLoaderOptifineIcon, + 'ornithe': TagLoaderOrnitheIcon, + 'paper': TagLoaderPaperIcon, + 'purpur': TagLoaderPurpurIcon, + 'quilt': TagLoaderQuiltIcon, + 'rift': TagLoaderRiftIcon, + 'spigot': TagLoaderSpigotIcon, + 'sponge': TagLoaderSpongeIcon, + 'vanilla': TagLoaderVanillaIcon, + 'velocity': TagLoaderVelocityIcon, + 'waterfall': TagLoaderWaterfallIcon, }