Skip to content

fix(download): 预设状态还没加载好时,下载完的 Mod 不再被误拉黑 - #91

Open
std-external wants to merge 1 commit into
std-microblock:masterfrom
std-external:fix/new-mod-activation
Open

std-external wants to merge 1 commit into
std-microblock:masterfrom
std-external:fix/new-mod-activation

Conversation

@std-external

Copy link
Copy Markdown
Contributor

问题

1.2.0 起「预设(Profile)」模式默认开启(src/celemod-ui/src/states.ts:395-403),而预设模式下 Mods/blacklist.txt 是一份由预设重建出来的白名单(src-tauri/src/blacklist.rs:438-453)。于是「下载完的 Mod 会不会生效」完全取决于下载完成时有没有把它写进某个预设;没写进去的话,紧接着的那次 apply 就会把它拉黑。

群里的报告(网吧环境,每台机器都是全新的):下载完 Mod 不生效,必须去管理页手动启用一次。1.1.x 不会这样——那时没有预设接管 blacklist.txt,下载后的 Mod 靠「不在黑名单里 = 启用」自然生效,所以这是 1.2.0 默认开预设后暴露出来的问题。

根因

下载完成后由 enable_installed_local_mods()(src-tauri/src/backend.rs:3115)把新 Mod 写进预设。它拿到的预设名来自前端 state 里的 currentProfileName(src/celemod-ui/src/stores/download.ts:275),而这个名字:

  • 全新安装时是空字符串:currentProfileName 不参与持久化,要靠 reloadBlacklistState() 加载完才赋值;在 Mods/ 目录还不存在时(刚选完游戏目录、Everest 还没装)这次加载会在最后的 apply_mod_profiles 写 blacklist.txt 时失败,前端只 console.error,名字就一直是空的。
  • 也可能指向一个已经不存在的预设(重命名/删除后)。

原来的写法只在「名字为空」时回退到 get_current_profiles(),名字不为空就直接当成预设名用:

let profiles = if current_profile_name.is_empty() {
    blacklist::get_current_profiles(game_path)
} else {
    vec![current_profile_name.to_string()]
};

两种情况下都会出问题:

  1. 名字非空但预设不存在 → switch_mod_profile_mods 直接 Err("Profile not found")(src-tauri/src/blacklist.rs:576),enable_installed_local_mods 提前返回,错误只写进日志(src-tauri/src/backend.rs:5976-5978)。新 Mod 没进预设,前端在下载结束时又会 reloadBlacklistState() → apply_mod_profiles → 这个 Mod 不在白名单里 → 被拉黑。
  2. 名字为空且磁盘上还没有任何预设 → 预设列表为空,但 apply_mod_blacklist_profiles 对空列表不报错(blacklist.rs:401-409 的校验对空集合恒真),于是它照常重写 blacklist.txt:最新鲜的 Mod 和已有全部 Mod 一起被拉黑,函数还返回成功。

本地跑出来的原始现象(新增测试里就能复现,见下):

enable_installed_local_mods(.., profile_enabled = true, current_profile_name = "", ..)
  → Ok(())
  → blacklist.txt = "# Profiles: []\n# This file is generated by CeleMod\n\nExisting.zip\nNew Mod.zip\n"

之后前端 ensureDefaultProfile 会用这份「全部禁用」的 blacklist.txt 造出一个空预设,adoptUntrackedMods 又只认领「当前还在启用状态」的 Mod(src/celemod-ui/src/modAdoption.ts:65),所以再也回不来了——用户只能去管理页点开关。

顺带一个同源的小问题(本 PR 不动,留作说明):拖拽 / 深度链接安装走的 install_local_packages() 只在「安装后禁用」时调用 disable_installed_local_mods(backend.rs:5732-5744),默认「启用」时后端不做任何事,全靠前端 adoptUntrackedMods 事后补救,同样会被上面那份空预设卡住。

修复

  • 新增 resolve_switch_profile_names():只有传来的名字确实存在于磁盘上的预设列表时才用它,否则回退到 get_current_profiles()(当前生效的预设),再不行回退到第一个预设。
  • 预设列表为空时(磁盘上还没有任何预设)不碰 blacklist.txt,直接返回:前端下一次 reload 会建默认预设并把新 Mod 认领进去,Mod 保持启用状态,不会被误拉黑。
  • 逻辑对 enable_installed_local_mods / disable_installed_local_mods 都生效(禁用路径同样不该在空预设列表下重写白名单)。

改动只在 src-tauri/src/backend.rs,不涉及 #90(下载卡住)改的 ureq.rs / stores/download.ts / DownloadList.tsx。

验证

cargo test --lib:116 passed; 0 failed(新增 3 个回归测试,原来 113 个全过):

  • downloaded_mod_stays_enabled_without_any_profile:全新目录、无预设、预设名为空 → 下载后的 Mod 和已有 Mod 都不会被写进 blacklist.txt(修复前:两个都被拉黑)。
  • downloaded_mod_is_enabled_when_profile_state_is_not_loaded:磁盘上有 Default 但前端还没加载到(名字为空)→ 新 Mod 进入 Default.enabled_mods。
  • downloaded_mod_is_enabled_when_profile_name_is_stale:传来的预设名已不存在 → 回退到真实存在的预设并把新 Mod 启用。

已有的 4 个下载默认行为测试(启用/禁用/依赖/直连模式)保持通过。

来源:QQ 群「Celeste Coders」群友报告(网吧环境每次全新安装,下载后必须去管理页手动启用一次)。诊断报告:reports/celemod-new-mod-activation.md

… missing

The UI passes its currently selected profile name to the download backend
together with profile mode.  On a fresh install that name is empty until the
first profile reload finishes (and the reload itself can fail while Mods/ does
not exist yet), and a name the profile folder no longer owns (renamed or
deleted profile) failed the switch with "Profile not found".

Either way the freshly downloaded Mod was never added to a profile and the
follow-up apply_mod_blacklist_profiles - the UI re-applies profiles when the
download finishes - rewrote blacklist.txt as a whitelist without it, so the Mod
was disabled until the user enabled it by hand in the Mod manager.  With an
empty name and no profile on disk the apply even happened with an empty profile
list, which blacklisted every installed Mod at once.

Resolve the profile names defensively (keep the given name only while it still
exists, otherwise use the active profiles or the first one) and leave
blacklist.txt untouched when no profile exists yet - the UI seeds its default
profile on the next reload and adopts the new Mod.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant