You are viewing a potentially older version of this package. View all versions.
s6652289-StackmasterFix-1.0.0 icon

StackmasterFix

[AI-generated] Patch for Stackmaster: crafting/upgrading from nearby chests did not consume materials (item duplication). Redirects the withdrawal to the real container and forces the ZDO write. Client-only. Read the applicability notice first.

Date uploaded 4 days ago
Version 1.0.0
Download link s6652289-StackmasterFix-1.0.0.zip
Downloads 50
Dependency string s6652289-StackmasterFix-1.0.0

This mod requires the following mods to function

denikson-BepInExPack_Valheim-5.4.2350 icon
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.2350
JStack424-Stackmaster-1.1.5 icon
JStack424-Stackmaster

Turn a messy Viking inventory into a tidy, adventure-ready loadout.

Preferred version: 1.1.5

README

StackmasterFix

适用性声明 / Applicability Notice

中文:本模组是给第三方模组 JStack424-Stackmaster 打的客户端补丁,修的是它在 「用箱子里的材料合成/升级」时**材料不扣减(等于复制)**的问题。

  • 这个 bug 是在我这台装了 30+ 模组、且以客机身份连远程专用服务器的环境里复现并定位的; 原模组作者与这些问题无关,有疑问请先找我,不要去找原作者。
  • 原模组的其他玩法可能并没有这个 bug,症状也可能是我这台的特定组合导致; 不保证适用于你的环境
  • 本补丁只做两件事:把扣减从「只读快照副本」改到「真实容器」,并在事务成功后强制写回 ZDO。 不改任何数值、不加任何功能。
  • 若上游之后自己修好了,请优先升级上游模组并停用本补丁

English: This is a client-side patch for the third-party mod JStack424-Stackmaster, fixing its bug where crafting/upgrading with nearby-chest materials does not consume them (i.e. item duplication).

  • The bug was reproduced and diagnosed on my setup: a 30+ mod client connecting to a remote dedicated server. The original mod's author is not responsible for any of this.
  • The original mod may be perfectly fine in your setup; this patch is not guaranteed to apply to you.
  • The patch only (1) redirects the withdrawal from a read-only snapshot copy to the live container and (2) forces the container's ZDO write after a successful transaction. It changes no values and adds no features.
  • If upstream fixes it, update Stackmaster and disable this patch.

🤖 本模组的代码由 AI 生成(AI Generated),需求、验证与发布由人类完成。


它修的是什么

Stackmaster 为了「不拿所有权也能读箱子」,把容器的序列化库存从 ZDO 解进一个临时 Inventorynew Inventory(true)),命名为 ContainerHandle.ResourceInventory,作者注释自述 never claims ownership or mutates the live Container inventory。 但它的写入路径复用了同一个 capture,于是 ExecuteWithRollback 实际是拿 runtime.Inventory.RemoveItem(...) 扣在那个只读副本上

new Inventory(true)m_temoraryInventory == true,游戏里 Inventory.Changed() 第一句就是 ldfld m_temoraryInventory; brfalse; ret永不发 m_onChanged ⇒ 不触发 Container.OnContainerChanged从不调用 Container.Save() ⇒ ZDO 里材料一点没少。 同时模组用 before - after同一个副本上量差值,必然判定成功 ⇒ 玩家白拿成品。

它是怎么修的

transpiler 把以下两个写入方法里的成员访问整体重定向:

目标方法 替换内容
Stackmaster.NearbyResourceService::ExecuteWithRollback RuntimeResourceStack.get_Inventory ×4、get_Item ×3
Stackmaster.ExpeditionKitAction::ExecuteAtomic RuntimeResourceStack.get_Inventory ×4、get_Item ×5
  • get_Inventory → 真实容器的活库存(Container.GetInventory()
  • get_Item活库存里同一槽位的那个物品对象 (必须成对替换:Inventory.RemoveItem(ItemData,int) 内部有 m_inventory.Contains(item)引用包含检查, 只换 Inventory 不换 Item 会直接扣不动)
  • 事务成功后:对本次真的重定向过、且确实是本机 owner 的容器执行 Container.Save() + ZDOMan.ForceSendZDO,确保写回权威数据

安全取向:拿不到安全写入权(IsOwner() == false、容器已卸载、反射句柄缺失)时,返回一个空库存, 让模组自己的 ExecuteWithRollback 返回 false 并取消本次操作。 宁可取消,也不放行复制。

配置

BepInEx/config/s6652289.StackmasterFix.cfg

默认 说明
FixCraftingFromStorage true 修「合成/升级不扣料」(锻造台、工作台)
FixExpeditionKit true 修「建造菜单 Alt+左键拉取远征包」上的同一问题
Verbose true 打印每次重定向与强制写回的细节

怎么看它生效了

日志(BepInEx/LogOutput.log)里搜 [Fix]

[Fix] 已打补丁:NearbyResourceService.ExecuteWithRollback(transpiler + 写回 postfix)
[Fix] transpiler 生效:NearbyResourceService.ExecuteWithRollback → get_Inventory 替换 4 处,get_Item 替换 3 处(共 167 条指令)。
[Fix] 扣减重定向 → 真实容器 $piece_chest uid=1:116561 活库存总数=500
[Fix] 已强制写回 ZDO: $piece_chest uid=1:116561 库存=497 zdoRev=182

关键判据:合成后用箱子材料,箱子真的少了 = 修好了。 若看到 [Fix] ★transpiler 未匹配到任何目标指令 ⇒ Stackmaster 版本变了,本补丁失效,请反馈。

安装 / 卸载

  • 安装:解压把 StackmasterFix.dll 放到 BepInEx/plugins/StackmasterFix/
  • 卸载:删掉该目录
  • 依赖:denikson-BepInExPack_Valheim(不需要 Jotunn、不联网、纯客户端)

源码

D:\WorkBuddy\StackmasterFix\(本地工作区)

已知边界

  • 本文只验证到「材料确实从真实容器扣掉、并写回 ZDO」。联机双端一致性、专用服务器长时间运行、 容器被他人同时打开等边界场景未做穷尽测试。
  • 上游若改变 RuntimeResourceStack / ExecuteWithRollback 的结构,本补丁会打印 transpiler 未匹配 并自然失效(不会造成新的破坏)。

CHANGELOG

Changelog

1.1.0

剔除冗余功能:上游 Stackmaster 1.1.5 已自行修好「合成/升级不扣料」,本补丁不再介入那一半。

  • 依据(只读上游源码 + 官方包 metadata 核对)
    • 1.1.5 changelog 首条声明该修复,并写明已在游戏内验证 (prepared withdrawals are recaptured from each selected owned Container.GetInventory() rather than from detached ZDO inventory snapshots …)。
    • NearbyResources.cs:1288Container.GetInventory() 活库存,:1294-1313 用它重建 mutableHandleruntimeStacks执行期对象就是活库存:1673-1681 另有 ReferenceEquals(runtime.Inventory, runtime.Container.Container.GetInventory()) 的 fail-closed 身份校验(注释直指 "grants a free crafted item"),并配 ExactResourceDebit 精确账本与逆序回滚。
    • ⇒ 本补丁针对 ExecuteWithRollback 的 transpiler 已退化为恒等替换,属纯冗余。
  • 移除ExecuteWithRollback 的 transpiler + 写回 postfix 分支、FixCraftingFromStorage 配置项、以及 NearbyResourceService 的类型查找。 DLL 内已确认 FixCraftingFromStorageExecuteWithRollback 零引用
  • 保留ExpeditionKitAction.ExecuteAtomic(远征包)。1.1.5 没有修这条路径 —— 它仍走 CaptureForExpedition → Discover(…, resourceReadOnly: true) 的 detached ZDO 快照, 而 crafting 专用的 TryCaptureOwnedPlan/mutableCapture 从未被它调用。故这一半原样保留。
  • 依赖JStack424-Stackmaster 1.1.4 → 1.1.5(不升会让 r2modman 要求把已升的上游降回去)。
  • 日志改进:transpiler 不再拿「指令总数」当方法指纹(上游一改 IL 就会退化成 未知方法(指令数 N),纯误导),改用 Harmony 注入的 __originalMethod 直接取方法名。
  • 阈值变化FixExpeditionKit=false 时补丁不再尝试挂载,直接打日志说明并退出(等同于未安装)。

DLL 重新编译:16,896 字节,sha256 da4e8133d5e5b8ef991cc6f366f8e64ed31022d1d02bd27b0f497c8af1f5342e

1.0.0

  • 首个版本。修复上游 JStack424-Stackmaster 1.1.4 的「用箱子材料合成/升级不扣料(复制)」问题。
  • 根因:Stackmaster 为了「不拿所有权也能读箱子」,把容器的序列化库存从 ZDO 解进一个 临时 Inventorynew Inventory(true)ContainerHandle.ResourceInventory), 但写入路径复用了同一个 capture ⇒ ExecuteWithRollback 实际是扣在只读副本上。 临时 Inventory 永不触发 Inventory.Changed() ⇒ 不会调用 Container.Save() ⇒ ZDO 里材料一点没少,而模组的自检在同一个副本上量差值必然判定成功 ⇒ 玩家白拿成品。
  • 修法:transpiler 把写入方法里的 RuntimeResourceStack.get_Inventory / get_Item 整体重定向到真实容器的活库存与活物品对象;事务成功后强制 Container.Save() + ZDOMan.ForceSendZDO。拿不到安全写入权时 fail-closed(取消操作,不放行复制)。
  • 覆盖:NearbyResourceService.ExecuteWithRollback(合成/升级/建造)与 ExpeditionKitAction.ExecuteAtomic(建造菜单 Alt+左键拉取远征包)。
  • 实测(2026-09-19):修复后逐笔扣减量与配方需求精确吻合 (建造 20/10/2 = 32、合成 −3、另一组 4/1 = 5),且合成事务内首次出现 Container.OnContainerChangedContainer.Save(修复前为 0 条)。
  • 配置项:FixCraftingFromStorageFixExpeditionKitVerbose(均默认开)。