玩家事件

基本信息

属性 值
事件实现类 20(api/event/Player*Event.java)
已注册为脚本类型 20(全部带 @ZenClass)
实际会被触发 5
声明但从不触发 15
管理器实现 api/event/MTEventManager.java(IEventManager 的实现)
事件基础设施 IEventManager、MTEventManager、IEventHandle、minetweaker/util/EventList

20 个事件类型

全部命名空间为 minetweaker.event.*,Java 类名见下表左列:

脚本名 Java 类 实际触发 触发点
PlayerInteractEvent PlayerInteractEvent 是 ForgeEventHandler.java:47
PlayerLoggedInEvent PlayerLoggedInEvent 是 FMLEventHandler.java:50
PlayerCraftingEvent PlayerCraftedEvent 是 FMLEventHandler.java:61
PlayerSmeltedEvent PlayerSmeltedEvent 是 FMLEventHandler.java:72
PlayerLoggedOutEvent PlayerLoggedOutEvent 是 FMLEventHandler.java:82
PlayerAttackEntityEvent 同名 否 无
PlayerBonemealEvent 同名 否 无
PlayerChangedDimensionEvent 同名 否 无
PlayerDeathDropsEvent 同名 否 无
PlayerFillBucketEvent 同名 否 无
PlayerInteractEntityEvent 同名 否 无
PlayerOpenContainerEvent 同名 否 无
PlayerPickupEvent 同名 否 无
PlayerPickupItemEvent 同名 否 无
PlayerPickupXpEvent 同名 否 无
PlayerRespawnEvent 同名 否 无
PlayerSleepInBedEvent 同名 否 无
PlayerUseHoeEvent 同名 否 无
PlayerUseItemStartEvent 同名 否 无
PlayerUseItemTickEvent 同名 否 无

「5 个触发」如何核实

grep -rn 'events\.publish' src/main/java 全仓只有 5 处命中,全部在两个 handler 里:

触发方法 宿主 FML/Forge 事件 发布语句
onPlayerInteract ForgeEventHandler net.minecraftforge.event.entity.player.PlayerInteractEvent ForgeEventHandler.java:38-48
onPlayerLoggedIn FMLEventHandler PlayerEvent.PlayerLoggedInEvent FMLEventHandler.java:41-51
onPlayerItemCrafted FMLEventHandler PlayerEvent.ItemCraftedEvent FMLEventHandler.java:53-67
onPlayerItemSmelted FMLEventHandler PlayerEvent.ItemSmeltedEvent FMLEventHandler.java:69-77
onPlayerLoggedOut FMLEventHandler PlayerEvent.PlayerLoggedOutEvent FMLEventHandler.java:79-83

两个 handler 在预初始化期注册(MineTweakerMod.java:136-137):

MinecraftForge.EVENT_BUS.register(new ForgeEventHandler());
FMLCommonHandler.instance().bus().register(new FMLEventHandler());

对另外 15 个事件,逐个统计「除自身类文件与 MTEventManager.java 外的引用数」,结果全部为 1,且那唯一一处都是 IEventManager 的接口方法声明。例如:

$ grep -rn '\bPlayerAttackEntityEvent\b' src/main/java | grep -v MTEventManager | grep -v 'api/event/PlayerAttackEntityEvent.java'
src/main/java/minetweaker/api/event/IEventManager.java:24:    IEventHandle onPlayerAttackEntity(IEventHandler<PlayerAttackEntityEvent> ev);

即:类型已注册、on* 订阅方法已实现、EventList 已分配,但没有任何 publish* 调用,事件永远不会到达脚本。这是 API 迁移留下的空壳,不是可用功能。

MTEventManager 的实现范围

MTEventManager 为全部 20 个事件各实现三件套(MTEventManager.java:78-390):

  • onXxx(IEventHandler<XxxEvent>) → IEventHandle
  • hasXxx() → boolean
  • publishXxx(XxxEvent) → void

clear()(:51-72)清空全部 20 个 EventList,由 reload() 步骤 2 调用(MineTweakerImplementationAPI.java:638),所以脚本订阅的事件监听器每次重载都会被清掉,必须写在脚本里重新注册。

缺陷:publishPlayerPickup 存在两个重载

MTEventManager 里 publishPlayerPickup 出现两次,靠参数类型区分:

签名 发布到 源码行
publishPlayerPickup(PlayerPickupEvent) elPlayerPickup MTEventManager.java:230-232
publishPlayerPickup(PlayerPickupXpEvent) elPlayerPickupXp MTEventManager.java:324-326

第二个名字应为 publishPlayerPickupXp。因为两个都是 0 调用方(见上节),目前无实际影响,但对阅读代码的人极具误导性。

事件句柄

IEventHandle(api/event/IEventHandle.java:12-13)标注 @ZenClass("minetweaker.event.IEventHandler")(注意脚本名与 Java 类名不同),继承 java.io.Closeable,只有 1 个 @ZenMethod:

@ZenMethod
@Override
void close();

脚本里保存 events.onPlayerCrafted(...) 的返回值即可用 .close() 取消订阅。

处理器实现

类 文件 说明
EventList<T> minetweaker/util/EventList.java:14 泛型双向链表式订阅列表:clear() :19、add() :23、hasHandlers() :39、isEmpty() :43、publish() :47;内嵌私有 EventNode implements IEventHandle(:63-76),其 close() 即取消订阅
IEventHandler<T> minetweaker/util/IEventHandler.java 函数式回调,单方法 void handle(T event)
CloseablePriorityList minetweaker/util/CloseablePriorityList.java 通用可关闭优先列表(EventList 之外的独立工具)

相关条目