外观
🔌 InvSync 开发者 API
基线:InvSync 2.5.31 的 project-api 与对应实现,2026-10-10 核对。公共 API 用于接入同步流程,不是跨节点事务系统。
依赖与生命周期
只依赖 com.xbaimiao.invsync:invsync-api,版本使用服务器实际插件版本;坐标可用性以 Maven 仓库实际发布为准,不因主插件版本存在就保证同版 API 已发布。
kotlin
repositories {
maven("https://maven.xbaimiao.com/repository/maven-public/")
}
dependencies {
// 只能 compileOnly;将 {version} 换成实际已发布版本。
compileOnly("com.xbaimiao.invsync:invsync-api:{version}")
}xml
<repositories>
<repository>
<id>xbaimiao</id>
<url>https://maven.xbaimiao.com/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.xbaimiao.invsync</groupId>
<artifactId>invsync-api</artifactId>
<version>{version}</version>
<scope>provided</scope>
</dependency>
</dependencies>yaml
# 必须依赖时使用 depend。软依赖则在初始化前确认插件存在且已启用。
depend: [InvSync]- 禁止把 API shade 到自己插件,避免两份事件/注册中心和类链接冲突。
- Kotlin 元数据目标 2.1,建议使用匹配的编译环境;API 传递依赖 kotlin-stdlib,Java 项目也需要它供编译期读取
Unit等类型。 - 自己提供 Bukkit/Paper 编译依赖。不要 relocate
com.xbaimiao.invsync.api。 - 在 InvSync 启用后调用
InvSyncAPI.getAPI();setAPI是内部注入方法,禁止第三方替换。
公开边界
API 包含 InvSyncAPI、Addon 注册与回调、PlayerData/PlayerBackup/PluginDataSync/SaveReason、事件和委托属性容器。
存储实现、Redis/Jedis、内部调度、序列化器、模组适配与预加载协议不是稳定 API,禁止反射依赖。API 不提供公开物品编解码器、原子编辑事务、会话所有权锁或完整同步状态查询。
线程约定
| 操作 | 要求 |
|---|---|
readFromPlayer、savePlayer、applyPlayerData | 正确的玩家线程/region;涉及 Bukkit 状态或同步回调 |
readFromRedisOrStorage、getBackups、getBackupById、isLock | 同步阻塞 I/O,移到工作线程 |
savePlayerData | 不只是纯 I/O:提交前可能查本服玩家并触发 Addon/事件;有玩家时使用玩家线程,检查状态与调用目的 |
createBackup | 持有有效的完整快照,内部异步保存,受备份开关限制 |
lock、deletePlayerData、unlockAll | 危险底层入口,不作为安全管理流程 |
Folia 使用其玩家调度器或正确的兼容调度层,不能把全局 Bukkit 主线程当作所有玩家 region。事件的 isAsynchronous 是提示,不代替 region 归属判断。
Future 回调可能在工作线程或调用线程执行,不能在 thenRun / thenAccept 内直接改玩家、发 GUI。不要在玩家线程 get() / join() 阻塞等 I/O。网络 Future 完成也不自动把线程切回玩家线程。
方法语义
| 方法 | 返回与限制 |
|---|---|
readFromPlayer(player) | 当前玩家快照;不是读 Redis/DB,不等于已持久化,也不是只读已启用字段的通用 DTO |
readFromRedisOrStorage(uuid) | Redis 优先,无缓存才查持久化;不存在为 null,I/O 异常不能当空数据;不会抢锁,不保证另一服在线玩家的最新内存状态 |
readFromRedisOrMysql(uuid) | 兼容旧名,已废弃,改用 Storage 版本 |
savePlayer(player, reason) | 读取本服在线玩家并保存,返回 CompletableFuture<Unit>;没有完整生命周期门禁/冻结和自动备份,不能当任意时刻安全强制保存 |
savePlayerData(data, reason) | 主流程中 Future 成功表示该保存路径完成 Redis 写(需要时)与存储写;调用前处理加载/交接/并发,数据提交后不要再修改 |
applyPlayerData(player, data) | 应用已启用字段与扩展,不持久化;不自动替你建立完整同步会话、冻结或触发 Done |
createBackup(data, reason) | CompletableFuture<Boolean>;备份关闭、空数据策略或失败可返回 false |
getBackups(uuid) | 阻塞读取,时间倒序列表 |
getBackupById(id) | 阻塞读取,缺失为 null |
restoreBackup(id) | 离线存储恢复入口;当前 true 只代表找到并提交,不完整等待内部保存 Future,不能等同持久化 ACK |
deletePlayerData(uuid) | 删除主档,当前备份不随之删除;没有完整事务或全网离线保护 |
lock(uuid, value) | 底层布尔锁操作异步排队,无所有权、无公开完成确认 |
isLock(uuid) | 阻塞读取,仅瞬时观察,不是原子检查并修改 |
unlockAll() | 全局危险清理,不能在有玩家在线的网络随意使用 |
不写“isLock 后编辑就是安全”示例
检查后另一个节点可能立即登录。接口没有事务或 CAS,也不能保证跨节点编辑、恢复、在线奖励原子性。必须设计维护隔离或自己的权威数据系统,避免把一次读写当可靠跨服发奖。
PlayerData 与扩展数据
- UUID 存字符串,多个 ByteArray 字段使用 lateinit;先检查
inventoryIsInit()等初始化辅助方法。 - ByteArray 是压缩/序列化载荷,不是 ItemStack 数组,禁止凭猜测 JSON/NBT 格式编辑。
fieldHashes由核心维护,不自己伪造。外部合法修改使用对应保存语义;主表 ORM 注解不是直接写库的契约。- 不要通过读取 PlayerData 后改 pluginData 再
savePlayerData来做离线 Addon 编辑。 当前保存实现会从本服 PluginDataCache 构建扩展数据,可能覆盖传入的 pluginData;公开 API 没有保证这种离线路径。 - 公共 API 未承诺“完整历史快照 + 跨服在线推送”;需要这些能力先与作者确认。
下一步
扩展上线前测试首次登录、同服重连、A→B→A、资源包等待、登录被拒、退出/关闭与格式升级。线程正确不等于数据逻辑完整。