第三节 如何编写监听器

上一节 下一节

第一章 第三节 如何编写监听器

参与编写者: MagicLu550,innc11,smartcmd

建议学习时间: 30分钟

学习要点: 学习如何构建一个简单的监听器,和自己构造事件

nukkit插件最主要的运行方式就是:

1
事件被触发 -> 执行动作

所以说监听器是插件中非常重要的部分

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

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

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

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

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

@EventHandler的源码如图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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基本都是监听一个事件的(我只尝试过一个事件的),代码中
这样使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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方法会被服务端调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
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());
}
}

例如这个代码,在玩家加入时,将会向玩家发送一个"你好 玩家的名字",这就是
我们监听器的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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实现我们自己
的事件。

1
this.getServer().getPluginManager().callEvent(Event e);

首先我们先定义一个事件类

1
2
3
4
5
6
7
package net.noyark.www;

import cn.nukkit.event.Event;

public class MyEvent extends Event {
}

之后,我们在监听器使用我们的事件

1
2
3
4
5
6
7
8
9
10
11
12
13
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){

}
}

之后,我们就可以使用我们的事件了。

1
this.getServer().getPluginManager().registerEvents(new OtherListener(),this);

如何触发我们的事件?
事件的触发则通过callEvent触发,假如,我们写一个玩家假如时,如果他的
名字叫abc,就触发MyEvent事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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,我将会添加这方面的内容

上一节 下一节