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