公开 API
编译依赖
<repositories><repository><id>jiuwu-releases</id><url>https://repo.crypticlib.com/repository/maven-public/</url></repository></repositories>
<dependency><groupId>emaki.jiuwu.craft</groupId><artifactId>emaki-item-api</artifactId><version>2.7.16</version><scope>provided</scope></dependency>repositories { maven("https://repo.crypticlib.com/repository/maven-public/") }
dependencies { compileOnly("emaki.jiuwu.craft:emaki-item-api:2.7.16") }仅编译依赖
不要安装、bundle、shade 或 relocate API Jar。runtime 已内嵌未 relocate 的 API 类;重复 ClassLoader 类型会破坏事件和 bridge。
门面与六个入口
先检查 EmakiItemApi.status().usable()。其余非 null 入口是:
catalog():定义、别名、身份与套装查询。operations():创建、刷新、套装同步与修复 GUI。repair():禁修标志、经济修复报价与执行。migration()(experimental):管理型文件/库存迁移。extensions():物品层预览 provider 注册。state():物品自定义持久状态的类型化读写。
install、uninstall、Bridge 只属于 runtime;第三方不得调用或实现。
Operations
create(id,amount)在 global-region owner thread 构建并触发可取消的EmakiItemCreateEvent。监听器可替换结果;取消映射为FailureKind.CANCELLED。refresh/forceRefresh返回重建后的 stack;实时库存物品须在 holder owner thread。refreshPlayer(player,trigger)与refreshEquippedSets(player,trigger)在玩家 owner thread,compare-before-write 冲突可返回携带实际ItemRefreshSummary的Partial。openRepairGui(player)返回EmakiResult<Unit>,玩家 owner thread。
Repair
repair().isDisabled、markDisabled、clearDisabled 管理 durability-disable 标志。quote(Player,ItemStack) 返回 RepairQuoteView;不可负担仍是成功报价且 affordable()==false。repair(Player,ItemStack) 走真实经济修复服务:事件、扣费、补偿、耐久提交和 post actions 都在同一 runtime 流程中。材料修复仍是 GUI/库存事务,没有伪造的简化 API。
Migration
preview(oldId,newId) 与 apply(oldId,newId,replaceReferences,keepAlias) 会同步执行文件系统工作,调用方必须选择非 tick worker;I/O 失败映射为 INTERNAL_ERROR,部分文件已提交时返回 Partial<MigrationOutcome>。migrateInventory(Player) 需要玩家 owner thread;migrateAllOnline() 只处理当前线程拥有的在线玩家,Folia 所有权不完整时返回 Partial<Integer>。
Extensions
registerLayerPreview(Plugin,ItemLayerPreviewProvider) 返回 owner-scoped registration;关闭 handle 或按 owner 注销。provider 使用克隆的 request/result,不得跨 owner 线程修改实时 stack。
ItemState
state() 是物品自定义持久状态的类型化读写层,字段由 ItemStateKey<T>(namespace + partition + 字段名 + ItemStateType)标识:
snapshot(item)返回不可变的ItemStateSnapshot:可用get(key)取类型化值、contains(key)判断字段是否存在、repaired()判断读取时是否顺带修复了元数据。item()返回克隆件。repair(item)修复元数据并回读快照。get(item,key)返回Optional<T>,类型不匹配时为空。set(item,key,value)、add(item,key,amount)、remove(item,key)返回ItemStateMutation<T>:含oldValue、newValue、delta,以及committed、changed、clamped、rejected与reason,被 clamp 或拒绝都能从返回值判断,不抛异常。
结果语义
无负载成功使用 EmakiResult<Unit>;通用可选负载访问器为 optionalValue()。FailureKind 只有 UNAVAILABLE、NOT_FOUND、INVALID_INPUT、REJECTED、CANCELLED、TARGET_OFFLINE、WRONG_THREAD、INTERNAL_ERROR。
就绪与 reload
status().ready() 的判据是"定义已加载",不是"组件已构造"。插件启用后组件立即非 null,但定义表在 reload 期间会被重建,因此:
- reload 窗口内
status()报loading,catalog()、operations()、repair()、migration()中返回EmakiResult的方法一律返回unavailable()——不是NOT_FOUND。把unavailable()当作"稍后重试",不要当作"该物品不存在"。 definitionIds()、exists(id)、typeIds()这类纯查询无法表达不可用,reload 期间可能返回陈旧或空结果。需要"定义确实加载好了"再动作时,用EmakiCoreLibApi.whenReady(plugin, "EmakiItem", callback),不要用空结果反推。- 缓存了 EmakiItem 定义的插件,要在每次重载后更新缓存,用
EmakiCoreLibApi.addModuleListener(plugin, "EmakiItem", phase -> ...):LOADING时失效、READY时重建。whenReady只响一次,不适合这个用途。
启动期依赖 EmakiItem 定义的插件应挂 whenReady 而不是在自己的 onEnable 里直接查表:模块加载顺序不保证 EmakiItem 的首次加载先于你的 onEnable。回调只触发一次,且运行线程不保证是 owner thread,触碰玩家或背包前需显式调度。