AE2 物品提取器
[!INFO] Git Commit:
93da3e1| Updated: 2026-10-01
MEInventoryStackExtractor(86 行)与 WirelessTerminalStackExtractor(246 行)是本 mod 最复杂的
两个基类,负责从 AE2 无线终端 / 便携单元 这类"没有真实容器、只有网格访问权"的物品里取料。
三个 AE 系 compat(CompatAppliedEnergistics、CompatWCT、CompatAE2FC)都只是这两个基类的匿名子类。
继承链
InventoryUtility.ItemStackExtractor (StructureLib 接口)
└── MEInventoryStackExtractor 抽象:AE2 存储 + 能量预算
└── WirelessTerminalStackExtractor 抽象:无线终端的网格定位 + 距离校验
├── CompatWCT (IWirelessCraftingTerminalItem)
└── CompatAE2FC (ItemBaseWirelessTerminal)
CompatAppliedEnergistics 的两个注册不走无线分支:1000-ae2-wireless 用
WirelessTerminalStackExtractor,1000-ae2-portable-cell 直接用 MEInventoryStackExtractor。
MEInventoryStackExtractor
能量预算钳制
取料前先用模拟模式算出电力最多支持多少个:
count = Math.min(count, (int) energy.extractAEPower(count, Actionable.SIMULATE, PowerMultiplier.ONE));
MEInventoryStackExtractor.java:57。注释说明意图是"把取料数量限制在电力上限内,这样后面就不必反复检查
电力限制"。PowerMultiplier.ONE 表示按 1:1 换算,AE2 中 1 AE = 1 RF。
逐项提取
遍历 getAvailableItems() 的结果,对每个匹配的 ItemStack 计算剩余额度并尝试提取
(MEInventoryStackExtractor.java:59-69),累计到 found >= count 就提前返回。模拟模式走
inv.extractItems(..., Actionable.SIMULATE, ...),实际模式走
AEApi.instance().storage().poweredExtraction(source, inv, toExtract, actionSource)
(poweredExtraction 会同时扣除电力并取出物品)。
声明支持的 API 类型
public boolean isAPIImplemented(APIType type) {
return type == APIType.EXTRACT_ONE_STACK || type == APIType.MAIN;
}
MEInventoryStackExtractor.java:40-42。EXTRACT_ONE_STACK(取指定物品)由 getItem 实现;
MAIN 由 takeFromStack 实现。其余 APIType 一律返回 false,StructureLib 遇到不支持的类型会跳过该来源。
抽象方法 fromItem
protected abstract Pair<IEnergySource, IMEInventoryHandler<IAEItemStack>> fromItem(ItemStack source, EntityPlayerMP player);
返回左能量源、右存储处理器。返回 null 表示"这个物品不适用",takeFromStack / getItem 都会直接返回 0。
便携单元的能量源是本 mod 自造的
1000-ae2-portable-cell 的 fromItem 里内联了一个 lambda 实现的 IEnergySource
(CompatAppliedEnergistics.java:39-45),并附有吐槽注释:
// copied from appeng.items.contents.PortableCellViewer.extractAEPower
// seriously why this isn't part of ae itself?
行为是:按 usePowerMultiplier 调整额度,模拟模式返回 usePowerMultiplier.divide(min(amt, 当前电量)),
实际模式返回 usePowerMultiplier.divide(item.extractAEPower(source, amt))。注意模拟分支返回的是
比值而非绝对量,这是从 AE2 原样抄来的行为(注释已声明来源),并非本 mod 引入。
WirelessTerminalStackExtractor
fromItem 的解析链条较长,任何一步失败都返回 null(:51-87):
AEApi.instance().registries().wireless().isWirelessTerminal(source)必须为真;- 取
IWirelessTermHandler,为null则放弃; wh.getEncryptionKey(source)解析为long(NumberFormatException捕获后放弃);getLocatableBy(serial)得到的对象必须是IGridHost;- 取
IGridNode(ForgeDirection.UNKNOWN)→IGrid,两者任一为null则放弃; grid.getCache(IStorageGrid.class)→sg.getItemInventory()非空;rangeCheck(source, player, grid)必须通过;- 用
new WirelessTerminalEnergySource(wh, source, player)包装能量源; - 用
getViewCellStorage(source)取出视图单元,构建IPartitionList过滤表。
若过滤表为 null(第 8 步抽象方法返回 null),直接 Pair.of(power, rawInventory) 暴露整个网格;
否则包装成 ExtractionFilteredMEInventoryHandler。
距离校验
for (final IGridNode n : targetGrid.getMachines(TileWireless.class)) {
final IWirelessAccessPoint wap = (IWirelessAccessPoint) n.getMachine();
DimensionalCoord loc = wap.getLocation();
if (!loc.isInWorld(player.getEntityWorld())) continue;
double range = wap.getRange();
double distX = ..., distY = ..., distZ = ...;
if (dist < range * range) return true;
}
WirelessTerminalStackExtractor.java:89-102。遍历目标网格上所有 TileWireless 访问点,任意一个在玩家
所在维度内且平方距离小于 range² 即通过。注意 loc.isInWorld(player.getEntityWorld()) 这一句:
跨维度的访问点被直接跳过,因此玩家在别的维度时即使离得近也不算数。
两个子类各自覆盖了 rangeCheck 来放行各自的"增强版":
CompatWCT.java:26-29—((IWirelessCraftingTerminalItem) source.getItem()).checkForBooster(source)为真则跳过距离校验(WCT 增幅器);CompatAE2FC.java:40-42—hasInfinityBoosterCard(source)为真则跳过(AE2FC 无限增幅卡)。
WirelessTerminalEnergySource
WirelessTerminalStackExtractor.java:106-128。内部类,实现 IEnergySource.extractAEPower:
模拟模式返回 wth.hasPower(player, amt, is) ? amt : 0,实际模式返回 wth.usePower(...) ? amt : 0。
若 wth 或 is 为 null 直接返回 0.0。这让无线终端的耗电走 AE2 自己的权限与增幅器判定,
而不是本 mod 自己算距离。
ExtractionFilteredMEInventoryHandler
WirelessTerminalStackExtractor.java:130-245,一个 115 行的 IMEInventoryHandler 装饰器,作用是
把视图单元的过滤表套在网格取料上:
extractItems:if (!filter.isListed(request)) return null;——未列在视图单元里的物品一律不取(:179);getAvailableItems:返回一个匿名IItemList包装,addStorage/addCrafting/addRequestable三个写入方法全部先过filter.isListed,其余方法(getFirstItem、size、iterator、findPrecise、findFuzzy等)直接委托给内部列表(:184-239);injectItems:不加过滤,直接透传(:172-175,注释// inject is not filtered)——本 mod 只管取料, 不干涉存入;- 其余
getAccess、isPrioritized、canAccept、getPriority、getSlot、validForPass、getChannel全部透传。
三个子类的 getViewCellStorage 实现各不相同:
| compat | 返回的视图单元存储 |
|---|---|
CompatAppliedEnergistics |
() -> new WirelessTerminalViewCells(is) |
CompatWCT |
() -> new WCTInventoryViewCell(is) |
CompatAE2FC |
() -> new WirelessTerminalViewCells(is) |