From eb6ef8b13fe5842f51a0cd7fa8a646d0fb63ed72 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Thu, 18 Apr 2024 14:54:13 +0900 Subject: Add option for rainbow --- .../java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java | 1 + .../rei/impl/client/gui/config/components/ConfigOptionWidget.java | 5 ++++- .../rei/impl/client/gui/config/options/AllREIConfigCategories.java | 3 ++- .../rei/impl/client/gui/config/options/AllREIConfigGroups.java | 2 ++ .../rei/impl/client/gui/config/options/AllREIConfigOptions.java | 2 ++ .../impl/client/gui/widget/entrylist/PaginatedEntryListWidget.java | 3 ++- runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json | 2 ++ 7 files changed, 15 insertions(+), 3 deletions(-) diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java index b08ad02ef..7840fa6cd 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java @@ -625,6 +625,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { public boolean scrollingEntryListWidget = false; @Comment("Declares whether entry panel should be invisible when not searching") public boolean hideEntryPanelIfIdle = false; + public boolean rainbow = false; public static class Layout { @Comment("Declares the position of the search field.") diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionWidget.java index f2a47a332..668583711 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/components/ConfigOptionWidget.java @@ -36,6 +36,7 @@ import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds; import me.shedaniel.rei.api.client.gui.widgets.Widgets; import me.shedaniel.rei.api.client.util.MatrixUtils; import me.shedaniel.rei.impl.client.gui.config.ConfigAccess; +import me.shedaniel.rei.impl.client.gui.config.options.AllREIConfigOptions; import me.shedaniel.rei.impl.client.gui.config.options.CompositeOption; import me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils; import me.shedaniel.rei.impl.client.gui.text.TextTransformations; @@ -48,6 +49,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import static me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils.literal; import static me.shedaniel.rei.impl.client.gui.config.options.ConfigUtils.translatable; @@ -59,7 +61,8 @@ public class ConfigOptionWidget { int[] height = {12}; Label fieldNameLabel; widgets.add(fieldNameLabel = Widgets.createLabel(new Point(0, 0), TextTransformations.highlightText(option.getName().copy(), option.getOptionNameHighlight(), style -> style.withColor(0xFFC0C0C0))) - .leftAligned()); + .leftAligned() + .rainbow(Objects.equals(option.getId(), AllREIConfigOptions.RAINBOW.getId()))); WidgetWithBounds optionValue = ConfigOptionValueWidget.create(access, option, width - 10 - fieldNameLabel.getBounds().width); widgets.add(Widgets.withTranslate(optionValue, () -> Matrix4f.createTranslateMatrix(width - optionValue.getBounds().width - optionValue.getBounds().x, 0, 0))); widgets.add(new WidgetWithBounds() { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigCategories.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigCategories.java index 9122bf1d4..40e94ae10 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigCategories.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigCategories.java @@ -40,7 +40,8 @@ public interface AllREIConfigCategories { OptionCategory APPEARANCE = make("appearance") .add(APPEARANCE_INTERFACE) - .add(APPEARANCE_TOOLTIPS); + .add(APPEARANCE_TOOLTIPS) + .add(APPEARANCE_ADVANCED); OptionCategory INPUT = make("input") .add(INPUT_KEYBINDS) /*.add(KEYBINDS_ADVANCED)*/; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigGroups.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigGroups.java index a661862a6..28b83e639 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigGroups.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigGroups.java @@ -39,6 +39,8 @@ public interface AllREIConfigGroups { OptionGroup APPEARANCE_TOOLTIPS = make("appearance.tooltips") .add(APPEND_MOD_NAMES) .add(APPEND_FAVORITES_HINT); + OptionGroup APPEARANCE_ADVANCED = make("appearance.advanced") + .add(RAINBOW); OptionGroup INPUT_KEYBINDS = make("input.keybinds") .add(RECIPE_KEYBIND) .add(USAGE_KEYBIND) diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java index 241bd41f1..30340bfba 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/config/options/AllREIConfigOptions.java @@ -69,6 +69,8 @@ public interface AllREIConfigOptions { CompositeOption RECIPE_LOOKUP_STYLE = make("appearance.recipe_lookup_style", i -> i.appearance.recipeScreenType, (i, v) -> i.appearance.recipeScreenType = v) .options(DisplayScreenType.ORIGINAL, DisplayScreenType.COMPOSITE) .defaultValue(() -> DisplayScreenType.ORIGINAL); + CompositeOption RAINBOW = make("appearance.rainbow", i -> i.appearance.rainbow, (i, v) -> i.appearance.rainbow = v) + .enabledDisabled(); CompositeOption APPEND_MOD_NAMES = make("appearance.append_mod_names", i -> i.advanced.tooltips.appendModNames, (i, v) -> i.advanced.tooltips.appendModNames = v) .enabledDisabled(); CompositeOption APPEND_FAVORITES_HINT = make("appearance.append_favorites_hint", i -> i.advanced.tooltips.displayFavoritesTooltip, (i, v) -> i.advanced.tooltips.displayFavoritesTooltip = v) diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/PaginatedEntryListWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/PaginatedEntryListWidget.java index 9e54d023c..ee44b082f 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/PaginatedEntryListWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/PaginatedEntryListWidget.java @@ -39,6 +39,7 @@ import me.shedaniel.rei.api.client.gui.widgets.Widgets; import me.shedaniel.rei.api.common.entry.EntryStack; import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.impl.client.ClientHelperImpl; +import me.shedaniel.rei.impl.client.config.ConfigManagerImpl; import me.shedaniel.rei.impl.client.gui.InternalTextures; import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; import me.shedaniel.rei.impl.client.gui.widget.BatchedEntryRendererManager; @@ -247,7 +248,7 @@ public class PaginatedEntryListWidget extends CollapsingEntryListWidget { }).tooltip(Component.translatable("text.rei.go_back_first_page"), Component.literal(" "), Component.translatable("text.rei.shift_click_to", Component.translatable("text.rei.choose_page")).withStyle(ChatFormatting.GRAY)).focusable(false).onRender((matrices, label) -> { label.setClickable(getTotalPages() > 1); label.setMessage(Component.literal(String.format("%s/%s", getPage() + 1, Math.max(getTotalPages(), 1)))); - }).rainbow(new Random().nextFloat() < 1.0E-4D || ClientHelperImpl.getInstance().isAprilFools.get())); + }).rainbow(new Random().nextFloat() < 1.0E-4D || ClientHelperImpl.getInstance().isAprilFools.get() || ConfigManagerImpl.getInstance().getConfig().appearance.rainbow)); } @Override diff --git a/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json b/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json index fa6f3dbd4..0146868cf 100755 --- a/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json +++ b/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json @@ -271,6 +271,8 @@ "config.rei.options.appearance.append_mod_names.desc": "Appends the containing namespace for entries. The appended line will be in italisised light blue.", "config.rei.options.appearance.append_favorites_hint": "Append Favorites Hint", "config.rei.options.appearance.append_favorites_hint.desc": "Shows a hint on how to favorite an entry, or a recipe.", + "config.rei.options.groups.appearance.advanced": "Advanced", + "config.rei.options.appearance.rainbow": "Rainbow o(ã€’īšã€’)o", "config.rei.options.groups.input.keybinds": "Keybinds", "config.rei.options.input.recipe": "View Recipes", "config.rei.options.input.usage": "View Usages", -- cgit From 7e6221c72d4a1383f682924b06bec397642ca9d5 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Thu, 18 Apr 2024 14:31:36 +0900 Subject: Fix #1611 --- .../rei/impl/client/gui/widget/EntryWidget.java | 2 +- .../impl/client/registry/display/DisplayCache.java | 88 +++++++++++ .../client/registry/display/DisplayCacheImpl.java | 172 +++++++++++++++++++++ .../registry/display/DisplayRegistryImpl.java | 12 +- .../client/registry/display/DisplaysHolder.java | 55 +------ .../registry/display/DisplaysHolderImpl.java | 144 +++++------------ .../shedaniel/rei/impl/client/view/ViewsImpl.java | 8 +- 7 files changed, 320 insertions(+), 161 deletions(-) create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCache.java create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCacheImpl.java diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java index 2c6b31874..58e745e34 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java @@ -329,7 +329,7 @@ public class EntryWidget extends Slot implements DraggableStackProviderWidget { Map, Boolean> filteringQuickCraftCategories = ConfigObject.getInstance().getFilteringQuickCraftCategories(); boolean shouldFilterDisplays = ConfigObject.getInstance().shouldFilterDisplays(); - for (Display display : displaysHolder.getAllDisplaysByOutputs(getEntries())) { + for (Display display : displaysHolder.cache().getAllDisplaysByOutputs(getEntries())) { CategoryIdentifier categoryIdentifier = display.getCategoryIdentifier(); Optional> configuration; if ((configuration = categoryRegistry.tryGet(categoryIdentifier)).isEmpty() diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCache.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCache.java new file mode 100644 index 000000000..b65aba97c --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCache.java @@ -0,0 +1,88 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 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.registry.display; + +import com.google.common.collect.Iterables; +import me.shedaniel.rei.api.common.display.Display; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.util.CollectionUtils; +import me.shedaniel.rei.impl.client.view.ViewsImpl; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +public interface DisplayCache { + boolean doesCache(); + + boolean isCached(Display display); + + void add(Display display); + + boolean remove(Display display); + + void endReload(); + + Set getDisplaysNotCached(); + + Set getDisplaysByInput(EntryStack stack); + + Set getDisplaysByOutput(EntryStack stack); + + default Iterable getAllDisplaysByInputs(List> stacks) { + if (stacks.isEmpty()) return List.of(); + Iterable inputCached = null; + if (doesCache()) { + for (EntryStack stack : stacks) { + Set set = getDisplaysByInput(stack); + inputCached = inputCached == null ? set : Iterables.concat(inputCached, set); + } + if (stacks.size() > 1) inputCached = CollectionUtils.distinctReferenceOf(inputCached); + } + Collection notCached = this.getDisplaysNotCached(); + if (notCached.isEmpty()) return inputCached == null ? List.of() : inputCached; + Iterable filteredNotCached = Iterables.filter(notCached, display -> + ViewsImpl.isUsagesFor(null, stacks, display)); + if (inputCached == null) return filteredNotCached; + return Iterables.concat(inputCached, filteredNotCached); + } + + default Iterable getAllDisplaysByOutputs(List> stacks) { + if (stacks.isEmpty()) return List.of(); + Iterable outputCached = null; + if (doesCache()) { + for (EntryStack stack : stacks) { + Set set = getDisplaysByOutput(stack); + outputCached = outputCached == null ? set : Iterables.concat(outputCached, set); + } + if (stacks.size() > 1) outputCached = CollectionUtils.distinctReferenceOf(outputCached); + } + Collection notCached = this.getDisplaysNotCached(); + if (notCached.isEmpty()) return outputCached == null ? List.of() : outputCached; + Iterable filteredNotCached = Iterables.filter(notCached, display -> + ViewsImpl.isRecipesFor(null, stacks, display)); + if (outputCached == null) return filteredNotCached; + return Iterables.concat(outputCached, filteredNotCached); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCacheImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCacheImpl.java new file mode 100644 index 000000000..80a73f82e --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayCacheImpl.java @@ -0,0 +1,172 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 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.registry.display; + +import com.google.common.base.Stopwatch; +import com.google.common.collect.Multimaps; +import com.google.common.collect.SetMultimap; +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import me.shedaniel.rei.api.client.config.ConfigObject; +import me.shedaniel.rei.api.common.display.Display; +import me.shedaniel.rei.api.common.entry.EntryIngredient; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.util.EntryStacks; +import me.shedaniel.rei.impl.common.InternalLogger; + +import java.util.Collections; +import java.util.Set; + +public class DisplayCacheImpl implements DisplayCache { + private final boolean cache; + private Set displaysCached = new ReferenceOpenHashSet<>(); + private Set displaysNotCached = Collections.synchronizedSet(new ReferenceOpenHashSet<>()); + private SetMultimap, Display> displaysByInput; + private SetMultimap, Display> displaysByOutput; + private boolean preprocessed = false; + + public DisplayCacheImpl(boolean init) { + this.cache = init && ConfigObject.getInstance().doesCacheDisplayLookup(); + this.displaysByInput = createSetMultimap(); + this.displaysByOutput = createSetMultimap(); + } + + @Override + public boolean doesCache() { + return this.cache; + } + + @Override + public boolean isCached(Display display) { + return this.cache && this.displaysCached.contains(display); + } + + @Override + public void add(Display display) { + if (this.cache) { + if (!preprocessed) { + this.displaysNotCached.add(display); + } else { + this.process(display); + this.displaysCached.add(display); + } + } else { + this.displaysNotCached.add(display); + } + } + + @Override + public boolean remove(Display display) { + if (this.cache) { + if (!preprocessed) { + return this.displaysNotCached.remove(display); + } else { + boolean removed = this.displaysCached.remove(display); + if (removed) { + for (EntryIngredient input : display.getInputEntries()) { + for (EntryStack stack : input) { + this.displaysByInput.remove(stack, display); + } + } + for (EntryIngredient output : display.getOutputEntries()) { + for (EntryStack stack : output) { + this.displaysByOutput.remove(stack, display); + } + } + } + return removed; + } + } else { + return this.displaysNotCached.remove(display); + } + } + + @Override + public void endReload() { + if (this.cache) { + if (preprocessed) { + InternalLogger.getInstance().error("DisplayCache#endReload called after preprocessed!"); + } + + InternalLogger.getInstance().debug("Processing %d displays for optimal lookup performance...", this.displaysNotCached.size()); + Stopwatch stopwatch = Stopwatch.createStarted(); + this.displaysCached = new ReferenceOpenHashSet<>(this.displaysNotCached.size()); + this.displaysByInput = createSetMultimap(); + this.displaysByOutput = createSetMultimap(); + for (Display display : this.displaysNotCached) { + this.process(display); + } + this.displaysCached.addAll(this.displaysNotCached); + this.displaysNotCached = Set.of(); + this.preprocessed = true; + InternalLogger.getInstance().debug("Processed displays for optimal lookup performance in %s.", stopwatch.stop()); + } + } + + @Override + public Set getDisplaysNotCached() { + return this.displaysNotCached; + } + + @Override + public Set getDisplaysByInput(EntryStack stack) { + return this.displaysByInput.get(stack); + } + + @Override + public Set getDisplaysByOutput(EntryStack stack) { + return this.displaysByOutput.get(stack); + } + + private void process(Display display) { + for (EntryIngredient input : display.getInputEntries()) { + for (EntryStack stack : input) { + this.displaysByInput.put(stack, display); + } + } + for (EntryIngredient output : display.getOutputEntries()) { + for (EntryStack stack : output) { + this.displaysByOutput.put(stack, display); + } + } + } + + private static SetMultimap, Display> createSetMultimap() { + return Multimaps.newSetMultimap( + new Object2ObjectOpenCustomHashMap<>(5000, new Hash.Strategy<>() { + @Override + public int hashCode(EntryStack stack) { + return Long.hashCode(EntryStacks.hashFuzzy(stack)); + } + + @Override + public boolean equals(EntryStack o1, EntryStack o2) { + return EntryStacks.equalsFuzzy(o1, o2); + } + }), + ReferenceOpenHashSet::new + ); + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayRegistryImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayRegistryImpl.java index 277ba64d7..d0d36130c 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayRegistryImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplayRegistryImpl.java @@ -86,7 +86,7 @@ public class DisplayRegistryImpl extends RecipeManagerContextImpl, List> getAll() { - return this.displaysHolder.get(); + return this.displaysHolder.getUnmodifiable(); } @Override @@ -201,8 +201,16 @@ public class DisplayRegistryImpl extends RecipeManagerContextImpl failedDisplays = new ArrayList<>(); for (List displays : getAll().values()) { - displays.removeIf(display -> !DisplayValidator.validate(display)); + for (Display display : displays) { + if (!DisplayValidator.validate(display)) { + failedDisplays.add(display); + } + } + } + for (Display display : failedDisplays) { + this.displaysHolder.remove(display); } this.displaysHolder.endReload(); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplaysHolder.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplaysHolder.java index 2480053d1..37ff40d50 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplaysHolder.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplaysHolder.java @@ -23,27 +23,24 @@ package me.shedaniel.rei.impl.client.registry.display; -import com.google.common.collect.Iterables; import me.shedaniel.rei.api.common.category.CategoryIdentifier; import me.shedaniel.rei.api.common.display.Display; -import me.shedaniel.rei.api.common.entry.EntryStack; -import me.shedaniel.rei.api.common.util.CollectionUtils; -import me.shedaniel.rei.impl.client.view.ViewsImpl; import org.jetbrains.annotations.Nullable; -import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; public interface DisplaysHolder { - boolean doesCache(); + DisplayCache cache(); void add(Display display, @Nullable Object origin); + boolean remove(Display display); + int size(); - Map, List> get(); + Map, List> getUnmodifiable(); @Nullable Object getDisplayOrigin(Display display); @@ -51,48 +48,4 @@ public interface DisplaysHolder { void endReload(); Set getDisplaysByKey(DisplayKey key); - - boolean isCached(Display display); - - Set getDisplaysNotCached(); - - Set getDisplaysByInput(EntryStack stack); - - Set getDisplaysByOutput(EntryStack stack); - - default Iterable getAllDisplaysByInputs(List> stacks) { - if (stacks.isEmpty()) return List.of(); - Iterable inputCached = null; - if (doesCache()) { - for (EntryStack stack : stacks) { - Set set = getDisplaysByInput(stack); - inputCached = inputCached == null ? set : Iterables.concat(inputCached, set); - } - if (stacks.size() > 1) inputCached = CollectionUtils.distinctReferenceOf(inputCached); - } - Collection notCached = this.getDisplaysNotCached(); - if (notCached.isEmpty()) return inputCached == null ? List.of() : inputCached; - Iterable filteredNotCached = Iterables.filter(notCached, display -> - ViewsImpl.isUsagesFor(null, stacks, display)); - if (inputCached == null) return filteredNotCached; - return Iterables.concat(inputCached, filteredNotCached); - } - - default Iterable getAllDisplaysByOutputs(List> stacks) { - if (stacks.isEmpty()) return List.of(); - Iterable outputCached = null; - if (doesCache()) { - for (EntryStack stack : stacks) { - Set set = getDisplaysByOutput(stack); - outputCached = outputCached == null ? set : Iterables.concat(outputCached, set); - } - if (stacks.size() > 1) outputCached = CollectionUtils.distinctReferenceOf(outputCached); - } - Collection notCached = this.getDisplaysNotCached(); - if (notCached.isEmpty()) return outputCached == null ? List.of() : outputCached; - Iterable filteredNotCached = Iterables.filter(notCached, display -> - ViewsImpl.isRecipesFor(null, stacks, display)); - if (outputCached == null) return filteredNotCached; - return Iterables.concat(outputCached, filteredNotCached); - } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplaysHolderImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplaysHolderImpl.java index bf09618c7..09a4ff1a1 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplaysHolderImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplaysHolderImpl.java @@ -23,21 +23,13 @@ package me.shedaniel.rei.impl.client.registry.display; -import com.google.common.base.Stopwatch; import com.google.common.collect.Multimaps; import com.google.common.collect.SetMultimap; -import it.unimi.dsi.fastutil.Hash; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; -import me.shedaniel.rei.api.client.config.ConfigObject; import me.shedaniel.rei.api.client.registry.category.CategoryRegistry; import me.shedaniel.rei.api.common.category.CategoryIdentifier; import me.shedaniel.rei.api.common.display.Display; -import me.shedaniel.rei.api.common.entry.EntryIngredient; -import me.shedaniel.rei.api.common.entry.EntryStack; -import me.shedaniel.rei.api.common.util.EntryStacks; import me.shedaniel.rei.impl.client.gui.widget.favorites.history.DisplayHistoryManager; -import me.shedaniel.rei.impl.common.InternalLogger; import net.minecraft.resources.ResourceLocation; import org.apache.commons.lang3.mutable.MutableInt; import org.jetbrains.annotations.Nullable; @@ -46,33 +38,26 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class DisplaysHolderImpl implements DisplaysHolder { - private final boolean cache; + private final DisplayCache cache; private final SetMultimap displaysByKey = Multimaps.newSetMultimap(new IdentityHashMap<>(), ReferenceOpenHashSet::new); private final Map, DisplaysList> displays = new ConcurrentHashMap<>(); - private final Map, List> unmodifiableDisplays; - private final WeakHashMap displaysBase = new WeakHashMap<>(); - private Set displaysCached = new ReferenceOpenHashSet<>(); - private Set displaysNotCached = Collections.synchronizedSet(new ReferenceOpenHashSet<>()); - private boolean preprocessed = false; - private SetMultimap, Display> displaysByInput; - private SetMultimap, Display> displaysByOutput; + private final Map, List> unmodifiableDisplays = new RemappingMap<>( + Collections.unmodifiableMap(displays), list -> { + if (list == null) { + return null; + } else { + return ((DisplaysList) list).synchronizedList; + } + }, key -> CategoryRegistry.getInstance().tryGet(key).isPresent()); + private final WeakHashMap originsMap = new WeakHashMap<>(); private final MutableInt displayCount = new MutableInt(0); public DisplaysHolderImpl(boolean init) { - this.cache = init && ConfigObject.getInstance().doesCacheDisplayLookup(); - this.unmodifiableDisplays = new RemappingMap<>(Collections.unmodifiableMap(displays), list -> { - if (list == null) { - return null; - } else { - return ((DisplaysList) list).synchronizedList; - } - }, key -> CategoryRegistry.getInstance().tryGet(key).isPresent()); - this.displaysByInput = createSetMultimap(); - this.displaysByOutput = createSetMultimap(); + this.cache = new DisplayCacheImpl(init); } @Override - public boolean doesCache() { + public DisplayCache cache() { return this.cache; } @@ -86,93 +71,63 @@ public class DisplaysHolderImpl implements DisplaysHolder { } this.displayCount.increment(); if (origin != null) { - synchronized (this.displaysBase) { - this.displaysBase.put(display, origin); + synchronized (this.originsMap) { + this.originsMap.put(display, origin); } } - if (this.cache) { - if (!preprocessed) { - this.displaysNotCached.add(display); - } else { - this.process(display); - this.displaysCached.add(display); - } - } else { - this.displaysNotCached.add(display); - } + this.cache.add(display); } @Override - public int size() { - return this.displayCount.intValue(); - } - - @Override - public Map, List> get() { - return this.unmodifiableDisplays; - } - - @Override - public void endReload() { - if (this.cache) { - InternalLogger.getInstance().debug("Processing %d displays for optimal lookup performance...", this.size()); - Stopwatch stopwatch = Stopwatch.createStarted(); - this.displaysCached = new ReferenceOpenHashSet<>(this.size()); - this.displaysByInput = createSetMultimap(); - this.displaysByOutput = createSetMultimap(); - for (Display display : this.displaysNotCached) { - this.process(display); + public boolean remove(Display display) { + if (this.displays.get(display.getCategoryIdentifier()).remove(display)) { + removeFallout(display); + if (this.displays.get(display.getCategoryIdentifier()).isEmpty()) { + this.displays.remove(display.getCategoryIdentifier()); } - this.displaysCached.addAll(this.displaysNotCached); - this.displaysNotCached = Set.of(); - this.preprocessed = true; - InternalLogger.getInstance().debug("Processed displays for optimal lookup performance in %s.", stopwatch.stop()); + return true; } + + return false; } - private void process(Display display) { - for (EntryIngredient input : display.getInputEntries()) { - for (EntryStack stack : input) { - this.displaysByInput.put(stack, display); - } + private void removeFallout(Display display) { + Optional location = display.getDisplayLocation(); + if (location.isPresent()) { + this.displaysByKey.remove(DisplayKey.create(display.getCategoryIdentifier(), location.get()), display); } - for (EntryIngredient output : display.getOutputEntries()) { - for (EntryStack stack : output) { - this.displaysByOutput.put(stack, display); - } + this.displayCount.decrement(); + synchronized (this.originsMap) { + this.originsMap.remove(display); } + this.cache.remove(display); } @Override - public Set getDisplaysByKey(DisplayKey key) { - return this.displaysByKey.get(key); - } - - @Override - public boolean isCached(Display display) { - return this.cache && this.displaysCached.contains(display); + public int size() { + return this.displayCount.intValue(); } @Override - public Set getDisplaysNotCached() { - return this.displaysNotCached; + public Map, List> getUnmodifiable() { + return this.unmodifiableDisplays; } @Override - public Set getDisplaysByInput(EntryStack stack) { - return this.displaysByInput.get(stack); + public void endReload() { + this.cache.endReload(); } @Override - public Set getDisplaysByOutput(EntryStack stack) { - return this.displaysByOutput.get(stack); + public Set getDisplaysByKey(DisplayKey key) { + return this.displaysByKey.get(key); } @Override @Nullable public Object getDisplayOrigin(Display display) { - synchronized (this.displaysBase) { - Object origin = this.displaysBase.get(display); + synchronized (this.originsMap) { + Object origin = this.originsMap.get(display); if (origin != null) { return origin; @@ -190,21 +145,4 @@ public class DisplaysHolderImpl implements DisplaysHolder { this.synchronizedList = Collections.synchronizedList(unmodifiableList); } } - - private SetMultimap, Display> createSetMultimap() { - return Multimaps.newSetMultimap( - new Object2ObjectOpenCustomHashMap<>(Math.max(10000, this.size() * 5 / 2), new Hash.Strategy<>() { - @Override - public int hashCode(EntryStack stack) { - return Long.hashCode(EntryStacks.hashFuzzy(stack)); - } - - @Override - public boolean equals(EntryStack o1, EntryStack o2) { - return EntryStacks.equalsFuzzy(o1, o2); - } - }), - ReferenceOpenHashSet::new - ); - } } 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 34bff7be2..79d27d9fd 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 @@ -286,9 +286,9 @@ public class ViewsImpl implements Views { } public static boolean isRecipesFor(@Nullable DisplaysHolder displaysHolder, List> stacks, Display display) { - if (displaysHolder != null && displaysHolder.isCached(display)) { + if (displaysHolder != null && displaysHolder.cache().isCached(display)) { for (EntryStack recipesFor : stacks) { - return displaysHolder.getDisplaysByOutput(recipesFor).contains(display); + return displaysHolder.cache().getDisplaysByOutput(recipesFor).contains(display); } } @@ -296,9 +296,9 @@ public class ViewsImpl implements Views { } public static boolean isUsagesFor(@Nullable DisplaysHolder displaysHolder, List> stacks, Display display) { - if (displaysHolder != null && displaysHolder.isCached(display)) { + if (displaysHolder != null && displaysHolder.cache().isCached(display)) { for (EntryStack recipesFor : stacks) { - return displaysHolder.getDisplaysByInput(recipesFor).contains(display); + return displaysHolder.cache().getDisplaysByInput(recipesFor).contains(display); } } -- cgit