Minecraft Java版的红石粉的更新是一个非常复杂的系统,许多模块的位置性问题(指同一个模块在不同的位置表现不一致)都由红石粉的更新导致。本专栏将解读一定数量的源码来解释红石线的更新顺序。然而,这个解释不能保证一定准确,对很多代码的解读仅为猜测,仅供参考。
源码来源:mcp
版本:JE 1.16
本专栏可能不适合完全没有红石基础的玩家。本专栏暂时只考虑红石线从无能量到有能量这个过程产生的更新,因为从有能量到无能量产生的更新量非常大而且过于复杂。

RedstoneWireBlock.java内的neighborChanged方法 (解读含大量猜测)
从这个方法的名字来看,应该是在周围方块对红石线进行更新时调用,先是检测红石线是否在一个有效的位置(比如不是悬空之类的),如果是在有效的位置,则调用updatePower方法,不然则生成一个红石粉掉落物,并将该方块从世界移除。

getStrongestSignal方法
将i赋值为world.getRedstonePowerFromNeighbors(pos),这里在查看了这个方法后猜测这个方法返回的是周围被充能方块中的最大值。接着又设了j=0,j是一个去储存周围红石线最大能量强度-1的变量。当i小于15时,检测周围水平面的红石线,顺序北东南西,以此检测对应方向偏移1格的红石线能量强度。blockpos1为原位置上方1格的方块,blockpos为水平偏移1格后的方块,通过检测blockpos1是否透明以及blockpos是否透明可以得到斜上或斜下是否可能有红石线,是否被压线。如果偏移1格后的位置blockpos的方块是非透明方块 并且在原本位置pos往上1格的位置blockpos1没有非透明方块,则比较偏移1格后再往上1格的红石线的能量强度,和j比较并取最大值。如果偏移1格后的位置blockpos的方块是透明方块,则比较偏移1格后再往下1格的红石线的能量强度和j比较并取最大值。最后返回 i 和 j-1(红石粉能量距离衰减) 中最大的。

updatePower方法
这个方法先是调用了上面的getStrongestSignal方法得到当前红石粉应该有的能量强度,并将这个值赋予i,当当前红石粉的能量强度不是i时,则将红石粉的能量设为i (这个if貌似只是检查方块状态是否正确,猜测可以忽略不记)。接着就是一条导致不同的位置红石线更新顺序不一样的代码了,这里创建了一个HashSet,并将当前位置放入了这个HashSet。这个hashCode()也被重新写了,在Vector3i.java里:

可以看到,一个坐标的哈希值等同于((Y + Z * 31) * 31 + X)。
接着从Direction.values()里面拿取六个方向:

顺序为下,上,北,南,西,东,并将这六个方向按这个顺序放入这个HashSet,最后遍历一遍这个HashSet,从里面拿取这6个方向的坐标以及自己本身的坐标共7个元素,按HashSet内的顺序更新周围的方块,这个更新的顺序为:

西,东,下,上,北,南。注意这里不会更新自己。
这里将HashSet内的7个坐标称为更新源,更新源的顺序可以自己写一个程序得出,只需要把HashCode给弄成上面写的就行:


这里的O代表红石线所处的位置,运行程序之后得到顺序 -Y +Z +X +Y -Z -X O,这是红石线在坐标(131,159,-249)产生的更新源顺序,这个坐标在任何地图产生的更新源顺序都是这个。接着从第一个更新源开始,更新周围的六个方块,不包括自己,每个更新源更新周围方块的顺序都为上面提到的 “西,东,下,上,北,南”,-X +X -Y +Y -Z +Z。

接着是实际操作,左边的命令方块输入say 1,右边输入say 2。最中间的红石线坐标为(132,159,-249),左边是+X,右边是-X。
那么拉下中间的拉杆谁输出呢?首先该拉杆会激活位置(132,159,-249)的红石线,该红石线状态改变,则产生更新源,顺序由上面的程序得出O -Y +Z +X +Y -Z -X

除了O以外的更新源只会更新蓝色标出的两个方块:

就算O不是第一个更新源,这两个红石粉周围的红石粉都没有能量,所以状态不改变。更新源O,按-X +X -Y +Y -Z +Z的顺序,先更新了右边-X方向的方块 (这里可以得出,这样的机器激活的第一个红石粉,可以直接使用在红石粉位置的-X +X -Y +Y -Z +Z的顺序,因为只有O是有效的更新源):

接着用程序计算上图右边绿色方块(131,159,-249)的更新源顺序,得到-Y +Z +X +Y -Z -X O,这里+X的更新源比-X靠前,这里下图红色方块造成的更新源绿色方块,只能更新蓝色方块,因为是唯一一个没被更新过的相邻的方块:

由于此方块右边两格都已经更新过了,所以只能向左边更新:

所以最终结果是左边的命令方块先输出,输出为先1后2。

用同样的方法对第二个拉杆试试,按照刚刚上面所说,第一个被激活的方块只需要管O更新源造成的更新,顺序是-X +X -Y +Y -Z +Z,所以右边-X方向的红石粉(绿色方块)先更新:

通过程序得到绿色方块(132,159,-249)的更新源顺序O -Y +Z +X +Y -Z -X,更新源O先更新,顺序是-X +X -Y +Y -Z +Z,-X先更新,所以绿色方块右边的红石粉先更新,最后同样的道理,左边两个方块已经更新过了,所以只能向右更新,命令方块应输出先2后1:

那有没有办法在这个拉杆拉下时让命令方块输出先1后2呢,我们知道(132,159,-249)处的更新源顺序是O -Y +Z +X +Y -Z -X,要让顺序调转,必须让+X方向先更新,我们可以用走线屏蔽掉O和-Y的更新源,+Z的更新源是没用的,因为周围没有红石线,直接到+X,让它更新左边第二格的红石线:

通过这么一个走线,要想先激活右边的命令方块,必须要+Y更新,然而+X在+Y前面,所以顺序会调转:

其他几个拉杆都是一样的,如果我上面说的有任何错误,或者这个解释在某些场合下失效了,欢迎指出。