Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
EasyCraft
[AI-generated] Load 5 items per interaction into smelters/refineries, with per-station material limits (e.g. charcoal kiln = wood only). Refuel torches/hearths, and auto-pull from nearby chests and carts. Client-only.
By s6652289
| Last updated | 3 days ago |
| Total downloads | 345 |
| Total rating | 0 |
| Categories | Mods AI Generated |
| Dependency string | s6652289-EasyCraft-1.0.6 |
| Dependants | 0 other packages depend on this package |
This mod requires the following mods to function
denikson-BepInExPack_Valheim
BepInEx pack for Valheim. Preconfigured with the correct entry point for mods and preferred defaults for the community.
Preferred version: 5.4.2350README
EasyCraft
AI 生成声明:本模组由 AI 智能体(WorkBuddy)在用户的明确需求与逐项验收下编写; 功能设计、真机测试与发布决定均由用户主导。
炼制站点 / 火源的装填增强。 由 EasyBatchFill 与 EasyNearbyFill 合并而成 —— 合并同时消掉了原先两者之间那条「必须成对升级、单升一边 BepInEx 直接加载失败」的跨程序集硬依赖。
功能
- 批量装填:一次交互装入
BatchSize个(默认 5),取代原版的一次 1 个。 站点:熔炉 / 高炉 / 炭窑 / 风车 / 纺车 / Eitr 精炼厂。 - 站点进料限制(1.0.2 新增):限定某个站点只允许自动装填指定材料。 默认 = 炭窑只烧木材(不烧细木 / 圆木)。
- 火源加燃料:火炬 / 壁灯 / 篝火 / 火盆 / 灶 一次加
BatchSize个; 营火(fire_pit)默认走「一次一个」名单:数量保持原版 1 个, 但仍然会从附近箱子自动取木头 —— 做饭的基础火一次烧 5 个木头太快。 - 就近取料:背包料不够时,从半径 30 米内的箱子自动补足。 默认认 木箱 / 加固型宝箱 / 黑金宝箱 / 灰石箱 / 桶 / 衣柜; 个人宝箱与珍宝箱有意排除。
- 板车也算料源(
IncludeCarts,默认开):静止停放的板车/雪橇与箱子同样会被取料; 正在被拖行的板车会跳过(位置在变,且去接管所有权可能把车抢走)。⚠️ 不需要把
Cart加进AllowedContainers—— 板车走的是独立的识别通道, 与箱子白名单无关(判断依据见下方「板车是怎么被认出来的」)。 - 占用保护:队友站在箱子旁就跳过它,避免抢走所有权导致对方物品回弹。
配置(5 节)
| 节 | 内容 |
|---|---|
1 - Batch Fill |
Enabled / BatchSize / StationOreAllow |
2 - Fire Sources |
Enabled / Exclude / Sources(留空 = 全部火源生效)/ OneAtATime / HoldInterval / RespectToggle |
3 - Nearby Chests |
Enabled / Radius / AllowedContainers / IncludeCarts / RespectWards / ClaimOwnership |
4 - Safety |
SkipInUse / SkipIfPlayerNear / PlayerNearDistance |
5 - Debug |
DebugLogging |
BepInEx/config/easycraft.client.only.cfg ⚠️ 改完必须重启游戏。
板车是怎么被认出来的(1.0.5 修正)
板车(Cart)与雪橇(Sled)用的都是游戏的 Vagon 组件,内部各带一个容器。
它们不在 AllowedContainers 白名单里 —— 走的是独立识别通道,两条信号任一命中即算板车:
| 优先级 | 信号 | 原理 |
|---|---|---|
| 主 | CartRegistry(本模组维护) |
用 Harmony 补丁复刻游戏自己的 Vagon.m_instances 清单(Awake 里加、OnDestroy 里删),再用 Vagon.m_container 反查出对应的容器 |
| 兜底 | Container.m_wagon != null |
游戏字段,两者互相引用 |
为什么不用 Container.m_wagon 当唯一依据(1.0.5 修的就是这个):
这个字段在整个游戏代码里零处写入(只靠预制体序列化),只有 3 处读取,
且全部带 null 判断:
Container.RPC_RequestOpen / RPC_RequestStack / RPC_RequestTakeAll
if (IsInUse() || (m_wagon != null && m_wagon.InUse())) { 拒绝本次开启 }
⇒ 说明游戏本身就预期它可能为 null,为 null 时一切正常、没有任何可见症状。 而旧版代码只认它 ⇒ 一旦为 null,板车就会被当成"名字不在白名单的普通箱子"静默跳过。
反过来 Vagon.m_container 是必须连的:UpdateMass / UpdateLoadVisualization
都靠它算载重(往板车里放东西会让车变沉,这是核心可见行为)。⇒ 以它为主信号才可靠。
什么情况会跳过板车
| 情况 | 是否取料 | 日志里会说什么 |
|---|---|---|
| 静止停放 | ✅ 取 | 正常取料日志 |
| 正在被拖行 | ❌ 跳过 | [板车被跳过] … → 正在被拖行(停放后即可取) |
IncludeCarts = false |
❌ 跳过 | [板车被跳过] … → IncludeCarts 开关是关的 |
| 有人正开着它的容器 | ❌ 跳过 | [有人正在用] …(受 SkipInUse 控制) |
| 守卫石无权限 | ❌ 跳过 | [守卫石无权限] …(受 RespectWards 控制) |
每条诊断都会带 板车[是(哪条信号)/否],能直接看出识别有没有生效。
站点进料限制 StationOreAllow(1.0.2 起)
原版炭窑能烧 木材 / 细木 / 圆木 三种,价值差很多。这一项让本模组的自动装填 只挑你允许的材料 —— 默认就是「炭窑只烧木材」:
charcoal_kiln=$item_wood
想同时限制别的站点,用分号加组:
charcoal_kiln=$item_wood ; smelter=$item_copperore,$item_tinore
-
材料名三种写法都认(忽略大小写):
$item_wood/item_wood/Wood -
站点名也三种都认:
charcoal_kiln(GameObject 名)/piece_charcoalkiln(本地化键)/charcoalkiln—— 内部会去掉piece_与下划线后归一化比较,所以顺手怎么写都能匹配上。 -
常用站点名:
站点 GameObject 名 炭窑 charcoal_kiln冶炼炉 smelter高炉 blastfurnace风车 windmill纺织机 piece_spinningwheel埃达之力提炼器 piece_eitrrefinery -
只影响本模组的自动装填(批量装填 + 就近取料);你自己手动往站点里放什么都不受限制。
-
留空 = 不限制任何站点(完全回到原版);某个站点写成
站点=(右边留空)= 单独放行该站点。 -
名字万一写错,站点不会变成"什么都装不进去" —— 会退回原版行为, 同时打一条 Warning 把该站点实际可用的材料名列出来,直接照抄即可。
-
生效情形看日志里的
站点进料限制生效:charcoal_kiln 只允许装填 [$item_wood](该站点可用共 3 种:…)。
三张火源名单叠加后的语义(1.0.1 起)
| Exclude | OneAtATime | 行为 |
|---|---|---|
| ✅ 命中 | ❌ 不在 | 完全交回原版(一次 1 个、也不从箱子取料) |
| ✅ 命中 | ✅ 在 | 数量固定 1 个,但料源(附近箱子)可用 ← 营火默认 |
| ❌ 未命中 | — | 正常接管,一次装 BatchSize 个 |
想给别的火源也用「一次一个 + 拉料」,把它的 GameObject 名同时加进 Exclude 和 OneAtATime。
⚠️ 填火源名字前先看这里
Exclude / Sources 里填的是 GameObject 名,它不一定等于本地化键:
| 官方中文 | GameObject 名 | 容易误写 |
|---|---|---|
| 营火(能做饭的基础火) | fire_pit |
piece_firepit |
| 篝火 | bonfire |
piece_bonfire |
| 灶 | hearth |
piece_hearth |
| 壁灯 | piece_dvergr_lantern |
— |
| 火炬 | piece_groundtorch / piece_groundtorchwood … |
— |
不确定某个火源到底叫什么,看日志里的 [火源探测] 行 ——
它会把「组件在=…」和「ZDO名=…」都打出来。
⚠️ 填箱子名字前先看这里(1.0.6 修正)
好消息:你几乎不用管下划线怎么写 —— 匹配已归一化,两种写法都认。
归一化规则 = 忽略大小写 + 去 piece_ 前缀 + 去掉所有下划线。
所以下面这些写法全部等价,随便挑一种:
piece_chest_blackmetal == piece_chestblackmetal == CHESTBLACKMETAL
为什么改成归一化(1.0.4 的"修正"其实修错了)
游戏里 GameObject 名与本地化键不是同一个字符串。真机日志实证(黑金宝箱采样 260 次):
| GameObject 名(匹配用的是这个) | 本地化键 | 一致? |
|---|---|---|
piece_chest |
$piece_chest |
✅ |
piece_chest_blackmetal |
$piece_chestblackmetal |
❌ 不一致 |
piece_chest_private |
$piece_chestprivate |
❌ 不一致 |
⇒ 多词箱子的 GameObject 名带下划线、本地化键不带;单词的 piece_chest 两者相同。
这就是为什么大箱子一直好使,而黑金箱等一直静默失配。
⚠️ 1.0.0~1.0.4 的经历值得一提:1.0.4 曾放话说"箱子名一律不带下划线",依据是扫了
resources.assets —— 而那份里装的其实是本地化键,不是预制体名,
结果把一个原本能匹配的名字(黑金箱)反而写坏了。现在两种写法都通,不必再纠结。
箱子对照表(归一化键 = 实际参与匹配的键)
| 官方中文 | GameObject 名 | 本地化键 | 归一化键 | 默认纳入 |
|---|---|---|---|---|
| 宝箱(木箱) | piece_chest_wood |
$piece_chestwood |
chestwood |
✅ |
| 加固型宝箱(大箱子) | piece_chest |
$piece_chest |
chest |
✅ |
| 黑金宝箱 | piece_chest_blackmetal |
$piece_chestblackmetal |
chestblackmetal |
✅ |
| 灰石箱 | piece_chest_grausten |
$piece_chestgrausten |
chestgrausten |
✅ |
| 桶 | piece_chest_barrel |
$piece_chestbarrel |
chestbarrel |
✅ |
| 衣柜 | piece_chest_warderobe |
$piece_chestwarderobe |
chestwarderobe |
✅ |
| 个人宝箱 | piece_chest_private |
$piece_chestprivate |
chestprivate |
排除 |
| 珍宝箱 | piece_chest_treasure |
$piece_chesttreasure |
chesttreasure |
排除 |
想恢复某个被排除的箱子(比如个人宝箱),把它的任意一种写法加进 AllowedContainers 即可:
piece_chestprivate ← 这样写
piece_chest_private ← 或者这样写,完全等价
启动自检会同时打印你写的原文和生效的归一化键,名字对不对得上可以当场核对:
就近取料=开:半径 30 米,允许的箱子 6 种 [piece_chest,piece_chestbarrel,…](板车=开),…
箱子名匹配已归一化(去 piece_ 前缀 + 去所有下划线 + 忽略大小写):生效匹配键=[chest,chestbarrel,…]
💡 现在站点名与箱子名用的是同一套归一化规则,不会再出现"这个要精确、那个能宽松"的混乱。
实现要点
- 站点装填:
Smelter.OnAddOre/OnAddFuel的 prefix;火源:Fireplace.Interact的 prefix。 - 料源:
Container.Awake/OnDestroyed维护活箱子登记表,搜索时以【机器】和【玩家】各自为圆心。 取料顺序:先吃背包 → 按距离更近优先逐个箱子取 → 取够即停。 - 取料前用
ZNetView.ClaimOwnership()接管箱子 ZDO 所有权 —— 必须, 否则Container.OnContainerChanged()的if (!IsOwner()) return;会让改动不被保存,产生复制。 - 🔴 Harmony prefix 铁律:处理不了就
return true完整交回原版,绝不静默return false—— 那会表现为「按 E 完全没反应」,极难排查。
联机
纯客户端。 主机、其他玩家都不需要装。
不声明 NetworkCompatibilityAttribute,不引用 Jotunn,不内嵌 ServerSync / CCS。