容器兼容(InventoryUtility 注册)
[!INFO] Git Commit:
93da3e1| Updated: 2026-10-01
本 mod 的第一大职责:把其它 mod 的容器注册进 StructureLib 的
com.gtnewhorizon.structurelib.util.InventoryUtility,使多方块自动放置 / 生存建造时能从这些
容器里自动取料,而不必把物品塞进玩家背包或箱子。
两个扩展点
InventoryUtility 提供两类注册入口:
registerInventoryProvider(String, InventoryProvider)— 按玩家取容器。StructureLib 在需要取料 时回调,返回InventoryIterable<IInventory>(Iterable+ 一个getInventory()用于标脏)。适用于 "玩家身上/身上某件装备里"的东西。registerStackExtractor(String, ItemStackExtractor)— 按物品取容器。适用于"右键某个背包物品 就能展开成容器"的东西。详见 AE2 物品提取器。
7 条注册记录
标识符的数字前缀决定注册顺序(StructureLib 按该字符串排序,数字小的先注册)。
注意两个 0999-*-need-before-ae2 的命名明确要求排在 1000-ae2-* 之前。
| 注册标识符 | 类型 | 目标内容 | 源码 |
|---|---|---|---|
0999-ae2wct-need-before-ae2 |
stackExtractor | WCT 无线合成终端 | CompatWCT.java:18 |
0999-ae2fc-need-before-ae2 |
stackExtractor | AE2FC 无线终端 | CompatAE2FC.java:25 |
1000-ae2-wireless |
stackExtractor | AE2 原版无线终端 | CompatAppliedEnergistics.java:24 |
1000-ae2-portable-cell |
stackExtractor | AE2 便携式存储单元 | CompatAppliedEnergistics.java:31 |
1000-forestry-backpack |
stackExtractor | 林业背包物品 | CompatForestry.java:15 |
1000-adventure-backpack |
stackExtractor | 探险背包物品 | CompatAdventureBackpack.java:16 |
5001-baubles |
inventoryProvider | 饰品栏 | CompatBaubles.java:15 |
6000-adventure-backpack |
inventoryProvider | 穿戴中的探险背包 | CompatAdventureBackpack.java:12 |
5001 与 6000 是仅有的两个 provider,编号远高于提取器,符合"provider 在提取器之后补充"的排序意图。
饰品栏(CompatBaubles)
InventoryUtility.registerInventoryProvider("5001-baubles",
player -> new InventoryIterable<>(BaublesApi.getBaubles(player)));
markDirty 直接透传给 inv.getInventory().markDirty()(CompatBaubles.java:25-27)——注意这是整个
饰品栏容器一次性标脏,而非按被取用的槽位精确标脏。
林业背包(CompatForestry)——ItemInventoryBackpack 的空 parent 补丁
林业自己的 ItemInventoryBackpack 在构造时需要一个 parent ItemStack,但从物品直接展开时没有 parent。
本 mod 用一个私有子类绕过:
private static class MyItemInventoryBackpack extends ItemInventoryBackpack {
protected ItemStack getParent() {
// this might be called during <init>. give it some dummy itemstack to not NPE
return source == null ? new ItemStack(Items.feather) : source;
}
}
CompatForestry.java:23-37。构造时先把 source 存为 null 传入 super(null, 背包尺寸, source),靠
getParent() 的空值分支返回 Items.feather(羽毛)避免 <init> 期间 NPE,随后在构造完成后回填真实
source。源码注释自称 hacky wacky, but works, for now.。背包容量取自
((ItemBackpack) source.getItem()).getBackpackSize(),随林业背包类型变化,不是固定值。
探险背包(CompatAdventureBackpack)
同一个 mod 同时注册了 provider 与 extractor(CompatAdventureBackpack.java:12-20):
- provider:仅当
Wearing.isWearingBackpack(player)为真时返回new InventoryBackpack(Wearing.getWearingBackpack(player)),否则返回null; - extractor:对
ItemAdventureBackpack物品返回new InventoryBackpack(source)。
末影背包门控(CompatBackpackMod)
InventoryUtility.registerEnableEnderCondition(
player -> Arrays.stream(player.inventory.mainInventory)
.anyMatch(stack -> stack != null && stack.getItem() instanceof ItemBackpackBase
&& BackpackUtil.isEnderBackpack(stack)));
CompatBackpackMod.java:17-20。这是唯一不走 provider/extractor 的注册,作用是限制而非提供。
源码注释解释了原因:逐个读/写每一个背包都要多次磁盘 IO,自动访问会导致严重卡顿,因此只对末影背包
开放——因为末影背包是存在玩家数据 NBT 里的,访问不涉及磁盘。
事件拦截:InventoryBogoSorter
CompatBogoSorter 是唯一不调用 InventoryUtility 的 compat,改为注册一个 Forge 事件处理器
(CompatBogoSorter.java:19):
@SubscribeEvent @SideOnly(Side.CLIENT)
public void onBeforeSortInGui(BeforeSortEvent event) {
if (!event.isFromKeybind()) return;
if (event.getSortKeyCode() != MOUSE_MIDDLE) return; // MOUSE_MIDDLE = -98
ItemStack item = event.isInGui() ? event.getPlayer().inventory.getItemStack()
: event.getPlayer().getHeldItem();
if (item == null || !(item.getItem() instanceof ItemConstructableTrigger)) return;
event.setCanceled(true);
}
作用是阻止误触:Bogo Sorter 默认绑中键整理背包,而中键同样是
StructureLib 构造触发器 的取通道热键。玩家手持/光标
悬停在构造触发器上时按下中键,会被取消以避免物品被整理掉。该处理只在客户端生效(@SideOnly(Side.CLIENT)),
且只在事件来自按键绑定时生效。
判定用的是 StructureLib 的 ItemConstructableTrigger(即 mod 依赖的具体原因之一),MOUSE_MIDDLE
是硬编码常量 -98(CompatBogoSorter.java:16),对应 1.7.10 中 LWJGL 之外的 Forge 键码约定。