Skip to content

快速开始 ​

全文分两部分

只想下载 jar 装来用, 看只想安装使用 一节就够了; 要自己编译或开发插件, 再看后面的构建与 API 集成说明

完成 Emaki runtime 的构建与部署, 并讲清第三方插件怎么正确引用 API. 记住: API Jar 只是编译时用的依赖, 它不是服务器插件, 不要放进 plugins/

只想安装使用 ​

  1. 下载各个 Emaki*.jar — 注意不要下载名字里带 -api 的那种
  2. 放进服务器的 plugins/ 目录 (EmakiCoreLib 必须装, 其他模块按需要装)
  3. 启动服务器, 先让它生成默认配置, 再按需要修改 plugins/Emaki*/ 下的文件
  4. 改完配置执行对应模块的重载命令, 或者重启服务器

想知道该装哪些模块, 直接看 项目介绍 里的"常见组合方式"

环境要求 ​

项目要求
Java25
服务端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 Jarruntime 版本API 坐标API 版本API Jar 放进 plugins/?
EmakiCoreLib-4.8.36.jar4.8.36emaki.jiuwu.craft:emaki-corelib-api:4.8.284.8.28否
EmakiAttribute-4.8.7.jar4.8.7emaki.jiuwu.craft:emaki-attribute-api:4.8.64.8.6否
EmakiForge-4.8.8.jar4.8.8emaki.jiuwu.craft:emaki-forge-api:4.8.64.8.6否
EmakiStrengthen-4.8.9.jar4.8.9emaki.jiuwu.craft:emaki-strengthen-api:4.8.74.8.7否
EmakiCooking-4.3.8.jar4.3.8emaki.jiuwu.craft:emaki-cooking-api:4.3.64.3.6否
EmakiGem-2.8.11.jar2.8.11emaki.jiuwu.craft:emaki-gem-api:2.8.82.8.8否
EmakiLevel-1.6.9.jar1.6.9emaki.jiuwu.craft:emaki-level-api:1.6.61.6.6否
EmakiSkills-2.8.8.jar2.8.8emaki.jiuwu.craft:emaki-skills-api:2.8.72.8.7否
EmakiItem-2.8.16.jar2.8.16emaki.jiuwu.craft:emaki-item-api:2.8.152.8.15否
EmakiCodex-1.1.6.jar1.1.6emaki.jiuwu.craft:emaki-codex-api:1.1.51.1.5否
EmakiStorage-1.1.5.jar1.1.5emaki.jiuwu.craft:emaki-storage-api:1.1.31.1.3否
EmakiStation-1.1.8.jar1.1.8emaki.jiuwu.craft:emaki-station-api:1.1.71.1.7否
EmakiAccessory-1.1.8.jar1.1.8emaki.jiuwu.craft:emaki-accessory-api:1.1.71.1.7否
EmakiMobs-1.0.13.jar1.0.13emaki.jiuwu.craft:emaki-mobs-api:1.0.91.0.9否
EmakiStructure-1.0.5.jar1.0.5emaki.jiuwu.craft:emaki-structure-api:1.0.11.0.1否
EmakiDungeon-1.0.2.jar1.0.2emaki.jiuwu.craft:emaki-dungeon-api:1.0.11.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, 调用方应通过 CoreLib scheduling() 或平台 scheduler 正确调度

推荐安装顺序 ​

  1. EmakiCoreLib: 所有业务模块的基础 runtime
  2. EmakiAttribute: 需要属性, 资源或战斗联动时安装
  3. 装备线: EmakiItem, EmakiForge, EmakiStrengthen, EmakiGem
  4. 成长/玩法线: EmakiLevel, EmakiSkills, EmakiCooking, EmakiCodex, EmakiStorage
  5. 按模块配置选择 PlaceholderAPI, Vault/ExcellentEconomy, MythicMobs, CraftEngine, ItemsAdder 等软依赖

实际加载顺序以各模块 paper-plugin.yml 为准. CoreLib 是基础依赖; 软依赖缺失时应只关闭对应桥接能力

首次启动检查 ​

  1. 只将需要的 runtime Jar 放入 plugins/
  2. 启动服务器, 等待生成默认配置
  3. 停服或确认没有玩家正在操作相关系统后修改 plugins/Emaki*/ 配置
  4. 重启, 先处理控制台最早出现的加载错误
  5. 逐模块验证 GUI, 命令, 配置重载和外部依赖桥接
  6. 第三方插件启动时记录各 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 的规则