下游 SDK 接入指南(agent-api / agent-kit)
面向业务插件开发者:如何让 Bukkit/Bungee 上的业务插件接入 Beacon agent,读有效配置、查服务发现。 普通业务插件:身份(serverId/zoneId)与数据库/ORM 走 CoreLib(经
CoreLibApi),本 SDK 只负责「读已合并配置 + 查发现」,两者不重叠。 注:CoreLib 自身的 serverId/zone 来源方向是「优先取自 Beacon agent、不在场才降级本地」,它用下方awaitIdentity取身份;这不影响普通业务插件继续从 CoreLib 拿身份。
1. SDK 组成(两个工件)
| 工件 | 是什么 | 何时用 |
|---|---|---|
beacon-agent-api | 纯 Java8 只读契约(接口 + 值对象),零第三方依赖 | 必需,直接面对 BeaconAgent 门面时 |
beacon-agent-kit | 纯 Java8 便捷层,只依赖 agent-api,封装下游样板 | 推荐,省去回退判据/订阅竞态等踩坑 |
两者运行期都由 BeaconAgent 插件提供(已 shade 进 BeaconAgent.jar/BeaconAgentProxy.jar),下游只 compileOnly 依赖、不打进自己的 jar。
2. 坐标与版本对齐
- 坐标:
top.wcpe.beacon:beacon-agent-api:<版本>、top.wcpe.beacon:beacon-agent-kit:<版本>。 - 消费仓库:
https://repo.wcpe.top/repository/maven-public/(正式发布的 SDK 坐标在此解析)。 - 版本:跟随仓库根
VERSION,与控制面 / 两个 agent jar 三组件恒一致。1.0.0起按 SemVer 管理公开契约。 - 版本对齐矩阵(硬约束):部署的 BeaconAgent 版本必须 ≥ 下游编译所用 agent-api/kit 版本(运行期提供方不得旧于编译期契约),否则可能
NoSuchMethodError。
本指南面向下游业务插件开发者,只需按上方坐标
compileOnly依赖,不需要把任何东西发布到远程仓库。 SDK 自身的构建与发布(远程仓库地址、凭据注入、RC/GA 晋级与校验)由维护者执行, 见运维手册与仓库Makefile的release-*目标,不属于下游接入内容。
3. 接入(下游 build + plugin.yml)
// 下游业务插件 build.gradle.kts
repositories {
maven("https://repo.wcpe.top/repository/maven-public/")
}
dependencies {
compileOnly("top.wcpe.beacon:beacon-agent-api:<版本>") // 只读契约
compileOnly("top.wcpe.beacon:beacon-agent-kit:<版本>") // 便捷层(可选但推荐)
}
TabooLib 插件按惯例对 BeaconAgent 声明软/硬依赖(让下游 ClassLoader 能解析 top.wcpe.beacon.agent.api.* / ...kit.* 并共享运行期门面)。
4. 最小接入示例
import top.wcpe.beacon.agent.kit.BeaconAccess
object MyEconomyPlugin : Plugin() {
private val beacon = BeaconAccess() // 无状态门面,可自由 new
@Awake(LifeCycle.ENABLE)
fun enable() {
// 读一份合并后的结构化配置;agent 不在场则回退本插件内置默认文件(回退由下游决定,kit 不做)
val raw = if (beacon.isBeaconPresent()) beacon.rawConfig("plugins/MyEconomy/config.yml").orElse(null)
else readBundledDefault("plugins/MyEconomy/config.yml")
reloadEconomy(raw)
// 订阅热更,只订阅本插件目录 plugins/MyEconomy/(别人的变更不会叫醒你):注册即重放当前值;
// agent 未就绪不丢订阅,周期 pump() 在其转可用后补注册重放
val sub = beacon.subscribeConfig("plugins/MyEconomy/") { changedDataIds, _ ->
// 回调是 Set 语义:给你「本次变了哪些 dataId」+ 变更后整体 md5(第二个参数这里不需要,用 _ 忽略),
// **不是**「单个 dataId + 新内容」——新内容要自己用 beacon.rawConfig(...) 现读
submit(async = true) { // 回调不在 MC 主线程,但重活/落地仍建议切线程
if (changedDataIds.contains("plugins/MyEconomy/config.yml")) {
reloadEconomy(beacon.rawConfig("plugins/MyEconomy/config.yml").orElse(null))
}
}
}
// sub 由你保管,DISABLE 时 sub.close();按需周期调用 sub.pump()
// 若要上报重载结果(交付据此做一票否决),实现 ReportingConfigChangeListener 而非裸 lambda:
// 无关变更必须 return ConfigReloadResult.ok(),见 §6 第 5 条
}
// 查发现务必在异步线程(同步 HTTP)
fun sameZonePeers(): List<String> {
if (!beacon.isBeaconPresent()) return emptyList()
val zone = corelibZoneId() // ← 业务插件的 zone 经 CoreLib 取(CoreLib 自身优先取自 Beacon agent)
return beacon.instancesInZone(corelibGroupId(), zone).map { it.serverId() }
}
}
三个容易写错的点:① 回调参数是集合(
Set<String> changedDataIds+String newMd5),不是「单个 dataId + 内容」, 新内容要自己按 dataId 现读;②dataId是配置文件登记时的逻辑名,即目标机上的相对路径(如plugins/MyEconomy/config.yml); ③ 注册即重放:注册成功时会立刻回调一次,携带当前匹配前缀的全部 dataId(无前缀时即全部 dataId)作为基线, 所以别指望「第一次回调 = 有人改了配置」;带前缀订阅若当前零匹配,这次重放会整次跳过(不回调)。