简体中文
快速开始
全文分两部分
只想下载 jar 装来用, 看只想安装使用 一节就够了; 要自己编译或开发插件, 再看后面的构建与 API 集成说明
完成 Emaki runtime 的构建与部署, 并讲清第三方插件怎么正确引用 API. 记住: API Jar 只是编译时用的依赖, 它不是服务器插件, 不要放进 plugins/
只想安装使用
- 下载各个
Emaki*.jar— 注意不要下载名字里带-api的那种 - 放进服务器的
plugins/目录 (EmakiCoreLib 必须装, 其他模块按需要装) - 启动服务器, 先让它生成默认配置, 再按需要修改
plugins/Emaki*/下的文件 - 改完配置执行对应模块的重载命令, 或者重启服务器
想知道该装哪些模块, 直接看 项目介绍 里的"常见组合方式"
环境要求
| 项目 | 要求 |
|---|---|
| Java | 25 |
| 服务端 | Paper / Folia, 构建 Paper API 基线为 26.1.2.build.74-stable |
| 构建工具 (自己编译时才需要) | Gradle 多模块工程 (wrapper 固定 Gradle 9.8.0) |
| 必装基础模块 | EmakiCoreLib |
构建 runtime
先把 JAVA_HOME 指向 JDK 25, 再在项目根目录执行下面的命令:
bash
./gradlew build -x test默认构建 27 个模块: 16 个 API 模块全部包含 (CoreLibApi, ItemApi, StrengthenApi, SkillsApi, AttributeApi, ForgeApi, CookingApi, GemApi, LevelApi, CodexApi, StorageApi, StationApi, AccessoryApi, MobsApi, StructureApi, DungeonApi), 以及 11 个 runtime (CoreLib, Forge, Strengthen, Cooking, Attribute, Level, Codex, Station, Mobs, Dungeon, Dummy). 仓库根目录存在 .key 文件时, settings.gradle 会额外纳入 6 个私有 runtime: Skills, Gem, Item, Storage, Structure, Accessory
插件本体在各模块的 Emaki*/build/libs/ 下. 你只需把 EmakiCoreLib-*.jar, EmakiForge-*.jar 这类 runtime Jar 放入服务器 plugins/
不要安装 API Jar, 也不要把它打包进自己的插件
emaki-*-api-*.jar 不能放进 plugins/, 也不能 shade 或 relocate 到你自己的插件产物里. 原因: 每个 Emaki runtime Jar 内部已经自带一份 API 类, 再多一份副本的话, Java 会把它们当成两个完全不同的类型, 结果就是事件监听和 API 调用悄悄失效 — 不报错, 很难排查
第三方插件引用 API
所有 API 都发布在:
text
https://repo.crypticlib.com/repository/maven-public/Maven
下面以 Forge 为例; 你要用哪个模块, 就把 artifactId 和版本换成后文表格里的目标模块. 示例版本按当前 build.gradle 写为 4.8.6 (Forge API 版本)
xml
<repositories>
<repository>
<id>jiuwu-releases</id>
<url>https://repo.crypticlib.com/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>emaki.jiuwu.craft</groupId>
<artifactId>emaki-forge-api</artifactId>
<version>4.8.6</version>
<scope>provided</scope>
</dependency>
</dependencies>Gradle Kotlin DSL
kotlin
repositories {
maven("https://repo.crypticlib.com/repository/maven-public/")
}
dependencies {
compileOnly("emaki.jiuwu.craft:emaki-forge-api:4.8.6")
}不要使用 implementation, api, Shadow relocation 或其他会把 API 类复制进产物的配置
当前 15 对 runtime / API 版本
以下版本来自当前各模块 build.gradle. runtime 与 API 的版本是各自独立维护的, 同一组的两个版本不一定相同
服主只需看前两列: 第 1 列是要放进 plugins/ 的 Jar 名, 第 2 列是它的版本. 后面三列给你开发时参考: 只用 provided / compileOnly 引用 API 构件, 别打进插件产物. "私有"门控只影响从源码构建时哪些 runtime 会参与编译, 不影响 API 坐标
| Runtime Jar | runtime 版本 | API 坐标 | API 版本 | API Jar 放进 plugins/? |
|---|---|---|---|---|
EmakiCoreLib-4.8.36.jar | 4.8.36 | emaki.jiuwu.craft:emaki-corelib-api:4.8.28 | 4.8.28 | 否 |
EmakiAttribute-4.8.7.jar | 4.8.7 | emaki.jiuwu.craft:emaki-attribute-api:4.8.6 | 4.8.6 | 否 |
EmakiForge-4.8.8.jar | 4.8.8 | emaki.jiuwu.craft:emaki-forge-api:4.8.6 | 4.8.6 | 否 |
EmakiStrengthen-4.8.9.jar | 4.8.9 | emaki.jiuwu.craft:emaki-strengthen-api:4.8.7 | 4.8.7 | 否 |
EmakiCooking-4.3.8.jar | 4.3.8 | emaki.jiuwu.craft:emaki-cooking-api:4.3.6 | 4.3.6 | 否 |
EmakiGem-2.8.11.jar | 2.8.11 | emaki.jiuwu.craft:emaki-gem-api:2.8.8 | 2.8.8 | 否 |
EmakiLevel-1.6.9.jar | 1.6.9 | emaki.jiuwu.craft:emaki-level-api:1.6.6 | 1.6.6 | 否 |
EmakiSkills-2.8.8.jar | 2.8.8 | emaki.jiuwu.craft:emaki-skills-api:2.8.7 | 2.8.7 | 否 |
EmakiItem-2.8.16.jar | 2.8.16 | emaki.jiuwu.craft:emaki-item-api:2.8.15 | 2.8.15 | 否 |
EmakiCodex-1.1.6.jar | 1.1.6 | emaki.jiuwu.craft:emaki-codex-api:1.1.5 | 1.1.5 | 否 |
EmakiStorage-1.1.5.jar | 1.1.5 | emaki.jiuwu.craft:emaki-storage-api:1.1.3 | 1.1.3 | 否 |
EmakiStation-1.1.8.jar | 1.1.8 | emaki.jiuwu.craft:emaki-station-api:1.1.7 | 1.1.7 | 否 |
EmakiAccessory-1.1.8.jar | 1.1.8 | emaki.jiuwu.craft:emaki-accessory-api:1.1.7 | 1.1.7 | 否 |
EmakiMobs-1.0.13.jar | 1.0.13 | emaki.jiuwu.craft:emaki-mobs-api:1.0.9 | 1.0.9 | 否 |
EmakiStructure-1.0.5.jar | 1.0.5 | emaki.jiuwu.craft:emaki-structure-api:1.0.1 | 1.0.1 | 否 |
EmakiDungeon-1.0.2.jar | 1.0.2 | emaki.jiuwu.craft:emaki-dungeon-api:1.0.1 | 1.0.1 | 否 |
各模块完整公开方法, 结果模型, 线程要求和事件覆盖请查看对应 docs/modules/<module>/api.md 与 events.md
运行时依赖声明
只写 API 编译依赖, 服务器不会自动加载对应的 runtime. 你要在自己的 paper-plugin.yml 里声明需要的插件依赖: 功能离了 Forge 就跑不了, 用硬依赖; 只是可选联动, 就用 soft dependency, 并在运行时探测:
java
if (!EmakiForgeApi.status().usable()) {
// 隐藏 Forge 联动; 不要让可选模块拖垮整个插件
return;
}bridge 没装或正处于重载窗口时, API 门面会返回非 null 的降级实现: 目录会是空的, 状态变更返回 EmakiResult 的 unavailable failure. 如果 soft-depend runtime 真的完全没装, 首次加载 API 类也可能发生链接错误; 所以可选集成要把相关类隔离, 并在边界捕获 LinkageError | RuntimeException
EmakiResult 最小处理方式
不要把 null, 空集合, 0 或 false 同时解释为"业务结果"和"插件缺失". 结果型操作使用 EmakiResult<T>:
java
if (result.isFailure()) {
handleFailure(result.failureKind(), result.reasonKey());
} else {
result.optionalValue().ifPresent(value -> handleValue(value, result.isPartial(), result.reasonKey()));
}EmakiResult<Unit> 表示无负载成功; optionalValue() 是统一的可选负载访问器. FailureKind 当前精确包括 UNAVAILABLE, NOT_FOUND, INVALID_INPUT, REJECTED, CANCELLED, TARGET_OFFLINE, WRONG_THREAD, INTERNAL_ERROR. 业务拒绝, 事件取消, 模块未安装和线程错误应分别处理
等待模块就绪
模块加载顺序不保证目标模块的首次数据加载早于你的 onEnable. 所以别在自己的 onEnable 里直接查目标模块的表, 改为挂就绪回调:
java
EmakiCoreLibApi.whenReady(this, "EmakiItem", () -> {
// 此时 EmakiItem 的定义已加载
buildMyIndex();
});- 回调只执行一次. 目标模块 reload 会重新经历 not-ready → ready, 但不会重放已触发的回调. 需要跟随每次 reload 用
addModuleListener(见下节), 不要在回调内重新注册 -- 那会无限递归直至StackOverflowError moduleName传字面量 (如"EmakiItem"), 不要引用目标模块 API jar 里的常量- 返回值是可关闭的
ReadinessRegistration; 目标模块已就绪时回调同步执行并返回 inactive 句柄 - 回调线程是把模块标记为就绪的那个线程, 不保证是 Bukkit owner thread. 触碰玩家, 背包, 世界或 GUI 前先用
EmakiCoreLibApi.scheduling()调度 - 只想"没就绪就跳过"时用
EmakiCoreLibApi.isModuleReady(name)轮询即可
reload 窗口内, 目标模块的 status() 会报 loading, 返回 EmakiResult 的方法返回 unavailable(); 而 Set, List, boolean 这类纯查询无法表达不可用, 可能返回陈旧或空结果. 不要把空结果当成"该条目不存在"
监听模块重载
你如果在插件里缓存了其他模块的内容, 就要在对方重载时失效并重建缓存. whenReady 只响一次, 所以改用 addModuleListener:
java
EmakiCoreLibApi.addModuleListener(this, "EmakiItem", phase -> {
switch (phase) {
case LOADING, ABSENT -> myCache.invalidate();
case READY -> myCache.rebuild();
}
});- 三个相位:
LOADING(开始替换数据, 失效缓存),READY(数据可用, 重建缓存, 首次加载与每次 reload 都触发),ABSENT(模块已停用) - 持续有效, 直到句柄关闭. 同一 owner 对同一模块重复注册是替换而非追加
- 注册时若模块已就绪, 不会立即回调; 需要当前状态就查
isModuleReady - 线程约束同
whenReady: 运行在发布状态的线程上, 不保证是 owner thread - 只提供"知道重载发生了", 不提供让第三方触发别的模块重载的入口
完整相位表与时序边界见 docs/modules/corelib/api.md
Folia 与线程
- 玩家, 实体, 背包或持有物品的操作要求对应 entity-owner thread
- 方块/位置操作要求 location/region owner thread
- global scheduler 不等于任意玩家 owner thread
- 返回
CompletableFuture只表示可以异步提交; future completion callback 不保证 Bukkit 安全线程 - 错误线程时, 公开写 API 应返回
FailureKind.WRONG_THREAD, 调用方应通过 CoreLibscheduling()或平台 scheduler 正确调度
推荐安装顺序
- EmakiCoreLib: 所有业务模块的基础 runtime
- EmakiAttribute: 需要属性, 资源或战斗联动时安装
- 装备线: EmakiItem, EmakiForge, EmakiStrengthen, EmakiGem
- 成长/玩法线: EmakiLevel, EmakiSkills, EmakiCooking, EmakiCodex, EmakiStorage
- 按模块配置选择 PlaceholderAPI, Vault/ExcellentEconomy, MythicMobs, CraftEngine, ItemsAdder 等软依赖
实际加载顺序以各模块 paper-plugin.yml 为准. CoreLib 是基础依赖; 软依赖缺失时应只关闭对应桥接能力
首次启动检查
- 只将需要的 runtime Jar 放入
plugins/ - 启动服务器, 等待生成默认配置
- 停服或确认没有玩家正在操作相关系统后修改
plugins/Emaki*/配置 - 重启, 先处理控制台最早出现的加载错误
- 逐模块验证 GUI, 命令, 配置重载和外部依赖桥接
- 第三方插件启动时记录各 API 的
status(), 但不要调用 runtime 生命周期专用的install,uninstall或实现Bridge
装备技能 PDC 编解码约定
别把 EquipmentSkillPdcCodec 当成就绪标志: 它只是 emaki-skills-api 里 emaki.jiuwu.craft.skills.api.pdc 包下的独立低层 PDC 协议工具, 不代表 EmakiSkills runtime 已就绪; Item, Forge, Gem, Strengthen 的内部嵌入/relocate 策略也不改变第三方插件必须使用 compileOnly 的规则