Files
netease-modsdk-wiki/docs/mcguide/28-电脑网络游戏/课程3:编写Bukkit插件/3-进阶内容/2-自定义事件.md
boybook 760c2dd9ad 2.6
2025-12-01 20:59:16 +08:00

4.9 KiB
Raw Blame History

front, hard, time
front hard time
进阶 30分钟

自定义事件

我们现在所了解的事件都是Bukkit提供的. 例如, 玩家移动等.
那如果我们想自己去做一个事件呢?

按需创建类

比如, 我想自己做出来一个RuaEvent, 实现在玩家聊天说rua的时候触发.
很明显, Bukkit只会提供玩家发送聊天信息的事件, 肯定不会单独为了实现在玩家聊天发送rua的时候单独做个事件. 那应该怎么做?

首先想到的应该是监听玩家聊天事件, 然后判断玩家聊天发送的内容是什么, 如果是rua做我想做的事情. 这是常规的解决方法.
但是如果我想做一个强化插件, 我想在玩家强化物品的时候触发一个事件给自己和其他插件, 那我应该怎么做? 不如自定义一个属于自己的事件!

这里我们以创建上文的RuaEvent事件举例, 我们的大致思路是这样的:

  1. 创建一个RuaEvent类.
  2. 监听玩家聊天, 判断玩家聊天内容, 如果是rua, 让Bukkit触发我们新建的RuaEvent对象.
  3. 向玩家发送消息rua.

我们就先新建一个类RuaEvent, 让其继承org.bukkit.event.Event类. 在该类中写下这些固定代码:

public class RuaEvent extends Event{
    private static final HandlerList handlers = new HandlerList();
    @Override
    public HandlerList getHandlers() {
        return handlers;
    }

    public static HandlerList getHandlerList() {
        return handlers;
    }
}

HandlerList储存与监听本事件的监听器相关的对象.
这意味着Bukkit中注册监听器的本质就是在每个对应的事件HandlerList中加入该监听器的有关对象.
这也意味着Bukkit中事件的触发本质是遍历被触发事件的HandlerList, 调用监听器对应方法.

假如我想让服务器里的玩家触发的所有事件, 已知所有的诸如PlayerJoinEvent等玩家事件都继承了PlayerEvent, 那我可以监听PlayerEvent事件吗?
答案是不可以, 因为PlayerEvent没有getHandlerList()方法, 结合上面的内容, 你应该可以意识到PlayerEvent是无法正常工作的吧.
所以你只能把所有Player开头的Event监听一个遍才可以达到目的!

现在我们的自定义事件雏形已经完成. 你可以根据自己的需要添加相关代码!
这里我们示例的RuaEvent代码最终如下:

public class RuaEvent extends Event {
    private static final HandlerList handlers = new HandlerList();
    private Player p;

    public RuaEvent(Player p){
        this.p = p;
    }
    
    public Player getPlayer(){
        return p;
    }
    
    @Override
    public HandlerList getHandlers() {
        return handlers;
    }
    
    public static HandlerList getHandlerList() {
        return handlers;
    }
}

可取消事件的实现

等一等, 这样做出来的事件没有setCancelled方法和isCancelled方法, 这是不可取消的事件.
如果想做成可取消事件, 需要实现Cancellable接口:

public class RuaEvent extends Event implements Cancellable{
    private static final HandlerList handlers = new HandlerList();
    private Player p;
    
    private boolean cancelledFlag = false;

    public RuaEvent(Player p){
        this.p = p;
    }
    
    public Player getPlayer(){
        return p;
    }
    
    @Override
    public HandlerList getHandlers() {
        return handlers;
    }
    
    public static HandlerList getHandlerList() {
        return handlers;
    }
    
    @Override
    public boolean isCancelled() {
        return cancelledFlag;
    }
    
    @Override
    public void setCancelled(boolean cancelledFlag) {
        this.cancelledFlag = cancelledFlag;
    }
}

如果是不可取消的事件, 无需实现Cancelled.
截止到现在, RuaEvent已经自定义成功, 现在我们只需要做第二步即可:

  1. 如果RuaEvent是个不可取消事件
@EventHandler
public void onPlayerChat_DEMO1 (PlayerChatEvent e){ //如果RuaEvent是个不可取消事件
    if(e.getMessage().equals("rua")) Bukkit.getServer().getPluginManager().callEvent(new RuaEvent(e.getPlayer())); //触发事件
    e.sendMessage("Rua!");
}
  1. 如果RuaEvent是个可取消事件
@EventHandler
public void onPlayerChat_DEMO1 (PlayerChatEvent e){ //如果RuaEvent是个可取消事件
    if(e.getMessage().equals("rua")){
        RuaEvent event = new RuaEvent(e.getPlayer());
        Bukkit.getServer().getPluginManager().callEvent(event);
        if(event.isCancelled()) {
            return; //事件被取消, 终止事件的处理
        }
        // 事件未取消对应的逻辑
        e.sendMessage("Rua!");
    }
}

在这里监听了PlayerChatEvent,但是此事件已被标记@Deprecated实际的开发过程中不推荐监听此事件.
实际开发中建议监听的是AsyncPlayerChatEvent事件. 注意这是异步监听用法基本类同于上述事件的监听具体请参见JavaDoc.