コンテンツにスキップ

Search API

Search は CustomCrafterAPI でレシピ検索を行うためのエントリポイントとなるオブジェクトです。 入力されたアイテムの配置と登録済みレシピを照合し、合致するレシピとその座標対応情報を返します。


CraftView はレシピ検索の引数となるクラスで、クラフト UI 上のアイテム配置を表します。

data class CraftView(
val materials: Map<CoordinateComponent, ItemStack>, // 入力されたアイテムの配置
val result: ItemStack // 成果物スロットのアイテム
)

materials のサイズは 1 以上 36 以下である必要があります。それ以外の場合は Search.search / Search.asyncSearchIllegalArgumentException がスローされます。

// 石を (0,0) に配置した CraftView を作成する例
val view = CraftView(
materials = mapOf(CoordinateComponent(0, 0) to ItemStack.of(Material.STONE)),
result = ItemStack.empty()
)

fun search(
crafterID: UUID,
view: CraftView,
forceSearchVanillaRecipe: Boolean = true,
onlyFirst: Boolean = false,
sourceRecipes: List<CRecipe> = CustomCrafterAPI.getRecipes()
): SearchResult

メインスレッドで動作する同期的な検索メソッドです。 登録済みレシピが多い場合や、predicates が重い処理を含む場合はサーバーの TPS に影響する可能性があります。 そのような場合は後述の asyncSearch の使用を推奨します。

引数概要
crafterIDクラフトを実行するプレイヤーの UUID
view入力されたアイテムの配置
forceSearchVanillaRecipetrue の場合は常にバニラレシピを検索する。false かつカスタムレシピが見つかった場合はバニラを検索しない
onlyFirsttrue にすると最初に合致したカスタムレシピのみ返す
sourceRecipes検索対象のレシピリスト (デフォルトは登録済み全レシピ)
val player: Player = /* ... */
val view = CraftView(
materials = mapOf(CoordinateComponent(0, 0) to ItemStack.of(Material.STONE)),
result = ItemStack.empty()
)
val result: Search.SearchResult = Search.search(player.uniqueId, view)

fun asyncSearch(
crafterID: UUID,
view: CraftView,
query: SearchQuery = SearchQuery.ASYNC_DEFAULT,
sourceRecipes: List<CRecipe> = CustomCrafterAPI.getRecipes()
): CompletableFuture<SearchResult>

仮想スレッドを用いた非同期検索メソッドです (5.0.17 以降)。 各レシピの検索は個別のスレッドで並列実行されるため、データベースや外部 API を呼び出すような重い predicates を持つレシピが多数存在する場合に特に効果的です。 CustomCrafterAPI の標準クラフト画面および全候補表示機能では、このメソッドが内部的に使用されています。

val player: Player = /* ... */
val view = CraftView(
materials = mapOf(CoordinateComponent(0, 0) to ItemStack.of(Material.STONE)),
result = ItemStack.empty()
)
val future: CompletableFuture<Search.SearchResult> = Search.asyncSearch(player.uniqueId, view)
future.thenAccept { result ->
// 非同期で結果を処理
val customs = result.customs()
println("合致したカスタムレシピ数: ${customs.size}")
}

SearchQueryasyncSearch の検索動作を制御するクラスです。

class SearchQuery(
val searchMode: SearchMode,
val vanillaSearchMode: VanillaSearchMode,
val asyncContext: AsyncContext? = null
)
概要
ALL (デフォルト)合致するすべてのカスタムレシピを返す
ONLY_FIRST最初に合致したカスタムレシピのみ返す。見つかり次第他の検索タスクをキャンセルする
概要
IF_CUSTOMS_NOT_FOUND (デフォルト)カスタムレシピが見つからなかった場合のみバニラを検索する
FORCEカスタムレシピの有無にかかわらず常にバニラを検索する
// ONLY_FIRST モードで検索する
val query = Search.SearchQuery(
searchMode = Search.SearchQuery.SearchMode.ONLY_FIRST,
vanillaSearchMode = Search.SearchQuery.VanillaSearchMode.IF_CUSTOMS_NOT_FOUND,
asyncContext = AsyncContext.ofTurnOff()
)
val future = Search.asyncSearch(player.uniqueId, view, query)

SearchResult は検索結果を保持するクラスです。

メソッド返り値概要
vanilla()Recipe?バニラレシピ。見つからなかった場合または検索しなかった場合は null
customs()List<Pair<CRecipe, MappedRelation>>合致したカスタムレシピとその座標対応のリスト
size()Intバニラとカスタムの合計合致数
getMergedResults()List<Pair<CRecipe, MappedRelation?>>バニラ・カスタムをまとめたリスト。バニラは MappedRelationnull になる
val result: Search.SearchResult = Search.search(player.uniqueId, view)
// バニラレシピの取得
val vanilla: Recipe? = result.vanilla()
vanilla?.let { println("バニラレシピ: ${it.result.type}") }
// カスタムレシピの取得
val customs: List<Pair<CRecipe, MappedRelation>> = result.customs()
customs.forEach { (recipe, relation) ->
println("カスタムレシピ: ${recipe.name}")
}
// 全候補をまとめて取得
result.getMergedResults().forEach { (recipe, relation) ->
println("レシピ: ${recipe.name}, 座標対応あり: ${relation != null}")
}

VanillaSearch は CustomCrafterAPI の検索フローを介さず、バニラのレシピのみを検索するためのオブジェクトです。

// 丸石から石炉を作るバニラレシピを検索する例
val view = CraftView(
materials = CoordinateComponent.squareFill(3)
.filter { it.x < 3 && it.y < 3 }
.associate { it to ItemStack.of(Material.COBBLESTONE) },
result = ItemStack.empty()
)
val vanillaRecipe: Recipe? = VanillaSearch.search(Bukkit.getWorlds().first(), view)
vanillaRecipe?.let { println("結果アイテム: ${it.result.type}") }

MappedRelation と MappedRelationComponent

Section titled “MappedRelation と MappedRelationComponent”

MappedRelation はレシピ上の座標と実際の入力スロット座標の対応関係を保持するクラスです。 MappedRelationComponent は 1 つの対応ペア (レシピ座標 → 入力座標) を表します。

data class MappedRelation(
val components: Set<MappedRelationComponent>
)
data class MappedRelationComponent(
val recipe: CoordinateComponent, // レシピ上の座標
val input: CoordinateComponent // 実際の入力スロットの座標
)

たとえば定形レシピで (0,0) に石を要求しているとき、プレイヤーが (2,2) にアイテムを配置してもレシピに合致する場合があります。 その際の MappedRelationComponentrecipe = (0,0), input = (2,2) となります。 この情報は ResultSupplier.Context.relationCRecipePredicate.Context.relation として渡され、どのスロットに何が配置されていたかを追跡するために使用します。