事件监听
事件是服务器里发生的事——天气变化、玩家移动、玩家砍树、捡起掉落物,都是事件。
可控与不可控事件
事件分两种:
- 可控事件:能取消(
setCancelled(true))。比如玩家移动可被取消 - 不可控事件:不能取消。比如玩家退出服务器,无法阻止
怎么判断能不能取消
查 JavaDoc 看这个事件有没有继承 Cancellable 接口。有就能 setCancelled。
监听器(Listener)
监听器是一个实现了 Listener 接口的类,里面用 @EventHandler 注解标记"要监听哪个事件"的方法。
服务器某事件触发时(如玩家移动),会创建对应的 PlayerMoveEvent 对象,如果你的插件注册了监听器,服务端就按 @EventHandler 找到对应方法调用。
完整示例:未登录不许移动
下面写一个"玩家右键空气才算登录,登录前不能移动"的插件:
java
public class HelloWorld extends JavaPlugin implements Listener {
private List<String> notLoggedIn = new ArrayList<>();
@Override
public void onEnable() {
getLogger().info("Hello World!");
Bukkit.getPluginManager().registerEvents(this, this); // 注册监听器
}
// 玩家进服 → 记入"未登录"列表
@EventHandler
public void onPlayerJoin(PlayerJoinEvent e) {
if (!notLoggedIn.contains(e.getPlayer().getName())) {
notLoggedIn.add(e.getPlayer().getName());
}
}
// 未登录玩家移动 → 取消
@EventHandler
public void onPlayerMove(PlayerMoveEvent e) {
if (notLoggedIn.contains(e.getPlayer().getName())) {
e.setCancelled(true);
}
}
// 右键空气 → 视为登录,移出列表
@EventHandler
public void onPlayerInteract(PlayerInteractEvent e) {
if (e.getAction() == Action.RIGHT_CLICK_AIR) {
notLoggedIn.remove(e.getPlayer().getName());
e.getPlayer().sendMessage(ChatColor.GREEN + "登录成功!");
}
}
}主类当监听器只适合小插件
这里偷懒让主类直接 implements Listener。插件大了应把监听器拆到单独的类,主类只负责注册:registerEvents(new MyListener(), this)。
注册监听器
在 onEnable 里注册:
java
Bukkit.getPluginManager().registerEvents(监听器对象, this);- 第一参:监听器实例(
this或new MyListener()) - 第二参:插件实例(主类
this)
@EventHandler 的参数
@EventHandler 可设置两个参数控制监听行为:
java
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onMove(PlayerMoveEvent e) { ... }priority —— 优先级
多个插件监听同一事件时,按优先级从低到高依次执行:
| 优先级 | 顺序 |
|---|---|
LOWEST | 最先 |
LOW | |
NORMAL(默认) | |
HIGH | |
HIGHEST | |
MONITOR | 最后,仅用于观察,不应修改事件 |
MONITOR 的用途
MONITOR 专门给"只看结果不改结果"的插件用(如日志、统计)。用了它就别再 setCancelled 或改数据。
ignoreCancelled —— 是否忽略已被取消的事件
默认 false。设为 true 后,若该事件已被低优先级的监听器取消,你的方法不会被调用。
java
@EventHandler(ignoreCancelled = true)
public void onBreak(BlockBreakEvent e) {
// 只有事件没被取消时才执行
}注意事项
玩家移动事件包含转头
PlayerMoveEvent 在玩家只转动视角(不改 X/Y/Z)时也会触发。它是高频事件,监听里别做重活,否则拖垮 TPS。如需只在真正位移时处理,比较 from 和 to 的坐标:
java
@EventHandler
public void onMove(PlayerMoveEvent e) {
if (e.getFrom().getX() == e.getTo().getX() &&
e.getFrom().getZ() == e.getTo().getZ()) return; // 只是转头,跳过
// 真正位移的逻辑
}事件是插件交互的核心。下一篇配置 API让插件能读写配置文件。
