快速开始
本页说明如何构建、部署 Emaki runtime,以及第三方插件如何正确引用 API。API Jar 只是编译依赖,不是服务器插件。
环境要求
| 项目 | 要求 |
|---|---|
| Java | 25 |
| 服务端 | Paper / Folia,构建 Paper API 基线为 1.21.8 |
| 构建工具 | Maven,多模块聚合工程 |
| 必装基础模块 | EmakiCoreLib |
构建 runtime
在项目根目录执行:
mvn -DskipTests package默认 reactor 共 22 个模块:14 个 API 模块全部包含(CoreLibApi、ItemApi、StrengthenApi、SkillsApi、AttributeApi、ForgeApi、CookingApi、GemApi、LevelApi、CodexApi、StorageApi、StationApi、AccessoryApi、MobsApi),以及 8 个 runtime(CoreLib、Forge、Strengthen、Cooking、Attribute、Level、Codex、Station)。根目录存在 .key 时会自动激活 private-modules profile,再追加 6 个 runtime:Skills、Gem、Item、Storage、Accessory、Mobs。也可显式运行:
mvn -DskipTests -Pprivate-modules package插件本体通常位于 Emaki*/target/。只把 EmakiCoreLib-*.jar、EmakiForge-*.jar 这类 runtime Jar 放入服务器 plugins/。
不要安装或打包 API Jar
emaki-*-api-*.jar 不得放入 plugins/,也不得 shade 或 relocate 到你的插件。每个 Emaki runtime Jar 已内嵌一份未 relocate 的 API 类;重复副本会由不同 ClassLoader 定义为不同类型,导致 Bukkit 事件监听与 API bridge 静默失效。
第三方插件引用 API
所有 API 都发布在:
https://repo.crypticlib.com/repository/maven-public/Maven
下面以 Forge 为例;将 artifactId 和版本替换为后文表格中的目标模块。示例版本按当前 POM 写为 4.7.11。
<repositories>
<repository>
<id>emaki</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.7.11</version>
<scope>provided</scope>
</dependency>
</dependencies>Gradle Kotlin DSL
repositories {
maven("https://repo.crypticlib.com/repository/maven-public/")
}
dependencies {
compileOnly("emaki.jiuwu.craft:emaki-forge-api:4.7.11")
}不要使用 implementation、api、Shadow relocation 或其他会把 API 类复制进产物的配置。
当前 14 对 runtime / API 版本
以下版本来自当前模块 POM。服务器安装 runtime Jar;开发工程仅以 provided / compileOnly 引用 API artifact。.key 私有 profile 只影响 runtime 模块是否参与构建,不改变 API 坐标。
| Runtime Jar | API 坐标 | 当前版本 | API Jar 放进 plugins/? |
|---|---|---|---|
EmakiCoreLib-4.8.1.jar | emaki.jiuwu.craft:emaki-corelib-api:4.8.1 | 4.8.1 | 否 |
EmakiAttribute-4.7.11.jar | emaki.jiuwu.craft:emaki-attribute-api:4.7.11 | 4.7.11 | 否 |
EmakiForge-4.7.11.jar | emaki.jiuwu.craft:emaki-forge-api:4.7.11 | 4.7.11 | 否 |
EmakiStrengthen-4.7.19.jar | emaki.jiuwu.craft:emaki-strengthen-api:4.7.19 | 4.7.19 | 否 |
EmakiCooking-4.2.9.jar | emaki.jiuwu.craft:emaki-cooking-api:4.2.9 | 4.2.9 | 否 |
EmakiGem-2.7.15.jar | emaki.jiuwu.craft:emaki-gem-api:2.7.15 | 2.7.15 | 否 |
EmakiLevel-1.5.8.jar | emaki.jiuwu.craft:emaki-level-api:1.5.8 | 1.5.8 | 否 |
EmakiSkills-2.7.11.jar | emaki.jiuwu.craft:emaki-skills-api:2.7.11 | 2.7.11 | 否 |
EmakiItem-2.7.16.jar | emaki.jiuwu.craft:emaki-item-api:2.7.16 | 2.7.16 | 否 |
EmakiCodex-1.0.5.jar | emaki.jiuwu.craft:emaki-codex-api:1.0.5 | 1.0.5 | 否 |
EmakiStorage-1.0.6.jar | emaki.jiuwu.craft:emaki-storage-api:1.0.6 | 1.0.6 | 否 |
EmakiStation-1.0.8.jar | emaki.jiuwu.craft:emaki-station-api:1.0.8 | 1.0.8 | 否 |
EmakiAccessory-1.0.3.jar | emaki.jiuwu.craft:emaki-accessory-api:1.0.3 | 1.0.3 | 否 |
EmakiMobs-1.0.6.jar | emaki.jiuwu.craft:emaki-mobs-api:1.0.6 | 1.0.6 | 否 |
各模块完整公开方法、结果模型、线程要求和事件覆盖请查看对应 docs/modules/<module>/api.md 与 events.md。
运行时依赖声明
API 编译依赖不会让服务器自动加载对应 runtime。你的 paper-plugin.yml 仍应声明所需插件依赖。例如功能必须依赖 Forge 时使用硬依赖;只是可选联动时使用 soft dependency,并在运行时探测:
if (!EmakiForgeApi.status().usable()) {
// 隐藏 Forge 联动;不要让可选模块拖垮整个插件
return;
}API 门面在 bridge 未安装或重载窗口中返回非 null 降级实现:目录通常为空,状态变更返回 EmakiResult 的 unavailable failure。真正完全没有 soft-depend runtime 时,首次加载 API 类也可能发生链接错误;可选集成应把相关类隔离,并在边界捕获 LinkageError | RuntimeException。
EmakiResult 最小处理方式
不要把 null、空集合、0 或 false 同时解释为“业务结果”和“插件缺失”。结果型操作使用 EmakiResult<T>:
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 里直接查目标模块的表,改为挂就绪回调:
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:
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 的规则。