conduit_cli/loaders/
neoforge.rs1use crate::core::{
2 events::{CoreCallbacks, CoreEvent},
3 installer::download::download_to_path,
4};
5use regex::Regex;
6use std::path::{Path, PathBuf};
7
8pub async fn get_latest_neoforge_version(
9 mc_version: &str,
10 callbacks: &mut dyn CoreCallbacks,
11) -> Result<String, Box<dyn std::error::Error>> {
12 callbacks.on_event(CoreEvent::Info(
13 "Fetching latest NeoForge version".to_string(),
14 ));
15
16 let metadata_url =
17 "https://maven.neoforged.net/releases/net/neoforged/neoforge/maven-metadata.xml";
18 let response = reqwest::get(metadata_url).await?.text().await?;
19
20 let parts: Vec<&str> = mc_version.split('.').collect();
21
22 let major = if parts[0] == "1" {
23 parts.get(1).copied().unwrap_or("")
24 } else {
25 parts[0]
26 };
27
28 let minor = if parts[0] == "1" {
29 parts.get(2).copied().unwrap_or("0")
30 } else {
31 parts.get(1).copied().unwrap_or("0")
32 };
33
34 let prefix = format!("{}.{}.", major, minor);
35
36 let pattern = format!(r"<version>({}.*?)</version>", regex::escape(&prefix));
37 let re = Regex::new(&pattern)?;
38
39 let versions: Vec<String> = re
40 .captures_iter(&response)
41 .map(|cap| cap[1].to_string())
42 .collect();
43
44 let version = versions
45 .last()
46 .cloned()
47 .ok_or_else(|| format!("No compatible NeoForge version found for MC {}", mc_version))?;
48
49 callbacks.on_event(CoreEvent::Info(format!(
50 "Found NeoForge version: {}",
51 version
52 )));
53
54 Ok(version)
55}
56
57pub async fn download_neoforge_installer(
58 mc_version: &str,
59 loader_version: &str,
60 install_path: &Path,
61 callbacks: &mut dyn CoreCallbacks,
62) -> Result<PathBuf, Box<dyn std::error::Error>> {
63 let version = if loader_version == "latest" {
64 get_latest_neoforge_version(mc_version, callbacks).await?
65 } else {
66 loader_version.to_string()
67 };
68
69 let url = format!(
70 "https://maven.neoforged.net/releases/net/neoforged/neoforge/{v}/neoforge-{v}-installer.jar",
71 v = version
72 );
73
74 let filename = "neoforge-installer.jar";
75 let installer_path = install_path.join(filename);
76
77 let display_name = format!("neoforge-{}-installer.jar", version);
78
79 download_to_path(&url, &installer_path, &display_name, callbacks).await?;
80
81 Ok(installer_path)
82}
83
84pub async fn execute_neoforge_installer(
85 installer_path: &Path,
86 callbacks: &mut dyn CoreCallbacks,
87) -> Result<PathBuf, Box<dyn std::error::Error>> {
88 callbacks.on_event(CoreEvent::TaskStarted(format!(
89 "Executing NeoForge installer: {}",
90 installer_path.display()
91 )));
92
93 let status = tokio::process::Command::new("java")
94 .arg("-jar")
95 .arg(installer_path)
96 .arg("--installServer")
97 .current_dir(installer_path.parent().unwrap_or(Path::new(".")))
98 .stdout(std::process::Stdio::null())
99 .status()
100 .await?;
101
102 callbacks.on_event(CoreEvent::TaskFinished);
103
104 std::fs::remove_file(installer_path).ok();
105
106 if !status.success() {
107 return Err(format!("Installer exited with code: {}", status).into());
108 }
109
110 Ok(installer_path.to_path_buf())
111}
112
113pub async fn post_install_neoforge(
114 installer_path: &std::path::Path,
115 install_path: &Path,
116 callbacks: &mut dyn CoreCallbacks,
117) -> Result<(), Box<dyn std::error::Error>> {
118 callbacks.on_event(CoreEvent::TaskStarted(format!(
119 "Executing Post-Install: {}",
120 installer_path.display()
121 )));
122
123 let eula_path = install_path.join("eula.txt");
124 std::fs::write(eula_path, "eula=true")?;
125
126 let log_names = ["neoforge-installer.jar.log", "installer.log"];
127
128 for log_name in log_names {
129 let log_path = install_path.join(log_name);
130 if log_path.exists() {
131 let _ = std::fs::remove_file(log_path);
132 }
133 }
134
135 #[cfg(unix)]
136 {
137 use std::os::unix::fs::PermissionsExt;
138 let run_sh = install_path.join("run.sh");
139 if let Ok(metadata) = std::fs::metadata(&run_sh) {
140 let mut perms = metadata.permissions();
141 perms.set_mode(0o755); let _ = std::fs::set_permissions(&run_sh, perms);
143 }
144 }
145
146 callbacks.on_event(CoreEvent::TaskFinished);
147
148 Ok(())
149}