diff --git a/crates/dtmm/src/controller/game.rs b/crates/dtmm/src/controller/game.rs index 68f9269..45ed27e 100644 --- a/crates/dtmm/src/controller/game.rs +++ b/crates/dtmm/src/controller/game.rs @@ -525,6 +525,7 @@ where ))] pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { let state = Arc::new(state); + let bundle_dir = state.game_dir.join("bundle"); let (_, game_info, deployment_info) = tokio::try_join!( async { @@ -565,16 +566,46 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { .map(|i| game_info.last_updated > i.timestamp) .unwrap_or(false) { - eyre::bail!("Game was updated since last mod deployment. Please reset first."); + tracing::warn!( + "Game was updated since last mod deployment. \ + Attempting to reconcile game files." + ); + + tokio::try_join!( + async { + let path = bundle_dir.join(BUNDLE_DATABASE_NAME); + let backup_path = path.with_extension("data.bak"); + + fs::copy(&path, &backup_path) + .await + .wrap_err("Failed to re-create backup for bundle database.") + }, + async { + let path = bundle_dir.join(BOOT_BUNDLE_NAME); + let backup_path = path.with_extension("bak"); + + fs::copy(&path, &backup_path) + .await + .wrap_err("Failed to re-create backup for boot bundle") + } + ) + .with_suggestion(|| { + "Reset the game using 'Reset Game', then verify game files.".to_string() + })?; + + tracing::info!( + "Successfully re-created game file backups. \ + Continuing mod deployment." + ); } } check_mod_order(&state)?; tracing::info!( - "Deploying {} mods to {}", + "Deploying {} mods to '{}'.", state.mods.iter().filter(|i| i.enabled).count(), - state.game_dir.join("bundle").display() + bundle_dir.display() ); tracing::info!("Build mod bundles"); @@ -589,36 +620,32 @@ pub(crate) async fn deploy_mods(state: ActionState) -> Result<()> { bundles.append(&mut more_bundles); if let Some(info) = &deployment_info { - let bundle_dir = Arc::new(state.game_dir.join("bundle")); - let tasks = info - .bundles - .iter() - .cloned() - .map(|v| (v, bundle_dir.clone())) - .filter_map(|(file_name, bundle_dir)| { - let contains = bundles.iter().any(|b2| { - let name = b2.name().to_murmur64().to_string(); - file_name == name - }); - - if !contains { - let task = async move { - let path = bundle_dir.join(&file_name); - - tracing::debug!("Removing unused bundle '{}'", file_name); - - if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { - format!("Failed to remove unused bundle '{}'", path.display()) - }) { - tracing::error!("{:?}", err); - } - }; - Some(task) - } else { - None - } + let bundle_dir = Arc::new(bundle_dir); + let tasks = info.bundles.iter().cloned().filter_map(|file_name| { + let contains = bundles.iter().any(|b2| { + let name = b2.name().to_murmur64().to_string(); + file_name == name }); + if !contains { + let bundle_dir = bundle_dir.clone(); + let task = async move { + let path = bundle_dir.join(&file_name); + + tracing::debug!("Removing unused bundle '{}'", file_name); + + if let Err(err) = fs::remove_file(&path).await.wrap_err_with(|| { + format!("Failed to remove unused bundle '{}'", path.display()) + }) { + tracing::error!("{:?}", err); + } + }; + Some(task) + } else { + None + } + }); + futures::future::join_all(tasks).await; } diff --git a/crates/dtmt/src/cmd/util.rs b/crates/dtmt/src/cmd/util.rs index c783ed1..c233425 100644 --- a/crates/dtmt/src/cmd/util.rs +++ b/crates/dtmt/src/cmd/util.rs @@ -8,7 +8,7 @@ use tokio::fs; use tokio_stream::wrappers::ReadDirStream; #[tracing::instrument] -pub async fn foo

(path: P) -> Vec +pub async fn process_path

(path: P) -> Vec where P: AsRef + std::fmt::Debug, { @@ -98,7 +98,10 @@ where I: Iterator + std::fmt::Debug, { let tasks = paths.map(|p| async move { - match tokio::spawn(async move { foo(&p).await }).await { + // Clippy doesn't understand that the block here is required to `move` in the reference. + // The task is spawned to make sure tokio can distribute these over threads. + #[allow(clippy::redundant_async_block)] + match tokio::spawn(async move { process_path(&p).await }).await { Ok(paths) => paths, Err(err) => { tracing::error!(%err, "failed to spawn task to resolve bundle paths"); @@ -111,6 +114,9 @@ where results.into_iter().flatten().collect() } +// `tracing::instrument` generates code that triggers this warning. +// Not much we can do to prevent that. +#[allow(clippy::let_with_type_underscore)] #[tracing::instrument(skip_all)] pub fn resolve_bundle_paths(paths: I) -> impl Stream where @@ -129,12 +135,12 @@ mod tests { use tempfile::tempdir; use tokio::process::Command; - use super::foo; + use super::process_path; #[tokio::test] async fn resolve_single_file() { let path = PathBuf::from("foo"); - let paths = foo(&path).await; + let paths = process_path(&path).await; assert_eq!(paths.len(), 1); assert_eq!(paths[0], path); } @@ -142,7 +148,7 @@ mod tests { #[tokio::test] async fn resolve_empty_directory() { let dir = tempdir().expect("failed to create temporary directory"); - let paths = foo(dir).await; + let paths = process_path(dir).await; assert!(paths.is_empty()); } @@ -170,7 +176,7 @@ mod tests { .await .expect("failed to create temporary files"); - let paths = foo(dir).await; + let paths = process_path(dir).await; assert_eq!(bundle_names.len(), paths.len()); diff --git a/lib/luajit2-sys b/lib/luajit2-sys index 18797c4..11c4edd 160000 --- a/lib/luajit2-sys +++ b/lib/luajit2-sys @@ -1 +1 @@ -Subproject commit 18797c4d2a53834210fd096dd39195ce7f2bce21 +Subproject commit 11c4eddaa4667ea7fffad40b034cf3fcb19fbdd3