levilamina::event · 事件¶
事件:订阅、读取载荷、修改载荷、取消。
缺少的键和值为 0 必须分开
契约 §5.1 记录了一次领地保护绕过:自定义维度里的一个事件读不到 dim,使用方写了 unwrap_or(0),把它当成主世界,结果放行了,日志里什么都没有。所以 Event 提供带类型的访问方式,缺少键和类型不符是两种不同的错误。
它还识别 _unresolved,这是宿主解析不出事件来源时注入的标记。Event::dim 这类方法遇到它会返回 Err,保护判断因此会以拒绝收场,不会拿一个编出来的 0 继续往下走。
Wiring 用来链式批量订阅,并把句柄放在一起保管。
函数¶
event::subscribe¶
| Rust | |
|---|---|
订阅一个事件。
| Rust | |
|---|---|
- 参数:
- id :
&str - handler :
impl FnMut(&mut Event<'_>) + Send + 'static
- id :
- 返回值类型:
Result<Listener> - 对应槽位:
subscribe_event
event::subscribe_with¶
| Rust | |
|---|---|
按指定的优先级订阅。
- 参数:
- id :
&str - priority :
Priority - handler :
impl FnMut(&mut Event<'_>) + Send + 'static
- id :
- 返回值类型:
Result<Listener> - 对应槽位:
subscribe_event
event::list¶
| Rust | |
|---|---|
宿主知道的所有事件 id,来自注册表,加上所有合成事件。
事件名拼错时,读一读它比去猜强。
- 返回值类型:
Vec<String> - 对应槽位:
list_events
event::exists¶
| Rust | |
|---|---|
这个宿主是否知道某个事件 id。
- 参数:
- id :
&str
- id :
- 返回值类型:
bool - 对应槽位:
list_events
Event¶
一次事件分发,也就是回调收到的东西。
它只在回调期间存在,不能保存下来,生命周期参数也会阻止这样做。
- 实现的 trait:
Debug
Event::id¶
| Rust | |
|---|---|
事件 id,例如 "ll::event::player::PlayerChatEvent",或者合成事件 "BlockDestroyEvent"。
- 返回值类型:
&str
Event::snbt¶
| Rust | |
|---|---|
原始的载荷 SNBT。调试时最直接。
- 返回值类型:
&str
Event::value¶
| Rust | |
|---|---|
解析后的载荷。第一次调用时解析,之后复用。
- 返回值类型:
Result<&NbtValue>
Event::value_opt¶
| Rust | |
|---|---|
载荷解析不了时返回 None,不报错。给把解析不了的载荷当作没有事件的观察型监听器用。
- 返回值类型:
Option<&NbtValue>
Event::str_at¶
| Rust | |
|---|---|
按路径读取一个字符串。缺少键和类型不符,都会在错误里写出键名。
- 参数:
- path :
&str
- path :
- 返回值类型:
Result<&str>
Event::i64_at¶
| Rust | |
|---|---|
- 参数:
- path :
&str
- path :
- 返回值类型:
Result<i64>
Event::i32_at¶
| Rust | |
|---|---|
- 参数:
- path :
&str
- path :
- 返回值类型:
Result<i32>
Event::f64_at¶
| Rust | |
|---|---|
- 参数:
- path :
&str
- path :
- 返回值类型:
Result<f64>
Event::bool_at¶
| Rust | |
|---|---|
- 参数:
- path :
&str
- path :
- 返回值类型:
Result<bool>
Event::opt_str¶
| Rust | |
|---|---|
宽松的写法:读不出来时返回 None,不说明原因。只用在读不到也无所谓的地方。
- 参数:
- path :
&str
- path :
- 返回值类型:
Option<String>
Event::opt_i64¶
| Rust | |
|---|---|
- 参数:
- path :
&str
- path :
- 返回值类型:
Option<i64>
Event::unresolved¶
| Rust | |
|---|---|
宿主没能解析的字段列表,即 _unresolved。
事件里带着的实体既不是在线玩家、也不在运行时的实体表里时,宿主会把字段名记在这里。列表不为空表示载荷不完整,保护判断应当拒绝,不要去猜。载荷根本解析不了时,列表也是空的,Self::check_complete 不会把这种情况算作完整。
- 返回值类型:
Vec<String>
Event::check_complete¶
| Rust | |
|---|---|
检查载荷是否完整:解析成功,并且 _unresolved 为空。解析不了的载荷不算完整,因为里面什么都没能解析出来。
- 返回值类型:
bool
Event::dim¶
| Rust | |
|---|---|
事件发生在哪个维度。
读不出来时返回 Err。以前的设计让调用方写 payload.i32_at("dim").unwrap_or(0),于是自定义维度(id 为 3 及以上)里的每个事件都被判定在主世界,在主世界拒绝、在别处放行的领地保护就这样被绕过了,日志里什么都没有。
- 返回值类型:
Result<i32>
Event::player¶
| Rust | |
|---|---|
事件里的玩家身份。
处理三种形状:合成事件的 _player:{name,xuid,uuid},宿主补充的 _player,以及只带名字的旧事件。以前调用方得自己知道这些区别。
- 返回值类型:
Option<PlayerIdentity>
Event::pos¶
| Rust | |
|---|---|
事件里的方块或位置坐标,即平铺的三个字段 x、y、z,这是合成事件的形状。
- 返回值类型:
Result<(i32, i32, i32)>
Event::pos_f64¶
| Rust | |
|---|---|
同上,但用浮点数,用于玩家位置之类。
- 返回值类型:
Result<(f64, f64, f64)>
Event::can_cancel¶
| Rust | |
|---|---|
这个事件能不能取消。
Some(true):能;Some(false):不能,Event::cancel会返回Err;-
None:不在表里,可能是第三方模组自己发出的事件,也可能是表还没跟上的上游新事件。这时cancel()照常写回,但没有人能替你确认它是否生效。 -
返回值类型:
Option<bool>
Event::cancel¶
| Rust | |
|---|---|
取消这个事件。
不能取消的事件返回 Err,并说明应该去拦哪个事件,比如 PlayerStartDestroyBlockEvent 会指向 PlayerDestroyBlockEvent。如果返回 (),保护模组会以为自己拦住了,这种误会比崩溃更危险,因为崩溃至少看得见。
不在表里的事件(can_cancel() 为 None)不受阻拦:照常写回,返回 Ok。SDK 不假装知道自己不知道的事。
Ok 只表示取消位已经写回给宿主,并不表示引擎停下了:有些钩子点处于更新到一半的状态,宿主在那里根本不接受取消。这个边界只写在事件文档里。
- 返回值类型:
Result<()>
Event::cancel_lenient¶
| Rust | |
|---|---|
不管能不能取消,都去取消。
只有一种正当的用法:通用的转发或代理组件,事件 id 在运行时才知道,拦不住也只能接受。业务代码请用 Event::cancel,并处理 Err。
- 返回值类型:
bool
Event::uncancel¶
| Rust | |
|---|---|
把 cancelled 写回 0,撤销之前的取消。
用于「先拦下、再判断、结果发现可以放行」的两段式决策。注意它只能撤销这个回调自己写的取消:别的模组在更早的优先级上做的取消撤销不了,宿主的事件总线也不允许把否决变回同意。
Event::set¶
| Rust | |
|---|---|
修改载荷里的一个字段。
写回的是差异:只有真正碰过的键会交还给宿主,没碰过的保持原样,所以同一个事件上的两个模组不会抹掉彼此的修改。
- 参数:
- path :
&str - value :
NbtValue
- path :
Event::edit¶
| Rust | |
|---|---|
任意改写。闭包收到载荷的一份可变副本。
这是唯一会复制整个载荷的路径;Event::set 和 Event::cancel 只写它们碰过的键。
- 参数:
- f :
impl FnOnce(&mut NbtValue)
- f :
Wiring¶
批量订阅,并把句柄放在一起保管。
比手动订阅多两点:订阅失败不会悄无声息,失败会记在 Wiring::failures 里,arm() 也可以整体失败;标签会写进日志,方便找到失败的那一项。
| Rust | |
|---|---|
Wiring::new¶
| Rust | |
|---|---|
- 参数:
- owner :
impl Into<String>
- owner :
- 返回值类型:
Wiring
Wiring::on¶
| Rust | |
|---|---|
以 Normal 优先级添加一个订阅。tag 只用于日志。
- 参数:
- id :
&str - tag :
&str - handler :
impl FnMut(&mut Event<'_>) + Send + 'static
- id :
- 返回值类型:
Wiring
Wiring::at¶
| Rust | |
|---|---|
以指定的优先级添加一个订阅。
- 参数:
- id :
&str - priority :
Priority - tag :
&str - handler :
impl FnMut(&mut Event<'_>) + Send + 'static
- id :
- 返回值类型:
Wiring
Wiring::arm¶
| Rust | |
|---|---|
执行订阅。任何一项失败都让整体失败,已经成功的会在返回前取消订阅,因为挂了一半的保护比没有保护更危险:有的点拦得住,有的拦不住,没有人知道是哪些。
- 返回值类型:
Result<Wiring> - 对应槽位:
subscribe_event
Wiring::arm_lenient¶
| Rust | |
|---|---|
宽松的写法:失败的记录下来,成功的照常挂上。适合有了更好、没有也行的可选功能。
- 返回值类型:
Wiring - 对应槽位:
subscribe_event
Wiring::failures¶
| Rust | |
|---|---|
没有挂上的那些,形如 (标签, 原因)。
- 返回值类型:
&[(String, String)]
Wiring::armed¶
| Rust | |
|---|---|
挂上了多少个。
- 返回值类型:
usize
Wiring::forget¶
| Rust | |
|---|---|
全部改为不自动取消订阅,一直存活到模组卸载。
PlayerIdentity¶
事件里的玩家身份。
三个字段各有各的用处,不能混用:
xuid唯一且不可更改,是权限或经济判断唯一能用的键。离线模式下可能为空。uuid同样稳定,适合用作存档的键。-
name用于显示。玩家可以改显示名,宿主解析名字时,账号名对不上就会退回到显示名(见bridge::resolvePlayer),所以不能拿它当身份。 -
实现的 trait:
Debug、Clone、Default、PartialEq、Eq、From
PlayerIdentity::selector¶
| Rust | |
|---|---|
返回一个可以用来调用接口的选择器。
优先用 xuid,因为它伪造不了。xuid 为空(离线模式)时,依次退回到 uuid 和名字。退回到 Name 时,PlayerSel::is_stable 为 false,权限或经济判断遇到这种情况要当心,因为名字会经过显示名的回退;见 sel 模块。
- 返回值类型:
crate::sel::PlayerSel
PlayerIdentity::is_identified¶
| Rust | |
|---|---|
有没有可靠的身份,即 xuid 或 uuid。拿它当权限的键之前,先问一下这个。
- 返回值类型:
bool
Listener¶
订阅句柄。丢弃它就会取消订阅。
Listener::forget 让它一直存活到模组卸载。卸载时宿主会移除剩下的订阅,真的调用 removeListener,失败了会报告,不会不声不响。
- 实现的 trait:
Drop
Listener::event_id¶
| Rust | |
|---|---|
所订阅的事件的 id。
- 返回值类型:
&str
Listener::forget¶
| Rust | |
|---|---|
放弃自动取消订阅。闭包随之泄漏,一直存活到进程结束。
Priority¶
| Rust | |
|---|---|
分发的优先级。值越小越先运行,和 ABI 里的 0 到 4 对齐。
- 实现的 trait:
Debug、Clone、Copy、PartialEq、Eq、PartialOrd、Ord、Default
EventRef¶
| Rust | |
|---|---|
早先的一代管它叫 EventRef。这个名字保留着,指向同一个类型。
EventPriority¶
| Rust | |
|---|---|
早先的一代把优先级叫作 EventPriority。