feat(termux): MC 启动接线——Linux arm64 natives + softpipe 子进程环境
- Library::native_classifier:Linux/aarch64 上把 LWJGL3 的 natives-linux 改写成 natives-linux-arm64(Mojang 清单只有 x86_64) - install::libraries:arm64 分类器在清单里没有下载信息时,按 Maven 相对路径 兜底到阿里云镜像(实测 Mojang libraries 404、Aliyun 200); 单测覆盖 arm64 兜底与 x86_64 不变 - launch::defaults:Termux glibc 构建启动 MC 时默认带 MESA_LOADER_DRIVER_OVERRIDE=swrast / LIBGL_ALWAYS_SOFTWARE=1(用户可覆盖) - 文档:JRE patchelf 一次性步骤与接线现状;README 同步 - 验证:patched Temurin java 直接执行;LWJGL arm64+GLFW 探针 gl-ok
This commit is contained in:
1 parent
02ed09ae49
commit
7eed6c6261
5 files changed
+179
-26
No files matched your search
+4
-4
@@ -15,7 +15,7 @@
|
|||||||
| Java 发现 | ✅ | `JAVA_HOME`/PATH + `$PREFIX/lib/jvm` 扫描 |
|
| Java 发现 | ✅ | `JAVA_HOME`/PATH + `$PREFIX/lib/jvm` 扫描 |
|
||||||
| Java 自动安装 | ✅ | `ull java install 17` 由内置镜像装完 openjdk-17(约 20 s) |
|
| Java 自动安装 | ✅ | `ull java install 17` 由内置镜像装完 openjdk-17(约 20 s) |
|
||||||
| GPU(wgpu) | ✅ | 自编 Mesa 26.2.6 + KGSL;wgpu 选中 Adreno 840(Vulkan/turnip) |
|
| GPU(wgpu) | ✅ | 自编 Mesa 26.2.6 + KGSL;wgpu 选中 Adreno 840(Vulkan/turnip) |
|
||||||
| Minecraft 运行 | 🟡 | 栈已验证:glibc JRE + LWJGL arm64 + GLX/softpipe(GL 3.3);启动器接线待做 |
|
| Minecraft 运行 | 🟡 | 栈已验证;启动器已接线(natives arm64 / softpipe env);待真实 MC 验证 |
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
@@ -39,9 +39,9 @@ DISPLAY=:0 ./unlucky-launcher-iced --software-renderer
|
|||||||
glibc Mesa 太旧(24.2.6 不认识 Adreno 840)。驱动缺失时自动降级 CPU,不会崩。
|
glibc Mesa 太旧(24.2.6 不认识 Adreno 840)。驱动缺失时自动降级 CPU,不会崩。
|
||||||
- **MC**:Android 原生 natives 走不通(LWJGL 没有官方 Android/bionic 产物,且 GLFW 的
|
- **MC**:Android 原生 natives 走不通(LWJGL 没有官方 Android/bionic 产物,且 GLFW 的
|
||||||
窗口模型与 termux-x11 的 X11 不兼容)。有效路线是 glibc JVM——Temurin 21 JRE + AWT、
|
窗口模型与 termux-x11 的 X11 不兼容)。有效路线是 glibc JVM——Temurin 21 JRE + AWT、
|
||||||
LWJGL arm64 + GLFW + GLX/softpipe 已手动跑通(GL 3.3)。启动器接线还差两步:
|
LWJGL arm64 + GLFW + GLX/softpipe 已手动跑通(GL 3.3)。启动器已接线:
|
||||||
Linux/aarch64 上把 natives 选成 `natives-linux-arm64`,并给 MC 子进程带 glibc JVM 与
|
Linux/aarch64 自动选 `natives-linux-arm64`(阿里云 Maven 兜底),MC 子进程默认带
|
||||||
`MESA_LOADER_DRIVER_OVERRIDE=swrast`。
|
softpipe 环境。还差把实例 Java 指到 glibc JRE 后跑一次真实 MC。
|
||||||
- Termux 上三条测试失败是环境差异(`/proc/*/children`、长路径脱敏、mtime flaky),
|
- Termux 上三条测试失败是环境差异(`/proc/*/children`、长路径脱敏、mtime flaky),
|
||||||
PC 的 Linux CI 不受影响。
|
PC 的 Linux CI 不受影响。
|
||||||
|
|
||||||
|
|||||||
@@ -79,31 +79,42 @@ pub fn plan(
|
|||||||
let Some(classifier) = library.native_classifier(environment) else {
|
let Some(classifier) = library.native_classifier(environment) else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let Some(artifact) = library.native_artifact(&classifier) else {
|
|
||||||
warnings.push(format!(
|
|
||||||
"{} 的 natives 分类器 {classifier} 没有下载信息,已跳过",
|
|
||||||
library.name
|
|
||||||
));
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let Ok(native_relative) = library.native_relative_path(&classifier) else {
|
let Ok(native_relative) = library.native_relative_path(&classifier) else {
|
||||||
warnings.push(format!("{} 的 natives 路径推导失败,已跳过", library.name));
|
warnings.push(format!("{} 的 natives 路径推导失败,已跳过", library.name));
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if !artifact.has_url() {
|
let dest = layout.libraries_dir().join(&native_relative);
|
||||||
warnings.push(format!(
|
// Linux arm64:Mojang 清单没有 arm64 分类器,兜底到 Maven 镜像(见常量注释)。
|
||||||
"{} 的 natives 构件 {classifier} 没有下载地址,已跳过",
|
let arm64_fallback = classifier == LINUX_ARM64_CLASSIFIER;
|
||||||
library.name
|
let (url, sha1, size) = match library.native_artifact(&classifier) {
|
||||||
));
|
Some(artifact) if artifact.has_url() => (
|
||||||
continue;
|
artifact.url.clone().unwrap_or_default(),
|
||||||
}
|
artifact.sha1.clone(),
|
||||||
|
artifact.size,
|
||||||
|
),
|
||||||
|
_ if arm64_fallback => (arm64_native_url(&native_relative), None, None),
|
||||||
|
Some(_) => {
|
||||||
|
warnings.push(format!(
|
||||||
|
"{} 的 natives 构件 {classifier} 没有下载地址,已跳过",
|
||||||
|
library.name
|
||||||
|
));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
warnings.push(format!(
|
||||||
|
"{} 的 natives 分类器 {classifier} 没有下载信息,已跳过",
|
||||||
|
library.name
|
||||||
|
));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
files.push(PlanFile {
|
files.push(PlanFile {
|
||||||
role: FileRole::Native,
|
role: FileRole::Native,
|
||||||
name: format!("{}:{classifier}", library.name),
|
name: format!("{}:{classifier}", library.name),
|
||||||
url: artifact.url.clone().unwrap_or_default(),
|
url,
|
||||||
sha1: artifact.sha1.clone(),
|
sha1,
|
||||||
size: artifact.size,
|
size,
|
||||||
dest: layout.libraries_dir().join(native_relative),
|
dest,
|
||||||
resource: ResourceKind::Library,
|
resource: ResourceKind::Library,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -141,6 +152,22 @@ fn maven_url(library: &Library, relative: &Path) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Linux arm64 的 natives 兜底仓库:Mojang 的 `libraries.minecraft.net` 对
|
||||||
|
/// `natives-linux-arm64` 一律 404(实测 3.3.2 的 lwjgl/glfw/opengl/stb/tinyfd/openal),
|
||||||
|
/// 同一坐标在 Maven Central / 阿里云镜像有。
|
||||||
|
pub const LINUX_ARM64_NATIVES_BASE: &str = "https://maven.aliyun.com/repository/public/";
|
||||||
|
|
||||||
|
/// Linux arm64 的 natives 分类器(见 `Library::native_classifier`)。
|
||||||
|
pub const LINUX_ARM64_CLASSIFIER: &str = "natives-linux-arm64";
|
||||||
|
|
||||||
|
/// arm64 natives 的兜底 URL(按相对路径拼镜像基址)。
|
||||||
|
fn arm64_native_url(relative: &Path) -> String {
|
||||||
|
format!(
|
||||||
|
"{LINUX_ARM64_NATIVES_BASE}{}",
|
||||||
|
relative.to_string_lossy().replace('\\', "/")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// 供上层判错用:库展开不产生硬失败,缺地址一律降级成告警。
|
/// 供上层判错用:库展开不产生硬失败,缺地址一律降级成告警。
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@@ -255,4 +282,52 @@ mod tests {
|
|||||||
.dest
|
.dest
|
||||||
.ends_with("versions/inst/libraries/net/minecraftforge/forge/1.20.1-47.2.0.jar"));
|
.ends_with("versions/inst/libraries/net/minecraftforge/forge/1.20.1-47.2.0.jar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn linux_arm64_lwjgl3_natives_fall_back_to_maven_mirror() {
|
||||||
|
// Mojang 的清单只有 `natives-linux`(x86_64)且没有 arm64 分类器下载信息;
|
||||||
|
// Linux arm64 要改写成 `natives-linux-arm64` 并兜底到 Maven 镜像。
|
||||||
|
let library = library_with_artifact(
|
||||||
|
r#"{"name":"org.lwjgl:lwjgl:3.3.2","natives":{"linux":"natives-linux"}}"#,
|
||||||
|
);
|
||||||
|
let env = Environment {
|
||||||
|
os: "linux".to_string(),
|
||||||
|
os_version: String::new(),
|
||||||
|
arch: "arm64".to_string(),
|
||||||
|
..environment()
|
||||||
|
};
|
||||||
|
let layout = GameLayout::new("/g/.minecraft");
|
||||||
|
let (files, warnings) = plan(&layout, "1.20.4", &[library], &env);
|
||||||
|
assert!(warnings.is_empty(), "不该有告警:{warnings:?}");
|
||||||
|
assert_eq!(files.len(), 1);
|
||||||
|
assert_eq!(files[0].name, "org.lwjgl:lwjgl:3.3.2:natives-linux-arm64");
|
||||||
|
assert_eq!(
|
||||||
|
files[0].url,
|
||||||
|
"https://maven.aliyun.com/repository/public/org/lwjgl/lwjgl/3.3.2/lwjgl-3.3.2-natives-linux-arm64.jar"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
files[0].sha1.is_none(),
|
||||||
|
"镜像兜底没有 sha1,只能靠大小/存在性"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn linux_x86_64_keeps_the_manifest_classifier() {
|
||||||
|
let library = library_with_artifact(
|
||||||
|
r#"{"name":"org.lwjgl:lwjgl:3.3.2","natives":{"linux":"natives-linux"}}"#,
|
||||||
|
);
|
||||||
|
let env = Environment {
|
||||||
|
os: "linux".to_string(),
|
||||||
|
os_version: String::new(),
|
||||||
|
arch: "x86_64".to_string(),
|
||||||
|
..environment()
|
||||||
|
};
|
||||||
|
let layout = GameLayout::new("/g/.minecraft");
|
||||||
|
let (files, warnings) = plan(&layout, "1.20.4", &[library], &env);
|
||||||
|
assert!(files.is_empty(), "x86_64 没有清单下载信息,不该凭空拼 URL");
|
||||||
|
assert!(
|
||||||
|
warnings.iter().any(|w| w.contains("natives-linux")),
|
||||||
|
"{warnings:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -172,6 +172,11 @@ impl Library {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// natives 分类器名:按 OS 查 `natives` 表并展开 `${arch}`(64 位为 `64`)。
|
/// natives 分类器名:按 OS 查 `natives` 表并展开 `${arch}`(64 位为 `64`)。
|
||||||
|
///
|
||||||
|
/// **Linux arm64 特例**:Mojang 的 LWJGL3 条目只写 `natives-linux`(x86_64),
|
||||||
|
/// 但同一个 Maven 坐标有 `natives-linux-arm64`。原样用 `natives-linux` 会下到 x86_64
|
||||||
|
/// 的 `.so`,在 aarch64 JVM 里加载失败;这里改成 arm64 分类器(下载地址的兜底见
|
||||||
|
/// `install::libraries`)。
|
||||||
pub fn native_classifier(&self, env: &Environment) -> Option<String> {
|
pub fn native_classifier(&self, env: &Environment) -> Option<String> {
|
||||||
let raw = self.natives.as_ref()?.get(&env.os)?;
|
let raw = self.natives.as_ref()?.get(&env.os)?;
|
||||||
let arch = if matches!(env.arch.as_str(), "x86_64" | "arm64") {
|
let arch = if matches!(env.arch.as_str(), "x86_64" | "arm64") {
|
||||||
@@ -179,7 +184,20 @@ impl Library {
|
|||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
Some(raw.replace("${arch}", arch))
|
let classifier = raw.replace("${arch}", arch);
|
||||||
|
if env.os == "linux"
|
||||||
|
&& env.arch == "arm64"
|
||||||
|
&& classifier == "natives-linux"
|
||||||
|
&& self.is_lwjgl3()
|
||||||
|
{
|
||||||
|
return Some("natives-linux-arm64".to_owned());
|
||||||
|
}
|
||||||
|
Some(classifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 是否是 LWJGL 3(`org.lwjgl:<module>`;LWJGL 2 的 group 是 `org.lwjgl.lwjgl`)。
|
||||||
|
fn is_lwjgl3(&self) -> bool {
|
||||||
|
self.name.starts_with("org.lwjgl:")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// natives 构件下载信息(按分类器查 `downloads.classifiers`)。
|
/// natives 构件下载信息(按分类器查 `downloads.classifiers`)。
|
||||||
|
|||||||
@@ -174,6 +174,13 @@ pub fn environment(req: &LaunchRequest, os: OsFamily) -> Vec<(String, String)> {
|
|||||||
set_env(&mut env, "MESA_LOADER_DRIVER_OVERRIDE", "llvmpipe");
|
set_env(&mut env, "MESA_LOADER_DRIVER_OVERRIDE", "llvmpipe");
|
||||||
set_env(&mut env, "GALLIUM_DRIVER", "llvmpipe");
|
set_env(&mut env, "GALLIUM_DRIVER", "llvmpipe");
|
||||||
}
|
}
|
||||||
|
// Termux + glibc 构建:termux-x11 下 glibc Mesa 的 zink 卡 DRI3,softpipe 是当前
|
||||||
|
// 可用路径。放在软件渲染块之后、用户自定义之前(用户显式设置仍可覆盖)。
|
||||||
|
if cfg!(target_os = "linux")
|
||||||
|
&& crate::platform::termux::is_termux(&crate::platform::paths::ProcessEnv)
|
||||||
|
{
|
||||||
|
apply_termux_gl_defaults(&mut env);
|
||||||
|
}
|
||||||
if os == OsFamily::MacOs {
|
if os == OsFamily::MacOs {
|
||||||
if let Some(icd) = &req.renderer.vulkan_icd {
|
if let Some(icd) = &req.renderer.vulkan_icd {
|
||||||
set_env(&mut env, "VK_ICD_FILENAMES", &icd.display().to_string());
|
set_env(&mut env, "VK_ICD_FILENAMES", &icd.display().to_string());
|
||||||
@@ -201,3 +208,43 @@ fn set_env(env: &mut Vec<(String, String)>, key: &str, value: &str) {
|
|||||||
env.push((key.to_string(), value.to_string()));
|
env.push((key.to_string(), value.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Termux(glibc 构建)上的默认 GL 环境:softpipe。
|
||||||
|
///
|
||||||
|
/// glibc Mesa 26 的 zink 在 termux-x11 下报 `DRI3 not available`,softpipe 是当前
|
||||||
|
/// 可用的软件路径;硬件路径等 DRI3 问题解决后再切。
|
||||||
|
fn apply_termux_gl_defaults(env: &mut Vec<(String, String)>) {
|
||||||
|
set_env(env, "MESA_LOADER_DRIVER_OVERRIDE", "swrast");
|
||||||
|
set_env(env, "LIBGL_ALWAYS_SOFTWARE", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::apply_termux_gl_defaults;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn termux_gl_defaults_force_softpipe() {
|
||||||
|
let mut env = Vec::new();
|
||||||
|
apply_termux_gl_defaults(&mut env);
|
||||||
|
assert!(env.contains(&(
|
||||||
|
"MESA_LOADER_DRIVER_OVERRIDE".to_string(),
|
||||||
|
"swrast".to_string()
|
||||||
|
)));
|
||||||
|
assert!(env.contains(&("LIBGL_ALWAYS_SOFTWARE".to_string(), "1".to_string())));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn termux_gl_defaults_overwrite_earlier_llvmpipe_choice() {
|
||||||
|
let mut env = vec![(
|
||||||
|
"MESA_LOADER_DRIVER_OVERRIDE".to_string(),
|
||||||
|
"llvmpipe".to_string(),
|
||||||
|
)];
|
||||||
|
apply_termux_gl_defaults(&mut env);
|
||||||
|
assert_eq!(
|
||||||
|
env.iter()
|
||||||
|
.find(|(key, _)| key == "MESA_LOADER_DRIVER_OVERRIDE")
|
||||||
|
.map(|(_, value)| value.as_str()),
|
||||||
|
Some("swrast")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -190,9 +190,22 @@ $PREFIX/glibc/lib/ld-linux-aarch64.so.1 \
|
|||||||
- 端到端探针:LWJGL 3.3.3 arm64 natives + GLFW + GLX → `GL_VERSION=3.3 (Compatibility
|
- 端到端探针:LWJGL 3.3.3 arm64 natives + GLFW + GLX → `GL_VERSION=3.3 (Compatibility
|
||||||
Profile) Mesa 26.2.6`、`GL_RENDERER=softpipe`、`gl-ok`。
|
Profile) Mesa 26.2.6`、`GL_RENDERER=softpipe`、`gl-ok`。
|
||||||
|
|
||||||
> 启动器侧还没接线:MC 的 library 清单在 Linux/aarch64 上要选
|
> **启动器已接线**(本分支):
|
||||||
> `natives-linux-arm64` 而不是 `natives-linux`(x64),并在启动 MC 的子进程里带上
|
> - Linux/aarch64 上 LWJGL3 的 natives 自动改写成 `natives-linux-arm64`
|
||||||
> `MESA_LOADER_DRIVER_OVERRIDE=swrast` 与 glibc JVM 的 `JAVA_HOME`。这两步是后续工作。
|
> (Mojang 的 `libraries.minecraft.net` 对 arm64 一律 404,下载兜底到阿里云 Maven 镜像);
|
||||||
|
> - Termux glibc 构建启动 MC 时,子进程默认带 `MESA_LOADER_DRIVER_OVERRIDE=swrast`
|
||||||
|
> 与 `LIBGL_ALWAYS_SOFTWARE=1`(用户自定义环境变量仍可覆盖)。
|
||||||
|
>
|
||||||
|
> **还差一步**:把实例的 Java 指到 glibc JRE。Temurin 解包后 `bin/java` 默认的
|
||||||
|
> interpreter 在 Android 上不存在,先 patchelf 一次即可直接执行:
|
||||||
|
>
|
||||||
|
> ```sh
|
||||||
|
> patchelf --set-interpreter "$PREFIX/glibc/lib/ld-linux-aarch64.so.1" \
|
||||||
|
> --set-rpath "$PREFIX/glibc/lib:$JRE/lib:$JRE/lib/server" "$JRE/bin/java"
|
||||||
|
> "$JRE/bin/java" -version
|
||||||
|
> ```
|
||||||
|
>
|
||||||
|
> 之后在实例设置里把 Java 指向 `$JRE/bin/java`(或 `--java`)即可。
|
||||||
|
|
||||||
## 5. GPU(已打通:自编 Mesa 26.2.6 + KGSL)
|
## 5. GPU(已打通:自编 Mesa 26.2.6 + KGSL)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user