Search API
Search とは
Section titled “Search とは”Search は CustomCrafterAPI でレシピ検索を行うためのエントリポイントとなるオブジェクトです。
入力されたアイテムの配置と登録済みレシピを照合し、合致するレシピとその座標対応情報を返します。
CraftView
Section titled “CraftView”CraftView はレシピ検索の引数となるクラスで、クラフト UI 上のアイテム配置を表します。
data class CraftView( val materials: Map<CoordinateComponent, ItemStack>, // 入力されたアイテムの配置 val result: ItemStack // 成果物スロットのアイテム)materials のサイズは 1 以上 36 以下である必要があります。それ以外の場合は Search.search / Search.asyncSearch で IllegalArgumentException がスローされます。
// 石を (0,0) に配置した CraftView を作成する例val view = CraftView( materials = mapOf(CoordinateComponent(0, 0) to ItemStack.of(Material.STONE)), result = ItemStack.empty())Search.search (同期検索)
Section titled “Search.search (同期検索)”fun search( crafterID: UUID, view: CraftView, forceSearchVanillaRecipe: Boolean = true, onlyFirst: Boolean = false, sourceRecipes: List<CRecipe> = CustomCrafterAPI.getRecipes()): SearchResultメインスレッドで動作する同期的な検索メソッドです。
登録済みレシピが多い場合や、predicates が重い処理を含む場合はサーバーの TPS に影響する可能性があります。
そのような場合は後述の asyncSearch の使用を推奨します。
| 引数 | 概要 |
|---|---|
crafterID | クラフトを実行するプレイヤーの UUID |
view | 入力されたアイテムの配置 |
forceSearchVanillaRecipe | true の場合は常にバニラレシピを検索する。false かつカスタムレシピが見つかった場合はバニラを検索しない |
onlyFirst | true にすると最初に合致したカスタムレシピのみ返す |
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)Search.asyncSearch (非同期検索)
Section titled “Search.asyncSearch (非同期検索)”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}")}SearchQuery
Section titled “SearchQuery”SearchQuery は asyncSearch の検索動作を制御するクラスです。
class SearchQuery( val searchMode: SearchMode, val vanillaSearchMode: VanillaSearchMode, val asyncContext: AsyncContext? = null)SearchMode
Section titled “SearchMode”| 値 | 概要 |
|---|---|
ALL (デフォルト) | 合致するすべてのカスタムレシピを返す |
ONLY_FIRST | 最初に合致したカスタムレシピのみ返す。見つかり次第他の検索タスクをキャンセルする |
VanillaSearchMode
Section titled “VanillaSearchMode”| 値 | 概要 |
|---|---|
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
Section titled “SearchResult”SearchResult は検索結果を保持するクラスです。
| メソッド | 返り値 | 概要 |
|---|---|---|
vanilla() | Recipe? | バニラレシピ。見つからなかった場合または検索しなかった場合は null |
customs() | List<Pair<CRecipe, MappedRelation>> | 合致したカスタムレシピとその座標対応のリスト |
size() | Int | バニラとカスタムの合計合致数 |
getMergedResults() | List<Pair<CRecipe, MappedRelation?>> | バニラ・カスタムをまとめたリスト。バニラは MappedRelation が null になる |
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
Section titled “VanillaSearch”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) にアイテムを配置してもレシピに合致する場合があります。
その際の MappedRelationComponent は recipe = (0,0), input = (2,2) となります。
この情報は ResultSupplier.Context.relation や CRecipePredicate.Context.relation として渡され、どのスロットに何が配置されていたかを追跡するために使用します。