事件¶
| Rust | |
|---|---|
接住返回的 Listener;丢掉它就退订。
用常量,不要写字面量¶
event::subscribe 收的是字符串,而拼错的 id 会订阅成功,然后一次都不触发。
names 模块把这个错挪到编译期:
| Rust | |
|---|---|
每个常量都注明了这个事件能不能取消、载荷里有什么。/pier events 列出某个构建能解析的全部 id。
两类事件¶
注册表事件来自 LeviLamina,写全名 ll::event::PlayerChatEvent。唯一后缀也能解析,
但上游哪天加一个同名事件,后缀就有歧义了。
合成事件是 Pier 用原生 detour 造的,因为 LeviLamina 没有对应物。它们是裸名字,
比如 BlockDestroyEvent。一共 29 个,补的正是保护场景里要紧的那些缺口:
非玩家造成的方块破坏、爆炸、水流跨界、活塞伸进邻居地皮、跨界大箱子合并。
names::ALL_SYNTHETIC 是完整清单,方便做启动自检。
读载荷¶
载荷是 SNBT。类型化取值把「缺键」和「类型不符」分开,也把这两者和「值恰好是 0」分开:
| Rust | |
|---|---|
ev.dim() 会返回错误,这正是重点。一个把解析不到的维度读成 0 的土地保护模组,
会在主世界里拒绝、在别的所有地方放行,而且悄无声息。请处理那个错误——
不知道的时候就拒绝。
宿主在解析不了载荷的一部分时会注入 _unresolved。ev.unresolved() 返回那些字段名,
非空就说明载荷不完整。保护判定在这种时候应该拒绝,而不是猜。
取消¶
cancel() 返回 Result,因为不是每个事件都能取消。不能取消的会说清楚,
并指出该拦哪一个:
| Text Only | |
|---|---|
这里返回 () 会让保护模组以为自己拦住了。那种「以为」比崩溃更糟,因为崩溃看得见。
Ok 只表示取消位送达了宿主,不表示引擎停了。有几个钩点处在半更新状态,
宿主在那里根本不接受取消;适用的事件常量里写明了。
改载荷¶
只改一个字段时用 ev.set("message", value) 更省事。
写回是差量的:只有你真正碰过的键才写回去,所以两个模组挂同一个事件不会互相抹掉对方的修改。
一次订一批¶
Wiring 把一批订阅攒起来,句柄也一起管:
| Rust | |
|---|---|
arm() 是整体成败:任何一条订阅失败,它会把已经挂上的那些退订掉再返回错误。
挂了一半的保护比没有更糟——有些点拦得住有些拦不住,而且没人知道是哪些。
另一个选择是 arm_lenient(),适合可有可无的功能,失败的记录在 failures() 里。