⚠️ 适用性须知 — 安装前请阅读。
该补丁专为 我自己深度魔改过的配置 (已加载30多个模组——BepInEx 5.4.2350,Valheim 1.0.15,由r2modman管理的30个已启用模组)。以下描述的症状 可能是我特定的模组组合导致的,而不是原模组本身的 bug.
- 如果你是 不 正是看到这个症状, 请不要安装这个 ——它对你可能毫无作用。
- 这个补丁可能根本不适用于你的配置 ——使用不同的模组列表,该症状可能永远不会出现。
- 即使症状相同, 根本原因可能不同 ——这只是根据我在自己机器上发现的情况所采用的变通方法。
- 我并不声称原模组有缺陷,也无意指责其作者。
如果你认为原模组行为异常, 请先向其作者反馈。 这只是我个人的“在我机器上能用”的临时办法, 但不保证对其他人也有效。
⚠️ 适用性声明 —— 请先读这一段,再决定装不装。
本补丁是在我自己那套环境里做出来的:当时本机同时装着 30+ 个模组 (BepInEx 5.4.2350 / 游戏 1.0.15 / r2modman 管理的 30 个启用模组)。 下面描述的“症状”有可能是我的这套模组组合互相冲突造成的,并不一定是原模组本身的 bug。
- 如果你没有遇到完全相同的症状,请不要安装本补丁 —— 它可能什么都不做;
- 本补丁可能不适用于你的环境:换一套模组组合,症状也许根本不会出现;
- 就算症状一样,成因也可能不同 —— 本补丁只是我按本机情况查出来的绕行方案;
- 我没有、也不主张任何“原模组有 bug”的结论,原模组作者与此事无关。
如果你觉得原模组有问题,请先向原模组作者反馈。这只是我个人“能跑就行”的绕行方案, 不保证适用于任何人。
简易种植 兼容性补丁(纯客户端,不改写 PlantEasily 任何文件)。 它不针对任何具体模组 —— 只是把几个模组共用的那几处运行时状态归位,使多方同时操作同一部件时不再互相干扰:
| # | 归位什么 | 一句话 |
|---|---|---|
| 1 | 网格幽灵的坐标缓存 | 整片作物不再叠在同一个点(症状只在多方共存时出现,单独装 PlantEasily 时不会发生) |
| 2 | 上次选中的作物 | 收什么就补回什么,不再一路沿用同一种 |
| 3 | 网格幽灵的全部格位坐标 | 大网格下不再有零星几株叠在一起(网格调大后才会出现) |
| 项 | 值 |
|---|---|
| 适用模组 | 轻松种植 2.2.2(2.0.0+ 逻辑相同,理论通用) |
| 本补丁版本 | 1.2.2 |
| 前置 | BepInEx 5.4.2333+ |
| 类型 | 客户端补丁(单人 / 联机客户端皆可,主机无需安装) |
| 依赖 | 无(未安装 PlantEasily 时静默不做事) |
使用PlantEasily一次种植一片时,整片作物全部叠在同一个点上。实测:4×4 = 15 个额外幽灵,坐标完全相同。
⚠️ 前提这个症状只在多方共存的环境里出现过只装
BepInExPack_Valheim 5.4.2350 + Advize-PlantEasily 2.2.0时不复现。
例(探针日志):
ghost[0..14] 全部 pos = (2062.731, 49.423, -4402.94) 不同坐标数 = 1 / 15 落点 1..15 全部 pos = (2062.731, 49.423, -4402.94)
放置幽灵的坐标并不是每帧都重算的 —— 它带一层版本号缓存。把这条链拆开看(下面都是反编译实测,不是推测):
① 幽灵网格.初始化幽灵 先把所有额外幽灵叠在一点
// 重建网格时,每个额外幽灵的位置被直接设成「主幽灵位置」 ExtraGhosts[i].transform.position = 主幽灵位置;
② 真正展开坐标靠 幽灵网格更新,但它有脏检查会提前退出
// GhostGrid.UpdateGhost if (cache.lastUpdatedVersion == _gridVersion) return; // ← 判定“已是最新”,直接返回,不更新位置
③ 而 幽灵缓存.初始化 不重置 最后更新版本
// GhostCache.Init —— 只设 piece / plant this.piece = go.GetComponent<Piece>(); this.plant = go.GetComponent<Plant>(); // ★ 没有重置 lastUpdatedVersion ★
④ 版本号只在「位置/朝向/格数变化」时才自增
// GhostGrid.UpdateGridVersion —— 只有这四种情况会 _gridVersion++ 位移 > 0.0001(约 1cm) | BaseRotation 变化 > 1° 有效朝向变化 > 0.1° | Rows / Columns 被改
⇒ 于是当**「复用了旧幽灵池 + 位置被重设 + 本帧版本号没变」三件事撞在一起时(放置流程尾部会经 玩家.设置放置幽灵 重建网格,而站在原地不动、不转视角**时版本号恰好不变),被复用的幽灵就会带着“已是最新版本”的缓存直接提前退出 ⇒ 坐标停在被重设的那一刻,再也不被修正。
这也解释了为什么症状看起来时有时无:移动超过 1cm 或转视角超过 1° 就会让版本号自增,下一次种植便自动恢复正常。
在 幽灵网格.构建网格 之后让版本号 +1:
[HarmonyPostfix] static void BuildGridPostfix() { int before = (int)_gridVersionField.GetValue(null); _gridVersionField.SetValue(null, before + 1); // 让所有幽灵缓存失效 }
缓存立即失效 ⇒ 下一帧 幽灵网格更新 必定重算每个幽灵的网格坐标 ⇒ 玩家点击种植时幽灵已经摆正。
网格版本 是 私有静态整数,只有 更新网格版本() 会写它,且该函数只会 +1 或保持不变、绝不回退,所以外部 +1 不破坏其单调性。 全程序集反查确认它只被模组内部使用,无跨模组引用。
| 之前 | 之后 | |
|---|---|---|
| 幽灵不同坐标数 | 1 / 15 | 15 / 15 |
| 落点坐标 | 15 个完全相同 | 15 个各不相同 |
日志(探针版):
[修复] BuildGrid 完成 → _gridVersion 340 → 341 [诊断] Update 后已恢复正常:15 个幽灵分布到 15 种不同坐标 —— 生效
连续 88 次网格重建(含多次种植)全部正常。
开 [采集]收获后重新种植 时,无论收获什么作物,都会补种成“你最近用耕耘机选中的那一种”。 于是混种地块越收越单一:今天最后种的是胡萝卜,明天收大麦、收萝卜,全给你补胡萝卜。
这个补丁把它改成了每种作物各补各的。
交互补丁.前缀 第二重载的 IL 逐条实证)| IL 偏移 | 逻辑 |
|---|---|
003D–0042 |
读 最后选中的植物名称 并 为空或空字符串 |
0049 |
非空 ⇒ 查表键 = 上次选择的植物名称 ← ★统一补种走这边 |
0050 |
为空 ⇒ 查表键 = 工具.获取预制体名称(被收获物) ← 各补各的走这条 |
005C |
ReplantDB.Registry.TryGetValue(键, out db),失败 ⇒ 不补种(交回原版) |
006C |
ModContext.PrefabRefs.TryGetValue(db.PlantName, out Piece) |
0090 |
玩家.满足要求 / 无放置费用 检查 |
00A6 |
PlacementController.PlacePiece(localPlayer, 被收获物, Plant.prefab) |
00C6 |
消耗资源 |
最后选择的植物名称 的唯一写点是 设置引用
(方法开头先置 null,仅当幽灵身上有 种植 组件时才重新赋值), 而它的唯一调用者是 幽灵网格.初始化幽灵
⇒ 只要种过一次某种作物,这个值就长期保留“那一种”。
该字段的读取点只有两处(全程序集成员反查,非推测):
交互补丁.前缀 —— 决定补种哪一种;悬停文本补丁。后置修复 —— 拼悬停提示 重新种植为:X。两处都带 为空或null 分支,空值是合法状态(该字段在 .cctor 里初始就是 空) ⇒ 把它压制为 null 只会让两边都退回“按被收获物”分支,没有别的副作用。
顺便一提:
回收数据库.注册表同时登记了 可拾取物名称与Plant名两套键(模组初始化补丁.前缀里注册表.添加(可拾取物.名称, 数据库)),所以“按被收获物查表”这条路本来就是通的 ——只是被上面那个非空值挡住了。
[和谐后置补丁] static void 设置引用后置补丁() { if (最后选中的植物名称 == null) return; // 本来就是空,不动 最后选中的植物名称 = null; // 让它退回"按被收获物"分支 }
重新种植为:<绿>X</绿> —— X 就是本次实际会补种的那种。改动生效后收大麦应显示大麦,收胡萝卜应显示胡萝卜。BepInEx/LogOutput.log 里会出现
首次拦截成功:LastSelectedPlantName「…」→ 置空,此后补种按实际收获的作物进行。[重新种植] 同种作物重植 改成 假,不用重启游戏,下一次补种立刻恢复“统一成一种”。网格调大之后,仍有零星几株作物种在同一个点上(不是整片叠住,是“有几株挤在一起”)。网格小的时候完全正常。
实测分界:本机
4×4(16 格)从不复现;1×20(20 格)起就能碰到。 关键变量是每帧能摆几个幽灵(幽灵更新批处理大小,本机 50)与帧内时序。
补丁 1 只解决了“整片叠住”,解决不了这个 —— 因为它保证的是 下一帧会被摆正,而不是**“点下去的时候已经摆正了”**。这里有两重缺口。
玩家.设置放置预览 重建网格(构建网格)与每帧的 幽灵网格.更新
在同一帧里的先后顺序不固定。若 更新 先跑:
更新 → 把各幽灵摆到网格坐标 BuildGrid→ InitializeGhosts 把所有幽灵位置【整体覆盖回主幽灵位置】 本帧再无第二次更新 ⇒ 停在主幽灵位置
幽灵网格。更新 不是一次摆完,而是每帧只摆一批(IL逐条实证):
0046 ldc.i4.1 / get_MaxActiveGhosts / add → N = MaxActiveGhosts + 1 004E config.GhostUpdateBatchSize 0059 Mathf::Min → B = min(GhostUpdateBatchSize, N) 0064 _ghostUpdateIndex % N → 从轮转游标开始 ★ 游标只在 Update 里 +1,永不复位 00C1 for (k = 0; k < B; k++) 每帧只处理 B 个
| 网格 | 总格数 | 每帧处理 B | 首帧漏掉 |
|---|---|---|---|
| 4×4 | 16 | 16 | 0 |
| 1×20 | 20 | 20 | 0 |
| 10×20 | 200 | 50 | 150 |
| 20×20 | 400 | 50 | 350 |
网格版本 的自增条件只有四条(更新网格版本 逐条实证): 位移 > 0.0001(约 1cm) / 朝向变化 > 1° / 有效朝向变化 > 0.1° / 行·列 变化。
鼠标一停(松手、切物品、站着不动)这四条全不成立 ⇒ 版本号冻结 ⇒ 更新幽灵 开头的脏检查(lastUpdatedVersion == _gridVersion ⇒ 早退) 把没轮到的幽灵全部挡在门外 ⇒ 它们永久停在主幽灵位置。
按下种植时,获取幽灵位置(行=0, 列=0) 对它们全部返回同一个 基础位置, 它们被摆成一摞同坐标幽灵、全部通过 评估状态 判定为合法,而 玩家放置棋子.后缀 只按 for (i = 0; i < MaxActiveGhosts; i++) 逐槽收集(没有坐标去重)⇒ 这几株全部种在同一点。
这也解释了为什么是“零星几株而不是整片:残留的永远只是上次游标之后的那一小段。
网格建造 之后不再依赖后续帧 —— 当场把 [1, MaxActiveGhosts] 全部再摆一次:
[HarmonyPostfix] static void BuildGridPostfix() { _gridVersionField.SetValue(null, before + 1); // ① 缓存失效(补丁 1) object cfg = _modConfigField.GetValue(null); // ModContext.config ⇒ ModConfig 实例 int maxActive = get_MaxActiveGhosts(); // ② 全量补摆(补丁 3) int columns = get_Columns(cfg); // ⚠️ 实例属性,必须带 target(1.2.2 修) for (int slot = 1; slot <= maxActive; slot++) { var go = GetGhostObject(slot); // ExtraGhosts[slot - 1] // ⚠️ 上游签名 = (int row, int column, int index),共 **3** 个参数,不含幽灵对象(1.2.1 修) var pos = GetGhostPosition(slot / columns, slot % columns, slot); UpdateGhostTransform(go, pos); // set_position + set_rotation GetGhostCache(go).lastUpdatedVersion = _gridVersion; // 防下帧被早退 } }
槽位 / 列 与 槽位 % 列 与原版 更新 里那两行 div / 移除 完全一致
(已核对编译后的 IL:div 与 雷姆 指令确实存在)。更新幽灵状态 ⇒ 不介入任何合法性判定,写错也绝不会凭空把一株作物判成“可种”或“不可种”。幽灵更新批大小 ⇒ 只在 建造网格 那一刻做一次全量,之后每帧仍保持原版的分批节奏,不吃性能。| 检查 | 结果 |
|---|---|
| 补摆后每个格位坐标唯一(9 种网格尺寸,含 20×20) | 全部唯一,无重合 |
方位分解与原版 div/雷姆 一致(逐槽比对) |
全部一致 |
| 缺口一(更新先跑)补摆后 | 全部摆正 |
| 缺口二(大网格分批)补摆后 | 全部摆正 |
把 [通用] 详细日志 改成 真,然后每次放下一片网格都会打一行:
[补摆 3] 网格 20 格(MaxActiveGhosts=19, Columns=20)→ 已摆正 19 个幽灵、 同步 19 个坐标缓存;累计补摆 3 次 / 57 个。
看 已摆正 N 个 是否等于 最大活动幽灵数 —— 相等就说明整片都摆正了。 若你看到的数字小于它,说明补摆被降级了(日志里会有 补摆降级:… 的警告),把日志发我。
⚠️ 特别注意,如果看到 补摆降级:… 的参数个数不符 或 补摆降级:… 是**实例**成员,但调用时没有目标实例, 说明句柄自检(1.2.1 起改为「参数个数 + 静态性」双重校验)把上游的签名变化拦截下来了——补摆会停用自保(补丁 1/2 不受影响),请把日志和 PlantEasily 版本号发我。
首次运行会在 BepInEx/config/s6652289.PlantEasilyFix.cfg 生成:
| 项 | 默认 | 说明 |
|---|---|---|
[通用] 已启用 |
真 |
总开关。关掉并重启即等同于未安装 |
[通用] 详细日志 |
假 |
每次修正 / 补摆写一行日志(排查用)。正常游玩建议关闭 |
[补种] 同作物补种 |
真 |
补种是否按“被收获的那种作物”来算。改完立即生效,无需重启 |
手动把 PlantEasilyFix.dll 放进去 BepInEx/plugins/。
r2modman:直接拖入本 zip,或把整个包放进
%APPDATA%\r2modmanPlus-local\Valheim\profiles\<profile>\BepInEx\plugins\PlantEasilyFix\。
整数 版本号,补丁2只设置了一个静态字符串为 空, 补丁 3 只写幽灵的 变形 位置/朝向与一个缓存版本号 ——
不碰资源、不碰存档数据、不介入任何种植合法性判定;advize.轻松种植.cfg;获取幽灵位置 / 更新幽灵变形 / 获取幽灵对象 /
幽灵缓存 / 最后更新版本 / 额外幽灵 / 最大活动幽灵数 /
列 及其宿主实例 ModContext.config)
缺任何一个都只是补摆降级关闭(日志会打一条警告),补丁 1 与补丁 2 照常工作; 其中 模组配置.列 是实例属性,必须搭配 配置 获取到的实例来调用 —— 1.2.0 / 1.2.1 漏了这一步(拿 null 当目标),每次调用都抛出 目标异常 被吞掉,
补摆整整两代从来没运行起来过(2026-09-25 真机实证:54 次调用、0 次成功)。1.2.2 修正;删掉 植物种植修复.dll 即可。没有任何持久化改动需要还原(配置项只影响本插件自身)。
幽灵网格.构建网格、
放置状态.设置引用),都不改原方法返回值。AI生成(AI生成)

修改前置工具
2026-09-12 14:33:53
修改前置工具

史诗战利品
randyknapp2026-09-09 21:20:10
为英灵神殿添加战利品掉落、魔法物品和附魔。

制作 建造 熔炼 烹饪 燃料 从容器中取出
aedenthorn2025-06-30 08:34:14
让你在制作、建造、使用熔炉和窑炉、添加燃料、烹饪食物等时,可以使用附近容器中的资源。

种植一切
Advize2025-09-09 11:15:37
重写了bkeyes93的种植增强模组。允许你的种植者种植浆果丛、蓟、蒲公英、蘑菇、此前无法获取的树种以及其他装饰性植物。还包括许多其他杂项功能(详见

无限制传送门
frostedplaything4102023-09-02 17:39:40
无限制传送门是一个客户端模组,允许玩家通过传送门传送任何物品。该模组提供广泛的自定义选项,让玩家能够控制物品限制、移除限制,

农场格子
Sarcenzzz2023-06-14 11:38:34
允许所有通过种植器种下的植物自动对齐并放置在网格中,确保它们有足够的生长空间。

更好的UI
MKMasa2021-04-15 09:19:42
这个模组对游戏界面做了一些细微的调整。你可以编辑并选择想要启用的更新内容。

更好的射箭
Ishid42026-09-10 16:16:51
为弓术玩法添加了许多功能和改动,包括全新的箭袋系统、可回收的箭矢、更精准的瞄准以及逼真的箭矢物理效果。