API 与集成
第三方插件通过 emaki-station-api 访问 EmakiStation。
Maven 坐标
<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 声明为依赖:
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 的跨模块就绪契约使用:
EmakiCoreLibApi.whenReady(myPlugin, "EmakiStation", () -> {
// EmakiStation 的配方与工作站已加载
});需要在 EmakiStation 每次重载后失效自己的缓存时,用常驻监听而不是在回调里重新注册 whenReady:
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 线程 | 打开工作站界面。 |
submitAsync 的 channel 参数已失效
材料现在来自「背包 + 仓库」的单一合并池并优先扣背包,因此 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 | 条目状态:WAITING、RUNNING、PENDING_CLAIM。 |
ConsumedMaterial | 已消耗材料记录。 |
PendingOutput | 待投递产物。 |
SubmitOutcome | 提交结果载荷。 |
MaterialChannel | 材料通道枚举,submitAsync 已忽略该参数。 |
OutputRouting | 产物路由。 |
ProgressMode | 推进方式。 |