lighty_launch/
arguments.rs1use lighty_version::version_metadata::VersionBuilder;
2use lighty_loaders::version::Version;
3use std::borrow::Cow;
4use std::collections::HashMap;
5
6pub trait Arguments<'a> {
7 fn build_arguments(
8 &self,
9 builder: &VersionBuilder,
10 username: &str,
11 uuid: &str,
12 ) -> Vec<String>;
13}
14
15impl<'a> Arguments<'a> for Version<'a> {
16 fn build_arguments(
17 &self,
18 builder: &VersionBuilder,
19 username: &str,
20 uuid: &str,
21 ) -> Vec<String> {
22 let variables = create_variable_map(self, builder, username, uuid);
24
25 let game_args = replace_variables_in_vec(&builder.arguments.game, &variables);
27
28 let mut jvm_args = builder.arguments.jvm
29 .as_ref()
30 .map(|jvm| replace_variables_in_vec(jvm, &variables))
31 .unwrap_or_else(|| build_default_jvm_args(&variables));
32
33 if !jvm_args.iter().any(|arg| arg.starts_with("-Djava.library.path=")) {
37 let natives_dir = variables.get("natives_directory").cloned().unwrap_or_default();
38 jvm_args.insert(0, format!("-Djava.library.path={}", natives_dir));
39 }
40
41 if !jvm_args.iter().any(|arg| arg.starts_with("-Dminecraft.launcher.brand=")) {
43 let launcher_name = variables.get("launcher_name").cloned().unwrap_or_default();
44 jvm_args.insert(0, format!("-Dminecraft.launcher.brand={}", launcher_name));
45 }
46
47 if !jvm_args.iter().any(|arg| arg.starts_with("-Dminecraft.launcher.version=")) {
48 let launcher_version = variables.get("launcher_version").cloned().unwrap_or_default();
49 jvm_args.insert(0, format!("-Dminecraft.launcher.version={}", launcher_version));
50 }
51
52 if !jvm_args.contains(&"-cp".to_string()) {
54 let classpath = variables.get("classpath").cloned().unwrap_or_default();
55 jvm_args.push("-cp".to_string());
56 jvm_args.push(classpath);
57 }
58
59 let mut full_args = jvm_args;
61 full_args.push(builder.main_class.main_class.clone());
62 full_args.extend(game_args);
63 tracing::debug!(args = ?full_args, "Launch arguments built");
64
65 full_args
66 }
67}
68
69fn create_variable_map<'a>(
71 version: &Version<'a>,
72 builder: &VersionBuilder,
73 username: &str,
74 uuid: &str,
75) -> HashMap<String, String> {
76 let mut map = HashMap::new();
77
78 #[cfg(target_os = "windows")]
79 let classpath_separator = ";";
80 #[cfg(not(target_os = "windows"))]
81 let classpath_separator = ":";
82
83 map.insert("auth_player_name".to_string(), username.to_string());
85 map.insert("auth_uuid".to_string(), uuid.to_string());
86 map.insert("auth_access_token".to_string(), "0".to_string());
87 map.insert("auth_xuid".to_string(), "0".to_string());
88 map.insert("clientid".to_string(), "{client-id}".to_string());
89 map.insert("user_type".to_string(), "legacy".to_string());
90 map.insert("user_properties".to_string(), "{}".to_string());
91
92 map.insert("version_name".to_string(), version.name.to_string());
94 map.insert("version_type".to_string(), "release".to_string());
95
96 map.insert("game_directory".to_string(), version.game_dirs.display().to_string());
98 map.insert("assets_root".to_string(), version.game_dirs.join("assets").display().to_string());
99 map.insert("natives_directory".to_string(), version.game_dirs.join("natives").display().to_string());
100 map.insert("library_directory".to_string(), version.game_dirs.join("libraries").display().to_string());
101
102 let assets_index_name = builder.assets_index
104 .as_ref()
105 .map(|idx| idx.id.clone())
106 .unwrap_or_else(|| version.minecraft_version.to_string());
107 map.insert("assets_index_name".to_string(), assets_index_name);
108
109 map.insert("launcher_name".to_string(), "LightyLauncher".to_string());
111 map.insert("launcher_version".to_string(), "1.0.0".to_string());
112
113 let classpath = build_classpath(version, &builder.libraries);
115 map.insert("classpath".to_string(), classpath);
116 map.insert("classpath_separator".to_string(), classpath_separator.to_string());
117
118 map
119}
120
121fn build_classpath<'a>(version: &Version<'a>, libraries: &[lighty_version::version_metadata::Library]) -> String {
123 #[cfg(target_os = "windows")]
124 let separator = ";";
125 #[cfg(not(target_os = "windows"))]
126 let separator = ":";
127
128 let lib_dir = version.game_dirs.join("libraries");
129
130 let mut classpath_entries: Vec<String> = libraries
131 .iter()
132 .filter_map(|lib| {
133 lib.path.as_ref().map(|path| {
134 lib_dir.join(path).display().to_string()
135 })
136 })
137 .collect();
138
139 classpath_entries.push(
141 version.game_dirs.join(format!("{}.jar", version.name)).display().to_string()
142 );
143
144 classpath_entries.join(separator)
145}
146
147fn build_default_jvm_args(variables: &HashMap<String, String>) -> Vec<String> {
149 let natives_dir = variables.get("natives_directory").cloned().unwrap_or_default();
150 let launcher_name = variables.get("launcher_name").cloned().unwrap_or_default();
151 let launcher_version = variables.get("launcher_version").cloned().unwrap_or_default();
152 let classpath = variables.get("classpath").cloned().unwrap_or_default();
153
154 vec![
155 "-Xms1024M".to_string(),
156 "-Xmx2048M".to_string(),
157 format!("-Djava.library.path={}", natives_dir),
158 format!("-Dminecraft.launcher.brand={}", launcher_name),
159 format!("-Dminecraft.launcher.version={}", launcher_version),
160 "-cp".to_string(),
161 classpath,
162 ]
163}
164
165
166fn replace_variables_in_vec(args: &[String], variables: &HashMap<String, String>) -> Vec<String> {
168 args.iter()
169 .map(|arg| replace_variables_cow(arg, variables).into_owned())
170 .collect()
171}
172
173fn replace_variables_cow<'a>(
176 input: &'a str,
177 variables: &HashMap<String, String>
178) -> Cow<'a, str> {
179 if !input.contains("${") {
181 return Cow::Borrowed(input); }
183
184 let mut result = String::with_capacity(input.len() + 128);
186 let mut last_end = 0;
187
188 for (start, _) in input.match_indices("${") {
190 if let Some(end_offset) = input[start..].find('}') {
191 let end = start + end_offset;
192 let key = &input[start + 2..end];
193
194 result.push_str(&input[last_end..start]);
196
197 if let Some(value) = variables.get(key) {
199 result.push_str(value);
200 } else {
201 result.push_str(&input[start..=end]);
202 }
203
204 last_end = end + 1;
205 }
206 }
207
208 result.push_str(&input[last_end..]);
210 Cow::Owned(result)
211}
212
213