Fix stale version fetching

This commit is contained in:
Wyvest 2026-04-01 04:16:13 -04:00
parent ad4c0bd361
commit c6f347f98c
No known key found for this signature in database
5 changed files with 96 additions and 69 deletions

View file

@ -325,41 +325,8 @@ export type VersionType =
*/
"old_beta"
const ARGS_MAP = { 'oneclient':'{"extractBundleOverrides":["bundle_path","cluster_id"],"isBundleSyncing":[],"downloadPackageFromBundle":["package","cluster_id","bundle_name","skip_compatibility"],"installUpdate":[],"getClustersGroupedByMajor":[],"getBundlesFor":["cluster_id"],"updateBundlePackages":["cluster_id"],"checkForUpdate":[],"cacheArt":["path"],"copyClusterContent":["source_id","target_id"],"refreshArt":["path"],"getVersions":[]}', 'events':'{"ingress":["event"],"process":["event"],"message":["event"]}', 'debug':'{"getOsVersion":[],"getBuildTimestamp":[],"getArch":[],"hasDevSettings":[],"isInDev":[],"getPackageVersion":[],"getType":[],"getLocale":[],"getFullDebugInfo":[],"getPlatform":[],"getFullDebugInfoParsedString":[],"getFamily":[],"getFullDebugInfoParsed":[],"getOsDistro":[],"openDevTools":[],"getGitCommitHash":[]}', 'core':'{"isClusterRunning":["cluster_id"],"getGameVersions":[],"searchPackages":["provider","query"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"openMsaLogin":[],"removePackage":["cluster_id","package_hash"],"setDefaultUser":["uuid"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"killProcess":["pid"],"getPackage":["provider","slug"],"convertUsernameUUID":["username_uuid"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"getClusters":[],"fetchMinecraftProfile":["uuid"],"getLogs":["id"],"getProfileOrDefault":["name"],"getRunningProcessesByClusterId":["cluster_id"],"getLinkedPackages":["cluster_id"],"getGlobalProfile":[],"refreshAccounts":[],"getPackageBody":["provider","body"],"togglePackage":["cluster_id","package_hash"],"createSettingsProfile":["name"],"getClusterById":["id"],"getWorlds":["id"],"createCluster":["options"],"installModpack":["modpack","cluster_id"],"open":["input"],"getLoadersForVersion":["mc_version"],"refreshAccount":["uuid"],"getScreenshots":["id"],"launchCluster":["id","uuid","search_for_java"],"setClusterStage":["id","stage"],"getLogByName":["id","name"],"getProcessLogTail":["id","max_lines"],"getUser":["uuid"],"writeSettings":["setting"],"fetchLoggedInProfile":["access_token"],"updateClusterProfile":["name","profile"],"removeCape":["access_token"],"readSettings":[],"updateClusterById":["id","request"],"getDefaultUser":["fallback"],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"getRunningProcesses":[],"getMultiplePackages":["provider","slugs"],"changeSkin":["access_token","skin_url","skin_variant"],"getUsers":[],"syncCluster":["cluster_id"],"getUsersFromAuthor":["provider","author"],"changeCape":["access_token","cape_uuid"],"removeUser":["uuid"],"setDiscordRPCMessage":["message"],"removeCluster":["id"]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}' }
export type Router = { 'debug': { openDevTools: () => Promise<void>,
isInDev: () => Promise<boolean>,
getArch: () => Promise<string>,
getFamily: () => Promise<string>,
getLocale: () => Promise<string>,
getType: () => Promise<string>,
getPlatform: () => Promise<string>,
getOsVersion: () => Promise<string>,
getOsDistro: () => Promise<string>,
getGitCommitHash: () => Promise<string>,
getBuildTimestamp: () => Promise<string>,
getPackageVersion: () => Promise<string>,
getFullDebugInfo: () => Promise<DebugInfoData>,
getFullDebugInfoParsed: () => Promise<DebugInfoParsedLine[]>,
getFullDebugInfoParsedString: () => Promise<string>,
hasDevSettings: () => Promise<boolean> },
'oneclient': { getClustersGroupedByMajor: () => Promise<Partial<{ [key in number]: ClusterModel[] }>>,
getBundlesFor: (clusterId: number) => Promise<ModpackArchive[]>,
getVersions: () => Promise<OnlineClusterManifest>,
extractBundleOverrides: (bundlePath: string, clusterId: number) => Promise<null>,
checkForUpdate: () => Promise<Update | null>,
installUpdate: () => Promise<null>,
downloadPackageFromBundle: (package: ModpackFileKind, clusterId: number, bundleName: string, skipCompatibility: boolean | null) => Promise<PackageModel>,
updateBundlePackages: (clusterId: number) => Promise<ApplyBundleUpdatesResult>,
isBundleSyncing: () => Promise<boolean>,
cacheArt: (path: string) => Promise<string>,
refreshArt: (path: string) => Promise<null>,
copyClusterContent: (sourceId: number, targetId: number) => Promise<CopyClusterResult> },
'events': { ingress: (event: IngressPayload) => Promise<void>,
message: (event: MessagePayload) => Promise<void>,
process: (event: ProcessPayload) => Promise<void> },
'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> },
'core': { getClusters: () => Promise<ClusterModel[]>,
const ARGS_MAP = { 'oneclient':'{"getBundlesFor":["cluster_id"],"installUpdate":[],"cacheArt":["path"],"extractBundleOverrides":["bundle_path","cluster_id"],"getVersions":[],"updateBundlePackages":["cluster_id"],"isBundleSyncing":[],"checkForUpdate":[],"refreshArt":["path"],"copyClusterContent":["source_id","target_id"],"downloadPackageFromBundle":["package","cluster_id","bundle_name","skip_compatibility"],"getClustersGroupedByMajor":[]}', 'folders':'{"fromCluster":["folder_name"],"openCluster":["folder_name"]}', 'debug':'{"getFullDebugInfo":[],"getArch":[],"getFullDebugInfoParsed":[],"openDevTools":[],"getOsDistro":[],"getLocale":[],"getGitCommitHash":[],"isInDev":[],"getOsVersion":[],"getBuildTimestamp":[],"getFamily":[],"getFullDebugInfoParsedString":[],"hasDevSettings":[],"getPlatform":[],"getPackageVersion":[],"getType":[]}', 'core':'{"isClusterRunning":["cluster_id"],"readSettings":[],"launchCluster":["id","uuid","search_for_java"],"writeSettings":["setting"],"getPackageVersions":["provider","slug","mc_version","loader","offset","limit"],"getLinkedPackages":["cluster_id"],"getLogs":["id"],"setClusterStage":["id","stage"],"killProcess":["pid"],"updateClusterById":["id","request"],"refreshAccounts":[],"getProcessLogTail":["id","max_lines"],"downloadExternalPackage":["package","cluster_id","force","skip_compatibility"],"getGameVersions":[],"getWorlds":["id"],"removePackage":["cluster_id","package_hash"],"togglePackage":["cluster_id","package_hash"],"openMsaLogin":[],"getLogByName":["id","name"],"installModpack":["modpack","cluster_id"],"createCluster":["options"],"getProfileOrDefault":["name"],"getPackageBody":["provider","body"],"removeUser":["uuid"],"getUsersFromAuthor":["provider","author"],"fetchMinecraftProfile":["uuid"],"fetchLoggedInProfile":["access_token"],"uploadSkinBytes":["access_token","skin_data","image_format","skin_variant"],"getPackage":["provider","slug"],"searchPackages":["provider","query"],"setDefaultUser":["uuid"],"getLoadersForVersion":["mc_version"],"createSettingsProfile":["name"],"getMultiplePackages":["provider","slugs"],"syncCluster":["cluster_id"],"changeSkin":["access_token","skin_url","skin_variant"],"getUser":["uuid"],"getDefaultUser":["fallback"],"getRunningProcesses":[],"downloadPackage":["provider","package_id","version_id","cluster_id","skip_compatibility"],"removeCape":["access_token"],"setDiscordRPCMessage":["message"],"getRunningProcessesByClusterId":["cluster_id"],"open":["input"],"getClusterById":["id"],"updateClusterProfile":["name","profile"],"changeCape":["access_token","cape_uuid"],"convertUsernameUUID":["username_uuid"],"removeCluster":["id"],"getScreenshots":["id"],"getClusters":[],"getUsers":[],"getGlobalProfile":[],"refreshAccount":["uuid"]}', 'events':'{"process":["event"],"ingress":["event"],"message":["event"]}' }
export type Router = { 'core': { getClusters: () => Promise<ClusterModel[]>,
getClusterById: (id: number) => Promise<ClusterModel | null>,
removeCluster: (id: number) => Promise<null>,
createCluster: (options: CreateCluster) => Promise<ClusterModel>,
@ -412,7 +379,40 @@ changeCape: (accessToken: string, capeUuid: string) => Promise<MojangFullPlayerP
removeCape: (accessToken: string) => Promise<MojangFullPlayerProfile>,
convertUsernameUUID: (usernameUuid: string) => Promise<MowojangProfile>,
setDiscordRPCMessage: (message: string) => Promise<null>,
open: (input: string) => Promise<null> } };
open: (input: string) => Promise<null> },
'events': { ingress: (event: IngressPayload) => Promise<void>,
message: (event: MessagePayload) => Promise<void>,
process: (event: ProcessPayload) => Promise<void> },
'folders': { fromCluster: (folderName: string) => Promise<string>,
openCluster: (folderName: string) => Promise<null> },
'debug': { openDevTools: () => Promise<void>,
isInDev: () => Promise<boolean>,
getArch: () => Promise<string>,
getFamily: () => Promise<string>,
getLocale: () => Promise<string>,
getType: () => Promise<string>,
getPlatform: () => Promise<string>,
getOsVersion: () => Promise<string>,
getOsDistro: () => Promise<string>,
getGitCommitHash: () => Promise<string>,
getBuildTimestamp: () => Promise<string>,
getPackageVersion: () => Promise<string>,
getFullDebugInfo: () => Promise<DebugInfoData>,
getFullDebugInfoParsed: () => Promise<DebugInfoParsedLine[]>,
getFullDebugInfoParsedString: () => Promise<string>,
hasDevSettings: () => Promise<boolean> },
'oneclient': { getClustersGroupedByMajor: () => Promise<Partial<{ [key in number]: ClusterModel[] }>>,
getBundlesFor: (clusterId: number) => Promise<ModpackArchive[]>,
getVersions: () => Promise<OnlineClusterManifest>,
extractBundleOverrides: (bundlePath: string, clusterId: number) => Promise<null>,
checkForUpdate: () => Promise<Update | null>,
installUpdate: () => Promise<null>,
downloadPackageFromBundle: (package: ModpackFileKind, clusterId: number, bundleName: string, skipCompatibility: boolean | null) => Promise<PackageModel>,
updateBundlePackages: (clusterId: number) => Promise<ApplyBundleUpdatesResult>,
isBundleSyncing: () => Promise<boolean>,
cacheArt: (path: string) => Promise<string>,
refreshArt: (path: string) => Promise<null>,
copyClusterContent: (sourceId: number, targetId: number) => Promise<CopyClusterResult> } };
export type { InferCommandOutput }

View file

@ -13,8 +13,8 @@ use crate::api::game::metadata::{self, download_minecraft};
use crate::api::ingress::IngressSendExt;
use crate::api::{java, setting_profiles};
use crate::error::LauncherResult;
use crate::store::Dirs;
use crate::store::ingress::{IngressType, SubIngress};
use crate::store::{Dirs, State};
use crate::utils::DatabaseModelExt;
use crate::utils::io::{self, IOError};
@ -176,20 +176,10 @@ async fn install_cluster(
setting_profiles::dao::get_profile_or_default(cluster.setting_profile_name.as_ref())
.await?;
let state = State::get().await?;
let mut metadata = state.metadata.write().await;
let versions_list = &metadata.get_vanilla_or_fetch().await?.versions;
let version_index = versions_list
.iter()
.position(|v| v.id == cluster.mc_version)
.ok_or_else(|| ClusterError::InvalidVersion(cluster.mc_version.clone()))?;
let version = versions_list[version_index].clone();
let minecraft_updated = metadata::is_version_updated(version_index, versions_list);
drop(metadata);
let (version, _version_index, minecraft_updated) =
metadata::resolve_minecraft_version(&cluster.mc_version)
.await
.map_err(|_| ClusterError::InvalidVersion(cluster.mc_version.clone()))?;
let loader_version = metadata::get_loader_version(
&cluster.mc_version,

View file

@ -133,20 +133,10 @@ pub async fn launch_minecraft(
let cwd = &io::canonicalize(dirs.clusters_dir().join(cluster.folder_name.clone()))?;
let (version, updated) = {
let metadata_store = state.metadata.read().await;
let versions = &metadata_store.get_vanilla()?.versions;
let version_index = versions
.iter()
.position(|it| it.id == cluster.mc_version)
.ok_or_else(|| anyhow::anyhow!("invalid game version {}", cluster.mc_version))?;
(
versions[version_index].clone(),
metadata::is_version_updated(version_index, versions),
)
};
let (version, _version_index, updated) =
metadata::resolve_minecraft_version(&cluster.mc_version)
.await
.map_err(|_| anyhow::anyhow!("invalid game version {}", cluster.mc_version))?;
let loader_version = metadata::get_loader_version(
&cluster.mc_version,

View file

@ -474,12 +474,12 @@ pub async fn get_loader_version(
return Ok(Some(resolved));
}
if loader_version.is_some() {
if !saw_matching_game_version || loader_version.is_some() {
tracing::warn!(
mc_version = %mc_version,
loader = ?loader,
requested_loader_version = ?loader_version,
"Requested loader version not found in cached metadata; refetching manifests"
"No matching loader metadata found in cache; refetching manifests"
);
metadata.fetch_all().await;
@ -488,13 +488,15 @@ pub async fn get_loader_version(
if let Some(resolved) = resolved_loader {
return Ok(Some(resolved));
}
}
if let Some(requested_loader_version) = loader_version {
if !saw_matching_game_version {
return Err(MetadataError::NoMatchingVersion.into());
}
return Err(MetadataError::RequestedLoaderVersionNotFound {
requested: loader_version.expect("checked as some").to_string(),
requested: requested_loader_version.to_string(),
}
.into());
}
@ -506,6 +508,35 @@ pub async fn get_loader_version(
}
}
#[tracing::instrument]
pub async fn resolve_minecraft_version(mc_version: &str) -> LauncherResult<(Version, usize, bool)> {
let state = State::get().await?;
let mut metadata = state.metadata.write().await;
let mut manifest = metadata.get_vanilla_or_fetch().await?;
let mut version_index = manifest.versions.iter().position(|it| it.id == mc_version);
if version_index.is_none() {
tracing::warn!(
mc_version = %mc_version,
"requested Minecraft version missing from cached manifest; refetching metadata"
);
metadata.fetch_all().await;
manifest = metadata.get_vanilla()?;
version_index = manifest.versions.iter().position(|it| it.id == mc_version);
}
let version_index = version_index.ok_or(MetadataError::NoMatchingVersion)?;
let versions = &manifest.versions;
Ok((
versions[version_index].clone(),
version_index,
is_version_updated(version_index, versions),
))
}
// MARK: Get Versions
/// Gets the list of Minecraft versions from the metadata store
#[tracing::instrument]

View file

@ -265,7 +265,23 @@ impl Metadata {
}
async fn fetch_vanilla_manifest(loader: GameLoader) -> LauncherResult<VanillaManifest> {
fetch_manifest(loader).await
match fetch_manifest(loader).await {
Ok(manifest) => Ok(manifest),
Err(err) => {
tracing::warn!(
"failed to fetch vanilla manifest from metadata mirror: {}; falling back to Mojang",
err
);
fetch_json::<VanillaManifest>(
Method::GET,
interfrost::api::minecraft::VERSION_MANIFEST_URL,
None,
None,
)
.await
}
}
}
async fn fetch_modded_manifest(loader: GameLoader) -> LauncherResult<ModdedManifest> {