事件总线与事件类

基本信息

属性 值
事件总线 com.sk89q.worldedit.util.eventbus.EventBus(EventBus.java:56)
总线获取 WorldEdit.getInstance().getEventBus()(WorldEdit.java)
注册方法 EventBus.register(Object)(:139)、unregister(Object)(:149)
事件基类 event/Event.java:22(public abstract class Event)
事件类总数 8 个具体事件 + 2 个接口/基类 + 2 个枚举

事件类清单

事件类 文件行 类型 说明
CommandEvent event/platform/CommandEvent.java:27 extends AbstractCancellable 玩家执行命令时投递,可取消
CommandSuggestionEvent event/platform/CommandSuggestionEvent.java:30 extends Event 客户端请求命令补全
ConfigurationLoadEvent event/platform/ConfigurationLoadEvent.java:27 extends Event 配置加载完成,携带 LocalConfiguration
EditSessionEvent event/extent/EditSessionEvent.java:65 extends Event 编辑会话创建/关闭;WorldEdit.java:859-861 的 @deprecated 注释注明旧的 EditSessionFactories 已废弃、应改注册 EditSessionEvent
PlatformReadyEvent event/platform/PlatformReadyEvent.java:25 extends Event 平台就绪
PlatformInitializeEvent event/platform/PlatformInitializeEvent.java:29 extends Event 平台初始化
BlockInteractEvent event/platform/BlockInteractEvent.java:29 implements Cancellable 方块交互,可取消
PlayerInputEvent event/platform/PlayerInputEvent.java:28 implements Cancellable 玩家输入,可取消
Cancellable event/Cancellable.java:23 interface 可取消事件接口
AbstractCancellable event/AbstractCancellable.java:23 abstract class Cancellable 的默认实现
InputType event/platform/InputType.java:22 enum 输入类型
Interaction event/platform/Interaction.java:22 enum 交互动作

分发机制

EventBus.post(Object event)(EventBus.java:165-185):

  1. flattenHierarchy(event.getClass()) 展开事件的类型继承链(:169),因此父类事件的处理器也会收到子类事件
  2. 按类型收集处理器,按优先级排序(Collections.sort(dispatching),:180)
  3. 逐个调用 dispatch(event, handler)(:183)

事件处理器异常处理

EventBus.dispatch 包裹每个处理器的调用(EventBus.java:194-200):

protected void dispatch(Object event, EventHandler handler) {
    try {
        handler.handleEvent(event);
    } catch (InvocationTargetException e) {
        logger.log(Level.SEVERE, "Could not dispatch event: " + event + " to handler " + handler, e);
    }
}

EventHandler.handleEvent 会把处理器的任何 Throwable 包进 InvocationTargetException(EventHandler.java:72-78)。

结论:某个订阅者抛异常不会中断其它订阅者——异常被记为 SEVERE 日志后吞掉,分发循环继续。这与「静默失败」有本质区别(有日志、级别为最高),但玩家侧同样看不到提示。

本 mod 内部的投递点

事件 投递位置 触发源
PlatformReadyEvent ForgeWorldEdit.java:196-198 FMLServerStartedEvent
ConfigurationLoadEvent WorldEditCommands.java:73-78 //we reload
com.sk89q.worldedit.event.platform.CommandEvent ForgeWorldEdit.java:208-214 Forge CommandEvent(拦截全部服务端命令)
CommandSuggestionEvent CommandWrapper.java:58-65 客户端 Tab 补全

注意 ForgeWorldEdit.onCommandEvent(:201-216)会对每个 MP 玩家执行的任意命令都投递 WorldEdit 的 CommandEvent,并把 event.parameters 与命令名用空格拼接(Joiner.on(" ").join(split))。这是 mod 扩展拦截玩家命令的入口。

已废弃的空实现 API

WorldEdit.setEditSessionFactory(EditSessionFactory)(WorldEdit.java:863-876)被标注 @Deprecated,方法体只打一条 SEVERE 日志、不做任何事:

"…but EditSessionFactories have been removed in favor of extending EditSession's extents.
 This may mean that any block logger / intercepters addons/plugins/mods that you have
 installed will not intercept WorldEdit's changes! Please notify the maintainer…"

它是为旧版方块记录/拦截插件保留的兼容空壳:调用不报错,但拦截功能不会生效。这是死代码三档中的第 (b) 类变体——保留了签名却已无实现,调用方不会得到任何错误提示。1.7.10 版要拦截世界改动,正确做法是注册 EditSessionEvent(event/extent/EditSessionEvent.java:65)。

EditSessionFactories 作为类在本仓已不存在(find . -name 'EditSessionFactories.java' 无命中),全仓仅剩上述 2 处文字提及(WorldEdit.java:860 的 @deprecated javadoc 与 :873 的日志文本)。

订阅示例

SessionManager 用 @Subscribe 订阅 ConfigurationLoadEvent(SessionManager.java:282-287),并在收到时重建会话存储后端。

相关条目