aboutsummaryrefslogtreecommitdiff
path: root/runtime-engine/views/src/main/java/me
diff options
context:
space:
mode:
authorshedaniel <daniel@shedaniel.me>2022-08-05 01:30:08 +0800
committershedaniel <daniel@shedaniel.me>2022-08-26 10:53:55 +0900
commit8c13c015031a0de865d2e767cd8e879754f803e2 (patch)
tree2bb6fa6578b63d1c216b863a6c4206295c044b36 /runtime-engine/views/src/main/java/me
parent8f5d3ef632f3d1a733c98ce5607c9fd5a0fd7567 (diff)
downloadRoughlyEnoughItems-8c13c015031a0de865d2e767cd8e879754f803e2.tar.gz
RoughlyEnoughItems-8c13c015031a0de865d2e767cd8e879754f803e2.tar.bz2
RoughlyEnoughItems-8c13c015031a0de865d2e767cd8e879754f803e2.zip
More work
Diffstat (limited to 'runtime-engine/views/src/main/java/me')
-rw-r--r--runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/AbstractViewSearchBuilder.java50
-rw-r--r--runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/LegacyWrapperViewSearchBuilder.java145
-rw-r--r--runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/ViewSearchBuilderImpl.java145
-rw-r--r--runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java516
-rw-r--r--runtime-engine/views/src/main/java/me/shedaniel/rei/impl/client/view/craftable/CraftableFilter.java131
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);</