本帖最后由 pca006132 于 2017-8-16 18:18 编辑

-------命令进阶-------
第三章 命令执行

本文首发于命令进阶教程全集
使用CC BY-SA-NC 4.0协议,转载请注明本页面链接


    既然本文件是一份介绍命令的教程,命令执行当然是必须介绍的。

    命令执行主要有三种。
    第一种为聊天栏执行,这也是我们测试少量命令时常用的方式。在那部分我们也会介绍如何阅读Log中命令有关的部分。
    第二种为命令方块执行,这是以前非常流行的方式,把命令写进方块里逐个执行。然而1.12由于命令函数(function)的出现,在大型地图里命令方块的出现频率已经有减少的趋势。然而小型系统继续使用命令方块也并无不可,甚至比较方便,因为不用修改存档文件内容,特别适合服务器使用。也更容易让新手掌握坐标的概念。之后的章节将会简单介绍命令方块。
    第三种为命令函数(function)执行,即把命令放在文件里然后用function命令执行,不需要放置大量方块,也有更好的优化。而且能够方便的调用别的命令函数,对大型系统更为友好。之后的章节将会详细介绍命令函数。
作为移植页面,格式可能会有偏差,请到教程全集中获得完整体验。
本页面可能随着版本推进而过期,内容以全集为准。




聊天栏

这个在屏幕左下角的就是聊天栏。
上方部分为聊天记录,实际上除了玩家之间的聊天以外也会记录很多不同资讯,如命令执行信息、错误, tellraw 命令信息等等。
当信息出现了一段时间之后就会慢慢消失,如果消息过多、过长也会导致之前的消息消失,需要玩家打开聊天栏(默认按 t/ 也能打开聊天栏)查看之前的信息。聊天记录的上翻条数是有限制的,超过限制的信息无法直接查阅,只会保存在log里,之后将会简介log。
下面该部分为输入框,默认可以按 t/ 唤出,你可以在其中输入文字。
输入文字之后按enter发送。如果文字以 / 开始则为命令(不理会开头那 / 执行),否则则为聊天信息,会被直接写进聊天栏并发布给其他玩家。
聊天栏是支持全选、复制等功能的。
比如Windows的话,全选为 Ctrl + A ,复制为 Ctrl + C ,黏贴为 Ctrl + V
MC内不允许直接输入 § 字符,请使用别的方式输入(如JSON内的 \u00a7 ,或使用命令函数等外部输入方式)

自动补全
输入命令时,按下 Tab 按钮可补全当前参数(选择器、NBT、JSON文本不支持补全)。
比如说在输入框里输入了 / ,然后按 Tab ,会出现这样的输出:

只要玩家没有按下其他按钮(切换视窗也是按下了其他按钮),再次按 Tab 时就会切换为下一个选项,比如上方的例子则从 /? 变为 /advancement
当玩家已经选择到最后一个选项了,再次按 Tab 按钮则会回到第一个选项。
如果玩家在按 Tab 前已经在当前参数输入了部分字符,按 Tab 则会寻找那些以当前字符开始的选项。
如输入了 /test 然后按 Tab 的话则只会出现 /testfor/testforblock/testforblocks 这几个选项。
这样的自动补全功能在需要输入较长的参数时是十分有效的。
适当使用自动补全可以加快命令编辑速度,并减少错误。
比如输入gamerule是超级容易出现大小写错误的。

命令执行失败信息
在聊天栏执行的命令如果出错就会显示执行失败信息。
有时它会直接指出命令中的错误,比如:

当命令无法找到目标实体时也会显示失败信息。




执行失败信息也有一种特别用途:查看NBT。
entitydatablockdata 命令在没有修改目标NBT时会当作执行失败并显示目标的NBT标签,因此我们能够如此查看NBT:
  1. /entitydata @e {}
复制代码
输出:

红色一大片,幸好NBT不算太复杂还能看。
之后的章节将会介绍怎么把这输出抽取:使用Log及命令方块。

Log
Log文件位于 /.minecraft/logs/ 里,一般我们要看最新的记录的话都会看 latest.log
Log文件本质上是一个纯文本文件。Log文件记录了游戏内各类输出和重要事情的详细资料、时间,比如说是加载文件错误、聊天信息、命令输出等等。



比如加载文件错误,可能会出现类似这样的信息(太长了只放上面一部分):
  1. [19:20:09] [Server thread/ERROR]: Couldn't read custom function abcd:test2 from C:\Users\Administrator\AppData\Roaming\.minecraft\saves\TEST\data\functions\abcd\test2.mcfunction
  2. java.lang.IllegalArgumentException: Unknown or invalid command '?scoreboard'
  3.     at bm.a(SourceFile:39) ...
复制代码
看上去挺吓人的,其实资料非常容易看:
  • 时间:时间就是[19:20:09],也就是当天的晚上七点二十分
  • 这是一个错误:[Server thread/ERROR] 这里都写着是Error了
  • 啥错误:Couldn't read custom function abcd:test2 from (路径)。也就是说不能加载在(路径)的自定义命令函数 abcd:test2
  • 错误原因: Unknown or invalid command '?scoreboard'。未知或错误命令 ?scoreboard
其实这是一个挺常见的问题。这是因为命令函数文件选择了 UTF-8 with BOM ,然后文件开头多了3个Byte,游戏就不认了。解决方法为...用 UTF-8 without BOM 储存。




命令输出、错误及所有聊天栏输出都是这样的:
  1. [13:18:21] [Client thread/INFO]: [CHAT] The data tag did not change: {HurtByTimestamp:0,Attributes:[{Base:30.0d,Name:"generic.maxHealth"},{Base:0.0d,Name:"generic.knockbackResistance"},{Base:0.699999988079071d,Name:"generic.movementSpeed"},{Base:0.0d,Name:"generic.armor"},{Base:0.0d,Name:"generic.armorToughness"},{Base:16.0d,Name:"generic.followRange"}],APX:5,Invulnerable:0b,FallFlying:0b,APY:81,APZ:6,PortalCooldown:0,AbsorptionAmount:0.0f,FallDistance:0.0f,DeathTime:0s,AttachFace:2b,HandDropChances:[0.085f,0.085f],PersistenceRequired:0b,Peek:0b,Motion:[0.0d,0.0d,0.0d],Leashed:0b,UUIDLeast:-7415565809278513862L,Health:30.0f,Color:0b,LeftHanded:0b,Air:300s,OnGround:0b,Dimension:0,NoAI:1b,Rotation:[180.0f,0.0f],HandItems:[{},{}],ArmorDropChances:[0.085f,0.085f,0.085f,0.085f],UUIDMost:-2813612295629749725L,Pos:[5.5d,81.0d,6.5d],Fire:0s,ArmorItems:[{},{},{},{}],CanPickUpLoot:0b,HurtTime:0s}
复制代码
[Client thread/INFO]: [CHAT] 代表了后面这段信息是在聊天栏里的。
由于是在文件里,我们可以直接复制这段文字然后去做别的处理。


命令方块
命令方块(Command Block, 简写: CB)就是一种能设置命令、执行命令的方块。
命令方块只能通过命令给予物品/放置。
常用命令:
  1. /give @p command_block
复制代码
命令方块执行命令与玩家/实体执行类似,也会储存命令执行统计。
然而某些默认针对执行玩家的命令(如 clear 命令)在命令方块执行时必须指定执行玩家,否则无效。
命令方块执行命令可以避免每次执行时手动输入的麻烦。(当然,第一次你还是得人手输入,除非是OOC)
也能在短时间执行大量命令,比如在1 gt内执行上千条命令。
然而已经逐渐被命令函数取代。
如果实际上需要运行大量命令的话,还是建议用function的。

有三种命令方块:脉冲命令方块(Impulse Command Block,简写: ICB),链锁命令方块(Chain Command Block,简写: CCB),及循环命令方块(Repeating Command Block,简写: RCB)。
三种命令方块具有不同的性质,但能够在命令方块的GUI里切换模式,不需要独立获取别的命令方块物品。

计划刻
要理解命令方块的运作,你需要先了解计划刻(Next Tick Entry)。
计划刻其实就是一个列表,里面储存着“预期在未来应当行动”的方块的坐标。当然,我们在大多数情况下只关心命令方块在其中的顺序。
比如有3个命令方块,我依次激活a、b、c,列表就会依次储存a,b,c。
然后下一个游戏刻的时候,mc就会根据计划刻里的位置,依次执行a,b,c里的命令。
三种命令方块
概念澄清: 激活≠执行。
激活是命令方块收到红石信号,或从auto:0b变为auto:1b的时候。
执行是命令方块执行命令的时候。
从时间的角度来看,对ICB及RCB来说,两者是相差1gt的(激活后1gt才执行)
对CCB来说,激活了也不知道会不会执行,得看是否满足执行条件。
脉冲命令方块

脉冲命令方块(Impulse Command Block,简写: ICB)被激活之后1gt会尝试执行里面的命令一次(视乎是否条件制约)。激活的时候也会让指向的链锁命令方块加入计划刻。
注意: ICB在本gt会否执行命令是看前1gt有没有被激活而不是本gt。这和计划刻有关。其运作原理如下。
首先,ICB在被激活的时候,mc会将其坐标加入计划刻。
下一gt的时候,mc会根据计划刻依次让指定坐标的cb执行命令。
因此,ICB的执行顺序并不是视乎坐标,而是看激活次序。这个可以通过一个简单的实验证明:
把一串ICB排成一行,每个里面的命令为say (第几个cb),并放进marker实体(tag=marker)。然后分别在第一个cb和最后的cb的位置执行
  1. execute @e[tag=marker] ~ ~ ~ blockdata ~ ~ ~ {auto:1b}
复制代码
之后会发现两次的执行顺序刚好相反,分别是1 2 3 4 5 6...n和n n-1 ... 3 2 1。
原理是:execute是根据选择到的实体顺序执行命令的,而选择器是根据距离选择实体的。而加入计划刻的时间也是视乎激活时间,也就是执行 blockdata 的时间
故此在第一个命令方块的位置执行时,会先激活第一个,然后第二个,如此类推到最后一个
在最后一个命令方块的位置执行时,会先激活最后一个,然后倒数第二个,如此类推到第一个。

链锁命令方块

链锁命令方块(Chain Command Block,简写: ccb)会在接收到来自指向它的命令方块的信号后(icb及rcb激活/循环后也会向着指着的方向发送信号),会尝试执行命令并传递信号给指向的链锁命令方块。
CCB执行命令时会:
  • 将指向的下一个CCB加入执行命令的检查链
  • 检查是否激活,条件制约等限制条件
  • 如果前者满足执行条件则执行命令
不过,默认每个CCB在1gt只能执行一次命令

进阶: 我们能够在执行的时候在后方接上CCB,以达到瞬间调用模组或循环等的需求。(默认一个信息只能传递65536次,可以通过修改 gamerule maxCommandChainLength 改变)
有两种方法: 使用结构方块放置或 clone 命令。
clone的好处为只需要一条命令,而结构方块的好处是不需要绝对坐标。
而且我们能够让CCB做成循环,方法就是让CCB链的尾端指向第一个CB,而且将CB的 UpdateLastExecution 设置为0b(即命令链的环)。
不过,由于function的出现,我们能更方便的做到这些事情,详情请见下一页

循环命令方块

循环命令方块(Repeating Command Block,缩写: rcb)被激活时会把自己加入计划刻,之后的1gt执行,执行后检查自己是否激活,是的话就加入计划刻,不停循环。
因此,可以看成被激活时(被激活后1gt开始)会不停执行命令,速度为每gt一次,也就是20hz(20次每秒,理想情况下)
所以,rcb适合使用在高频的系统里。
然而,这导致了一个问题: 在rcb之后执行的cb如果关掉了rcb,rcb由于计划刻不会删除,因此还会执行多一次。
因此一些需要检查条件决定是否继续执行的系统,或许需要使用icb以确保不会出现意料之外的执行。
条件制约
当命令方块是条件制约(conditional,简写: cond,block states:  conditional:true )的时候,它会检查指着它箭头尾部的命令方块在本gt有没有成功执行命令
当左边的成功,右边的也能执行





即使左边的成功,右边的也不能执行,因为左边的没有指着右边的cb的箭头尾部


如果本gt指着它箭头尾部的命令方块没有执行过或是执行失败(消失了也不行...),则不会执行。
避免使用长串的条件制约以模拟if,以免中间某些命令因某些原因失败导致后面的全部都不能执行。详见基础逻辑中的布尔逻辑运算。
使用条件制约时,请确保自己清楚里面的命令的成功、失败条件是什么,以免出现意外情况。

保持开启
NBT相关知识请参阅后方NBT章节

保持开启(NBT:  auto:1b )就是模拟命令方块被红石激活的状态,能够激活命令方块。
ICB只会在auto从0b转为1b之后1gt执行命令(可以是1gt内先设置为0b然后1b)。
RCB就是当auto为1b的时候(当然,算上计划刻的话会有点延迟)就会执行命令。
CCB则是在检查是否执行的时候检查auto/激活,只要auto为1b或者被红石激活了就会执行命令(可以在执行前的瞬间激活,不需要提早1gt)
因此,CCB经常会设置为auto:1b(实际上CCB的默认模式就是auto:1b),因为这比较方便而且能节省空间(ccb能拐弯叠在一起而之间不需要有空隙放置红石块)。
除此以外,某些系统会以marker定位cb的位置,并以auto来控制CCB是否执行及ICB是否在下1gt执行。
*OOC
即Only One Command,通过一条命令执行大量命令/放置大量命令方块。能够方便的导入命令到世界里。
这里只会简单的讲解一下其原理。实际做法可以有很多, 也有很多优化的方法。
OOC一般是通过矿车骑矿车骑矿车...一直骑下去(或者是一堆矿车骑着掉落沙), 每个矿车都是命令方块矿车, 而里面有着需要执行的命令, 而最底下是掉落沙状态的红石块和激活铁轨。
OOC是一条summon命令, 上面骑着那么一大堆东西。在生成时, 掉落沙很快会掉到地上变成方块, 而上面的矿车就会掉到方块上, 被激活并执行命令。
OOC充分利用了MC每条命令最多有32767(虽然经常没达到这个极限就炸了)的字数极限, 一次导入一大堆命令, 很大程度上方便了在文字档案里写命令而不是在MC里直接写的玩家。同时,也有不少玩家利用这个特性来分享命令模块。
设置
需要设置部分游戏规则以让玩家能愉快的玩命令方块。
  1. gamerule commandBlockOutput false
复制代码
避免命令方块执行结果刷屏



  1. gamerule logAdminCommands false
复制代码
避免在服务器里刷爆log...

命令函数
命令函数(Function,Wiki译名为函数),是1.12新加入的功能。用途为在被调用的gt执行命令函数文件内的全部命令,且支持无延迟调用别的命令函数。
本章节将会分为三部分:
  • 命令函数文件
  • 命令函数命令
  • 命令函数常用逻辑
文件
命令函数是一个文本文件,编码为UTF-8无BOM,后缀为 .mcfunction
放在 /.minecraft/(存档)/data/functions/(命名空间)/(任意路径)/(名称).mcfunction (任意路径可以为空)。
命名空间、文件夹名称、文件名称建议为小写英文字符(游戏无论大小写也一律会当作小写),字词之间以 _ 分隔。
例子(从 functions 文件夹开始):
  • /test/a/b/c.mcfunction 。即命名空间为 test ,路径为 a/b/c.mcfunction
  • /test2/a.mcfunction 。即命名空间为 test2 ,路径为 a.mcfunction
  • /a.mcfunction错误,必须放在某个命名空间之内。
可以通过在cmd里输入 type 文件名称 ,查看输出,来分辨文件是否无BOM
如果有BOM,第一个字符会为 ? 或者是乱码,无Bom则为正常的字符。

函数名称
命令里经常需要使用函数名称,由命名空间及路径组成。格式为:
  1. (命名空间):(路径,删去后缀名)
复制代码
在上方的例子中,前两个命令函数的函数名称分别为:
  • test:a/b/c
  • test2:a
在命令中使用时,函数名称无视命名空间和函数名的大小写。
战利品表(Loot table)以及进度(Advancement)也是使用这个格式。

文件内容
文件内容为多条命令,每条命令占据一行(不能/ 符号作开始),空行及注释会被忽略。
注释:以 # 作开始的行,如 #这是一条注释
空行会被忽略
例子:
  1. # test:1
  2. say test
  3. say 1

  4. setblock ~ ~-1 ~ stone
复制代码
命令
关于执行点,请参见 命令执行

高频执行函数
  1. gamerule gameLoopFunction <函数名称>
复制代码
每gt执行一次指定命令函数(于gt完结时执行,故一些数值如 HurtTime 只能检测到9)。
注意,执行坐标为(0, 0, 0),权限等级为2,即无法执行 op 这类命令。

执行单一函数一次
  1. function <函数名称> [<if|unless> <选择器>]
复制代码
  • 函数名称: 需要执行的命令函数的函数名称(见上方)。
  • if: 如果后方选择器选择到任何目标就执行命令函数。
  • unless: 如果后方选择器选择不到目标就执行命令函数。
  • 没有if/unless: 执行命令函数。
可以在一个命令函数中无延迟调用别的命令函数,命令函数里的命令的执行坐标为当前这条 function 命令的执行坐标。
即使命令函数里曾经利用如 tp 命令修改执行者坐标,其他命令的执行坐标也不会改变。要确保能获得执行者最新坐标的话请使用 execute 命令。

也能够调用自己(递归,recursion),然而切记添加跳出条件,否则便会出现无限循环(仍受 gamerule maxCommandChainLength 执行命令数限制,然而终究是不好的)。
当目标命令函数执行完毕后会回归到本命令调用后的位置然后继续执行。执行顺序如下图所示(从左上方开始):

所以输出为
  1. start a
  2. start b
  3. start b (这是recursion的部分)
  4. end b (这是recursion的部分)
  5. end b
  6. end a
复制代码
进度Reward
可以通过进度(Advancement)的reward对目标玩家执行一次命令函数。这会在进度章节说明。
限制命令执行数量
可以通过命令限制1次命令函数执行的总命令数量。
  1. gamerule maxCommandChainLength <数量>
复制代码
或许你们能够用这个来干掉别人的反作弊吧... 你们能够用这个来关掉失控了的系统。
除了这个以外我也没想到啥用途了。

逻辑
其实也没啥逻辑可言...就模块化而已,玩多了就熟了。
部分人生经验:
  • 类似功能的弄成一个命名空间
  • 一组达成一个功能的命令就放在一个命令函数里。经常需要调用的也可以放在一个命令函数里。
  • 尽量不要在主要高频的命令函数里写太多命令,最好是调用别的命令函数,这样会更容易维护。
  • 部分需要特定时候才执行的命令可以把那部分的命令放在单一命令函数,使用 if|unless ,以实体的tag/分数作控制。
  • 如果一个命令函数会被经常调用,但中间某部分命令会被替换:把前后的命令放进新的函数。
  • 递归(调用自己)切记要以 if|unless 进行调用。
  • 部分事件可以考虑使用进度(Advancement),详情见之后的事件章节。



这部分是给会编程的玩家看的。

命令函数是没有回传(Return)的,所以你不能提早完结命令函数。你只能把后面的放进别的命令函数放进别的命令函数里,看情况进行调用。



例子
本教程里需要使用命令函数的部分会在命令开始标识这是一个函数文件,比如:
  1. # test:test1

  2. say 这是一条命令
复制代码
这个就是放在 data/functions/test/test1.mcfunction 里的函数内容了。



详细例子可以参见 记分板登录系统进阶登录系统(递归地狱,不建议新手马上阅读)。[groupid=546]Command Block Logic[/groupid]