diff --git a/.github/workflows/theseus-build.yml b/.github/workflows/theseus-build.yml index 6b83d5bfde1..233b16c97e6 100644 --- a/.github/workflows/theseus-build.yml +++ b/.github/workflows/theseus-build.yml @@ -53,6 +53,7 @@ jobs: namespace-profile-medium-amd64-macos, namespace-profile-medium-amd64-windows, namespace-profile-medium-amd64, + namespace-profile-medium-arm64, ] include: - platform: namespace-profile-medium-amd64-macos @@ -61,6 +62,8 @@ jobs: artifact-target-name: x86_64-pc-windows-msvc - platform: namespace-profile-medium-amd64 artifact-target-name: x86_64-unknown-linux-gnu + - platform: namespace-profile-medium-arm64 + artifact-target-name: aarch64-unknown-linux-gnu runs-on: ${{ matrix.platform }} diff --git a/Cargo.lock b/Cargo.lock index 28231e36b75..cd2d543f384 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12390,6 +12390,7 @@ dependencies = [ "shlex 1.3.0", "sqlx", "sysinfo", + "tar", "tauri", "tempfile", "thiserror 2.0.17", diff --git a/apps/daedalus_client/library-patches.json b/apps/daedalus_client/library-patches.json index df750e02b30..f4e8ee451e5 100644 --- a/apps/daedalus_client/library-patches.json +++ b/apps/daedalus_client/library-patches.json @@ -2883,5 +2883,551 @@ ] } ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.1", + "match": [ + "org.lwjgl:lwjgl:3.4.1" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "46883f3b622d8b4d7f27b627ca3360cda3db0e0e", + "size": 120615, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl/3.4.1/lwjgl-3.4.1-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-natives-linux-arm64:3.4.1-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.3", + "match": [ + "org.lwjgl:lwjgl:3.4.3" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "101c753b129dbdeed565798d259b2b9544ef3878", + "size": 126343, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl/3.4.3/lwjgl-3.4.3-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-natives-linux-arm64:3.4.3-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.1", + "match": [ + "org.lwjgl:lwjgl-freetype:3.4.1" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "8f37d0da3386ff602ec54cd06626881895711041", + "size": 1309568, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-freetype/3.4.1/lwjgl-freetype-3.4.1-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-freetype-natives-linux-arm64:3.4.1-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.3", + "match": [ + "org.lwjgl:lwjgl-freetype:3.4.3" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "6babd5ddf9b4850e91a952d494cfd22b895ce200", + "size": 1300898, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-freetype/3.4.3/lwjgl-freetype-3.4.3-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-freetype-natives-linux-arm64:3.4.3-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.1", + "match": [ + "org.lwjgl:lwjgl-glfw:3.4.1" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "e5e87034c47118960746077dba46280e8de864b3", + "size": 141530, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-glfw/3.4.1/lwjgl-glfw-3.4.1-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-glfw-natives-linux-arm64:3.4.1-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.1", + "match": [ + "org.lwjgl:lwjgl-jemalloc:3.4.1" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "7891964dfb723209c6d02b0401432348fb707cc0", + "size": 238730, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-jemalloc/3.4.1/lwjgl-jemalloc-3.4.1-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc-natives-linux-arm64:3.4.1-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.3", + "match": [ + "org.lwjgl:lwjgl-jemalloc:3.4.3" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "b558d38d8eaa32831bb66835aae163428fe69646", + "size": 235928, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-jemalloc/3.4.3/lwjgl-jemalloc-3.4.3-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-jemalloc-natives-linux-arm64:3.4.3-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.1", + "match": [ + "org.lwjgl:lwjgl-openal:3.4.1" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "3729b70cdd42df5571b075e051fa2fc8586dc538", + "size": 839895, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-openal/3.4.1/lwjgl-openal-3.4.1-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal-natives-linux-arm64:3.4.1-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.3", + "match": [ + "org.lwjgl:lwjgl-openal:3.4.3" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "f2083556a7d8a02e4abeb2b933642250ef06c61d", + "size": 866036, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-openal/3.4.3/lwjgl-openal-3.4.3-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-openal-natives-linux-arm64:3.4.3-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.1", + "match": [ + "org.lwjgl:lwjgl-opengl:3.4.1" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "61a4103e56bbaeb74ad3f19ec14299fd6891c4b0", + "size": 79752, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-opengl/3.4.1/lwjgl-opengl-3.4.1-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl-natives-linux-arm64:3.4.1-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.3", + "match": [ + "org.lwjgl:lwjgl-opengl:3.4.3" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "84f229d793388be57ae5555b0cfee09b5990fa20", + "size": 80850, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-opengl/3.4.3/lwjgl-opengl-3.4.3-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-opengl-natives-linux-arm64:3.4.3-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.3", + "match": [ + "org.lwjgl:lwjgl-sdl:3.4.3" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "577e41b6d0006f3d3f841c14abe19736e695a760", + "size": 1460461, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-sdl/3.4.3/lwjgl-sdl-3.4.3-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-sdl-natives-linux-arm64:3.4.3-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.1", + "match": [ + "org.lwjgl:lwjgl-shaderc:3.4.1" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "bc5c99b2eb7e2109c6db21cfced15a6851f8111b", + "size": 3397476, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-shaderc/3.4.1/lwjgl-shaderc-3.4.1-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-shaderc-natives-linux-arm64:3.4.1-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.3", + "match": [ + "org.lwjgl:lwjgl-shaderc:3.4.3" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "f17eb9b12086809a31e61734dabc1dea80f6b7b2", + "size": 3540777, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-shaderc/3.4.3/lwjgl-shaderc-3.4.3-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-shaderc-natives-linux-arm64:3.4.3-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.1", + "match": [ + "org.lwjgl:lwjgl-spvc:3.4.1" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "e2eaa8f516d43fe11b8253e6243b3d8e1315c9c8", + "size": 1139919, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-spvc/3.4.1/lwjgl-spvc-3.4.1-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-spvc-natives-linux-arm64:3.4.1-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.3", + "match": [ + "org.lwjgl:lwjgl-spvc:3.4.3" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "85d2636eb1368a3864fb10faec02a4269020b31e", + "size": 1166740, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-spvc/3.4.3/lwjgl-spvc-3.4.3-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-spvc-natives-linux-arm64:3.4.3-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.1", + "match": [ + "org.lwjgl:lwjgl-stb:3.4.1" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "3bc107f901f931fea07cb0d80b1d74a34b806a2b", + "size": 259116, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-stb/3.4.1/lwjgl-stb-3.4.1-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb-natives-linux-arm64:3.4.1-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.3", + "match": [ + "org.lwjgl:lwjgl-stb:3.4.3" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "74f1cd22ccf4fd7432b982372b8a34c93cd9aacf", + "size": 258996, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-stb/3.4.3/lwjgl-stb-3.4.3-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-stb-natives-linux-arm64:3.4.3-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.1", + "match": [ + "org.lwjgl:lwjgl-tinyfd:3.4.1" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "20771d2b4e01f5295156912ab62e170508aef618", + "size": 45433, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-tinyfd/3.4.1/lwjgl-tinyfd-3.4.1-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-tinyfd-natives-linux-arm64:3.4.1-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.1", + "match": [ + "org.lwjgl:lwjgl-vma:3.4.1" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "de40d43c5947c4a4f91376af8f2c00e5396cc109", + "size": 59715, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-vma/3.4.1/lwjgl-vma-3.4.1-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-vma-natives-linux-arm64:3.4.1-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] + }, + { + "_comment": "Add linux-arm64 support for LWJGL 3.4.3", + "match": [ + "org.lwjgl:lwjgl-vma:3.4.3" + ], + "additionalLibraries": [ + { + "downloads": { + "artifact": { + "sha1": "0a9e20e7d8ae4d0106cd23ae58a1b72ca151c49f", + "size": 60863, + "url": "https://repo.maven.apache.org/maven2/org/lwjgl/lwjgl-vma/3.4.3/lwjgl-vma-3.4.3-natives-linux-arm64.jar" + } + }, + "name": "org.lwjgl:lwjgl-vma-natives-linux-arm64:3.4.3-lwjgl.1", + "rules": [ + { + "action": "allow", + "os": { + "name": "linux-arm64" + } + } + ] + } + ] } ] diff --git a/apps/daedalus_client/src/minecraft.rs b/apps/daedalus_client/src/minecraft.rs index 364d7f23c75..ddbbd4a45ac 100644 --- a/apps/daedalus_client/src/minecraft.rs +++ b/apps/daedalus_client/src/minecraft.rs @@ -14,6 +14,10 @@ use tokio::sync::Semaphore; #[tracing::instrument(skip(semaphore))] pub async fn fetch(semaphore: Arc) -> Result { + let library_patches_sha1 = sha1_async(bytes::Bytes::from_static( + include_bytes!("../library-patches.json"), + )) + .await?; let upload_files = DashMap::new(); let modrinth_manifest = fetch_json::( &format_url(&format!( @@ -34,6 +38,8 @@ pub async fn fetch(semaphore: Arc) -> Result { // If they are not, we will fetch them let (fetch_versions, existing_versions) = if let Some(mut modrinth_manifest) = modrinth_manifest { + let patches_changed = modrinth_manifest.library_patches_sha1.as_ref() + != Some(&library_patches_sha1); let (mut fetch_versions, mut existing_versions) = (Vec::new(), Vec::new()); @@ -50,6 +56,7 @@ pub async fn fetch(semaphore: Arc) -> Result { .original_sha1 .as_ref() .is_some_and(|x| x == &version.sha1) + && !patches_changed { existing_versions.push(modrinth_version); } else { @@ -153,6 +160,7 @@ pub async fn fetch(semaphore: Arc) -> Result { ); let new_manifest = VersionManifest { + library_patches_sha1: Some(library_patches_sha1), latest: mojang_manifest.latest, versions: new_versions, }; diff --git a/packages/app-lib/Cargo.toml b/packages/app-lib/Cargo.toml index 9bb14ce8a4c..74e679a98be 100644 --- a/packages/app-lib/Cargo.toml +++ b/packages/app-lib/Cargo.toml @@ -99,6 +99,7 @@ sqlx = { workspace = true, features = [ "uuid", ] } sysinfo = { workspace = true, features = ["disk", "system"] } +tar = { workspace = true } tauri = { workspace = true, features = ["unstable"], optional = true } tempfile = { workspace = true } thiserror = { workspace = true } diff --git a/packages/app-lib/src/api/jre.rs b/packages/app-lib/src/api/jre.rs index 9dd5e01cb1a..611c2b71115 100644 --- a/packages/app-lib/src/api/jre.rs +++ b/packages/app-lib/src/api/jre.rs @@ -150,8 +150,13 @@ async fn auto_install_java_inner( Some(java_step_progress(1)), ) .await?; + let archive_type = if cfg!(all(target_os = "linux", target_arch = "aarch64")) { + "tar.gz" + } else { + "zip" + }; let metadata_url = format!( - "https://api.azul.com/metadata/v1/zulu/packages?arch={}&java_version={}&os={}&archive_type=zip&javafx_bundled=false&java_package_type=jre&page_size=1", + "https://api.azul.com/metadata/v1/zulu/packages?arch={}&java_version={}&os={}&archive_type={archive_type}&javafx_bundled=false&java_package_type=jre&crac_supported=false&release_status=ga&availability_types=CA&page_size=1", std::env::consts::ARCH, java_version, std::env::consts::OS @@ -282,23 +287,20 @@ async fn auto_install_java_inner( ) .await?; } - let mut archive = zip::ZipArchive::new(std::io::Cursor::new(file)) - .map_err(|_| { - crate::Error::from(crate::ErrorKind::InputError( - "Failed to read java zip".to_string(), - )) - })?; - - // removes the old installation of java - if let Some(file) = archive.file_names().next() - && let Some(dir) = file.split('/').next() - { - let path = path.join(dir); - - if path.exists() { - io::remove_dir_all(path).await?; - } - } + let archive_suffix = format!(".{archive_type}"); + let directory_name = download + .name + .file_name() + .and_then(|name| name.to_str()) + .and_then(|name| name.strip_suffix(archive_suffix.as_str())) + .filter(|name| !name.is_empty() && *name != "." && *name != "..") + .ok_or_else(|| { + crate::ErrorKind::InputError("Invalid Java archive name".to_string()) + })?; + let mut base_path = path.join(directory_name); + io::create_dir_all(&path).await?; + let staging = tempfile::tempdir_in(&path) + .map_err(|err| io::IOError::with_path(err, &path))?; if let Some(loading_bar) = &loading_bar { emit_loading(loading_bar, 0.0, Some("Extracting java"))?; @@ -324,22 +326,43 @@ async fn auto_install_java_inner( ) .await?; } - archive.extract(&path).map_err(|_| { - crate::Error::from(crate::ErrorKind::InputError( - "Failed to extract java zip".to_string(), - )) - })?; + let staging = tokio::task::spawn_blocking(move || { + if archive_type == "tar.gz" { + let decoder = flate2::read::GzDecoder::new(file.as_ref()); + tar::Archive::new(decoder) + .unpack(staging.path()) + .map_err(|err| io::IOError::with_path(err, staging.path()))?; + } else { + let mut archive = zip::ZipArchive::new(std::io::Cursor::new(file)) + .map_err(|err| { + crate::ErrorKind::InputError(format!("Failed to read Java ZIP: {err}")) + })?; + archive.extract(staging.path()).map_err(|err| { + crate::ErrorKind::InputError(format!("Failed to extract Java ZIP: {err}")) + })?; + } + Ok::<_, crate::Error>(staging) + }) + .await??; + let extracted_path = staging.path().join(directory_name); + #[cfg(target_os = "macos")] + let java_path = extracted_path.join("Contents/Home/bin/java"); + #[cfg(not(target_os = "macos"))] + let java_path = extracted_path.join("bin").join(jre::JAVA_BIN); + if !java_path.is_file() { + return Err(crate::ErrorKind::InputError( + "Java archive does not contain the expected executable".to_string(), + ).into()); + } + if base_path.exists() { + io::remove_dir_all(&base_path).await?; + } + tokio::fs::rename(&extracted_path, &base_path) + .await + .map_err(|err| io::IOError::with_path(err, &base_path))?; if let Some(loading_bar) = &loading_bar { emit_loading(loading_bar, 10.0, Some("Done extracting java"))?; } - let mut base_path = path.join( - download - .name - .file_stem() - .unwrap_or_default() - .to_string_lossy() - .to_string(), - ); #[cfg(target_os = "macos")] { diff --git a/packages/app-lib/src/util/platform.rs b/packages/app-lib/src/util/platform.rs index 3932cfea4f7..434a2c1af66 100644 --- a/packages/app-lib/src/util/platform.rs +++ b/packages/app-lib/src/util/platform.rs @@ -23,7 +23,7 @@ pub fn os_rule( if let Some(name) = &rule.name { if minecraft_updated - && (name != &Os::LinuxArm64 || name != &Os::LinuxArm32) + && (name != &Os::LinuxArm64 && name != &Os::LinuxArm32) { rule_match &= Os::native() == name.get_os() || &Os::native_arch(java_arch) == name; diff --git a/packages/daedalus/src/minecraft.rs b/packages/daedalus/src/minecraft.rs index f31e08b1716..3d31339c564 100644 --- a/packages/daedalus/src/minecraft.rs +++ b/packages/daedalus/src/minecraft.rs @@ -68,6 +68,9 @@ pub struct LatestVersion { #[derive(Serialize, Deserialize, Debug, Clone)] /// Data of all game versions of Minecraft pub struct VersionManifest { + /// SHA1 of the library patches used to generate this manifest. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub library_patches_sha1: Option, /// A struct containing the latest snapshot and release of the game pub latest: LatestVersion, /// A list of game versions of Minecraft