diff options
| author | shedaniel <daniel@shedaniel.me> | 2020-12-20 19:59:49 +0800 |
|---|---|---|
| committer | shedaniel <daniel@shedaniel.me> | 2020-12-20 19:59:49 +0800 |
| commit | 67171a5ff24ed77e6c4cc889543e8dfb543e8fe5 (patch) | |
| tree | 02909ac26d2b74ebf08f5c463f1b9a60483468df /runtime/src/main/java/me/shedaniel/rei/impl/RecipeHelperImpl.java | |
| parent | 9784e9f7228fc0aa3ca814e3830dbd81996a3693 (diff) | |
| download | RoughlyEnoughItems-67171a5ff24ed77e6c4cc889543e8dfb543e8fe5.tar.gz RoughlyEnoughItems-67171a5ff24ed77e6c4cc889543e8dfb543e8fe5.tar.bz2 RoughlyEnoughItems-67171a5ff24ed77e6c4cc889543e8dfb543e8fe5.zip | |
wip more
Signed-off-by: shedaniel <daniel@shedaniel.me>
Diffstat (limited to 'runtime/src/main/java/me/shedaniel/rei/impl/RecipeHelperImpl.java')
| -rw-r--r-- | runtime/src/main/java/me/shedaniel/rei/impl/RecipeHelperImpl.java | 648 |
1 files changed, 648 insertions, 0 deletions
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/RecipeHelperImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/RecipeHelperImpl.java new file mode 100644 index 000000000..d48fcec02 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/RecipeHelperImpl.java @@ -0,0 +1,648 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.impl; + +import com.google.common.base.Stopwatch; +import com.google.common.collect.*; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.RoughlyEnoughItemsCore; +import me.shedaniel.rei.api.*; +import me.shedaniel.rei.api.entry.EntryStacks; +import me.shedaniel.rei.api.fluid.FluidSupportProvider; +import me.shedaniel.rei.api.plugins.REIPluginV0; +import me.shedaniel.rei.api.subsets.SubsetsRegistry; +import me.shedaniel.rei.impl.subsets.SubsetsRegistryImpl; +import me.shedaniel.rei.utils.CollectionUtils; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.Util; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeManager; +import org.apache.commons.lang3.tuple.MutablePair; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@ApiStatus.Internal +@Environment(EnvType.CLIENT) +public class RecipeHelperImpl implements RecipeHelper { + + private static final Comparator<FocusedStackProvider> FOCUSED_STACK_PROVIDER_COMPARATOR = Comparator.comparingDouble(FocusedStackProvider::getPriority).reversed(); + private static final Comparator<DisplayVisibilityHandler> VISIBILITY_HANDLER_COMPARATOR = Comparator.comparingDouble(DisplayVisibilityHandler::getPriority).reversed(); + @SuppressWarnings("rawtypes") + private static final Comparator<Recipe> RECIPE_COMPARATOR = Comparator.comparing((Recipe o) -> o.getId().getNamespace()).thenComparing(o -> o.getId().getPath()); + + private final List<FocusedStackProvider> focusedStackProviders = Lists.newArrayList(); + private final List<AutoTransferHandler> autoTransferHandlers = Lists.newArrayList(); + private final List<RecipeFunction<?>> recipeFunctions = Lists.newArrayList(); + private final Multimap<Class<? extends Screen>, ClickAreaHandler<?>> screenClickAreas = HashMultimap.create(); + private final int[] recipeCount = {0}; + private final Map<ResourceLocation, List<RecipeDisplay>> recipeDisplays = Maps.newHashMap(); + private final BiMap<RecipeCategory<?>, ResourceLocation> categories = HashBiMap.create(); + private final Map<ResourceLocation, ButtonAreaSupplier> autoCraftAreaSupplierMap = Maps.newHashMap(); + private final Map<ResourceLocation, List<List<? extends EntryStack<?>>>> categoryWorkingStations = Maps.newHashMap(); + private final List<DisplayVisibilityHandler> displayVisibilityHandlers = Lists.newArrayList(); + private final List<LiveRecipeGenerator<RecipeDisplay>> liveRecipeGenerators = Lists.newArrayList(); + private RecipeManager recipeManager; + private boolean arePluginsLoading = false; + + @Override + public List<EntryStack<?>> findCraftableEntriesByItems(Iterable<? extends EntryStack<?>> inventoryItems) { + List<EntryStack<?>> craftables = new ArrayList<>(); + for (List<RecipeDisplay> value : recipeDisplays.values()) + for (RecipeDisplay recipeDisplay : Lists.newArrayList(value)) { + int slotsCraftable = 0; + List<? extends List<? extends EntryStack<?>>> requiredInput = recipeDisplay.getRequiredEntries(); + for (List<? extends EntryStack<?>> slot : requiredInput) { + if (slot.isEmpty()) { + slotsCraftable++; + continue; + } + back: + for (EntryStack<?> possibleType : inventoryItems) { + for (EntryStack<?> slotPossible : slot) + if (EntryStacks.equalsIgnoreCount(possibleType, slotPossible)) { + slotsCraftable++; + break back; + } + } + } + if (slotsCraftable == recipeDisplay.getRequiredEntries().size()) + recipeDisplay.getResultingEntries().stream().flatMap(Collection::stream).collect(Collectors.toCollection(() -> craftables)); + } + return craftables.stream().distinct().collect(Collectors.toList()); + } + + @Override + public boolean arePluginsLoading() { + return arePluginsLoading; + } + + @Override + public void registerCategory(RecipeCategory<?> category) { + categories.put(category, category.getIdentifier()); + recipeDisplays.put(category.getIdentifier(), Lists.newArrayList()); + categoryWorkingStations.put(category.getIdentifier(), Lists.newArrayList()); + } + + @SafeVarargs + @Override + public final void registerWorkingStations(ResourceLocation category, List<? extends EntryStack<?>>... workingStations) { + categoryWorkingStations.get(category).addAll(Arrays.asList(workingStations)); + } + + @Override + public void registerWorkingStations(ResourceLocation category, EntryStack<?>... workingStations) { + categoryWorkingStations.get(category).addAll(Stream.of(workingStations).map(Collections::singletonList).collect(Collectors.toList())); + } + + @Override + public List<List<? extends EntryStack<?>>> getWorkingStations(ResourceLocation category) { + return categoryWorkingStations.getOrDefault(category, Collections.emptyList()); + } + + @Override + public void registerDisplay(RecipeDisplay display) { + ResourceLocation identifier = Objects.requireNonNull(display.getRecipeCategory()); + if (!recipeDisplays.containsKey(identifier)) + return; + recipeCount[0]++; + recipeDisplays.get(identifier).add(display); + } + + private void registerDisplay(ResourceLocation categoryIdentifier, RecipeDisplay display, int index) { + if (!recipeDisplays.containsKey(categoryIdentifier)) + return; + recipeCount[0]++; + recipeDisplays.get(categoryIdentifier).add(index, display); + } + + @Override + public Map<RecipeCategory<?>, List<RecipeDisplay>> buildMapFor(ClientHelper.ViewSearchBuilder builder) { + Stopwatch stopwatch = Stopwatch.createStarted(); + Set<ResourceLocation> categories = builder.getCategories(); + List<EntryStack<?>> recipesFor = builder.getRecipesFor(); + List<EntryStack<?>> usagesFor = builder.getUsagesFor(); + + Map<RecipeCategory<?>, List<RecipeDisplay>> result = Maps.newLinkedHashMap(); + for (Map.Entry<RecipeCategory<?>, ResourceLocation> entry : this.categories.entrySet()) { + RecipeCategory<?> category = entry.getKey(); + ResourceLocation categoryId = entry.getValue(); + List<RecipeDisplay> allRecipesFromCategory = getAllRecipesFromCategory(category); + + Set<RecipeDisplay> set = Sets.newLinkedHashSet(); + if (categories.contains(categoryId)) { + for (RecipeDisplay display : allRecipesFromCategory) { + if (isDisplayVisible(display)) { + set.add(display); + } + } + if (!set.isEmpty()) { + CollectionUtils.getOrPutEmptyList(result, category).addAll(set); + } + continue; + } + for (RecipeDisplay display : allRecipesFromCategory) { + if (!isDisplayVisible(display)) continue; + if (!recipesFor.isEmpty()) { + back: + for (List<? extends EntryStack<?>> results : display.getResultingEntries()) { + for (EntryStack<?> otherEntry : results) { + for (EntryStack<?> stack : recipesFor) { + if (EntryStacks.equalsIgnoreCount(otherEntry, stack)) { + set.add(display); + break back; + } + } + } + } + } + if (!usagesFor.isEmpty()) { + back: + for (List<? extends EntryStack<?>> input : display.getInputEntries()) { + for (EntryStack<?> otherEntry : input) { + for (EntryStack<?> stack : usagesFor) { + if (EntryStacks.equalsIgnoreCount(otherEntry, stack)) { + set.add(display); + break back; + } + } + } + } + } + } + for (EntryStack<?> stack : usagesFor) { + if (isStackWorkStationOfCategory(categoryId, stack)) { + set.addAll(CollectionUtils.filter(allRecipesFromCategory, this::isDisplayVisible)); + break; + } + } + if (!set.isEmpty()) { + CollectionUtils.getOrPutEmptyList(result, category).addAll(set); + } + } + + for (LiveRecipeGenerator<RecipeDisplay> liveRecipeGenerator : liveRecipeGenerators) { + Set<RecipeDisplay> set = Sets.newLinkedHashSet(); + for (EntryStack<?> stack : recipesFor) { + Optional<List<RecipeDisplay>> recipeForDisplays = liveRecipeGenerator.getRecipeFor(stack); + if (recipeForDisplays.isPresent()) { + for (RecipeDisplay display : recipeForDisplays.get()) { + if (isDisplayVisible(display)) + set.add(display); + } + } + } + for (EntryStack<?> stack : usagesFor) { + Optional<List<RecipeDisplay>> usageForDisplays = liveRecipeGenerator.getUsageFor(stack); + if (usageForDisplays.isPresent()) { + for (RecipeDisplay display : usageForDisplays.get()) { + if (isDisplayVisible(display)) + set.add(display); + } + } + } + Optional<List<RecipeDisplay>> displaysGenerated = liveRecipeGenerator.getDisplaysGenerated(builder); + if (displaysGenerated.isPresent()) { + for (RecipeDisplay display : displaysGenerated.get()) { + if (isDisplayVisible(display)) + set.add(display); + } + } + if (!set.isEmpty()) { + CollectionUtils.getOrPutEmptyList(result, getCategory(liveRecipeGenerator.getCategoryIdentifier())).addAll(set); + } + } + + String message = String.format("Built Recipe View in %s for %d categories, %d recipes for, %d usages for and %d live recipe generators.", + stopwatch.stop().toString(), categories.size(), recipesFor.size(), usagesFor.size(), liveRecipeGenerators.size()); + if (ConfigObject.getInstance().doDebugSearchTimeRequired()) { + RoughlyEnoughItemsCore.LOGGER.info(message); + } else { + RoughlyEnoughItemsCore.LOGGER.trace(message); + } + return result; + } + + @Override + public Map<RecipeCategory<?>, List<RecipeDisplay>> getRecipesFor(EntryStack<?> stack) { + return buildMapFor(ClientHelper.ViewSearchBuilder.builder().addRecipesFor(stack)); + } + + @Override + public RecipeCategory<?> getCategory(ResourceLocation identifier) { + return categories.inverse().get(identifier); + } + + @Override + public RecipeManager getRecipeManager() { + return recipeManager; + } + + private boolean isStackWorkStationOfCategory(ResourceLocation category, EntryStack<?> stack) { + for (List<? extends EntryStack<?>> stacks : getWorkingStations(category)) { + for (EntryStack<?> entryStack : stacks) { + if (EntryStacks.equalsFuzzy(entryStack, stack)) + return true; + } + } + return false; + } + + @Override + public Map<RecipeCategory<?>, List<RecipeDisplay>> getUsagesFor(EntryStack<?> stack) { + return buildMapFor(ClientHelper.ViewSearchBuilder.builder().addUsagesFor(stack)); + } + + @Override + public List<RecipeCategory<?>> getAllCategories() { + return Lists.newArrayList(categories.keySet()); + } + + @Override + public Optional<ButtonAreaSupplier> getAutoCraftButtonArea(RecipeCategory<?> category) { + if (!autoCraftAreaSupplierMap.containsKey(category.getIdentifier())) + return Optional.ofNullable(bounds -> new Rectangle(bounds.getMaxX() - 16, bounds.getMaxY() - 16, 10, 10)); + return Optional.ofNullable(autoCraftAreaSupplierMap.get(category.getIdentifier())); + } + + @Override + public void registerAutoCraftButtonArea(ResourceLocation category, ButtonAreaSupplier rectangle) { + if (rectangle == null) { + autoCraftAreaSupplierMap.remove(category); + } else + autoCraftAreaSupplierMap.put(category, rectangle); + } + + private void startSection(MutablePair<Stopwatch, String> sectionData, String section) { + sectionData.setRight(section); + RoughlyEnoughItemsCore.LOGGER.debug("Reloading Section: \"%s\"", section); + sectionData.getLeft().reset().start(); + } + + private void endSection(MutablePair<Stopwatch, String> sectionData) { + sectionData.getLeft().stop(); + String section = sectionData.getRight(); + RoughlyEnoughItemsCore.LOGGER.debug("Reloading Section: \"%s\" done in %s", section, sectionData.getLeft().toString()); + sectionData.getLeft().reset(); + } + + private void pluginSection(MutablePair<Stopwatch, String> sectionData, String sectionName, List<REIPluginV0> list, Consumer<REIPluginV0> consumer) { + for (REIPluginV0 plugin : list) { + startSection(sectionData, sectionName + " for " + plugin.getPluginIdentifier().toString()); + try { + consumer.accept(plugin); + } catch (Throwable e) { + RoughlyEnoughItemsCore.LOGGER.error(plugin.getPluginIdentifier().toString() + " plugin failed to " + sectionName + "!", e); + } + endSection(sectionData); + } + } + + public void tryRecipesLoaded(RecipeManager recipeManager) { + try { + recipesLoaded(recipeManager); + } catch (Throwable throwable) { + throwable.printStackTrace(); + } + arePluginsLoading = false; + } + + public void recipesLoaded(RecipeManager recipeManager) { + long startTime = Util.getMillis(); + MutablePair<Stopwatch, String> sectionData = new MutablePair<>(Stopwatch.createUnstarted(), ""); + + startSection(sectionData, "reset-data"); + arePluginsLoading = true; + ScreenHelper.clearLastRecipeScreenData(); + recipeCount[0] = 0; + this.recipeManager = recipeManager; + this.recipeDisplays.clear(); + this.categories.clear(); + this.autoCraftAreaSupplierMap.clear(); + this.screenClickAreas.clear(); + this.categoryWorkingStations.clear(); + this.recipeFunctions.clear(); + this.displayVisibilityHandlers.clear(); + this.liveRecipeGenerators.clear(); + this.autoTransferHandlers.clear(); + this.focusedStackProviders.clear(); + + DisplayHelperImpl displayHelper = (DisplayHelperImpl) DisplayHelper.getInstance(); + EntryRegistryImpl entryRegistry = (EntryRegistryImpl) EntryRegistry.getInstance(); + EntryTypeRegistryImpl entryTypeRegistry = EntryTypeRegistryImpl.getInstance(); + + entryTypeRegistry.reset(); + FavoriteEntryTypeRegistryImpl.getInstance().clear(); + ((SubsetsRegistryImpl) SubsetsRegistry.getInstance()).reset(); + ((FluidSupportProviderImpl) FluidSupportProvider.getInstance()).reset(); + displayHelper.resetData(); + displayHelper.resetCache(); + BaseBoundsHandler baseBoundsHandler = new BaseBoundsHandlerImpl(); + displayHelper.registerHandler(baseBoundsHandler); + displayHelper.setBaseBoundsHandler(baseBoundsHandler); + List<REIPluginEntry> plugins = RoughlyEnoughItemsCore.getPlugins(); + plugins.sort(Comparator.comparingInt(REIPluginEntry::getPriority).reversed()); + RoughlyEnoughItemsCore.LOGGER.info("Reloading REI, registered %d plugins: %s", plugins.size(), plugins.stream().map(REIPluginEntry::getPluginIdentifier).map(ResourceLocation::toString).collect(Collectors.joining(", "))); + Collections.reverse(plugins); + entryRegistry.resetToReloadStart(); + List<REIPluginV0> reiPluginV0s = new ArrayList<>(); + endSection(sectionData); + for (REIPluginEntry plugin : plugins) { + startSection(sectionData, "pre-register for " + plugin.getPluginIdentifier().toString()); + try { + if (plugin instanceof REIPluginV0) { + ((REIPluginV0) plugin).preRegister(); + reiPluginV0s.add((REIPluginV0) plugin); + } + } catch (Throwable e) { + RoughlyEnoughItemsCore.LOGGER.error(plugin.getPluginIdentifier().toString() + " plugin failed to pre register!", e); + } + endSection(sectionData); + } + pluginSection(sectionData, "register-entry-types", reiPluginV0s, plugin -> plugin.registerEntryTypes(entryTypeRegistry)); + pluginSection(sectionData, "register-bounds", reiPluginV0s, plugin -> plugin.registerBounds(displayHelper)); + pluginSection(sectionData, "register-entries", reiPluginV0s, plugin -> plugin.registerEntries(entryRegistry)); + pluginSection(sectionData, "register-categories", reiPluginV0s, plugin -> plugin.registerPluginCategories(this)); + pluginSection(sectionData, "register-displays", reiPluginV0s, plugin -> plugin.registerRecipeDisplays(this)); + pluginSection(sectionData, "register-others", reiPluginV0s, plugin -> plugin.registerOthers(this)); + pluginSection(sectionData, "post-register", reiPluginV0s, REIPluginV0::postRegister); + startSection(sectionData, "recipe-functions"); + if (!recipeFunctions.isEmpty()) { + List<Recipe<?>> allSortedRecipes = getAllSortedRecipes(); + for (int i = allSortedRecipes.size() - 1; i >= 0; i--) { + Recipe<?> recipe = allSortedRecipes.get(i); + for (RecipeFunction<?> recipeFunction : recipeFunctions) { + try { + if (recipeFunction.recipeFilter.test(recipe)) { + registerDisplay(recipeFunction.category, recipeFunction.get(recipe), 0); + } + } catch (Throwable e) { + RoughlyEnoughItemsCore.LOGGER.error("Failed to add recipes!", e); + } + } + } + } + endSection(sectionData); + startSection(sectionData, "fill-handlers"); + if (getDisplayVisibilityHandlers().isEmpty()) + registerRecipeVisibilityHandler(new DisplayVisibilityHandler() { + @Override + public InteractionResult handleDisplay(RecipeCategory<?> category, RecipeDisplay display) { + return InteractionResult.SUCCESS; + } + + @Override + public float getPriority() { + return -1f; + } + }); + registerFocusedStackProvider(new FocusedStackProvider() { + @Override + @NotNull + public InteractionResultHolder<EntryStack<?>> provide(Screen screen) { + if (screen instanceof AbstractContainerScreen) { + AbstractContainerScreen<?> containerScreen = (AbstractContainerScreen<?>) screen; + if (containerScreen.hoveredSlot != null && !containerScreen.hoveredSlot.getItem().isEmpty()) + return InteractionResultHolder.success(EntryStacks.of(containerScreen.hoveredSlot.getItem())); + } + return InteractionResultHolder.pass(EntryStack.empty()); + } + + @Override + public double getPriority() { + return -1.0; + } + }); + displayHelper.registerHandler(new OverlayDecider() { + @Override + public boolean isHandingScreen(Class<?> screen) { + return true; + } + + @Override + public InteractionResult shouldScreenBeOverlaid(Class<?> screen) { + return AbstractContainerScreen.class.isAssignableFrom(screen) ? InteractionResult.SUCCESS : InteractionResult.PASS; + } + + @Override + public float getPriority() { + return -10; + } + }); + endSection(sectionData); + + // Clear Cache + displayHelper.resetCache(); + REIHelper.getInstance().getOverlay().ifPresent(REIOverlay::queueReloadOverlay); + + startSection(sectionData, "entry-registry-finalise"); + + // Finish Reload + entryRegistry.finishReload(); + + endSection(sectionData); + startSection(sectionData, "entry-registry-refilter"); + + arePluginsLoading = false; + entryRegistry.refilter(); + + endSection(sectionData); + startSection(sectionData, "finalizing"); + + // Clear Cache Again! + displayHelper.resetCache(); + REIHelper.getInstance().getOverlay().ifPresent(REIOverlay::queueReloadOverlay); + + displayVisibilityHandlers.sort(VISIBILITY_HANDLER_COMPARATOR); + endSection(sectionData); + + long usedTime = Util.getMillis() - startTime; + RoughlyEnoughItemsCore.LOGGER.info("Reloaded %d stack entries, %d recipes displays, %d exclusion zones suppliers, %d overlay deciders, %d visibility handlers and %d categories (%s) in %dms.", + entryRegistry.getEntryStacks().count(), recipeCount[0], BaseBoundsHandler.getInstance().supplierSize(), displayHelper.getAllOverlayDeciders().size(), getDisplayVisibilityHandlers().size(), categories.size(), categories.keySet().stream().map(RecipeCategory::getCategoryName).collect(Collectors.joining(", ")), usedTime); + } + + @Override + public AutoTransferHandler registerAutoCraftingHandler(AutoTransferHandler handler) { + autoTransferHandlers.add(handler); + autoTransferHandlers.sort(Comparator.comparingDouble(AutoTransferHandler::getPriority).reversed()); + return handler; + } + + @Override + public void registerFocusedStackProvider(FocusedStackProvider provider) { + focusedStackProviders.add(provider); + focusedStackProviders.sort(FOCUSED_STACK_PROVIDER_COMPARATOR); + } + + @Override + @Nullable + public EntryStack<?> getScreenFocusedStack(Screen screen) { + for (FocusedStackProvider provider : focusedStackProviders) { + InteractionResultHolder<EntryStack<?>> result = Objects.requireNonNull(provider.provide(screen)); + if (result.getResult() == InteractionResult.SUCCESS) { + if (!result.getObject().isEmpty()) + return result.getObject(); + return null; + } else if (result.getResult() == InteractionResult.FAIL) + return null; + } + return null; + } + + @Override + public List<AutoTransferHandler> getSortedAutoCraftingHandler() { + return autoTransferHandlers; + } + + @Override + public int getRecipeCount() { + return recipeCount[0]; + } + + @Override + public List<Recipe<?>> getAllSortedRecipes() { + return getRecipeManager().getRecipes().parallelStream().sorted(RECIPE_COMPARATOR).collect(Collectors.toList()); + } + + @Override + public Map<RecipeCategory<?>, List<RecipeDisplay>> getAllRecipes() { + return buildMapFor(ClientHelper.ViewSearchBuilder.builder().addAllCategories()); + } + + @Override + public Map<RecipeCategory<?>, List<RecipeDisplay>> getAllRecipesNoHandlers() { + Map<RecipeCategory<?>, List<RecipeDisplay>> result = Maps.newLinkedHashMap(); + for (Map.Entry<RecipeCategory<?>, ResourceLocation> entry : categories.entrySet()) { + RecipeCategory<?> category = entry.getKey(); + ResourceLocation categoryId = entry.getValue(); + List<RecipeDisplay> displays = recipeDisplays.get(categoryId); + if (displays != null && !displays.isEmpty()) { + result.put(category, displays); + } + } + return result; + } + + @Override + public List<RecipeDisplay> getAllRecipesFromCategory(RecipeCategory<?> category) { + return recipeDisplays.get(category.getIdentifier()); + } + + @Override + public void registerRecipeVisibilityHandler(DisplayVisibilityHandler visibilityHandler) { + displayVisibilityHandlers.add(visibilityHandler); + } + + @Override + public void unregisterRecipeVisibilityHandler(DisplayVisibilityHandler visibilityHandler) { + displayVisibilityHandlers.remove(visibilityHandler); + } + + @Override + public List<DisplayVisibilityHandler> getDisplayVisibilityHandlers() { + return Collections.unmodifiableList(displayVisibilityHandlers); + } + + @Override + public boolean isDisplayNotVisible(RecipeDisplay display) { + return !isDisplayVisible(display); + } + + @Override + public boolean isDisplayVisible(RecipeDisplay display) { + RecipeCategory<?> category = getCategory(display.getRecipeCategory()); + try { + for (DisplayVisibilityHandler displayVisibilityHandler : displayVisibilityHandlers) { + InteractionResult visibility = displayVisibilityHandler.handleDisplay(category, display); + if (visibility != InteractionResult.PASS) + return visibility == InteractionResult.SUCCESS; + } + } catch (Throwable throwable) { + RoughlyEnoughItemsCore.LOGGER.error("Failed to check if the recipe is visible!", throwable); + } + return true; + } + + @Override + public <T extends AbstractContainerScreen<?>> void registerContainerClickArea(ScreenClickAreaProvider<T> rectangleSupplier, Class<T> screenClass, ResourceLocation... categories) { + registerClickArea(screen -> { + Rectangle rectangle = rectangleSupplier.provide(screen).clone(); + rectangle.translate(screen.leftPos, screen.topPos); + return rectangle; + }, screenClass, categories); + } + + @Override + public <T extends Screen> void registerClickArea(ScreenClickAreaProvider<T> rectangleSupplier, Class<T> screenClass, ResourceLocation... categories) { + registerClickArea(screenClass, rectangleSupplier.toHandler(() -> categories)); + } + + @Override + public <T extends Screen> void registerClickArea(Class<T> screenClass, ClickAreaHandler<T> handler) { + this.screenClickAreas.put(screenClass, handler); + } + + @Override + public <T extends Recipe<?>> void registerRecipes(ResourceLocation category, Predicate<Recipe<?>> recipeFilter, Function<T, RecipeDisplay> mappingFunction) { + recipeFunctions.add(new RecipeFunction<>(category, recipeFilter, mappingFunction)); + } + + @Override + public void registerLiveRecipeGenerator(LiveRecipeGenerator<?> liveRecipeGenerator) { + liveRecipeGenerators.add((LiveRecipeGenerator<RecipeDisplay>) liveRecipeGenerator); + } + + public Multimap<Class<? extends Screen>, ClickAreaHandler<?>> getClickAreas() { + return screenClickAreas; + } + + private static class RecipeFunction<T extends Recipe<?>> { + private ResourceLocation category; + private Predicate<Recipe<?>> recipeFilter; + private Function<T, RecipeDisplay> mappingFunction; + + public RecipeFunction(ResourceLocation category, Predicate<Recipe<?>> recipeFilter, Function<T, RecipeDisplay> mappingFunction) { + this.category = category; + this.recipeFilter = recipeFilter; + this.mappingFunction = mappingFunction; + } + + private <A> RecipeDisplay get(A screen) { + return mappingFunction.apply((T) screen); + } + } +} |
