本帖最后由 Cat_Anchor 于 2026-7-10 16:10 编辑
7 月 6 日和 7 日是我的期末考试,很高兴我基本没有退步,保持了高二下学期期中的水平。 于是我重构了一遍万象添补中填充几何体的命令,打开 Minecraft,打算生成一些棱柱圆锥什么的玩玩。 
我填充了一个钻石块棱柱,好像没问题。重构很顺利。再试试 keep 模式…… 
也没问题。再填充一个棱锥,这次用绿宝石块…… 
似乎也很好。这时,我灵机一动:要是填充一个从地底一直伸到天穹的锥体,一定很壮观。于是我修改了命令,起始高度是相对向下 200 格,锥体高度是 200: fillpyramid ~~-200~ 16 200 8 emerald_block
没有进一步检查,我立刻执行了这条命令。聊天栏瞬间弹出提示,填充方块数量级轻松破万,而且很快又上了十万,二十万,三十万……我逐渐感到不对劲,怎么还没有完成?锥体怎么还没有刺破地表?按理说,一个八边形,每条边长 16,这应该不是什么非常大的面积——大约 1236 平方米。体积在八万左右。不会填充这么多。 我在聊天栏输入 -m 3,尝试调用早期开发遗留的功能,这会让自己进入旁观模式。跑到地下一看,确实有绿宝石块,已经出现在极深洞穴的底部了;但是再认真观察一下,我发现只有一层。 “为什么填充了这么久还只有一层?”这时,我注意到绿宝石块覆盖的范围非常广,不存在没有绿宝石块的地方。“难道是参数搞错了?”我打开聊天栏查看上次输入的命令。 
这时,我注意到参数顺序是“边长”“边数”“高度”,我把参数顺序搞错了。16 200 8 定义的是以每条边长为 16 的正 200 边形为底,8 为高的锥体。稍微计算一下,就知道体积在 200 多万的位置。 我等了很久很久,因为方块操作太多,而且在未加载区块中放置方块很容易导致线程挂起,我甚至能看到这样的景象: 
最后等了 4810.627 秒,填充了 2597228 个方块,操作才完成。平均速度是每秒 540 个方块。而且由于“在未加载区块放置方块”实际上是将放置结构的请求推入队列,又因为一个代码问题,完成之后结构就删掉了,最后也没有完全放置出来这个锥体。
以上,就是仅仅由一个错误参数引起的麻烦了。有什么办法,可以减少这些错误? 原版的命令,参数都是按照语法来的,多数情况下并不会过于复杂。但是像填充锥体这种命令,要在一行里挤下一堆定义锥体的参数,还是过于复杂了。如果可以把命令写成 fillpyramid ~~-200~ sideCount:16 sideLen:8 height:200 emerald_block 的形式,虽然要多写不少字,但是参数作用更清楚了。 有时候我们甚至希望向自定义命令传入 JSON 文本作为参数。原版的 /tellraw 命令支持这样做,而且输入 JSON 时不需要用引号括起来,JSON 中的引号也不需要转义。但是自定义命令不行,它的参数没有单独的“JSON 文本”类型,只有“字符串”类型。把 JSON 文本作为字符串参数传入命令的时候,我们需要用引号括起来,而且要转义。 让我们用最基本的 JSON 说明这一点。假设 /tellraw 的 JSON 参数支持这样写: {
color: 'red',
text: '我好饿'
}
那么有 /tellraw @s {"color":"red","text":"我好饿"}
非常简单,对吧。但是如果我们有自定义命令,比如 /announce <JSON:字符串>,那么这段 JSON 就要写成 /announce "{\"color\":\"red\",\"text\":\"我好饿\"}"
有了转义符,复杂度一下子就上来了。其实现在还好,但是稍微复杂一点,很容易陷入转义地狱——转义转义符的符号,转义转义转义符的符号的符号,…… 为了解决以上问题,隆重向你介绍—— Cat Anchor's data transfer formaT, *.cat
这是一种轻量数据交换格式。引用一段万象添补更新日志: - CAT(Cat Anchor's data transfer formaT)是一种自定义数据交换格式,用于解决命令参数过多而无法便捷输入的问题。CAT 格式与 JSON 相似,但不支持部分 JSON 支持的功能。
- 在 CAT 中,由于命令参数较为简单,我们不需要明确参数值的类型。因此,不需要引号来定义字符串。命令参数一般不包括空格、逗号、括号等特殊符号,因此它们仍然可以用于定界符。
- 书写 CAT 格式的数据时,使用 key: value 的格式。不需要为键或者值添加引号。同时,禁止键或值包含引号、括号、逗号、冒号、空格等特殊符号。
- value 可以是若干字符,若 value 可以解析为以下任意类型,则认为 value 为此类型,否则认为 value 是字符串。
- 浮点数或整数。不支持十六进制或科学计数法。
- 布尔值。可以将 true 写为 t,将 false 写为 f。
- 空值。这包括 null 和 undefined。
- 数组或对象。它们需要用 () 指定。无法直接判断 () 到底是数组还是对象,具体类型取决于它的内容。
- 如果 () 中包含键值对,则是对象;否则是数组。多个数组的元素或对象的键值对,都使用空格分隔。
- 例如,如果将一段 JSON 作为参数传入命令,那么参数应该这样写:
"{\"name\":\"idk\",\"color\":{\"red\":0.5,\"green\":0.4,\"blue\":0.7},\"scores\":[9,8,9.5]}"。如果使用 CAT 格式,则只需要写为:"name:idk color:(red:0.5 green:0.4 blue:0.7) scores:(9 8 9.5)",更加便捷。
回到刚才的命令,使用 CAT 书写,就得到了 /announce "color:red text:我好饿"
非常简洁。
CAT 的目的是尽可能减少不必要的符号,对人类友好。因此,实际上起作用符号的可以只有空格、冒号 : 和括号 (),第四个可选的符号是引号 '。 事实上,为了简洁,CAT 的第一个版本甚至无法定义字符串——引号没有任何作用!后来,我发现有时候需要输入空格和冒号,这才在 0.17.22 中加入了引号的支持。尽管如此,CAT 仍然支持不带引号的字符串。 在 JSON 中,我最不能理解的就是“不能有尾随逗号”和“键必须带引号”了。在 CAT 中,我们显然非常自由,因为 CAT 空格起 JSON 逗号的作用,leading & trailing spaces(也就是前导空格和尾随空格)显然都不是问题。而且就像 JavaScript 那样,键不需要引号,尽管也可以有引号。 在 CAT 中,除了“逗号是普通字符”这一点有些奇怪以外,它定义数组和对象的方式也非常奇怪。我们只有 (),只能用它们定义数组和对象。事实上,无论是数组还是对象,在 CAT 中的表示都是 ()——空数组和空对象的表示完全一样!当然,这么说不严谨,下文会再解释。 如果要定义数组,那么 () 是空数组,这是已知的。如果数组里有元素,那么 (a b c d e) 显然是合理的。注意,CAT 字符串不需要引号,所以上面就相当于 JS 的 ['a', 'b', 'c', 'd', 'e']。如果你好奇为什么不继续写 f,很不巧,在 CAT 中,f 是 false 的别名。是的,为了简单,我把 t 定义为 true,把 f 定义为 false。 定义对象怎么办?当然是——跟着直觉走了。比如 (aaa:t bbb:f) 就是 JS 的 {aaa: true, bbb: false}。非常简单。当然,也支持嵌套,(anArray:(0 1 2) apple:null banana:(count:2.5 type:normal)) 完全可行。 但是……如果我们就是想表示空对象,而不是空数组,这时候怎么办?用意念赋予 () 空对象的含义吗?当然不是。其实解决方法相当麻烦,不优雅,不好,而且是意料之外情理之中:(parseItAsAnEmptyObject:undefined) 会解析为一个空对象。当然,不是 parseItAsAnEmptyObject(翻译:把它解析为一个空对象)在起作用,解析器不会自然语言,真正关键的是后面这个 undefined。实际上,CAT 支持 undefined 关键字,那么我们可以先定义一个有键值对的对象,把里面键的值定义为 undefined(未定义),就可以让这个键消失,就只剩下空对象了。
其实开发 CAT 的目的只是为了避免自定义命令的参数个数上限。一条自定义命令最多只能定义八个参数,而且只能有一个语法条目——不能重载(overload,也就是一条命令的不同参数的分支)。起初我还在用转义过的 JSON,后来我实在受不了了,开发了 CAT。 虽然 CAT 的解析器复杂度高,兼容性也不好,但是它在命令传参方面做得很好,好过了转义的 JSON,好过了原版的空格分隔,这样我就满意了。 以上就是 CAT 的故事。 |