From 9879eb2e72f82ad7b865b45da2f7077ec621ae0b Mon Sep 17 00:00:00 2001 From: Maciek <108873945+rozwader@users.noreply.github.com> Date: Wed, 16 Sep 2026 18:30:20 +0200 Subject: [PATCH] fix: fixed versions metadata not healing when unable to parse or read (#835) --- packages/oneclient_mc/src/install.rs | 127 +++++++++++++------------- packages/oneclient_mc/src/manifest.rs | 22 +++-- 2 files changed, 76 insertions(+), 73 deletions(-) diff --git a/packages/oneclient_mc/src/install.rs b/packages/oneclient_mc/src/install.rs index d29184f4..6f3bf560 100644 --- a/packages/oneclient_mc/src/install.rs +++ b/packages/oneclient_mc/src/install.rs @@ -724,22 +724,60 @@ pub async fn download_version_info( .join(&version_id) .join(format!("{version_id}.json")); - let result = if path.exists() && !force { - let data = polyio::read(&path).await?; - serde_json::from_slice(&data)? - } else { - tracing::debug!( - version_id = %version_id, - "downloading Minecraft version metadata" - ); + if path.exists() && !force { + match polyio::read_json::(&path).await { + Ok(cached) => return Ok(cached), + Err(err) => { + tracing::warn!( + path = %path.display(), + "cached version metadata is unusable, redownloading: {err}" + ); + } + } + } - let version_url = version.url.parse().map_err(McError::Url)?; + tracing::debug!( + version_id = %version_id, + "downloading Minecraft version metadata" + ); + + let version_url = version.url.parse().map_err(McError::Url)?; + let requester = ctx.net.clone(); + let mut info: VersionInfo = match progress { + Some(progress) => { + progress + .run_child( + format!("Version metadata ({version_id})"), + 1, + TaskCategory::Metadata, + |child| { + let requester = requester.clone(); + async move { + child.set_progress(0, Some(1)); + let result = requester + .send_json(Method::GET, version_url, None, &[]) + .await + .map_err(McError::from)?; + child.set_progress(1, Some(1)); + Ok::(result) + } + }) + .await? + } + None => requester + .send_json(Method::GET, version_url, None, &[]) + .await + .map_err(McError::from)?, + }; + + if let Some(loader) = loader { + let loader_url = loader.url.parse().map_err(McError::Url)?; let requester = ctx.net.clone(); - let mut info: VersionInfo = match progress { + let partial: interfrost::api::modded::PartialVersionInfo = match progress { Some(progress) => { progress .run_child( - format!("Version metadata ({version_id})"), + format!("Loader metadata ({version_id})"), 1, TaskCategory::Metadata, |child| { @@ -747,76 +785,39 @@ pub async fn download_version_info( async move { child.set_progress(0, Some(1)); let result = requester - .send_json(Method::GET, version_url, None, &[]) + .send_json(Method::GET, loader_url, None, &[]) .await .map_err(McError::from)?; child.set_progress(1, Some(1)); - Ok::(result) + Ok::( + result, + ) } }) .await? } None => requester - .send_json(Method::GET, version_url, None, &[]) + .send_json(Method::GET, loader_url, None, &[]) .await .map_err(McError::from)?, }; - if let Some(loader) = loader { - let loader_url = loader.url.parse().map_err(McError::Url)?; - let requester = ctx.net.clone(); - let partial: interfrost::api::modded::PartialVersionInfo = match progress { - Some(progress) => { - progress - .run_child( - format!("Loader metadata ({version_id})"), - 1, - TaskCategory::Metadata, - |child| { - let requester = requester.clone(); - async move { - child.set_progress(0, Some(1)); - let result = requester - .send_json(Method::GET, loader_url, None, &[]) - .await - .map_err(McError::from)?; - child.set_progress(1, Some(1)); - Ok::( - result, - ) - } - }) - .await? - } - None => requester - .send_json(Method::GET, loader_url, None, &[]) - .await - .map_err(McError::from)?, - }; - - let legacy_args = info.minecraft_arguments.clone(); - info = interfrost::api::modded::merge_partial_version(partial, info); - if info.minecraft_arguments.is_none() { - info.minecraft_arguments = legacy_args; - } - - for lib in &mut info.libraries { - lib.name = lib.name.replace("${interpulse.gameVersion}", &version.id); - } + let legacy_args = info.minecraft_arguments.clone(); + info = interfrost::api::modded::merge_partial_version(partial, info); + if info.minecraft_arguments.is_none() { + info.minecraft_arguments = legacy_args; } - info.id.clone_from(&version_id); - - if let Some(parent) = path.parent() { - polyio::create_dir_all(parent).await?; + for lib in &mut info.libraries { + lib.name = lib.name.replace("${interpulse.gameVersion}", &version.id); } + } - polyio::write(&path, &serde_json::to_vec(&info)?).await?; + info.id.clone_from(&version_id); - info - }; + polyio::write_json_atomic(&path, &info).await?; - Ok(result) + Ok(info) } #[tracing::instrument(skip_all, level = "debug")] diff --git a/packages/oneclient_mc/src/manifest.rs b/packages/oneclient_mc/src/manifest.rs index 8272a98d..5a8a1009 100644 --- a/packages/oneclient_mc/src/manifest.rs +++ b/packages/oneclient_mc/src/manifest.rs @@ -103,27 +103,29 @@ impl MetadataStore { let mut save_file = false; let mut metadata = Self::default(); - if let Ok(bytes) = polyio::read(&path).await { - if let Ok(inner) = serde_json::from_slice::(&bytes) { + match polyio::read_json::(&path).await { + Ok(inner) => { metadata.inner = inner; if metadata.refetch_errored(ctx).await > 0 { save_file = true; } - } else { + } + Err(err) => { + if path.exists() { + tracing::warn!( + path = %path.display(), + "cached metadata manifest is unusable, refetching: {err}" + ); + } + metadata.fetch_all(ctx).await; save_file = true; } - } else { - metadata.fetch_all(ctx).await; - save_file = true; } if save_file { - if let Some(parent) = path.parent() { - polyio::create_dir_all(parent).await?; - } - polyio::write(&path, &serde_json::to_vec(&metadata.inner)?).await?; + polyio::write_json_atomic(&path, &metadata.inner).await?; } *self = metadata;