历史版本 v6.3.2。 本页内容为历史发布版本,不再接受更新。

查看本页在 v6.4.0 中的版本 →

Skip to content

外部插件 API ​

自 v6.2.2 起

UltiTools-API 6.2.2 及更高版本可用。

外部插件 API 允许运行在 Paper 服务器上的任何普通 Bukkit 插件使用 UltiTools 框架功能,无需继承 UltiToolsPlugin。你的插件保持普通的 JavaPlugin 不变——只需调用 UltiToolsAPI.connect(this),框架就会扫描你的包中的 @Service、@CmdExecutor、@EventListener、@Scheduled 等注解。

快速开始 ​

1. 添加依赖 ​

Maven

xml
<dependency>
    <groupId>com.ultikits</groupId>
    <artifactId>UltiTools-API</artifactId>
    <version>6.3.2</version>
    <scope>provided</scope>
</dependency>

2. 在 plugin.yml 中声明依赖 ​

yaml
name: MyExternalPlugin
version: '1.0.0'
main: com.example.myplugin.MyExternalPlugin
depend: [UltiTools]

必须声明对 UltiTools 的依赖

depend: [UltiTools] 是必须的。UltiTools 必须在你的插件之前加载,这样调用 connect() 时框架才可用。

3. 连接和断开 ​

java
package com.ultikits.docs.external;

import com.ultikits.ultitools.abstracts.command.BaseCommandExecutor;
import com.ultikits.ultitools.abstracts.data.BaseDataEntity;
import com.ultikits.ultitools.annotations.*;
import com.ultikits.ultitools.annotations.command.*;
import com.ultikits.ultitools.api.UltiToolsAPI;
import com.ultikits.ultitools.interfaces.DataOperator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;

import java.util.UUID;

public class MyExternalPlugin extends JavaPlugin {

    @Override
    public void onEnable() {
        UltiToolsAPI.connect(this);
        // Your plugin is now wired into UltiTools!
    }

    @Override
    public void onDisable() {
        UltiToolsAPI.disconnect(this);
    }
}

就这样。框架会自动扫描 com.example.myplugin(从主类名推导出)中的注解类。

扫描内容 ​

调用 connect() 时,框架扫描你插件的基础包并注册:

注解功能
@Service注册为 IoC 容器中的 Bean
@CmdExecutor命令类自动注册到 Bukkit
@EventListener监听器自动注册到 Bukkit
@Scheduled方法注册为 Bukkit 定时任务
@PlayerCache玩家退出时移除该玩家的条目(Map<UUID, ?>;自 v6.3.0 起也支持 Set<UUID> 与 Map<?, UUID>),可选保存回调(saveBeforeRemove)
@ModuleEventHandler订阅 UltiTools 事件总线

编写服务 ​

服务的写法与 UltiTools 模块完全一样——使用 @Service 和 @Autowired:

java
package com.ultikits.docs.external;

import com.ultikits.ultitools.abstracts.command.BaseCommandExecutor;
import com.ultikits.ultitools.abstracts.data.BaseDataEntity;
import com.ultikits.ultitools.annotations.*;
import com.ultikits.ultitools.annotations.command.*;
import com.ultikits.ultitools.api.UltiToolsAPI;
import com.ultikits.ultitools.interfaces.DataOperator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;

import java.util.UUID;

@Service
public class GreetingService {

    @Autowired
    private StatsService statsService; // Injected by the IoC container

    public void greetPlayer(Player player) {
        player.sendMessage("Welcome! You have " + statsService.getVisits(player) + " visits.");
    }
}

编写命令 ​

像 UltiTools 模块一样使用 @CmdExecutor 和 @CmdMapping:

java
package com.ultikits.docs.external;

import com.ultikits.ultitools.abstracts.command.BaseCommandExecutor;
import com.ultikits.ultitools.abstracts.data.BaseDataEntity;
import com.ultikits.ultitools.annotations.*;
import com.ultikits.ultitools.annotations.command.*;
import com.ultikits.ultitools.api.UltiToolsAPI;
import com.ultikits.ultitools.interfaces.DataOperator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;

import java.util.UUID;

@CmdExecutor(alias = {"greet"}, permission = "myplugin.greet", description = "Greet command")
@CmdTarget(CmdTarget.CmdTargetType.PLAYER)
public class GreetCommand extends BaseCommandExecutor {

    @Autowired
    private GreetingService greetingService;

    @CmdMapping(format = "")
    public void greet(@CmdSender Player player) {
        greetingService.greetPlayer(player);
    }

    @Override
    protected void handleHelp(CommandSender sender) { }
}

命令描述使用纯字符串

命令描述使用 @CmdExecutor 的原始 description 属性。外部插件不支持 i18n() 方法——请使用纯字符串。

编写事件监听器 ​

java
package com.ultikits.docs.external;

import com.ultikits.ultitools.abstracts.command.BaseCommandExecutor;
import com.ultikits.ultitools.abstracts.data.BaseDataEntity;
import com.ultikits.ultitools.annotations.*;
import com.ultikits.ultitools.annotations.command.*;
import com.ultikits.ultitools.api.UltiToolsAPI;
import com.ultikits.ultitools.interfaces.DataOperator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;

import java.util.UUID;

@EventListener
public class JoinListener implements Listener {

    @Autowired
    private GreetingService greetingService;

    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent event) {
        greetingService.greetPlayer(event.getPlayer());
    }
}

数据存储 ​

使用 UltiToolsAPI.getDataOperator() 获取数据操作器。使用 JSON 或 SQLite 后端时,数据存储在你插件自己的数据文件夹中,而不是 UltiTools 的文件夹。使用 MySQL 时,数据存放在服务器配置的 MySQL 数据库中、以 @Table 命名的表里,因此请选用其他插件和模块都不使用的表名。getDataOperator 会拒绝既不在你插件 JAR 中、也未传给 connect 的实体类。

java
package com.ultikits.docs.external;

import com.ultikits.ultitools.abstracts.command.BaseCommandExecutor;
import com.ultikits.ultitools.abstracts.data.BaseDataEntity;
import com.ultikits.ultitools.annotations.*;
import com.ultikits.ultitools.annotations.command.*;
import com.ultikits.ultitools.api.UltiToolsAPI;
import com.ultikits.ultitools.interfaces.DataOperator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;

import java.util.UUID;

@Data @Builder @NoArgsConstructor @AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Table("player_stats")
public class StatsEntity extends BaseDataEntity<String> {
    @Column("player_id") private String playerId;
    @Column("visits") private int visits;
}
java
package com.ultikits.docs.external;

import com.ultikits.ultitools.abstracts.command.BaseCommandExecutor;
import com.ultikits.ultitools.abstracts.data.BaseDataEntity;
import com.ultikits.ultitools.annotations.*;
import com.ultikits.ultitools.annotations.command.*;
import com.ultikits.ultitools.api.UltiToolsAPI;
import com.ultikits.ultitools.interfaces.DataOperator;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;

import java.util.UUID;

@Service
public class StatsService {

    private final DataOperator<StatsEntity> dataOp;

    public StatsService() {
        JavaPlugin plugin = JavaPlugin.getPlugin(MyExternalPlugin.class);
        this.dataOp = UltiToolsAPI.getDataOperator(plugin, StatsEntity.class);
    }

    public int getVisits(Player player) {
        StatsEntity stats = dataOp.query()
            .where("player_id").eq(player.getUniqueId().toString())
            .first();
        return stats != null ? stats.getVisits() : 0;
    }
}

存储后端由服务端配置决定

存储后端(JSON、SQLite 或 MySQL)由 UltiTools 服务端配置决定,而非你的插件。你的代码无论使用哪种后端都一样。查询列名必须使用实体的 @Column 值,而不是 Java 字段名。

事件总线 ​

使用 UltiToolsAPI.getEventBus() 进行插件间通信:

java
// 发布事件(MyCustomEvent 必须继承 ModuleEvent)
UltiToolsAPI.getEventBus().publish(new MyCustomEvent(data));

// 在 @Service 类中通过注解订阅
@ModuleEventHandler
public void onCustomEvent(MyCustomEvent event) {
    // 处理来自任何模块或外部插件的事件
    // 参数类型不继承 ModuleEvent 时会被跳过,只打一条 WARNING 日志,不会注册
}

API 参考 ​

方法描述
UltiToolsAPI.connect(JavaPlugin)将插件连接到 UltiTools 框架
UltiToolsAPI.connect(JavaPlugin, Class<?>...)(v6.3.0+)连接,并同时登记插件拥有但不在其 JAR 中的实体类
UltiToolsAPI.disconnect(JavaPlugin)断开插件与框架的连接
UltiToolsAPI.isConnected(JavaPlugin)检查插件是否已连接
UltiToolsAPI.getDataOperator(JavaPlugin, Class)获取限定于该插件的 DataOperator(JSON 与 SQLite 下即其数据文件夹)
UltiToolsAPI.getEventBus()获取共享的 EventBus 实例

生命周期 ​

  • 自动断开:如果你的插件被禁用(服务器停止、/reload),UltiTools 会通过 PluginDisableEvent 监听器自动断开连接。你不一定需要在 onDisable() 中调用 disconnect(),但这是个好习惯。
  • UltiTools 关闭:当 UltiTools 自身关闭时,所有外部插件会通过 disconnectAll() 自动断开。
  • 清理:断开连接时,你的插件注册的所有命令、监听器、定时任务、事件总线订阅和玩家缓存 Bean 都会被移除。
  • 连接被拒绝(v6.3.0 起):如果框架在构建容器之后拒绝了你的插件、connect() 因此抛出异常——例如某个命令执行器声明的注解无法被执行,或使用了绑定配置的 @Scheduled/@CmdCD——框架为你的插件登记的内容不会残留,修正后的版本可以在同一次服务器运行中再次连接(issue #537)。

自 v6.3.2 起,后续定时任务、玩家缓存、EventBus、命令或监听器注册失败时,也会逆序清理已尝试的登记,包括只完成了一部分的失败阶段,然后释放容器、数据作用域与本次新增的所有权。框架重新抛出同一个原始异常;清理失败作为 suppressed 异常附加,不阻止其余清理尝试。清理本身失败时,资源可能残留至重启,请在重新连接前检查 suppressed 异常。插件初始化代码自行造成的任意副作用不会被回滚,初始化不具事务性。

与 UltiTools 模块的区别 ​

功能UltiTools 模块外部插件
基类extends UltiToolsPluginextends JavaPlugin
注册方式@UltiToolsModule + registerSelf()UltiToolsAPI.connect(this)
国际化plugin.i18n("key") 可用不可用——使用纯字符串
配置实体完整支持(@ConfigEntity)不可用 — 请使用 Bukkit getConfig()
绑定到配置项的 @Scheduled / @CmdCD(v6.3.0+)支持,见绑定到配置项的时间会被拒绝,请使用字面量 period / delay / value
数据存储plugin.getDataOperator(Class)UltiToolsAPI.getDataOperator(plugin, Class)
重载/ul reload 重新加载模块配置与语言;JAR 变化仍需重启服务器不可用;请自行实现重载命令,JAR 变化需重启
plugin.yml为 6.3.0 构建的模块写 api-version: 630(使用绑定到配置项的 @Scheduled / @CmdCD 时必须为 630)depend: [UltiTools]

完整示例项目 ​

查看 UltiTools-External-Example 仓库,获取一个完整的示例项目,演示了在标准 Bukkit 插件中使用 @Service、@CmdExecutor、@EventListener、@Autowired 和 DataOperator CRUD。

贡献者

The avatar of contributor named as Claude Opus 5.5 Claude Opus 5.5
The avatar of contributor named as Ling Bao Ling Bao
The avatar of contributor named as a a

基于 MIT 许可发布