跳转到正文
iPlugin

Compass 扩展

Provider、批量目标与持久化 Storage

Compass 提供三种接入方式。选择最符合数据归属的一种,不要同时为同一批数据重复接入。

普通目标

适合一次性或短期目标:

XerrAPI.compasses.setTarget(
    viewer,
    "navigation",
    "quest:main",
    questLocation,
    "quest",
    "主线任务",
    100
)

目标由 Xerr 保存在当前运行期,不会自动跨服务器重启持久化。

批量替换

插件已经拥有目标列表时,可以一次替换自己的快照:

XerrAPI.compasses.replaceTargets(
    plugin,
    viewer.uniqueId,
    "navigation",
    targets
)

Xerr 会使用插件名作为目标命名空间,只替换该插件的数据,不影响其它插件。

Provider

Provider 适合“数据始终由你的插件掌管”的场景,例如任务插件已经维护了所有任务目标:

val registration = XerrAPI.compasses.registerProvider(
    plugin,
    20
) { viewerId, compassId ->
    CompletableFuture.completedFuture(loadCachedTargets(viewerId, compassId))
}

规则:

  • 同一玩家、罗盘和 Provider 最多一个任务在运行。
  • 返回空列表会清除该 Provider 上一次的目标。
  • 失败时保留上一份成功快照并稍后重试。
  • Provider 目标是只读的,普通 remove 和 clear 不会伪装成删除外部数据。
  • 关闭返回的 AutoCloseable 可以注销。
  • 提供方插件关闭时 Xerr 会自动注销。

回调开始阶段不要阻塞数据库或网络。可以返回已有缓存,也可以返回真正异步完成的 Future。

Storage

Storage 适合“Xerr 管理运行时目标,你的插件只负责保存”的场景。

class DatabaseCompassStorage : CompassStorage {

    override fun read(playerId: UUID): CompletableFuture<String?> {
        return database.read(playerId)
    }

    override fun write(
        playerId: UUID,
        document: String
    ): CompletableFuture<Void> {
        return database.write(playerId, document)
    }

    override fun delete(playerId: UUID): CompletableFuture<Void> {
        return database.delete(playerId)
    }
}

注册:

XerrAPI.compasses
    .registerStorage(plugin, DatabaseCompassStorage())
    .thenAccept { storage ->
        storage.setTarget(
            player.uniqueId,
            "navigation",
            CompassTarget(
                id = "home",
                source = CompassSource.Fixed(
                    XerrAPI.compasses.position(homeLocation)
                ),
                marker = "home"
            )
        )
    }

传给 Storage Registration 的目标 ID 是局部 ID。Xerr 会自动加上插件命名空间,因此多个插件都可以使用 home,不会互相覆盖。

document

read 和 write 中的 document 是 Xerr 维护的版本化 JSON。存储实现应把它当作不透明字符串原样保存:

  • 不解析字段。
  • 不修改内容。
  • 不自行合并目标。

Xerr 负责目标格式和版本迁移。

生命周期

Xerr 会负责:

  • 注册时加载当前在线玩家。
  • 玩家进服时加载。
  • 运行时立即更新 HUD。
  • 同一插件和玩家的写入串行执行。
  • 新变化合并到下一份完整快照。
  • 目标为空时调用 delete。
  • 写入失败后保留待保存状态并重试。
  • 玩家退出和插件关闭时刷新最后快照。

需要等待首次读取:

storage.ready(playerId)

需要确认数据已经写入:

storage.flush(playerId)
storage.flushAll()

关闭数据库连接前:

storage.closeAsync().thenRun {
    database.close()
}

普通 setTarget Future 表示运行时修改已接纳,不表示数据库已经完成写入。

多插件共存

  • 每个插件拥有自己的命名空间。
  • 同一插件只能注册一个 Storage。
  • 多个不同插件可以同时注册各自 Storage。
  • Provider、Storage 和普通目标可以同时存在,只要不要重复提供同一业务数据。
  • Xerr 负责合并显示、目标生命周期和各命名空间隔离。