Unlucky Launcher v0.2.1
CI / runtime smoke (macos arm64, probe) (push) Skipped
CI / fmt (rustfmt --check) (push) Canceled after 0s
CI / clippy (ubuntu-latest) (push) Canceled after 0s
CI / clippy (windows-latest) (push) Canceled after 0s
CI / test (ubuntu-latest) (push) Canceled after 0s
CI / test (windows-latest) (push) Canceled after 0s
CI / build release (ubuntu-latest) (push) Canceled after 0s
CI / build release (windows-latest) (push) Canceled after 0s
CI / runtime smoke (ubuntu x64, xvfb) (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好一个文件) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s

This commit is contained in:
AskaEth committed 2026-11-06 20:47:19 +08:00
commit 5046f96f7e
1092 files changed
+211068

No files matched your search

+203
View File
@@ -0,0 +1,203 @@
//! 默认参数表:JVM 优化、编码、log4j、禁用参数、环境变量。
//!
//! 平移自 `analyse/02` §4.3(自动优化)、§4.4(编码/Log4j)、§4.1 #33(禁用参数)、
//! §4.10(环境变量)。所有函数都是纯函数——同样的输入永远给同样的参数,便于快照测试。
use crate::launch::platform::OsFamily;
use crate::launch::request::{LaunchRequest, ModLoaderKind, RendererOptions, Resolution};
/// 旧版清单的默认 JVM 参数(`analyse/02` §4.7 的 `DEFAULT_JVM_ARGUMENTS`)。
///
/// 现代清单把这两项写在自己的 `arguments.jvm` 里,只有 legacy 需要补。
pub fn legacy_jvm_arguments() -> Vec<String> {
vec![
"-Djava.library.path=${natives_directory}".to_string(),
"-cp".to_string(),
"${classpath}".to_string(),
]
}
/// stdout/stderr 编码参数。
///
/// Java ≥19 用 `-Dstdout.encoding`,更早用 `-Dsun.stdout.encoding`(`analyse/02` §4.4:
/// 用户指定的 `-Dfile.encoding` 会决定日志泵的解码方式,这里必须跟随)。
pub fn stream_encoding_args(java_major: u32, encoding: &str) -> Vec<String> {
if java_major >= 19 {
vec![
format!("-Dstdout.encoding={encoding}"),
format!("-Dstderr.encoding={encoding}"),
]
} else {
vec![
format!("-Dsun.stdout.encoding={encoding}"),
format!("-Dsun.stderr.encoding={encoding}"),
]
}
}
/// Log4j2 RCE 缓解三件套(`analyse/02` §4.1 #9)。
pub fn log4j_mitigation_args() -> Vec<String> {
vec![
"-Djava.rmi.server.useCodebaseOnly=true".to_string(),
"-Dcom.sun.jndi.rmi.object.trustURLCodebase=false".to_string(),
"-Dcom.sun.jndi.cosnaming.object.trustURLCodebase=false".to_string(),
]
}
/// 自动优化参数(`analyse/02` §4.3)。
///
/// `user_specified_gc` 为真时不注入 G1 整组(用户显式挑了 GC 就别打架);
/// 其它每一条都还会被 `CommandBuilder` 的默认值抑制再筛一遍。
pub fn optimization_args(
java_major: u32,
bits: u32,
physical_memory_mb: Option<u64>,
user_specified_gc: bool,
) -> Vec<String> {
let mut args = vec![
"-XX:+UnlockExperimentalVMOptions".to_string(),
"-XX:+UnlockDiagnosticVMOptions".to_string(),
];
if java_major >= 8 && !user_specified_gc {
for arg in [
"-XX:+UseG1GC",
"-XX:G1MixedGCCountTarget=5",
"-XX:G1NewSizePercent=20",
"-XX:G1ReservePercent=20",
"-XX:MaxGCPauseMillis=50",
"-XX:G1HeapRegionSize=32m",
] {
args.push(arg.to_string());
}
}
// 崩溃诊断关键项:关掉“快速抛出时省略栈”,保证日志里有完整堆栈。
args.push("-XX:-OmitStackTraceInFastThrow".to_string());
if java_major <= 8 {
args.push("-XX:MaxInlineLevel=15".to_string());
}
if bits >= 64 && physical_memory_mb.is_some_and(|mb| mb > 4096) {
for arg in [
"-XX:-DontCompileHugeMethods",
"-XX:MaxNodeLimit=240000",
"-XX:NodeLimitFudgeFactor=8000",
"-XX:TieredCompileTaskTimeout=10000",
"-XX:ReservedCodeCacheSize=400M",
"-XX:NonNMethodCodeHeapSize=12M",
"-XX:ProfiledCodeHeapSize=194M",
"-XX:NmethodSweepActivity=1",
] {
args.push(arg.to_string());
}
}
if bits >= 64 && (25..=26).contains(&java_major) {
args.push("-XX:+UseCompactObjectHeaders".to_string());
}
args
}
/// 版本特定的 JVM 开关(`analyse/02` §4.1 #15)。
pub fn version_specific_args(java_major: u32) -> Vec<String> {
match java_major {
16 => vec!["--illegal-access=permit".to_string()],
24 | 25 => vec!["--sun-misc-unsafe-memory-access=allow".to_string()],
_ => Vec::new(),
}
}
/// Forge/NeoForge 的证书校验豁免(`analyse/02` §4.1 #16)。
pub fn fml_args() -> Vec<String> {
vec![
"-Dfml.ignoreInvalidMinecraftCertificates=true".to_string(),
"-Dfml.ignorePatchDiscrepancies=true".to_string(),
]
}
/// 渲染器相关 `-D` 参数(`analyse/02` §4.1 #17,v1 只做 native 路径覆盖)。
///
/// 软件渲染的细节开关走环境变量([`environment`]),因为 Mesa/llvmpipe 读的是 env。
pub fn renderer_args(renderer: &RendererOptions) -> Vec<String> {
let mut args = Vec::new();
if let Some(dir) = &renderer.mesa_native_dir {
args.push(format!("-Dorg.lwjgl.librarypath={}", dir.display()));
}
args
}
/// 被禁止的参数(最后统一清理,`analyse/02` §4.1 #33)。
pub fn forbidden_args(java_major: u32) -> Vec<&'static str> {
if java_major >= 9 {
vec!["-Xincgc"]
} else {
Vec::new()
}
}
/// 旧版清单的默认游戏参数(分辨率,`analyse/02` §4.1 #26)。
pub fn legacy_game_args(resolution: Option<&Resolution>) -> Vec<String> {
let mut args = Vec::new();
if let Some(res) = resolution {
args.push("--width".to_string());
args.push(res.width.to_string());
args.push("--height".to_string());
args.push(res.height.to_string());
}
args
}
/// 进程环境变量(`analyse/02` §4.10)。
///
/// 顺序:实例标识 → 渲染器开关 → 加载器标记 → 用户自定义(最后覆盖)。
pub fn environment(req: &LaunchRequest, os: OsFamily) -> Vec<(String, String)> {
let mut env: Vec<(String, String)> = Vec::new();
set_env(&mut env, "INST_NAME", &req.instance.name);
set_env(&mut env, "INST_ID", &req.instance.id);
set_env(
&mut env,
"INST_DIR",
&req.dirs.game_dir.display().to_string(),
);
set_env(
&mut env,
"INST_MC_DIR",
&req.dirs.game_dir.display().to_string(),
);
set_env(
&mut env,
"INST_JAVA",
&req.java.binary.display().to_string(),
);
if req.mod_loader == ModLoaderKind::OptiFine {
set_env(&mut env, "INST_OPTIFINE", "1");
}
if req.renderer.software {
set_env(&mut env, "LIBGL_ALWAYS_SOFTWARE", "1");
set_env(&mut env, "MESA_LOADER_DRIVER_OVERRIDE", "llvmpipe");
set_env(&mut env, "GALLIUM_DRIVER", "llvmpipe");
}
if os == OsFamily::MacOs {
if let Some(icd) = &req.renderer.vulkan_icd {
set_env(&mut env, "VK_ICD_FILENAMES", &icd.display().to_string());
set_env(&mut env, "VK_DRIVER_FILES", &icd.display().to_string());
}
}
match req.mod_loader {
ModLoaderKind::Forge => set_env(&mut env, "FORGE_INSTALL", "1"),
ModLoaderKind::NeoForge => set_env(&mut env, "NEOFORGE_INSTALL", "1"),
ModLoaderKind::Fabric => set_env(&mut env, "FABRIC_INSTALL", "1"),
ModLoaderKind::Quilt => set_env(&mut env, "QUILT_INSTALL", "1"),
ModLoaderKind::LiteLoader => set_env(&mut env, "LITELOADER_INSTALL", "1"),
ModLoaderKind::OptiFine | ModLoaderKind::Vanilla => {}
}
for (key, value) in &req.user.environment {
set_env(&mut env, key, value);
}
env
}
fn set_env(env: &mut Vec<(String, String)>, key: &str, value: &str) {
if let Some(slot) = env.iter_mut().find(|(existing, _)| existing == key) {
slot.1 = value.to_string();
} else {
env.push((key.to_string(), value.to_string()));
}
}