# 配置API

# 配置文件和配置API

配置文件用来储存配置信息, 以便使用文件开关功能、储存数据、修改信息.
我们往往需要读写配置文件. Bukkit为我们提供了配置API.

配置API是BukkitAPI提供的读写配置文件的工具. 其相对而言较为简单, 是插件开发中常用的API.

目前为止, 配置API只有YAML配置功能可用. 这也是大多数插件为什么配置文件是YAML文件的原因.
在本文中, 我们也将使用YAML配置API.

# 了解YAML文件

# 键值对

相信开服的经验已经使你对YAML文件有了初步认识.
YAML文件的文件后缀是.yml. 其配置文件结构需要严格遵守YAML标准.

下面是一个符合标准的YAML配置文件的内容:

Settings:
  DebugMode: true
  Time:
    CoolDown: 10
Data:
  player1:
    NickName: HandsomeBoy
    Score: 50
    TotalTime: 40
    Title:
    - Toilet Protecter
    - Widow Maker
    - Chicken Fucker

相信你可以根据空格看出每个项目之间的所属关系, 如下:

我们把上面所属关系图中, 矩形框内的东西叫做键(Key). 例如, Settings是一个键, Data是个键. Settings键下存在DebugModeTime两个子键, 它们分别叫做Settings.DebugMode键和Settings.Time. 同理, 在Settings.Time键下还有CoolDown这个子键, 这个子键叫Settings.Time.CoolDown键.

我们可以用这样的命名方法来称呼一个YAML文件中的任一一个键了. 并且还可以根据名称看出所属关系.
例如, Data.player1.Score键对应的值是 50.

在YAML中, 键和值一一对应, 一个键一定会有一个值.

# 数据类型

通常可以用配置文件存储一些基本类型(int、double、boolean)、String、数组和可被序列化的对象.

Bukkit中给出的一些对象有些是可以直接存进配置文件的, 这需要看这个类是不是实现了ConfigurationSerializable接口. 例如, Player类型的对象就可以被直接存入配置文件, 因为查阅JavaDoc后可以发现它实现了ConfigurationSerializable.

后续会详细介绍, 这里需要知道判断方法.

在上面的配置文件中, 配置文件里储存了:

  1. 存储了一个boolean类型的值(Settings.DebugMode键).
  2. 存储了一些数字类型的值.
  3. 存储了一个String字符串(Data.player1.NickName键).
  4. 存储了一个StringList(YAML里的StringList就是Java中的List<String>, 例如Data.player1.Title键).

YAML中注释以#表示.

#就像这样写注释, 配置文件读取时会忽略掉注释
Settings:
  DebugMode: true

相信你可以通过这个例子明白配置文件中可以储存哪些数据了.

# 对于不存在的数据

很明显, 上面的配置文件中, 并没有Data.player2.NickName键, 那么如果我非要获取Data.player2.NickName键的值, 获取到的数据是什么呢?
答案是null. 换句话说, YAML里所有不存在的键, 值是null.

请记住这句话. 我们可以根据这个原理推导出, 如果你想删除一个已经存在的键, 那就是把这个键的值设置为null.

# 操作默认配置文件

这里的默认配置文件指的是config.yml文件.
首先我们需要准备一个默认的config.yml文件. 这个文件会在插件检测到plugins\插件名文件夹下没有config.yml文件时被放入该文件夹中.
在插件jar文件里, 默认的config.yml文件要与plugin.yml文件处于同一目录下, 所以创建默认config.yml的方法与创建plugin.yml文件的操作方法一致. 在这里我们在默认config.yml文件中存入我们一开始举的例子.

# 读取config.yml数据

下面做一个插件, 在玩家登陆服务器时, 给玩家显示配置文件Data.玩家名.Score键对应的值.

public class HelloWorld extends JavaPlugin implements Listener{
    public void onEnable(){
        saveDefaultConfig(); //这个代码会自动判断插件配置文件里是不是有config.yml, 没有就会放入默认的config.yml
        Bukkit.getPluginManager().registerEvents(this,this);
    }  
  
    public void onDisable(){}
  
    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent e){
        //在这里我们监听了PlayerJoinEvent, 并操作`config.yml`
        String key = "Data." + e.getPlayer().getName() + ".Score"; //这是我们要获取的键名
        int score;
        if(getConfig().contains(key)){ //先判断一下有没有这个键
            score = getConfig().getInt(key); //有的话读取
        } else {
            score = 0; //没有的话就按0处理
        }
        e.getPlayer().sendMessage("你的积分是: " + score); //然后给玩家发送
    }
}

如果你用getConfig().getString(key)获取玩家数据Score键的值, 那么获取到的就是一个String字符串.
也就是, YAML中值对应的数据类型具体是什么, 关键要看你用的getter是什么.

# 写入数据到config.yml

我们再来做个"加分项", 玩家挖掉一个石头后, 给他加分.

public class HelloWorld extends JavaPlugin implements Listener{
    public void onEnable(){
        saveDefaultConfig();
        Bukkit.getPluginManager().registerEvents(this,this);
    }  
  
    public void onDisable(){}
  
    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent e){
        //这里代码跟上面是一模一样的, 这里只是做了简化, 因为原先的if占篇幅太大
        String key = "Data." + e.getPlayer().getName() + ".Score";
        int score = getConfig().contains(key)?getConfig().getInt(key):0;
        e.getPlayer().sendMessage("你的积分是: " + score);
    }

    @EventHandler
    public void onBlockBreak(BlockBreakEvent e){
        if (e.isCancelled()) return; //判断此事件是不是被其它插件取消掉了
        if(e.getBlock().getType() == Material.STONE){ //判断类型, 是石头
            String key = "Data." + e.getPlayer().getName() + ".Score";
            int score = getConfig().contains(key)?getConfig().getInt(key):0; //获取玩家当前积分, 如果从未记录此玩家的积分数据则默认为0
            getConfig().set(key,score + 10); //挖一个石头加10分

            //但是写到这里要小心!你只是修改了内存上的数据, 你没有修改硬盘上的config.yml文件里的数据!
            saveConfig(); //所以要注意, 修改数据要记得保存
        }
    }
}

由此, 你需要小心, getConfig()的内容是内存上的内容, 修改它并没有修改硬盘上的内容, 关服/重载后就会消失, 因此要注意保存!

set不区分数据类型是什么, 存储数据全部都用set方法. set不管这个键在配置文件里存不存在, 都会写入这个数据.

还记得我们一开始说的YAML里所有不存在的键, 值是null吗? 如果你想删除掉player3的数据, 那你应该写成:

getConfig().set("Data.player3",null);

这样配置文件里Data键下就没有player3的数据了,也就达到了删除一个键的目的.