Botania 集成
BetterBuildersWands 通过 Botania 的 IBlockProvider 接口,让魔杖可以从
玩家背包里的花盆等方块提供者中抽取方块,而不必真的把方块放进背包。
⚠️ 源码中的类名和作者注释都叫它 “providers”,但它只对接 Botania (
vazkii.botania.api.item.IBlockProvider),并非通用提供者框架。
触发条件
BasicPlayerShim.areProvidersEnabled() 用 NoClassDefFoundError 探测 Botania 是否在类路径上:
private static boolean areProvidersEnabled() {
try {
boolean disable = new Object() instanceof IBlockProvider; // 触发类加载
return true;
} catch (NoClassDefFoundError e) {
return false;
}
}
未安装 Botania 时 providersEnabled = false,countItems / useItem
里的提供者分支直接跳过,模组其余功能不受影响。
计数阶段:扩大"可用方块"上限
BasicPlayerShim.countItems(itemStack, isNBTSensitive) 遍历 mainInventory,
除精确匹配外还累加所有 IBlockProvider 物品能提供的数量:
for (ItemStack inventoryStack : player.inventory.mainInventory) {
if (inventoryStack != null && itemStack.isItemEqual(inventoryStack)
&& (!isNBTSensitive || ItemStack.areItemStackTagsEqual(itemStack, inventoryStack))) {
total += Math.max(0, inventoryStack.stackSize);
} else if (providersEnabled && inventoryStack != null
&& inventoryStack.getItem() instanceof IBlockProvider) {
IBlockProvider prov = (IBlockProvider) inventoryStack.getItem();
int provCount = prov.getBlockCount(player, itemStack, inventoryStack, block, meta);
if (provCount == -1) return Integer.MAX_VALUE; // ← 无限量提供者
total += provCount;
}
}
| 行为 | 说明 |
|---|---|
返回 -1 |
视为无限供应,直接返回 Integer.MAX_VALUE |
| 返回具体数量 | 累加进总数 |
| 返回值单位 | 与 getBlockCount 一致(方块数),最终再 total / itemStack.stackSize 换算 |
因为上限是 min(wand.getMaxBlocks, countItems),一个"无限供应"的花盆会让
countItems 变成 Integer.MAX_VALUE,于是单次上限完全由魔杖等级决定。
消耗阶段:真正从花盆里挖方块
BasicPlayerShim.useItem() 的优先级顺序:
| 优先级 | 来源 | 说明 |
|---|---|---|
| 1 | Ztones ofanix 特判 |
目标是圆石且背包有 ofanix → 直接返回全额,不扣任何东西 |
| 2 | AE2 网络 | getAEItem(itemStack, needUse, true),Actionable.MODULATE 真扣 |
| 3 | 背包 | 按 slotPriority 顺序扣除,背包留到最后 |
| 4 | Botania 提供者 | 循环 prov.provideBlock(...) 直到够数或提供者耗尽 |
背包与 AE2 都不够时才轮到提供者:
if (!providers.isEmpty()) {
IBlockProvider prov = (IBlockProvider) providers.get(0).getItem();
for (ItemStack provStack : providers) {
final int available = prov.getBlockCount(player, itemStack, provStack, block, meta);
if (available != 0) {
while (prov.provideBlock(player, itemStack, provStack, block, meta, true)
&& needUse > toUse) { ++toUse; }
player.inventoryContainer.detectAndSendChanges();
}
}
}
源码注释说明了这一设计的原因: “IBlockProvider does not support removing more than one item in an atomic operation.” ——
provideBlock一次只能挖一个,所以只能循环调用。
slotPriority 在 BasicPlayerShim 构造时反向填表(注释 Inventory > Hotbar > Backhand),
确保先扣背包深处的物品、最后动快捷栏,减少玩家手感上的物品栏错乱。
与创造模式的区别
创造模式走 CreativePlayerShim,直接覆写两个方法,不经过 Botania 逻辑:
| 方法 | BasicPlayerShim |
CreativePlayerShim |
|---|---|---|
countItems |
遍历背包 + Botania + AE2 | 恒返回 Integer.MAX_VALUE |
useItem |
真扣 | 恒返回 itemStack.stackSize(不扣) |
创造模式玩家因此永远拿得到材料,且不消耗魔杖耐久
(WandWorker.placeBlocks 里的 if (!isCreative))。
相关条目
- AE2 Integration - 同一套
countItems/useItem的另一个数据源 - Backhand 集成 - 副手方块作为材料来源
- BFS 方块扩展算法 -
countItems如何决定搜索上限 - 魔杖等级系统 -
min(wand.getMaxBlocks, countItems)