跳到正文

公开 API

包名:cn.sealplugins.items.api

SealItemsApi

kotlin
interface SealItemsApi {
    fun openIdentification(playerUuid: UUID): IdentificationOpenResponse
}

参数

playerUuid 是目标玩家 UUID。调用时玩家通常应在线;API 接受 UUID 是为了避免公共契约直接泄漏 Bukkit Player 类型。

返回

kotlin
data class IdentificationOpenResponse(
    val opened: Boolean,
    val reason: String? = null,
)
  • opened=true:服务端已接受打开请求。
  • opened=false:没有打开;reason 可能解释原因。
  • reason 可为空,调用方必须提供自己的 fallback。

该方法不承诺 GUI 一定是原版或 ArcartX;实际 provider 由服务端配置和玩家资源就绪状态决定。

Kotlin 完整示例

kotlin
package com.example.mysealaddon

import cn.sealplugins.items.api.SealItemsApi
import org.bukkit.entity.Player
import org.bukkit.plugin.java.JavaPlugin

class MySealAddon : JavaPlugin() {
    fun openIdentificationFor(player: Player) {
        val api = server.servicesManager.load(SealItemsApi::class.java)
        if (api == null) {
            player.sendMessage("§c鉴定服务当前未安装或尚未就绪。")
            return
        }

        val response = runCatching {
            api.openIdentification(player.uniqueId)
        }.getOrElse { failure ->
            logger.warning("调用 SealItems 鉴定 API 失败:${failure.message}")
            player.sendMessage("§c鉴定服务暂时发生异常。")
            return
        }

        if (!response.opened) {
            player.sendMessage("§c无法打开鉴定:${response.reason ?: "未知原因"}")
        }
    }
}

runCatching 只用于隔离跨插件异常,不应吞掉日志;业务拒绝仍通过 response 处理。

Java 完整示例

java
package com.example.mysealaddon;

import cn.sealplugins.items.api.IdentificationOpenResponse;
import cn.sealplugins.items.api.SealItemsApi;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;

public final class MySealAddon extends JavaPlugin {
    public void openIdentificationFor(Player player) {
        SealItemsApi api = getServer().getServicesManager().load(SealItemsApi.class);
        if (api == null) {
            player.sendMessage("§c鉴定服务当前未安装或尚未就绪。");
            return;
        }

        try {
            IdentificationOpenResponse result =
                api.openIdentification(player.getUniqueId());
            if (!result.getOpened()) {
                String reason = result.getReason() == null
                    ? "未知原因"
                    : result.getReason();
                player.sendMessage("§c无法打开鉴定:" + reason);
            }
        } catch (RuntimeException exception) {
            getLogger().warning("调用 SealItems 鉴定 API 失败:" + exception.getMessage());
            player.sendMessage("§c鉴定服务暂时发生异常。");
        }
    }
}

Kotlin data class 对 Java 暴露 getOpened()getReason()

服务生命周期示例

简单插件在每次命令时重新 load 就足够。若缓存:

java
private volatile SealItemsApi sealItems;

@Override
public void onEnable() {
    refreshSealItems();
}

private void refreshSealItems() {
    sealItems = getServer().getServicesManager().load(SealItemsApi.class);
}

@Override
public void onDisable() {
    sealItems = null;
}

还必须监听服务注册/注销或在每次使用前验证。仅在 onEnable 获取一次会在 /reload、插件管理器重载或启动顺序变化后持有失效引用。

当前没有的公共 API

以下能力虽在插件内部存在,但不是公共契约:

  • 按 ID 发放物品;
  • 直接构建 ItemStack;
  • 读取/写入物品 PDC;
  • 请求物品迁移;
  • 打开改造/宝石工作台;
  • 提交重铸、洗炼或孔位操作;
  • 读取所有物品/词条/品质快照;
  • 订阅内部事务事件。

不要通过反射、内部包依赖或复制 codec 绕过。内部类可随开发快照变化,也会破坏服务端的权限、费用、CAS 和审计保护。

提议新 API 时应说明

调用者是谁、线程、输入与稳定身份、权限由谁负责、失败枚举、幂等性、插件禁用时行为、是否会触发费用/随机、数据兼容承诺和最小用例。优先增加小而不可误用的接口。

Minecraft 服务端插件使用与开发文档