Valheim
Install with App

Details

Latest version
1.2.2
Last Updated
First Uploaded
Downloads
1.8K
Likes
0
Size
28KB
Dependants
ADDatHost Valheim hosting
30% off

Changelog

1.2.2

再修 bug:补丁 3「大网格全量补摆」仍然从未生效 —— 1.2.1 只修了一半。

  • 根因(2026-09-25 真机实证):ModConfig.Columns 是实例属性 (上游 ModConfig 是 sealed class,该属性无 static),而 1.2.0 / 1.2.1 用 Invoke(null, null) 调它 ⇒ 每次都抛 TargetException: Non-static method requires a target., 被 RepositionAllGhosts 自己的 catch 吞成一条 LogError。 该行在 for 循环之前,所以补摆一次都没进入过循环。 真机日志:补摆幽灵坐标失败(…):Non-static method requires a target. × 54, 而 [补摆 …] 成功自证行 0。
  • 为什么 1.2.1 的自检没拦住:当时新增的 RequireArity 只核对参数个数 (get_Columns 是 0 参 ✓),没核对静态性 ⇒ 句柄解析通过、启动日志照样打 「已生效:…并当场补摆全部网格格位」。这与 1.2.0 那次(4 参)是同一个 「解析得到 ≠ 调用得动」失效模式的第二次出现。
  • 修正:
    1. 解析期从静态字段 ModContext.config 取 ModConfig 实例(新增句柄 _modConfigInstanceField), 调用期改为 _columnsGetter.Invoke(modConfigInstance, null);
    2. RequireArity 升级为 参数个数 + 静态性双检(新增 hasInstance 形参): 实例成员拿不到实例时,在解析阶段就停用补摆并打出明确日志,不再等到运行时被吞掉;
    3. 不再尝试 GhostGrid.Columns —— 上游 GhostGrid 根本没有该成员,而 HarmonyX 的 AccessTools.Property 找不到时会打一条 Could not find property … GhostGrid and name Columns 的 Warning,与「补摆降级」混淆,纯属噪音(真机日志行 242 就是它)。
  • IL 复核(asm_ildump.py):_columnsGetter 的 Invoke 前已是 ldloc.0(实例)而不再是 ldnull; 同类静态句柄(_maxActiveGhostsGetter)则仍是 ldnull —— 形成对照。
  • 适配确认(官方 2.2.2 DLL metadata):ModConfig.get_Columns / get_Rows = mdStatic=False(实例); 而 GhostGrid 的 GetGhostPosition / GetGhostObject / GetGhostCache / UpdateGhostTransform / get_MaxActiveGhosts / _gridVersion 全部 static=True。
  • 顺带订正文档:README 的伪代码片段此前仍写着 1.2.0 的错误调用 (GetGhostPosition(go, slot % columns, slot / columns, slot) —— 4 个实参,且 row/col 顺序颠倒), 已改为与实现一致的 3 参 GetGhostPosition(slot / columns, slot % columns, slot)。
  • DLL 重新编译:15,360 字节,sha256 0c8bc0bfe52d51daee245f18935c0a739317d18c5e2b6322a2c76380ccfd7340。

连读三代:1.2.0 跑不了(4 参)→ 1.2.1 还是跑不了(实例当 null 调)→ 1.2.2 才真正跑起来。 两次根因同类,都是"反射句柄解析成功 ≠ 调用得动"。

1.2.1

修 bug:补丁 3「大网格全量补摆」自 1.2.0 起从未真正生效。

  • 根因:GhostGrid.GetGhostPosition 的签名是 3 个参数 (int row, int column, int index),而 1.2.0 按 (go, col, row, slot) 传了 4 个实参 ⇒ 每次调用抛 TargetParameterCountException,被 RepositionAllGhosts 自己的 catch 吞成一条 LogError ⇒ 补摆一次都没成功过。 而启动自检照样打印「已生效:…并当场补摆全部网格格位」⇒ 极具误导性。
  • 修正:调用改回 3 参 (row, col, slot)(row = slot / Columns、col = slot % Columns, 与上游 Update 的 div/rem 一致)。行为与 1.2.0 的设计意图完全相同,只是这次真的会跑。
  • 防复发:新增 RequireArity —— 解析阶段逐个核对每个反射句柄的参数个数 (GetGhostPosition 3、UpdateGhostTransform 2、GetGhostObject/GetGhostCache 1、 MaxActiveGhosts/Columns 0),不符即停用补摆并打出「上游给 N 个、本补丁按 M 个调用」的明确日志, 不再静默。
  • 可自证:首次成功补摆时无条件打一行 [补摆 …] 已摆正 N 个幽灵、同步 N 个坐标缓存 (不再依赖 [General] VerboseLog)—— 1.2.0 的失效正是藏在静默里,这次让它一开机就看得出来。
  • 适配确认:对 Advize-PlantEasily 2.2.1 官方 DLL 做了 metadata 核对 —— GetGhostPosition = 3 参、UpdateGhostTransform = 2 参、GhostCache 字段 piece / plant / lastUpdatedVersion 均在;2.2.0 与 2.2.1 的这些签名完全相同 (⇒ 1.2.0 的 bug 与版本升级无关,是从写下来那天就有的)。
  • DLL 重新编译:14,336 字节,sha256 9ff095b1dead21af54fff1ad21719ee9cabc319e289a1c440389d9a2318c8da9。
  • 编译后 IL 复核(asm_ildump.py):newarr System.Object 前的 ldc.i4.3 已从 4 变 3, 且三个实参装载顺序为 ldloc row → ldloc col → ldloc slot,与上游 GetGhostPosition(int row, int column, int index) 逐一对应。

一处方法学更正

1.2.0 的"离线验证"(verify_reposition_logic.py 等)用 Python 重算了坐标算法、 枚举了 BuildGrid/Update 的帧内时序,但从没做过真实反射调用 ⇒ 正好落在「离线模拟只能验证拿到输入之后的分支,输入从哪来必须真机验」的盲区里。 本次改为直接对官方 DLL 读 metadata 核对参数个数。

⚠️ 1.2.2 补注:本版声称的「只是这次真的会跑」并未成立 —— columns 那行(_columnsGetter.Invoke(null, null))在 for 循环之前就抛了 TargetException。 也就是说 1.2.1 修对了参数个数,但补摆依然一次都没跑起来。详见 1.2.2 条目。

1.2.0

新增第三条机制:大网格幽灵「全量补摆」—— 修复网格调大后仍有零星几株作物种到一起。 DLL 重新编译:13824 字节。

  • 起因:用户反馈「小范围种植没问题,范围调大后还是会有一些种到一起」。
  • 为什么补丁 1(BuildGrid 后 _gridVersion++)不够 —— 它保证的是 "下一帧会被摆正",而不是"点下去的时候已经摆正了"。查下来有两重缺口:
    • 缺口一(帧内时序,小网格也会中招):Player.SetupPlacementGhost 重建网格(BuildGrid) 与每帧的 GhostGrid.Update 在同一帧内的先后顺序不固定。若 Update 先跑, 它摆好的坐标随后被 BuildGrid → InitializeGhosts 整体覆盖回主幽灵位置, 而本帧再无第二次 Update(BuildGrid 的唯一调用点是 PlayerSetupPlacementGhost.Postfix 0052)。
    • 缺口二(分批轮转,大网格才中招):GhostGrid.Update 每帧只摆 B = min(GhostUpdateBatchSize, 总格数) 个,且从 _ghostUpdateIndex(只 +1、永不复位) 开始轮转。本机 GhostUpdateBatchSize=50:4×4(16) 与 1×20(20) 一帧摆完, 10×20(200) 首帧漏 150 个、20×20(400) 首帧漏 350 个。
    • 叠加条件:_gridVersion 只在位移 > 1cm / 朝向 > 1° / 有效朝向 > 0.1° / Rows·Columns 变化时才自增 ⇒ 鼠标一停,版本号冻结 ⇒ UpdateGhost 开头的脏检查把没轮到的幽灵全部早退挡掉 ⇒ 它们永久停在主幽灵位置。
    • 按下种植时 GetGhostPosition(0, 0) 对它们全返回同一个 BasePosition, 而 PlayerPlacePiece.Postfix 只按 for (i = 0; i < MaxActiveGhosts; i++) 逐槽收集 (无坐标去重)⇒ 这几株全部种在同一点。
  • 修复:BuildGrid 之后不再依赖后续帧 —— 当场把 [1, MaxActiveGhosts] 全部补摆: GetGhostObject(slot) → GetGhostPosition(go, slot % Columns, slot / Columns, slot) → UpdateGhostTransform(go, pos) → GetGhostCache(go).lastUpdatedVersion = _gridVersion。
    • 方位分解 slot / Columns、slot % Columns 与原版 Update 里那两行 div/rem 完全一致 (已核对编译后 IL:两条指令确实都在)。
    • ✅ 只写坐标与缓存版本号,不调 UpdateGhostStatus ⇒ 不介入任何合法性判定。
    • ✅ 不推高 GhostUpdateBatchSize ⇒ 只在 BuildGrid 那一刻做一次全量, 之后每帧仍是原版分批节奏,不吃性能(这是本方案优于"抬批次"之处)。
  • 自证日志([General] VerboseLog = true 时):
    [补摆 3] 网格 20 格(MaxActiveGhosts=19, Columns=20)→ 已摆正 19 个幽灵、
             同步 19 个坐标缓存;累计补摆 3 次 / 57 个。
    
    已摆正 N 个 应等于 MaxActiveGhosts。
  • 反射句柄缺失时自动降级:新增的 8 个反射目标(GetGhostPosition / UpdateGhostTransform / GetGhostObject / GetGhostCache / lastUpdatedVersion / ExtraGhosts / MaxActiveGhosts / Columns)缺任何一个都只关闭补摆 (各打一条 补摆降级:… 警告),补丁 1 / 2 照常工作。

离线验证

  • verify_plantEasilyFix_1_2_0.py:DLL 自证 全部命中 PASS —— 三条机制的方法名与目标成员、补摆链路的 8 个反射目标、版本号与配置键, 并确认 RepositionAllGhosts 的 IL 里 div / rem 指令确实存在。
  • verify_reposition_logic.py:9 种网格尺寸(含 20×20)下 补摆后每个格位坐标唯一、方位分解与原版 div/rem 逐槽一致 ✅。
  • verify_reposition_order.py:枚举 BuildGrid / Update 的帧内先后顺序, 确认两重缺口都真实存在、且补摆对两种顺序都成立 ✅。

一处诊断更正(记录在案)

初版诊断把本机 1×20 归因于"分批轮转漏掉 30 个"——这是错的: 1×20 总格数只有 20,batch=50 一帧就能扫完全部,分批不构成缺口。 真正的缺口是上面的缺口一(帧内时序);分批轮转只在 10×20 及以上的大网格上 作为第二重缺口叠加进来。补摆方案对两者都覆盖,故结论与实现不变,仅归因更正。

1.1.2

仅文档 —— DLL 仍与 1.1.0 逐字节一致(10752 字节,sha256 c1700599807e4087…)。

  • 把「兼容性补丁」这一新定位推到线上:1.1.1 只带了适用性声明,正文仍按"修 bug"叙述; 本版把正文一并改掉。
  • README 正文重写:章节标题从「症状 / 根因 / 修复方式」改为中性的 「默认行为 / 实现位置 / 归位方式」;明确写出 「只装 BepInExPack_Valheim + PlantEasily 时该症状不复现」;撤掉全部归因措辞; 开头的概述改成"把几个模组共用的那两处运行时状态归位"。
  • manifest.json 描述改为 [AI-generated] Compatibility patch for Advize-PlantEasily: keeps grid planting and auto-replant correct when other mods operate on the same placement piece; auto-replant also grows back the crop you harvested. Read the applicability notice first.
  • 源码 src/PlantEasilyFixPlugin.cs 的类级 XML 注释同步为同一口径 (注释不进 IL,故仍不重新编译)。

1.1.1

仅文档 —— DLL 一字未改。

  • README 顶部新增中英双语「适用性声明 / Applicability Notice」:本补丁是在本机 (BepInEx 5.4.2350 / 游戏 1.0.15 / 30 个启用模组)那套环境下做出来的,所修症状 有可能是模组组合互相冲突造成的,并不一定是原模组本身的 bug,可能不适用于他人的环境; 如遇问题请先向原模组作者反馈。
  • manifest.json 的 description 末尾加一句 Read the applicability notice first.; README 表格里的「本补丁版本」同步更新为 1.1.1。
  • 正文定位整体改为「兼容性补丁」:确认「只装 BepInExPack + PlantEasily 时该症状不复现」之后, README 里原先按"原模组自身的 bug"叙述的措辞全部撤掉,统一表述为 "把几个模组共用的运行时状态归位",且不指名任何具体模组; manifest.json 的描述同步改为 Compatibility patch ...; 章节标题也从「修复方式 / 症状」改成中性的「归位方式 / 默认行为」。
  • 源码 src/PlantEasilyFixPlugin.cs 的类级 XML 注释同步改为同一口径 (注释不进 IL,所以不影响产物,也就没有重新编译)。
  • 补丁本体未重新编译:PlantEasilyFix.dll 10752 字节, sha256 c1700599807e408753f766a17868199ce1bf8632413947bad33db763478ce05b —— 与 1.1.0 完全一致,已装 1.1.0 的不需要换文件。

1.1.0

新增「按作物补种」修正 —— 自动补种不再统一成同一种作物。

  • 新增 Harmony postfix:Advize_PlantEasily.PlacementState.SetReferences → 把静态字段 LastSelectedPlantName 置回 null。
  • 效果:开启 [Harvesting]ReplantOnHarvest 后,每一种被收割的作物都补种回它自己, 而不是统一补成"你最近用耕耘机选中的那一种"。混种地块不再越收越单一。
  • 该字段只有两处读取(补种判定 + 悬停提示),且都有空值分支 —— 置空无副作用; 悬停提示 Replant as: X 也会同步显示"实际会补种的那种",可直接在游戏里核对。
  • 新增配置项:[Replant] SameCropReplant = true。关掉即恢复 PlantEasily 原行为, 改动无需重启游戏,下一次补种即时生效。
  • 新增模组图标 icon.png(两株高矮不同的作物 + 补丁对勾)。

1.0.0

  • 首个版本:修复 PlantEasily 网格种植(Rows/Columns ≥ 2)时,整片作物 (如 4×4 = 16 株)全部叠在同一个点上的 bug。
  • 实现:Harmony postfix Advize_PlantEasily.GhostGrid.BuildGrid → _gridVersion++, 让所有幽灵的坐标缓存失效,下一帧必定重算。
Thunderstore development is made possible with ads. Please consider making an exception to your adblock.