minecraft_java_rs_core/loader/
mod.rs1pub mod fabric;
2pub mod forge;
3pub mod forge_patcher;
4pub mod neoforge;
5pub mod quilt;
6pub mod types;
7
8use async_trait::async_trait;
9use tokio::sync::mpsc::Sender;
10
11use crate::error::LoaderError;
12use crate::launcher::events::LaunchEvent;
13use crate::launcher::options::LaunchOptions;
14use crate::models::loader::LoaderType;
15
16async fn save_loader_version(
22 loader_dir: &std::path::Path,
23 id: &str,
24 json: &impl serde::Serialize,
25) -> Result<(), LoaderError> {
26 let dir = loader_dir.join("versions").join(id);
27 tokio::fs::create_dir_all(&dir).await?;
28 let content = serde_json::to_string(json)?;
29 tokio::fs::write(dir.join(format!("{id}.json")), content).await?;
30 Ok(())
31}
32
33use self::fabric::{FabricMC, FabricVariant};
34use self::forge::ForgeMC;
35use self::neoforge::NeoForgeMC;
36use self::quilt::QuiltMC;
37use self::types::{LoaderInstallInput, LoaderResult};
38
39#[async_trait]
42pub trait ModLoader: Send + Sync {
43 async fn install(
44 &self,
45 options: &LaunchOptions,
46 input: &LoaderInstallInput,
47 client: &reqwest::Client,
48 event_tx: &Sender<LaunchEvent>,
49 ) -> Result<LoaderResult, LoaderError>;
50}
51
52pub fn create_loader(loader_type: LoaderType) -> Box<dyn ModLoader> {
55 match loader_type {
56 LoaderType::Forge => Box::new(ForgeMC::new()),
57 LoaderType::NeoForge => Box::new(NeoForgeMC::new()),
58 LoaderType::Fabric => Box::new(FabricMC::new(FabricVariant::Modern)),
59 LoaderType::LegacyFabric => Box::new(FabricMC::new(FabricVariant::Legacy)),
60 LoaderType::Quilt => Box::new(QuiltMC::new()),
61 }
62}
63
64#[async_trait]
67impl ModLoader for FabricMC {
68 async fn install(
69 &self,
70 options: &LaunchOptions,
71 input: &LoaderInstallInput,
72 client: &reqwest::Client,
73 event_tx: &Sender<LaunchEvent>,
74 ) -> Result<LoaderResult, LoaderError> {
75 let loader_name = match self.loader_type() {
76 LoaderType::LegacyFabric => "legacyfabric",
77 _ => "fabric",
78 };
79 let json = self
80 .download_json(&input.mc_version, &options.loader.build, client)
81 .await?;
82 let libraries = self
83 .download_libraries(options, &json, client, event_tx)
84 .await?;
85 save_loader_version(&options.loader_dir(loader_name), &json.id, &json).await?;
86 let extra_game_args = json.minecraft_arguments
87 .as_deref()
88 .map(|s| s.split_whitespace().map(str::to_owned).collect())
89 .unwrap_or_default();
90 Ok(LoaderResult {
91 libraries,
92 main_class: json.main_class,
93 loader_version: json.id,
94 loader_type: self.loader_type(),
95 extra_game_args,
96 extra_jvm_args: vec![],
97 })
98 }
99}
100
101#[async_trait]
102impl ModLoader for QuiltMC {
103 async fn install(
104 &self,
105 options: &LaunchOptions,
106 input: &LoaderInstallInput,
107 client: &reqwest::Client,
108 event_tx: &Sender<LaunchEvent>,
109 ) -> Result<LoaderResult, LoaderError> {
110 let json = self
111 .download_json(&input.mc_version, &options.loader.build, client)
112 .await?;
113 let libraries = self
114 .download_libraries(options, &json, client, event_tx)
115 .await?;
116 save_loader_version(&options.loader_dir("quilt"), &json.id, &json).await?;
117 let extra_game_args = json.minecraft_arguments
118 .as_deref()
119 .map(|s| s.split_whitespace().map(str::to_owned).collect())
120 .unwrap_or_default();
121 Ok(LoaderResult {
122 libraries,
123 main_class: json.main_class,
124 loader_version: json.id,
125 loader_type: LoaderType::Quilt,
126 extra_game_args,
127 extra_jvm_args: vec![],
128 })
129 }
130}
131
132#[async_trait]
133impl ModLoader for ForgeMC {
134 async fn install(
135 &self,
136 options: &LaunchOptions,
137 input: &LoaderInstallInput,
138 client: &reqwest::Client,
139 event_tx: &Sender<LaunchEvent>,
140 ) -> Result<LoaderResult, LoaderError> {
141 let (version_id, main_class, libraries, extra_game_args, extra_jvm_args) = self
142 .install(
143 options,
144 &input.mc_version,
145 &input.java_path,
146 &input.mc_jar,
147 &input.mc_json,
148 &options.loader.build,
149 client,
150 event_tx,
151 )
152 .await?;
153
154 Ok(LoaderResult {
155 libraries,
156 main_class,
157 loader_version: version_id,
158 loader_type: LoaderType::Forge,
159 extra_game_args,
160 extra_jvm_args,
161 })
162 }
163}
164
165#[async_trait]
166impl ModLoader for NeoForgeMC {
167 async fn install(
168 &self,
169 options: &LaunchOptions,
170 input: &LoaderInstallInput,
171 client: &reqwest::Client,
172 event_tx: &Sender<LaunchEvent>,
173 ) -> Result<LoaderResult, LoaderError> {
174 let (version_id, main_class, libraries, extra_game_args, extra_jvm_args) = self
175 .install(
176 options,
177 &input.mc_version,
178 &input.java_path,
179 &input.mc_jar,
180 &input.mc_json,
181 &options.loader.build,
182 client,
183 event_tx,
184 )
185 .await?;
186
187 Ok(LoaderResult {
188 libraries,
189 main_class,
190 loader_version: version_id,
191 loader_type: LoaderType::NeoForge,
192 extra_game_args,
193 extra_jvm_args,
194 })
195 }
196}
197
198#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn create_loader_forge_is_dyn() {
206 let _loader: Box<dyn ModLoader> = create_loader(LoaderType::Forge);
207 }
208
209 #[test]
210 fn create_loader_neoforge_is_dyn() {
211 let _loader: Box<dyn ModLoader> = create_loader(LoaderType::NeoForge);
212 }
213
214 #[test]
215 fn create_loader_fabric_is_dyn() {
216 let _loader: Box<dyn ModLoader> = create_loader(LoaderType::Fabric);
217 }
218
219 #[test]
220 fn create_loader_legacy_fabric_is_dyn() {
221 let _loader: Box<dyn ModLoader> = create_loader(LoaderType::LegacyFabric);
222 }
223
224 #[test]
225 fn create_loader_quilt_is_dyn() {
226 let _loader: Box<dyn ModLoader> = create_loader(LoaderType::Quilt);
227 }
228
229 #[test]
230 fn all_loader_types_are_dispatchable() {
231 let types = [
232 LoaderType::Forge,
233 LoaderType::NeoForge,
234 LoaderType::Fabric,
235 LoaderType::LegacyFabric,
236 LoaderType::Quilt,
237 ];
238 for t in types {
239 let _loader = create_loader(t);
240 }
241 }
242}