aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src-tauri/src/core
diff options
context:
space:
mode:
authorHsiangNianian <i@jyunko.cn>2026-01-16 18:38:47 +0800
committerHsiangNianian <i@jyunko.cn>2026-01-16 18:42:12 +0800
commit1119f6c3cf421da2f2db92873efae8135c76b678 (patch)
tree57ee487c5003807e984c084a31a37c34e30820cf /src-tauri/src/core
parent52cf610264444bde95c1feae58414bb9849855eb (diff)
downloadDropOut-1119f6c3cf421da2f2db92873efae8135c76b678.tar.gz
DropOut-1119f6c3cf421da2f2db92873efae8135c76b678.zip
feat: enhance Java version management for Minecraft versions
Added functionality to determine and validate the required Java version for Minecraft versions, including checks for compatibility with older versions. Implemented event emissions for version installation and deletion, and updated the UI to reflect Java version requirements and installation status. Improved version metadata handling and added support for deleting versions.
Diffstat (limited to 'src-tauri/src/core')
-rw-r--r--src-tauri/src/core/forge.rs116
-rw-r--r--src-tauri/src/core/java.rs58
-rw-r--r--src-tauri/src/core/manifest.rs7
3 files changed, 150 insertions, 31 deletions
diff --git a/src-tauri/src/core/forge.rs b/src-tauri/src/core/forge.rs
index 0528b76..65bf413 100644
--- a/src-tauri/src/core/forge.rs
+++ b/src-tauri/src/core/forge.rs
@@ -16,6 +16,7 @@ use std::path::PathBuf;
const FORGE_PROMOTIONS_URL: &str =
"https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json";
const FORGE_MAVEN_URL: &str = "https://maven.minecraftforge.net/";
+const FORGE_FILES_URL: &str = "https://files.minecraftforge.net/";
/// Represents a Forge version entry.
#[derive(Debug, Deserialize, Serialize, Clone)]
@@ -180,27 +181,93 @@ pub fn generate_version_id(game_version: &str, forge_version: &str) -> String {
format!("{}-forge-{}", game_version, forge_version)
}
-/// Fetch the Forge installer manifest to get the library list
-async fn fetch_forge_installer_manifest(
+/// Try to download the Forge installer from multiple possible URL formats.
+/// This is necessary because older Forge versions use different URL patterns.
+async fn try_download_forge_installer(
game_version: &str,
forge_version: &str,
-) -> Result<ForgeInstallerManifest, Box<dyn Error + Send + Sync>> {
+) -> Result<bytes::Bytes, Box<dyn Error + Send + Sync>> {
let forge_full = format!("{}-{}", game_version, forge_version);
-
- // Download the installer JAR to extract version.json
- let installer_url = format!(
- "{}net/minecraftforge/forge/{}/forge-{}-installer.jar",
- FORGE_MAVEN_URL, forge_full, forge_full
- );
-
- println!("Fetching Forge installer from: {}", installer_url);
-
- let response = reqwest::get(&installer_url).await?;
- if !response.status().is_success() {
- return Err(format!("Failed to download Forge installer: {}", response.status()).into());
+ // For older versions (like 1.7.10), the URL needs an additional -{game_version} suffix
+ let forge_full_with_suffix = format!("{}-{}", forge_full, game_version);
+
+ // Try different URL formats for different Forge versions
+ // Order matters: try most common formats first, then fallback to alternatives
+ let url_patterns = vec![
+ // Standard Maven format (for modern versions): forge/{game_version}-{forge_version}/forge-{game_version}-{forge_version}-installer.jar
+ format!(
+ "{}net/minecraftforge/forge/{}/forge-{}-installer.jar",
+ FORGE_MAVEN_URL, forge_full, forge_full
+ ),
+ // Old version format with suffix (for versions like 1.7.10): forge/{game_version}-{forge_version}-{game_version}/forge-{game_version}-{forge_version}-{game_version}-installer.jar
+ // This is the correct format for 1.7.10 and similar old versions
+ format!(
+ "{}net/minecraftforge/forge/{}/forge-{}-installer.jar",
+ FORGE_MAVEN_URL, forge_full_with_suffix, forge_full_with_suffix
+ ),
+ // Files.minecraftforge.net format with suffix (for old versions like 1.7.10)
+ format!(
+ "{}maven/net/minecraftforge/forge/{}/forge-{}-installer.jar",
+ FORGE_FILES_URL, forge_full_with_suffix, forge_full_with_suffix
+ ),
+ // Files.minecraftforge.net standard format (for older versions)
+ format!(
+ "{}maven/net/minecraftforge/forge/{}/forge-{}-installer.jar",
+ FORGE_FILES_URL, forge_full, forge_full
+ ),
+ // Alternative Maven format
+ format!(
+ "{}net/minecraftforge/forge/{}-{}/forge-{}-{}-installer.jar",
+ FORGE_MAVEN_URL, game_version, forge_version, game_version, forge_version
+ ),
+ // Alternative files format
+ format!(
+ "{}maven/net/minecraftforge/forge/{}-{}/forge-{}-{}-installer.jar",
+ FORGE_FILES_URL, game_version, forge_version, game_version, forge_version
+ ),
+ ];
+
+ let mut last_error = None;
+ for url in url_patterns {
+ println!("Trying Forge installer URL: {}", url);
+ match reqwest::get(&url).await {
+ Ok(response) => {
+ if response.status().is_success() {
+ match response.bytes().await {
+ Ok(bytes) => {
+ println!("Successfully downloaded Forge installer from: {}", url);
+ return Ok(bytes);
+ }
+ Err(e) => {
+ last_error = Some(format!("Failed to read response body: {}", e));
+ continue;
+ }
+ }
+ } else {
+ last_error = Some(format!("HTTP {}: {}", response.status(), url));
+ continue;
+ }
+ }
+ Err(e) => {
+ last_error = Some(format!("Request failed: {}", e));
+ continue;
+ }
+ }
}
- let bytes = response.bytes().await?;
+ Err(format!(
+ "Failed to download Forge installer from any URL. Last error: {}",
+ last_error.unwrap_or_else(|| "Unknown error".to_string())
+ )
+ .into())
+}
+
+/// Fetch the Forge installer manifest to get the library list
+async fn fetch_forge_installer_manifest(
+ game_version: &str,
+ forge_version: &str,
+) -> Result<ForgeInstallerManifest, Box<dyn Error + Send + Sync>> {
+ let bytes = try_download_forge_installer(game_version, forge_version).await?;
// Extract version.json from the JAR (which is a ZIP file)
let cursor = std::io::Cursor::new(bytes.as_ref());
@@ -274,23 +341,10 @@ pub async fn run_forge_installer(
forge_version: &str,
java_path: &PathBuf,
) -> Result<(), Box<dyn Error + Send + Sync>> {
- // Download the installer JAR
- let installer_url = format!(
- "{}net/minecraftforge/forge/{}-{}/forge-{}-{}-installer.jar",
- FORGE_MAVEN_URL, game_version, forge_version, game_version, forge_version
- );
-
let installer_path = game_dir.join("forge-installer.jar");
- // Download installer
- let client = reqwest::Client::new();
- let response = client.get(&installer_url).send().await?;
-
- if !response.status().is_success() {
- return Err(format!("Failed to download Forge installer: {}", response.status()).into());
- }
-
- let bytes = response.bytes().await?;
+ // Download installer using the same multi-URL approach
+ let bytes = try_download_forge_installer(game_version, forge_version).await?;
tokio::fs::write(&installer_path, &bytes).await?;
// Run the installer in headless mode
diff --git a/src-tauri/src/core/java.rs b/src-tauri/src/core/java.rs
index 1d57d21..0c7769b 100644
--- a/src-tauri/src/core/java.rs
+++ b/src-tauri/src/core/java.rs
@@ -881,6 +881,64 @@ pub fn get_recommended_java(required_major_version: Option<u64>) -> Option<JavaI
}
}
+/// Get compatible Java for a specific Minecraft version with upper bound
+/// For older Minecraft versions (1.13.x and below), we need Java 8 specifically
+/// as newer Java versions have compatibility issues with old Forge versions
+pub fn get_compatible_java(
+ app_handle: &AppHandle,
+ required_major_version: Option<u64>,
+ max_major_version: Option<u32>,
+) -> Option<JavaInstallation> {
+ let installations = detect_all_java_installations(app_handle);
+
+ if let Some(max_version) = max_major_version {
+ // Find Java version within the acceptable range
+ installations.into_iter().find(|java| {
+ let major = parse_java_version(&java.version);
+ let meets_min = if let Some(required) = required_major_version {
+ major >= required as u32
+ } else {
+ true
+ };
+ meets_min && major <= max_version
+ })
+ } else if let Some(required) = required_major_version {
+ // Find exact match or higher (no upper bound)
+ installations.into_iter().find(|java| {
+ let major = parse_java_version(&java.version);
+ major >= required as u32
+ })
+ } else {
+ // Return newest
+ installations.into_iter().next()
+ }
+}
+
+/// Check if a Java installation is compatible with the required version range
+pub fn is_java_compatible(
+ java_path: &str,
+ required_major_version: Option<u64>,
+ max_major_version: Option<u32>,
+) -> bool {
+ let java_path_buf = PathBuf::from(java_path);
+ if let Some(java) = check_java_installation(&java_path_buf) {
+ let major = parse_java_version(&java.version);
+ let meets_min = if let Some(required) = required_major_version {
+ major >= required as u32
+ } else {
+ true
+ };
+ let meets_max = if let Some(max_version) = max_major_version {
+ major <= max_version
+ } else {
+ true
+ };
+ meets_min && meets_max
+ } else {
+ false
+ }
+}
+
/// Detect all installed Java versions (including system installations and DropOut downloads)
pub fn detect_all_java_installations(app_handle: &AppHandle) -> Vec<JavaInstallation> {
let mut installations = detect_java_installations();
diff --git a/src-tauri/src/core/manifest.rs b/src-tauri/src/core/manifest.rs
index d92ae58..637b935 100644
--- a/src-tauri/src/core/manifest.rs
+++ b/src-tauri/src/core/manifest.rs
@@ -25,6 +25,13 @@ pub struct Version {
pub time: String,
#[serde(rename = "releaseTime")]
pub release_time: String,
+ /// Java version requirement (major version number)
+ /// This is populated from the version JSON file if the version is installed locally
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub java_version: Option<u64>,
+ /// Whether this version is installed locally
+ #[serde(rename = "isInstalled", skip_serializing_if = "Option::is_none")]
+ pub is_installed: Option<bool>,
}
pub async fn fetch_version_manifest() -> Result<VersionManifest, Box<dyn Error + Send + Sync>> {