第七十四章 探究:execute的運行流程
(注:非常不推薦在手機上閱讀此章節(jié),請使用平板或電腦閱讀此章)
?。ū菊掠玫搅舜罅康淖址嫞锌赡軙霈F(xiàn)嚴(yán)重的錯位情況,可手動調(diào)整字體和大小至最佳狀態(tài))
?。ù苏鹿?jié)已于2022年7月17日重寫)
在第六十九章,我為了提醒你注意各個子命令的順序,專門舉了個例子:
/execute as @e at @e run tp @s ~~1 ~
但是你是否有注意到游戲運行這條指令的過程:
①將玩家傳送至玩家上方1米的位置(玩家此時抬高了1米)
?、趯⑼婕覀魉椭链迕裆戏?米的位置(玩家此時位于村民上方1米)
③將玩家傳送至羊上方1米的位置(玩家此時位于羊上方1米)
?、軐⒋迕駛魉椭镣婕疑戏?米的位置(村民此時位于玩家原本位置上方1米,玩家此時位于羊上方1米)
?、輰⒋迕駛魉椭链迕裆戏?米的位置(村民此時位于村民原本位置上方1米,玩家此時位于羊上方1米)
⑥將村民傳送至羊上方1米的位置(村民和玩家此時位于羊上方1米)
?、邔⒀騻魉椭镣婕疑戏?米的位置(村民和玩家此時位于羊原本位置上方1米,羊位于玩家原本位置上方1米)
?、鄬⒀騻魉椭链迕裆戏?米的位置(村民和玩家此時位于羊原本位置上方1米,羊位于村民原本位置上方1米)
?、釋⒀騻魉椭裂蛏戏?米的位置(村民、玩家和羊此時都位于羊原本位置上方1米)
這個過程有何特殊的呢?
你仔細(xì)看看第④、⑤、⑦、⑧和⑨條過程,你有沒有什么發(fā)現(xiàn)?
當(dāng)游戲?qū)⒋迕駛魉椭镣婕疑戏?米的位置時,雖然玩家已經(jīng)被傳送至了羊上方1米的位置,但游戲仍然將村民傳送至玩家原本位置上方1米,而不是羊上方2米的位置。
這是怎么回事?
我們設(shè)玩家(2,2,2)為A、村民(3,2,3)為B、羊(4,2,4)為C,游戲在運行execute時,其實它的流程是這樣的:
execute---A---------B---------C
游戲先解析as @e,得到了上面的三個目標(biāo)。
execute---A---------B---------C
------------↓---------↓----------↓
---------2·2·2-----2·2·2-----2·2·2
------------↓---------↓----------↓
---------3·2·3-----3·2·3-----3·2·3
------------↓---------↓----------↓
---------4·2·4-----4·2·4-----4·2·4
然后游戲會解析at @e,預(yù)先將實體的位置記錄下來。上面為了方便展示,用x·y·z來表示坐標(biāo)。
execute---A------------------------B-----------------------C
------------↓-------------------|----↓------------------|-----↓
---------2·2·2—3·2·3—4·2·4-|-2·2·2—3·2·3—4·2·4-|-2·2·2—3·2·3—4·2·4
------------↓-------↓-------↓---|----↓------↓-------↓---|----↓-------↓-------↓
-----------①------②------③---|---④-----⑤------⑥---|---⑦------⑧------⑨
?、伲?tp 玩家名 2 3 2
?、冢?tp 玩家名 3 3 3
?、郏?tp 玩家名 4 3 4
④:/tp 村民UUID 2 3 2
?、荩?tp 村民UUID 3 3 3
?、蓿?tp 村民UUID 4 3 4
?、撸?tp 羊UUID 2 3 2
⑧:/tp 羊UUID 3 3 3
?、幔?tp 羊UUID 4 3 4
接下來游戲會解析run tp @s ~~1 ~,根據(jù)三要素,將其中的目標(biāo)選擇器和相對坐標(biāo)等參數(shù)具體化(但計分板分?jǐn)?shù)之類的不會具體化,因為沒必要),得到具體的指令(如上)。
最后,游戲運行具體的指令,也就是本章最開頭的那九個過程。
其中,最重要的,也是最關(guān)鍵的一點,就在于execute指令解析at @e的過程。
execute并不是說運行一次解析一次,而是先全部解析了再運行,所以并不會使得『村民傳到玩家傳送過后上方1米的位置』之類的事情發(fā)生。
能理解吧?
那問題來了,如果execute再套一個execute會發(fā)生什么?比如我們將上述指令寫成:
/execute as @e run execute at @e run tp @s ~~1 ~
其實效果還是一樣的,具體原因就等你自己去推導(dǎo)吧,按照我上面的流程去推導(dǎo)。
這就是Java execute 1.13+版本的運行流程。如果你還不懂,我們再看一個簡單并且效果比較明顯的例子。
設(shè)有盔甲架A和B,分別位于主世界的(40,-60,29)和(42,-60,29)??准蹵的生成時間比盔甲架B更早,已加載區(qū)塊中沒有其他盔甲架。在盔甲架A、B旁運行如下指令:
/execute as @e[type=minecraft:armor_stand] at @s run tp @e[type=minecraft:armor_stand,distance=1..3]~~10 ~
讓我們分析一下,運行上述指令會發(fā)生什么。
首先,如果我們按照正常的思維去分析這條指令,就會得到以下結(jié)果:
A會先將B傳送到自己上方10米的位置,B由于處于那個位置無法選取到A來傳送,最終僅僅B會被傳送到A的上方10米處。
但其實,如果你真的去運行這條指令,就會發(fā)現(xiàn)A和B都會被傳送到對方原位置的上方10米處。
為什么?我們按照游戲的思維分析一下就可以了:
execute---A---------B
游戲先解析as @e[type=minecraft:armor_stand],得到了上面的兩個目標(biāo):盔甲架A和盔甲架B。
execute---A--------------B
------------↓--------------↓
-------40·-60·29-----42·-60·29
然后游戲會解析at @s,預(yù)先將實體的位置記錄下來。
execute---A--------------B
------------↓--------------↓
-------40·-60·29-----42·-60·29
------------↓--------------↓
-----------①-------------②
?、伲?tp 盔甲架B的UUID 40 -50 29
?、冢?tp 盔甲架A的UUID 42 -50 29
接下來游戲會解析run tp @e[type=minecraft:armor_stand,distance=1..3]~~10 ~,根據(jù)三要素,具體化指令,得到具體的指令。由于此時還未傳送,所以目標(biāo)選擇器會分別選擇到『盔甲架A』和『盔甲架B』。
最后,游戲按照順序執(zhí)行指令,分別將盔甲架A和盔甲架B傳送到對方上面10米高的位置。
這個例子比較簡單,你應(yīng)該能夠理解吧?
所以你明白了嗎?
上面講的是Java1.13更新后的execute指令其運行的具體流程,那么Java1.13更新前的呢?以及基巖版的呢?
2016年6月22日,MCBBS大佬pca006132在『礦工茶館』發(fā)布了一個猜猜樂(ID:594475),大致的問題如下:
execute @e ~~~... summon ArmorStand,這個指令在初始實體不同數(shù)目的時候出來的結(jié)果是什么
沒想到竟然沒人能夠解答這個問題,于是這位大佬在次日講解了這個問題(帖子ID:594698)。他舉了一個簡單的例子:
當(dāng)初始實體數(shù)為2時,運行execute @e ~~~ execute @e ~~~ summon Armorstand
這個例子的結(jié)果竟然是8。
那如果在相同的初始情況下,運行execute @e ~~~ execute @e ~~~ execute @e ~~~ summon Armorstand,即套了三個execute的指令會發(fā)生什么?
答案是:2048。
很令人震驚??!那為什么會這樣呢?
如果我們在Java1.13及以上版本,運行類似的指令,將達(dá)不到一樣的效果,因為在Java1.13之前,execute的運行邏輯是完全不一樣的。
那么到底是個怎么個邏輯法呢?其實在Java1.13前,execute并不會在運行前先存好各種數(shù)據(jù),而是運行一遍解析一遍。以上面那個嵌套了3層execute的指令為例子,我們來解析一下。
條件:初始兩個實體A(1,2,1)和B(2,2,2),A比B離執(zhí)行地點更近。
execute---A----------B
------------↓
----------1·2·1
游戲先解析第一個『execute @e ~~~』,得到了上面的結(jié)果。后面我們將會忽略執(zhí)行地點,因為這邊不需要考慮執(zhí)行地點的影響。
execute---A----------B
------------↓
---------A——B
游戲按照順序,先以A為執(zhí)行者運行指令,并解析了第二個『execute @e ~~~』,得到了上面的結(jié)果。
execute---A----------B
------------↓
---------A——B
---------↓
------A——B
游戲按照順序,再次以A為執(zhí)行者運行指令,并解析了第三個『execute @e ~~~』,得到了上面的結(jié)果。
execute---A----------B
------------↓
---------A——B
---------↓
------A——B
------↓-----↓
------C-----D
第三個execute運行指令,產(chǎn)生了新的盔甲架C和D。
execute------A----------B
---------------↓
---------A————B
---------↓---------↓
--------+2---B—A—C—D
游戲回到第二層execute,以目標(biāo)選擇器順序選取B為執(zhí)行者,由于之前已經(jīng)生成了C和D,所以B運行第三層execute指令時,會選取到4個實體來運行指令,最終實體數(shù)量+4(現(xiàn)在為8=2+2+4)。
execute---A-----------------B
------------↓-----------------↓
----------+6----B—A—C—D—E—F—G—H
游戲回到第一層execute,以目標(biāo)選擇器順序選取B為執(zhí)行者。由于已經(jīng)有了八個實體,因此這一次第二層execute會選取到八個實體來運行第三層execute。
execute---A-------------------------B
------------↓-------------------------↓
----------+6----B——A——C——D———E———F———G———H
-----------------↓-----↓-----↓-----↓-------↓-------↓-------↓-------↓
--增加實體數(shù)---+8--+16--+32-+64--+128--+256---+512--+1024
--增加后數(shù)量----16---32---64---128----256---512----1024---2048
隨后,游戲按照順序依次以這八個實體運行指令,實體數(shù)量在此過程中快速增長,最終變?yōu)?048。
不難發(fā)現(xiàn),每一次第三層的execute指令被運行,都會將當(dāng)前實體數(shù)量×2,而上面一共運行了10次第三層的execute,相當(dāng)于2被乘以了10次2,也就是2×2×2×2×2×2×2×2×2×2×2,即2的11次方,結(jié)果為2048,即2048個實體。
實在是太令人驚訝了是不是?在Java1.13以下的execute指令中,execute僅僅會在被選取的執(zhí)行者開始執(zhí)行指令時才會進(jìn)行下一步的解析動作,而且不會一下子就將所有執(zhí)行者運行指令的情況全部解析出來再運行指令。
所以,Java1.13對execute的改動不僅僅是格式上的,還有運行流程上的改動。
如果你并不能很好理解上面為什么會由2個實體產(chǎn)生出2048個實體,別擔(dān)心,我們繼續(xù)以剛才兩個盔甲架互相傳送為例子,看看類似的指令在Java1.13以下的版本有何不同的效果。
還是設(shè)有盔甲架A和B,分別位于主世界的(40,60,29)和(42,60,29)??准蹵的比盔甲架B更靠近執(zhí)行地點,已加載區(qū)塊中沒有其他盔甲架。在盔甲架A、B旁運行如下指令:
/execute @e[type=armor_stand]~~~ teleport @e[type=armor_stand,r=3,rm=1]~~10 ~
游戲先解析『execute @e[type=armor_stand]~~~』得到如下結(jié)果:
execute---A----------B
------------↓
-------40·60·29
然后以A為執(zhí)行者,解析『teleport @e[type=armor_stand,r=3,rm=1]~~10 ~』,得到了如下指令:
/teleport 盔甲架B的UUID 40 70 29
運行上述指令,盔甲架B被傳送至(40,70,29)處。隨后游戲以B為執(zhí)行者,先解析執(zhí)行地點參數(shù)『~~~』,得到如下結(jié)果:
execute---A------------------B
------------↓------------------↓
-------40·60·29---------40·70·29
--將B傳送至40·70·29
接下來,游戲以B為執(zhí)行者,再次解析指令,得到如下內(nèi)容:
選擇器'@e[type=armor_stand,r=3,rm=1]'什么都沒找到
沒錯,由于B被傳送到了(40,70,29),因此目標(biāo)選擇器就選不到A,自然就無法執(zhí)行指令。最終,正如我們在最開始以正常思維分析的那樣,得到了如下結(jié)果:
A會先將B傳送到自己上方10米的位置,B由于處于那個位置無法選取到A來傳送,最終僅僅B會被傳送到A的上方10米處。
雖然在Java1.13更新后,我們的『正常思維』沒用了,但在Java1.13以下版本還是很準(zhǔn)的。
那在基巖版呢?
作者在基巖版也測試過了(用的上面的兩個盔甲架tp法),確認(rèn)基巖版不管是舊版還是新版(1.19.10更新的)的execute,都是會得到和Java版1.13以下版本一模一樣的數(shù)據(jù)。
其中,對于目前還在測試的新版execute,用的是如下指令:
execute as @e[type=armor_stand] at @s run tp @e[type=armor_stand,r=3,rm=1]~~10 ~
也就是說,如果你在基巖版運行上面套了3層execute的生成指令,在初始實體數(shù)為2的情況下,也會得到有2048個實體的纟
......
......
......
:(
你的電腦遇到問題,需要重新啟動。
我們只收集某些錯誤信息,然后為你重新啟動。
......
......
......
——附錄:跟本章有關(guān)系的MCBBS帖子原鏈接
www.mcbbs.net/thread-594698-1-1.html
www.mcbbs.net/thread-594475-1-1.html
?。ㄉ鲜鎏泳驯籑CBBS論壇系統(tǒng)自動關(guān)閉)