diff options
| author | shedaniel <daniel@shedaniel.me> | 2023-05-31 23:18:02 +0800 |
|---|---|---|
| committer | shedaniel <daniel@shedaniel.me> | 2023-06-01 16:55:08 +0800 |
| commit | 15ff1dcfe181d3469b5a1d28986cf965f0e95df4 (patch) | |
| tree | ce5801c45facc01b5245bce7b3f519c39e4c9312 | |
| parent | c14a1cc216730037319f2a18370fd29b1993fe71 (diff) | |
| download | RoughlyEnoughItems-15ff1dcfe181d3469b5a1d28986cf965f0e95df4.tar.gz RoughlyEnoughItems-15ff1dcfe181d3469b5a1d28986cf965f0e95df4.tar.bz2 RoughlyEnoughItems-15ff1dcfe181d3469b5a1d28986cf965f0e95df4.zip | |
Fix #1396 and fix #998
13 files changed, 550 insertions, 164 deletions
diff --git a/api/src/main/java/me/shedaniel/rei/api/client/config/ConfigObject.java b/api/src/main/java/me/shedaniel/rei/api/client/config/ConfigObject.java index 45d66c860..02afcd7f4 100644 --- a/api/src/main/java/me/shedaniel/rei/api/client/config/ConfigObject.java +++ b/api/src/main/java/me/shedaniel/rei/api/client/config/ConfigObject.java @@ -272,6 +272,9 @@ public interface ConfigObject { boolean doesCacheEntryRendering(); + @ApiStatus.Experimental + boolean doesCacheDisplayLookup(); + boolean doDebugRenderTimeRequired(); boolean doMergeDisplayUnderOne(); diff --git a/api/src/main/java/me/shedaniel/rei/api/common/util/CollectionUtils.java b/api/src/main/java/me/shedaniel/rei/api/common/util/CollectionUtils.java index eb0449f48..bd8376e53 100644 --- a/api/src/main/java/me/shedaniel/rei/api/common/util/CollectionUtils.java +++ b/api/src/main/java/me/shedaniel/rei/api/common/util/CollectionUtils.java @@ -23,12 +23,10 @@ package me.shedaniel.rei.api.common.util; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; -import com.google.common.collect.UnmodifiableIterator; +import com.google.common.collect.*; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; import me.shedaniel.rei.api.common.entry.EntryStack; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; @@ -550,4 +548,23 @@ public class CollectionUtils { return size; } } + + public static <T> Iterable<T> distinctReferenceOf(Iterable<T> iterable) { + return () -> new AbstractIterator<T>() { + private final Set<T> set = new ReferenceOpenHashSet<>(); + private final Iterator<T> iterator = iterable.iterator(); + + @Override + protected T computeNext() { + while (iterator.hasNext()) { + T next = iterator.next(); + if (set.add(next)) { + return next; + } + } + + return endOfData(); + } + }; + } } diff --git a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java index f1b1110e8..46f6589f2 100644 --- a/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java +++ b/runtime/src/main/java/me/shedaniel/rei/RoughlyEnoughItemsCoreClient.java @@ -78,6 +78,7 @@ import me.shedaniel.rei.impl.client.registry.category.CategoryRegistryImpl; import me.shedaniel.rei.impl.client.registry.display.DisplayRegistryImpl; import me.shedaniel.rei.impl.client.registry.screen.ScreenRegistryImpl; import me.shedaniel.rei.impl.client.search.SearchProviderImpl; +import me.shedaniel.rei.impl.client.search.SearchRuntime; import me.shedaniel.rei.impl.client.search.method.InputMethodRegistryImpl; import me.shedaniel.rei.impl.client.subsets.SubsetsRegistryImpl; import me.shedaniel.rei.impl.client.transfer.TransferHandlerRegistryImpl; @@ -231,6 +232,7 @@ public class RoughlyEnoughItemsCoreClient { new FavoriteEntryTypeRegistryImpl(), new SubsetsRegistryImpl(), new TransferHandlerRegistryImpl(), + new SearchRuntime(), new REIRuntimeImpl(), new ConfigAddonRegistryImpl()), "clientPluginManager"); } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/REIRuntimeImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/REIRuntimeImpl.java index 2e7018ad8..fad1fdf6c 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/REIRuntimeImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/REIRuntimeImpl.java @@ -47,7 +47,6 @@ import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; import me.shedaniel.rei.impl.client.gui.hints.HintProvider; import me.shedaniel.rei.impl.client.gui.widget.CachedEntryListRender; import me.shedaniel.rei.impl.client.gui.widget.search.OverlaySearchField; -import me.shedaniel.rei.impl.client.search.argument.Argument; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; @@ -255,7 +254,6 @@ public class REIRuntimeImpl implements REIRuntime { @Override public void startReload() { - Argument.resetCache(false); getOverlay().ifPresent(ScreenOverlay::queueReloadOverlay); lastDisplayScreen.clear(); if (!RenderSystem.isOnRenderThread()) { @@ -272,7 +270,6 @@ public class REIRuntimeImpl implements REIRuntime { @Override public void endReload(ReloadStage stage) { - Argument.resetCache(true); getOverlay().ifPresent(ScreenOverlay::queueReloadOverlay); } 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 f8e1f02d0..47f84a7c4 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 @@ -303,6 +303,11 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { } @Override + public boolean doesCacheDisplayLookup() { + return advanced.miscellaneous.cachingDisplayLookup; + } + + @Override public boolean doDebugRenderTimeRequired() { return advanced.layout.debugRenderTimeRequired; } @@ -726,6 +731,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData { private boolean newFastEntryRendering = true; @ConfigEntry.Gui.PrefixText private boolean cachingFastEntryRendering = false; + private boolean cachingDisplayLookup = true; } public static class Filtering { 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 7d23a89d7..777b44d06 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 @@ -60,6 +60,8 @@ import me.shedaniel.rei.impl.client.gui.InternalTextures; import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; import me.shedaniel.rei.impl.client.gui.dragging.CurrentDraggingStack; import me.shedaniel.rei.impl.client.gui.widget.favorites.FavoritesListWidget; +import me.shedaniel.rei.impl.client.registry.display.DisplayRegistryImpl; +import me.shedaniel.rei.impl.client.registry.display.DisplaysHolder; import me.shedaniel.rei.impl.client.view.ViewsImpl; import net.minecraft.ChatFormatting; import net.minecraft.CrashReport; @@ -317,22 +319,24 @@ public class EntryWidget extends Slot implements DraggableStackProviderWidget { try { DisplayRegistry displayRegistry = DisplayRegistry.getInstance(); + DisplaysHolder displaysHolder = ((DisplayRegistryImpl) displayRegistry).displaysHolder(); CategoryRegistry categoryRegistry = CategoryRegistry.getInstance(); Map<CategoryIdentifier<?>, Boolean> filteringQuickCraftCategories = ConfigObject.getInstance().getFilteringQuickCraftCategories(); - for (Map.Entry<CategoryIdentifier<?>, List<Display>> entry : displayRegistry.getAll().entrySet()) { + boolean shouldFilterDisplays = ConfigObject.getInstance().shouldFilterDisplays(); + + for (Display display : displaysHolder.getAllDisplaysByOutputs(getEntries())) { + CategoryIdentifier<?> categoryIdentifier = display.getCategoryIdentifier(); Optional<? extends CategoryRegistry.CategoryConfiguration<?>> configuration; - if ((configuration = categoryRegistry.tryGet(entry.getKey())).isEmpty() - || categoryRegistry.isCategoryInvisible(configuration.get().getCategory())) continue; - if (!filteringQuickCraftCategories.getOrDefault(entry.getKey(), configuration.get().isQuickCraftingEnabledByDefault())) continue; - for (Display display : entry.getValue()) { - if ((!ConfigObject.getInstance().shouldFilterDisplays() || displayRegistry.isDisplayVisible(display)) - && ViewsImpl.isRecipesFor(getEntries(), display)) { - AutoCraftingEvaluator.AutoCraftingResult result = AutoCraftingEvaluator.evaluateAutoCrafting(false, false, display, null); - if (result.successful) { - this.display = display; - this.displayTooltipComponent = Suppliers.memoize(() -> new DisplayTooltipComponent(display)); - return result.successfulHandler; - } + if ((configuration = categoryRegistry.tryGet(categoryIdentifier)).isEmpty() + || categoryRegistry.isCategoryInvisible(configuration.get().getCategory())) continue; + if (!filteringQuickCraftCategories.getOrDefault(categoryIdentifier, configuration.get().isQuickCraftingEnabledByDefault())) + continue; + if ((!shouldFilterDisplays || displayRegistry.isDisplayVisible(display))) { + AutoCraftingEvaluator.AutoCraftingResult result = AutoCraftingEvaluator.evaluateAutoCrafting(false, false, display, null); + if (result.successful) { + this.display = display; + this.displayTooltipComponent = Suppliers.memoize(() -> new DisplayTooltipComponent(display)); + return result.successfulHandler; } } } @@ -351,7 +355,7 @@ public class EntryWidget extends Slot implements DraggableStackProviderWidget { } if (display != null) { - if (ViewsImpl.isRecipesFor(getEntries(), display)) { + if (ViewsImpl.isRecipesFor(null, getEntries(), display)) { AutoCraftingEvaluator.AutoCraftingResult result = AutoCraftingEvaluator.evaluateAutoCrafting(false, false, display, null); if (result.successful) { return result.successfulHandler; @@ -428,7 +432,8 @@ public class EntryWidget extends Slot implements DraggableStackProviderWidget { } } - protected void drawExtra(PoseStack matrices, int mouseX, int mouseY, float delta) {} + protected void drawExtra(PoseStack matrices, int mouseX, int mouseY, float delta) { + } @Override @Nullable 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 b875b8530..1aff7f720 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 @@ -24,10 +24,6 @@ package me.shedaniel.rei.impl.client.registry.display; import com.google.common.base.Preconditions; -import com.google.common.collect.ForwardingMap; -import com.google.common.collect.ForwardingMapEntry; -import com.google.common.collect.Iterators; -import com.google.common.collect.Sets; import dev.architectury.event.EventResult; import me.shedaniel.rei.api.client.plugins.REIClientPlugin; import me.shedaniel.rei.api.client.registry.category.CategoryRegistry; @@ -43,9 +39,7 @@ import me.shedaniel.rei.api.common.plugins.PluginManager; import me.shedaniel.rei.impl.common.InternalLogger; import me.shedaniel.rei.impl.common.registry.RecipeManagerContextImpl; import net.minecraft.world.item.crafting.Recipe; -import org.apache.commons.lang3.mutable.MutableInt; import org.apache.commons.lang3.mutable.MutableLong; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -53,127 +47,17 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiPredicate; import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.UnaryOperator; public class DisplayRegistryImpl extends RecipeManagerContextImpl<REIClientPlugin> implements DisplayRegistry { - private final WeakHashMap<Display, Object> displaysBase = new WeakHashMap<>(); - private final Map<CategoryIdentifier<?>, DisplaysList> displays = new ConcurrentHashMap<>(); - private final Map<CategoryIdentifier<?>, List<Display>> unmodifiableDisplays; private final Map<CategoryIdentifier<?>, List<DynamicDisplayGenerator<?>>> displayGenerators = new ConcurrentHashMap<>(); private final List<DynamicDisplayGenerator<?>> globalDisplayGenerators = new ArrayList<>(); private final List<DisplayVisibilityPredicate> visibilityPredicates = new ArrayList<>(); private final List<DisplayFiller<?>> fillers = new ArrayList<>(); - private final MutableInt displayCount = new MutableInt(0); + private final MutableLong lastAddWarning = new MutableLong(-1); + private DisplaysHolder displaysHolder = new DisplaysHolderImpl(false); public DisplayRegistryImpl() { super(RecipeManagerContextImpl.supplier()); - - this.unmodifiableDisplays = new RemappingMap<>(Collections.unmodifiableMap(displays), list -> { - if (list == null) { - return null; - } else { - return ((DisplaysList) list).unmodifiableList; - } - }, key -> CategoryRegistry.getInstance().tryGet(key).isPresent()); - } - - private static class RemappingMap<K, V> extends ForwardingMap<K, V> { - protected final Map<K, V> map; - protected final UnaryOperator<V> remapper; - protected final Predicate<K> keyPredicate; - - public RemappingMap(Map<K, V> map, UnaryOperator<V> remapper, Predicate<K> keyPredicate) { - this.map = map; - this.remapper = remapper; - this.keyPredicate = keyPredicate; - } - - @Override - @NotNull - protected Map<K, V> delegate() { - return map; - } - - @Override - public V get(Object key) { - if (keyPredicate.test((K) key)) { - return remapper.apply(super.get(key)); - } else { - return null; - } - } - - @Override - public boolean containsKey(@Nullable Object key) { - return super.containsKey(key) && keyPredicate.test((K) key); - } - - @Override - public Set<K> keySet() { - return Sets.filter(super.keySet(), keyPredicate::test); - } - - @SuppressWarnings("UnstableApiUsage") - @Override - @NotNull - public Set<Entry<K, V>> entrySet() { - return this.new StandardEntrySet() { - @Override - public Iterator<Entry<K, V>> iterator() { - return mapIterator(map.entrySet().iterator()); - } - }; - } - - @Override - public int size() { - return keySet().size(); - } - - @Override - public Collection<V> values() { - return new AbstractCollection<V>() { - @Override - public Iterator<V> iterator() { - return Iterators.transform(entrySet().iterator(), Entry::getValue); - } - - @Override - public int size() { - return RemappingMap.this.size(); - } - }; - } - - private Iterator<Entry<K, V>> mapIterator(Iterator<Entry<K, V>> iterator) { - return Iterators.transform(Iterators.filter(iterator, entry -> this.keyPredicate.test(entry.getKey())), - this::mapEntry); - } - - private Entry<K, V> mapEntry(Entry<K, V> entry) { - return new ForwardingMapEntry<>() { - @Override - @NotNull - protected Entry<K, V> delegate() { - return entry; - } - - @Override - public V getValue() { - return remapper.apply(entry.getValue()); - } - }; - } - } - - private static class DisplaysList extends ArrayList<Display> { - private final List<Display> unmodifiableList; - private final List<Display> synchronizedList; - - public DisplaysList() { - this.synchronizedList = Collections.synchronizedList(this); - this.unmodifiableList = Collections.unmodifiableList(synchronizedList); - } } @Override @@ -183,11 +67,9 @@ public class DisplayRegistryImpl extends RecipeManagerContextImpl<REIClientPlugi @Override public int displaySize() { - return displayCount.getValue(); + return this.displaysHolder.size(); } - private MutableLong lastAddWarning = new MutableLong(-1); - @Override public void add(Display display, @Nullable Object origin) { if (!PluginManager.areAnyReloading()) { @@ -199,19 +81,12 @@ public class DisplayRegistryImpl extends RecipeManagerContextImpl<REIClientPlugi } } - displays.computeIfAbsent(display.getCategoryIdentifier(), location -> new DisplaysList()) - .add(display); - displayCount.increment(); - if (origin != null) { - synchronized (displaysBase) { - displaysBase.put(display, origin); - } - } + this.displaysHolder.add(display, origin); } @Override public Map<CategoryIdentifier<?>, List<Display>> getAll() { - return unmodifiableDisplays; + return this.displaysHolder.get(); } @Override @@ -287,11 +162,10 @@ public class DisplayRegistryImpl extends RecipeManagerContextImpl<REIClientPlugi @Override public void startReload() { super.startReload(); - this.displays.clear(); + this.displaysHolder = new DisplaysHolderImpl(true); this.displayGenerators.clear(); this.visibilityPredicates.clear(); this.fillers.clear(); - this.displayCount.setValue(0); } @Override @@ -304,13 +178,19 @@ public class DisplayRegistryImpl extends RecipeManagerContextImpl<REIClientPlugi } } - for (CategoryIdentifier<?> identifier : displays.keySet()) { + for (CategoryIdentifier<?> identifier : getAll().keySet()) { if (CategoryRegistry.getInstance().tryGet(identifier).isEmpty()) { InternalLogger.getInstance().error("Found displays registered for unknown registry", new IllegalStateException(identifier.toString())); } } - InternalLogger.getInstance().debug("Registered %d displays", displayCount.getValue()); + this.displaysHolder.endReload(); + + InternalLogger.getInstance().debug("Registered %d displays", displaySize()); + } + + public DisplaysHolder displaysHolder() { + return displaysHolder; } @Override @@ -349,12 +229,13 @@ public class DisplayRegistryImpl extends RecipeManagerContextImpl<REIClientPlugi @Override @Nullable public Object getDisplayOrigin(Display display) { - return displaysBase.get(display); + return this.displaysHolder.getDisplayOrigin(display); } private record DisplayFiller<D extends Display>( BiPredicate<Object, DisplayAdditionReasons> predicate, Function<Object, D> mappingFunction - ) {} + ) { + } } 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 new file mode 100644 index 000000000..07b5879cf --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplaysHolder.java @@ -0,0 +1,96 @@ +/* + * 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.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(); + + void add(Display display, @Nullable Object origin); + + int size(); + + Map<CategoryIdentifier<?>, List<Display>> get(); + + @Nullable + Object getDisplayOrigin(Display display); + + void endReload(); + + boolean isCached(Display display); + + Set<Display> getDisplaysNotCached(); + + Set<Display> getDisplaysByInput(EntryStack<?> stack); + + Set<Display> getDisplaysByOutput(EntryStack<?> stack); + + default Iterable<Display> getAllDisplaysByInputs(List<EntryStack<?>> stacks) { + if (stacks.isEmpty()) return List.of(); + Iterable<Display> inputCached = null; + if (doesCache()) { + for (EntryStack<?> stack : stacks) { + Set<Display> set = getDisplaysByInput(stack); + inputCached = inputCached == null ? set : Iterables.concat(inputCached, set); + } + if (stacks.size() > 1) inputCached = CollectionUtils.distinctReferenceOf(inputCached); + } + Collection<Display> notCached = this.getDisplaysNotCached(); + if (notCached.isEmpty()) return inputCached == null ? List.of() : inputCached; + Iterable<Display> filteredNotCached = Iterables.filter(notCached, display -> + ViewsImpl.isUsagesFor(null, stacks, display)); + if (inputCached == null) return filteredNotCached; + return Iterables.concat(inputCached, filteredNotCached); + } + + default Iterable<Display> getAllDisplaysByOutputs(List<EntryStack<?>> stacks) { + if (stacks.isEmpty()) return List.of(); + Iterable<Display> outputCached = null; + if (doesCache()) { + for (EntryStack<?> stack : stacks) { + Set<Display> set = getDisplaysByOutput(stack); + outputCached = outputCached == null ? set : Iterables.concat(outputCached, set); + } + if (stacks.size() > 1) outputCached = CollectionUtils.distinctReferenceOf(outputCached); + } + Collection<Display> notCached = this.getDisplaysNotCached(); + if (notCached.isEmpty()) return outputCached == null ? List.of() : outputCached; + Iterable<Display> 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 new file mode 100644 index 000000000..f1f930639 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/registry/display/DisplaysHolderImpl.java @@ -0,0 +1,193 @@ +/* + * 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.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.common.InternalLogger; +import org.apache.commons.lang3.mutable.MutableInt; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class DisplaysHolderImpl implements DisplaysHolder { + private final boolean cache; + private final Map<CategoryIdentifier<?>, DisplaysList> displays = new ConcurrentHashMap<>(); + private final Map<CategoryIdentifier<?>, List<Display>> unmodifiableDisplays; + private final WeakHashMap<Display, Object> displaysBase = new WeakHashMap<>(); + private Set<Display> displaysCached = new ReferenceOpenHashSet<>(); + private Set<Display> displaysNotCached = Collections.synchronizedSet(new ReferenceOpenHashSet<>()); + private boolean preprocessed = false; + private SetMultimap<EntryStack<?>, Display> displaysByInput; + private SetMultimap<EntryStack<?>, Display> displaysByOutput; + 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).unmodifiableList; + } + }, key -> CategoryRegistry.getInstance().tryGet(key).isPresent()); + this.displaysByInput = createSetMultimap(); + this.displaysByOutput = createSetMultimap(); + } + + @Override + public boolean doesCache() { + return this.cache; + } + + @Override + public void add(Display display, @Nullable Object origin) { + this.displays.computeIfAbsent(display.getCategoryIdentifier(), location -> new DisplaysList()) + .add(display); + this.displayCount.increment(); + if (origin != null) { + synchronized (this.displaysBase) { + this.displaysBase.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); + } + } + + @Override + public int size() { + return this.displayCount.intValue(); + } + + @Override + public Map<CategoryIdentifier<?>, List<Display>> 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); + } + 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()); + } + } + + 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); + } + } + } + + @Override + public boolean isCached(Display display) { + return this.cache && this.displaysCached.contains(display); + } + + @Override + public Set<Display> getDisplaysNotCached() { + return this.displaysNotCached; + } + + @Override + public Set<Display> getDisplaysByInput(EntryStack<?> stack) { + return this.displaysByInput.get(stack); + } + + @Override + public Set<Display> getDisplaysByOutput(EntryStack<?> stack) { + return this.displaysByOutput.get(stack); + } + + @Override + |
