minecraft_java_rs_core/game/
lwjgl_native.rs1use serde::Deserialize;
2
3use crate::error::LaunchError;
4use crate::models::minecraft::{Library, MinecraftVersionJson};
5
6macro_rules! lwjgl_bytes {
17 ($arch:literal, $ver:literal) => {
18 include_bytes!(concat!(
19 "../../assets/LWJGL/",
20 $arch,
21 "/",
22 $ver,
23 ".json"
24 ))
25 .as_ref()
26 };
27}
28
29fn arm_lwjgl_data(arch: &str, version: &str) -> Option<&'static [u8]> {
32 let version = if version.contains("2.9") { "2.9.4" } else { version };
34
35 match (arch, version) {
36 ("aarch64", "2.9.4") => Some(lwjgl_bytes!("aarch64", "2.9.4")),
37 ("aarch64", "3.1.2") => Some(lwjgl_bytes!("aarch64", "3.1.2")),
38 ("aarch64", "3.2.2") => Some(lwjgl_bytes!("aarch64", "3.2.2")),
39 ("aarch64", "3.3.1") => Some(lwjgl_bytes!("aarch64", "3.3.1")),
40 ("aarch64", "3.3.2") => Some(lwjgl_bytes!("aarch64", "3.3.2")),
41 ("aarch", "2.9.4") => Some(lwjgl_bytes!("aarch", "2.9.4")),
42 ("aarch", "3.3.1") => Some(lwjgl_bytes!("aarch", "3.3.1")),
43 _ => None,
44 }
45}
46
47pub fn process_json(version: &mut MinecraftVersionJson) -> Result<(), LaunchError> {
58 let mapped_arch = match std::env::consts::ARCH {
59 "aarch64" => "aarch64",
60 "arm" => "aarch",
61 _ => return Ok(()), };
63
64 let version_jinput = find_version(&version.libraries, &["net.java.jinput:jinput-platform:", "net.java.jinput:jinput:"]);
66 let version_lwjgl = find_version(&version.libraries, &["org.lwjgl:lwjgl:", "org.lwjgl.lwjgl:lwjgl:"]);
67
68 if version_jinput.is_some() {
70 version.libraries.retain(|lib| !lib.name.contains("jinput"));
71 }
72
73 if let Some(lwjgl_ver) = version_lwjgl {
75 version.libraries.retain(|lib| !lib.name.contains("lwjgl"));
76
77 match arm_lwjgl_data(mapped_arch, &lwjgl_ver) {
78 Some(bytes) => {
79 let set: LwjglLibrarySet = serde_json::from_slice(bytes)?;
80 version.libraries.extend(set.libraries);
81 }
82 None => {
83 }
87 }
88 }
89
90 Ok(())
91}
92
93fn find_version(libs: &[Library], prefixes: &[&str]) -> Option<String> {
98 libs.iter()
99 .find(|lib| prefixes.iter().any(|p| lib.name.starts_with(p)))
100 .and_then(|lib| lib.name.split(':').last())
101 .map(|v| v.to_string())
102}
103
104#[derive(Deserialize)]
106struct LwjglLibrarySet {
107 libraries: Vec<Library>,
108}
109
110pub fn uses_lwjgl2(version: &MinecraftVersionJson) -> bool {
119 version
120 .libraries
121 .iter()
122 .any(|lib| lib.name.starts_with("org.lwjgl.lwjgl:lwjgl:2."))
123}
124
125#[cfg(target_os = "linux")]
127pub fn xrandr_in_path() -> bool {
128 std::env::var_os("PATH")
129 .map(|p| {
130 std::env::split_paths(&p)
131 .any(|dir| dir.join("xrandr").is_file())
132 })
133 .unwrap_or(false)
134}
135
136#[cfg(target_os = "linux")]
145pub async fn write_xrandr_stub(dir: &std::path::Path) -> Result<(), LaunchError> {
146 use std::os::unix::fs::PermissionsExt;
147
148 let stub = dir.join("xrandr");
149 if stub.exists() {
150 return Ok(());
151 }
152 tokio::fs::create_dir_all(dir).await?;
153
154 let script = "\
155#!/bin/sh
156# Minimal xrandr stub — used by LWJGL 2 on systems without the real xrandr.
157W=1920; H=1080
158if command -v xdpyinfo >/dev/null 2>&1; then
159 RES=$(xdpyinfo 2>/dev/null | awk '/dimensions:/{print $2}' | head -1)
160 if [ -n \"$RES\" ]; then W=${RES%x*}; H=${RES#*x}; fi
161fi
162printf 'Screen 0: minimum 8 x 8, current %s x %s, maximum 32767 x 32767\\n' \"$W\" \"$H\"
163printf 'HDMI-1 connected %sx%s+0+0 (normal left inverted right x axis y axis) 0mm x 0mm\\n' \"$W\" \"$H\"
164printf ' %sx%s 60.00*+\\n' \"$W\" \"$H\"
165";
166 tokio::fs::write(&stub, script).await?;
167 tokio::fs::set_permissions(&stub, std::fs::Permissions::from_mode(0o755)).await?;
168 Ok(())
169}
170
171#[cfg(test)]
174mod tests {
175 use super::*;
176
177 fn make_lib(name: &str) -> Library {
178 Library {
179 name: name.to_string(),
180 rules: None,
181 natives: None,
182 downloads: None,
183 url: None,
184 loader: None,
185 }
186 }
187
188 fn libs(names: &[&str]) -> Vec<Library> {
189 names.iter().map(|n| make_lib(n)).collect()
190 }
191
192 #[test]
193 fn find_version_returns_last_colon_segment() {
194 let l = libs(&["org.lwjgl:lwjgl:3.3.1", "org.lwjgl:lwjgl-opengl:3.3.1"]);
195 assert_eq!(find_version(&l, &["org.lwjgl:lwjgl:"]), Some("3.3.1".into()));
196 }
197
198 #[test]
199 fn find_version_returns_none_when_absent() {
200 let l = libs(&["com.example:something:1.0"]);
201 assert_eq!(find_version(&l, &["org.lwjgl:lwjgl:"]), None);
202 }
203
204 #[test]
205 fn find_version_matches_multiple_prefixes() {
206 let l = libs(&["org.lwjgl.lwjgl:lwjgl:2.9.4"]);
207 let v = find_version(&l, &["org.lwjgl:lwjgl:", "org.lwjgl.lwjgl:lwjgl:"]);
208 assert_eq!(v, Some("2.9.4".into()));
209 }
210
211 #[test]
212 fn arm_lwjgl_data_normalises_29x() {
213 assert!(arm_lwjgl_data("aarch64", "2.9.0").is_some());
215 assert!(arm_lwjgl_data("aarch64", "2.9.1").is_some());
216 assert!(arm_lwjgl_data("aarch64", "2.9.4").is_some());
217 }
218
219 #[test]
220 fn arm_lwjgl_data_returns_none_for_unknown() {
221 assert!(arm_lwjgl_data("aarch64", "4.0.0").is_none());
222 assert!(arm_lwjgl_data("x86_64", "3.3.1").is_none());
223 }
224
225 #[test]
226 fn process_json_noop_on_current_arch() {
227 if matches!(std::env::consts::ARCH, "aarch64" | "arm") {
229 return; }
231
232 let mut version = MinecraftVersionJson {
233 id: "1.20.4".into(),
234 version_type: "release".into(),
235 assets: None,
236 asset_index: None,
237 downloads: None,
238 libraries: libs(&["org.lwjgl:lwjgl:3.3.1", "org.lwjgl:lwjgl-opengl:3.3.1"]),
239 arguments: None,
240 minecraft_arguments: None,
241 java_version: None,
242 main_class: None,
243 has_natives: false,
244 };
245
246 let original_count = version.libraries.len();
247 process_json(&mut version).unwrap();
248 assert_eq!(version.libraries.len(), original_count);
250 }
251}