比较器收到方块更新后的行为浅析

ComparatorBlock.update()

当比较器收到更新时,首先调用了calculateOutputSignal(),并将其返回值赋值给i

接下来会获取比较器方块坐标内的方块实体,若该方块实体为ComparatorBlockEntity,则获取其outputSignal并赋值给j,然后将outputSignal设为i

j不等于i或比较器处于比较模式,调用hasPower()并将其返回值(boolean)赋值给bl
然后获取比较器的POWERED状态(boolean)赋值给bl2

hasPower()的内容大致是:
先调用ComparatorBlock.getPower(),若其返回0则直接返回false
否则获取比较器两侧红石块和红石粉的信号强度,若有多个则取最大值,若getPower()的返回值 > 信号强度,则返回true
getPower()的返回值与 信号强度相等且比较器处于比较模式,返回true

bl2trueblfalse(即比较器状态为亮且比较器的输出为0),则将比较器的POWERED状态设为false(灭);
bl2falsebltrue(即比较器状态为灭且比较器的输出不为0),则将比较器的POWERED状态设为true(亮)。

最后向比较器指向的位置及其毗邻(不含比较器自身位置)发出NC更新。

private void update(World world, BlockPos pos, BlockState state) {
    int i = this.calculateOutputSignal(world, pos, state);
    BlockEntity blockEntity = world.getBlockEntity(pos);
    int j = 0;
    if (blockEntity instanceof ComparatorBlockEntity) {
        ComparatorBlockEntity comparatorBlockEntity = (ComparatorBlockEntity)blockEntity;
        j = comparatorBlockEntity.getOutputSignal();
        comparatorBlockEntity.setOutputSignal(i);
    }
    if (j != i || state.get(MODE) == ComparatorMode.COMPARE) {
        boolean bl = this.hasPower(world, pos, state);
        boolean bl2 = state.get(POWERED);
        if (bl2 && !bl) {
            world.setBlockState(pos, (BlockState)state.with(POWERED, false), 2);
        } else if (!bl2 && bl) {
            world.setBlockState(pos, (BlockState)state.with(POWERED, true), 2);
        }
        this.updateTarget(world, pos, state);
    }
}

ComparatorBlock.calculateOutputSignal()

这个方法的逻辑很简单:若比较器处于减法模式,返回正面输入减侧面输入的值,若小于0则返回0;
否则调用ComparatorBlock.getPower()并返回该方法的返回值。

private int calculateOutputSignal(World world, BlockPos pos, BlockState state) {
    if (state.get(MODE) == ComparatorMode.SUBTRACT) {
        return Math.max(this.getPower(world, pos, state) - this.getMaxInputLevelSides(world, pos, state), 0);
    }
    return this.getPower(world, pos, state);
}

ComparatorBlock.getPower()

这个方法首先调用了AbstractRedstoneGateBlock.getPower()并将其返回值赋值给i

AbstractRedstoneGateBlock.getPower()的内容大致是:
先检测其面向(检测)的方块的充能等级,若大于等于15,则直接返回充能等级
否则检测其面向方块是否为红石粉,若是,则返回充能等级和红石粉的信号强度中较大的值,否则返回充能等级,若充能等级小于0则返回0。

然后定义了一个blockState为比较器面向(检测)的方块,若该方块有比较器输出(即该方块可被比较器检测),则返回该方块的比较器输出

protected int getPower(World world, BlockPos pos, BlockState state) {
    int i = super.getPower(world, pos, state);
    Direction direction = state.get(FACING);
    BlockPos blockPos = pos.offset(direction);
    BlockState blockState = world.getBlockState(blockPos);
    if (blockState.hasComparatorOutput()) {
        i = blockState.getComparatorOutput(world, blockPos);
    } else if (i < 15 && blockState.isSolidBlock(world, blockPos)) {
        blockPos = blockPos.offset(direction);
        blockState = world.getBlockState(blockPos);
        ItemFrameEntity itemFrameEntity = this.getAttachedItemFrame(world, direction, blockPos);
        int j = Math.max(itemFrameEntity == null ? Integer.MIN_VALUE : itemFrameEntity.getComparatorPower(), blockState.hasComparatorOutput() ? blockState.getComparatorOutput(world, blockPos) : Integer.MIN_VALUE);
        if (j != Integer.MIN_VALUE) {
            i = j;
        }
    }
    return i;
}

比较器直接检测容器时的输出计算

当比较器检测一个有比较器输出的方块时,会调用该方块的getComparatorOutput()方法

对于非蜂巢(蜂箱)/蛋糕/炼药锅/命令方块/堆肥桶/上方不含有有有效物品栏的矿车的探测铁轨/末地传送门框架/唱片机/讲台/重生锚的有比较器输出的方块
对于熔炉(烟熏炉/高炉)/木桶/酿造台/箱子(陷阱箱)/发射器(投掷器)/漏斗/潜影盒,这个方法调用了ScreenHandler.calculateComparatorOutput()并将该方块所在坐标内的方块实体作为参数传给该方法

ScreenHandler.calculateComparatorOutput()

(在这之前还有一个方法将输入的BlockEntity转成了一个Inventory,此处省略)

若该方块实体没有物品栏,直接返回0;
否则遍历物品栏内的每一格并检测其中的ItemStack,若这个ItemStack不为空,则将它的物品数量除以这种物品的最大堆叠量,并将这个值加到f(初值为0)上,同时使i(初值为0)加1。

物品栏遍历完成后,进行以下计算:
i > 0,将i设为1,否则为0;
f / 物品栏格数 * 14 + i
返回向下取整后的计算结果。

public static int calculateComparatorOutput(@Nullable Inventory inventory) {
    if (inventory == null) {
        return 0;
    }
    int i = 0;
    float f = 0.0f;
    for (int j = 0; j < inventory.size(); ++j) {
        ItemStack itemStack = inventory.getStack(j);
        if (itemStack.isEmpty()) continue;
        f += (float)itemStack.getCount() / (float)Math.min(inventory.getMaxCountPerStack(), itemStack.getMaxCount());
        ++i;
    }
    return MathHelper.floor((f /= (float)inventory.size()) * 14.0f) + (i > 0 ? 1 : 0);
}

比较器透过方块检测容器及物品展示框时的输出计算

回到ComparatorBlock.getPower()里继续往下看,当i(即比较器面向的方块的充能等级) < 15,且该方块是一个可充能方块时,获取比较器面向的第二格内的朝向正确的物品展示框并获取其比较器信号输出,若符合条件的展示框数量不为1,则取Integer.MIN_VALUE(即-2147483648);
然后检测比较器面向的第二格的方块是否有比较器输出,若有则调用该方块的getComparatorOutput()方法获取其比较器输出,此处不再赘述,若无则取Integer.MIN_VALUE
若这两数非均为Integer.MIN_VALUE,则取其中最大值赋值给i并作为该方法的返回值并返回。

比较器收到方块更新后的行为浅析
https://bsrserver.org:8443/blogs/1253677388441583616
Author
HeyBlack
Posted on
Jun 21, 2024
Updated on
Jun 21, 2024
License
byncsa
cc