多块系统

多块系统

为构建多方块结构提供基础。

开发者请注意,请先运行命令 /mbs display true。

MultiBlockSystem

MultiBlockSystem 是一个为模组作者和整合包制作者设计的强大框架,用于轻松创建、验证和分发复杂的自定义多方块结构。通过提供精简的工具集,本模组将搭建庞大而复杂机械的过程转变为安全且高度优化的体验。

✨ 特性

灵活的模式设计:利用简单、直观的基于字符串的系统来定义多方块模式,支持从基础机械到巨大多层结构的一切。

高级结构验证:系统提供强大的视觉反馈,允许建造者切换验证显示,以确保每个方块都精确放置在需要的位置。

高性能扫描:通过智能的、基于后台的异步扫描,模组能够捕捉庞大的结构设计,即使是最复杂的构建,也不会影响服务器性能。

安全分发:内置的混淆和压缩功能确保您的自定义结构定义紧凑且安全,非常适合整合包开发者包含独特、专有的内容。

适合模组作者的命令套件:全面的命令行界面允许快速导出、重新加载和配置管理,而无需重启游戏。

1. 从其他模组注册的方法

build.gradle(软依赖)

dependencies {
    // 使用 compileOnly 表示软依赖(游戏可以在没有此模组的情况下启动)
    compileOnly fg.deobf("com.multiblocksystem:multiblocksystem:1.0.0:api")
}

mods.toml

[[dependencies.yourmod]]
    modId="multiblocksystem"
    mandatory=false          # 软依赖 = 没有此模组也能启动
    versionRange="[1.0,)"
    ordering="BEFORE"
    side="BOTH"

注册代码

// YourMod.java
@Mod("yourmod")
public class YourMod {
    public YourMod() {
        FMLJavaModLoadingContext.get().getModEventBus()
            .addListener(this::onCommonSetup);
    }

    private void onCommonSetup(FMLCommonSetupEvent event) {
        event.enqueueWork(() -> {
            if (!ModList.get().isLoaded("multiblocksystem")) return;
            YourMultiBlockSetup.register();
        });
    }
}

// YourMultiBlockSetup.java
public class YourMultiBlockSetup {
    public static void register() {

        // ===== 1. 注册控制器方块 =====
        MultiBlockRegistry.registerAsController(
            new ResourceLocation("yourmod", "blast_furnace_controller"),
            new BlastFurnaceControllerBehavior()
        );

        // ===== 2. 将您的方块注册为 INPUT =====
        // 如果拥有 IItemHandler Capability,一行代码即可
        MultiBlockRegistry.registerAsLinkable(
            new ResourceLocation("yourmod", "input_hopper"),
            LinkRole.INPUT,
            LinkableBehavior.fromForgeCapability()
        );

        // ===== 3. 将原版箱子注册为 INPUT =====
        // (原版方块也可以注册)
        MultiBlockRegistry.registerAsLinkable(
            Blocks.CHEST,
            LinkRole.INPUT,
            LinkableBehavior.fromForgeCapability()
        );

        // ===== 4. 自定义存储则需要自行实现 =====
        MultiBlockRegistry.registerAsLinkable(
            new ResourceLocation("yourmod", "big_tank"),
            LinkRole.FUEL,
            new LinkableBehavior() {
                @Override
                public IFluidStorage getFluidStorage(Level level, BlockPos pos) {
                    BlockEntity be = level.getBlockEntity(pos);
                    if (be instanceof BigTankBlockEntity tank) {
                        return tank.getFluidStorage();
                    }
                    return null;
                }
                @Override
                public boolean hasFluidStorage() { return true; }
                @Override
                public boolean hasItemStorage()  { return false; }
            }
        );

        // ===== 5. 注册结构体形状 =====
        MultiBlockRegistry.registerStructure(
            new ResourceLocation("yourmod", "blast_furnace"),
            StructureDefinition.fromPattern(
                List.of(
                    new String[]{"FFF", "FCF", "FFF"},  // Y=0
                    new String[]{"   ", " . ", "   "}   // Y=1
                ),
                Map.of(
                    'F', Blocks.BLAST_FURNACE.defaultBlockState(),
                    'C', YourBlocks.BLAST_FURNACE_CONTROLLER.get().defaultBlockState()
                ),
                'C'
            ),
            new CapabilitySpec()
                .require(LinkRole.INPUT,  1)
                .require(LinkRole.OUTPUT, 1)
        );

        // ===== 6. 注册端口方块(支持4层检测) =====
        // 第1层:直接在方块类上实现 IPortBlock 接口的方法
        // 第2层:通过 API 注册的方法(如下所示)
        // 第3层:如果有 Forge Capability 则自动适配(IItemHandler/IEnergyStorage/IFluidHandler)
        // 第4层:通过数据包的方块标签注册(data/multiblocksystem/tags/blocks/multiblock_ports.json)
        MultiBlockRegistry.registerPort(
            new ResourceLocation("yourmod", "item_input_port"),
            PortRegistry.get(new ResourceLocation("multiblocksystem", "item_input"))
        );
        MultiBlockRegistry.registerPort(
            new ResourceLocation("yourmod", "energy_output_port"),
            PortRegistry.get(new ResourceLocation("multiblocksystem", "energy_output"))
        );
    }
}

2. ControllerBehavior 实现示例

public class BlastFurnaceControllerBehavior implements ControllerBehavior {

    @Override
    public ResourceLocation getDefaultStructureId() {
        return new ResourceLocation("yourmod", "blast_furnace");
    }

    @Override
    public void onFormed(Level level, BlockPos pos, IMultiBlockNetwork network) {
        BlockEntity be = level.getBlockEntity(pos);
        if (be instanceof BlastFurnaceControllerBE ctrl) {
            ctrl.setFormed(true);
        }
    }

    @Override
    public void onBroken(Level level, BlockPos pos, IMultiBlockNetwork network) {
        BlockEntity be = level.getBlockEntity(pos);
        if (be instanceof BlastFurnaceControllerBE ctrl) {
            ctrl.setFormed(false);
        }
    }

    @Override
    public void tick(Level level, BlockPos pos, IMultiBlockNetwork network) {
        IItemStorage input  = network.getItemStorage(LinkRole.INPUT);
        IItemStorage output = network.getItemStorage(LinkRole.OUTPUT);

        Amount inputCost   = Amount.of(3);
        Amount outputYield = Amount.of(5);

        boolean canRun = network.canProcess(
            List.of(IBigItemStack.of(Items.IRON_ORE, inputCost)),
            List.of(IBigItemStack.of(Items.IRON_INGOT, outputYield))
        );
        if (!canRun) return;

        // 消耗 1/3 FE/tick 的配方
        Amount energyCost = Amount.of(1, 3);
        Amount consumed = network.consumeEnergy(energyCost, true);
        if (!consumed.isGreaterThanOrEqual(energyCost)) return;

        network.consumeEnergy(energyCost, false);
        network.processTransfer(
            List.of(IBigItemStack.of(Items.IRON_ORE,    inputCost)),
            List.of(IBigItemStack.of(Items.IRON_INGOT,  outputYield))
        );
    }
}

3. Amount 计算的具体示例

Amount 是一个封闭接口(sealed interface),其实现仅限于两个 record 类:

实现 内部表示 用途
LongAmount long 分子 / 分母 一般值(高速)
BigAmount BigInteger 分子 / 分母 超过 long 的超大值

工厂方法(Amount.of(...))会自动选择最优实现,并且当计算结果在 long 范围内时,BigAmount 会被降级为 LongAmount。

// 整数
Amount a = Amount.of(100);           // → LongAmount(100, 1)
Amount b = Amount.of(3);             // → LongAmount(3, 1)

// 分数
Amount oneThird  = Amount.of(1, 3);  // → LongAmount(1, 3)
Amount twoThirds = Amount.of(2, 3);  // → LongAmount(2, 3)
Amount half      = Amount.of(1, 2);  // → LongAmount(1, 2)

// 四则运算(均为返回新 Amount 的不可变操作)
Amount sum      = oneThird.add(twoThirds);       // 1/3 + 2/3 = 1 → LongAmount(1, 1)
Amount diff     = a.subtract(b);                 // 100 - 3 = 97
Amount product  = oneThird.multiply(3);          // 1/3 * 3 = 1(零误差)
Amount quotient = a.divide(b);                   // 100 / 3 = 100/3

// 比较
boolean gt  = a.isGreaterThan(b);               // true
boolean gte = oneThird.isGreaterThanOrEqual(half); // false
boolean eq  = product.equals(Amount.ONE);        // true

// 转换
BigInteger val1 = product.floor();              // BigInteger(1)
BigInteger val2 = quotient.ceil();              // BigInteger(34)
double val3 = oneThird.toDouble();              // 0.33333...(用于显示)

// 显示
System.out.println(oneThird.toDisplayString());  // "1/3  (≈0.333333)"
System.out.println(product.toDisplayString());   // "1"
System.out.println(quotient.toDisplayString());  // "100/3  (≈33.333333)"

// 字符串序列化(用于 JSON 保存)
String json = oneThird.toSerialString();         // "1/3"
Amount restored = Amount.fromSerialString(json); // → LongAmount(1, 3)

// NBT 序列化
CompoundTag tag = oneThird.toNbt();
Amount fromTag = Amount.fromNbt(tag);

// 能量效率计算(零误差)
Amount efficiency = Amount.of(2, 3);             // 66.666...%
Amount baseCost   = Amount.of(100);
Amount actualCost = baseCost.multiply(efficiency); // 200/3 FE
long floorCost    = actualCost.floor().longValueExact(); // 66(仅写入 Minecraft 时使用)

LongAmount 的自动升级

LongAmount 使用 Math.multiplyExact / Math.addExact 进行溢出检测,当结果超出 long 范围时,会自动升级为 BigAmount:

Amount huge = Amount.of(Long.MAX_VALUE);
Amount one  = Amount.of(1);
Amount result = huge.add(one);  // → BigAmount (超出 long 范围,自动升级)

BigAmount 的自动降级

BigAmount 在计算结果分子和分母都在 long 范围内时,会自动降级为 LongAmount:

Amount big = Amount.of(BigInteger.valueOf(99999999999L), BigInteger.valueOf(3));
Amount small = Amount.of(3);
Amount result = big.multiply(small);  // → LongAmount (结果在 long 范围内)

4. /mbs 命令参考

MultiBlockSystem 提供以下服务器命令:

/mbs export

扫描指定范围的方块,生成多方块定义文件。

/mbs export <pos1> <pos2> <core> <filename> [ex1_pos1] [ex1_pos2] ... [exN_pos1] [exN_pos2]
参数 类型 必需 说明
pos1 BlockPos 是 扫描范围的角坐标(支持相对坐标 ~ ~ ~)
pos2 BlockPos 是 扫描范围的另一个角坐标
core BlockPos 是 控制器方块的坐标(相对坐标的原点)
filename String 是 输出文件名(无需 .json 扩展名)
exN_pos1 BlockPos 否 排除区域 N 的角坐标(支持 Tab 补全)
exN_pos2 BlockPos 否 排除区域 N 的另一个角坐标

排除区域:由每个 ex_pos1 / ex_pos2 对定义的 AABB(轴对齐边界框)内的方块将不被扫描。最多可指定 200 个排除区域(由于 Forge 命令树递归深度限制)。

# 基本示例
/mbs export ~ ~ ~ ~10 ~10 ~10 ~ ~5 my_structure

# 带排除区域的示例(排除第150层到第300层)
/mbs export 0 0 0 100 300 100 ~ ~150 my_tower 0 150 0 100 300 100

/mbs reload

重新加载磁盘上的多方块定义文件。

/mbs reload

将重新扫描 multiblocks/ 目录中的所有 .json 文件,并更新结构体定义。控制器和链接方块的注册不会被清除(因为它们只在 FMLCommonSetupEvent 中注册一次)。

/mbs display

切换结构正确性显示的标记。

/mbs display [enabled]
参数 类型 必需 说明
enabled boolean 否 true 开启显示,false 关闭显示

无参数执行时显示当前状态。


5. BigInteger 存储支持

IBigIntegerItemHandler

IBigIntegerItemHandler 是将 Forge 的 IItemHandler 扩展到 BigInteger 规模的鸭子接口(直接继承自 IItemHandler)。

方法 说明
getSlotLimitBig(int slot) 以 BigInteger 返回槽位的最大容量
insertItemBig(int slot, ItemStack, BigInteger, boolean) 以 BigInteger 规模插入物品
extractItemBig(int slot, Item, BigInteger, boolean) 以 BigInteger 规模提取物品
getTotalCountBig(Item) 以 BigInteger 返回指定物品在所有槽位的总持有量

所有方法都包含默认实现,即使对于未实现的 IItemHandler,也能保证基于 long 的回退行为。

MixinItemStackHandler

为 Forge 的 ItemStackHandler(具体类)自动附加 IBigIntegerItemHandler 的 Mixin。

// multiblocksystem.mixins.json
{
  "mixins": ["MixinItemStackHandler"]
}

由于 ItemStackHandler 已经实现了 IItemHandler,IBigIntegerItemHandler 的默认方法会直接调用 getSlots()、insertItem()、getSlotLimit() 等来提供 BigInteger 操作。这是一个仅用于标记的 Mixin,不包含额外代码。

检测方法:

IItemHandler handler = ...;
if (handler instanceof IBigIntegerItemHandler bigHandler) {
    // 使用 BigInteger 规模的操作
    BigInteger limit = bigHandler.getSlotLimitBig(slot);
} else {
    // 常规 IItemHandler — ForgeItemHandlerAdapter 回退到 long 基础
}

ForgeItemHandlerAdapter 的双重路径

ForgeItemHandlerAdapter 是 IItemHandler → IItemStorage 的桥接器,通过 instanceof IBigIntegerItemHandler 检查执行双重路径处理:

  • 支持 BigInteger 时:直接调用 IBigIntegerItemHandler 的方法
  • 不支持时:回退到基于 long 的聚合

Forge 标准的 ItemStackHandler 通过 Mixin 自动支持 BigInteger。拥有自定 IItemHandler 实现的模组可以手动实现 IBigIntegerItemHandler 以启用 BigInteger 操作。


6. KubeJS 集成

MultiBlockSystem 作为可选的软依赖集成了对 KubeJS 的支持。即使在不存在 KubeJS 的环境中,模组也能正常运行。

6.1 插件注册

KubeJS 会从 src/main/resources/kubejs.plugins.txt 自动检测插件:

// src/main/resources/kubejs.plugins.txt
com.multiblocksystem.kubejs.MultiBlockKubeJSPlugin multiblocksystem

6.2 KubeJSPlugin(Java 侧)

package com.multiblocksystem.kubejs;

import dev.latvian.mods.kubejs.KubeJSPlugin;
import dev.latvian.mods.kubejs.script.BindingsEvent;
import dev.latvian.mods.kubejs.script.ScriptType;
import com.multiblocksystem.api.behavior.LinkRole;
import com.multiblocksystem.CapabilitySpec;

public class MultiBlockKubeJSPlugin extends KubeJSPlugin {

    @Override
    public void registerBindings(BindingsEvent event) {
        if (event.getType() != ScriptType.STARTUP) return;

        event.add("MultiBlockRegistry", new MultiBlockRegistryJS());
        event.add("LinkRole",           LinkRole.class);
        event.add("CapabilitySpec",     CapabilitySpec.class);
    }
}

注意: 方法名是 registerBindings 而非 addBindings。它定义在 KubeJS 2001.x (Forge 1.20.1) 的 KubeJSPlugin 基类中。

6.3 注册适配器(MultiBlockRegistryJS)

从 KubeJS 脚本调用的包装类。负责 String → ResourceLocation 转换、JS 对象 → BlockState 转换。

方法 说明
registerController(String, ControllerBehavior) 通过方块 ID 注册控制器
registerController(Block, ControllerBehavior) 通过 Block 对象注册控制器
registerLinkable(String, LinkRole) 通过方块 ID 注册链接方块(自动检测 Forge IItemHandler)
registerLinkable(String, LinkRole, LinkableBehavior) 通过方块 ID 注册链接方块(自定义行为)
registerLinkable(Block, LinkRole) 通过 Block 对象注册链接方块
registerPort(String, String) 通过方块 ID 和端口 TypeID 注册端口方块
registerStructure(String, List<String[]>, Map, String) 通过 KubeJS 风格的字符串模式注册结构体
registerStructure(String, List<String[]>, Map, String, CapabilitySpec) 注册结构体(带规格)
isController(String) 检查指定方块是否已注册为控制器
isLinkable(String) 检查指定方块是否已注册为链接方块
isPort(String) 检查指定方块是否已注册为端口

6.4 KubeJS 脚本示例(startup_scripts/my_multiblocks.js)

// KubeJS 在 MultiBlockSystem 加载后执行脚本,因此可以直接从全局使用 MultiBlockRegistry,而无需等待 StartupEvents.postInit。

// ===== 注册控制器方块 =====
MultiBlockRegistry.registerController(
    "mymod:blast_furnace_controller",
    {
        getDefaultStructureId: () => "mymod:blast_furnace",

        onFormed: (level, pos, network) => {
            console.log("Blast furnace formed at " + pos);
        },

        tick: (level, pos, network) => {
            const input  = network.getItemStorage(LinkRole.INPUT);
            const output = network.getItemStorage(LinkRole.OUTPUT);

            const cost  = Amount.of(3);
            const yield = Amount.of(5, 2);

            const canRun = network.canProcess(
                [{ item: "minecraft:iron_ore",    count: cost  }],
                [{ item: "minecraft:iron_ingot",  count: yield }]
            );
            if (!canRun) return;

            network.processTransfer(
                [{ item: "minecraft:iron_ore",    count: cost  }],
                [{ item: "minecraft:iron_ingot",  count: yield }]
            );
        }
    }
);

// ===== 将原版方块注册为链接方块 =====
MultiBlockRegistry.registerLinkable("minecraft:chest",  LinkRole.INPUT);
MultiBlockRegistry.registerLinkable("minecraft:barrel", LinkRole.OUTPUT);

// ===== 注册端口方块 =====
// 让尚未实现 IPortBlock 的方块被识别为端口
MultiBlockRegistry.registerPort("mymod:item_input_port",  "multiblocksystem:item_input");
MultiBlockRegistry.registerPort("mymod:energy_output_port", "multiblocksystem:energy_output");

// ===== 注册结构体定义 =====
MultiBlockRegistry.registerStructure(
    "mymod:blast_furnace",
    [
        ["FFF", "FCF", "FFF"],
        ["   ", " . ", "   "]
    ],
    {
        "F": "minecraft:blast_furnace",
        "C": "mymod:blast_furnace_controller"
    },
    "C",
    new CapabilitySpec()
        .require(LinkRole.INPUT,  1)
        .require(LinkRole.OUTPUT, 1)
);

6.5 build.gradle 设置(使用 KubeJS 时)

repositories {
    maven {
        url "https://maven.latvian.dev/releases"
        content { includeGroup "dev.latvian.mods" }
    }
}

dependencies {
    // KubeJS 使用 compileOnly(运行时由 KubeJS 侧提供)
    compileOnly fg.deobf("dev.latvian.mods:kubejs-forge:2001.6.5-build.26") {
        transitive = false
    }
}

API 包: dev.latvian.mods.kubejs.*(不是 dev.latvian.kubejs.*) Maven GAV: dev.latvian.mods:kubejs-forge:2001.6.5-build.26


7. Amount 计算的具体示例(JS)

// JS 侧的 Amount 计算示例
const a = Amount.of(1, 3);   // 1/3
const b = Amount.of(1, 6);   // 1/6
const c = a.add(b);          // 1/3 + 1/6 = 1/2  ← 完全精确
const d = a.multiply(3);     // 1/3 * 3 = 1       ← 零误差

console.log(c.toDisplayString());  // "1/2  (≈0.500000)"
console.log(d.toDisplayString());  // "1"

// 能量效率计算
const efficiency = Amount.of(2, 3);  // 66.666...% efficiency
const baseCost   = Amount.of(100);   // 100 FE base cost
const actualCost = baseCost.multiply(efficiency);  // 200/3 FE ← 零误差

// 仅在写入 Minecraft 时使用 floor/ceil
const longCost = actualCost.floor();  // 66 (BigInteger)

8. 注意事项

RegistryImpl.clearStructures() 的使用方法

/mbs reload 只应调用 clearStructures()。controllers 和 linkables 是在 FMLCommonSetupEvent 中注册的,因此不应在服务器重载时清除。

Amount 的 GCD 成本

LongAmount 使用基于 long 的高速 GCD,因此一般值的计算几乎是瞬时的。只有 BigAmount 使用 BigInteger.gcd(),具有 O(log n) 的成本。如果每帧大量调用,建议缓存到 final 字段中。

ForgeItemHandlerAdapter 的聚合上限

当 ForgeItemHandlerAdapter 检测到实现 IBigIntegerItemHandler 的 IItemHandler 时,会使用基于 BigInteger 的聚合(理论上无上限)。对于不支持的 IItemHandler,则使用 long 聚合,总容量可达 Long.MAX_VALUE(约 9.2×10¹⁸)。但单个槽位的操作受限于 Forge 的 IItemHandler,即每个槽位最多 Integer.MAX_VALUE 个物品。Forge 标准的 ItemStackHandler 已通过 Mixin 自动实现 IBigIntegerItemHandler,因此默认为 BigInteger 兼容。

StructureDefinition.fromPattern 的字符编码

字符 含义
(空格) 任意(什么都可以,不参与判定)
. (点) 空气(必须为空)
其他 必须为一词典中指定的方块
coreChar 控制器位置(相对坐标的原点)

ExcludeRegion 的使用方法

ExcludeRegion 通过 AABB(轴对齐边界框)定义扫描排除的区域。可通过 /mbs export 命令的排除参数指定,或将 List<ExcludeRegion> 传给 ChunkScanTask 手动使用。


9. API 参考

9.1 核心类型

类 包 说明
Amount api.amount 有理数类型封闭接口。通过 Amount.of(n) / Amount.of(n, d) 创建
LongAmount api.amount long 分子/分母的 record。溢出时自动升级
BigAmount api.amount BigInteger 分子/分母的 record。结果在 long 范围内时自动降级
IBigItemStack api.amount Item + Amount 的组合。通过 IBigItemStack.of(item, amount) 创建
CapabilitySpec root 多方块成立所需的链接配置。通过 .require(LinkRole, count) 添加
ExcludeRegion root 扫描排除区域(AABB)

→ 计算示例请参阅 第 3 节

9.2 链接方块用接口(由其他模组实现)

IMultiBlockController — 控制器 BlockEntity 实现

包: api.compat — 另请参阅 第 1 节的注册代码

方法 参数 说明
addLink(BlockPos, LinkRole) 链接方块坐标, 角色 记录链接方块的连接
removeLink(BlockPos) 链接方块坐标 解除链接
hasLink(BlockPos) 方块坐标 检查指定坐标是否已链接
getLinkedBlocks() — 返回所有链接目标的 Map<BlockPos, LinkRole>

使用方法: 如果您的 BlockEntity 继承 AbstractMultiBlockController,将自动实现。手动实现时,可直接 implements IMultiBlockController。

IMultiBlockLink — 链接方块的 BlockEntity 实现

包: api.compat

方法 参数 返回值 说明
connectToController(BlockPos, LinkRole) 控制器坐标, 角色 void 记录到控制器的连接
disconnectController() — void 解除连接
isConnected() — boolean 是否正在连接到控制器
getControllerPos() — @Nullable BlockPos 连接目标控制器坐标
getRole() — LinkRole 当前角色
getItemHandler() — IItemHandler 返回 Forge 的 IItemHandler

使用方法: 如果您的 BlockEntity 继承 AbstractLinkBlockEntity,将自动实现。

9.3 抽象基类(通过继承使用)

AbstractMultiBlockController — 控制器 BlockEntity 的基类

包: com.multiblocksystem(根)

方法 类型 说明
tick() concrete 主循环。每40 tick检查一次结构,成立期间调用 processRecipes()
onFormationChanged(boolean formed) protected, 建议重写 结构成立状态变化时的回调
isFormed() concrete 当前结构是否成立
getInventoryForRole(LinkRole) concrete 指定角色的聚合 IItemHandler
getInputInventory() / getOutputInventory() concrete 常用角色的快捷方式
getTemplateId() concrete 关联的结构定义的 ResourceLocation
setTemplateId(ResourceLocation) concrete 手动指定结构定义

继承时的最低实现:

public class MyControllerBE extends AbstractMultiBlockController {
    public MyControllerBE(BlockPos pos, BlockState state) {
        super(MY_BE_TYPE.get(), pos, state);
    }

    @Override
    protected void onFormationChanged(boolean formed) {
        // 结构成立/崩溃时的处理(GUI更新、粒子等)
    }
}

详情请参阅 第 1 节的注册代码 / 第 2 节的实现示例

AbstractLinkBlockEntity — 链接方块的 BlockEntity 基类

包: com.multiblocksystem(根)

方法 类型 说明
connectToController(...) / disconnectController() concrete IMultiBlockLink 的实现
isConnected() / getControllerPos() / getRole() concrete 获取连接状态
getItemHandler() concrete 返回内部的 ItemStackHandler(槽位数: DEFAULT_SLOTS = 9)
onConnected(LinkRole role) protected, 建议重写 连接时的回调
onDisconnected() protected, 建议重写 断开时的回调
onDestroyed(Level) concrete 从 Block.onRemove 调用以通知控制器

继承时的最低实现:

public class MyLinkBE extends AbstractLinkBlockEntity {
    public MyLinkBE(BlockPos pos, BlockState state) {
        super(MY_BE_TYPE.get(), pos, state);
    }

    @Override
    protected void onConnected(LinkRole role) {
        // 连接时的处理(视觉特效等)
    }
}

9.4 行为定义接口(用于委派)

ControllerBehavior — 控制器的行为

包: api.behavior — 所有方法均有默认实现

方法 参数 说明
onFormed(Level, BlockPos, IMultiBlockNetwork) 结构成立时调用
onBroken(Level, BlockPos, IMultiBlockNetwork) 结构崩溃时调用
onLinked(Level, BlockPos, BlockPos, LinkRole, IMultiBlockNetwork) 链接连接时调用
onUnlinked(Level, BlockPos, BlockPos, LinkRole, IMultiBlockNetwork) 链接断开时调用
tick(Level, BlockPos, IMultiBlockNetwork) 成立期间每 tick 调用
getDefaultStructureId() 负责的结构 ID(null 则自动判定)
getValidStructureIds() 可负责的结构 ID 集合
ControllerBehavior.simple(String) static 最小配置的工厂

详情请参阅 第 2 节

LinkableBehavior — 链接方块的行为

包: api.behavior — 存储获取均返回 @Nullable 的默认实现

方法 返回值 说明
getItemStorage(Level, BlockPos) @Nullable IItemStorage 物品存储
getEnergyStorage(Level, BlockPos) @Nullable IEnergyStorage 能量存储
getFluidStorage(Level, BlockPos) @Nullable IFluidStorage 流体存储
getGenericStorage(Level, BlockPos, ResourceKey) @Nullable IGenericStorage 通用存储
onLinked(Level, BlockPos, LinkRole, IMultiBlockNetwork) void 连接时回调
onUnlinked(Level, BlockPos, LinkRole) void 断开时回调
hasItemStorage() / hasEnergyStorage() / hasFluidStorage() boolean 存储有无的预先声明
LinkableBehavior.fromForgeCapability() static 自动包装 Forge IItemHandler

详情请参阅 第 1 节的注册代码

9.5 网络与注册

类/接口 包 说明
IMultiBlockNetwork api.network 从控制器操作网络状态的入口(实现由内部提供)
MultiBlockNetwork root 控制器⇔链接方块的连接/断开逻辑(static utility)
MultiBlockRegistry api.registration API 的唯一注册窗口
StructureDefinition api.registration 结构体定义包装器
MultiBlockEvent api Forge 事件(Formed / Destroyed)
MultiBlockAPI api 内部运行时API(通常不直接使用)
IPortBlock api.port 作为端口方块的功能方块需要实现的接口
IPortType api.port 定义端口类型的接口(getId / getCategory)
PortRegistry api.port IPortType 的全局注册表
MultiBlockTags api.tags 数据包用标签定义(MULTIBLOCK_PORTS)

IMultiBlockNetwork 的主要方法

方法 参数 返回值 说明
getControllerPos() — BlockPos 控制器坐标
isFormed() — boolean 结构是否成立
getItemStorage(LinkRole) 角色 IItemStorage 按角色获取物品存储(聚合)
getAllItemStorage() — IItemStorage 跨所有角色的存储
getEnergyStorage(LinkRole) 角色 IEnergyStorage 能量存储
getFluidStorage(LinkRole) 角色 IFluidStorage 流体存储
canProcess(List<IBigItemStack>, List<IBigItemStack>) 输入, 输出 boolean 检查是否可转移
processTransfer(List<IBigItemStack>, List<IBigItemStack>) 输入, 输出 boolean INPUT→OUTPUT 物品转移
consumeEnergy(Amount, boolean) 量, simulate Amount 能量消耗

详情请参阅 第 2 节的实现示例

9.6 存储接口

接口 包 主要方法
IItemStorage api.storage insert(IBigItemStack, simulate) / extract(Item, Amount, simulate) / getCount(Item) / getCapacity()
IEnergyStorage api.storage 基于 BigInteger 的能量
IFluidStorage api.storage 基于 BigInteger 的流体
IGenericStorage api.storage 通用资源(魔力、热量等)

9.7 BigInteger 支持(内部机制)

类 包 说明
IBigIntegerItemHandler api.compat 将 IItemHandler 扩展到 BigInteger 规模的鸭子接口
ForgeItemHandlerAdapter api.behavior IItemHandler → IItemStorage 桥接器(自动检测 BigInteger)
MixinItemStackHandler mixin 为 ItemStackHandler 自动附加 IBigIntegerItemHandler 的标记 Mixin

详情请参阅 第 5 节

9.8 端口方块相关(4层检测)

结构体内端口坐标处由玩家放置的方块是否被认可为所需端口,按以下优先级检测:

层 检测方法 条件
第1层 IPortBlock 接口 方块类实现 IPortBlock,且 getPortType().getId() 匹配
第2层 MultiBlockRegistry API 注册 通过 registerPort() 注册的方块 ID,且 IPortType 匹配
第3层 Forge Capability