[上一章](第四章-如何编写命令.md) [下一章](第六章-如何编写plugin.yml.md)
# 第一部分 第五章 如何编写配置文件
参与编写者: MagicLu550
#### 建议学习时间: 40分钟
##### 学习要点: 了解如何创建配置文件，了解使用SimpleConfig

配置文件用于存储插件的配置信息，供用户自定义和修改，以及存储一些永久数据，我们称
配置文件属于 **持久层**。

nukkit提供了多种配置文件格式，如yaml,json,properties等，其中最常用的是yaml,
我们主要讲解这个配置文件格式，其他如果想要了解，可以发送issues，我们可以补充。

一. 关于yaml

nukkit的yaml框架基于[snakeYaml](https://github.com/bmoliveira/snake-yaml)实现的,
snakeYaml是一款使用广泛的yaml解析库,我们可以从它的[官网](https://yaml.org/type/index.html)了解
他们的语法，在nukkit开发中，我们更多使用key: value的映射形式

这是一个yaml的文件案例
```yaml
server:
 name: 12
 player:
  - nihao
  - xiaoming
  - xiaogang
 time:
  year: 2019
```
yaml的标准语法是使用空格来划分级别，前面为键，后面为值，且值和冒号之间有空格，
虽然yaml的语法不止如此简单，但是我们最常用的也就这些很简单的东西。

yaml的数组有两个表示形式
```yaml
array1: ["1","2"] #yaml的注释
array2: [1,2] #yaml不允许有重复的键
array3:
 - 1
 - 2
 - 3

```
如果还要了解其他，可以上yaml官网查看他们语法，这里只讲解这些语法

二. 如何使用nukkit的配置文件库

原本的snakeYaml使用起来比较复杂，因此nukkit官方提供了简化,同一
使用Config对象来表达和操作。

使用默认的配置

默认配置文件为config.yml,可以通过saveDefaultConfig()方法来实现，
前提是你的resources下面要创建一个config.yml,这个方法会默认在
plugins/${你的插件名字}下创建一个config.yml,并且会把resources下面的那个
config.yml内容复制过来.
![5-01](images/5-01.png)
之后我们调用这个方法即可
![5-02](images/5-02.png)
我们这里打开一个服务器，做一个实验可以看看,插件名为FirstPlugin
![5-03](images/5-03.png)
我们插件被加载成功,我们看看我们的文件夹
![5-04](images/5-04.png)
发现.../plugins/FirstPlugin下面出现了一模一样的config.yml

使用自定义的配置

自定义配置有两种方式，一种是已经初始化的文件，一种是空白文件，元素后期添加

1. 初始化的文件
saveResource(String fileName, boolean replace)

第一个是文件名称，默认是this.getDataFolder()+"/"+fileName的路径
getDataFolder()为.../plugins/你的插件名 ,不包含后面的"/",使用时记得注意
假如你的插件名称为abc,那么路径为.../plugins/abc

第二个是是否替换文件，每次重新启动服务器时，会把文件内容重新更新，如果为false,
则不会更新。或者已经存在config，将不会换掉。如果为true，那么就会换掉它。之前修改
的内容就会被替换(实际上加了true就是把文件删掉重新创建一遍)

而文件初始化的来源和之前的config.yml一样，我们可以看看saveDefaultConfig()源码会发现，
它的本质也是saveResource

PluginBase.java
```
    @Override
    public void saveDefaultConfig() {
        if (!this.configFile.exists()) {
            this.saveResource("config.yml", false);
        }
    }

```
2. 空白文件
空白文件的文件默认没有getDataFolder(),它的默认路径是和nukkit.jar同一级别的文件目录.
```
Config config = new Config(this.getDataFolder()+"/myConfig.yml",Config.YAML);
config.save();//保存文件,文件不存在就会创建
```
Config可以实现对文件的操作,例如先前的config.yml,我们想对其实现操作,可以
```
Config config = new Config(this.getDataFolder()+"/config.yml",Config.YAML);
config.set("me","12");//将me修改为12,如果me不存在将创建me
config.save();
```
当然值得注意，每次重新运行，set都会修改一次me,所以如果手动修改了me，就会被还原。
所以你可以做一个条件判断来避免还原的问题，比如判断文件是否存在。
当然这是初始化文件出现的问题，我还是建议初始化文件使用saveResource来初始化

Config的常用方法
1.构造方法

我们可以看官方源码

Config.java
```
/**
     * Constructor for Config instance with undefined file object
     *
     * @param type - Config type
     */
    public Config(int type) {
        this.type = type;
        this.correct = true;
        this.config = new ConfigSection();
    }

    /**
     * Constructor for Config (YAML) instance with undefined file object
     */
    public Config() {
        this(Config.YAML);
    }

    public Config(String file) {
        this(file, Config.DETECT);
    }

    public Config(File file) {
        this(file.toString(), Config.DETECT);
    }

    public Config(String file, int type) {
        this(file, type, new ConfigSection());
    }

    public Config(File file, int type) {
        this(file.toString(), type, new ConfigSection());
    }


```
file为文件名称，默认路径为nukkit的根目录
type为类型，主要使用的类型是

Config.java
```
 public static final int DETECT = -1; //Detect by file extension
    public static final int PROPERTIES = 0; // .properties
    public static final int CNF = Config.PROPERTIES; // .cnf
    public static final int JSON = 1; // .js, .json
    public static final int YAML = 2; // .yml, .yaml
    //public static final int EXPORT = 3; // .export, .xport
    //public static final int SERIALIZED = 4; // .sl
    public static final int ENUM = 5; // .txt, .list, .enum
    public static final int ENUMERATION = Config.ENUM;


```
我们主要使用yaml,json.properties,其他大家可以自行查询。

如何实现动态内容

比如一些插件可以这样做
```yaml
message: ${player}加入了信息
```

其实可以使用replace做到
```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){
        String message;
        //获取到内容后...
        e.getPlayer().sendMessage(message.replace("${player}",e.getPlayer().getName()));
    }
}
```
当然我们也可以基于反射做一个简单的解析工具

```java
class Utils{
        String[] vals = {"player","ip"};
        Map map = new HashMap(){
            {
               put("player","getName");
               put("ip","getAddress");
            }
        };
        public String translate(Event e,String message){
            try{
                Set<Map.Entry<String,String>> entries = map.entrySet();
                for(Map.Entry<String,String> e1:entries){
                    message = message.replace("${"+e1.getKey()+"}",e.getClass().getDeclaredMethod(e1.getValue()).invoke(e).toString());
                } 
            }catch (Exception e2){
                e2.printStackTrace();
            }
            return message;
        }
}

```

[上一章](第四章-如何编写命令.md) [下一章](第六章-如何编写plugin.yml.md)