diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java index b815882c3..cd44c7434 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java @@ -26,10 +26,10 @@ 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 it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import me.shedaniel.rei.api.client.REIRuntime; import me.shedaniel.rei.api.client.config.ConfigObject; import me.shedaniel.rei.api.client.registry.category.CategoryRegistry; @@ -108,7 +108,7 @@ private static Map, List> _buildMapFor(ViewSearc Stopwatch stopwatch = Stopwatch.createStarted(); boolean processingVisibilityHandlers = builder.isProcessingVisibilityHandlers(); - Set> categories = builder.getCategories(); + Set> categories = new HashSet<>(builder.getCategories()); Set> filteringCategories = builder.getFilteringCategories(); List> recipesForStacks = builder.getRecipesFor(); List> usagesForStacks = builder.getUsagesFor(); @@ -122,27 +122,20 @@ private static Map, List> _buildMapFor(ViewSearc DisplayRegistry displayRegistry = DisplayRegistry.getInstance(); DisplaysHolder displaysHolder = ((DisplayRegistryImpl) displayRegistry).displaysHolder(); - Map, List> 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 allRecipesFromCategory = displayRegistry.get((CategoryIdentifier) categoryId); - - Set set = Sets.newLinkedHashSet(); - if (categories.contains(categoryId)) { - for (Display display : allRecipesFromCategory) { + Map, Set> result = Maps.newLinkedHashMap(); + forCategories(processingVisibilityHandlers, filteringCategories, displayRegistry, result, (configuration, categoryId, displays, set) -> { + if (categories.contains(categoryId)) { // If the category is in the search, add all displays + for (Display display : displays) { if (!processingVisibilityHandlers || displayRegistry.isDisplayVisible(display)) { set.add(display); } } if (!set.isEmpty()) { - CollectionUtils.getOrPutEmptyList(result, category).addAll(set); + getOrPutEmptyLinkedSet(result, configuration.getCategory()).addAll(set); } - continue; + return; } - for (Display display : allRecipesFromCategory) { + for (Display display : displays) { if (processingVisibilityHandlers && !displayRegistry.isDisplayVisible(display)) continue; if (!recipesForStacks.isEmpty()) { if (isRecipesFor(displaysHolder, recipesForStacks, display)) { @@ -153,42 +146,12 @@ private static Map, List> _buildMapFor(ViewSearc if (!usagesForStacks.isEmpty()) { if (isUsagesFor(displaysHolder, 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(displaysHolder, recipesForStacksWildcard, display)) { - set.add(display); - continue; - } - } - if (!usagesForStacksWildcard.isEmpty()) { - if (isUsagesFor(displaysHolder, 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); - } - } + }); + // Generate live displays per category int generatorsCount = 0; for (Map.Entry, List>> entry : displayRegistry.getCategoryDisplayGenerators().entrySet()) { @@ -204,90 +167,65 @@ private static Map, List> _buildMapFor(ViewSearc } if (!set.isEmpty()) { - CollectionUtils.getOrPutEmptyList(result, category).addAll(set); + getOrPutEmptyLinkedSet(result, category).addAll(set); } } Consumer displayConsumer = display -> { CategoryIdentifier categoryIdentifier = display.getCategoryIdentifier(); if (!filteringCategories.isEmpty() && !filteringCategories.contains(categoryIdentifier)) return; - CollectionUtils.getOrPutEmptyList(result, CategoryRegistry.getInstance().get(categoryIdentifier).getCategory()).add(display); + getOrPutEmptyLinkedSet(result, CategoryRegistry.getInstance().get(categoryIdentifier).getCategory()).add(display); }; for (DynamicDisplayGenerator generator : (List>) (List>) displayRegistry.getGlobalDisplayGenerators()) { generatorsCount++; generateLiveDisplays(displayRegistry, wrapForError(generator), builder, displayConsumer); } - Map, List> resultSpeced = (Map, List>) (Map) new LinkedHashMap<>(result); - // optimize displays - if (builder.isMergingDisplays() && ConfigObject.getInstance().doMergeDisplayUnderOne()) { - for (Map.Entry, List> entry : result.entrySet()) { - DisplayMerger merger = (DisplayMerger) entry.getKey().getDisplayMerger(); - - if (merger != null) { - class Wrapped implements DisplaySpec { - private final Display display; - private List ids = null; - private final int hash; - - public Wrapped(Display display) { - this.display = display; - this.hash = merger.hashOf(display); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Wrapped)) return false; - Wrapped wrapped = (Wrapped) o; - return hash == wrapped.hash && merger.canMerge(display, wrapped.display); - } - - @Override - public int hashCode() { - return hash; - } - - @Override - public Display provideInternalDisplay() { - return display; - } - - @Override - public Collection provideInternalDisplayIds() { - if (ids == null) { - ids = new ArrayList<>(); - Optional location = display.getDisplayLocation(); - if (location.isPresent()) { - ids.add(location.get()); - } - } - return ids; - } - - public void add(Display display) { - Optional location = display.getDisplayLocation(); - if (location.isPresent()) { - provideInternalDisplayIds().add(location.get()); - } + if (CollectionUtils.allMatch(result.values(), Set::isEmpty) && (!recipesForStacksWildcard.isEmpty() || !usagesForStacksWildcard.isEmpty())) { + // Run wildcard search because no displays were found + forCategories(processingVisibilityHandlers, filteringCategories, displayRegistry, result, (configuration, categoryId, displays, set) -> { + if (categories.contains(categoryId)) return; + for (Display display : displays) { + if (processingVisibilityHandlers && !displayRegistry.isDisplayVisible(display)) continue; + if (!recipesForStacksWildcard.isEmpty()) { + if (isRecipesFor(displaysHolder, recipesForStacksWildcard, display)) { + set.add(display); + continue; } } - Map wrappedSet = new LinkedHashMap<>(); - List 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); + if (!usagesForStacksWildcard.isEmpty()) { + if (isUsagesFor(displaysHolder, usagesForStacksWildcard, display)) { + set.add(display); } } - - resultSpeced.put(entry.getKey(), (List) (List) wrappeds); + } + }); + } + + forCategories(processingVisibilityHandlers, filteringCategories, displayRegistry, result, (configuration, categoryId, displays, set) -> { + if (categories.contains(categoryId)) return; + for (EntryStack usagesFor : Iterables.concat(usagesForStacks, usagesForStacksWildcard)) { + if (isStackWorkStationOfCategory(configuration, usagesFor)) { + categories.add(categoryId); + if (processingVisibilityHandlers) { + set.addAll(CollectionUtils.filterToSet(displays, displayRegistry::isDisplayVisible)); + } else { + set.addAll(displays); + } + break; } } + }); + + Map, List> resultSpec = (Map, List>) (Map) new LinkedHashMap<>(); + for (CategoryRegistry.CategoryConfiguration configuration : CategoryRegistry.getInstance()) { + Set displays = result.get(configuration.getCategory()); + if (displays == null) continue; + resultSpec.put(configuration.getCategory(), new ArrayList<>(displays)); + } + // optimize displays + if (builder.isMergingDisplays() && ConfigObject.getInstance().doMergeDisplayUnderOne()) { + mergeAndOptimize(result, resultSpec); } String message = String.format("Built Recipe View in %s for %d categories, %d recipes for, %d usages for and %d live recipe generators.", @@ -297,7 +235,30 @@ public void add(Display display) { } else { InternalLogger.getInstance().trace(message); } - return resultSpeced; + return resultSpec; + } + + private static void forCategories(boolean processingVisibilityHandlers, Set> filteringCategories, DisplayRegistry displayRegistry, Map, Set> result, QuadConsumer, CategoryIdentifier, List, Set> displayConsumer) { + for (CategoryRegistry.CategoryConfiguration configuration : CategoryRegistry.getInstance()) { + if (processingVisibilityHandlers && CategoryRegistry.getInstance().isCategoryInvisible(configuration.getCategory())) continue; + CategoryIdentifier categoryId = configuration.getCategoryIdentifier(); + if (!filteringCategories.isEmpty() && !filteringCategories.contains(categoryId)) continue; + List allRecipesFromCategory = displayRegistry.get((CategoryIdentifier) categoryId); + Set set = new LinkedHashSet<>(); + displayConsumer.accept(configuration, categoryId, allRecipesFromCategory, set); + if (!set.isEmpty()) { + getOrPutEmptyLinkedSet(result, configuration.getCategory()).addAll(set); + } + } + } + + public static Set getOrPutEmptyLinkedSet(Map> map, A key) { + Set b = map.get(key); + if (b != null) { + return b; + } + map.put(key, new ReferenceOpenHashSet<>()); + return map.get(key); } public static boolean isRecipesFor(@Nullable DisplaysHolder displaysHolder, List> stacks, Display display) { @@ -334,7 +295,7 @@ private static boolean checkUsages(List> stacks, Display display, return false; } - private static Iterable sortAutoCrafting(List displays) { + private static Iterable sortAutoCrafting(Iterable displays) { Set successfulDisplays = new LinkedHashSet<>(); Set applicableDisplays = new LinkedHashSet<>(); @@ -555,6 +516,84 @@ private static boolean isStackWorkStationOfCategory(CategoryRegistry.Categor @Override public void startReload() { + + } + + private static void mergeAndOptimize(Map, Set> displays, Map, List> resultSpec) { + for (Map.Entry, Set> entry : displays.entrySet()) { + DisplayMerger merger = (DisplayMerger) entry.getKey().getDisplayMerger(); + + if (merger != null) { + Map wrappedSet = new LinkedHashMap<>(); + List specs = new ArrayList<>(); + + for (Display display : sortAutoCrafting(entry.getValue())) { + WrappedDisplaySpec wrapped = new WrappedDisplaySpec(merger, display); + if (wrappedSet.containsKey(wrapped)) { + wrappedSet.get(wrapped).add(display); + } else { + wrappedSet.put(wrapped, wrapped); + specs.add(wrapped); + } + } + + resultSpec.put(entry.getKey(), (List) (List) specs); + } + } + } + + private static class WrappedDisplaySpec implements DisplaySpec { + private final DisplayMerger merger; + private final Display display; + private List ids = null; + private final int hash; + + public WrappedDisplaySpec(DisplayMerger merger, Display display) { + this.merger = merger; + this.display = display; + this.hash = merger.hashOf(display); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof WrappedDisplaySpec)) return false; + WrappedDisplaySpec wrapped = (WrappedDisplaySpec) o; + return hash == wrapped.hash && merger.canMerge(display, wrapped.display); + } + + @Override + public int hashCode() { + return hash; + } + + @Override + public Display provideInternalDisplay() { + return display; + } + + @Override + public Collection provideInternalDisplayIds() { + if (ids == null) { + ids = new ArrayList<>(); + Optional location = display.getDisplayLocation(); + if (location.isPresent()) { + ids.add(location.get()); + } + } + return ids; + } + + public void add(Display display) { + Optional location = display.getDisplayLocation(); + if (location.isPresent()) { + provideInternalDisplayIds().add(location.get()); + } + } + } + @FunctionalInterface + private interface QuadConsumer { + void accept(P1 p1, P2 p2, P3 p3, P4 p4); } }