创建变量拓展
本页将会讲述如何创建自己的 PlaceholderExpansion
,用于自己的插件(推荐)或上传至 eCloud。
值得注意的是 PlaceholderAPI 依赖于其安装的拓展。PlaceholderAPI 仅进行核心的解析与替换工作,而拓展可以让其他插件在其消息内使用任何安装的变量。
你既可以直接从 eCloud 自行下载变量,也可以通过 PlaceholderAPI 的下载命令安装。
章节目录
开始
对于初学者,你需要现决定你想创建哪种 PlaceholderExpansion
。这里有多种创建变量拓展的方式。本章节将会讲述最常见的一种。
普通拓展部分
下文的所有示例共享一个属于 PlaceholderExpansion
类的剖普通部分。
为了避免重复解释基础方法的基本信息,以及简化文章,我们只讲述其中的必要/基础部分。
PlaceholderExpansion 基本结构
package at.helpch.placeholderapi.example.expansion;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
public class SomeExpansion extends PlaceholderExpansion {
@Override
@NotNull
public String getAuthor() {
return "Author"; // 这个方法允许你设置变量拓展的作者。不能为空。
}
@Override
@NotNull
public String getIdentifier() {
return "example"; // ID 即为变量中从第一个 `%` 符号(对于括号变量则为 `{`)开始到第一个下划线 `_` 前的内容。ID 不可以包含 `%`、`{`、`}` 和 `_` 符号。
// 如果你仍需要在其中使用这些字符,请覆写 `getName()` 方法。
}
@Override
@NotNull
public String getVersion() {
return "1.0.0"; // 这个方法会返回变量拓展的版本。不能为空。
// 因为这是一个字符串, 所以可以填入其他内容, 但还是推荐你将其保持在数字的版本格式。
// PlaceholderAPI 会使用这个字符串与 eCloud 上的最新版本(如果上传的话)进行比较,来检查是否有更新可用。
// 如果你的拓展是包含在插件中的,那么这部分不重要。
}
// 这些方法默认不覆写.
// 你需要覆写其中一个.
@Override
public String onRequest(OfflinePlayer player, @NotNull String params) {
// 由 PlaceholderAPI 调用,解析其中的变量。
// 在未被覆写时会调用 `onPlaceholderRequest(Player, String)`,将 OfflinePlayer 转化为 Player 对象(若可能),否则会返回 `null`。
// 推荐对 OfflinePlayer 使用这个方法,这样就可以在无需判断在线的前提下检查他们的数据了。
// **参数:**
// * `player` - 非空的 OffinePlayer 实例,用作解析变量的对象。
// * `params` - 非空的字符串,代表第一个 `%`(若为括号变量则为 `{`)与 `_` 之间的内容。
}
@Override
public String onPlaceholderRequest(Player player, @NotNull String params) {
// 由 PlaceholderAPI 通过 `onRequest(OfflinePlayer, String)` 调用,解析其中的变量。
// 在未被覆写时会调用 `null`,使得 PlaceholderAPI 将其判定为无效变量。
// **参数:**
// * `player` - 非空的 Player 实例,用作解析变量时的对象。
// * `params` - 非空的字符串,代表第一个 `%`(若为括号变量则为 `{`)与 `_` 之间的内容。
}
}
信息
在创建相对变量时无需覆写 onRequest(OfflinePlayer, String)
或 onPlaceholderRequest(Player, String)
。
创建内部变量拓展
内部变量拓展为直接与其依赖插件集成的类。
推荐使用这种方法创建变量拓展,因为它有如下优势:
- 无需
canRegister()
方法覆写。因为你的变量拓展就是插件的一部分,它依赖的东西无需覆写。 - 读取插件数据更容易。使用依赖注入,你可以更简单地获取某些数据,如配置文件中的值。
警告
内部变量拓展不会被 PlaceholderAPI 自动注册,因为它们不是 expansions 文件夹下的单独 .jar 文件。
请见“注册变量拓展”部分获悉详情。
另外你还需要覆写此方法,并将 persist()
设置为 true
。这可以告知 PlaceholderAPI 不要在插件重载时卸载你的变量拓展,否则会使其失效。
完整示例
请浏览 PlaceholderExpansion 基本结构部分来了解示例中的所有普通方法。
package at.helpch.placeholderapi.example.expansion;
import at.helpch.placeholderapi.example.SomePlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
public class SomeExpansion extends PlaceholderExpansion {
private final SomePlugin plugin; // 模拟插件,用来展示使用依赖注入来获取插件相关数据。
public SomeExpansion(SomePlugin plugin) {
this.plugin = plugin;
}
@Override
@NotNull
public String getAuthor() {
return String.join(", ", plugin.getDescription().getAuthors()); // 我们可以使用插件中的 `plugin.yml` 文件标注的作者作为该变量的作者名。
}
@Override
@NotNull
public String getIdentifier() {
return "example";
}
@Override
@NotNull
public String getVersion() {
return plugin.getDescription().getVersion(); // 因为变量拓展是内部的,因此可以使用 `plugin.yml` 中设置的版本。
}
@Override
public boolean persist() {
return true; // 必须设置,否则插件重载时 PlaceholderAPI 会将其卸载。
}
@Override
public String onRequest(OfflinePlayer player, @NotNull String params) {
if (params.equalsIgnoreCase("placeholder1")) {
return plugin.getConfig().getString("placeholders.placeholder1", "default1"); // 获取插件 `config.yml` 文件中数据的示例。
}
if (params.equalsIgnoreCase("placeholder2")) {
return plugin.getConfig().getString("placeholders.placeholder1", "default1"); // 获取插件 `config.yml` 文件中数据的示例。
}
return null; // 到达这一部分说明给定的参数无效,所以我们会返回 `null` 来告知 PlaceholderAPI 该变量无效。
}
}
注册你的变量拓展
因为变量拓展是内部的,因此 PlaceholderAPI 不会自动载入它,我们需要手动完成这一步。
这可以通过创建一个变量拓展的新示例并调用其 register()
方法完成。
这里是一个简明示例:
package at.helpch.placeholderapi.example;
import at.helpch.placeholderapi.example.expansion.SomeExpansion;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
public class SomePlugin extends JavaPlugin {
@Override
public void onEnable() {
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { // 我们检查 PlaceholderAPI 是否存在并启用,否则就会抛出报错。
// 另外,确保你已经在插件的 `plugin.yml` 中将 PlaceholderAPI 设置为了(软)依赖(见上文 #将PlaceholderAPI 设为(软)依赖)
new SomeExpansion(this).register(); // 这会向 PlaceholderAPI 注册我们的变量拓展。它也可以将插件类作为依赖注入拓展类,使我们可以使用它。
}
}
}
创建外部变量拓展
外部变量拓展是位于 PlaceholderAPI 的 expansions
文件夹下的独立 Jar 文件,包含了 PlaceholderExpansion
的拓展类。
只推荐你在如下情况中制作外部变量拓展。
- 变量拓展不依赖任何插件。
- 变量拓展依赖插件但你不能将其内置(插件不属于你)。
如果你没有满足上面这些条件,这表示变量拓展会像一个插件一样对你造成负担,因此还是建议你制作内部变量拓展。
外部变量拓展的好处包括可以通过 PlaceholderAPI 重载它,也可以将其上传至 eCloud,以通过命令 /papi ecloud download
下载。
缺点就是为了检查对应插件是否存在,步骤会更加麻烦一些。
完整实例(无依赖)
请浏览 PlaceholderExpansion 基本结构部分来了解示例中的所有普通方法。
package at.helpch.placeholderapi.example.expansion;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
public class SomeExpansion extends PlaceholderExpansion {
@Override
@NotNull
public String getAuthor() {
return "Author";
}
@Override
@NotNull
public String getIdentifier() {
return "example";
}
@Override
@NotNull
public String getVersion() {
return "1.0.0";
}
@Override
public String onRequest(OfflinePlayer player, @NotNull String params) {
if (params.equalsIgnoreCase("placeholder1")) {
return "text1";
}
if (params.equalsIgnoreCase("placeholder2")) {
return "text2";
}
return null; // 到达这一部分说明给定的参数无效,所以我们会返回 `null` 来告知 PlaceholderAPI 该变量无效。
}
}
完整示例(有依赖)
请浏览 PlaceholderExpansion 基本结构部分来了解示例中的所有普通方法。
package at.helpch.placeholderapi.example.expansion;
import at.helpch.placeholderapi.example.SomePlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
public class SomeExpansion extends PlaceholderExpansion {
private SomePlugin plugin; // 我们在 `canRegister()` 方法中设置了这个实例的值,这表示它不可以被设置为 final。
@Override
@NotNull
public String getAuthor() {
return "Author";
}
@Override
@NotNull
public String getIdentifier() {
return "example";
}
@Override
@NotNull
public String getVersion() {
return "1.0.0"
}
@Override
public String getRequiredPlugin() {
return "SomePlugin"; // 变量拓展依赖的插件。
// 推荐设置这个,这样 PlaceholderAPI 就可以检测是否有插件缺失。
}
@Override
public boolean canRegister() { // 这会实现两个目的:
// 1. 它将 `plugin` 的实例通过 Bukkit 的 PluginManager 设置为 `SomePlugin`,返回了一个能分配到 `SomePlugin` 的 JavaPlugin 实例。
// 2. 它会检查返回的实例是否非空。如果是,则会使得 `canRegister()` 返回 false,使得 PlaceholderAPI 不注册我们的变量拓展。
return (plugin = (SomePlugin) Bukkit.getPluginManager().getPlugin(getRequiredPlugin())) != null;
}
@Override
public String onRequest(OfflinePlayer player, @NotNull String params) {
if (params.equalsIgnoreCase("placeholder1")) {
return plugin.getConfig().getString("placeholders.placeholder1", "default1"); // 获取插件 `config.yml` 文件中数据的示例。
}
if (params.equalsIgnoreCase("placeholder2")) {
return plugin.getConfig().getString("placeholders.placeholder1", "default1"); // 获取插件 `config.yml` 文件中数据的示例。
}
return null; // 到达这一部分说明给定的参数无效,所以我们会返回 `null` 来告知 PlaceholderAPI 该变量无效。
}
}
创建相对变量拓展
信息
相对变量总是以 rel_
开头以便于区分,这意味着如果你制作了一个名为 friend_is_friend
的相对变量,则其完整用法为 %rel_friend_is_friend%
。
相对变量拓展特殊之处在于其要求输入两个玩家名称,以此比较其间的关系。
若要创建一个相对变量,你需要先继承一个 Relational
实例至你的变量拓展。你还需要拓展 PlaceholderExpansion
类。
继承这个实例会添加一个 onPlaceholderRequest(Player, Player, String)
,前两个参数为第一及第二个玩家,而第三个参数则是第二个 _
之后与最后 %
之前的内容(或者若变量为括号变量,则为 }
)。
完整示例
请浏览 PlaceholderExpansion 基本结构部分来了解示例中的所有普通方法。
这是一个使用相对变量的完整示例。
为了简明,我们在这里使用了内部变量拓展的安装方法并假设 SomePlugin
提供了一个 areFriends(Player, Player)
,能基于玩家是否为好友返回 true 或 false 的方法。
package at.helpch.placeholderapi.example.expansion;
import at.helpch.placeholderapi.example.SomePlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import me.clip.placeholderapi.expansion.Relational
import org.bukkit.ChatColor;
import org.bukkit.Player;
import org.jetbrains.annotations.NotNull;
public class SomeExpansion extends PlaceholderExpansion implements Relational {
private final SomePlugin plugin; // 模拟插件,用来展示使用依赖注入来获取插件相关数据。
public SomeExpansion(SomePlugin plugin) {
this.plugin = plugin;
}
@Override
@NotNull
public String getAuthor() {
return String.join(", ", plugin.getDescription().getAuthors()); // 我们可以使用插件中的 `plugin.yml` 文件标注的作者作为该变量的作者名。
}
@Override
@NotNull
public String getIdentifier() {
return "example";
}
@Override
@NotNull
public String getVersion() {
return plugin.getDescription().getVersion(); // 因为变量拓展是内部的,因此可以使用 `plugin.yml` 中设置的版本。
}
@Override
public boolean persist() {
return true; // 必须设置,否则插件重载时 PlaceholderAPI 会将其卸载。
}
@Override
public String onPlaceholderRequest(Player one, Player two, String identifier) {
if (one == null || two == null) {
return null; // 我们的变量需要两个玩家都存在,否则就返回 null。
}
if (identifier.equalsIgnoreCase("friends")) { // ID 匹配(表示变量为 `%rel_example_friends%` 或 `{rel_example_friends}`)之后,我们会通过插件的 `areFriends(Player, Player)` 方法检查玩家甲与乙是否为好友关系。
// 若是,则返回绿色文本。否则返回红色文本。
if (plugin.areFriends(one, two)) {
return ChatColor.GREEN + one.getName() + " 与 " + two.getName() + " 是好友!";
} else {
return ChatColor.RED + one.getName() + " 与 " + two.getName() + " 不是好友!";
}
}
return null; // 到达这一部分说明给定的参数无效,所以我们会返回 `null` 来告知 PlaceholderAPI 该变量无效。
}
}
别忘了注册你的变量。