主题
这页不是面向服主的配置文档,而是纯面向二开作者的开发者说明。
目标只有一个:
让你在接入 HNCore 时,先看清 该依赖什么、别依赖什么、什么时候应该重新取服务、哪些能力适合业务层直接使用。
如果你正在写:
- HN 系列下游插件
- 自己的 Bukkit / Paper 业务插件
- 需要复用共享存储、ClusterBus、物品库、GUI Kit 的模块
建议先读这页,再读 HNCore Java API。
一、先记住一个总原则
业务插件优先依赖 HNCoreAPI,不要直接依赖 HNCore 的底层实现类。
从构建角度看,当前推荐方式是:
- 开发依赖:
hncore-api - 服务器运行:
hncore
也就是说,下游插件编译时应优先依赖 hncore-api,而不是直接拿运行版 hncore 作为开发依赖。
也就是说,优先顺序应该是:
- 先看
HNCoreAPI有没有现成入口 - 如果有,就优先通过
HNCoreAPI获取服务 - 只有在确实做底层扩展时,才考虑更低层的实现类
这是因为当前 HNCore 已经形成了比较明确的能力分层。
二、对外能力分层
可以把 HNCore 当前对外能力简单分成三层。
二点五、当前构建与依赖方式
当前 HNCore 已经拆成:
hncore-api:给下游插件编译依赖hncore:服务器实际运行插件
推荐写法:
kotlin
dependencies {
compileOnly("com.github.hnplugins:hncore-api:x.y.z")
}其中 x.y.z 应替换为你当前部署的 HNCore 对应版本。
如果你当前正在接入 HNCore 1.1.0 这一轮新增的公共能力,例如:
- 物品 identity / matcher / serial 协议
- PlaceholderProvider 统一出口
- ItemSpec / ItemCodec / ItemSnapshot
那下游项目也应该同步使用 hncore-api:1.1.0,不要继续把依赖写成 1.0.0。
服务器运行时仍然安装 HNCore 的运行版插件 jar,不需要额外再放一份单独的 hncore-api 到 plugins 目录。
1)稳定公共入口层(推荐业务插件直接使用)
主要指:
HNCoreAPIClusterBusClusterActionRegistryClusterStatusServiceClusterPingServiceItemLibraryGatewayItemFacadePlayerLookupServiceCoreScheduler- GUI Kit 对外入口
- 公式引擎入口
- 统一授权接入入口
ConfigManager创建入口
这层的特点是:
- 语义相对稳定
- 已经被设计成给下游业务模块复用
- 文档、命令、配置都围绕这些入口组织
2)高级公共能力层(能用,但要知道边界)
主要指:
SharedPubSubServiceSharedKeyValueServiceSharedStorageManager- 共享数据库相关 manager / section 访问
这层适合:
- 你明确知道自己在做“共享基础设施接入”
- 你清楚 Redis / MySQL / fallback 的语义
- 你不是在设计新的业务广播协议,而是在复用底层能力
3)底层实现层(不建议业务插件直接依赖)
例如:
StorageRedisServiceStorageMysqlServiceStorageMysqlKeyValueStore- Cluster transport / impl 细节类
这层的特点是:
- 暴露出来不代表推荐直接依赖
- 更接近 HNCore 内部实现
- 未来演进时更可能变化
如果你的目标只是做一个业务插件,通常不要把这些类当成主要接入面。
占位符推荐规范
如果你的业务插件要通过 HNCore 暴露 PlaceholderAPI 占位符,建议把这三层概念分开理解:
1)内部命名空间
这是 HNCore 内部统一占位符服务使用的 token 语义:
text
attribute.attack_damage
mail.unread
shop.balance也就是说,内部统一使用 namespace.key。
2)统一桥接入口
HNCore 会自动提供一个统一的 PlaceholderAPI 入口:
text
%hncore_<namespace>_<key>%例如:
text
%hncore_attribute_attack_damage%
%hncore_mail_unread%这套写法适合:
- 从框架视角统一理解所有业务命名空间
- 做调试和底层规范说明
- 在 HNCore 文档里统一表达各模块占位符出口
3)业务前缀入口
现在 PlaceholderProvider 还可以声明自己的业务前缀,例如:
java
@Override
public List<String> papiIdentifiers() {
return List.of("hnattr");
}这样 HNCore 会自动额外暴露:
text
%hnattr_attack_damage%而不需要业务插件自己再手写一层独立的 PlaceholderExpansion 壳子。
推荐实践
- 对最终用户 / 服主文档:优先写业务前缀入口
- 例如:
%hnattr_attack_damage%
- 例如:
- 对框架 / 开发者文档:可以补充统一桥接入口
- 例如:
%hncore_attribute_attack_damage%
- 例如:
这样既保留统一框架视角,又不会把 hncore_ 这个前缀强行暴露成唯一用户入口。
三、最推荐的接入方式
1)日志与配置
最普通的业务插件,几乎都可以从这里开始:
java
var logger = HNCoreAPI.getLogger(this);
var configManager = HNCoreAPI.createConfigManager(this);适用场景:
- 自己的插件配置文件管理
- 统一日志输出风格
2)共享物品库与高层物品门面
如果你的模块要兼容多个外部物品库,不要自己分别接 Mythic / Neige / Baikiruto。
优先方式:
java
var gateway = HNCoreAPI.getItemLibraryGateway();
var result = gateway.resolve("bi", "example_item", 1);如果你的场景还涉及 identity、标签读写、匹配规则或物品编解码,则进一步优先:
java
var itemFacade = HNCoreAPI.getItemFacade();适合:
- 邮件附件
- 奖励发放
- 商店商品
- 任务奖励
- 配置化生成物品
2.5)玩家查找
如果你的模块需要统一处理名称 / UUID 解析、在线补全或显示名,不要每个项目都自己重复写一套。
优先方式:
java
var lookup = HNCoreAPI.getPlayerLookupService();
var target = lookup.resolve(input);
var display = lookup.displayName(target);2.6)统一调度器
如果你的模块只是想切主线程、跑异步或做延时任务,优先考虑:
java
HNCoreAPI.getScheduler().runSync(this, task);
HNCoreAPI.getScheduler().runAsync(this, task);这样更容易和 HNCore 当前的统一线程语义保持一致。
3)ClusterBus
如果你的模块要做跨服控制消息,不要直接自己拼 Redis channel。
优先方式:
java
var bus = HNCoreAPI.getClusterBus();
var registry = HNCoreAPI.getClusterActionRegistry();然后按 namespace/action 组织业务消息。
4)GUI Kit
如果你要做的是可复用 GUI,而不是一次性的硬编码小菜单,优先考虑:
createGuiTemplateLoader(...)createGuiTemplateRenderer()createGuiContext()
5)公式引擎
如果你有数值表达式或脚本公式需求,优先走:
createExpressionEngine(...)createNumericFormulaEngine(...)
而不是自己再内嵌另一套公式系统。
四、生命周期:什么时候可以取服务
HNCore 必须先完成启用
下游插件最基本的前提是:
- HNCore 必须先启用
- 你的插件再开始取
HNCoreAPI相关服务
所以依赖插件一般要确保:
plugin.yml里正确声明依赖 / softdependonEnable()里先做依赖检查
一个常见写法:
java
var hook = HNCoreAPI.createPluginHook(this);
if (hook.hookRequired("HNCore")) {
return;
}有些 API 可能返回 null
这不一定代表异常,很多时候只是当前能力未启用。
例如:
getSharedDatabaseManager():共享数据库未启用时可能为nullgetSharedPubSubService():Redis Pub/Sub 未就绪时可能为nullgetClusterBus():极早期阶段或异常状态下可能为nullgetSharedItemLibraryService():旧兼容入口,在 HNCore 尚未完成初始化时可能为null
需要补充的是:
getItemLibraryGateway()作为门面对象通常可以直接获取- 但它背后的具体来源是否可用,仍应通过
isSourceAvailable(...)或解析结果来判断
所以对外模块的原则是:
- 先判空 / 先判可用,再使用
五、reload 语义:哪些对象不要长期死缓存
这是当前最关键的开发注意点之一。
1)HNCore reload 会重建部分运行时服务
当前 HNCore.reload() / /hncore reload 不只是读文件,它还会重建:
- 共享数据库
- 共享存储
- ClusterBus
- ClusterStatusService
- ClusterPingService
- 共享物品库相关状态
- 脚本系统运行态
2)这意味着什么
这意味着某些对象在 reload 后可能已经不是原来的实例。
尤其是:
ClusterBusClusterActionRegistryClusterStatusServiceClusterPingService- 共享数据库 / 共享存储相关 service
3)推荐做法
做法 A:每次使用时重新取
最稳:
java
var bus = HNCoreAPI.getClusterBus();适合:
- 调用频率不高
- 只是偶尔广播或查询状态
做法 B:缓存,但要有重绑机制
如果你必须缓存,比如你注册了一批 cluster handler,那么应该像 HNAttribute 那样做:
- 定期检查当前 bus / registry 是否已更换
- 如果变了,注销旧订阅并重新绑定
4)不推荐做法
不建议这样:
- 插件启动时拿一次
ClusterBus - 永久保存到字段里
- 从此不再刷新
因为 /hncore reload 后这个引用可能已经过期。
五点五、日志等级:INFO / DEBUG / TRACE 怎么选
当前 HNCore 的日志已经从单纯 debug: true/false 升级为等级模型,推荐主配置写法:
yml
log:
level: INFO一般建议:
INFO:日常生产环境DEBUG:模块级排障TRACE:高噪声明细链路,例如 SQL、公式、ClusterBus 明细、Buff / Mythic 技能过程
旧写法 debug: true/false 仍兼容,但新文档和新项目都更推荐统一使用 log.level。
如果你在自己的下游项目里使用 HNCoreAPI.getLogger(...),现在也可以逐步开始使用:
logger.trace(...)logger.isDebugEnabled()logger.isTraceEnabled()
六、线程语义:哪些回调在哪个线程
1)ClusterActionRegistry 的 handler
当前 ClusterActionRegistry.dispatch(...) 会把 handler 切回 Bukkit 主线程执行。
也就是说:
- 你在
registry.register(namespace, action, handler)里写的 handler - 默认可以安全地访问大多数 Bukkit 主线程 API
这也是为什么它比直接裸用 Redis Pub/Sub 更适合业务层。
2)ClusterPingService 的 CompletableFuture
pingAll() / pingNode() 返回的是 CompletableFuture。
需要注意:
- 它的完成回调不保证一定在主线程
- 如果你在
thenAccept(...)里要操作 Bukkit API,最好自己切回主线程
3)SharedPubSubService
如果你直接使用 SharedPubSubService:
subscribe(...)与subscribeAsync(...)线程语义不同- 这已经是偏底层用法
如果你只是做业务广播,优先改用 ClusterBus + ClusterActionRegistry,这样线程语义更清晰。
七、ClusterBus:什么时候该用它,什么时候不该用它
推荐用 ClusterBus 的场景
- 模块级跨服 reload
- 指定玩家跨服刷新
- 管理指令广播
- 轻量控制消息
- ping / status / 健康检查
不建议直接用 ClusterBus 传什么
不建议把它当成:
- 大量数据复制通道
- 高频大包同步层
- 长事务结果总线
它更适合:
- 控制消息
- 短 payload
- 明确 namespace/action 的指令型通信
namespace/action 设计建议
推荐这样组织:
- namespace:按插件或子系统划分
- action:按具体动作划分
例如:
hnattribute/reloadhnattribute/refresh-playerhnmail/mail-syncmyplugin/rebuild-cache
targetNode 的建议
- 管理广播:用
broadcast(...) - 定向控制:用
sendTo(...) - ping / 诊断:优先用
ClusterPingService
八、SharedPubSubService 和 ClusterBus 该怎么选
这是最容易混的点。
用 ClusterBus,如果你在做业务层跨服控制
例如:
- 某插件要广播“刷新玩家数据”
- 某插件要广播“重载配置”
- 某插件要做轻量节点间信号交互
用 SharedPubSubService,如果你明确在做底层 Redis 通道接入
例如:
- 你已经有一套明确的频道协议
- 你就是要直接操作 Redis channel
- 你能自己承担线程与协议边界
简单判断规则
如果你在犹豫:
“我是不是该直接 publish 一个 Redis channel?”
大多数情况下答案是:
先不要,优先看能不能改成 ClusterBus。
九、SharedKeyValueService 该怎么理解
这是共享键值存储抽象,不是“必须是 Redis”。
当前语义是:
- 优先 Redis
- Redis 不可用时回退 MySQL
- 两者都不可用时抛异常
适合场景:
- 轻量共享状态
- 短字符串配置 / 标记
- 简单分布式标志位
不适合场景:
- 大对象存储
- 复杂查询
- 事务型业务数据
如果你要做复杂结构化数据,优先看共享数据库,而不是把一切塞进 key-value。
十、ItemLibraryGateway / ItemFacade 该怎么理解
当前更推荐把物品库相关能力分成两层理解:
ItemLibraryGateway
这是统一解析外部物品库物品的稳定门面,不是某一个库的专用包装。
推荐业务模块使用它的原因:
- 减少对具体外部插件 API 的直接耦合
- 可以统一失败语义
- 可以统一配置写法
- 可以统一 source 别名与规范来源处理
推荐做法:
- 对外配置里让用户写
source + itemId + amount - 业务代码优先通过
getItemLibraryGateway()解析
ItemFacade
如果你的模块不只是“解析库物品”,还要处理:
- identity
- 标签读写
- 匹配规则
- 物品编解码
那更适合直接把 ItemFacade 当成高层入口。
不要做的事:
- 一边接
ItemLibraryGateway/ItemFacade - 一边又直接硬编码调 Mythic / Neige / Baikiruto API
这样会把抽象层价值抵消掉。
十一、GUI Kit 当前适合做到哪一步
当前 GUI Kit 更适合:
- 标题 / 布局 / 静态模板
- 占位符渲染
- 分页骨架
- 通用导航按钮
当前仍然推荐:
- 模板负责外观
- Java 代码负责点击逻辑
也就是说,不要预期它已经变成一套“把全部业务逻辑都写进 yml”的重型框架。
十二、一个最小插件接入示例
java
public final class MyPlugin extends JavaPlugin {
@Override
public void onEnable() {
var hook = HNCoreAPI.createPluginHook(this);
if (hook.hookRequired("HNCore")) {
return;
}
var logger = HNCoreAPI.getLogger(this);
logger.info("HNCore version = " + HNCoreAPI.getVersion());
var bus = HNCoreAPI.getClusterBus();
var registry = HNCoreAPI.getClusterActionRegistry();
if (bus != null && registry != null) {
registry.register("myplugin", "refresh", context -> {
logger.info("收到 cluster refresh");
});
}
}
}十三、一个更稳的 ClusterBus 使用建议
如果你的插件长期依赖 cluster handler,推荐思路是:
- 启动时先绑定一次
- 保存当前
bus / registry引用 - 在合适时机检查引用是否变化
- 如果变化,注销旧订阅并重新注册
如果你不想自己做这套重绑逻辑,那就至少:
- 不要长期缓存旧 bus
- 每次广播前重新
HNCoreAPI.getClusterBus()
十四、推荐阅读顺序
如果你是开发者,建议顺序是:
- 先读这页:开发者 API 与集成指南
- 再读:HNCore Java API
- 如果你要做跨服,再读:ClusterBus 与集群控制面
- 如果你要做配置与运维联动,再读:storage.yml 配置说明
- 如果你要做 GUI,再读:GUI Kit 与模板规范
十五、最后给开发者的一句话
把 HNCore 当成:
- 一套能力底座
- 一套稳定入口集合
- 一套不要越层乱用的分层系统
你接入得越贴近 HNCoreAPI 和稳定公共接口,后面维护成本通常就越低。
