[上一章](第二章-插件要素.md) [下一章](第四章-如何编写命令.md)
# 第一部分 第三章 如何编写监听器
参与编写者: MagicLu550,innc11
#### 建议学习时间: 30分钟
##### 学习要点: 学习如何构建一个简单的监听器,和自己构造事件

其实看一看这章，没有啥可讲的。监听器的内容很简单，很多人认识它的困难主要是概念上，
而不是使用上。

注册监听器有两个步骤: 1.定义监听器 2.注册监听器

nukkit监听器的构成: 事件监听和优先级

nukkit的监听器是通过 **反射(reflect)** 实现的，因此基于它，开发者容易上手，且上手
更简便。nukkit的监听器设计形同bukkit，也易于bukkit上手

nukkit声明一个事件的监听管理器是通过注解实现的，即@EventHandler

@EventHandler的源码如图
```java
package cn.nukkit.event;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 定义一个事件的处理器的注解。<br>
 * Annotation that defines a handler.
 *
 * <p>一个处理器的重要程度被称作处理器的<b>优先级</b>，优先级高的处理器有更多的决定权。参见：{@link #priority()}<br>
 * The importance of a handler is called its <b>priority</b>, handlers with higher priority speaks louder then
 * lower ones. See: {@link #priority()}</p>
 *
 * <p>处理器可以选择忽略或不忽略被取消的事件，这种特性可以在{@link #ignoreCancelled()}中定义。<br>
 * A handler can choose to ignore a cancelled event or not, that can be defined in {@link #ignoreCancelled()}.</p>
 *
 * @author MagicDroidX(code) @ Nukkit Project
 * @author 粉鞋大妈(javadoc) @ Nukkit Project
 * @see cn.nukkit.event.Listener
 * @see cn.nukkit.event.Event
 * @since Nukkit 1.0 | Nukkit API 1.0.0
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventHandler {
    /**
     * 定义这个处理器的优先级。<br>
     * Define the priority of the handler.
     *
     * <p>Nukkit调用处理器时会按照优先级从低到高的顺序调用，这样保证了高优先级的监听器能覆盖低优先级监听器做出的处理。
     * 调用的先后顺序如下：<br> </p>
     * When Nukkit calls all handlers, ones with lower priority is called earlier,
     * that make handlers with higher priority can replace the decisions made by lower ones.
     * The order that Nukkit call handlers is from the first to the last as:
     * <ol>
     * <li>EventPriority.LOWEST
     * <li>EventPriority.LOW
     * <li>EventPriority.NORMAL
     * <li>EventPriority.HIGH
     * <li>EventPriority.HIGHEST
     * <li>EventPriority.MONITOR
     * </ol>
     *
     * @return 这个处理器的优先级。<br>The priority of this handler.
     * @see cn.nukkit.event.EventHandler
     */
    EventPriority priority() default EventPriority.NORMAL;

    /**
     * 定义这个处理器是否忽略被取消的事件。<br>
     * Define if the handler ignores a cancelled event.
     *
     * <p>如果为{@code true}而且事件发生，这个处理器不会被调用，反之相反。<br>
     * If ignoreCancelled is {@code true} and the event is cancelled, the method is
     * not called. Otherwise, the method is always called.</p>
     *
     * @return 这个处理器是否忽略被取消的事件。<br>Whether cancelled events should be ignored.
     * @see cn.nukkit.event.EventHandler
     */
    boolean ignoreCancelled() default false;
}

```
第一个是优先级，第二个是是否忽略事件被取消

默认的优先级是normal,从注释可以知道，这是他们的先后顺序，LOWEST会最先被调用，其次是LOW，最后是MONITOR，如果在LOWEST监听器中调用了Event.setCancelled(true)，Nukkit则会忽略掉后面的 ignoreCancelled 被设置为true或者保持默认的优先级更高的监听器

- EventPriority.LOWEST

- EventPriority.LOW

- EventPriority.NORMAL

- EventPriority.HIGH

- EventPriority.HIGHEST

- EventPriority.MONITOR

而实现监听器的优先级标记是通过 **枚举(Enum)** 实现的

优先级的目的是为了保证监听器按照顺序执行，以使得一个监听器操作完会
进入下一个监听器继续执行，以确保执行的有序性。下一个监听器的相同操作
会覆盖之前监听器的相同操作。

同时，事件可以被我手动取消的，但是有时候事件虽然取消，但依然需要操作，
那么ignoreCancelled可以发挥作用了,当然默认是忽略掉取消的事件，也就是
说取消的事件默认不会被监听(这里可能有所错误，我这个不太常用)

一个EventHandler基本都是监听一个事件的(我只尝试过一个事件的)，代码中
这样使用
```java
package net.noyark.www;

import cn.nukkit.event.EventHandler;
import cn.nukkit.event.Listener;
import cn.nukkit.event.player.PlayerJoinEvent;

public class OtherListener implements Listener {

    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent e){
        //执行代码
    }
}

```
这里的意思就是当玩家进入服务器时，就会触发PlayerJoinEvent事件，服务器会
形成一个PlayerJoinEvent对象，并且调用先前注册的有关PlayerJoinEvent的
EventHandler，将对象传入，这样就实现了一个 **事件的调用**
获取对象的内容，则通过e调用即可，我们不需要很明白它的具体细节，只需要知道，
使用EventHandler注解的方法(且有一个Event的子类类型参数，并且它所在的监听器被注册)，
就会在事件发生时，相应的被调用，如上文所讲，这里的代码案例就是当发生玩家加入时，
这个onPlayerJoin方法会被服务端调用。
```java
package net.noyark.www;

import cn.nukkit.event.EventHandler;
import cn.nukkit.event.Listener;
import cn.nukkit.event.player.PlayerJoinEvent;

public class OtherListener implements Listener {

    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent e){
        e.getPlayer().sendMessage("你好 "+e.getPlayer());
    }
}
```
例如这个代码，在玩家加入时，将会向玩家发送一个"你好 玩家的名字"，这就是
我们监听器的作用
```java
package net.noyark.www;

import cn.nukkit.event.EventHandler;
import cn.nukkit.event.EventPriority;
import cn.nukkit.event.Listener;
import cn.nukkit.event.player.PlayerJoinEvent;

public class OtherListener implements Listener {

    @EventHandler(priority = EventPriority.HIGH,ignoreCancelled = true)
    public void onPlayerJoin(PlayerJoinEvent e){
        e.getPlayer().sendMessage("你好 "+e.getPlayer());
    }
}
```
如果要使用我们之前所说的参数，则这样使用。

事实上，监听器的基本使用方式也就这些，nukkit也提供了很多事件给予我们使用，也允许我们
自己制作事件自己使用，在第二部分中，我们将会讲解提供了哪些事件

如何自己定义一个事件

事实上，nukkit的事件是通过callEvent调用的，所以，我们同样可以通过callEvent实现我们自己
的事件。
```
    this.getServer().getPluginManager().callEvent(Event e);
```
首先我们先定义一个事件类
```java
package net.noyark.www;

import cn.nukkit.event.Event;

public class MyEvent extends Event {
}

```
之后，我们在监听器使用我们的事件
```java
package net.noyark.www;

import cn.nukkit.event.EventHandler;
import cn.nukkit.event.Listener;


public class OtherListener implements Listener {

    @EventHandler
    public void onMy(MyEvent e){
        
    }
}
```
之后[注册监听器](第二章-插件要素.md)，我们就可以使用我们的事件了。
```
    this.getServer().getPluginManager().registerEvents(new OtherListener(),this);
```
如何触发我们的事件？
事件的触发则通过callEvent触发，假如，我们写一个玩家假如时，如果他的
名字叫abc，就触发MyEvent事件
```java
package net.noyark.www;

import cn.nukkit.event.EventHandler;
import cn.nukkit.event.Listener;
import cn.nukkit.event.player.PlayerJoinEvent;


public class OtherListener implements Listener {

    @EventHandler
    public void onMy(MyEvent e){
        
    }
    
    @EventHandler
    public void onPLayerJoin(PlayerJoinEvent e){
        if("abc".equals(e.getPlayer().getName())){
            Example.getPlugin().getServer()
                    .getPluginManager()
                    .callEvent(new MyEvent());
        }
    }
}

```
这里，我们完成了我们的自定义事件

假如我们要注册一个EventHandler，不去注册其他的该如何。
事实上，可以实现这个，nukkit提供了registerEvent方法，
可以注册单个EventHandler，但不太常用，我这里也不会再过多
阐述了，如果想了解，可以发issue，我将会添加这方面的内容

[上一章](第二章-插件要素.md) [下一章](第四章-如何编写命令.md)
