Skip to content

事件监听 ​

事件是服务器里发生的事——天气变化、玩家移动、玩家砍树、捡起掉落物,都是事件。

可控与不可控事件 ​

事件分两种:

  • 可控事件:能取消(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让插件能读写配置文件。