简体中文
API 与集成
第三方插件通过 emaki-station-api 访问 EmakiStation
Maven 坐标
xml
<dependency>
<groupId>emaki.jiuwu.craft</groupId>
<artifactId>emaki-station-api</artifactId>
<version>1.1.7</version>
<scope>provided</scope>
</dependency>不要把 emaki-station-api-*.jar 放进服务器 plugins/. 它只是编译期构件
在 paper-plugin.yml 里把 EmakiStation 声明为依赖:
yaml
dependencies:
server:
EmakiStation:
load: BEFORE
required: false
join-classpath: true门面
emaki.jiuwu.craft.station.api.EmakiStationApi 是静态门面:
| 方法 | 说明 |
|---|---|
status() | 可用性与身份元数据, 返回 CoreLib 的 ApiStatus |
catalog() | 查询层, 见下文 StationCatalog |
operations() | 操作层, 见下文 StationOperations |
extensions() | 预留扩展面, 当前是空接口 |
install(Bridge) / uninstall(Bridge) | 仅供 EmakiStation 自身生命周期调用, 标注 @ApiStatus.Internal |
访问器永不返回 null: EmakiStation 缺失时, 各层返回显式的 FailureKind.UNAVAILABLE 结果. 调用方必须以该结果判定可用性, 不得把 NullPointerException 当作可用性信号
status() 的三个文本字段来自 bridge: pluginName 是 EmakiStation, pluginVersion 是描述符里的模块版本 (1.1.8), apiVersion 是 bridge 内置的接口约定常量 "1.0.0". 该值不等于 emaki-station-api 构件的版本号, 调用方不得用它判断 API 依赖版本
Bridge 标注 @ApiStatus.NonExtendable, 第三方插件不得实现
就绪判据
status().ready() 的判据是模块的 contentReady: 配置, 工作站与配方数据已加载完成, 而不是"服务对象非空". reload 期间该判据为 false, 操作层返回 UNAVAILABLE, 不把时序问题伪装成"数据不存在"
配合 CoreLib 的跨模块就绪接口约定使用:
java
EmakiCoreLibApi.whenReady(myPlugin, "EmakiStation", () -> {
// EmakiStation 的配方与工作站已加载
});需要在 EmakiStation 每次重载后失效自己的缓存时, 用长期监听而不是在回调里重新注册 whenReady:
java
EmakiCoreLibApi.addModuleListener(myPlugin, "EmakiStation", phase -> {
if (phase == ModuleReadinessPhase.READY) {
rebuildMyCache();
}
});StationCatalog 查询层
StationCatalog 标注 @ApiStatus.NonExtendable. 其方法为同步调用, 读取已加载的配置, 可在任意线程调用
| 方法 | 说明 |
|---|---|
List<StationView> stations() | 列出所有已加载工作站, 按稳定 id 顺序; 运行时不可用时返回空 |
Optional<StationView> station(String stationId) | 按 id 查工作站 |
List<RecipeView> recipes() | 列出所有配方 |
Optional<RecipeView> recipe(String recipeId) | 按 id 查配方 |
List<RecipeView> recipesOf(String stationId) | 列出某工作站开放的配方 |
CompletableFuture<EmakiResult<QueueSnapshot>> queueSnapshotAsync(UUID playerId, String stationId) | 读取某玩家在某工作站的队列快照 |
queueSnapshotAsync 之所以异步, 是因为未缓存的队列需要从磁盘读取. 该方法对离线玩家同样有效: 队列数据按玩家的文件状态读取, 不要求 owner 在场. 玩家没有条目时返回空队列, 不返回失败
调用方不得假设该 future 在 owner 线程上完成
StationOperations 操作层
标注 @ApiStatus.NonExtendable
| 方法 | 线程 | 说明 |
|---|---|---|
CompletableFuture<EmakiResult<SubmitOutcome>> submitAsync(UUID playerId, String stationId, String recipeId, long batch, MaterialChannel channel) | 任意线程, 要求目标玩家在线 | 提交合成. batch 必须为正 |
CompletableFuture<EmakiResult<Unit>> cancelAsync(UUID playerId, String stationId, int index) | 任意线程, 要求目标玩家在线 | 取消一条队列条目, index 从 0 开始 |
CompletableFuture<EmakiResult<Integer>> claimAsync(UUID playerId) | 任意线程, 要求目标玩家在线 | 领取该玩家在所有工作站的可投递待领取产物, 载荷是实际清掉的条目数 |
EmakiResult<Unit> openGui(Player player, String stationId) | 必须在该玩家的 owner 线程 | 打开工作站界面 |
submitAsync 的 channel 参数已失效
材料现在来自"背包 + 仓库"的单一合并池并优先扣背包, 因此 channel 不再选择任何东西, 会被忽略. 该参数保留只为源码兼容
对已有调用方有两个后果: 传 MaterialChannel.BACKPACK 不再被拒绝 (历史上返回的 station.api_backpack_unsupported 已不会产生); 传 MaterialChannel.STORAGE 时, 提交会按合并池规则扣料, 背包材料会被动用
操作层的失败原因键
submitAsync 在参数为空, batch <= 0 时分别返回 station.submit_bad_request / station.bad_batch; 模块未就绪返回 UNAVAILABLE. cancelAsync 与 claimAsync 在目标玩家离线时返回 targetOffline(), cancelAsync 还会在未知工作站时返回 station.unknown_station
openGui 的失败原因键为 station.open_bad_request, UNAVAILABLE, station.unknown_station, station.queue_not_loaded, station.missing_layout 或 FailureKind.WRONG_THREAD
cancelAsync 的退还规则
退还总是回到材料原本来自的通道, 与工作站的产物路由无关. 部分退还仍报告成功, 缺口作为 Partial 原因键携带
openGui 刻意同步
该方法立即触及观察者与其背包窗口, 因此必须在该玩家的 owner 线程执行. Folia 上是实体调度器 owner, Paper 上是主服务器线程. 从其他线程调用返回 FailureKind.WRONG_THREAD, 不会代为安排一次稍后的打开
模型类型
emaki.jiuwu.craft.station.api.model 包:
| 类型 | 说明 |
|---|---|
StationView | 工作站视图 |
RecipeView | 配方视图 |
MaterialRequirementView | 材料需求视图 |
QueueSnapshot | 队列快照 |
QueueEntryView | 队列条目视图 |
QueueEntryState | 条目状态: WAITING, RUNNING, PENDING_CLAIM |
ConsumedMaterial | 已消耗材料记录 |
PendingOutput | 待投递产物 |
SubmitOutcome | 提交结果载荷 |
MaterialChannel | 材料通道枚举, submitAsync 已忽略该参数 |
OutputRouting | 产物路由 |
ProgressMode | 推进方式 |