执行统计
命令执行的时候会统计影响到的东西数量、成功执行次数与执行结果。
所有统计都是整数,储存在NBT里,需要时可以直接用nbt查询或以stats
命令绑定到 指定玩家名/实体 的记分板分数。
注意: 之后的执行结果会覆盖之前的数值。这在进行execute
命令嵌套的时候,往往会让人搞错每一层嵌套涉及到的实体的最终统计值究竟来自哪一层命令。
执行统计包括:
- 成功执行次数(SuccessCount),如
testfor @a {SelectedItem:{id:"minecraft:stone"}}
的符合玩家数量。 - 影响实体数(AffectedEntities),如
testfor @a
选择到的玩家数量。 - 影响方块数(AffectedBlocks),或是探测到的方块数。如
testforblocks
或clone
探测/复制到的方块数。 - 影响物品数(AffectedItems),或是探测到的物品数。如
clear
成功清除/检测到(如果最大清除数量设置为0)的物品数量。 - 查询结果(QueryResult),部分查询类的命令会把结果写进这统计里,如
time query gametime
、worldborder get
等。
命令执行过程
当游戏需要执行一条命令的时候,它会作出以下处理:
- 把命令以空格为分隔符分成不同的命令参数,然后在命令列表里搜寻第一个参数所代表的命令。即,如果命令第一个空格前的部分为
scoreboard
,则将判断这是一条记分板命令。如果没有对应的命令,则报错“未知命令”。 - 尝试判断这条命令是否对多个目标执行,实现方式是找出命令中第一个可选择多个目标的目标选择器。具体过程如下:
- 检测目标命令参数在命令的格式中是否是一个实体参数。(比如
give @a stone
的第二个参数@a
)。 - 如果是,则检查此命令参数是否是一个选择器。
- 如果是,则检查此选择器最多能选择到实体的数目是否为非1(
@a
和@e
以外的默认选择1个。如果存在c
参数则看c
参数的数量。)
例子:@e ≠ 1
@e[c=1] = 1
@r = 1
@r[c=5] ≠ 1
@a[c=-5] ≠ 1
- 如果是,则代表找到了第一个选择多目标的目标选择器,即该命令对多实体执行,搜寻终止;否则则以下一个命令参数为目标继续重复1-3步,直到找到满足条件的命令参数或遍历完全部命令参数。
- 检测目标命令参数在命令的格式中是否是一个实体参数。(比如
- 如果经由第二步的判断得到这是一个多目标的命令,则根据找到的目标选择器搜寻所有满足条件的实体并排序,然后逐个替换具体实体至选择器位置。之后,尝试逐条执行替换后的命令。
比如
give @a[tag=stone] stone
游戏会先把命令分为不同的参数,就是 give
、@a[tag=stone]
、stone
。
然后我们找到的第一个可选择多个目标的选择器参数就是@a[tag=stone]
。
假设当前世界有5个玩家,都有stone
这tag,分别为a
、b
、c
、d
、e
。假设离执行坐标的距离是a
最近,b
次之,如此类推。
那么那命令会依次被替换为:
give a stone
give b stone
give c stone
give d stone
give e stone
并且依次被执行。
由于此原则,一些命令如
scoreboard players operation
就有一个很大的限制:两个目标之间必须有一个只选择一个目标,并且必须写清楚是选择一个。
虽然替换的过程在一条一般命令中只会替换一次,但在一些复合命令(命令嵌套)中会替换多次。例如execute
嵌套,就很容易出现需要多次选择不同实体的情况。根据这个替换逻辑可以看到,实际上执行的命令数增加的很快。
成功次数、实体数
好了,就算我们知道这命令执行机制,那么又如何呢?
其实SuccessCount与AffectedEntities和第一个可选择多个目标的目标选择器
有关。
- AffectedEntities:
第一个可选择多个目标的目标选择器
选择到的实体数。不论之后的命令是否成功执行。这个在命令执行前就会被设置。 - SuccessCount: 命令被替换后成功执行的次数。不多于AffectedEntities数值。这个在命令执行后才会被设置。
故此我们如果要知道真正被影响的实体数,或许我们需要使用SuccessCount。
例外情况
spreadplayers
及scoreboard teams join
命令都是例外情况,其SuccessCount最大为1,其AffectedEntities才是受影响的实体数。
因为此命令不定义所谓的选择器参数,因此游戏不会对选择器进行替换。(因为此命令有特别处理选择器的方式)
参见
会Java的朋友如果想看源码,请看mcp的
net.minecraft.command.CommandBase
里,有一个isUsernameIndex
的函数。这会告诉CommandHandler
哪儿是实体目标。net.minecraft.command.CommandHandler
。这是上面说的命令执行逻辑部分。net.minecraft.command.EntitySelector
。这里的matchesMultiplePlayers
定义了怎样的选择器才算是只选择一个目标。
stats的例子: