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
87 .minecraft_arguments
88 .as_deref()
89 .map(|s| s.split_whitespace().map(str::to_owned).collect())
90 .unwrap_or_default();
91 Ok(LoaderResult {
92 libraries,
93 main_class: json.main_class,
94 loader_version: json.id,
95 loader_type: self.loader_type(),
96 extra_game_args,
97 extra_jvm_args: vec![],
98 })
99 }
100}
101
102#[async_trait]
103impl ModLoader for QuiltMC {
104 async fn install(
105 &self,
106 options: &LaunchOptions,
107 input: &LoaderInstallInput,
108 client: &reqwest::Client,
109 event_tx: &Sender<LaunchEvent>,
110 ) -> Result<LoaderResult, LoaderError> {
111 let json = self
112 .download_json(&input.mc_version, &options.loader.build, client)
113 .await?;
114 let libraries = self
115 .download_libraries(options, &json, client, event_tx)
116 .await?;
117 save_loader_version(&options.loader_dir("quilt"), &json.id, &json).await?;
118 let extra_game_args = json
119 .minecraft_arguments
120 .as_deref()
121 .map(|s| s.split_whitespace().map(str::to_owned).collect())
122 .unwrap_or_default();
123 Ok(LoaderResult {
124 libraries,
125 main_class: json.main_class,
126 loader_version: json.id,
127 loader_type: LoaderType::Quilt,
128 extra_game_args,
129 extra_jvm_args: vec![],
130 })
131 }
132}
133
134#[async_trait]
135impl ModLoader for ForgeMC {
136 async fn install(
137 &self,
138 options: &LaunchOptions,
139 input: &LoaderInstallInput,
140 client: &reqwest::Client,
141 event_tx: &Sender<LaunchEvent>,
142 ) -> Result<LoaderResult, LoaderError> {
143 let (version_id, main_class, libraries, extra_game_args, extra_jvm_args) = self
144 .install(
145 options,
146 &input.mc_version,
147 &input.java_path,
148 &input.mc_jar,
149 &input.mc_json,
150 &options.loader.build,
151 client,
152 event_tx,
153 )
154 .await?;
155
156 Ok(LoaderResult {
157 libraries,
158 main_class,
159 loader_version: version_id,
160 loader_type: LoaderType::Forge,
161 extra_game_args,
162 extra_jvm_args,
163 })
164 }
165}
166
167#[async_trait]
168impl ModLoader for NeoForgeMC {
169 async fn install(
170 &self,
171 options: &LaunchOptions,
172 input: &LoaderInstallInput,
173 client: &reqwest::Client,
174 event_tx: &Sender<LaunchEvent>,
175 ) -> Result<LoaderResult, LoaderError> {
176 let (version_id, main_class, libraries, extra_game_args, extra_jvm_args) = self
177 .install(
178 options,
179 &input.mc_version,
180 &input.java_path,
181 &input.mc_jar,
182 &input.mc_json,
183 &options.loader.build,
184 client,
185 event_tx,
186 )
187 .await?;
188
189 Ok(LoaderResult {
190 libraries,
191 main_class,
192 loader_version: version_id,
193 loader_type: LoaderType::NeoForge,
194 extra_game_args,
195 extra_jvm_args,
196 })
197 }
198}
199
200#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn create_loader_forge_is_dyn() {
208 let _loader: Box<dyn ModLoader> = create_loader(LoaderType::Forge);
209 }
210
211 #[test]
212 fn create_loader_neoforge_is_dyn() {
213 let _loader: Box<dyn ModLoader> = create_loader(LoaderType::NeoForge);
214 }
215
216 #[test]
217 fn create_loader_fabric_is_dyn() {
218 let _loader: Box<dyn ModLoader> = create_loader(LoaderType::Fabric);
219 }
220
221 #[test]
222 fn create_loader_legacy_fabric_is_dyn() {
223 let _loader: Box<dyn ModLoader> = create_loader(LoaderType::LegacyFabric);
224 }
225
226 #[test]
227 fn create_loader_quilt_is_dyn() {
228 let _loader: Box<dyn ModLoader> = create_loader(LoaderType::Quilt);
229 }
230
231 #[test]
232 fn all_loader_types_are_dispatchable() {
233 let types = [
234 LoaderType::Forge,
235 LoaderType::NeoForge,
236 LoaderType::Fabric,
237 LoaderType::LegacyFabric,
238 LoaderType::Quilt,
239 ];
240 for t in types {
241 let _loader = create_loader(t);
242 }
243 }
244}