API 与集成
第三方插件通过 emaki-accessory-api 访问 EmakiAccessory。
Maven 坐标
<dependency>
<groupId>emaki.jiuwu.craft</groupId>
<artifactId>emaki-accessory-api</artifactId>
<version>1.0.3</version>
<scope>provided</scope>
</dependency>不要把 emaki-accessory-api-*.jar 放进服务器 plugins/。它只是编译期构件。
在 paper-plugin.yml 里把 EmakiAccessory 声明为依赖:
dependencies:
server:
EmakiAccessory:
load: BEFORE
required: false
join-classpath: true门面
emaki.jiuwu.craft.accessory.api.EmakiAccessoryApi 是静态门面:
| 方法 | 说明 |
|---|---|
status() | 可用性与身份元数据,返回 CoreLib 的 ApiStatus。 |
catalog() | 查询层,见下文 AccessoryCatalog。 |
install(Bridge) / uninstall(Bridge) | 仅供 EmakiAccessory 自身生命周期调用。 |
访问器永不返回 null。EmakiAccessory 缺失时 catalog 返回空答案,因此调用方不能把 NullPointerException 当作可用性信号。
Bridge 标注 @ApiStatus.NonExtendable,第三方插件不得实现。
没有操作层与扩展层
这是刻意的设计。饰品通过 EmakiAttribute 与 EmakiSkills 各自已有的 owner 作用域扩展点推送贡献,不存在饰品专属的战斗或技能管线给第三方挂钩,因此本模块只暴露查询层。
要影响饰品带来的属性或技能,应当去 EmakiAttribute / EmakiSkills 的对应扩展点,而不是找 EmakiAccessory 要接口。
不要缓存层对象
在使用点解析 catalog(),不要把它存进字段:backing bridge 在 reload 时会被替换。
// 正确
EmakiAccessoryApi.catalog().parts();
// 错误:reload 后这个引用会指向旧 bridge
private final AccessoryCatalog cached = EmakiAccessoryApi.catalog();AccessoryCatalog 查询层
标注 @ApiStatus.NonExtendable。
| 方法 | 线程 | 说明 |
|---|---|---|
List<AccessoryPartView> parts() | 任意线程 | 列出所有部位定义。 |
Optional<AccessoryPartView> part(String partId) | 任意线程 | 按 id 查部位。 |
List<String> slotInstanceIds() | 任意线程 | 列出所有槽位实例 id。 |
List<String> pageIds() | 任意线程 | 列出所有饰品页 id,按页顺序。1.0.3 起。 |
String enabledPage(UUID playerId) | 该玩家的 owner 线程 | 当前生效页 id;未知或该页当前不可用时返回空字符串。1.0.3 起。 |
Map<String, EquippedAccessoryView> equipped(UUID playerId) | 该玩家的 owner 线程 | 该玩家在生效页上装备的饰品,键为槽位实例 id。 |
Map<String, EquippedAccessoryView> equippedOnPage(UUID playerId, String pageId) | 该玩家的 owner 线程 | 指定页上存放的饰品。1.0.3 起。 |
int equippedSetPieces(UUID playerId, String setId) | 该玩家的 owner 线程 | 该玩家某套装的已装备件数,只统计生效页。 |
部位、槽位与页 id 查询是配置事实,任何线程都可调用。
玩家查询读取内存会话缓存,必须在该玩家的 owner 线程调用。玩家数据尚未加载完成时返回空结果而不是阻塞——因此空结果不能反推成「这个玩家没戴任何饰品」。
多页语义有三点要注意:
equipped()只报告生效页。要看其他页用equippedOnPage()。- 玩家缺少生效页权限时,
enabledPage()返回空字符串、equipped()返回空 Map,但存储物品不受影响,仍可被玩家取回。 equipped()包含孤儿槽位,方便调用方看到待取回的物品;不需要时用EquippedAccessoryView#orphaned()过滤。孤儿槽位不参与套装件数。
这些方法都是纯查询,没有就绪门禁。reload 期间可能读到陈旧数据,需要在 reload 后失效自己的缓存时用 CoreLib 的常驻就绪监听:
EmakiCoreLibApi.addModuleListener(myPlugin, "EmakiAccessory", phase -> {
if (phase == ModuleReadinessPhase.READY) {
rebuildMyCache();
}
});模型类型
emaki.jiuwu.craft.accessory.api.model 包:
AccessoryPartView
public record AccessoryPartView(String partId, int count, String displayName, List<String> slotInstanceIds)| 成员 | 说明 |
|---|---|
partId() | 部位 id。 |
count() | 展开出的槽位数,至少为 1。 |
displayName() | MiniMessage 显示名,可能为空字符串。 |
slotInstanceIds() | 展开后的槽位实例 id,按序号顺序,格式 <partId>_<序号>,序号从 1 开始。列表不可变。 |
EquippedAccessoryView
public record EquippedAccessoryView(String pageId, String slotInstanceId, String partId, ItemStack item, boolean orphaned)| 成员 | 说明 |
|---|---|
pageId() | 该饰品所在的页 id。1.0.3 起。 |
slotInstanceId() | 槽位实例 id。 |
partId() | 所属部位 id;部位已不存在时是原始前缀。 |
item() | 存储物品的防御性副本,槽位为空时为 null。 |
orphaned() | 该槽位是否已不被所属页声明。 |
item() 每次调用都返回一份克隆,调用方无法通过视图改动存储状态。