Files
Unlucky-Launcher/crates/core/src/launch/defaults.rs
T
AskaEth 5046f96f7e
CI / runtime smoke (macos arm64, probe) (push) Skipped
Guards / core 零 UI 守门 (push) Waiting to run
Guards / presenter 零 UI 守门 (push) Waiting to run
Guards / design 零第三方依赖守门 (push) Waiting to run
Guards / unsafe 落点守门(集合必须恰好一个文件) (push) Waiting to run
Guards / 文件行数 ≤400 守门 (push) Waiting to run
Guards / 依赖许可与来源(cargo-deny) (push) Waiting to run
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
Unlucky Launcher v0.2.1
2026-11-06 20:47:19 +08:00

204 lines
7.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 默认参数表: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()));
}
}