docs(core): inherit::merge 里写清 minecraftArguments(子覆盖父)与 arguments.game(追加)的区别

M222 的准入条件就是这条;不给结论留档,下一个人还会以为「叠一层」随便往哪个字段写都行。
This commit is contained in:
AskaEth committed 2026-11-10 19:34:12 +08:00
1 parent c311032080
commit 51ea32284d
2 files changed
+59

No files matched your search

+10
View File
@@ -132,6 +132,16 @@ pub fn apply_patches(base: VersionJson, patches: &[VersionJson]) -> VersionJson
/// 这两点**与 analyse/03 §8.3 第 15 条建议相左**(该条主张逐字段/逐 key 合并)。
/// 保留现状的理由是生态兼容:现实 modpack/loader 的版本 JSON 按 HMCL/原版语义编写,
/// 逐 key 合并会让"子层想整份换掉 downloads"的场景语义漂移。经 tower 裁决 2026-10-29 维持。
///
/// # 两种 args 表达的语义**不一样**(M222 核实,别混)
///
/// - `arguments.game` / `arguments.jvm`:父在前、子在后**追加**([`Arguments::merge`]);
/// - `minecraftArguments`(pre-1.13 的单串):**子覆盖父**,子不写才继承。
///
/// 所以「叠一层加载器」只能往 `arguments.game` 里加参数;往 `minecraftArguments` 里写会把父链
/// (例如 Forge 1.12.2 的 `--tweakClass FMLTweaker`)整串顶掉。回归:
/// `crates/core/tests/liteloader/install.rs` 的 `merge_laws_liteloader_depends_on` 与
/// `stacks_on_forge_and_keeps_the_forge_tweak_class`。
pub fn merge(child: &VersionJson, parent: &VersionJson) -> VersionJson {
let mut extra = parent.extra.clone();
extra.extend(child.extra.iter().map(|(k, v)| (k.clone(), v.clone())));
+49
View File
@@ -238,3 +238,52 @@ fn new_style_parent_args_are_appended_not_replaced() {
"父层参数必须在前:{game:?}"
);
}
/// LiteLoader 生效所依赖的**两条继承合并律**(M222 第一步核实,直接钉在合并函数上)。
///
/// 1. `arguments.game`:父在前、子在后**追加**(所以 tweakClass 能叠在父链参数之后);
/// 2. `minecraftArguments`:**子覆盖父**(所以 loader 层绝不能写它,否则父链的 tweakClass
/// 整串消失)。
///
/// 抓得住:有人把 `minecraftArguments` 改成追加(HMCL/原版语义不是这样),或把
/// `arguments.game` 改成整体替换(LiteLoader/其它叠层加载器当场失效)。
/// 抓不住:真实启动 argv 的顺序(那是 `launch::game_args` 的第 24/25 项,
/// 由 `tests/launch/cmdline_snapshot.rs` 钉住;本 mission 没有改那一层)。
#[test]
fn merge_laws_liteloader_depends_on() {
use unlucky_core::game::inherit::merge;
use unlucky_core::game::manifest::VersionJson;
let parse = |json: &str| VersionJson::from_json_str(json).expect("夹具应可解析");
let parent = parse(
r#"{"id":"p","minecraftArguments":"--tweakClass FMLTweaker","arguments":{"game":["--parent"]}}"#,
);
let child = parse(r#"{"id":"c","arguments":{"game":["--child"]}}"#);
let merged = merge(&child, &parent);
assert_eq!(
merged.minecraft_arguments.as_deref(),
Some("--tweakClass FMLTweaker"),
"子没写就继承父(这条是「叠在 Forge 上」的前提)"
);
let game: Vec<String> = merged
.arguments
.as_ref()
.expect("arguments")
.expand(&Environment::host())
.game;
assert_eq!(
game,
["--parent", "--child"],
"arguments.game 是父在前、子在后追加"
);
// 子写了 minecraftArguments:整串替换(不是追加)。
let loud_child = parse(r#"{"id":"c2","minecraftArguments":"--tweakClass LiteLoaderTweaker"}"#);
let merged = merge(&loud_child, &parent);
assert_eq!(
merged.minecraft_arguments.as_deref(),
Some("--tweakClass LiteLoaderTweaker"),
"子覆盖父:这正是 loader 层不许写 minecraftArguments 的原因"
);
}