[上一章](第一章-如何搭建环境.md) [下一章](第三章-如何编写监听器.md)

# 第一部分 第二章 插件要素
参与编写者: MagicLu550
#### 建议学习时间: 50分钟
##### 学习要点: 了解nukkit的项目结构，认识一些普通组件的定义和注册

插件要素这章会概括性的介绍nukkit的一些主要组件，后面的章节将会对他们系统的介绍。

所有的插件式编程都有它的一套运行标准，如maven插件,PocketMine插件，以及我们的nukkit插件等。

我称插件的规则元素为一个**插件的要素**,主要是运行的主类所依托的父类或一个动作所依托的父类(PluginBase等),它
用于对服务器阅读的参数表(如web.xml,plugin.yml等)，以及执行一系列动作的附件(如监听器,Servlet,过滤器,命令等)

nukkit的插件以**PluginBase**的代表,以plugin.yml为运行依据,以监听器等附件为动作,这些动作基于在PluginBase里
注册,所以nukkit的结构主要为这些
```
    1. plugin.yml [必须有]
    2. 继承于PluginBase的主类 [必须有]
    3. Listener(监听器),Command(命令),Timer(计时器)
    
```
nukkit允许主类和监听器重叠,当我们制作简单的插件，为避免复杂，可以一个类担当主类和监听器的角色
并且我们也可以使用简单的方式来执行我们的命令。复杂项目中，我们不建议这么做

1. 注册一个监听器
```java
package net.noyark.www;

import cn.nukkit.event.Listener;
import cn.nukkit.plugin.PluginBase;

/**
 * 实现一个Listener代表了一个监听器，注册时，监听器才真正生效
 * 另外,Listener是一个接口，我们需要实现(implements)它
 */
public class Example extends PluginBase implements Listener {
    
    @Override
    public void onEnable() {
        //插件的管理由PluginManager实现，后期会讲解这个组件
        //listener为一个监听器对象，这里我们将服务器事先生成的主类对象传入
        //若要调用服务器生成的PluginBase对象，我们必须要传入this而不是再new一个
        //当然，注册监听器时您可以再new一个，但是不建议这么做
        //第二个plugin对象是主类对象，这个必须要传入this的
        //可能有人疑惑，那在其他类里该怎么调用之前生成的对象呢?这个疑惑将会在接下来代码实现
        this.getServer().getPluginManager().registerEvents(this,this);
    }
}

```

如果跨类如何做这件事?
我们这里定义了一个Example类和一个OtherListener类。
OtherListener实现了Listener接口。
如图所示实现了跨类的注册
![2-01](images/2-01.png)
那么如何实现在监听器使用实现注册好的主类对象?
下面代码解决了这个问题，要注意，plugin = this这段代码
要定义在监听器对象被定义之前，这样定义一个getPlugin方法就可以在监听器获取到了

```java
package net.noyark.www;


import cn.nukkit.plugin.PluginBase;

public class Example extends PluginBase{

    private static Example plugin;
    @Override
    public void onEnable() {
        plugin = this;
        this.getServer().getPluginManager().registerEvents(new OtherListener(),this);
    }

    public static Example getPlugin() {
        return plugin;
    }
}
```
在监听器中调用
```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){
        Example.getPlugin().getServer();
    }
}
```
[监听器具体内容](第三章-如何编写监听器.md)将在后面讲解

2. 注册一个[命令](第四章-如何编写命令.md)
其实注册个命令就很简单，如何跨类等等和上面基本一样，但是主类是不能注册一个命令对象的
(因为命令是继承Command对象)
但是也有简便的方法

第一种:
[1]
```java
package net.noyark.www;


import cn.nukkit.command.Command;
import cn.nukkit.command.CommandSender;
import cn.nukkit.plugin.PluginBase;

public class Example extends PluginBase{

    private static Example plugin;
    @Override
    public void onEnable() {
        plugin = this;
        this.getServer().getPluginManager().registerEvents(new OtherListener(),this);
    }

    public static Example getPlugin() {
        return plugin;
    }

    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        //这里返回true代表执行成功，false则不成功
        //sender 是发送命令的对象
        //command 命令对象
        // [1]
        //label 是标签，如果注册命令时label为null，就会默认为命令名，label的组成是fallBackPrefix+:+label
        //label这个地方其实也不是很懂。一般注册时都是""或者null
        //通过源码推测label和fallBackPrefix的组合是命令的唯一标识
        //
        //args 命令参数,比如/hello 1 2的参数为1和2,存储在数组中
        //这里使用命令通过equals
        //如何得到指令名称个人习惯原因
        if("hello".equals(command.getName())){
            //执行相关操作
            return true;//最好加上
        }
        //这种方式虽然方便，但是命令多，且命令功能复杂时会难以维护
        //少部分命令可以使用它
        return true;
    }
}
```
当然第一种方式，命令要真正被监听，我们还要在plugin.yml里声明
```yaml
permissions: #这个标签我们只写一次就ok了
  plugin.hello:
    description: 你好
    default: op #权限组，后期会讲到
commands: #这个标签我们只写一次就ok了
  hello:
    description: 你好
    usage: "/hello"
    permission: plugin.hello
```
第二种是创建命令类
这个其实不用多说，和前面一样使用
```java
package net.noyark.www;

import cn.nukkit.command.Command;
import cn.nukkit.command.CommandSender;

public class MyCommand extends Command {

    //hello为指令名字,后面description是它的介绍，其他功能将后期讲解
    public MyCommand() {
        super("hello","一个测试指令");
    }

    @Override
    public boolean execute(CommandSender commandSender, String label, String[] strings) {
        return true;
    }
}
```
注册很简单,第一个参数后面会讲解
```java
package net.noyark.www;

import cn.nukkit.plugin.PluginBase;

public class Example extends PluginBase{
    
    @Override
    public void onEnable() {
        this.getServer().getCommandMap().register("",new MyCommand());
    }
    
}
```

3.[计时器部分](第三章-计时器的介绍.md)我们将在后面讲解，它的体系较复杂一些

计时器主要形同java的Runnable，线程池等等，他们本质也是实现多线程。
也是开发者使用最广泛的一类组件。

4. 如何写plugin.yml
    plugin.yml第一章提到过，plugin.yml最主要的是
    
    ```yaml
     name: FirstPlugin             
     main: net.noyark.www.Example  
     version: "0.0.1"              
     author: myName
     api: ["1.0.8"]                
     description: My first plugin
    ```
snake1999曾说: 缺少他们的插件是不被nukkit运行的。
这些都是不能缺少的，在接下来章节中，将会系统的介绍里面常用的标签
目前从现在的维护者知悉，api写什么其实并不重要，因为nukkit向下兼容,不像
pocketMine一样变动很大,nukkit会淘汰掉过时的函数，不会删除，因此
可以保证老插件运行，但值得注意的是，不代表淘汰函数会完美运行，能
运行插件不代表不会出现bug，但这也是相对pocketMine最方便的优势了。


[上一章](第一章-如何搭建环境.md) [下一章](第三章-如何编写监听器.md)