FEATURE_HEADER_
This feature was created for debugging purposes only.
Edit test.
Large video upload test.
🎬 2026-03-19-18-50-46.mp4 (34.7 MB)
[▶ View / Download]
Long text to check for reading experiences.
顾名思义,比较器更新是专用于更新比较器的更新。
以下行为会发出比较器更新:
比较器既可以直接检测容器容量,也可以间隔一个可传递红石信号的方块检测容器容量。因此比较器更新的范围为容器水平方向的毗邻范围内的比较器,或二阶毗邻范围内间隔一个可传递红石信号的方块的比较器。比较器更新不会对比较器以外的方块造成更新。
对一般的容器:
每个物品槽位填充比例的平均值*14向下取整,如果容器不是空的,那么输出信号强度再+1。即:当容器为空,输出 0。当容器不为空,输出容器的平均填充比例到 1~15 的映射并向下取整。对讲台:
当前页码/总页码*14向下取整再+1。一个比较器当前的状态与它本应该的状态不同时,这个比较器就构成了一个比较器更新检测器(CUD,Comparator Update Detector)。一个最简单的例子是用比较器间隔一个方块检测一个有填充的堆肥桶,然后用活塞将堆肥桶推走。此时比较器依然有输出信号。
由于其他可复位的 CUD 结构与涉及原理较为复杂,在此处仅作展示,不作展开。
图为基于红石粉粉转向的 CUD。
可以看到 BUD 并未启动,而 CUD 检测到了漏斗发出的比较器更新。
红石元件在被放置时会进行自检,即检查自身是否应当改变状态。例如放置一个红石块,然后放置一个中继器,并且这个中继器的输入端紧邻着红石块。此时中继器没有受到更新,但还是亮起了,这就是因为中继器在被放置时进行了自检,发现自己应该亮起,于是亮起。
活塞自身到位也可以视作一种 “被放置”,因此活塞自身在伸出后到位和拉回后到位也都会进行一次自检。
setBlockState函数setBlockState通过一个bitflags即FLAG控制更新行为,在World.java的World类中:
在源码中,NC 更新主要通过两种方式调用:
updateNeighbor及其衍生flag中bit0 = 1的setBlockStateupdateNeighbor及其衍生,即updateNeighbor updateNeighborsExcept updateNeighborsAlways。
其中updateNeighbor是最根本的方法,传入pos, sourceBlock, sourcePos三个参数,分别为要更新的方块坐标pos,发出更新的方块类型sourceBlock和发出更新的方块坐标sourcePos。
updateNeighborsAlways和updateNeighborsExcept通过调用NeighborUpdater.updateNeighbors进行更新,在NeighborUpdater.java的NeighborUpdater实例中:
因此 NC 更新顺序为西东下上北南。
其中在Block.java中定义:
对于以上的调用方法,只有调用updateNeighborsAlways和updateNeighborsExcept的sourcePOS和setBlockState的pos可以称作 “更新核”,即以一个方块为中心对毗邻六个方块(或除去某个方向的方块)进行更新的 “核”。而updateNeighbor是简单直接的 “更新”。
仅 PP 更新时,FLAG通常为NOTIFY_LISTENERS;同时发出 PP 更新和 NC 更新时,FLAG通常为NOTIFY_ALL。部分覆写了onBlockAdded方法的方块具有不同的调用方式,见
在各个方块中,NC 更新由方块的neighborUpdate函数响应。
# Test Feature
This feature was created for debugging purposes only.
---
Edit test.
---
Large video upload test.
🎬 **2026-03-19-18-50-46.mp4** (34.7 MB)
[[▶ View / Download]](https://beta.techmc.wiki/api/files/proxy?path=data%2Fvideos%2F1774159737605-2026-03-19-18-50-46.mp4)
---
Long text to check for reading experiences.

---
## 1.5 比较器更新
### 1.5.1 比较器更新的概念与行为
顾名思义,比较器更新是专用于更新比较器的更新。
以下行为会发出比较器更新:
- 一般的容器,如箱子、木桶、漏斗等中的物品数量发生变化。信号强度计算方式见下。
- 更广义上的容器,有“容量”概念的方块例如堆肥桶(信号强度与堆肥等级相等,范围为0\~9)、炼药锅(信号强度与装液体的等级相等,范围为0\~3)。
- 物品展示框,物品展示框输出的比较器信号强度取决于展示物品的旋转角度,仅输出强度为1\~8的信号。不放置物品时信号强度为0。
- 讲台,信号强度计算方式见下。
- 探测铁轨上方有掉落物。
- 探测铁轨上有带有容器的矿车,如运输矿车(箱子矿车)、漏斗矿车时,矿车内物品数量发生变化时会在20gt后发出比较器更新。信号强度计算方式与一般容器相同。
- 雕纹书架(1.20+),信号强度与最后一次放置或取出的书的位置相等,第一排为1\~3,第二排为2\~6。
比较器既可以直接检测容器容量,也可以间隔一个可传递红石信号的方块检测容器容量。因此比较器更新的范围为容器水平方向的毗邻范围内的比较器,或二阶毗邻范围内间隔一个可传递红石信号的方块的比较器。比较器更新不会对比较器以外的方块造成更新。
### 1.5.2 比较器信号强度计算
对一般的容器:
- 输出信号强度=对`每个物品槽位填充比例的平均值*14`向下取整,如果容器不是空的,那么输出信号强度再`+1`。即:当容器为空,输出0。当容器不为空,输出容器的平均填充比例到1~15的映射并向下取整。
对讲台:
- 当讲台上不放置书时,输出信号强度为0。
- 当讲台上放置书且书只有一页时,输出信号强度为15。
- 当讲台上放置书且书不只有一页时,输出信号强度为`当前页码/总页码*14`向下取整再`+1`。
- 即:当讲台为空,输出0。当讲台不为空,输出当前页码占总页码的比例到1~15的映射并向下取整。
### 1.5.3 比较器更新检测器(CUD)
一个比较器当前的状态与它**本应该的状态**不同时,这个比较器就构成了一个比较器更新检测器(CUD,Comparator Update Detector)。一个最简单的例子是用比较器间隔一个方块检测一个有填充的堆肥桶,然后用活塞将堆肥桶推走。此时比较器依然有输出信号。
由于其他可复位的CUD结构与涉及原理较为复杂,在此处仅作展示,不作展开。


> *图为基于红石粉粉转向的CUD。*
>
> 可以看到BUD并未启动,而CUD检测到了漏斗发出的比较器更新。
## 1.6 方块自检
红石元件在被放置时会进行**自检**,即检查自身是否应当改变状态。例如放置一个红石块,然后放置一个中继器,并且这个中继器的输入端紧邻着红石块。此时中继器没有受到更新,但还是亮起了,这就是因为中继器在被放置时进行了**自检**,发现自己应该亮起,于是亮起。
活塞自身到位也可以视作一种“被放置”,因此活塞自身在伸出后到位和拉回后到位也都会进行一次**自检**。
## 1.7【进阶】NC更新与PP更新的调用与响应
### 1.7.1 `setBlockState`函数
`setBlockState`通过一个`bitflags`即`FLAG`控制更新行为,在`World.java`的`World`类中:
```java
public boolean setBlockState(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
...
BlockState blockState = worldChunk.setBlockState(pos, state, (flags & Block.MOVED) != 0);
if (blockState != null) {
BlockState blockState2 = this.getBlockState(pos);
if (blockState2 == state) {
...
// 由FLAG控制的NC更新的调用
if ((flags & Block.NOTIFY_NEIGHBORS) != 0) {
this.updateNeighbors(pos, blockState.getBlock());
if (!this.isClient && state.hasComparatorOutput()) {
this.updateComparators(pos, block);
}
}
// 由FLAG控制的PP更新的调用
if ((flags & Block.FORCE_STATE) == 0 && maxUpdateDepth > 0) {
int i = flags & ~(Block.NOTIFY_NEIGHBORS | Block.SKIP_DROPS);
blockState.prepare(this, pos, i, maxUpdateDepth - 1);
state.updateNeighbors(this, pos, i, maxUpdateDepth - 1);
state.prepare(this, pos, i, maxUpdateDepth - 1);
}
...
}
return true;
}
return false;
}
```
### 1.7.2 NC更新的调用与响应
在源码中,NC更新主要通过两种方式调用:
- `updateNeighbor`及其衍生
- `flag`中`bit0 = 1`的`setBlockState`
`updateNeighbor`及其衍生,即`updateNeighbor` `updateNeighborsExcept` `updateNeighborsAlways`。
其中`updateNeighbor`是最根本的方法,传入`pos, sourceBlock, sourcePos`三个参数,分别为要更新的方块坐标`pos`,发出更新的方块类型`sourceBlock`和发出更新的方块坐标`sourcePos`。
`updateNeighborsAlways`和`updateNeighborsExcept`通过调用`NeighborUpdater.updateNeighbors`进行更新,在`NeighborUpdater.java`的`NeighborUpdater`实例中:
```java
public static final Direction[] UPDATE_ORDER = new Direction[]{Direction.WEST, Direction.EAST, Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH};
default public void updateNeighbors(BlockPos pos, Block sourceBlock, @Nullable Direction except) {
for (Direction direction : UPDATE_ORDER) {
if (direction == except) continue;
this.updateNeighbor(pos.offset(direction), sourceBlock, pos);
}
}
```
因此NC更新顺序为西东下上北南。
其中在`Block.java`中定义:
```java
// 向周围方块广播方块更新事件
public static final int NOTIFY_NEIGHBORS = 1;
// 提醒需要对此方块改变做出反应的监听器和客户端
public static final int NOTIFY_LISTENERS = 2;
// 默认的方块变更时行为:与一起启用上述 NOTIFY_NEIGHBORS 和 NOTIFY_LISTENERS同效。
public static final int NOTIFY_ALL = 3;
// 跳过更新,强制设置方块状态。
public static final int FORCE_STATE = 16;
```
对于以上的调用方法,只有调用`updateNeighborsAlways`和`updateNeighborsExcept`的`sourcePOS`和`setBlockState`的`pos`可以称作“更新核”,即以一个方块为中心对毗邻六个方块(或除去某个方向的方块)进行更新的“核”。而`updateNeighbor`是简单直接的“更新”。
仅PP更新时,`FLAG`通常为`NOTIFY_LISTENERS`;同时发出PP更新和NC更新时,`FLAG`通常为`NOTIFY_ALL`。部分覆写了`onBlockAdded`方法的方块具有不同的调用方式,见[下文](#174-红石元件的nc更新与pp更新的调用)
在各个方块中,NC更新由方块的`neighborUpdate`函数响应。public boolean setBlockState(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) { ... BlockState blockState = worldChunk.setBlockState(pos, state, (flags & Block.MOVED) != 0); if (blockState != null) { BlockState blockState2 = this.getBlockState(pos); if (blockState2 == state) { ... // 由 FLAG 控制的 NC 更新的调用 if ((flags & Block.NOTIFY_NEIGHBORS) != 0) { this.updateNeighbors(pos, blockState.getBlock()); if (!this.isClient && state.hasComparatorOutput()) { this.updateComparators(pos, block); } } // 由 FLAG 控制的 PP 更新的调用 if ((flags & Block.FORCE_STATE) == 0 && maxUpdateDepth > 0) { int i = flags & ~(Block.NOTIFY_NEIGHBORS | Block.SKIP_DROPS); blockState.prepare(this, pos, i, maxUpdateDepth - 1); state.updateNeighbors(this, pos, i, maxUpdateDepth - 1); state.prepare(this, pos, i, maxUpdateDepth - 1); } ... } return true; } return false; }public static final Direction[] UPDATE_ORDER = new Direction[]{Direction.WEST, Direction.EAST, Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH};default public void updateNeighbors(BlockPos pos, Block sourceBlock, @Nullable Direction except) { for (Direction direction : UPDATE_ORDER) { if (direction == except) continue; this.updateNeighbor(pos.offset(direction), sourceBlock, pos); }}// 向周围方块广播方块更新事件public static final int NOTIFY_NEIGHBORS = 1;// 提醒需要对此方块改变做出反应的监听器和客户端public static final int NOTIFY_LISTENERS = 2;// 默认的方块变更时行为:与一起启用上述 NOTIFY_NEIGHBORS 和 NOTIFY_LISTENERS 同效。public static final int NOTIFY_ALL = 3;// 跳过更新,强制设置方块状态。public static final int FORCE_STATE = 16;