Skip to content

SealItems API 类型参考

先阅读开发者接入教程。本页列出 sealitems-api 1.0.0 的真实公开类型;调用方必须 compileOnly,不能把 API JAR 打进自己的插件,也不能把它单独放进服务端 plugins/

使用规则

  • GUI 与玩家状态方法按注释在 Bukkit 主线程调用。
  • API 不会替错误线程自动调度,也不会替调用方阻塞等待。
  • 所有状态枚举都应完整处理;不要只检查布尔值后忽略 reason
  • 事件数据和 View 是只读快照,不应通过反射修改。

SealItemsApi.kt

kotlin
package cn.sealplugins.items.api

import java.util.UUID

data class IdentificationOpenResponse(
    val opened: Boolean,
    val reason: String? = null,
)

data class EnhancementOpenResponse(
    val opened: Boolean,
    val reason: String? = null,
)

data class DecompositionOpenResponse(
    val opened: Boolean,
    val reason: String? = null,
)

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

    /** Must be called on the Bukkit primary thread. */
    fun openEnhancement(playerUuid: UUID): EnhancementOpenResponse

    /** Opens the configured decomposition GUI. Must be called on the Bukkit primary thread. */
    fun openDecomposition(playerUuid: UUID): DecompositionOpenResponse
}

CraftingApiModels.kt

kotlin
package cn.sealplugins.items.api

import java.util.UUID

enum class CraftingActionStatus {
    OPENED,
    APPLIED,
    DISABLED,
    RECIPE_UNAVAILABLE,
    PLAYER_OFFLINE,
    NO_SPACE,
    STOPPED,
    WRONG_THREAD,
    INVALID_AMOUNT,
    BUSY,
}

data class CraftingOpenResponse(
    val status: CraftingActionStatus,
    val reason: String? = null,
) {
    val opened: Boolean
        get() = status == CraftingActionStatus.OPENED
}

data class CraftingBlueprintGiveResponse(
    val status: CraftingActionStatus,
    val requestedAmount: Int,
    val deliveredAmount: Int,
    val reason: String? = null,
) {
    val successful: Boolean
        get() = status == CraftingActionStatus.APPLIED
}

/**
 * Platform-neutral crafting service. Implementations never schedule or block an incorrect caller thread.
 */
interface SealItemsCraftingApi {
    fun openCrafting(playerUuid: UUID): CraftingOpenResponse

    fun giveBlueprint(
        playerUuid: UUID,
        recipeId: String,
        amount: Int,
    ): CraftingBlueprintGiveResponse
}

enum class CraftingConditionStatus {
    PASS,
    FAIL,
    UNAVAILABLE,
}

class CraftingExternalCondition(
    val requestId: String,
    val recipeId: String,
    val key: String,
    arguments: Map<String, String>,
) {
    val arguments: Map<String, String> = java.util.Map.copyOf(arguments)
}

class CraftingConditionBatchRequest(
    val playerUuid: UUID,
    conditions: List<CraftingExternalCondition>,
) {
    val conditions: List<CraftingExternalCondition> = java.util.List.copyOf(conditions)
}

data class CraftingConditionEvaluation(
    val status: CraftingConditionStatus,
    val messageKey: String? = null,
)

class CraftingConditionBatchResult(
    evaluations: Map<String, CraftingConditionEvaluation>,
) {
    val evaluations: Map<String, CraftingConditionEvaluation> = java.util.Map.copyOf(evaluations)
}

/**
 * Must be fast, main-thread safe and free of blocking I/O. Provider IDs are stable `namespace:path` values.
 */
interface CraftingConditionProvider {
    val providerId: String

    fun evaluate(request: CraftingConditionBatchRequest): CraftingConditionBatchResult
}

SealItemsSetsApi.kt

kotlin
package cn.sealplugins.items.api

import java.util.UUID

enum class SetsActionStatus {
    OPENED,
    DISABLED,
    PLAYER_OFFLINE,
    NOT_READY,
    STOPPED,
    WRONG_THREAD,
}

data class SetsOpenResponse(
    val status: SetsActionStatus,
    val reason: String? = null,
) {
    val opened: Boolean
        get() = status == SetsActionStatus.OPENED
}

enum class SetCatalogCollectionFilter {
    ALL,
    UNSTARTED,
    COLLECTING,
    COMPLETED,
}

data class SetCatalogQuery(
    val categoryId: String? = null,
    val collectionFilter: SetCatalogCollectionFilter = SetCatalogCollectionFilter.ALL,
    val text: String = "",
) {
    init {
        require(text.length <= 64) { "catalog search text must not exceed 64 characters" }
        require(categoryId == null || Regex("[a-z0-9][a-z0-9_-]{0,63}").matches(categoryId)) { "category id is invalid" }
    }
}

class SetCatalogView(entries: List<SetCatalogEntryView>) {
    val entries: List<SetCatalogEntryView> = java.util.List.copyOf(entries)
}

class SetDefinitionsView(definitions: List<SetDefinitionView>) {
    val definitions: List<SetDefinitionView> = java.util.List.copyOf(definitions)
}

class SetCategoriesView(categories: List<SetCategoryView>) {
    val categories: List<SetCategoryView> = java.util.List.copyOf(categories)
}

class SetAcquisitionSourcesView(
    val itemId: String,
    sources: List<SetAcquisitionSourceView>,
) {
    val sources: List<SetAcquisitionSourceView> = java.util.List.copyOf(sources)
}

/**
 * Read-only sets service published separately from [SealItemsApi].
 * Player-state methods and [openSets] must be called on the Bukkit primary thread; implementations never schedule or
 * block an incorrect caller thread. Static definition and acquisition-source views are immutable publication reads.
 */
interface SealItemsSetsApi {
    fun openSets(playerUuid: UUID): SetsOpenResponse

    fun currentState(playerUuid: UUID): PlayerSetStateView

    fun currentCollection(playerUuid: UUID): PlayerCollectionView

    fun catalog(
        playerUuid: UUID,
        query: SetCatalogQuery,
    ): SetCatalogView

    fun acquisitionSources(itemId: String): SetAcquisitionSourcesView

    fun definitions(): SetDefinitionsView

    fun categories(): SetCategoriesView

    fun isMechanicActive(
        playerUuid: UUID,
        mechanicId: String,
    ): Boolean
}

SetsApiModels.kt

kotlin
package cn.sealplugins.items.api

import java.time.Instant

enum class SetCountingModeView {
    DISTINCT_MEMBERS,
    EQUIPPED_SLOTS,
}

enum class SetRewardModeView {
    CUMULATIVE,
    HIGHEST_ONLY,
}

enum class SetAttributeOperationView {
    ADD,
    TOTAL_PERCENT,
}

data class SetIconView(
    val material: String,
    val customModelData: Int? = null,
)

data class SetAttributeContributionView(
    val attributeId: String,
    val operation: SetAttributeOperationView,
    val value: String,
)

class SetRewardView(
    attributes: List<SetAttributeContributionView>,
    mechanicIds: Set<String>,
) {
    val attributes: List<SetAttributeContributionView> = java.util.List.copyOf(attributes)
    val mechanicIds: Set<String> = java.util.Set.copyOf(mechanicIds)
}

class SetMemberView(
    val itemId: String,
    slotIds: Set<String>,
) {
    val slotIds: Set<String> = java.util.Set.copyOf(slotIds)
}

class SetNodeView(
    val id: String,
    val pieces: Int,
    val displayName: String,
    description: List<String>,
    val rewards: SetRewardView,
) {
    val description: List<String> = java.util.List.copyOf(description)
}

class SetDefinitionView(
    val id: String,
    val categoryId: String,
    val order: Int,
    val displayName: String,
    description: List<String>,
    val iconItemId: String,
    val countingMode: SetCountingModeView,
    val rewardMode: SetRewardModeView,
    members: List<SetMemberView>,
    nodes: List<SetNodeView>,
) {
    val description: List<String> = java.util.List.copyOf(description)
    val members: List<SetMemberView> = java.util.List.copyOf(members)
    val nodes: List<SetNodeView> = java.util.List.copyOf(nodes)
}

enum class PlayerSetStateStatus {
    DISABLED,
    LOADING,
    READY,
    UNAVAILABLE,
    STOPPED,
}

class PlayerSetProgressView(
    val setId: String,
    val pieceCount: Int,
    equippedItemIds: Set<String>,
    equippedSlotIds: Set<String>,
    reachedNodeIds: Set<String>,
    activeNodeIds: Set<String>,
) {
    val equippedItemIds: Set<String> = java.util.Set.copyOf(equippedItemIds)
    val equippedSlotIds: Set<String> = java.util.Set.copyOf(equippedSlotIds)
    val reachedNodeIds: Set<String> = java.util.Set.copyOf(reachedNodeIds)
    val activeNodeIds: Set<String> = java.util.Set.copyOf(activeNodeIds)
}

class PlayerSetStateView(
    val publicationRevision: Long,
    val status: PlayerSetStateStatus,
    progress: List<PlayerSetProgressView>,
    activeLinkageIds: Set<String>,
    mechanicIds: Set<String>,
    val detail: String? = null,
) {
    val progress: List<PlayerSetProgressView> = java.util.List.copyOf(progress)
    val activeLinkageIds: Set<String> = java.util.Set.copyOf(activeLinkageIds)
    val mechanicIds: Set<String> = java.util.Set.copyOf(mechanicIds)
}

class SetCategoryView(
    val id: String,
    val order: Int,
    val displayName: String,
    description: List<String>,
    val icon: SetIconView,
) {
    val description: List<String> = java.util.List.copyOf(description)
}

enum class SetCollectionStatusView {
    UNSTARTED,
    COLLECTING,
    COMPLETED,
}

class SetCatalogEntryView(
    val setId: String,
    val categoryId: String,
    val order: Int,
    val displayName: String,
    description: List<String>,
    val iconItemId: String,
    val equippedPieces: Int,
    reachedNodeIds: Set<String>,
    activeNodeIds: Set<String>,
    val collectedPieces: Int,
    val totalPieces: Int,
    val collectionStatus: SetCollectionStatusView,
) {
    val description: List<String> = java.util.List.copyOf(description)
    val reachedNodeIds: Set<String> = java.util.Set.copyOf(reachedNodeIds)
    val activeNodeIds: Set<String> = java.util.Set.copyOf(activeNodeIds)
}

enum class PlayerCollectionStatus {
    DISABLED,
    LOADING,
    READY,
    UNAVAILABLE,
}

enum class PlayerCollectionReasonCode {
    NONE,
    DISABLED_BY_CONFIG,
    INITIAL_LOADING,
    DATABASE_UNAVAILABLE,
    RETRYING,
    OUTBOX_PROTECTED,
    STOPPED,
}

class PlayerCollectionView(
    val status: PlayerCollectionStatus,
    confirmedItemIds: Set<String>,
    pendingItemIds: Set<String>,
    firstDiscoveredAt: Map<String, Instant>,
    val generation: Long,
    val reasonCode: PlayerCollectionReasonCode,
    val detail: String? = null,
) {
    val confirmedItemIds: Set<String> = java.util.Set.copyOf(confirmedItemIds)
    val pendingItemIds: Set<String> = java.util.Set.copyOf(pendingItemIds)
    val firstDiscoveredAt: Map<String, Instant> = java.util.Map.copyOf(firstDiscoveredAt)
}

enum class SetAcquisitionSourceTypeView {
    CRAFTING,
    VANILLA_LOOT,
    DECOMPOSITION,
    MYTHIC_MOBS,
    QUEST,
    SHOP,
    DUNGEON,
    BOSS,
    EVENT,
    OTHER,
}

class SetAcquisitionSourceView(
    val id: String,
    val type: SetAcquisitionSourceTypeView,
    val automatic: Boolean,
    val displayName: String,
    description: List<String>,
    val icon: SetIconView?,
) {
    val description: List<String> = java.util.List.copyOf(description)
}

class SetMechanicDescriptor(
    val id: String,
    val displayName: String,
    description: List<String>,
    val owner: String,
) {
    val description: List<String> = java.util.List.copyOf(description)

    init {
        require(namespacedMechanicIdPattern.matches(id)) { "mechanic id must use namespace:path" }
        require(displayName.isNotBlank()) { "mechanic display name must not be blank" }
        require(this.description.none(String::isBlank)) { "mechanic description must not contain blank lines" }
        require(owner.isNotBlank()) { "mechanic owner must not be blank" }
    }
}

class SetMechanicProviderSnapshot(
    val providerId: String,
    val generation: Long,
    descriptors: List<SetMechanicDescriptor>,
) {
    val descriptors: List<SetMechanicDescriptor> = java.util.List.copyOf(descriptors)

    init {
        require(providerIdPattern.matches(providerId)) { "provider id must be a stable lowercase namespace" }
        require(generation >= 0) { "provider generation must not be negative" }
        require(this.descriptors.map(SetMechanicDescriptor::id).distinct().size == this.descriptors.size) {
            "mechanic descriptor ids must be unique"
        }
        require(this.descriptors.all { it.id.substringBefore(':') == providerId }) {
            "mechanic descriptor namespace must equal provider id"
        }
    }
}

/**
 * Platform-neutral descriptor provider. The returned snapshot must be cheap to obtain and must not perform blocking I/O.
 * It exposes no player state, event callback, script or mutable parameter map.
 */
interface SealItemsSetMechanicProvider {
    fun snapshot(): SetMechanicProviderSnapshot
}

private val providerIdPattern = Regex("[a-z0-9][a-z0-9_.-]{0,62}")
private val namespacedMechanicIdPattern = Regex("[a-z0-9][a-z0-9_.-]{0,62}:[a-z0-9][a-z0-9_./-]{0,126}")

EnhancementApiModels.kt

kotlin
package cn.sealplugins.items.api

import java.util.UUID

data class EnhancementFailureOutcome(
    val rawDowngrade: Int,
    val finalLevel: Int,
    val probabilityUnits: Long,
    val protectedLevels: Int,
)

enum class EnhancementItemCostKind {
    SEAL_ITEM,
    VANILLA,
    ENHANCEMENT_MATERIAL,
}

enum class EnhancementItemCostRole {
    REQUIRED,
    LUCK,
    PROTECTION,
    BOOST,
}

data class EnhancementItemCost(
    val kind: EnhancementItemCostKind,
    val id: String,
    val amount: Int,
    val role: EnhancementItemCostRole = EnhancementItemCostRole.REQUIRED,
)

enum class EnhancementCurrencyCostKind {
    VAULT,
    PLAYER_POINTS,
}

data class EnhancementCurrencyCost(
    val kind: EnhancementCurrencyCostKind,
    val amount: String,
)

/** Immutable, platform-neutral facts exposed immediately before the one random roll. */
class EnhancementPreparedAttempt(
    val requestId: UUID,
    val playerId: UUID,
    val itemInstanceId: UUID,
    val itemId: String,
    val beforeLevel: Int,
    val targetLevel: Int,
    val schemeId: String,
    val failureCount: Int,
    val successChanceUnits: Long,
    val hardGuaranteeAt: Int?,
    val luckBonusUnits: Long,
    val protectionCapacity: Int,
    val progressKey: String,
    itemCosts: List<EnhancementItemCost>,
    val currencyCost: EnhancementCurrencyCost?,
    val currentCheckpoint: Int,
    failureOutcomes: List<EnhancementFailureOutcome>,
) {
    val itemCosts: List<EnhancementItemCost> = java.util.List.copyOf(itemCosts)
    val failureOutcomes: List<EnhancementFailureOutcome> = java.util.List.copyOf(failureOutcomes)
}

enum class EnhancementResultStatus {
    APPLIED,
    CONFLICT,
    PROTECTED,
    BLOCKED,
    INSUFFICIENT_COST,
    ECONOMY_REJECTED,
    ECONOMY_UNKNOWN,
    FAILED,
}

/** Immutable, platform-neutral terminal result. UNKNOWN never claims that external currency was charged. */
class EnhancementResult(
    val requestId: UUID,
    val playerId: UUID,
    val itemInstanceId: UUID,
    val status: EnhancementResultStatus,
    val reason: String?,
    val successful: Boolean?,
    val beforeLevel: Int?,
    val afterLevel: Int?,
    val highestCheckpoint: Int?,
    val schemeId: String?,
    val rawDowngrade: Int?,
    val protectedLevels: Int?,
    localCosts: List<EnhancementItemCost>,
    val economyClassification: String?,
    val requestedCurrency: EnhancementCurrencyCost?,
    val balanceBefore: String?,
    val balanceAfter: String?,
    milestones: List<String>,
    finalContributions: List<String>,
) {
    val localCosts: List<EnhancementItemCost> = java.util.List.copyOf(localCosts)
    val milestones: List<String> = java.util.List.copyOf(milestones)
    val finalContributions: List<String> = java.util.List.copyOf(finalContributions)
}