diff --git a/daedalus/src/modded.rs b/daedalus/src/modded.rs index 2519719..e360931 100644 --- a/daedalus/src/modded.rs +++ b/daedalus/src/modded.rs @@ -12,7 +12,11 @@ use bincode::{Decode, Encode}; /// The latest version of the format the fabric model structs deserialize to pub const CURRENT_FABRIC_FORMAT_VERSION: usize = 0; -/// The latest version of the format the fabric model structs deserialize to +/// The latest version of the format the legacy-fabric model structs deserialize to +pub const CURRENT_LEGACY_FABRIC_FORMAT_VERSION: usize = 0; +/// The latest version of the format the babric model structs deserialize to +pub const CURRENT_BABRIC_FORMAT_VERSION: usize = 0; +/// The latest version of the format the forge model structs deserialize to pub const CURRENT_FORGE_FORMAT_VERSION: usize = 0; /// The dummy replace string library names, inheritsFrom, and version names should be replaced with diff --git a/daedalus_client/src/babric.rs b/daedalus_client/src/babric.rs new file mode 100644 index 0000000..4383f1e --- /dev/null +++ b/daedalus_client/src/babric.rs @@ -0,0 +1,314 @@ +use crate::fabric::{ + fetch_fabric_like_version, fetch_fabric_like_versions, FabricVersions, +}; +use crate::{download_file, format_url, upload_file_to_bucket, Error}; +use daedalus::minecraft::{Library, VersionManifest}; +use daedalus::modded::PartialVersionInfo; +use daedalus::modded::{ + LoaderVersion, Manifest, Version, DUMMY_REPLACE_STRING, +}; +use std::sync::Arc; +use tokio::sync::Semaphore; +use tokio::sync::{Mutex, RwLock}; + +const BABRIC_META_URL: &str = "https://meta.babric.glass-launcher.net/v2"; + +pub async fn retrieve_data( + minecraft_versions: &VersionManifest, + uploaded_files: &mut Vec, + semaphore: Arc, +) -> Result<(), Error> { + let mut list = fetch_babric_versions(None, semaphore.clone()).await?; + let old_manifest = daedalus::modded::fetch_manifest(&format_url(&format!( + "babric/v{}/manifest.json", + daedalus::modded::CURRENT_BABRIC_FORMAT_VERSION, + ))) + .await + .ok(); + + let mut versions = if let Some(old_manifest) = old_manifest { + old_manifest.game_versions + } else { + Vec::new() + }; + + let loaders_mutex = RwLock::new(Vec::new()); + + { + let mut loaders = loaders_mutex.write().await; + + for loader in &list.loader { + loaders.push((Box::new(loader.stable), loader.version.clone())) + } + + list.loader + .retain(|x| loaders.iter().any(|val| val.1 == x.version)) + } + + const DUMMY_GAME_VERSION: &str = "b1.7.3"; + + let loader_version_mutex = Mutex::new(Vec::new()); + let uploaded_files_mutex = Arc::new(Mutex::new(Vec::new())); + + let loader_versions = futures::future::try_join_all( + loaders_mutex.read().await.clone().into_iter().map( + |(stable, loader)| async { + { + if versions.iter().any(|x| { + x.id == DUMMY_REPLACE_STRING + && x.loaders.iter().any(|x| x.id == loader) + }) { + return Ok(None); + } + } + + let version = fetch_babric_version( + DUMMY_GAME_VERSION, + &loader, + semaphore.clone(), + ) + .await?; + + Ok::, String, PartialVersionInfo)>, Error>( + Some((stable, loader, version)), + ) + }, + ), + ) + .await?; + + let visited_artifacts_mutex = Arc::new(Mutex::new(Vec::new())); + futures::future::try_join_all(loader_versions.into_iter() + .flatten().map( + |(stable, loader, version)| async { + let libs = futures::future::try_join_all( + version.libraries.into_iter().map(|mut lib| async { + { + let mut visited_assets = + visited_artifacts_mutex.lock().await; + + if visited_assets.contains(&lib.name) { + lib.name = lib.name.replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING); + lib.url = Some(format_url("maven/")); + + return Ok(lib); + } else { + visited_assets.push(lib.name.clone()) + } + } + + if lib.name.contains(DUMMY_GAME_VERSION) { + lib.name = lib.name.replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING); + futures::future::try_join_all(list.game.clone().into_iter().map(|game_version| async { + let semaphore = semaphore.clone(); + let uploaded_files_mutex = uploaded_files_mutex.clone(); + let lib_name = lib.name.clone(); + let lib_url = lib.url.clone(); + + async move { + let artifact_path = + daedalus::get_path_from_artifact(&lib_name.replace(DUMMY_REPLACE_STRING, &game_version.version))?; + + let artifact = download_file( + &format!( + "{}{}", + lib_url.unwrap_or_else(|| { + "https://maven.glass-launcher.net/babric/".to_string() + }), + artifact_path + ), + None, + semaphore.clone(), + ) + .await?; + + upload_file_to_bucket( + format!("{}/{}", "maven", artifact_path), + artifact.to_vec(), + Some("application/java-archive".to_string()), + &uploaded_files_mutex, + semaphore.clone(), + ) + .await?; + + Ok::<(), Error>(()) + }.await?; + + Ok::<(), Error>(()) + })).await?; + lib.url = Some(format_url("maven/")); + + return Ok(lib); + } + + let artifact_path = + daedalus::get_path_from_artifact(&lib.name)?; + + let artifact = download_file( + &format!( + "{}{}", + lib.url.unwrap_or_else(|| { + "https://maven.glass-launcher.net/babric/".to_string() + }), + artifact_path + ), + None, + semaphore.clone(), + ) + .await?; + + lib.url = Some(format_url("maven/")); + + upload_file_to_bucket( + format!("{}/{}", "maven", artifact_path), + artifact.to_vec(), + Some("application/java-archive".to_string()), + &uploaded_files_mutex, + semaphore.clone(), + ) + .await?; + + Ok::(lib) + }), + ) + .await?; + + let version_path = format!( + "babric/v{}/versions/{}.json", + daedalus::modded::CURRENT_BABRIC_FORMAT_VERSION, + &loader + ); + + upload_file_to_bucket( + version_path.clone(), + serde_json::to_vec(&PartialVersionInfo { + arguments: version.arguments, + id: version + .id + .replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING), + main_class: version.main_class, + release_time: version.release_time, + time: version.time, + type_: version.type_, + inherits_from: version + .inherits_from + .replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING), + libraries: libs, + minecraft_arguments: version.minecraft_arguments, + processors: None, + data: None, + })?, + Some("application/json".to_string()), + &uploaded_files_mutex, + semaphore.clone(), + ) + .await?; + + { + let mut loader_version_map = loader_version_mutex.lock().await; + async move { + loader_version_map.push(LoaderVersion { + id: loader.to_string(), + url: format_url(&version_path), + stable: *stable, + }); + } + .await; + } + + Ok::<(), Error>(()) + }, + )) + .await?; + + versions.push(Version { + id: DUMMY_REPLACE_STRING.to_string(), + stable: true, + loaders: loader_version_mutex.into_inner(), + }); + + for version in &list.game { + versions.push(Version { + id: version.version.clone(), + stable: version.stable, + loaders: vec![], + }); + } + + versions.sort_by(|x, y| { + minecraft_versions + .versions + .iter() + .position(|z| x.id == z.id) + .unwrap_or_default() + .cmp( + &minecraft_versions + .versions + .iter() + .position(|z| y.id == z.id) + .unwrap_or_default(), + ) + }); + + for version in &mut versions { + version.loaders.sort_by(|x, y| { + list.loader + .iter() + .position(|z| { + x.id.split('-').next().unwrap_or_default() == &*z.version + }) + .unwrap_or_default() + .cmp( + &list + .loader + .iter() + .position(|z| { + y.id.split('-').next().unwrap_or_default() + == z.version + }) + .unwrap_or_default(), + ) + }) + } + + upload_file_to_bucket( + format!( + "babric/v{}/manifest.json", + daedalus::modded::CURRENT_BABRIC_FORMAT_VERSION, + ), + serde_json::to_vec(&Manifest { + game_versions: versions, + })?, + Some("application/json".to_string()), + &uploaded_files_mutex, + semaphore, + ) + .await?; + + if let Ok(uploaded_files_mutex) = Arc::try_unwrap(uploaded_files_mutex) { + uploaded_files.extend(uploaded_files_mutex.into_inner()); + } + + Ok(()) +} + +async fn fetch_babric_version( + version_number: &str, + loader_version: &str, + semaphore: Arc, +) -> Result { + fetch_fabric_like_version( + version_number, + loader_version, + semaphore, + BABRIC_META_URL, + ) + .await +} + +async fn fetch_babric_versions( + url: Option<&str>, + semaphore: Arc, +) -> Result { + fetch_fabric_like_versions(url, semaphore, BABRIC_META_URL).await +} diff --git a/daedalus_client/src/fabric.rs b/daedalus_client/src/fabric.rs index 6aa8f91..1fa67ed 100644 --- a/daedalus_client/src/fabric.rs +++ b/daedalus_client/src/fabric.rs @@ -288,16 +288,17 @@ pub async fn retrieve_data( const FABRIC_META_URL: &str = "https://meta.fabricmc.net/v2"; -async fn fetch_fabric_version( +pub async fn fetch_fabric_like_version( version_number: &str, loader_version: &str, semaphore: Arc, + meta_url: &str, ) -> Result { Ok(serde_json::from_slice( &download_file( &format!( "{}/versions/loader/{}/{}/profile/json", - FABRIC_META_URL, version_number, loader_version + meta_url, version_number, loader_version ), None, semaphore, @@ -306,9 +307,23 @@ async fn fetch_fabric_version( )?) } +async fn fetch_fabric_version( + version_number: &str, + loader_version: &str, + semaphore: Arc, +) -> Result { + fetch_fabric_like_version( + version_number, + loader_version, + semaphore, + FABRIC_META_URL, + ) + .await +} + #[derive(Serialize, Deserialize, Debug, Clone)] /// Versions of fabric components -struct FabricVersions { +pub struct FabricVersions { /// Versions of Minecraft that fabric supports pub game: Vec, /// Available versions of the fabric loader @@ -317,7 +332,7 @@ struct FabricVersions { #[derive(Serialize, Deserialize, Debug, Clone)] /// A version of Minecraft that fabric supports -struct FabricGameVersion { +pub struct FabricGameVersion { /// The version number of the game pub version: String, /// Whether the Minecraft version is stable or not @@ -326,7 +341,7 @@ struct FabricGameVersion { #[derive(Serialize, Deserialize, Debug, Clone)] /// A version of the fabric loader -struct FabricLoaderVersion { +pub struct FabricLoaderVersion { /// The separator to get the build number pub separator: String, /// The build number @@ -339,16 +354,24 @@ struct FabricLoaderVersion { pub stable: bool, } /// Fetches the list of fabric versions -async fn fetch_fabric_versions( +pub async fn fetch_fabric_like_versions( url: Option<&str>, semaphore: Arc, + meta_url: &str, ) -> Result { Ok(serde_json::from_slice( &download_file( - url.unwrap_or(&*format!("{}/versions", FABRIC_META_URL)), + url.unwrap_or(&*format!("{}/versions", meta_url)), None, semaphore, ) .await?, )?) } + +async fn fetch_fabric_versions( + url: Option<&str>, + semaphore: Arc, +) -> Result { + fetch_fabric_like_versions(url, semaphore, FABRIC_META_URL).await +} diff --git a/daedalus_client/src/legacy_fabric.rs b/daedalus_client/src/legacy_fabric.rs new file mode 100644 index 0000000..6f8c3ce --- /dev/null +++ b/daedalus_client/src/legacy_fabric.rs @@ -0,0 +1,315 @@ +use crate::fabric::{ + fetch_fabric_like_version, fetch_fabric_like_versions, FabricVersions, +}; +use crate::{download_file, format_url, upload_file_to_bucket, Error}; +use daedalus::minecraft::{Library, VersionManifest}; +use daedalus::modded::PartialVersionInfo; +use daedalus::modded::{ + LoaderVersion, Manifest, Version, DUMMY_REPLACE_STRING, +}; +use std::sync::Arc; +use tokio::sync::Semaphore; +use tokio::sync::{Mutex, RwLock}; + +const LEGACY_FABRIC_META_URL: &str = "https://meta.legacyfabric.net/v2"; + +pub async fn retrieve_data( + minecraft_versions: &VersionManifest, + uploaded_files: &mut Vec, + semaphore: Arc, +) -> Result<(), Error> { + let mut list = + fetch_legacy_fabric_versions(None, semaphore.clone()).await?; + let old_manifest = daedalus::modded::fetch_manifest(&format_url(&format!( + "legacy-fabric/v{}/manifest.json", + daedalus::modded::CURRENT_LEGACY_FABRIC_FORMAT_VERSION, + ))) + .await + .ok(); + + let mut versions = if let Some(old_manifest) = old_manifest { + old_manifest.game_versions + } else { + Vec::new() + }; + + let loaders_mutex = RwLock::new(Vec::new()); + + { + let mut loaders = loaders_mutex.write().await; + + for loader in &list.loader { + loaders.push((Box::new(loader.stable), loader.version.clone())) + } + + list.loader + .retain(|x| loaders.iter().any(|val| val.1 == x.version)) + } + + const DUMMY_GAME_VERSION: &str = "1.8.9"; + + let loader_version_mutex = Mutex::new(Vec::new()); + let uploaded_files_mutex = Arc::new(Mutex::new(Vec::new())); + + let loader_versions = futures::future::try_join_all( + loaders_mutex.read().await.clone().into_iter().map( + |(stable, loader)| async { + { + if versions.iter().any(|x| { + x.id == DUMMY_REPLACE_STRING + && x.loaders.iter().any(|x| x.id == loader) + }) { + return Ok(None); + } + } + + let version = fetch_legacy_fabric_version( + DUMMY_GAME_VERSION, + &loader, + semaphore.clone(), + ) + .await?; + + Ok::, String, PartialVersionInfo)>, Error>( + Some((stable, loader, version)), + ) + }, + ), + ) + .await?; + + let visited_artifacts_mutex = Arc::new(Mutex::new(Vec::new())); + futures::future::try_join_all(loader_versions.into_iter() + .flatten().map( + |(stable, loader, version)| async { + let libs = futures::future::try_join_all( + version.libraries.into_iter().map(|mut lib| async { + { + let mut visited_assets = + visited_artifacts_mutex.lock().await; + + if visited_assets.contains(&lib.name) { + lib.name = lib.name.replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING); + lib.url = Some(format_url("maven/")); + + return Ok(lib); + } else { + visited_assets.push(lib.name.clone()) + } + } + + if lib.name.contains(DUMMY_GAME_VERSION) { + lib.name = lib.name.replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING); + futures::future::try_join_all(list.game.clone().into_iter().map(|game_version| async { + let semaphore = semaphore.clone(); + let uploaded_files_mutex = uploaded_files_mutex.clone(); + let lib_name = lib.name.clone(); + let lib_url = lib.url.clone(); + + async move { + let artifact_path = + daedalus::get_path_from_artifact(&lib_name.replace(DUMMY_REPLACE_STRING, &game_version.version))?; + + let artifact = download_file( + &format!( + "{}{}", + lib_url.unwrap_or_else(|| { + "https://repo.legacyfabric.net/repository/legacyfabric/".to_string() + }), + artifact_path + ), + None, + semaphore.clone(), + ) + .await?; + + upload_file_to_bucket( + format!("{}/{}", "maven", artifact_path), + artifact.to_vec(), + Some("application/java-archive".to_string()), + &uploaded_files_mutex, + semaphore.clone(), + ) + .await?; + + Ok::<(), Error>(()) + }.await?; + + Ok::<(), Error>(()) + })).await?; + lib.url = Some(format_url("maven/")); + + return Ok(lib); + } + + let artifact_path = + daedalus::get_path_from_artifact(&lib.name)?; + + let artifact = download_file( + &format!( + "{}{}", + lib.url.unwrap_or_else(|| { + "https://repo.legacyfabric.net/repository/legacyfabric/".to_string() + }), + artifact_path + ), + None, + semaphore.clone(), + ) + .await?; + + lib.url = Some(format_url("maven/")); + + upload_file_to_bucket( + format!("{}/{}", "maven", artifact_path), + artifact.to_vec(), + Some("application/java-archive".to_string()), + &uploaded_files_mutex, + semaphore.clone(), + ) + .await?; + + Ok::(lib) + }), + ) + .await?; + + let version_path = format!( + "legacy-fabric/v{}/versions/{}.json", + daedalus::modded::CURRENT_LEGACY_FABRIC_FORMAT_VERSION, + &loader + ); + + upload_file_to_bucket( + version_path.clone(), + serde_json::to_vec(&PartialVersionInfo { + arguments: version.arguments, + id: version + .id + .replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING), + main_class: version.main_class, + release_time: version.release_time, + time: version.time, + type_: version.type_, + inherits_from: version + .inherits_from + .replace(DUMMY_GAME_VERSION, DUMMY_REPLACE_STRING), + libraries: libs, + minecraft_arguments: version.minecraft_arguments, + processors: None, + data: None, + })?, + Some("application/json".to_string()), + &uploaded_files_mutex, + semaphore.clone(), + ) + .await?; + + { + let mut loader_version_map = loader_version_mutex.lock().await; + async move { + loader_version_map.push(LoaderVersion { + id: loader.to_string(), + url: format_url(&version_path), + stable: *stable, + }); + } + .await; + } + + Ok::<(), Error>(()) + }, + )) + .await?; + + versions.push(Version { + id: DUMMY_REPLACE_STRING.to_string(), + stable: true, + loaders: loader_version_mutex.into_inner(), + }); + + for version in &list.game { + versions.push(Version { + id: version.version.clone(), + stable: version.stable, + loaders: vec![], + }); + } + + versions.sort_by(|x, y| { + minecraft_versions + .versions + .iter() + .position(|z| x.id == z.id) + .unwrap_or_default() + .cmp( + &minecraft_versions + .versions + .iter() + .position(|z| y.id == z.id) + .unwrap_or_default(), + ) + }); + + for version in &mut versions { + version.loaders.sort_by(|x, y| { + list.loader + .iter() + .position(|z| { + x.id.split('-').next().unwrap_or_default() == &*z.version + }) + .unwrap_or_default() + .cmp( + &list + .loader + .iter() + .position(|z| { + y.id.split('-').next().unwrap_or_default() + == z.version + }) + .unwrap_or_default(), + ) + }) + } + + upload_file_to_bucket( + format!( + "legacy-fabric/v{}/manifest.json", + daedalus::modded::CURRENT_LEGACY_FABRIC_FORMAT_VERSION, + ), + serde_json::to_vec(&Manifest { + game_versions: versions, + })?, + Some("application/json".to_string()), + &uploaded_files_mutex, + semaphore, + ) + .await?; + + if let Ok(uploaded_files_mutex) = Arc::try_unwrap(uploaded_files_mutex) { + uploaded_files.extend(uploaded_files_mutex.into_inner()); + } + + Ok(()) +} + +async fn fetch_legacy_fabric_version( + version_number: &str, + loader_version: &str, + semaphore: Arc, +) -> Result { + fetch_fabric_like_version( + version_number, + loader_version, + semaphore, + LEGACY_FABRIC_META_URL, + ) + .await +} + +async fn fetch_legacy_fabric_versions( + url: Option<&str>, + semaphore: Arc, +) -> Result { + fetch_fabric_like_versions(url, semaphore, LEGACY_FABRIC_META_URL).await +} diff --git a/daedalus_client/src/main.rs b/daedalus_client/src/main.rs index 92fee4c..34ec8f0 100644 --- a/daedalus_client/src/main.rs +++ b/daedalus_client/src/main.rs @@ -6,8 +6,10 @@ use std::sync::Arc; use std::time::Duration; use tokio::sync::Semaphore; +mod babric; mod fabric; mod forge; +mod legacy_fabric; mod minecraft; #[derive(thiserror::Error, Debug)] @@ -77,6 +79,26 @@ async fn main() { Ok(..) => {} Err(err) => error!("{:?}", err), }; + match legacy_fabric::retrieve_data( + &manifest, + &mut uploaded_files, + semaphore.clone(), + ) + .await + { + Ok(..) => {} + Err(err) => error!("{:?}", err), + }; + match babric::retrieve_data( + &manifest, + &mut uploaded_files, + semaphore.clone(), + ) + .await + { + Ok(..) => {} + Err(err) => error!("{:?}", err), + }; match forge::retrieve_data( &manifest, &mut uploaded_files,