简体中文
公开 API
编译依赖
xml
<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.8.15</version><scope>provided</scope></dependency>kotlin
repositories { maven("https://repo.crypticlib.com/repository/maven-public/") }
dependencies { compileOnly("emaki.jiuwu.craft:emaki-item-api:2.8.15") }仅编译依赖
不要安装, bundle, shade 或 relocate API Jar. runtime 已内嵌未 relocate 的 API 类; 重复 ClassLoader 类型会破坏事件和 bridge
门面与六个入口
前置条件: EmakiItemApi.status().usable() 返回 true. 通过后以下入口非 null:
catalog(): 定义, 别名, 身份与套装查询operations(): 创建, 刷新, 套装同步与修复 GUIrepair(): 禁修标志, 经济修复报价与执行migration()(experimental): 管理型文件/库存迁移extensions(): 物品层预览 provider 与自定义 effect 类型注册state(): 物品自定义持久状态的类型化读写
install, uninstall, Bridge 只属于 runtime; 第三方不得调用或实现
Operations
create(id,amount)在 global-region owner thread 构建并触发可取消的EmakiItemCreateEvent. 监听器可替换结果; 取消映射为FailureKind.CANCELLEDrefresh/forceRefresh返回重建后的 stack; 实时库存物品须在 holder owner threadrefreshPlayer(player,trigger)与refreshEquippedSets(player,trigger)在玩家 owner thread, compare-before-write 冲突可返回携带实际ItemRefreshSummary的PartialopenRepairGui(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
自定义 effects 类型
registerEffectType(Plugin owner, ItemEffectType type) 返回 ItemEffectRegistration; unregisterEffectTypes(Plugin owner) 移除某插件的全部自定义效果类型. 注册后, 物品定义可以在六个内置 type (variables, ea_attribute, es_skill, accessory_slot, name_action, lore_action) 之外声明自己的 effects: - type: "<你的 id>". 内置 id 是保留的: 注册它们会被拒绝并返回不生效的 handle
ItemEffectType 只有两个方法:
typeId(): 与effects[].type匹配的 id; 空白 id 使注册不生效parse(ItemEffectParseContext): 把一条原始 effect 条目转成可应用的效果; 返回null表示跳过该条目 (例如配置不适用于当前物品)
解析结果是 ItemEffect, 只有 apply(ItemEffectApplyContext) 是抽象方法, clear(ItemEffectApplyContext) 默认空实现. EmakiItem 在同一物品上一定先调用 clear 再调用 apply: clear 用于清除本效果在既往构建中写入的数据 (必须容忍物品本就不含该数据), apply 用于重新写入. 只写自己命名空间的数据, 不要改动或删除其他模块的持久化数据
上下文类型由 EmakiItem 提供, 第三方不得实现:
ItemEffectParseContext:definitionId()返回声明该效果的物品定义 id;effect()返回原始 effect 条目 (含type), 是只含纯数据的防御性副本, 不可修改ItemEffectApplyContext继承前者, 并加itemStack()(正在构建的物品, 直接写它, 不要保留引用),variables()(定义已解析的表达式变量, 可直接代入模板),equipSlot()(定义归一化后的装备槽, 例如main_hand)
行为边界:
type与typeId()比较前都会被归一化: 去除首尾空白, 按Locale.ROOT小写, 空格替换为下划线- 某个
type没有已注册处理器时, 该条目被跳过且不输出任何提示, 物品定义不会被拒绝; 因此可以引用尚未安装的 provider - 注册与注销都会丢弃 EmakiItem 已构建物品的原型缓存, 受影响定义会在下次构建时重新解析
- 注册内部已同步, 可从任意线程调用;
typeId()在注册时读取,parse与ItemEffect的回调都在构建该物品的线程上运行, 必须非阻塞, 不得从中调度任务, 不得保留传入的 stack - 原始 effect 配置只在定义至少含一个自定义效果时进入物品定义签名, 因此不含自定义效果的物品签名不变; 改写自定义效果的配置即可触发物品更新, 无需额外签名钩子
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, 触碰玩家或背包前需显式调度