Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

add-recipe single inserts a duplicate when the civ already knows the recipe

未关闭 适合新手
#6,022 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 3 天内回复

还没有人认领这个 Issue。

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
78/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
lua
领域
tooling

调研方向

从 add-recipe.lua 开始,并比较 single 插入循环与用于保护批量插入的 addItems 检查。报告将 historical_entity.resources 中的重复条目认定为已确认的错误;请验证其行为,并确保完成意味着重复执行 single 命令不会再添加一份副本。digger 路由和 nil 检查是同一文件中的其他发现,可能需要单独确定范围。

由索引模型根据 Issue 内容生成。

描述

add-recipe single <TYPE>:<ID> appends to the civ's resource vector without checking whether the subtype is already in there. Run it twice for the same item and you get two copies in historical_entity.resources.<x>_type.

Confirmed in game (DF + DFHack, fortress mode). Running

add-recipe single SHOES:ITEM_SHOES_BOOTS

twice on an item the civ already knew gave me a visible duplicate entry.

The single path in add-recipe.lua is upfront about skipping the check:

local addedItem = nil
--assume the user knows what they're doing, so no need for sanity checks
for _, item in ipairs(all) do
  if (item.id == itemId) then
    known:insert('#', item.subtype)
    addedItem = item
    break
  end
end

The bulk paths are fine. addItems guards with not checkKnown(known, subtype) before inserting, so this is specific to single.

The duplicates are mostly cosmetic, but they stick around in the save and there's no in-game way to see them or get rid of them.

Adding a checkKnown(known, item.subtype) call in that loop would make it behave like all/native.


Two smaller things in the same file, from reading it rather than playing. Happy to split either out into its own issue.

1. single doesn't route diggers. It looks the category up in the categories table, and there's no digger entry there, so WEAPON:ITEM_WEAPON_PICK ends up in resources.weapon_type. addItems deliberately sends mining weapons to resources.digger_type instead, with the comment "prevents picks from being duplicated, and puts great picks in correct category". Something does read that field: internal/quickfort/stockflow.lua:306 uses resources.digger_type to build MakeWeapon orders for diggers. So the two vectors aren't interchangeable.

Partial in-game note: add-recipe single WEAPON:ITEM_WEAPON_PICK on a civ that already knew picks did not produce a duplicate among the known diggers, which is what you'd expect if the subtype went into weapon_type instead. I did not separately confirm that it appeared under weapons, so treat that half as unverified.

2. Two nil checks that can't fire. Lines 4-12:

local resources = df.historical_entity.find(df.global.plotinfo.civ_id).resources
local civ = df.historical_entity.find(df.global.plotinfo.civ_id).entity_raw
...
if (resources == nil) then
  qerror("Could not find entity resources")
elseif (civ == nil) then

If find returns nil, line 4 has already thrown attempt to index a nil value. So running add-recipe with no fortress loaded gets you a Lua traceback instead of either of the qerror messages that were meant to come out.

I checked whether this is a general pattern before bringing it up: of the 439 scripts in DFHack/scripts, 70 sites dereference a find() result in the same expression, and add-recipe.lua is the only one that then nil-tests the result. The rest either guard first or have an argument that can't be nil. So it's one file, not a sweep.


Context: this came out of review on DFHack/scripts#1603, where @chdoc asked for it to be filed rather than worked around in the GUI.

Disclosure: I used AI assistance (Claude) for the code reading and for drafting this write-up. I verified the main bug in game myself, and I'm the one filing it.

主要语言
C++
星标
2.1k
派生
509
平均合并
2 天 19 小时
30 天内合并 PR
25

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

DFHack/dfhack 的其他 Issue

查看 DFHack/dfhack 的全部 Issue

相似的 Issue

更多 C++ Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。