什么是世界实体 / 算子
在原版创作的环境下,数据包总是难免使用实体来运行,复杂的中大型数据包更是如此。为了避免系统资源浪费(主要是频繁召唤和杀死临时实体、使用目标选择器等),我们一般倾向于设置一个常态存在的实体来辅助系统运行。这个实体鸽子院一般称之为「世界实体」,而我为了少打几个字,也会称为「算子」。不过算子是个更大些的概念,不单指世界实体,因此下文大部分地方我还是使用「世界实体」来称呼。
(实际上我用算子来指代一切用作临时运算/中转/储存的载体,或者说系统运行依赖的对象。一般是专供系统使用,不做他用,也不可被玩家所感知。也包括特定方块、临时storage、临时记分板项。恒定存在的可以叫常算子或者环境算子之类的,不过既然发明这个叫法一半的原因是懒,当然是直接笼统地叫算子就行了。)
世界实体是一种常态存在、作为系统环境的实体算子。影算子的全称是「末影珍珠投掷物实体常算子」或「末影珍珠投掷物世界实体」。
实体算子的功能
一般来说,实体算子会承担以下功能
物品缓冲(中介):提供一个「车间」(可操作的物品槽位),允许进行物品相关操作。例如将储存的物品 nbt 转化为可操作的物品对象,或是在没办法直接转移的时候作为中继。也可以使用物品来进行一些其他操作
- 将储存在 storage 或其他 nbt 空间中的物品信息转化为物品对象。(比如背包切换或备份功能)
- 在批量操作玩家背包物品时,一般会先将数据转存到 storage 方便操作,再通过拥有物品槽位的世界实体将数据实例化。
- 老版本中没有 item replace from,不能直接将玩家背包九格映射到箱子或反过来。需要通过物品中介处理 Slot 后再操作。
- 使用任意一个物品作为媒介,通过 set_name 或 set_lore 来进行JSON文本组件解析。可以是单纯的解析,也可以进行字符串合并等较高级操作。
- 将物品移动到物品中介上,并通过 if items 或谓词判断其是否属于某个物品标签。
- ...
文本解析:将文本组件解析成文字。对于 1.20.4 以上的世界实体,只要允许物品中介,同时也就允许了文本解析。除了物品中介,告示牌、文本实体等也可以进行解析。
坐标相关
- 修改 Pos 再 tp 目标到自身,实现动态 tp (已可以使用宏替代)
- 构成单位圆计算三角函数
- 通过 tp 将二元的朝向转换为三元的坐标组,可以用于赋予实体向前的 Motion 等操作。
- 通过将分数存储为复数个算子的坐标,可以方便地进行排序。
- ...
实体指针:通过将 UUID 数组赋予 Thrower、Owner 等 NBT,允许使用 execute on 指向该 UUID 代表的实体。
各种世界实体的优缺点
盔甲架 (as/ams)
最传统的世界实体,从 1.8 盔甲架更新出来之后就一直有人在用。甚至被某些人鱼塘那帮老鱼干带到了 1.21.3 。
最早从 1.8 就广泛作为世界实体(或称system实体)存在了。因为旧版本不存在 if score 子命令,因此比较或检验分数必须依赖于存在的实体,而不能是虚拟实体。因此许多设置、参数必须通过一个系统实体(也就是现在的世界实体)来保存和调用。这或许可以算作是「世界实体」这个概念的源点。
事实上,盔甲架同时还是标记用实体,甚至整个「实体算子」(专门参与系统运行的实体)概念的源点。
优点
- 适用版本最多
- 物品槽位最多(护甲物品 + 武器物品)
缺点
- NBT 比较多,精神洁癖的话不会喜欢(会稍微拖慢运行速度)
- 虽然可以使用物品来储存 nbt, 但是链路要长一点。
- 模型比较复杂。目前不清楚 Invisible 之后还会不会显示模型,如果是的话也算个小缺点吧。
说起来并没有什么大缺点但是用的人慢慢少了
区域效果云 (aec)
和盔甲架出现的时代差不多,稍晚一点。算是第二代的实体算子,直到标记实体出来之后都还有不少地方会用到。比如用来提高 tp 刷新率的 uaec(用于被骑乘,每tick自动更新,可以让 tp 的实体更“跟手”)。
- 同样拥有出现得早的优势
- 简洁,用起来不会有心理障碍
作为标记很常见,能够自动消失。因此写顺手了会顺便把世界实体也设计成 aec
缺点
- 功能太简单。不支持物品中继、不支持实体指针,也没有能存 nbt 的地方。
- 简洁的优点完全被标记实体盖过了。
实际上 ams 和 aec 都是旧时代的老东西了。一般也就是旧版本升上来的老物懒得搬迁所以还用着,或者作者习惯改不过来。
标记实体 (mk/mkr)
可以认为 Marker 是 Mojang 将 marker:1b 盔甲架的功能独立出来而得的特化实体。
因为本身就是为了标记、储存数据而生,因此自然也就比用老一代的 as aec 更顺理成章。
但是由于我们往往需要在世界实体上寄托数据存储之外的功能,因此使用 Marker 作为世界实体也就只是最简方案。
优点
- 直接不存在于客户端,理论上客户端性能最优。或许还可以减少一些服务器发包。
- 有一个专门的数据存储路径(@s data),在需要在实体上储存或缓存数据的时候性能最优
但是世界实体其实是和NBT存储(storage)竞争这个岗位的,因为其实没什么数据非要存在世界实体上不可。
缺点
- 功能过简,不支持物品中介
和实体指针。
基本上可以看做是优化并融合了部分 ams 功能的 aec。
物品实体
没想到吧,物品也能作为世界实体。这个世界实体在鸽子院是由小豆娘 @xiaodou123 最先应用的,用于 vve 的实体指针。
优点
- 允许作为实体指针操作
- 能够进行物品中介。其槽位在 1.20.4 以后能被 item 和 if items (子)命令调用,也能够对其使用修饰器、谓词、复制粘贴等等操作,不会不如盔甲架灵活。
- 也能够用 item nbt 暂存数据,只是不方便而且不能长久存在。
缺点
- 最大的缺点就是『掉落物』这个存在形式太容易被误杀或者钻空子了。扫地大妈、玩家嫌卡了顺手杀一下都有可能导致系统出错。甚至如果不放在玩家够不到的地方,或是被传送到某些地方作为中介,还可能被漏斗或漏斗矿车吸走,从而产生系统错乱,或是被用来刷物品。
- 有不可隐藏的模型。
物品展示实体
新版本中我用得最多的世界实体,也是我的前置库 VDC 目前所选择的标准世界实体。(准备替换为末影珍珠了)
作为世界实体的物品展示实体实际上是物品实体的一种变形。
相对于物品实体来说:
优点
- 同样能够进行物品中介和数据暂存
- 实体类型相比物品而言安全非常多
- 完全可以隐藏的模型使其不会被玩家发现,因此不需要做隐藏处理。顺便可以降低一星半点的消耗。
缺点
- 无法作为实体指针
为什么使用影算子(末影珍珠世界实体)
目标选择器还是 UUID
即使没有入门命令的人一般也会知道,选择非玩家实体有目标选择器和 UUID 两种形式。
由于世界实体是一个“静态”的实体,也就是说它的参数是预设的、已知的。因此我们可以在召唤世界实体的时候添加 NBT 来为其指定特定的 UUID。并且,由于 UUID 的生成规则,我们可以确定这一 UUID 完全不会自然生成。所以,指定世界实体并不像其他非玩家实体一样,必须依赖于目标选择器或实体指针。
目标选择器的效率
众所周知,目标选择器由于结构比较复杂,能耗往往比直接使用 UUID 要高不少。在需要非常频繁的使用世界实体的场景,特别是 VVE(物理引擎) 这样非常复杂、高能耗的场景,使用 UUID 索引实体相比目标选择器,是立竿见影的高效。
但是为什么至今为止,明明鸽子院的大家已经不约而同地在实践中使用固定 UUID 索引,但对于是否使用 UUID 索引这件事,仍然蕴含争议与犹疑,举棋不定呢?这就要说到实体卸载风险了。
实体卸载风险
UUID 是唯一的。这是优势,也是遗憾。
在约定俗称、规范使用的情况下,使用 UUID 索引并不会有问题。但如果开发者使用不规范、开发中疏忽、出现BUG或服务器管理员抽风,就有可能卡出已加载区块,到世界外。
一旦世界实体离开世界,除了 @s 就没有办法索引到了。更糟糕的是,一旦区块卸载,就完全没有办法处理了。大部分情况下,也没有线索去找出世界实体到底跑到了什么地方。并且,由于 UUID 的唯一性,我们也没法召唤出新的世界实体来继续使用。
这样一来,所有用到这个世界实体的地方都会失效,直接抽风,废档。就好像古早指令房里谁偷偷在角落土里埋了个高频kill的命令方块一样绝望。
当然,在规范使用,即取即还的情况下,或是统一将
#tick 函数架空使用,并在 tick 尾回收世界实体的情况下,这个情况是不用过于担忧的。因此大家还是会倾向于使用 UUID,只是仍然心存顾虑。
#tick.json
..["vdc:tick"]..
#vdc:root.mcf
execute as 0-0-0-0-3 in overworld run function vdc:tick
#vdc:tick.mcf
function #vdc:tickhead
function #vdc:tickpre3
...
function #vdc:tickmain
...
function #vdc:tickap3
function #vdc:tickend
tp @s 0 0 0
这是一段上述的“架空 tick 标签”的示例。
目前还没实装,打算装入我的前置库 vdc。
将所有本来其他数据包的 tick 函数由命令标签Minecraft:tick 转移到
#vdc:tick 即可。其中 0 0 0 是常加载区块。
除了尾部回收世界实体确保数据包安全性之外,这个架空还可以提供将主循环拆分头尾的功能,并且附带 3+3 个额外的不同优先级。总共有 9 档。
使用者可以以此规划不同数据包的执行顺序。还可以在最前面规定一些不允许跨tick的内容,比如只能存在 1 tick的实体标签,比如自动删除掉落物和玩家手上的 UI 物品(用于物品类 GUI 的背景板、按钮和预览等的只读物品),这样一来就不需要多个包分别 kill 。
由于这个架空 tick 函数像是将除 schedule 和命令方块外的所有主循环都包了起来,并提供稳定的世界实体和 tick 头自动处理的资源等运行环境,我将其称为主循环容器。
末影珍珠最大的优势就是,它在 1.21.4 之后,能够自动加载区块。这虽然是传送到未加载区块 1 tick 之后才能生效,但是配合
孤岛化还是共享
风险分摊
性能最优
多算子还是单算子
多算子操作
自由引入
前置与最简系统
扩展
交互实体世界实体
驴世界实体
潜影弹世界实体
方块算子
命名牌
床
命令方块
潜影箱
实体指针操作