1use std::collections::HashMap;
8
9use crate::{
10 core::{
11 maven::MavenCoordinate,
12 version::{Library, LibraryArtifact, LibraryDownloads, VersionJson},
13 },
14 platform::{Arch, Os, Platform},
15};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum CompatibilityPolicy {
20 Auto,
22 Disabled,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum CompatibilityPatch {
29 LegacyMacArm64Lwjgl2,
31 MacArm64Lwjgl3,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum WindowingStrategy {
38 CurrentProcess,
40 MacOsAppBundle,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub struct WindowingHint {
47 pub strategy: WindowingStrategy,
49 pub requires_visible_window_verification: bool,
51 pub reason: &'static str,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub struct JavaRuntimeHint {
58 pub major_version: i32,
60 pub arch: Arch,
62 pub distribution_hint: &'static str,
64 pub reason: &'static str,
66}
67
68#[derive(Debug, Clone, PartialEq)]
70pub struct CompatibilityResult {
71 pub version: VersionJson,
73 pub applied_patches: Vec<CompatibilityPatch>,
75 pub java_runtime: Option<JavaRuntimeHint>,
77 pub windowing: WindowingHint,
79}
80
81pub fn apply_compatibility(
85 version: &VersionJson,
86 platform: Platform,
87 policy: CompatibilityPolicy,
88) -> CompatibilityResult {
89 if policy == CompatibilityPolicy::Disabled {
90 return CompatibilityResult {
91 version: version.clone(),
92 applied_patches: Vec::new(),
93 java_runtime: None,
94 windowing: current_process_windowing_hint(),
95 };
96 }
97
98 if needs_legacy_macos_lwjgl2_patch(version, platform) {
99 return CompatibilityResult {
100 version: apply_legacy_macos_lwjgl2_patch(version),
101 applied_patches: vec![CompatibilityPatch::LegacyMacArm64Lwjgl2],
102 java_runtime: Some(JavaRuntimeHint {
103 major_version: 8,
104 arch: Arch::Aarch64,
105 distribution_hint: "Azul Zulu Java 8 arm64",
106 reason: "Legacy LWJGL 2 Minecraft versions need an arm64 Java 8 runtime on Apple Silicon.",
107 }),
108 windowing: legacy_macos_lwjgl2_windowing_hint(),
109 };
110 }
111
112 if needs_macos_arm64_lwjgl3_patch(version, platform) {
113 return CompatibilityResult {
114 version: apply_macos_arm64_lwjgl3_patch(version),
115 applied_patches: vec![CompatibilityPatch::MacArm64Lwjgl3],
116 java_runtime: Some(JavaRuntimeHint {
117 major_version: version
118 .java_version
119 .as_ref()
120 .map(|java| java.major_version)
121 .unwrap_or(8),
122 arch: Arch::Aarch64,
123 distribution_hint: "arm64 Java runtime matching version.json javaVersion",
124 reason: "Older LWJGL 3 Minecraft versions need arm64 macOS native libraries on Apple Silicon.",
125 }),
126 windowing: current_process_windowing_hint(),
127 };
128 }
129
130 CompatibilityResult {
131 version: version.clone(),
132 applied_patches: Vec::new(),
133 java_runtime: None,
134 windowing: current_process_windowing_hint(),
135 }
136}
137
138fn current_process_windowing_hint() -> WindowingHint {
139 WindowingHint {
140 strategy: WindowingStrategy::CurrentProcess,
141 requires_visible_window_verification: false,
142 reason: "The version can be launched as a normal Java process by the launcher.",
143 }
144}
145
146fn legacy_macos_lwjgl2_windowing_hint() -> WindowingHint {
147 WindowingHint {
148 strategy: WindowingStrategy::MacOsAppBundle,
149 requires_visible_window_verification: true,
150 reason: "Legacy LWJGL 2 can create 0x0 invisible windows when spawned directly from a CLI process on Apple Silicon; launch from a macOS app bundle or other GUI host and verify the visible window.",
151 }
152}
153
154fn needs_legacy_macos_lwjgl2_patch(version: &VersionJson, platform: Platform) -> bool {
155 platform.os == Os::MacOs
156 && platform.arch == Arch::Aarch64
157 && version
158 .libraries
159 .iter()
160 .any(|library| library.name.starts_with("org.lwjgl.lwjgl:lwjgl:"))
161}
162
163fn needs_macos_arm64_lwjgl3_patch(version: &VersionJson, platform: Platform) -> bool {
164 platform.os == Os::MacOs
165 && platform.arch == Arch::Aarch64
166 && version
167 .libraries
168 .iter()
169 .any(|library| library.name.starts_with("org.lwjgl:"))
170 && !version.libraries.iter().any(|library| {
171 library.name.contains("3.3.1-mmachina.1")
172 || library.name.contains(":natives-macos-arm64")
173 || library.name.contains(":natives-osx-arm64")
174 })
175}
176
177fn apply_legacy_macos_lwjgl2_patch(version: &VersionJson) -> VersionJson {
178 let mut patched = version.clone();
179 patched
180 .libraries
181 .retain(|library| !is_legacy_lwjgl2_replaced_library(&library.name));
182 patched.libraries.extend(legacy_macos_lwjgl2_libraries());
183 patched
184}
185
186fn apply_macos_arm64_lwjgl3_patch(version: &VersionJson) -> VersionJson {
187 let mut patched = version.clone();
188 patched
189 .libraries
190 .retain(|library| !is_lwjgl3_replaced_library(&library.name));
191 patched.libraries.extend(macos_arm64_lwjgl3_libraries());
192 patched
193}
194
195fn is_legacy_lwjgl2_replaced_library(name: &str) -> bool {
196 [
197 "org.lwjgl.lwjgl:",
198 "net.java.jinput:",
199 "net.java.jutils:",
200 "ca.weblite:java-objc-bridge:",
201 "com.mojang:text2speech:",
202 ]
203 .iter()
204 .any(|prefix| name.starts_with(prefix))
205}
206
207fn is_lwjgl3_replaced_library(name: &str) -> bool {
208 name.starts_with("org.lwjgl:") || name.starts_with("ca.weblite:java-objc-bridge:")
209}
210
211fn legacy_macos_lwjgl2_libraries() -> Vec<Library> {
212 vec![
213 artifact_library(
214 "com.mojang:text2speech:1.11.3",
215 "https://libraries.minecraft.net/com/mojang/text2speech/1.11.3/text2speech-1.11.3.jar",
216 "f378f889797edd7df8d32272c06ca80a1b6b0f58",
217 13164,
218 None,
219 ),
220 artifact_library(
221 "ca.weblite:java-objc-bridge:1.1.0-mmachina.1",
222 "https://github.com/MinecraftMachina/Java-Objective-C-Bridge/releases/download/1.1.0-mmachina.1/java-objc-bridge-1.1.jar",
223 "369a83621e3c65496348491e533cb97fe5f2f37d",
224 91947,
225 None,
226 ),
227 native_library(
228 "net.java.jinput:jinput-platform:2.0.5",
229 "net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar",
230 "https://libraries.minecraft.net/net/java/jinput/jinput-platform/2.0.5/jinput-platform-2.0.5-natives-osx.jar",
231 "53f9c919f34d2ca9de8c51fc4e1e8282029a9232",
232 12186,
233 ),
234 artifact_library(
235 "net.java.jinput:jinput:2.0.5",
236 "https://libraries.minecraft.net/net/java/jinput/jinput/2.0.5/jinput-2.0.5.jar",
237 "39c7796b469a600f72380316f6b1f11db6c2c7c4",
238 208338,
239 None,
240 ),
241 artifact_library(
242 "net.java.jutils:jutils:1.0.0",
243 "https://libraries.minecraft.net/net/java/jutils/jutils/1.0.0/jutils-1.0.0.jar",
244 "e12fe1fda814bd348c1579329c86943d2cd3c6a6",
245 7508,
246 None,
247 ),
248 lwjgl_platform_library(),
249 artifact_library(
250 "org.lwjgl.lwjgl:lwjgl:2.9.4-nightly-20150209",
251 "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl/2.9.4-nightly-20150209/lwjgl-2.9.4-nightly-20150209.jar",
252 "697517568c68e78ae0b4544145af031c81082dfe",
253 1047168,
254 None,
255 ),
256 artifact_library(
257 "org.lwjgl.lwjgl:lwjgl_util:2.9.4-nightly-20150209",
258 "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl_util/2.9.4-nightly-20150209/lwjgl_util-2.9.4-nightly-20150209.jar",
259 "d51a7c040a721d13efdfbd34f8b257b2df882ad0",
260 173887,
261 None,
262 ),
263 ]
264}
265
266fn macos_arm64_lwjgl3_libraries() -> Vec<Library> {
267 vec![
268 artifact_library(
269 "ca.weblite:java-objc-bridge:1.1.0-mmachina.1",
270 "https://github.com/MinecraftMachina/Java-Objective-C-Bridge/releases/download/1.1.0-mmachina.1/java-objc-bridge-1.1.jar",
271 "369a83621e3c65496348491e533cb97fe5f2f37d",
272 91947,
273 None,
274 ),
275 lwjgl3_macos_arm64_library(
276 "lwjgl-glfw",
277 "e9a101bca4fa30d26b21b526ff28e7c2d8927f1b",
278 130128,
279 "71d793d0a5a42e3dfe78eb882abc2523a2c6b496",
280 129076,
281 ),
282 lwjgl3_macos_arm64_library(
283 "lwjgl-jemalloc",
284 "4fb94224378d3588d52d2beb172f2eeafea2d546",
285 36976,
286 "b0be721188d2e7195798780b1c5fe7eafe8091c1",
287 103478,
288 ),
289 lwjgl3_macos_arm64_library(
290 "lwjgl-openal",
291 "d48e753d85916fc8a200ccddc709b36e3865cc4e",
292 88880,
293 "6b80fc0b982a0723b141e88859c42d6f71bd723f",
294 346131,
295 ),
296 lwjgl3_macos_arm64_library(
297 "lwjgl-opengl",
298 "962c2a8d2a8cdd3b89de3d78d766ab5e2133c2f4",
299 929233,
300 "bb575058e0372f515587b5d2d04ff7db185f3ffe",
301 41667,
302 ),
303 lwjgl3_macos_arm64_library(
304 "lwjgl-stb",
305 "703e4b533e2542560e9f94d6d8bd148be1c1d572",
306 113273,
307 "98f0ad956c754723ef354d50057cc30417ef376a",
308 178409,
309 ),
310 lwjgl3_macos_arm64_library(
311 "lwjgl-tinyfd",
312 "1203660b3131cbb8681b17ce6437412545be95e0",
313 6802,
314 "015b931a2daba8f0c317d84c9d14e8e98ae56e0c",
315 41384,
316 ),
317 lwjgl3_macos_arm64_library(
318 "lwjgl",
319 "8e664dd69ad7bbcf2053da23efc7848e39e498db",
320 719038,
321 "984df31fadaab86838877b112e5b4e4f68a00ccf",
322 42693,
323 ),
324 ]
325}
326
327fn artifact_library(
328 name: &str,
329 url: &str,
330 sha1: &str,
331 size: i64,
332 path_override: Option<&str>,
333) -> Library {
334 let path = path_override.map(ToOwned::to_owned).unwrap_or_else(|| {
335 MavenCoordinate::parse(name)
336 .expect("static coordinate")
337 .artifact_path()
338 .to_string_lossy()
339 .to_string()
340 });
341 Library {
342 name: name.to_string(),
343 url: None,
344 rules: Vec::new(),
345 downloads: Some(LibraryDownloads {
346 artifact: Some(LibraryArtifact {
347 path,
348 url: url.to_string(),
349 sha1: sha1.to_string(),
350 size,
351 }),
352 classifiers: HashMap::new(),
353 }),
354 natives: None,
355 extract: None,
356 }
357}
358
359fn lwjgl3_macos_arm64_library(
360 artifact: &str,
361 artifact_sha1: &str,
362 artifact_size: i64,
363 native_sha1: &str,
364 native_size: i64,
365) -> Library {
366 let name = format!("org.lwjgl:{artifact}:3.3.1-mmachina.1");
367 let artifact_path = MavenCoordinate::parse(&name)
368 .expect("static coordinate")
369 .artifact_path()
370 .to_string_lossy()
371 .to_string();
372 let native_path = format!(
373 "org/lwjgl/{artifact}/3.3.1-mmachina.1/{artifact}-3.3.1-mmachina.1-natives-macos.jar"
374 );
375 let release_base =
376 "https://github.com/MinecraftMachina/lwjgl3/releases/download/3.3.1-mmachina.1";
377
378 let mut classifiers = HashMap::new();
379 classifiers.insert(
380 "natives-macos".to_string(),
381 LibraryArtifact {
382 path: native_path,
383 url: format!("{release_base}/{artifact}-natives-macos-arm64.jar"),
384 sha1: native_sha1.to_string(),
385 size: native_size,
386 },
387 );
388 let mut natives = HashMap::new();
389 natives.insert("osx".to_string(), "natives-macos".to_string());
390 let mut extract = HashMap::new();
391 extract.insert("exclude".to_string(), vec!["META-INF/".to_string()]);
392
393 Library {
394 name,
395 url: None,
396 rules: Vec::new(),
397 downloads: Some(LibraryDownloads {
398 artifact: Some(LibraryArtifact {
399 path: artifact_path,
400 url: format!("{release_base}/{artifact}.jar"),
401 sha1: artifact_sha1.to_string(),
402 size: artifact_size,
403 }),
404 classifiers,
405 }),
406 natives: Some(natives),
407 extract: Some(extract),
408 }
409}
410
411fn native_library(name: &str, path: &str, url: &str, sha1: &str, size: i64) -> Library {
412 let mut classifiers = HashMap::new();
413 classifiers.insert(
414 "natives-osx".to_string(),
415 LibraryArtifact {
416 path: path.to_string(),
417 url: url.to_string(),
418 sha1: sha1.to_string(),
419 size,
420 },
421 );
422 let mut natives = HashMap::new();
423 natives.insert("osx".to_string(), "natives-osx".to_string());
424 let mut extract = HashMap::new();
425 extract.insert("exclude".to_string(), vec!["META-INF/".to_string()]);
426
427 Library {
428 name: name.to_string(),
429 url: None,
430 rules: Vec::new(),
431 downloads: Some(LibraryDownloads {
432 artifact: None,
433 classifiers,
434 }),
435 natives: Some(natives),
436 extract: Some(extract),
437 }
438}
439
440fn lwjgl_platform_library() -> Library {
441 let mut library = native_library(
442 "org.lwjgl.lwjgl:lwjgl-platform:2.9.4-nightly-20150209-mmachina.2",
443 "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar",
444 "https://github.com/MinecraftMachina/lwjgl/releases/download/2.9.4-20150209-mmachina.2/lwjgl-platform-2.9.4-nightly-20150209-natives-osx.jar",
445 "eff546c0b319d6ffc7a835652124c18089c67f36",
446 488316,
447 );
448 if let Some(downloads) = &mut library.downloads {
449 downloads.artifact = Some(LibraryArtifact {
450 path: "org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar".to_string(),
451 url: "https://libraries.minecraft.net/org/lwjgl/lwjgl/lwjgl-platform/2.9.4-nightly-20150209/lwjgl-platform-2.9.4-nightly-20150209.jar".to_string(),
452 sha1: "b04f3ee8f5e43fa3b162981b50bb72fe1acabb33".to_string(),
453 size: 22,
454 });
455 }
456 library
457}