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
92impl LaunchOptions {
93 pub fn save_dir(&self) -> PathBuf {
96 match &self.instance {
97 Some(inst) => self.path.join("instances").join(inst),
98 None => self.path.clone(),
99 }
100 }
101
102 pub fn loader_dir(&self, name: &str) -> PathBuf {
106 match &self.loader.path {
107 Some(p) => PathBuf::from(p),
108 None => self.path.join("loader").join(name),
109 }
110 }
111
112 pub fn clamped_concurrency(&self) -> u32 {
114 self.download_concurrency.clamp(1, 30)
115 }
116
117 pub fn clamped_verify_concurrency(&self) -> u32 {
119 self.verify_concurrency.clamp(1, 16)
120 }
121}
122
123#[derive(Debug, Clone, Deserialize, Serialize)]
126pub struct MemoryConfig {
127 #[serde(default = "defaults::memory_min")]
129 pub min: String,
130 #[serde(default = "defaults::memory_max")]
132 pub max: String,
133}
134
135impl Default for MemoryConfig {
136 fn default() -> Self {
137 Self {
138 min: defaults::memory_min(),
139 max: defaults::memory_max(),
140 }
141 }
142}
143
144#[derive(Debug, Clone, Deserialize, Serialize, Default)]
147pub struct ScreenConfig {
148 pub width: Option<u32>,
149 pub height: Option<u32>,
150 #[serde(default)]
152 pub fullscreen: bool,
153}
154
155#[derive(Debug, Clone, Deserialize, Serialize)]
158pub struct JavaOptions {
159 #[serde(default)]
161 pub path: Option<PathBuf>,
162
163 #[serde(default)]
165 pub version: Option<String>,
166
167 #[serde(default = "defaults::java_image_type")]
169 pub image_type: String,
170}
171
172impl Default for JavaOptions {
173 fn default() -> Self {
174 Self {
175 path: None,
176 version: None,
177 image_type: defaults::java_image_type(),
178 }
179 }
180}
181
182#[derive(Debug, Clone, Deserialize, Serialize, Default)]
185pub struct LoaderConfig {
186 pub loader_type: Option<LoaderType>,
188
189 #[serde(default = "defaults::loader_build")]
192 pub build: String,
193
194 #[serde(default)]
196 pub enable: bool,
197
198 #[serde(default)]
201 pub path: Option<String>,
202
203 #[serde(default)]
206 pub config: Option<LoaderInnerConfig>,
207}
208
209#[derive(Debug, Clone, Deserialize, Serialize)]
211pub struct LoaderInnerConfig {
212 pub java_path: String,
213 pub minecraft_jar: String,
214 pub minecraft_json: String,
215}
216
217mod defaults {
220 pub fn timeout_secs() -> u64 { 10 }
221 pub fn download_concurrency() -> u32 { 5 }
222 pub fn verify_concurrency() -> u32 { 4 }
223 pub fn memory_min() -> String { "1G".into() }
224 pub fn memory_max() -> String { "2G".into() }
225 pub fn java_image_type() -> String { "jre".into() }
226 pub fn loader_build() -> String { "latest".into() }
227}
228
229#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn save_dir_without_instance() {
237 let opts = make_opts(None);
238 assert_eq!(opts.save_dir(), PathBuf::from("/mc"));
239 }
240
241 #[test]
242 fn save_dir_with_instance() {
243 let opts = make_opts(Some("test-world".into()));
244 assert_eq!(opts.save_dir(), PathBuf::from("/mc/instances/test-world"));
245 }
246
247 #[test]
248 fn concurrency_clamp() {
249 let mut opts = make_opts(None);
250 opts.download_concurrency = 0;
251 assert_eq!(opts.clamped_concurrency(), 1);
252 opts.download_concurrency = 99;
253 assert_eq!(opts.clamped_concurrency(), 30);
254 opts.download_concurrency = 5;
255 assert_eq!(opts.clamped_concurrency(), 5);
256 }
257
258 #[test]
259 fn verify_concurrency_clamp() {
260 let mut opts = make_opts(None);
261 opts.verify_concurrency = 0;
262 assert_eq!(opts.clamped_verify_concurrency(), 1);
263 opts.verify_concurrency = 99;
264 assert_eq!(opts.clamped_verify_concurrency(), 16);
265 opts.verify_concurrency = 4;
266 assert_eq!(opts.clamped_verify_concurrency(), 4);
267 }
268
269 #[test]
270 fn memory_defaults() {
271 let m = MemoryConfig::default();
272 assert_eq!(m.min, "1G");
273 assert_eq!(m.max, "2G");
274 }
275
276 fn make_opts(instance: Option<String>) -> LaunchOptions {
277 use crate::models::minecraft::Authenticator;
278 LaunchOptions {
279 path: PathBuf::from("/mc"),
280 version: "1.20.4".into(),
281 authenticator: Authenticator {
282 access_token: "tok".into(),
283 name: "Player".into(),
284 uuid: "uuid".into(),
285 xbox_account: None,
286 user_properties: None,
287 client_id: None,
288 client_token: None,
289 },
290 timeout_secs: 10,
291 download_concurrency: 5,
292 verify_concurrency: 4,
293 memory: MemoryConfig::default(),
294 java: JavaOptions::default(),
295 loader: LoaderConfig::default(),
296 screen: ScreenConfig::default(),
297 verify: false,
298 game_args: vec![],
299 jvm_args: vec![],
300 instance,
301 url: None,
302 mcp: None,
303 intel_enabled_mac: false,
304 bypass_offline: false,
305 skip_bundle_check: false,
306 }
307 }
308}