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