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