package net.minecraft.client.multiplayer; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.ChatFormatting; import net.minecraft.Util; import net.minecraft.client.ClientRecipeBook; import net.minecraft.client.gui.screens.recipebook.RecipeCollection; import net.minecraft.client.searchtree.FullTextSearchTree; import net.minecraft.client.searchtree.IdSearchTree; import net.minecraft.client.searchtree.SearchTree; import net.minecraft.core.HolderLookup; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import net.minecraft.tags.TagKey; import net.minecraft.util.context.ContextMap; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.TooltipFlag.Default; import net.minecraft.world.item.crafting.display.SlotDisplayContext; import net.minecraft.world.level.Level; @Environment(EnvType.CLIENT) public class SessionSearchTrees { private static final SessionSearchTrees.Key RECIPE_COLLECTIONS = new SessionSearchTrees.Key(); private static final SessionSearchTrees.Key CREATIVE_NAMES = new SessionSearchTrees.Key(); private static final SessionSearchTrees.Key CREATIVE_TAGS = new SessionSearchTrees.Key(); private CompletableFuture> creativeByNameSearch = CompletableFuture.completedFuture(SearchTree.empty()); private CompletableFuture> creativeByTagSearch = CompletableFuture.completedFuture(SearchTree.empty()); private CompletableFuture> recipeSearch = CompletableFuture.completedFuture(SearchTree.empty()); private final Map reloaders = new IdentityHashMap(); private void register(SessionSearchTrees.Key key, Runnable reloader) { reloader.run(); this.reloaders.put(key, reloader); } public void rebuildAfterLanguageChange() { for (Runnable runnable : this.reloaders.values()) { runnable.run(); } } private static Stream getTooltipLines(Stream items, Item.TooltipContext context, TooltipFlag tooltipFlag) { return items.flatMap(itemStack -> itemStack.getTooltipLines(context, null, tooltipFlag).stream()) .map(component -> ChatFormatting.stripFormatting(component.getString()).trim()) .filter(string -> !string.isEmpty()); } public void updateRecipes(ClientRecipeBook recipeBook, Level level) { this.register( RECIPE_COLLECTIONS, () -> { List list = recipeBook.getCollections(); RegistryAccess registryAccess = level.registryAccess(); Registry registry = registryAccess.lookupOrThrow(Registries.ITEM); Item.TooltipContext tooltipContext = Item.TooltipContext.of(registryAccess); ContextMap contextMap = SlotDisplayContext.fromLevel(level); TooltipFlag tooltipFlag = Default.NORMAL; CompletableFuture completableFuture = this.recipeSearch; this.recipeSearch = CompletableFuture.supplyAsync( () -> new FullTextSearchTree( recipeCollection -> getTooltipLines( recipeCollection.getRecipes().stream().flatMap(recipeDisplayEntry -> recipeDisplayEntry.resultItems(contextMap).stream()), tooltipContext, tooltipFlag ), recipeCollection -> recipeCollection.getRecipes() .stream() .flatMap(recipeDisplayEntry -> recipeDisplayEntry.resultItems(contextMap).stream()) .map(itemStack -> registry.getKey(itemStack.getItem())), list ), Util.backgroundExecutor() ); completableFuture.cancel(true); } ); } public SearchTree recipes() { return (SearchTree)this.recipeSearch.join(); } public void updateCreativeTags(List items) { this.register( CREATIVE_TAGS, () -> { CompletableFuture completableFuture = this.creativeByTagSearch; this.creativeByTagSearch = CompletableFuture.supplyAsync( () -> new IdSearchTree(itemStack -> itemStack.getTags().map(TagKey::location), items), Util.backgroundExecutor() ); completableFuture.cancel(true); } ); } public SearchTree creativeTagSearch() { return (SearchTree)this.creativeByTagSearch.join(); } public void updateCreativeTooltips(HolderLookup.Provider registries, List items) { this.register( CREATIVE_NAMES, () -> { Item.TooltipContext tooltipContext = Item.TooltipContext.of(registries); TooltipFlag tooltipFlag = Default.NORMAL.asCreative(); CompletableFuture completableFuture = this.creativeByNameSearch; this.creativeByNameSearch = CompletableFuture.supplyAsync( () -> new FullTextSearchTree( itemStack -> getTooltipLines(Stream.of(itemStack), tooltipContext, tooltipFlag), itemStack -> itemStack.getItemHolder().unwrapKey().map(ResourceKey::location).stream(), items ), Util.backgroundExecutor() ); completableFuture.cancel(true); } ); } public SearchTree creativeNameSearch() { return (SearchTree)this.creativeByNameSearch.join(); } @Environment(EnvType.CLIENT) static class Key { } }