Skip to content

API 与集成

第三方插件通过 emaki-station-api 访问 EmakiStation。

Maven 坐标

xml
<dependency>
    <groupId>emaki.jiuwu.craft</groupId>
    <artifactId>emaki-station-api</artifactId>
    <version>1.0.8</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 当作可用性信号。

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 查询层

标注 @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 线程打开工作站界面。

submitAsyncchannel 参数已失效

材料现在来自「背包 + 仓库」的单一合并池并优先扣背包,因此 channel 不再选择任何东西,会被忽略。参数保留只为源码兼容。

对已有调用方有两个后果:传 MaterialChannel.BACKPACK 过去会被拒绝并返回 station.api_backpack_unsupported该原因键现在不再产生;传 MaterialChannel.STORAGE 时提交仍可能动用背包。

cancelAsync 的退还语义

退还总是回到材料原本来自的通道,与工作站的产物路由无关。部分退还仍报告成功,缺口作为 Partial 原因键携带。

openGui 刻意同步

它立即触及观察者与其背包窗口,因此必须在该玩家的 owner 线程执行。Folia 上是实体调度器 owner,Paper 上是主服务器线程。从其他线程调用返回 FailureKind.WRONG_THREAD不会代为安排一次稍后的打开。

模型类型

emaki.jiuwu.craft.station.api.model 包:

类型说明
StationView工作站视图。
RecipeView配方视图。
MaterialRequirementView材料需求视图。
QueueSnapshot队列快照。
QueueEntryView队列条目视图。
QueueEntryState条目状态:WAITINGRUNNINGPENDING_CLAIM
ConsumedMaterial已消耗材料记录。
PendingOutput待投递产物。
SubmitOutcome提交结果载荷。
MaterialChannel材料通道枚举,submitAsync 已忽略该参数。
OutputRouting产物路由。
ProgressMode推进方式。