fix(core): 同一 loader 版本 id 撞两条父链时具名报错,不覆盖盘上清单(旧实例会静默换父链)
This commit is contained in:
1 parent
9c6b5eaa9b
commit
0eaefebf3b
2 files changed
+84
No files matched your search
@@ -182,6 +182,21 @@ impl LoaderInstallPlan {
|
||||
/// 父版本缺了)。**不能默默回落成叠原版**:那会装出一份和用户意图不同的实例。
|
||||
pub fn build(input: LoaderPlanInput<'_>) -> Result<Self, LoaderError> {
|
||||
let release = input.release;
|
||||
// 同一个 loader 版本 id 不能同时表示两条父链(`<原版>-liteloader-<ver>` 与
|
||||
// 「叠在 Forge 上」撞同一个目录):盘上那份的 `inheritsFrom` 与本次请求不符就具名报错,
|
||||
// 而不是把旧清单覆盖掉——旧实例会静默换父链,启动行为跟着变。
|
||||
if let Some(existing) = existing_parent(input.layout, &release.version_id) {
|
||||
if existing != release.parent_version_id {
|
||||
return Err(LoaderError::unsupported(format!(
|
||||
"{} 已装在盘上,父版本是 {existing},与本次请求的 {} 不同",
|
||||
release.version_id, release.parent_version_id
|
||||
))
|
||||
.with_hint(format!(
|
||||
"loader 版本 id 由「原版版本 + 加载器 + 加载器版本」决定,叠在别的层上会撞同一个目录:先删掉 versions/{}(旧实例也会失去这一层),或换一个加载器版本",
|
||||
release.version_id
|
||||
)));
|
||||
}
|
||||
}
|
||||
if !input.parent_installed && !release.parent_is_vanilla() {
|
||||
return Err(LoaderError::not_found(format!(
|
||||
"父版本 {} 还没装好:{} {} 要叠在它上面",
|
||||
@@ -262,3 +277,17 @@ impl LoaderInstallPlan {
|
||||
summary
|
||||
}
|
||||
}
|
||||
|
||||
/// 盘上那份 `versions/<id>/<id>.json` 的 `inheritsFrom`(不存在 / 读不出 / 没写 → `None`)。
|
||||
///
|
||||
/// 只用来挡「同一版本 id 撞两条父链」,不参与其它判定:读失败不该让安装失败,
|
||||
/// 所以错误一律吞成 `None`(真正的父子链校验由 [`crate::game::inherit::resolve`] 与
|
||||
/// [`LoaderPlanInput::parent_installed`] 负责)。
|
||||
fn existing_parent(layout: &GameLayout, version_id: &str) -> Option<String> {
|
||||
let text = std::fs::read_to_string(layout.version_json(version_id)).ok()?;
|
||||
let manifest = VersionJson::from_json_str(&text).ok()?;
|
||||
manifest
|
||||
.inherits_from
|
||||
.map(|parent| parent.trim().to_string())
|
||||
.filter(|parent| !parent.is_empty())
|
||||
}
|
||||
@@ -155,3 +155,58 @@ fn cancel_leaves_no_manifest() {
|
||||
unlucky_core::game::GameError::MissingParent { .. }
|
||||
));
|
||||
}
|
||||
|
||||
/// 同一个 loader 版本 id 被两条父链请求:**具名报错**,且盘上那份清单一个字节都不动。
|
||||
///
|
||||
/// 为什么必须有这道门:`<原版>-liteloader-<ver>` 对「叠原版」与「叠 Forge」是同一个目录名,
|
||||
/// 直接覆盖会让已经登记、指向这个版本 id 的旧实例**静默换父链**(启动行为跟着变)。
|
||||
#[test]
|
||||
fn parent_collision_is_refused_instead_of_overwriting() {
|
||||
let temp = TempDir::new("parent-collision");
|
||||
let layout = GameLayout::new(temp.path());
|
||||
put_version(&layout, "1.12.2", VANILLA_1122);
|
||||
put_version(
|
||||
&layout,
|
||||
"1.12.2-forge-14.23.5.2860",
|
||||
crate::support::FORGE_1122,
|
||||
);
|
||||
|
||||
let vanilla_release = liteloader_release("1.12.2");
|
||||
let existing = serde_json::json!({
|
||||
"id": vanilla_release.version_id,
|
||||
"inheritsFrom": "1.12.2",
|
||||
"mainClass": "net.minecraft.launchwrapper.Launch",
|
||||
"arguments": {"game": ["--tweakClass", "com.mumfrey.liteloader.launch.LiteLoaderTweaker"]},
|
||||
"libraries": []
|
||||
});
|
||||
put_version(
|
||||
&layout,
|
||||
&vanilla_release.version_id,
|
||||
&serde_json::to_string_pretty(&existing).expect("序列化"),
|
||||
);
|
||||
let before = std::fs::read_to_string(layout.version_json(&vanilla_release.version_id))
|
||||
.expect("读旧清单");
|
||||
|
||||
let forge_release = liteloader_release("1.12.2-forge-14.23.5.2860");
|
||||
assert_eq!(
|
||||
forge_release.version_id, vanilla_release.version_id,
|
||||
"两条父链撞同一个 id"
|
||||
);
|
||||
let error = LoaderInstallPlan::build(LoaderPlanInput {
|
||||
layout: &layout,
|
||||
release: &forge_release,
|
||||
environment: &Environment::host(),
|
||||
parent_installed: true,
|
||||
})
|
||||
.expect_err("撞父链必须报错");
|
||||
assert_eq!(error.kind(), LoaderErrorKind::Unsupported);
|
||||
assert!(
|
||||
error.message().contains("1.12.2-forge-14.23.5.2860") && error.message().contains("1.12.2"),
|
||||
"要说清两条父链各是什么:{}",
|
||||
error.message()
|
||||
);
|
||||
|
||||
let after = std::fs::read_to_string(layout.version_json(&vanilla_release.version_id))
|
||||
.expect("读旧清单");
|
||||
assert_eq!(before, after, "报错之后盘上那份清单不许被改动");
|
||||
}
|
||||
Reference in new issue
Block a user