Capability 开发教程
自定义 Capability、注册与查找、跨模块通信的完整开发教程——原理、内置能力表、提供方/使用方示例、EventBus。
Capability 开发教程
Capability 是 Suite 各模块之间协作的标准方式。宿主在 ModuleRegistry 中维护一张能力注册表:模块启动时注册自己提供的接口,其他模块按 Java 接口类型查找并调用——全程无需 import 对方实现类。
为什么用 Capability
| 方式 | 问题 |
|---|---|
直接 import 其他模块的 Service | ClassLoader 隔离、循环依赖、版本耦合 |
| Bukkit 自定义事件 | 适合广播,不适合「调用对方业务方法」 |
| Capability | 只依赖 axs-api 中的接口,松耦合、可 softdepends |
架构一览
数据流:
三种 Capability 形态
1. 单例 Capability(最常见)
一个接口类型全局只有一个实现。后注册会覆盖先注册。
适用于:TitleGrantable、MailDispatchable、MapNavigable 等。
2. 多实例 Capability
PlayerDataPurgeable 与 DatabaseMigratable 允许多个模块各注册一个实例。宿主把它们放入独立列表,供 /axs purge、/axs migrate 统一调度。
3. 全局 EventBus(内置)
宿主在初始化时注册唯一的 EventBusCapability,任何模块可发布/订阅主题,适合 1 对多广播。
与 SignalDispatchable(面向 EventPacket 规则引擎的 1 对 1 触发)互补。
加载顺序与 softdepends
module.yml 中:
depends:硬依赖,保证提供方先于使用方onEnablesoftdepends:软依赖,提供方可能不存在——使用方必须判空
即使写了 depends: [title],仍建议用 Supplier 延迟查找,以应对 reload 顺序边缘情况。
提供方:如何暴露能力
步骤 1 — 定义接口
接口放在 axs-api(官方能力)或你自己的 api 子包(第三方能力),供他人 compileOnly:
步骤 2 — 实现并在 startService 注册
步骤 3 — 官方模块示例(Title)
使用方:如何调用其他模块
基本用法
推荐:Supplier 延迟查找
避免在 onEnable 瞬间因加载顺序得到 null:
外部 Bukkit 插件接入:AxsCapabilities
上文 getCapability() 仅供 AXS 模块内部使用。面向外部 Bukkit 插件,Suite 提供静态门面 AxsCapabilities(axs-api 的 xuanmo.arcartxsuite.api 包),配合 @PublicCapability 注解做可见性过滤——只有标注该注解的接口才能被外部获取,其余一律返回 null。
当前公开的 Capability
| 接口 | 提供方 | 用途 |
|---|---|---|
EventBusCapability | 宿主 | 主题 pub/sub |
SecondaryPasswordAccess | 宿主 | 二级密码访问 |
AxsMailService | 邮件发送/查询/领取/删除(全异步) | |
AxsMarketService | market | 市场 UI/挂单/搜索/计数 |
MapNavigable | map | 地图第三方插件注册路径点推送、导航回调、导航互斥 |
QuestGpsNavigable | questgps | 任务推送/接取/追踪/门禁检查 |
BattlePassAccess | battlepass | 战令经验/等级/档位/任务进度 |
FishingAccess | fishing | 钓鱼小游戏/图鉴/经验/疲劳 |
RegionQueryable | regions | 保护区域查询 |
BossTrackerQueryable | entitytracker | 活跃 Boss 会话查询 |
LotteryAccess | lottery | 抽奖积分/兑换/开箱 |
LoginViewQueryable | loginview | 认证状态/打开登录界面 |
PropAccessible | prop | 道具列表/应用到主手 |
AnnouncerBroadcastable / SubtitlePlayable | announcer | 公告广播 / 播放字幕组 |
RgbRenderable | rgb | 渐变文本渲染 |
TitleGrantable / TitleConfigQueryable | title | 发放称号 / 称号元数据 |
ChatCardSendable / ChatMutable | chat | 聊天卡片 / 禁言管理 |
TabRefreshable | tab | 刷新 Tab 列表 |
CombatEffectTriggerable | combateffect | 触发战斗特效 |
SignalDispatchable | eventpacket | 触发规则引擎信号 |
MenuOpenable | menu | 打开配置菜单 |
AfkRewardDispatchable | afkreward | 挂机状态/原地挂机 |
WarehouseAutoDepositable | warehouse | 仓库自动存入 |
ExtraBackpackAccess | extrabackpack | 扩展背包访问 |
PickupNotifiable / PickupInterceptor | pickup | 拾取通知 / 拾取拦截(分模式注册) |
QQBotNotifiable / QQBotBroadcastable / QqBindCapable | qqbot | QQ 群事件/推送/绑定查询 |
EssentialsQueryable | essentials | AFK/隐身/禁言/昵称查询 |
OnlineRewardsQueryable | onlinerewards | 签到/在线时长查询与操作 |
InteractionState | conversation | 对话交互状态 |
TooltipDataCapable | tooltip | 物品提示数据 |
完整方法签名见 Capability API 参考。
接入步骤
- 插件
plugin.yml声明depend: [ArcartXSuite](或softdepend) - 构建时
compileOnly依赖axs-api - 调用
AxsCapabilities.get(接口.class),结果判 null
AxsCapabilities.get(Class)— 类型未公开、未注册或本体未就绪时返回nullAxsCapabilities.has(Class)— 判断指定公开 capability 是否可用- 模块热卸载后对应 capability 自动摘除,下次
get()返回null——建议每次使用时现取,不要长期缓存
EventBus 使用
声明发布主题
若模块通过 EventBus 发布事件,应在 publishedTopics() 中声明,以便其他模块在启动时通过 hasPublisher(topic) 检测:
基类会在 startService() 之前自动将这些主题注册到 EventBus。
内置 Capability 能力图
| Capability 接口 | 提供模块 | 公开 | 典型使用方 | 用途 |
|---|---|---|---|---|
TitleGrantable | title | ✅ | eventpacket, battlepass | 发放称号 |
TitleConfigQueryable | title | ✅ | tab, chat | 查询称号元数据 |
MailDispatchable | — | eventpacket, onlinerewards | 按预设发邮件(内部) | |
AxsMailService | ✅ | 外部插件 | 邮件完整服务 | |
AxsMarketService | market | ✅ | 外部插件 | 市场 UI/挂单/搜索/计数 |
SubtitlePlayable | announcer | ✅ | eventpacket | 播放字幕组 |
AnnouncerBroadcastable | announcer | ✅ | 外部插件 | 即时/排队公告广播 |
ChatCardSendable | chat | ✅ | eventpacket | 发送聊天卡片 |
ChatMutable | chat | ✅ | essentials | 禁言/解禁 |
QuestGpsNavigable | questgps | ✅ | eventpacket, 外部插件 | 任务导航 |
MapNavigable | map | ✅ | questgps, 外部插件 | 地图外部导航点 |
TabRefreshable | tab | ✅ | title, chat | 刷新 Tab 列表 |
CombatEffectTriggerable | combateffect | ✅ | eventpacket, prop | 触发战斗特效 |
SignalDispatchable | eventpacket | ✅ | onlinerewards, afkreward | 触发规则引擎信号 |
BattlePassAccess | battlepass | ✅ | 外部插件 | 战令经验/等级/档位 |
FishingAccess | fishing | ✅ | 外部插件 | 钓鱼状态/经验/疲劳 |
RegionQueryable | regions | ✅ | 外部插件 | 区域信息查询 |
BossTrackerQueryable | entitytracker | ✅ | 外部插件 | 活跃 Boss 会话查询 |
LotteryAccess | lottery | ✅ | 外部插件 | 抽奖积分/兑换/开箱 |
LoginViewQueryable | loginview | ✅ | 外部插件 | 认证状态/打开登录界面 |
PropAccessible | prop | ✅ | 外部插件 | 道具列表/应用到主手 |
RgbRenderable | rgb | ✅ | 外部插件 | 渐变文本渲染 |
OnlineRewardsQueryable | onlinerewards | ✅ | 外部插件 | 签到/在线时长 |
QQBotBroadcastable | qqbot | ✅ | eventpacket, mail | 推送到 QQ 群 |
QQBotNotifiable | qqbot | ✅ | 第三方 | 监听群消息/进退群 |
QqBindCapable | qqbot | ✅ | loginview | QQ 绑定查询 |
WarehouseAutoDepositable | warehouse | ✅ | pickup | 拾取自动入库 |
ExtraBackpackAccess | extrabackpack | ✅ | 外部插件 | 扩展背包访问 |
PickupNotifiable / PickupInterceptor | pickup | ✅ | warehouse | 拾取通知/拦截(分模式) |
EssentialsQueryable | essentials | ✅ | tab, chat | AFK/隐身/禁言/昵称 |
MenuOpenable | menu | ✅ | essentials, 第三方 | 打开配置菜单 |
AfkRewardDispatchable | afkreward | ✅ | essentials | 挂机状态/原地挂机 |
InteractionState | conversation | ✅ | pickup, prop | 对话中让出共享按键 |
EventBusCapability | 宿主 | ✅ | market, qqbot, 第三方/外部插件 | 主题 pub/sub |
SecondaryPasswordAccess | 宿主 | ✅ | 外部插件 | 二级密码访问 |
TooltipDataCapable | tooltip | ✅ | 外部插件 | 物品提示数据 |
PlayerDataPurgeable | 多模块 | — | 宿主 /axs purge | 清除玩家数据 |
DatabaseMigratable | 多模块 | — | 宿主 /axs migrate | 跨库迁移 |
PolygonSelectionCapability | 宿主/regions | — | fishing 等 | 多边形选区管理 |
ModuleAdminCapability | 宿主 | — | 调试工具 | 模块管理(@Internal) |
宿主命令与 Capability
部分 Capability 由宿主命令统一调用,模块只需注册即可接入:
| 命令 | 使用的 Capability |
|---|---|
/axs purge <玩家> <模块|all> | PlayerDataPurgeable |
/axs migrate <模块> <方向> | DatabaseMigratable |
实现 PlayerDataPurgeable 时 moduleId() 必须与本模块 id 一致。
第三方模块:完整示例
模块 A(提供方) — 声望系统:
模块 B(使用方) — 商店折扣:
B 的 Jar 不需要依赖 A 的实现类,只需 compileOnly A 发布的 api 接口 Jar。
生命周期与清理
| 事件 | 行为 |
|---|---|
模块 onEnable | 在 startService() 中 registerCapability |
模块 onDisable | 宿主自动 removeCapabilities(moduleId) |
/axs unload | 同上,并关闭 ClassLoader |
不要在 onDisable 里留悬挂引用
使用方应把 Capability 引用置空;提供方无需手动 unregister(宿主会处理)。若你在静态字段缓存了 Capability,unload 后可能持有已失效对象。
最佳实践 checklist
- 接口放在
api包,仅含业务方法,不暴露内部 Service - 使用方对
getCapability结果始终判 null - 可选依赖写
softdepends,必选依赖写depends - 使用
Supplier延迟查找 - 单接口单一职责;不要做一个「万能 Capability」
- 为对外 Capability 编写 Javadoc 与 wiki 说明
- 持久化模块实现
PlayerDataPurgeable/DatabaseMigratable以接入宿主运维命令