minecraft_java_rs_core/launcher/
options.rs1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4
5use crate::models::{loader::LoaderType, minecraft::Authenticator};
6
7#[derive(Debug, Clone, Deserialize, Serialize)]
13pub struct LaunchOptions {
14 pub path: PathBuf,
17
18 pub version: String,
21
22 pub authenticator: Authenticator,
24
25 #[serde(default = "defaults::timeout_secs")]
27 pub timeout_secs: u64,
28
29 #[serde(default = "defaults::download_concurrency")]
31 pub download_concurrency: u32,
32
33 #[serde(default = "defaults::verify_concurrency")]
36 pub verify_concurrency: u32,
37
38 #[serde(default)]
39 pub memory: MemoryConfig,
40
41 #[serde(default)]
42 pub java: JavaOptions,
43
44 #[serde(default)]
45 pub loader: LoaderConfig,
46
47 #[serde(default)]
48 pub screen: ScreenConfig,
49
50 #[serde(default)]
52 pub verify: bool,
53
54 #[serde(default)]
56 pub game_args: Vec<String>,
57
58 #[serde(default)]
60 pub jvm_args: Vec<String>,
61
62 #[serde(default)]
65 pub instance: Option<String>,
66
67 #[serde(default)]
69 pub url: Option<String>,
70
71 #[serde(default)]
73 pub mcp: Option<String>,
74
75 #[serde(default)]
77 pub intel_enabled_mac: bool,
78
79 #[serde(default)]
82 pub bypass_offline: bool,
83
84 #[serde(default)]
89 pub skip_bundle_check: bool,
90
91 #[serde(default)]
98 pub force_ipv4: bool,
99}
100
101impl LaunchOptions {
102 pub fn save_dir(&self) -> PathBuf {
105 match &self.instance {
106 Some(inst) => self.path.join("instances").join(inst),
107 None => self.path.clone(),
108 }
109 }
110
111 pub fn loader_dir(&self, name: &str) -> PathBuf {
115 match &self.loader.path {
116 Some(p) => PathBuf::from(p),
117 None => self.path.join("loader").join(name),
118 }
119 }
120
121 pub fn clamped_concurrency(&self) -> u32 {
126 self.download_concurrency.clamp(1, 64)
127 }
128
129 pub fn clamped_verify_concurrency(&self) -> u32 {
131 self.verify_concurrency.clamp(1, 16)
132 }
133}
134
135#[derive(Debug, Clone, Deserialize, Serialize)]
138pub struct MemoryConfig {
139 #[serde(default = "defaults::memory_min")]
141 pub min: String,
142 #[serde(default = "defaults::memory_max")]
144 pub max: String,
145}
146
147impl Default for MemoryConfig {
148 fn default() -> Self {
149 Self {
150 min: defaults::memory_min(),
151 max: defaults::memory_max(),
152 }
153 }
154}
155
156#[derive(Debug, Clone, Deserialize, Serialize, Default)]
159pub struct ScreenConfig {
160 pub width: Option<u32>,
161 pub height: Option<u32>,
162 #[serde(default)]
164 pub fullscreen: bool,
165}
166
167#[derive(Debug, Clone, Deserialize, Serialize)]
170pub struct JavaOptions {
171 #[serde(default)]
173 pub path: Option<PathBuf>,
174
175 #[serde(default)]
177 pub version: Option<String>,
178
179 #[serde(default = "defaults::java_image_type")]
181 pub image_type: String,
182}
183
184impl Default for JavaOptions {
185 fn default() -> Self {
186 Self {
187 path: None,
188 version: None,
189 image_type: defaults::java_image_type(),
190 }
191 }
192}
193
194#[derive(Debug, Clone, Deserialize, Serialize, Default)]
197pub struct LoaderConfig {
198 pub loader_type: Option<LoaderType>,
200
201 #[serde(default = "defaults::loader_build")]
204 pub build: String,
205
206 #[serde(default)]
208 pub enable: bool,
209
210 #[serde(default)]
213 pub path: Option<String>,
214
215 #[serde(default)]
218 pub config: Option<LoaderInnerConfig>,
219}
220
221#[derive(Debug, Clone, Deserialize, Serialize)]
223pub struct LoaderInnerConfig {
224 pub java_path: String,
225 pub minecraft_jar: String,
226 pub minecraft_json: String,
227}
228
229mod defaults {
232 pub fn timeout_secs() -> u64 {
233 10
234 }
235 pub fn download_concurrency() -> u32 {
236 5
237 }
238 pub fn verify_concurrency() -> u32 {
239 4
240 }
241 pub fn memory_min() -> String {
242 "1G".into()
243 }
244 pub fn memory_max() -> String {
245 "2G".into()
246 }
247 pub fn java_image_type() -> String {
248 "jre".into()
249 }
250 pub fn loader_build() -> String {
251 "latest".into()
252 }
253}
254
255#[cfg(test)]
258mod tests {
259 use super::*;
260
261 #[test]
262 fn save_dir_without_instance() {
263 let opts = make_opts(None);
264 assert_eq!(opts.save_dir(), PathBuf::from("/mc"));
265 }
266
267 #[test]
268 fn save_dir_with_instance() {
269 let opts = make_opts(Some("test-world".into()));
270 assert_eq!(opts.save_dir(), PathBuf::from("/mc/instances/test-world"));
271 }
272
273 #[test]
274 fn concurrency_clamp() {
275 let mut opts = make_opts(None);
276 opts.download_concurrency = 0;
277 assert_eq!(opts.clamped_concurrency(), 1);
278 opts.download_concurrency = 99;
279 assert_eq!(opts.clamped_concurrency(), 64);
280 opts.download_concurrency = 5;
281 assert_eq!(opts.clamped_concurrency(), 5);
282 }
283
284 #[test]
285 fn verify_concurrency_clamp() {
286 let mut opts = make_opts(None);
287 opts.verify_concurrency = 0;
288 assert_eq!(opts.clamped_verify_concurrency(), 1);
289 opts.verify_concurrency = 99;
290 assert_eq!(opts.clamped_verify_concurrency(), 16);
291 opts.verify_concurrency = 4;
292 assert_eq!(opts.clamped_verify_concurrency(), 4);
293 }
294
295 #[test]
296 fn memory_defaults() {
297 let m = MemoryConfig::default();
298 assert_eq!(m.min, "1G");
299 assert_eq!(m.max, "2G");
300 }
301
302 fn make_opts(instance: Option<String>) -> LaunchOptions {
303 use crate::models::minecraft::Authenticator;
304 LaunchOptions {
305 path: PathBuf::from("/mc"),
306 version: "1.20.4".into(),
307 authenticator: Authenticator {
308 access_token: "tok".into(),
309 name: "Player".into(),
310 uuid: "uuid".into(),
311 xbox_account: None,
312 user_properties: None,
313 client_id: None,
314 client_token: None,
315 },
316 timeout_secs: 10,
317 download_concurrency: 5,
318 verify_concurrency: 4,
319 memory: MemoryConfig::default(),
320 java: JavaOptions::default(),
321 loader: LoaderConfig::default(),
322 screen: ScreenConfig::default(),
323 verify: false,
324 game_args: vec![],
325 jvm_args: vec![],
326 instance,
327 url: None,
328 mcp: None,
329 intel_enabled_mac: false,
330 bypass_offline: false,
331 skip_bundle_check: false,
332 force_ipv4: false,
333 }
334 }
335}