diff options
| author | shedaniel <daniel@shedaniel.me> | 2022-08-05 01:30:08 +0800 |
|---|---|---|
| committer | shedaniel <daniel@shedaniel.me> | 2022-08-26 10:53:55 +0900 |
| commit | 8c13c015031a0de865d2e767cd8e879754f803e2 (patch) | |
| tree | 2bb6fa6578b63d1c216b863a6c4206295c044b36 /runtime-engine/views/src/main/java | |
| parent | 8f5d3ef632f3d1a733c98ce5607c9fd5a0fd7567 (diff) | |
| download | RoughlyEnoughItems-8c13c015031a0de865d2e767cd8e879754f803e2.tar.gz RoughlyEnoughItems-8c13c015031a0de865d2e767cd8e879754f803e2.tar.bz2 RoughlyEnoughItems-8c13c015031a0de865d2e767cd8e879754f803e2.zip | |
More work
Diffstat (limited to 'runtime-engine/views/src/main/java')
5 files changed, 987 insertions, 0 deletions
diff --git a/runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/AbstractViewSearchBuilder.java b/runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/AbstractViewSearchBuilder.java new file mode 100644 index 000000000..f814bf078 --- /dev/null +++ b/runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/AbstractViewSearchBuilder.java @@ -0,0 +1,50 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 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.client.view; + +import me.shedaniel.rei.api.client.gui.screen.DisplayScreen; +import me.shedaniel.rei.api.client.view.ViewSearchBuilder; +import me.shedaniel.rei.impl.display.DisplaySpec; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.Screen; + +import java.util.Collection; +import java.util.stream.Stream; + +abstract class AbstractViewSearchBuilder implements ViewSearchBuilder { + public ViewSearchBuilder fillPreferredOpenedCategory() { + if (getPreferredOpenedCategory() == null) { + Screen currentScreen = Minecraft.getInstance().screen; + if (currentScreen instanceof DisplayScreen displayScreen) { + setPreferredOpenedCategory(displayScreen.getCurrentCategoryId()); + } + } + return this; + } + + @Override + public Stream<DisplaySpec> streamDisplays() { + return buildMapInternal().values().stream().flatMap(Collection::stream); + } +} diff --git a/runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/LegacyWrapperViewSearchBuilder.java b/runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/LegacyWrapperViewSearchBuilder.java new file mode 100644 index 000000000..406787e9f --- /dev/null +++ b/runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/LegacyWrapperViewSearchBuilder.java @@ -0,0 +1,145 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 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.client.view; + +import me.shedaniel.rei.api.client.registry.display.DisplayCategory; +import me.shedaniel.rei.api.client.view.ViewSearchBuilder; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.impl.display.DisplaySpec; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public final class LegacyWrapperViewSearchBuilder extends AbstractViewSearchBuilder { + private final Map<DisplayCategory<?>, List<DisplaySpec>> map; + @Nullable + private EntryStack<?> inputNotice; + @Nullable + private EntryStack<?> outputNotice; + @Nullable + private CategoryIdentifier<?> preferredOpenedCategory = null; + + public LegacyWrapperViewSearchBuilder(Map<DisplayCategory<?>, List<DisplaySpec>> map) { + this.map = map; + } + + @Override + public ViewSearchBuilder addCategory(CategoryIdentifier<?> category) { + return this; + } + + @Override + public ViewSearchBuilder addCategories(Collection<CategoryIdentifier<?>> categories) { + return this; + } + + @Override + public Set<CategoryIdentifier<?>> getCategories() { + return Collections.emptySet(); + } + + @Override + public ViewSearchBuilder filterCategory(CategoryIdentifier<?> category) { + return this; + } + + @Override + public ViewSearchBuilder filterCategories(Collection<CategoryIdentifier<?>> categories) { + return this; + } + + @Override + public Set<CategoryIdentifier<?>> getFilteringCategories() { + return Collections.emptySet(); + } + + @Override + public <T> ViewSearchBuilder addRecipesFor(EntryStack<T> stack) { + return this; + } + + @Override + public List<EntryStack<?>> getRecipesFor() { + return inputNotice == null ? Collections.emptyList() : Collections.singletonList(outputNotice); + } + + @Override + public <T> ViewSearchBuilder addUsagesFor(EntryStack<T> stack) { + return this; + } + + @Override + public List<EntryStack<?>> getUsagesFor() { + return inputNotice == null ? Collections.emptyList() : Collections.singletonList(inputNotice); + } + + @Override + public ViewSearchBuilder setPreferredOpenedCategory(@Nullable CategoryIdentifier<?> category) { + this.preferredOpenedCategory = category; + return this; + } + + @Override + @Nullable + public CategoryIdentifier<?> getPreferredOpenedCategory() { + return this.preferredOpenedCategory; + } + + public <T> LegacyWrapperViewSearchBuilder addInputNotice(@Nullable EntryStack<T> stack) { + this.inputNotice = stack; + return this; + } + + public <T> LegacyWrapperViewSearchBuilder addOutputNotice(@Nullable EntryStack<T> stack) { + this.outputNotice = stack; + return this; + } + + @Override + public Map<DisplayCategory<?>, List<DisplaySpec>> buildMapInternal() { + fillPreferredOpenedCategory(); + return this.map; + } + + @Override + public boolean isMergingDisplays() { + return true; + } + + @Override + public ViewSearchBuilder mergingDisplays(boolean mergingDisplays) { + return this; + } + + @Override + public boolean isProcessingVisibilityHandlers() { + return false; + } + + @Override + public ViewSearchBuilder processingVisibilityHandlers(boolean processingVisibilityHandlers) { + return this; + } +} diff --git a/runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/ViewSearchBuilderImpl.java b/runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/ViewSearchBuilderImpl.java new file mode 100644 index 000000000..35ed31b39 --- /dev/null +++ b/runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/ViewSearchBuilderImpl.java @@ -0,0 +1,145 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 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.client.view; + +import com.google.common.base.Suppliers; +import me.shedaniel.rei.api.client.registry.display.DisplayCategory; +import me.shedaniel.rei.api.client.view.ViewSearchBuilder; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.impl.client.view.AbstractViewSearchBuilder; +import me.shedaniel.rei.impl.client.view.ViewsImpl; +import me.shedaniel.rei.impl.display.DisplaySpec; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Supplier; + +public final class ViewSearchBuilderImpl extends AbstractViewSearchBuilder { + private final Set<CategoryIdentifier<?>> filteringCategories = new HashSet<>(); + private final Set<CategoryIdentifier<?>> categories = new HashSet<>(); + private final List<EntryStack<?>> recipesFor = new ArrayList<>(); + private final List<EntryStack<?>> usagesFor = new ArrayList<>(); + @Nullable + private CategoryIdentifier<?> preferredOpenedCategory = null; + private boolean mergeDisplays = true; + private boolean processVisibilityHandlers = true; + private final Supplier<Map<DisplayCategory<?>, List<DisplaySpec>>> map = Suppliers.memoize(() -> ViewsImpl.buildMapFor(this)); + + @Override + public ViewSearchBuilder addCategory(CategoryIdentifier<?> category) { + this.categories.add(category); + return this; + } + + @Override + public ViewSearchBuilder addCategories(Collection<CategoryIdentifier<?>> categories) { + this.categories.addAll(categories); + return this; + } + + @Override + public Set<CategoryIdentifier<?>> getCategories() { + return categories; + } + + @Override + public <T> ViewSearchBuilder addRecipesFor(EntryStack<T> stack) { + this.recipesFor.add(stack); + return this; + } + + @Override + public List<EntryStack<?>> getRecipesFor() { + return recipesFor; + } + + @Override + public <T> ViewSearchBuilder addUsagesFor(EntryStack<T> stack) { + this.usagesFor.add(stack); + return this; + } + + @Override + public List<EntryStack<?>> getUsagesFor() { + return usagesFor; + } + + @Override + public ViewSearchBuilder setPreferredOpenedCategory(@Nullable CategoryIdentifier<?> category) { + this.preferredOpenedCategory = category; + return this; + } + + @Override + @Nullable + public CategoryIdentifier<?> getPreferredOpenedCategory() { + return this.preferredOpenedCategory; + } + + @Override + public ViewSearchBuilder filterCategory(CategoryIdentifier<?> category) { + this.filteringCategories.add(category); + return this; + } + + @Override + public ViewSearchBuilder filterCategories(Collection<CategoryIdentifier<?>> categories) { + this.filteringCategories.addAll(categories); + return this; + } + + @Override + public Set<CategoryIdentifier<?>> getFilteringCategories() { + return filteringCategories; + } + + @Override + public Map<DisplayCategory<?>, List<DisplaySpec>> buildMapInternal() { + fillPreferredOpenedCategory(); + return this.map.get(); + } + + @Override + public boolean isMergingDisplays() { + return mergeDisplays; + } + + @Override + public ViewSearchBuilder mergingDisplays(boolean mergingDisplays) { + this.mergeDisplays = mergingDisplays; + return this; + } + + @Override + public boolean isProcessingVisibilityHandlers() { + return processVisibilityHandlers; + } + + @Override + public ViewSearchBuilder processingVisibilityHandlers(boolean processingVisibilityHandlers) { + this.processVisibilityHandlers = processingVisibilityHandlers; + return this; + } +} diff --git a/runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java b/runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java new file mode 100644 index 000000000..c0e6381e9 --- /dev/null +++ b/runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java @@ -0,0 +1,516 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 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.client.view; + +import com.google.common.base.Stopwatch; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import it.unimi.dsi.fastutil.longs.Long2LongMap; +import it.unimi.dsi.fastutil.longs.Long2LongMaps; +import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; +import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.client.registry.category.CategoryRegistry; +import me.shedaniel.rei.api.client.registry.display.DisplayCategory; +import me.shedaniel.rei.api.client.registry.display.DisplayRegistry; +import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator; +import me.shedaniel.rei.api.client.view.ViewSearchBuilder; +import me.shedaniel.rei.api.client.view.Views; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.display.Display; +import me.shedaniel.rei.api.common.display.DisplayMerger; +import me.shedaniel.rei.api.common.entry.EntryIngredient; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.entry.comparison.ComparisonContext; +import me.shedaniel.rei.api.common.entry.type.EntryDefinition; +import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes; +import me.shedaniel.rei.api.common.plugins.PluginManager; +import me.shedaniel.rei.api.common.transfer.info.MenuInfo; +import me.shedaniel.rei.api.common.transfer.info.MenuInfoRegistry; +import me.shedaniel.rei.api.common.transfer.info.MenuSerializationContext; +import me.shedaniel.rei.api.common.transfer.info.stack.SlotAccessor; +import me.shedaniel.rei.api.common.util.CollectionUtils; +import me.shedaniel.rei.api.common.util.EntryIngredients; +import me.shedaniel.rei.api.common.util.EntryStacks; +import me.shedaniel.rei.impl.client.view.craftable.CraftableFilter; +import me.shedaniel.rei.impl.client.gui.widget.AutoCraftingEvaluator; +import me.shedaniel.rei.impl.client.util.CrashReportUtils; +import me.shedaniel.rei.impl.common.InternalLogger; +import me.shedaniel.rei.impl.display.DisplaySpec; +import net.minecraft.CrashReport; +import net.minecraft.ReportedException; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +@ApiStatus.Internal +public class ViewsImpl implements Views { + private static final ThreadLocal<ViewSearchBuilder> BUILDER = new ThreadLocal<>(); + + @Nullable + @Override + public ViewSearchBuilder getContext() { + return BUILDER.get(); + } + + public static Map<DisplayCategory<?>, List<DisplaySpec>> buildMapFor(ViewSearchBuilder builder) { + BUILDER.set(builder); + + try { + return _buildMapFor(builder); + } finally { + BUILDER.remove(); + } + } + + private static Map<DisplayCategory<?>, List<DisplaySpec>> _buildMapFor(ViewSearchBuilder builder) { + if (PluginManager.areAnyReloading()) { + InternalLogger.getInstance().info("Cancelled Views buildMap since plugins have not finished reloading."); + return Maps.newLinkedHashMap(); + } + + Stopwatch stopwatch = Stopwatch.createStarted(); + boolean processingVisibilityHandlers = builder.isProcessingVisibilityHandlers(); + Set<CategoryIdentifier<?>> categories = builder.getCategories(); + Set<CategoryIdentifier<?>> filteringCategories = builder.getFilteringCategories(); + List<EntryStack<?>> recipesForStacks = builder.getRecipesFor(); + List<EntryStack<?>> usagesForStacks = builder.getUsagesFor(); + Function<EntryStack<?>, Collection<EntryStack<?>>> wildcardFunction = stack -> { + EntryStack<?> wildcard = stack.wildcard(); + if (EntryStacks.equalsFuzzy(wildcard, stack)) return Collections.emptyList(); + return Collections.singletonList(wildcard); + }; + List<EntryStack<?>> recipesForStacksWildcard = CollectionUtils.flatMap(recipesForStacks, wildcardFunction); + List<EntryStack<?>> usagesForStacksWildcard = CollectionUtils.flatMap(usagesForStacks, wildcardFunction); + DisplayRegistry displayRegistry = DisplayRegistry.getInstance(); + + Map<DisplayCategory<?>, List<Display>> result = Maps.newLinkedHashMap(); + for (CategoryRegistry.CategoryConfiguration<?> categoryConfiguration : CategoryRegistry.getInstance()) { + DisplayCategory<?> category = categoryConfiguration.getCategory(); + if (processingVisibilityHandlers && CategoryRegistry.getInstance().isCategoryInvisible(category)) continue; + CategoryIdentifier<?> categoryId = categoryConfiguration.getCategoryIdentifier(); + if (!filteringCategories.isEmpty() && !filteringCategories.contains(categoryId)) continue; + List<Display> allRecipesFromCategory = displayRegistry.get((CategoryIdentifier<Display>) categoryId); + + Set<Display> set = Sets.newLinkedHashSet(); + if (categories.contains(categoryId)) { + for (Display display : allRecipesFromCategory) { + if (!processingVisibilityHandlers || displayRegistry.isDisplayVisible(display)) { + set.add(display); + } + } + if (!set.isEmpty()) { + CollectionUtils.getOrPutEmptyList(result, category).addAll(set); + } + continue; + } + for (Display display : allRecipesFromCategory) { + if (processingVisibilityHandlers && !displayRegistry.isDisplayVisible(display)) continue; + if (!recipesForStacks.isEmpty()) { + if (isRecipesFor(recipesForStacks, display)) { + set.add(display); + continue; + } + } + if (!usagesForStacks.isEmpty()) { + if (isUsagesFor(usagesForStacks, display)) { + set.add(display); + continue; + } + } + } + if (set.isEmpty() && (!recipesForStacksWildcard.isEmpty() || !usagesForStacksWildcard.isEmpty())) { + for (Display display : allRecipesFromCategory) { + if (processingVisibilityHandlers && !displayRegistry.isDisplayVisible(display)) continue; + if (!recipesForStacksWildcard.isEmpty()) { + if (isRecipesFor(recipesForStacksWildcard, display)) { + set.add(display); + continue; + } + } + if (!usagesForStacksWildcard.isEmpty()) { + if (isUsagesFor(usagesForStacksWildcard, display)) { + set.add(display); + continue; + } + } + } + } + for (EntryStack<?> usagesFor : Iterables.concat(usagesForStacks, usagesForStacksWildcard)) { + if (isStackWorkStationOfCategory(categoryConfiguration, usagesFor)) { + if (processingVisibilityHandlers) { + set.addAll(CollectionUtils.filterToSet(allRecipesFromCategory, displayRegistry::isDisplayVisible)); + } else { + set.addAll(allRecipesFromCategory); + } + break; + } + } + if (!set.isEmpty()) { + CollectionUtils.getOrPutEmptyList(result, category).addAll(set); + } + } + + int generatorsCount = 0; + + for (Map.Entry<CategoryIdentifier<?>, List<DynamicDisplayGenerator<?>>> entry : displayRegistry.getCategoryDisplayGenerators().entrySet()) { + CategoryIdentifier<?> categoryId = entry.getKey(); + DisplayCategory<?> category = CategoryRegistry.getInstance().get(categoryId).getCategory(); + if (processingVisibilityHandlers && CategoryRegistry.getInstance().isCategoryInvisible(category)) continue; + if (!filteringCategories.isEmpty() && !filteringCategories.contains(categoryId)) continue; + Set<Display> set = new LinkedHashSet<>(); + generatorsCount += entry.getValue().size(); + + for (DynamicDisplayGenerator<Display> generator : (List<DynamicDisplayGenerator<Display>>) (List<? extends DynamicDisplayGenerator<?>>) entry.getValue()) { + generateLiveDisplays(displayRegistry, wrapForError(generator), builder, set::add); + } + + if (!set.isEmpty()) { + CollectionUtils.getOrPutEmptyList(result, category).addAll(set); + } + } + + Consumer<Display> displayConsumer = display -> { + CategoryIdentifier<?> categoryIdentifier = display.getCategoryIdentifier(); + if (!filteringCategories.isEmpty() && !filteringCategories.contains(categoryIdentifier)) return; + CollectionUtils.getOrPutEmptyList(result, CategoryRegistry.getInstance().get(categoryIdentifier).getCategory()).add(display); + }; + for (DynamicDisplayGenerator<Display> generator : (List<DynamicDisplayGenerator<Display>>) (List<? extends DynamicDisplayGenerator<?>>) displayRegistry.getGlobalDisplayGenerators()) { + generatorsCount++; + generateLiveDisplays(displayRegistry, wrapForError(generator), builder, displayConsumer); + } + + Map<DisplayCategory<?>, List<DisplaySpec>> resultSpeced = (Map<DisplayCategory<?>, List<DisplaySpec>>) (Map) new LinkedHashMap<>(result); + // optimize displays + if (builder.isMergingDisplays() && ConfigObject.getInstance().doMergeDisplayUnderOne()) { + for (Map.Entry<DisplayCategory<?>, List<Display>> entry : result.entrySet()) { + DisplayMerger<Display> merger = (DisplayMerger<Display>) entry.getKey().getDisplayMerger(); + + if (merger != null) { + class Wrapped implements DisplaySpec { + private Display display; + private List<ResourceLocation> ids = null; + + public Wrapped(Display display) { + this.display = display; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Wrapped)) return false; + Wrapped wrapped = (Wrapped) o; + return merger.canMerge(display, wrapped.display); + } + + @Override + public int hashCode() { + return merger.hashOf(display); + } + + @Override + public Display provideInternalDisplay() { + return display; + } + + @Override + public Collection<ResourceLocation> provideInternalDisplayIds() { + if (ids == null) { + ids = new ArrayList<>(); + Optional<ResourceLocation> location = display.getDisplayLocation(); + if (location.isPresent()) { + ids.add(location.get()); + } + } + return ids; + } + + public void add(Display display) { + Optional<ResourceLocation> location = display.getDisplayLocation(); + if (location.isPresent()) { + provideInternalDisplayIds().add(location.get()); + } + } + } + Map<Wrapped, Wrapped> wrappedSet = new LinkedHashMap<>(); + List<Wrapped> wrappeds = new ArrayList<>(); + + for (Display display : sortAutoCrafting(entry.getValue())) { + Wrapped wrapped = new Wrapped(display); + if (wrappedSet.containsKey(wrapped)) { + wrappedSet.get(wrapped).add(display); + } else { + wrappedSet.put(wrapped, wrapped); + wrappeds.add(wrapped); + } + } + + resultSpeced.put(entry.getKey(), (List<DisplaySpec>) (List) wrappeds); + } + } + } + + 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(), categories.size(), recipesForStacks.size(), usagesForStacks.size(), generatorsCount); + if (ConfigObject.getInstance().doDebugSearchTimeRequired()) { + InternalLogger.getInstance().info(message); + } else { + InternalLogger.getInstance().trace(message); + } + return resultSpeced; + } + + public static boolean isRecipesFor(List<EntryStack<?>> stacks, Display display) { + return checkUsages(stacks, display, display.getOutputEntries()); + } + + public static boolean isUsagesFor(List<EntryStack<?>> stacks, Display display) { + return checkUsages(stacks, display, display.getInputEntries()); + } + + private static boolean checkUsages(List<EntryStack<?>> stacks, Display display, List<EntryIngredient> entries) { + for (EntryIngredient results : entries) { + for (EntryStack<?> otherEntry : results) { + for (EntryStack<?> recipesFor : stacks) { + if (EntryStacks.equalsFuzzy(otherEntry, recipesFor)) { + return true; + } + } + } + } + + return false; + } + + private static Iterable<Display> sortAutoCrafting(List<Display> displays) { + Set<Display> successfulDisplays = new LinkedHashSet<>(); + Set<Display> applicableDisplays = new LinkedHashSet<>(); + + for (Display display : displays) { + AutoCraftingEvaluator.AutoCraftingResult result = AutoCraftingEvaluator.evaluateAutoCrafting(false, false, display, null); + + if (result.successful) { + successfulDisplays.add(display); + } else if (result.hasApplicable) { + applicableDisplays.add(display); + } + } + + return Iterables.concat(successfulDisplays, applicableDisplays, + Iterables.filter(displays, display -> !successfulDisplays.contains(display) && !applicableDisplays.contains(display))); + } + + private static <T extends Display> void generateLiveDisplays(DisplayRegistry displayRegistry, DynamicDisplayGenerator<T> generator, ViewSearchBuilder builder, Consumer<T> displayConsumer) { + boolean processingVisibilityHandlers = builder.isProcessingVisibilityHandlers(); + + for (EntryStack<?> stack : builder.getRecipesFor()) { + Optional<List<T>> recipeForDisplays = generator.getRecipeFor(stack); + if (recipeForDisplays.isPresent()) { + for (T display : recipeForDisplays.get()) { + if (!processingVisibilityHandlers || displayRegistry.isDisplayVisible(display)) { + displayConsumer.accept(display); + } + } + } + } + + for (EntryStack<?> stack : builder.getUsagesFor()) { + Optional<List<T>> usageForDisplays = generator.getUsageFor(stack); + if (usageForDisplays.isPresent()) { + for (T display : usageForDisplays.get()) { + if (!processingVisibilityHandlers || displayRegistry.isDisplayVisible(display)) { + displayConsumer.accept(display); + } + } + } + } + + Optional<List<T>> displaysGenerated = generator.generate(builder); + if (displaysGenerated.isPresent()) { + for (T display : displaysGenerated.get()) { + if (!processingVisibilityHandlers || displayRegistry.isDisplayVisible(display)) { + displayConsumer.accept(display); + } + } + } + } + + private static <T extends Display> DynamicDisplayGenerator<T> wrapForError(DynamicDisplayGenerator<T> generator) { + return new DynamicDisplayGenerator<>() { + @Override + public Optional<List<T>> getRecipeFor(EntryStack<?> entry) { + try { + return generator.getRecipeFor(entry); + } catch (Throwable throwable) { + CrashReport report = CrashReportUtils.essential(throwable, "Error while generating recipes for an entry stack"); + CrashReportUtils.renderer(report, entry); + InternalLogger.getInstance().throwException(new ReportedException(report)); + return Optional.empty(); + } + } + + @Override + public Optional<List<T>> getUsageFor(EntryStack<?> entry) { + try { + return generator.getUsageFor(entry); + } catch (Throwable throwable) { + CrashReport report = CrashReportUtils.essential(throwable, "Error while generating usages for an entry stack"); + CrashReportUtils.renderer(report, entry); + InternalLogger.getInstance().throwException(new ReportedException(report)); + return Optional.empty(); + } + } + + @Override + public Optional<List<T>> generate(ViewSearchBuilder builder) { + try { + return generator.generate(builder); + } catch (Throwable throwable) { + CrashReport report = CrashReportUtils.essential(throwable, "Error while generating |
