更新概念与不同类型的更新
本部分是更新理论的入门。
基础部分
- 更新理论的认识
- NC 更新、PP 更新、比较器更新等不同类型的更新
- 方块放置时的自检行为
进阶部分(源码分析)
- 简要的 NC 更新与 PP 更新的调用与响应
- 方块自检的调用
1 广义更新
在 Minecraft 中,方块与方块通过互相 “通知” 来建立相互的联系。
有一天,你突发奇想 —— 在地上放置了一个音符盒。然后,你又紧邻着音符盒放置了一个红石块,音符盒发出了悦耳的声音。那么,音符盒是怎么知道自己应该恰好在这个时候发出声音的吗?难道音符盒会一直给玩家发消息:我要发出声音吗?我要发出声音吗?我要发出声音吗…… 这显然是不合理的。实际上,音符盒能够发出声音,是因为红石块在被放置时,“通知” 了音符盒:嘿,这里有点变化,你注意一下。
这种通知行为就称为广义上的更新。
需要注意的是,红石块并不会通知 “我是一个红石块” 或者 “这里从空气变成了红石块”,而是非常简单地通知:这里有点变化。也就是说,在 Minecraft 中,更新行为并不包含行为的具体信息。
2 更新的类型
在 Minecraft 中,更新有不止一种类型。
让我们继续上文中的例子。这天,你又灵光一现 —— 在地上先放置了一个红石块,然后间隔一个方块放置了一个栅栏门,在红石块和栅栏门的中间放置了音符盒。你突然发现,音符盒怎么没有发出声音呢?于是,你右键栅栏门,把栅栏门打开了,但音符盒还是没有发出声音。按理来说,上文中的 “放置红石块” 和 “栅栏门打开” 都是某种变化,它们都应该 “通知” 音符盒,为什么音符盒没有如愿地发出声音呢?带着疑惑,你直接把栅栏门破坏掉了,此时音符盒又发出声音了。
—— 这是为什么呢?
你于是猜想:“放置红石块” 和 “栅栏门打开” 的 “通知” 也许有什么差别。
实际上,这两个通知的确是不同的。现如今我们分别称之为NC 更新和PP 更新。
此时你又思考:为什么音符盒被放置的时候,不会立即发出声音呢?它难道在 “出生” 的时候不会看一看 “自己要不要说两句话” 吗?—— 没错,音符盒比较懒,它确实不会 “检查自己要不要发出声音”。
这种在被放置等情况下检查自身的行为,被称为自检。自检可以被视作一种特殊的更新。而音符盒不会进行自检。
你在先前的学习中又了解到:比较器是一种能够检测容器中物品数量的红石元件,根据容器中物品数量的多少输出 0-15 的不同强度高低的红石信号。你想到:当容器中物品数量发生变化时,容器并没有必要发出NC 更新或PP 更新,毕竟只是内部物品多少改变了,并不会对周围方块产生什么太大影响。但你知道比较器却能够及时地改变自身输出的红石信号强度,说明比较器还是能够收到这种变化的 “通知”。这是因为容器在容器中物品数量发生变化时,也会发出一种特殊的通知。
这样的通知被称为比较器更新。
3 NC 更新
3.1 NC 更新的概念与行为
方块在被放置、破坏、或发生能够显著影响周围方块的变化时,会发出NC 更新。例如:
- 方块的放置与破坏
- 红石粉能量等级发生变化
- 活塞的推拉 *
- ...
官方提供的反混淆中 NC 更新为
neighborChanged,与 mcp 反混淆相同。yarn 反混淆中则称为updateNeighbors
但一些变化不会发出NC 更新,最为常见的有:
- 可连接方块的连接状态改变,例如玻璃板与其他方块连接、各种墙与其他方块连接
- 红石粉的连接状态发生变化,例如一个与它南北侧红石粉相连的红石粉又与东侧的一个红石粉相连接
- 活板门、栅栏门的开关
- 发射器、投掷器的激活状态改变(!重要)
- 漏斗的激活状态改变
- ...
活塞的具体行为不在本篇讨论范围内。
一个发出 NC 更新的 “方块” 称为更新核1,大部分方块发出更新时,更新核是它本身。
更新核发出 NC 更新时,按照西东下上北南的顺序,依次对更新核西侧紧邻、东侧紧邻…… 的方块发出 NC 更新。这样的更新就是通常意义上的更新。
部分方块在发出 NC 更新时不只有一个更新核,它们具有特殊的更新核与范围:
- 红石粉的能量等级变化为二阶毗邻更新2。
- 带有指向的红石元件,例如红石中继器、比较器、侦测器,它们在激活状态发生变化时先单独更新它们输出端指向的方块,再以输出端指向的方块为更新核发出 NC 更新,并且在 NC 更新时不会更新它们自身方块。
- 平放的动力铁轨切换激活状态时,先以自身为更新核产生 NC 更新,再以下方方块为更新核产生 NC 更新。
- 斜放的动力铁轨切换激活状态时,更新顺序为上、中、下、中、下、上(以自身为更新核产生 NC 更新简写为中、以上方方块为更新核简写为上、以下方方块为更新核简写为下,详细解读参见 [斜向铁轨的更新行为](./特殊的更新行为.md#3.2-[!ADVANCED] 斜侧铁轨的更新行为))。
以上是常见且较为典型的例子。更多的特殊情况可以参见Wiki - 方块更新
3.2 NC 更新的性质、QC 激活、BUD 装置
NC 更新最典型的性质是能够使BUD 装置响应。
一个方块当前的状态与它本应该的状态不同时,这个方块就可以称作是一个BUD 装置,即方块更新检测器(Block Update Detector)。这种方块状态也可以称作是BUD 态。
最典型的 BUD 装置是一个被充能(受到红石信号)但未被激活(未伸出)的活塞。活塞具有特殊的充能范围,当活塞本身和活塞上方的一个方块(可以为空气)被充能时,活塞就可以被认为被充能。通过充能活塞上方的空气充能活塞,这种充能方式就称为QC 充能。这样被充能的活塞在受到 NC 更新后伸出,就称作QC 激活。
QC (quasi connectivity) 称为半连接性,活塞、粘性活塞、投掷器、发射器都具有这种性质。
想象一个活塞的斜上方放置了一个红石块,此时这个活塞被 QC 充能。而红石块的放置只会更新它毗邻的六个方块,无法更新到活塞,所以没有任何方块通知活塞 “这里有一个红石信号”。此时这个活塞本应伸出,但却因为没有受到 NC 更新而没有伸出。也就是当前的状态与它本应该的状态不同,即此时这个活塞处于BUD 态,该活塞就构成了一个BUD 装置。
此时,如果在活塞边上放置一个方块,方块放置就会发出 NC 更新。活塞受到 NC 更新,解除 BUD 态。
一个最简单常用的可以自行复位的 BUD 装置如下:
图中的红石块QC 充能下方粘性活塞,当粘性活塞受到 NC 更新后伸出,红石块不再充能,而后粘性活塞收回,红石块再次 QC 充能粘性活塞。3
4 PP 更新
4.1 PP 更新的概念与行为
几乎所有的变化都会发出PP 更新。除了:
- 使用命令放置方块
- 使用调试棒改变方块状态
等极其特殊的情况;但还是有一个特例:
- 粘性活塞推动正在激活的侦测器,到位时侦测器不发出 PP 更新。
官方提供的反混淆中 PP 更新为
updateShape,即更新形状(例如,各种墙的形状、活板门的开关、红石元件的激活状体改变,都可以称作这里的 “形状”),该反混淆名更有益于理解 PP 更新。PP 更新的命名来源于 mcp 反混淆中是postPlacement方法。yarn 反混淆中则称为getStateForNeighborUpdate
和 NC 更新不同,PP 更新的顺序为西东北南下上。
在上文中提到的 * 不会发出NC 更新 *的行为,都是较为典型的发出PP 更新但不发出NC 更新的行为。同样,上文中的BUD 装置无法用来检测PP 更新。
例如这里的栅栏门状态发生变化,发出 pp 更新,但并不会被活塞 BUD 装置检测到
需要注意的是,除了放置与破坏方块外,大部分红石元件的 NC 更新范围和 PP 更新范围是不一样的。例如中继器的 NC 更新范围是输出端及输出端的毗邻(除中继器自身),而其 PP 更新范围是自身的毗邻。
PP 更新可以理解为自身发生变化的更新,而 NC 更新可以理解为通知可能需要改变状态的方块的更新。
同样以中继器为例,中继器的亮灭属于自身发生了变化,通过 PP 更新通知自身毗邻的方块。而中继器亮起会充能它输出端的方块,如果输出端的方块能够传递红石信号,这个方块还会再向它的毗邻传递红石信号;所以说中继器的亮起有可能会影响它输出端的方块以及输出端毗邻的方块,因此中继器通过 NC 更新通知可能受到影响的方块检查是否要改变状态。
又例如,当投掷器被激活,投掷物品时,投掷物品这一行为并不会对周围方块造成影响4,因此投掷器激活并不发出 NC 更新。但是投掷器的确改变了激活状态,这种状态改变使得投掷器发出了 PP 更新。
这样,PP 更新与 NC 更新的差异以及它们更新范围的不同就易于理解了。
4.2 PP 更新的性质
既然上文中的 BUD 装置无法检测 PP 更新,那么什么装置能够检测(响应)PP 更新呢?
答案是 —— 侦测器。侦测器长得像脸的一面为检测 PP 更新的一面,有红色小点的一面为输出红石信号的一面。
侦测器响应、且仅响应 PP 更新。5但由于大多数情况 NC 更新总是伴随 PP 更新的,所以这里的 “仅” 可能体现的并不明显。不过我们还是可以通过一些方法来体现:当侦测器直接面向中继器时,中继器亮灭会激活侦测器;但如果侦测器面向中继器输出端指向的方块(空气),中继器的亮灭则不会激活侦测器。这里也很好理解:当中继器指向一个空气时,空气不会有任何变化,因此侦测器自然也不会响应了。
通过 BUD 装置与侦测器的配合使用,我们可以很方便的在不翻阅源码的情况下测出各个红石元件的 NC 更新范围和 PP 更新范围。
5 比较器更新
5.1 比较器更新的概念与行为
顾名思义,比较器更新是专用于更新比较器的更新。
以下行为会发出比较器更新:
- 一般的容器,如箱子、木桶、漏斗等中的物品数量发生变化。信号强度计算方式见下。
- 更广义上的容器,有 “容量” 概念的方块例如堆肥桶(信号强度与堆肥等级相等,范围为 0~9)、炼药锅(信号强度与装液体的等级相等,范围为 0~3)。
- 物品展示框,物品展示框输出的比较器信号强度取决于展示物品的旋转角度,仅输出强度为 1~8 的信号。不放置物品时信号强度为 0。
- 讲台,信号强度计算方式见下。
- 探测铁轨上方有掉落物。
- 探测铁轨上有带有容器的矿车,如运输矿车(箱子矿车)、漏斗矿车时,矿车内物品数量发生变化时会在 20gt 后发出比较器更新。信号强度计算方式与一般容器相同。
- 雕纹书架(1.20+),信号强度与最后一次放置或取出的书的位置相等,第一排为 1~3,第二排为 2~6。
比较器既可以直接检测容器容量,也可以间隔一个可传递红石信号的方块检测容器容量。因此比较器更新的范围为容器水平方向的毗邻范围内的比较器,或二阶毗邻范围内间隔一个可传递红石信号的方块的比较器。比较器更新不会对比较器以外的方块造成更新。
5.2 比较器信号强度计算
对一般的容器:
- 输出信号强度 = 对
每个物品槽位填充比例的平均值*14向下取整,如果容器不是空的,那么输出信号强度再+1。即:当容器为空,输出 0。当容器不为空,输出容器的平均填充比例到 1~15 的映射并向下取整。
对讲台:
- 当讲台上不放置书时,输出信号强度为 0。
- 当讲台上放置书且书只有一页时,输出信号强度为 15。
- 当讲台上放置书且书不只有一页时,输出信号强度为
当前页码/总页码*14向下取整再+1。 - 即:当讲台为空,输出 0。当讲台不为空,输出当前页码占总页码的比例到 1~15 的映射并向下取整。
5.3 比较器更新检测器(CUD)
一个比较器当前的状态与它本应该的状态不同时,这个比较器就构成了一个比较器更新检测器(CUD,Comparator Update Detector)。一个最简单的例子是用比较器间隔一个方块检测一个有填充的堆肥桶,然后用活塞将堆肥桶推走。此时比较器依然有输出信号。
由于其他可复位的 CUD 结构与涉及原理较为复杂,在此处仅作展示,不作展开。
图为基于红石粉粉转向的 CUD。
可以看到 BUD 并未启动,而 CUD 检测到了漏斗发出的比较器更新。
6 方块自检
红石元件在被放置时会进行自检,即检查自身是否应当改变状态。例如放置一个红石块,然后放置一个中继器,并且这个中继器的输入端紧邻着红石块。此时中继器没有受到更新,但还是亮起了,这就是因为中继器在被放置时进行了自检,发现自己应该亮起,于是亮起。
活塞自身到位也可以视作一种 “被放置”,因此活塞自身在伸出后到位和拉回后到位也都会进行一次自检。
9 更新行为的控制
ServerWorld.setBlockState 方法中,一个整数参数被传入:flag
这个 flag 实际上被视为一个 9 位二进制码,这 9 位中,每一位都控制了不同的行为
10 flag 的 9 位标志定义
11 常量解释
Block.java 中定义了多个更新行为,他们分别是





