關(guān)于強(qiáng)轉(zhuǎn)抑制器的筆記
一、強(qiáng)轉(zhuǎn)抑制器的理論
這部分的代碼來源是 1.19.2 Yarn。
更新抑制,是技術(shù)生存玩法中的一項非常常用且重要的技術(shù)。它的本質(zhì)原理是通過拋出一個異常,來打斷代碼的執(zhí)行。在常用的包含一個鐵軌陣列的抑制器中,我們用遞歸的方塊更新,拋出了一個棧溢出異常,實現(xiàn)了打斷代碼的目的。但是,棧溢出異常這個具體的異常類型并不是本質(zhì)的,只要是個異常就可以了。在 1.19,棧溢出更新抑制被修復(fù)了。但是我們有內(nèi)存溢出更新抑制,這就是基于內(nèi)存溢出錯誤而實現(xiàn)更新抑制的一種方法。
ClassCastException 類型轉(zhuǎn)換異常,簡稱 CCE,是 Java 的一個運(yùn)行時異常,當(dāng)一個對象被強(qiáng)制類型轉(zhuǎn)換到一個它不符合的類型的時候被拋出。因此,如果我們可以通過玩家操作拋出一個 CCE,也可以用來實現(xiàn)更新抑制。一般而言,我們并不會有這樣的機(jī)會。但是方塊實體替換科技的發(fā)展給了我們這樣的希望。
(方塊實體替換請看 www.bilibili.com/video/BV1B8411W7Di)
比如說,如果將一個箱子的方塊實體換到一個發(fā)射器上,當(dāng)發(fā)射器執(zhí)行發(fā)射動作的時候,會運(yùn)行下面的代碼:

它會獲取自己位置的方塊實體,然后不加判斷地將其強(qiáng)制轉(zhuǎn)換為一個發(fā)射器方塊實體。但是我們換了一個箱子的方塊實體上去,就拋出了 ClassCastException,導(dǎo)致游戲崩潰……于是就崩檔了……
這個 CCE 的位置太危險了,但是也給了我們新的希望——也許代碼里面有別的類似地不加判斷的強(qiáng)制類型轉(zhuǎn)換,可以讓我們通過玩家操作瞬時地拋出一個 CCE?于是我查找了游戲代碼里面的所有 getBlockEntity() 的調(diào)用,大概有一百處的樣子,經(jīng)過一處處地翻找有了發(fā)現(xiàn):

當(dāng)一個潛影盒計算自己的比較器輸出的時候,它會獲得自己的方塊實體,強(qiáng)制類型轉(zhuǎn)換為一個物品欄,然后丟進(jìn) calculateComparatorOutput() 這個函數(shù)。實際上,函數(shù)?calculateComparatorOutput() 里面已經(jīng)包括了安全的類型轉(zhuǎn)換的邏輯,麻將寫在這里的這一處不加判斷的類型轉(zhuǎn)換是完全沒有必要的,也是危險的。甚至潛影盒之外的別的容器,比如箱子計算自己的比較器輸出的時候都沒有這個轉(zhuǎn)換,這一處代碼是麻將的祖?zhèn)鞔a,從 1.11 潛影盒加入傳到現(xiàn)在都沒變過……
但是這一處強(qiáng)制類型轉(zhuǎn)換是可以利用的——我們?nèi)绻軌蜃屢粋€潛影盒擁有一個非物品欄的方塊實體,那么每當(dāng)比較器想要計算它的輸出的時候,就會拋出 CCE。那么如果我們將一個比較器從這樣的潛影盒里面輸出,那么每次更新這個比較器,比較器就會重新計算一次自己該輸出的能量以判斷是否應(yīng)該更新自己的狀態(tài),于是就會計算這個潛影盒的輸出,然后就拋異常了!這個潛影盒加上這個比較器,就構(gòu)成了一個沒有狀態(tài)、不需要重置的更新抑制器。
由于它是基于強(qiáng)制類型轉(zhuǎn)換異常工作的,這樣的抑制器我們把它叫做 強(qiáng)轉(zhuǎn)抑制器,英文名叫做 cast suppressor 或者 CCE suppressor;另外,因為這個潛影盒是打不開的,又具有更新抑制的能力,有時我們也稱這個潛影盒為 魔法盒。
二、強(qiáng)轉(zhuǎn)抑制器的制作
這部分的代碼來源是 1.15.2 Yarn。
為了實現(xiàn)方塊實體替換,我們要找到合適的帶有方塊實體的方塊,它被破壞的時候,在刪除自己的方塊實體之前會發(fā)出一次更新,方便我們用別的更新抑制方法打斷邏輯,跳過刪除方塊實體這一步。同時,為了潛影盒可以發(fā)出 CCE,我們需要換上去的方塊實體不是一個物品欄。通過查閱代碼我們可以知道,講臺(1.14+)和唱片機(jī)(1.11 - 1.13)可以滿足我們的要求。鑒于大部分讀者都是高版本玩家,我就只講講臺的方法就好了。
關(guān)于方塊實體替換的原理,我在前面是給了一個鏈接的,你自己去看就好了。為了破壞掉講臺而不破壞它的方塊實體,我們要在講臺的 onRemove() 方法這里發(fā)出一次可以被抑制的更新,那么我們來看一下邏輯:

原版有一個特性是,當(dāng)講臺上面的書被翻頁的時候,會發(fā)出一個 2gt 長的紅石信號,這個時候講臺處于啟動狀態(tài)。我們讀代碼知道,當(dāng)啟動狀態(tài)的講臺被破壞的時候,會更新自己下方的毗鄰。因此,我們在視頻中,tick freeze 了之后先翻書,然后打開中繼器下的活板門,讓它浮空,然后我們打掉講臺,講臺在這里更新中繼器,中繼器碎裂,更新到一個棧溢出抑制器的 bud 鏈,觸發(fā)更新抑制,后續(xù)邏輯被打斷了。我們就成功破壞掉了講臺,而保留了它的方塊實體。
這個方法需要 tick freeze,并不是很生存友好。我們有下面兩個替代的方法:第一個方法是這樣的,請看下面動圖:

我們翻書的時候,下面的鐵軌亮起,觸發(fā)了更新抑制。這是早于講臺啟動狀態(tài)解除的計劃刻被計劃的,那么這個計劃刻就不再有了,講臺變成了一個常亮的狀態(tài),這個時候我們等抑制器復(fù)位,然后直接拆掉講臺就可以了。這種方法適合 1.18- 的版本,因為棧溢出更新抑制仍然適用,我們可以承擔(dān)一次額外的更新抑制。
在 1.15 和 1.18 之間的某個版本之后,放了書的講臺被拆掉,會在刪掉自己的方塊實體之前發(fā)出一次比較器更新。因此,在至少 1.18(1.15 不行,之間的版本沒測試過),還可以這樣做:

現(xiàn)在,我們在保留方塊實體地拆掉講臺之后,在那個位置放一個潛影盒上去,就可以得到一個有講臺方塊實體的潛影盒了。這就是我們的強(qiáng)轉(zhuǎn)抑制器的核心,更新一個從它這里取輸出的比較器,就會觸發(fā)基于 CCE 的更新抑制!

三、強(qiáng)轉(zhuǎn)抑制器對物品分身的應(yīng)用、強(qiáng)轉(zhuǎn)抑制器的關(guān)閉
我們說過,強(qiáng)轉(zhuǎn)抑制器的觸發(fā),是在從魔法盒中取輸出的比較器計算自己的能量的時候觸發(fā)的。但是,比較器收到比較器更新,也會計算自己的能量啊,也會觸發(fā)更新抑制。也就是說,我們可以在 1.11-1.17 這段版本,不需要放置浮空比較器,而快速地制作物品分身!請看下面視頻:https://www.bilibili.com/video/BV1dh411K7uC
當(dāng)物品被放進(jìn)投擲器,投擲器物品欄發(fā)生變化,發(fā)出比較器更新,抑制器中的那個比較器重新計算自己的能量,更新抑制觸發(fā)!我們就完成了一次物品分身。連續(xù)這樣操作,我們就可以以極為快速的方式制作物品分身,這是目前已知的最好的物品分身制作的方法。
我之前說過,強(qiáng)轉(zhuǎn)抑制器是無狀態(tài)的,更新到比較器就拋異常,但是這也不絕對。異常的觸發(fā)是在計算潛影盒輸出的時候發(fā)生的,那么如果比較器受到更新可以不計算這個輸出,就不會拋出異常了。怎么做呢?我們可以對比較器后面的固體方塊用紅石粉施加 15 的能量,這樣的話,比較器看到 15 就被覆寫了,不再會檢測容器,就不會拋異常了,強(qiáng)轉(zhuǎn)抑制器就被關(guān)閉了。

如圖所示,燈亮,抑制器啟動,燈滅,抑制器關(guān)閉。另外,強(qiáng)轉(zhuǎn)抑制器的工作是需要用比較器從潛影盒取輸出的,所以說如果把比較器拆了,只有潛影盒在那里,也是安全的,不會觸發(fā)更新抑制。
四、強(qiáng)轉(zhuǎn)抑制器對 1.19 的影響
1.19 更新,最受技術(shù)玩家詬病的改動,就是棧溢出更新抑制的修復(fù)了。但是,我們有辦法將更新抑制奪回來!潛影盒的那段代碼是麻將的祖?zhèn)鞔a,從 1.11 到現(xiàn)在的最新版本 1.20.1 從未變過,這意味著強(qiáng)轉(zhuǎn)抑制器直到最新版本都可用!
這可不是一臺更新跳略器,這是一臺貨真價實的更新抑制器!
在 1.19,我們已經(jīng)有了基于內(nèi)存溢出的更新抑制器,但是它極其不友好,抑制一次,服務(wù)器需要卡頓五分鐘,如果用來切門的話顯然力不從心。但是強(qiáng)轉(zhuǎn)抑制器不一樣,它運(yùn)行起來幾乎沒有卡頓,和以前的棧溢出抑制器一樣絲滑,這意味著 1.19 也可以大規(guī)模使用更新抑制,進(jìn)行雕花下界門之類的應(yīng)用。
但是建造強(qiáng)轉(zhuǎn)抑制器在 1.19 并非易事,因為用到的方塊實體替換需要一臺真正的更新抑制器。為了在 1.19 的服務(wù)器使用強(qiáng)轉(zhuǎn)抑制器,你有兩個選擇——其一,使用一次內(nèi)存溢出更新抑制實現(xiàn)方塊實體替換,建造第一臺強(qiáng)轉(zhuǎn)抑制器,然后用已有的強(qiáng)轉(zhuǎn)抑制器進(jìn)行更新抑制,制造更多的強(qiáng)轉(zhuǎn)抑制器。其二,在 1.18- 的版本用棧溢出抑制器建造好你的強(qiáng)轉(zhuǎn)抑制器,然后升版本升到 1.19。內(nèi)存溢出更新抑制較為麻煩,但是你只需要實施它一次,所以可以接受,況且伊鴿納他們也即將發(fā)布更便宜的內(nèi)存溢出更新抑制的方法了。
等到那時候,1.19 的服務(wù)器可以花幾個小時的時間操作第一次內(nèi)存溢出,解鎖強(qiáng)轉(zhuǎn)抑制器的科技,然后更新抑制將像以前的版本一樣,滿血復(fù)活。