From 5b369f247b9f18819331dc98f3547a2291599625 Mon Sep 17 00:00:00 2001 From: "Calum H. (IMB11)" Date: Mon, 3 Aug 2026 19:09:02 +0100 Subject: [PATCH] fix: show "inspecting modpack" Shows when determining external files for modpacks which have many overrides/over gb of content files. --- packages/app-lib/src/api/pack/install_from.rs | 53 +++++++-- packages/app-lib/src/event/mod.rs | 3 + packages/app-lib/src/install/runner.rs | 110 ++++++++++++++---- 3 files changed, 129 insertions(+), 37 deletions(-) diff --git a/packages/app-lib/src/api/pack/install_from.rs b/packages/app-lib/src/api/pack/install_from.rs index c894b94ad2..a4fbf9e839 100644 --- a/packages/app-lib/src/api/pack/install_from.rs +++ b/packages/app-lib/src/api/pack/install_from.rs @@ -1,5 +1,7 @@ use crate::State; use crate::data::ModLoader; +use crate::event::LoadingBarType; +use crate::event::emit::{emit_loading, init_loading}; use crate::install::{ InstallErrorContext, InstallPhaseDetails, InstallPhaseId, InstallProgress, InstallProgressReporter, @@ -18,7 +20,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::future::Future; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::pin::Pin; #[derive(Serialize, Deserialize, Eq, PartialEq)] @@ -153,6 +155,17 @@ pub struct CreatePack { const MAX_LOCAL_FILE_HASH_LOOKUP_SIZE: u64 = 1024 * 1024 * 1024; +pub(crate) fn get_local_pack_instance(path: &Path) -> CreatePackInstance { + CreatePackInstance { + name: path + .file_stem() + .unwrap_or_default() + .to_string_lossy() + .to_string(), + ..Default::default() + } +} + #[derive(Clone, Debug)] pub struct CreatePackDescription { pub icon: Option, @@ -182,11 +195,23 @@ pub async fn get_instance_from_pack( ..Default::default() }), CreatePackLocation::FromFile { path } => { - let file_name = path - .file_stem() - .unwrap_or_default() - .to_string_lossy() - .to_string(); + let mut instance = get_local_pack_instance(&path); + let inspection = init_loading( + LoadingBarType::PackImport { + pack_name: instance.name.clone(), + }, + 100.0, + "Inspecting modpack", + ) + .await + .ok(); + if let Some(inspection) = &inspection { + let _ = emit_loading( + inspection, + 1.0, + Some("Reading local modpack"), + ); + } let is_known_file = if tokio::fs::metadata(&path).await?.len() <= MAX_LOCAL_FILE_HASH_LOOKUP_SIZE @@ -214,6 +239,13 @@ pub async fn get_instance_from_pack( } else { false }; + if let Some(inspection) = &inspection { + let _ = emit_loading( + inspection, + 39.0, + Some("Inspecting modpack files"), + ); + } let external_files_in_modpack = super::install_mrpack::get_external_files_from_mrpack( @@ -221,12 +253,9 @@ pub async fn get_instance_from_pack( ) .await?; - Ok(CreatePackInstance { - name: file_name, - unknown_file: !is_known_file, - external_files_in_modpack, - ..Default::default() - }) + instance.unknown_file = !is_known_file; + instance.external_files_in_modpack = external_files_in_modpack; + Ok(instance) } } } diff --git a/packages/app-lib/src/event/mod.rs b/packages/app-lib/src/event/mod.rs index 3a1dabb954..af1d5fe69f 100644 --- a/packages/app-lib/src/event/mod.rs +++ b/packages/app-lib/src/event/mod.rs @@ -157,6 +157,9 @@ pub enum LoadingBarType { pack_id: Option, pack_version: Option, }, + PackImport { + pack_name: String, + }, MinecraftDownload { instance_id: String, instance_name: String, diff --git a/packages/app-lib/src/install/runner.rs b/packages/app-lib/src/install/runner.rs index 801d957a11..1c2446aa10 100644 --- a/packages/app-lib/src/install/runner.rs +++ b/packages/app-lib/src/install/runner.rs @@ -16,6 +16,7 @@ use crate::ErrorKind; use crate::api::pack::install_from::{ CreatePackLocation, generate_pack_from_file, generate_pack_from_version_id_with_reporter, get_instance_from_pack, + get_local_pack_instance, }; use crate::api::pack::install_mrpack::install_zipped_mrpack_files_with_reporter; use crate::event::InstancePayloadType; @@ -163,16 +164,6 @@ pub async fn retry_job(job_id: Uuid) -> crate::Result { job.state.progress.phase = InstallPhaseId::PreparingInstance; job.state.progress.progress = None; job.state.progress.details = InstallPhaseDetails::Empty; - if let Err(error) = prepare_initial_instance(&mut job.state, &state).await { - if let Err(cleanup_error) = - recovery::apply_cleanup(&job.state, &state).await - { - tracing::error!( - "Error cleaning up install job {job_id} retry preparation: {cleanup_error}" - ); - } - return Err(error); - } job.state.record_event(InstallJobEventKind::JobQueued { kind: job.state.request.kind(), }); @@ -197,6 +188,42 @@ pub async fn retry_job(job_id: Uuid) -> crate::Result { return Err(error); } }; + emit_install_job(&record.snapshot()).await?; + + if let Err(error) = prepare_initial_instance(&mut job.state, &state).await { + let error_view = install_error_view( + job.state.progress.phase, + &error, + job.state.context.clone(), + ); + if let Err(terminal_error) = + terminalize_failed_job(job_id, job.state, error_view, &state).await + { + tracing::error!( + "Failed to terminalize retried install job {job_id}: {terminal_error}" + ); + } + return Err(error); + } + let record = match store::update_state(job_id, &job.state, &state).await { + Ok(record) => record, + Err(error) => { + let error_view = install_error_view( + job.state.progress.phase, + &error, + job.state.context.clone(), + ); + if let Err(terminal_error) = + terminalize_failed_job(job_id, job.state, error_view, &state) + .await + { + tracing::error!( + "Failed to terminalize retried install job {job_id}: {terminal_error}" + ); + } + return Err(error); + } + }; if let Err(error) = lock_existing_instance_if_needed(&job.state, &state).await { @@ -337,31 +364,39 @@ async fn start(request: InstallRequest) -> crate::Result { let state = State::get().await?; let id = Uuid::new_v4(); let mut job_state = InstallJobState::new(request); + set_initial_display(&mut job_state); + let record = + store::insert(id, &job_state, InstallJobStatus::Queued, &state).await?; + emit_install_job(&record.snapshot()).await?; + if let Err(error) = prepare_initial_instance(&mut job_state, &state).await { - if let Err(cleanup_error) = - recovery::apply_cleanup(&job_state, &state).await + let error_view = install_error_view( + job_state.progress.phase, + &error, + job_state.context.clone(), + ); + if let Err(terminal_error) = + terminalize_failed_job(id, job_state, error_view, &state).await { tracing::error!( - "Error cleaning up install job preparation: {cleanup_error}" + "Failed to terminalize install job {id} after setup error: {terminal_error}" ); } return Err(error); } - let record = match store::insert( - id, - &job_state, - InstallJobStatus::Queued, - &state, - ) - .await - { + let record = match store::update_state(id, &job_state, &state).await { Ok(record) => record, Err(error) => { - if let Err(cleanup_error) = - recovery::apply_cleanup(&job_state, &state).await + let error_view = install_error_view( + job_state.progress.phase, + &error, + job_state.context.clone(), + ); + if let Err(terminal_error) = + terminalize_failed_job(id, job_state, error_view, &state).await { tracing::error!( - "Error cleaning up untracked install job {id}: {cleanup_error}" + "Failed to terminalize install job {id} after setup error: {terminal_error}" ); } return Err(error); @@ -422,7 +457,12 @@ async fn prepare_initial_instance( location, post_install_edit, } => { - let preview = get_instance_from_pack(location).await?; + let preview = match location { + CreatePackLocation::FromFile { path } => { + get_local_pack_instance(&path) + } + location => get_instance_from_pack(location).await?, + }; let name = post_install_edit .as_ref() .and_then(|edit| edit.name.clone()) @@ -1443,6 +1483,26 @@ fn set_display( job_state.display = Some(InstallJobDisplay { title, icon }); } +fn set_initial_display(job_state: &mut InstallJobState) { + let display = match &job_state.request { + InstallRequest::CreateModpackInstance { location, .. } => { + match location { + CreatePackLocation::FromVersionId { + title, icon_url, .. + } => Some((title.clone(), icon_url.clone())), + CreatePackLocation::FromFile { path } => { + Some((get_local_pack_instance(path).name, None)) + } + } + } + _ => None, + }; + + if let Some((title, icon)) = display { + set_display(job_state, title, icon); + } +} + fn install_error_view( phase: InstallPhaseId, error: &crate::Error,