跳到正文
烈焰甘蔗Wiki

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))。

相关条目

浏览全部模组