use crate::util::{download_file, fetch_json, format_url}; use crate::{insert_mirrored_artifact, Error, MirrorArtifact, UploadFile}; use daedalus::modded::{Manifest, PartialVersionInfo, DUMMY_REPLACE_STRING}; use dashmap::DashMap; use serde::Deserialize; use std::sync::Arc; use tokio::sync::Semaphore; #[tracing::instrument(skip(semaphore, upload_files, mirror_artifacts))] pub async fn fetch_fabric( semaphore: Arc, upload_files: &DashMap, mirror_artifacts: &DashMap, ) -> Result<(), Error> { fetch( daedalus::modded::CURRENT_FABRIC_FORMAT_VERSION, "fabric", "https://meta.fabricmc.net/v2", "https://maven.fabricmc.net/", &[], semaphore, upload_files, mirror_artifacts, ) .await } #[tracing::instrument(skip(semaphore, upload_files, mirror_artifacts))] pub async fn fetch_quilt( semaphore: Arc, upload_files: &DashMap, mirror_artifacts: &DashMap, ) -> Result<(), Error> { fetch( daedalus::modded::CURRENT_QUILT_FORMAT_VERSION, "quilt", "https://meta.quiltmc.org/v3", "https://maven.quiltmc.org/repository/release/", &[ // This version is broken as it contains invalid library coordinates "0.17.5-beta.4", ], semaphore, upload_files, mirror_artifacts, ) .await } #[allow(clippy::too_many_arguments)] #[tracing::instrument(skip(semaphore, upload_files, mirror_artifacts))] async fn fetch( format_version: usize, mod_loader: &str, meta_url: &str, maven_url: &str, skip_versions: &[&str], semaphore: Arc, upload_files: &DashMap, mirror_artifacts: &DashMap, ) -> Result<(), Error> { let modrinth_manifest = fetch_json::( &format_url(&format!("{mod_loader}/v{format_version}/manifest.json",)), &semaphore, ) .await .ok(); let fabric_manifest = fetch_json::( &format!("{meta_url}/versions"), &semaphore, ) .await?; // We check Modrinth's fabric version manifest and compare if the fabric version exists in Modrinth's database // We also check intermediary versions that are newly added to query let (fetch_fabric_versions, fetch_intermediary_versions) = if let Some(modrinth_manifest) = modrinth_manifest { let (mut fetch_versions, mut fetch_intermediary_versions) = (Vec::new(), Vec::new()); for version in &fabric_manifest.loader { if !modrinth_manifest .game_versions .iter() .any(|x| x.loaders.iter().any(|x| x.id == version.version)) && !skip_versions.contains(&&*version.version) { fetch_versions.push(version); } } for version in &fabric_manifest.intermediary { if !modrinth_manifest .game_versions .iter() .any(|x| x.id == version.version) && fabric_manifest .game .iter() .any(|x| x.version == version.version) { fetch_intermediary_versions.push(version); } } (fetch_versions, fetch_intermediary_versions) } else { ( fabric_manifest .loader .iter() .filter(|x| !skip_versions.contains(&&*x.version)) .collect(), fabric_manifest.intermediary.iter().collect(), ) }; const DUMMY_GAME_VERSION: &str = "1.21"; if !fetch_intermediary_versions.is_empty() { for x in &fetch_intermediary_versions { insert_mirrored_artifact( &x.maven, None, vec![maven_url.to_string()], false, mirror_artifacts, )?; } } if !fetch_fabric_versions.is_empty() { let fabric_version_manifest_urls = fetch_fabric_versions .iter() .map(|x| { format!( "{}/versions/loader/{}/{}/profile/json", meta_url, DUMMY_GAME_VERSION, x.version ) }) .collect::>(); let fabric_version_manifests = futures::future::try_join_all( fabric_version_manifest_urls .iter() .map(|x| download_file(x, None, &semaphore)), ) .await? .into_iter() .map(|x| serde_json::from_slice(&x)) .collect::, serde_json::Error>>()?; let patched_version_manifests = fabric_version_manifests .into_iter() .map(|mut version_info| { for lib in &mut version_info.libraries { let new_name = lib .name .replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING); // Hard-code: This library is not present on fabric's maven, so we fetch it from MC libraries if &*lib.name == "net.minecraft:launchwrapper:1.12" { lib.url = Some( "https://libraries.minecraft.net/".to_string(), ); } // If a library is not intermediary, we add it to mirror artifacts to be mirrored if lib.name == new_name { insert_mirrored_artifact( &new_name, None, vec![lib .url .clone() .unwrap_or_else(|| maven_url.to_string())], false, mirror_artifacts, )?; } else { lib.name = new_name; } lib.url = Some(format_url("maven/")); } version_info.id = version_info .id .replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING); version_info.inherits_from = version_info .inherits_from .replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING); Ok(version_info) }) .collect::, Error>>()?; let serialized_version_manifests = patched_version_manifests .iter() .map(|x| serde_json::to_vec(x).map(bytes::Bytes::from)) .collect::, serde_json::Error>>()?; serialized_version_manifests .into_iter() .enumerate() .for_each(|(index, bytes)| { let loader = fetch_fabric_versions[index]; let version_path = format!( "{mod_loader}/v{format_version}/versions/{}.json", loader.version ); upload_files.insert( version_path, UploadFile { file: bytes, content_type: Some("application/json".to_string()), }, ); }); } if !fetch_fabric_versions.is_empty() || !fetch_intermediary_versions.is_empty() { let fabric_manifest_path = format!("{mod_loader}/v{format_version}/manifest.json",); let loader_versions = daedalus::modded::Version { id: DUMMY_REPLACE_STRING.to_string(), stable: true, loaders: fabric_manifest .loader .into_iter() .map(|x| { let version_path = format!( "{mod_loader}/v{format_version}/versions/{}.json", x.version, ); daedalus::modded::LoaderVersion { id: x.version, url: format_url(&version_path), stable: x.stable, } }) .collect(), }; let manifest = daedalus::modded::Manifest { game_versions: std::iter::once(loader_versions) .chain(fabric_manifest.game.into_iter().map(|x| { daedalus::modded::Version { id: x.version, stable: x.stable, loaders: vec![], } })) .collect(), }; upload_files.insert( fabric_manifest_path, UploadFile { file: bytes::Bytes::from(serde_json::to_vec(&manifest)?), content_type: Some("application/json".to_string()), }, ); } Ok(()) } #[derive(Deserialize, Debug, Clone)] struct FabricVersions { pub loader: Vec, pub game: Vec, #[serde(alias = "hashed")] pub intermediary: Vec, } #[derive(Deserialize, Debug, Clone)] struct FabricLoaderVersion { // pub separator: String, // pub build: u32, // pub maven: String, pub version: String, #[serde(default)] pub stable: bool, } #[derive(Deserialize, Debug, Clone)] struct FabricIntermediaryVersion { pub maven: String, pub version: String, } #[derive(Deserialize, Debug, Clone)] struct FabricGameVersion { pub version: String, pub stable: bool, }