Skip to content

Latest commit

 

History

History
325 lines (230 loc) · 13.7 KB

6-2.md

File metadata and controls

325 lines (230 loc) · 13.7 KB

6-2 使用其它插件的 API

在插件开发中,我们虽然希望尽可能自己完成开发,但有的时候还是得依赖其它的插件。

plugin.yml

要使用其它插件的 API,我们需要在 plugin.yml 中做一些修改。

depend:
  - Vault
  - RarityCommons
softdepend:
  - HoofPower

加上的就是这两个字段,depend 中是必需依赖,如果没有安装,Bukkit 会拒绝加载本插件。

softdepend非必需依赖

另外,如果你想检查一个插件有没有被加载,可以使用:

Bukkit.getPluginManager().getPlugin("插件的名字");
// 如果加载了,返回值就是一个有效的 JavaPlugin 对象,否则是 null,插件名区分大小写

使用 API

下面我们以使用 Vault 经济插件的 API 为例,介绍插件 API 的使用方法。

一些高级的插件将 API 从插件本身中分离出来,一些没有。如果有 API,我们就应该找到这个 API,如果没有,那就使用插件 Jar 文件。

?> 为什么要使用 API
这里我们用到的 API 实际上就是接口,它没有被最终包含在我们的插件中,只是在编写和编译时用于提供类型。它就是一个「空壳」,只有外表没有内在,这使得 API 变得很小,易于使用。
开发时我们也不需要关注 API 的实现,只需要知道「有这个 API」就可以了。不懂?看图看图啦~
APID
左边是开发时的场景,右边是插件打包后的场景。
API 做的事,就是图中粉红色的部分。最终打包时,实现和 API 都会被丢掉。
上面所说的「空壳」,就是去掉了紫色部分后的结果。紫色部分无论是在开发还是在成果中都是不必要的
也就是说,我们对着粉色部分(API)盖房子,完全不需要紫色部分(实现),最后把粉色部分拿走,就是产物了。

Vault 是有 API 的,所以首先我们要找到该插件的 API,如果这个插件有 API,该 API 应该在它的「开发人员指南」部分有所提供。

Vault 的 API 位于 VaultAPI 仓库中。

咦?这里怎么没有下载链接?

Vault 使用了一种叫做 Maven 的工具发布它的 API,因此我们的项目也需要升级到 Maven。

创建 Maven 项目

原先 Maven 是需要单独进行安装的,但现在它已经被集成到 IDEA 中了。

打开「Project Structure」,转到「Modules」,单击左上角的「+」、「New module」。

在新的窗口中选择「Maven」,单击「Next」。

在新的窗口中输入项目的名字,项目的存储地点和 「GroupId」,「ArtifactId」。

「GroupId」 是这个项目组的名字,可以是 net.mcbbs.xxx,可以是 rarityeg.plugins,也可以是什么别的。

「ArtifactId」 是该模块的名字,在插件开发中一般以插件名小写组成。

这里「Location」比较坑,Maven 并不会为你选择一个合适的地点,需要将路径设置到之前的文件夹中(在我这里是 RarityPlugins)。

最终的配置如下:

CONF.png

单击「Finish」完成。

pom.xml

我不想在这里引入许多复杂的概念,因此我们以最简单的方式介绍 pom.xml

pom.xml 是 Maven 模块中的一个文件,Maven 看着它来构建项目。

展开刚刚创建的「ExampleMavenPlugin」,你就能看到 pom.xml,它看上去像这样:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>rarityeg</groupId>
    <artifactId>examplemavenplugin</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

</project>

现在我们回到 VaultAPI 的介绍页面,其中写着这样的内容:

How to include the API with Maven:

<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>
<dependencies>
    <dependency>
        <groupId>com.github.MilkBowl</groupId>
        <artifactId>VaultAPI</artifactId>
        <version>1.7</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

复制其中的内容,粘贴到 pom.xml<project></project> 中间的任意位置:

DEP.png

IDEA 将某些部分标注为了红色,表示「有错」,这是什么意思呢?

这表示「没有同步」,Maven 的作用之一是「依赖管理」。刚刚我们粘贴进来的那部分中包括了 VaultAPI 的地址信息,Maven 用它来寻找 VaultAPI 的文件。

现在这个文件还在网络上,IDEA 在本地找不到,就会发出警告。

单击一下图中的按钮,这表示「同步」,IDEA 就会开始同步 Maven 项目,稍等一会,等到下面的进度条完成,并且 VaultAPI 字样变为黑色,同步就完成了:

AFTERSYNC.png

创建主类

在 Maven 这里,我们不是在 src 下创建包,而要在 src/main/java 中创建包。为什么呢?

Maven 不仅能够用于 Java 的构建,还能够用于 C#、Ruby 等项目,因此该文件夹被放到了 src/main/java 下,换句话说,你就把 src/main/java 当作之前的 src 就行啦。

右键 java,创建包,包名自己写。

现在就可以在你的包中创建类啦!

添加服务端依赖

在 Maven 中,我们无法通过之前的方式添加「spigot-1.16.5」作为依赖,这会与 Maven 的依赖系统冲突。

幸运的是,Paper 提供了它的 API 的 Maven 版本,既然 Paper 这么好,我们就抛弃 Spigot,改用 Paper 吧。

查询 Paper 的仓库介绍,里面有这样的内容:

  • Maven Repo (for paper-api):
<repository>
   <id>papermc</id>
   <url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
  • Artifact Information:
<dependency>
   <groupId>com.destroystokyo.paper</groupId>
   <artifactId>paper-api</artifactId>
   <version>1.16.5-R0.1-SNAPSHOT</version>
   <scope>provided</scope>
</dependency>

我们将 <repository> 部分插入到 pom.xml 中的 <repositories></repositories> 之间,将 <dependency> 部分插入到 <dependencies></dependencies> 之间,就像这样:

ADD

你现在是不是对 Maven 的配置有一点点感觉了?看到 <repository>,就插入 <repositories> 中,看到 <dependency>,就插入 <dependencies> 中。

修改之后,右上角的按钮又出现了,我们再按一下「同步」,稍等一会,com.destroystokyo 字样就会变成黑色,即代表导入完成。

怎么样?Maven 使用起来是不是很方便?既然这样,我们就抛弃之前的导入方法,全部改用 Maven 吧,耶!晋升成功~

?> 到底怎么回事
Maven 的 pom.xml 中记载着项目的依赖信息,<repositories> 表示「仓库」,也就是在哪些地方查找依赖,<dependencies> 则表示要使用的依赖。
IDEA 和 Maven 会按照配置在网络上寻找对应的 Jar 包并下载下来以供开发使用。只需要配置 pom.xml,再按一下「同步」,这明显比「Project Structure」方便多了嘛!
只要有了像上面这样的配置信息(如 Paper 提供的),使用 Maven 就能够获取全世界任何开发者发布的项目文件以供开发,这就是 Maven 的强大之处——一套统一的标准,一个简便的方法。

哦,对了,我们 plugin.yml 的位置也要发生变化,位于 src/main/resources 下而不是 src 下了。

检查 Vault 是否装载

onEnable 方法中即可检查:

boolean isVaultEnabled = !(Bukkit.getPluginManager().getPlugin("Vault") == null);

如果 Vault 启用了,我们就可以调用它的 API,如果没有,那我们需要进行相应的善后措施。

使用插件 API

接下来就可以参照 Vault 的文档进行开发了,那些提供了 API 的插件一般都会提供开发文档。

例如,Vault 的快速上手代码:

!> 这是示例代码
这部分代码是 Vault 提供的,仅提示了使用方法,请不要尝试运行它!

package com.example.plugin;

import java.util.logging.Logger;

import net.milkbowl.vault.chat.Chat;
import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.economy.EconomyResponse;
import net.milkbowl.vault.permission.Permission;

import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;

public class ExamplePlugin extends JavaPlugin {
    
    private static final Logger log = Logger.getLogger("Minecraft");
    private static Economy econ = null;
    private static Permission perms = null;
    private static Chat chat = null;

    @Override
    public void onDisable() {
        log.info(String.format("[%s] Disabled Version %s", getDescription().getName(), getDescription().getVersion()));
    }

    @Override
    public void onEnable() {
        if (!setupEconomy() ) {
            log.severe(String.format("[%s] - Disabled due to no Vault dependency found!", getDescription().getName()));
            getServer().getPluginManager().disablePlugin(this);
            return;
        }
        setupPermissions();
        setupChat();
    }
    
    private boolean setupEconomy() {
        if (getServer().getPluginManager().getPlugin("Vault") == null) {
            return false;
        }
        RegisteredServiceProvider<Economy> rsp = getServer().getServicesManager().getRegistration(Economy.class);
        if (rsp == null) {
            return false;
        }
        econ = rsp.getProvider();
        return econ != null;
    }
    
    private boolean setupChat() {
        RegisteredServiceProvider<Chat> rsp = getServer().getServicesManager().getRegistration(Chat.class);
        chat = rsp.getProvider();
        return chat != null;
    }
    
    private boolean setupPermissions() {
        RegisteredServiceProvider<Permission> rsp = getServer().getServicesManager().getRegistration(Permission.class);
        perms = rsp.getProvider();
        return perms != null;
    }
    
    public boolean onCommand(CommandSender sender, Command command, String commandLabel, String[] args) {
        if(!(sender instanceof Player)) {
            log.info("Only players are supported for this Example Plugin, but you should not do this!!!");
            return true;
        }
        
        Player player = (Player) sender;
        
        if(command.getLabel().equals("test-economy")) {
            // Lets give the player 1.05 currency (note that SOME economic plugins require rounding!)
            sender.sendMessage(String.format("You have %s", econ.format(econ.getBalance(player.getName()))));
            EconomyResponse r = econ.depositPlayer(player, 1.05);
            if(r.transactionSuccess()) {
                sender.sendMessage(String.format("You were given %s and now have %s", econ.format(r.amount), econ.format(r.balance)));
            } else {
                sender.sendMessage(String.format("An error occured: %s", r.errorMessage));
            }
            return true;
        } else if(command.getLabel().equals("test-permission")) {
            // Lets test if user has the node "example.plugin.awesome" to determine if they are awesome or just suck
            if(perms.has(player, "example.plugin.awesome")) {
                sender.sendMessage("You are awesome!");
            } else {
                sender.sendMessage("You suck!");
            }
            return true;
        } else {
            return false;
        }
    }
    
    public static Economy getEconomy() {
        return econ;
    }
    
    public static Permission getPermissions() {
        return perms;
    }
    
    public static Chat getChat() {
        return chat;
    }
}

RegisteredServiceProvider 是「中间商」。由于我们只能访问到 Vault 的 API,这个中间商就负责将一个接口与一个实现它的类「绑」起来。也就是说,通过它的 getProvider 方法,我们获得了符合这个接口的一个类实例。至于那个实例具体怎么样……不在考虑范围之内。

还不明白?图!

SP

打比方说就是,你提供需要的服务(接口的 class)给 Bukkit,Bukkit 为你去找符合条件的酒店(提供方),你得到酒店地址后也不用了解它的底细(知道具体的类),走进去,里面就有你需要的服务(实现了接口)。

得到了接口的实现后,就可以开始进行开发了。关于究竟要如何利用一个插件为你提供的类、方法等内容,这是各个插件作者决定的,笔者也帮不上忙。你需要阅读他们编写的文档。

对了!最后打包时,记得把对应的 API 一起打包进去,可以参考 AC-1-3 中打包 JDBC 的过程(右键,「Extract Into Output Root」)。

总结

  1. 创建 Maven 项目,添加依赖
  2. 检查插件是否可用
  3. 使用「中间商」获得接口对应的对象
  4. 像使用 Bukkit API 一样使用 Vault API 吧!

就是这样。