Valheim
You are viewing a potentially older version of this package. View Latest Version
Install with App

Details

Date Uploaded
last month
Downloads
1.9K
Size
18KB
ADDatHost Valheim hosting
30% off

PlantEasily Fix

⚠️ APPLICABILITY NOTICE — please read this before installing.

This patch was written for my own, heavily modded setup (30+ mods loaded — BepInEx 5.4.2350, Valheim 1.0.15, 30 enabled mods managed by r2modman). The symptom described below may be caused by my particular mod combination rather than by a bug in the original mod.

  • If you are not seeing exactly this symptom, please don't install this — it may do nothing for you.
  • This patch may not apply to your setup at all — with a different mod list the symptom may never appear.
  • Even with the same symptom, the root cause may be different — this is only a workaround derived from what I found on my machine.
  • I do not claim the original mod is buggy, and no blame is directed at its author.

If you think the original mod misbehaves, please report it to its author first. This is just my personal "works-on-my-machine" workaround, with no guarantee it works for anyone else.

⚠️ 适用性声明 —— 请先读这一段,再决定装不装。

本补丁是在我自己那套环境里做出来的:当时本机同时装着 30+ 个模组 (BepInEx 5.4.2350 / 游戏 1.0.15 / r2modman 管理的 30 个启用模组)。 下面描述的“症状”有可能是我这套模组组合互相冲突造成的,并不一定是原模组本身的 bug。

  • 如果你没有遇到完全相同的症状,请不要安装本补丁 —— 它可能什么都不做;
  • 本补丁可能不适用于你的环境:换一套模组组合,症状也许根本不会出现;
  • 就算症状一样,成因也可能不同 —— 本补丁只是我按本机情况查出来的绕行方案;
  • 我没有、也不主张任何“原模组有 bug”的结论,原模组作者与此事无关。

如果你觉得原模组有问题,请先向原模组作者反馈。这只是我个人“能跑就行”的绕行方案, 不保证适用于任何人。

给 Advize-PlantEasily 打的两个行为修正补丁(纯客户端,不改写 PlantEasily 任何文件):

# 修什么 一句话
1 重叠种植 网格种植时整片作物全部叠在同一个点 → 摆正
2 自动补种 收什么都补成同一种 → 改成每种作物各补各的
项 值
适用模组 Advize-PlantEasily 2.2.0(2.0.0+ 逻辑相同,理论通用)
本补丁版本 1.1.1
前置 BepInEx 5.4.2333+
类型 客户端补丁(单人 / 联机客户端皆可,主机无需安装)
依赖 无(未安装 PlantEasily 时静默不做事)

补丁 1 · 重叠种植

症状

用 PlantEasily 一次种一片时,整片作物全部叠在同一个点上。 实测:4×4 = 15 个额外幽灵,坐标完全相同。

例(修复前的探针日志):

ghost[0..14] 全部 pos = (2062.731, 49.423, -4402.94)
不同坐标数 = 1 / 15
落点 1..15  全部 pos = (2062.731, 49.423, -4402.94)

根因

作者代码里有两处设计撞在一起:

① GhostGrid.InitializeGhosts 先把所有幽灵叠在一点

// 重建网格时,每个额外幽灵的位置被直接设成「主幽灵位置」
ExtraGhosts[i].transform.position = 主幽灵位置;

② 真正摆开坐标靠 GhostGrid.Update,但它有脏检查会早退

// GhostGrid.UpdateGhost
if (cache.lastUpdatedVersion == _gridVersion)
    return;                      // ← 判定"已是最新",直接返回,不更新位置

③ 而 GhostCache.Init 不重置 lastUpdatedVersion

// GhostCache.Init —— 只设 piece / plant
this.piece = go.GetComponent<Piece>();
this.plant = go.GetComponent<Plant>();
// ★ 没有重置 lastUpdatedVersion ★

④ 触发点在批量种植协程的末尾

// <BulkPlanting>d__10.MoveNext 尾部(IL 偏移 +0389 起)
_isPlanting = false;
Player.SetupPlacementGhost();      // ← 重建幽灵网格
ReEnableRenderers(...);

SetupPlacementGhost → BuildGrid → GrowPoolIfNeeded(复用旧幽灵池)+ InitializeGhosts ⇒ 位置被重设为"全叠住",而复用幽灵身上残留的 lastUpdatedVersion 仍等于当前 _gridVersion ⇒ 下一帧 UpdateGhost 全部早退 ⇒ 位置永远停在"全叠住"状态。

修复方式

在 GhostGrid.BuildGrid 之后让版本号 +1:

[HarmonyPostfix]
static void BuildGridPostfix()
{
    int before = (int)_gridVersionField.GetValue(null);
    _gridVersionField.SetValue(null, before + 1);   // 让所有幽灵缓存失效
}

所有幽灵的缓存立即失效 ⇒ 下一帧 GhostGrid.Update 必定重算每个幽灵的网格坐标 ⇒ 玩家点击种植时幽灵已经摆正。

_gridVersion 是 private static int,只有 UpdateGridVersion() 会写它, 且该函数只会 +1 或保持不变、绝不回退,所以外部 +1 不破坏其单调性。 全局搜索确认它是纯模组内部使用(readonly 初始化 + 两处使用),无跨模组引用。

修复效果(探针实测,同一台机器)

修复前 修复后
幽灵不同坐标数 1 / 15 15 / 15
落点坐标 15 个完全相同 15 个各不相同

修复后日志(探针版):

[修复] BuildGrid 完成 → _gridVersion 340 → 341
[诊断] Update 后已恢复正常:15 个幽灵分布到 15 种不同坐标 —— 修复生效

连续 88 次网格重建(含多次种植)全部正常。


补丁 2 · 自动补种按「被收获的那种作物」

症状

开着 [Harvesting]ReplantOnHarvest 时,收任何作物都补种成"你最近用耕耘机选中的那一种"。 于是混种地块越收越单一:今天最后种的是胡萝卜,明天收大麦、收萝卜,全给你补胡萝卜。

根因(InteractPatches.Prefix 第二重载的 IL 逐条实证)

IL 偏移 逻辑
003D–0042 读 PlacementState.LastSelectedPlantName 并 IsNullOrEmpty
0049 非空 ⇒ 查表键 = LastSelectedPlantName ← ★统一补种走这条
0050 为空 ⇒ 查表键 = Utils.GetPrefabName(被收获物) ← 各补各的走这条
005C ReplantDB.Registry.TryGetValue(键, out db),失败 ⇒ 不补种(交回原版)
006C ModContext.PrefabRefs.TryGetValue(db.PlantName, out Piece)
0090 Player.HaveRequirements / m_noPlacementCost 检查
00A6 PlacementController.PlacePiece(localPlayer, 被收获物, Plant.prefab)
00C6 Player.ConsumeResources

LastSelectedPlantName 的唯一写点是 PlacementState.SetReferences (方法开头先置 null,仅当幽灵身上有 Plant 组件时才重新赋值), 而它的唯一调用者是 GhostGrid.InitializeGhosts ⇒ 只要种过一次某种作物,这个值就长期留着"那一种"。

该字段的读取点只有两处(已用全程序集成员反查确认,非推测):

  1. InteractPatches.Prefix —— 决定补种哪一种;
  2. HoverTextPatches.Postfix —— 拼悬停提示 Replant as: X。

两处都带 IsNullOrEmpty 分支,空值是合法状态(该字段在 .cctor 里初始就是 null) ⇒ 把它压制为 null 只会让两边都退回"按被收获物"分支,没有别的副作用。

顺带一提:ReplantDB.Registry 里同时登记了 Pickable 名与 Plant 名两套键 (ModInitPatches.Prefix 里 Registry.Add(pickable.name, db)), 所以"按被收获物查表"这条路本来就是通的 —— 只是被上面那个非空值挡住了。

修复方式

[HarmonyPostfix]
static void SetReferencesPostfix()
{
    if (LastSelectedPlantName == null) return;       // 本来就是空,不动
    LastSelectedPlantName = null;                    // 让它退回"按被收获物"分支
}

怎么自己在游戏里验证

  1. 看提示:悬停一株成熟作物,hover 文本会多一行 Replant as: <绿>X</绿> —— X 就是本次实际会补种的那种。修复后收大麦应显示大麦,收胡萝卜应显示胡萝卜。
  2. 看日志:BepInEx/LogOutput.log 里会出现 首次拦截成功:LastSelectedPlantName「…」→ 置空,此后补种按实际收获的作物进行。
  3. 想对比原行为:把 [Replant] SameCropReplant 改成 false,不用重启游戏, 下一次补种立刻恢复"统一成一种"。

配置

首次运行会在 BepInEx/config/s6652289.PlantEasilyFix.cfg 生成:

项 默认 说明
[General] Enabled true 总开关。关掉并重启即等同于未安装
[General] VerboseLog false 每次修正写一行日志(排查用)。正常游玩建议关闭
[Replant] SameCropReplant true 补种是否按「被收获的那种作物」。改完立即生效,无需重启

安装

手动:把 PlantEasilyFix.dll 放进 BepInEx/plugins/。

r2modman:直接拖入本 zip,或把整个包放进 %APPDATA%\r2modmanPlus-local\Valheim\profiles\<profile>\BepInEx\plugins\PlantEasilyFix\。


安全性

  • 补丁 1 只动一个 int 版本号,补丁 2 只置一个静态字符串为 null —— 不碰位置、资源、存档数据;
  • 不修改 PlantEasily 的任何配置项,无需改 advize.PlantEasily.cfg;
  • 未安装 PlantEasily 时,本插件等 2 分钟后静默退出,不做任何事;
  • 所有反射调用都包在 try/catch 里 —— 补丁自身出问题最多是"这一次没修到", 不会让游戏崩溃;
  • PlantEasily 版本变化导致方法/字段找不到时,只记一条 Error 并放弃该条补丁,不影响游戏;
  • 两条补丁互相独立:某一条挂不上不影响另一条。

卸载

删掉 PlantEasilyFix.dll 即可。没有任何持久化改动需要还原(配置项只影响本插件自身)。


备注

  • 本补丁是给第三方模组打的行为修正,不改写 PlantEasily 的文件; PlantEasily 作者一旦自行修复,本补丁可直接卸载。
  • 与其它补丁/模组无冲突:只挂两个 postfix(GhostGrid.BuildGrid、 PlacementState.SetReferences),都不改原方法返回值。

AI 生成(AI-generated)

Thunderstore development is made possible with ads. Please consider making an exception to your adblock.