From e35aa217aa3bbba5653201186add39de871fae07 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Sun, 27 Feb 2022 19:06:59 +0800 Subject: Fix #787 --- .../rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java | 2 +- .../shedaniel/rei/impl/common/entry/type/EntryRegistryImpl.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'runtime/src/main/java') diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java index e0c9206e4..78854bfa9 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/AbstractDisplayViewingScreen.java @@ -173,7 +173,7 @@ public abstract class AbstractDisplayViewingScreen extends Screen implements Dis protected void transformFiltering(List setupDisplay) { for (EntryWidget widget : Widgets.walk(setupDisplay, EntryWidget.class::isInstance)) { if (widget.getEntries().size() > 1) { - Collection> refiltered = EntryRegistry.getInstance().refilterNew(widget.getEntries()); + Collection> refiltered = EntryRegistry.getInstance().refilterNew(false, widget.getEntries()); if (!refiltered.isEmpty()) { widget.clearStacks(); widget.entries(refiltered); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/EntryRegistryImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/EntryRegistryImpl.java index 247de3c47..ac523d3d5 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/EntryRegistryImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/EntryRegistryImpl.java @@ -197,7 +197,7 @@ public class EntryRegistryImpl implements EntryRegistry { int index = entries.lastIndexOf(afterEntry); entries.add(index, stack); } else entries.add(stack); - preFilteredList.addAll(refilterNew(Collections.singletonList(stack))); + preFilteredList.addAll(refilterNew(true, Collections.singletonList(stack))); queueSearchUpdate(); } } @@ -216,7 +216,7 @@ public class EntryRegistryImpl implements EntryRegistry { int index = entries.lastIndexOf(afterEntry); entries.addAll(index, filtered); } else entries.addAll(filtered); - preFilteredList.addAll(refilterNew(filtered)); + preFilteredList.addAll(refilterNew(true, filtered)); queueSearchUpdate(); } } @@ -231,8 +231,8 @@ public class EntryRegistryImpl implements EntryRegistry { @ApiStatus.Internal @Override - public Collection> refilterNew(Collection> entries) { - if (lastRefilterWarning != null) { + public Collection> refilterNew(boolean warn, Collection> entries) { + if (lastRefilterWarning != null && warn) { if (lastRefilterWarning.getValue() > 0 && System.currentTimeMillis() - lastRefilterWarning.getValue() > 5000) { RoughlyEnoughItemsCore.LOGGER.warn("Detected runtime EntryRegistry modification, this can be extremely dangerous, or be extremely inefficient!"); } -- cgit From 6ff07fe77b6d15157f262a766798e683e55c2c57 Mon Sep 17 00:00:00 2001 From: shedaniel Date: Mon, 28 Feb 2022 10:05:31 +0800 Subject: Add search caching hint --- .../client/config/entries/ReloadPluginsEntry.java | 26 +++-- .../impl/client/gui/widget/EntryListWidget.java | 32 +++--- .../rei/impl/client/gui/widget/EntryWidget.java | 6 +- .../rei/impl/client/search/AsyncSearchManager.java | 34 ++++++- .../rei/impl/client/search/SearchProviderImpl.java | 16 +++ .../rei/impl/client/search/argument/Argument.java | 113 ++++++++++++++++++--- .../argument/type/AlwaysMatchingArgumentType.java | 8 +- .../client/search/argument/type/ArgumentType.java | 5 +- .../argument/type/IdentifierArgumentType.java | 25 ++--- .../search/argument/type/ModArgumentType.java | 22 ++-- .../search/argument/type/RegexArgumentType.java | 13 +-- .../search/argument/type/TagArgumentType.java | 33 +++--- .../search/argument/type/TextArgumentType.java | 12 +-- .../search/argument/type/TooltipArgumentType.java | 31 +++--- .../client/runtime/DefaultClientRuntimePlugin.java | 1 + .../client/runtime/SearchFilterPrepareWatcher.java | 80 +++++++++++++++ 16 files changed, 345 insertions(+), 112 deletions(-) create mode 100644 runtime/src/main/java/me/shedaniel/rei/plugin/client/runtime/SearchFilterPrepareWatcher.java (limited to 'runtime/src/main/java') diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/ReloadPluginsEntry.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/ReloadPluginsEntry.java index ad1d13cd5..fd715ede5 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/ReloadPluginsEntry.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/ReloadPluginsEntry.java @@ -31,6 +31,7 @@ import me.shedaniel.rei.RoughlyEnoughItemsCore; import me.shedaniel.rei.RoughlyEnoughItemsCoreClient; import me.shedaniel.rei.api.common.plugins.PluginManager; import me.shedaniel.rei.impl.client.gui.screen.ConfigReloadingScreen; +import me.shedaniel.rei.impl.client.search.argument.Argument; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.chat.NarratorChatListener; import net.minecraft.client.gui.components.AbstractWidget; @@ -48,7 +49,7 @@ import java.util.Optional; @ApiStatus.Internal public class ReloadPluginsEntry extends AbstractConfigListEntry { private int width; - private AbstractWidget buttonWidget = new Button(0, 0, 0, 20, NarratorChatListener.NO_TITLE, button -> { + private AbstractWidget reloadPluginsButton = new Button(0, 0, 0, 20, NarratorChatListener.NO_TITLE, button -> { RoughlyEnoughItemsCore.PERFORMANCE_LOGGER.clear(); RoughlyEnoughItemsCoreClient.reloadPlugins(null, null); }) { @@ -62,12 +63,16 @@ public class ReloadPluginsEntry extends AbstractConfigListEntry { } } }; - private List children = ImmutableList.of(buttonWidget); + private AbstractWidget reloadSearchButton = new Button(0, 0, 0, 20, NarratorChatListener.NO_TITLE, button -> { + Argument.SEARCH_CACHE.clear(); + }); + private List children = ImmutableList.of(reloadPluginsButton, reloadSearchButton); public ReloadPluginsEntry(int width) { super(NarratorChatListener.NO_TITLE, false); this.width = width; - buttonWidget.setMessage(new TranslatableComponent("text.rei.reload_config")); + reloadPluginsButton.setMessage(new TranslatableComponent("text.rei.reload_config")); + reloadSearchButton.setMessage(new TranslatableComponent("text.rei.reload_search")); } @Override @@ -89,11 +94,16 @@ public class ReloadPluginsEntry extends AbstractConfigListEntry { public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isSelected, float delta) { super.render(matrices, index, y, x, entryWidth, entryHeight, mouseX, mouseY, isSelected, delta); Window window = Minecraft.getInstance().getWindow(); - this.buttonWidget.active = this.isEditable(); - this.buttonWidget.y = y; - this.buttonWidget.x = x + entryWidth / 2 - width / 2; - this.buttonWidget.setWidth(width); - this.buttonWidget.render(matrices, mouseX, mouseY, delta); + this.reloadPluginsButton.active = this.isEditable(); + this.reloadPluginsButton.y = y; + this.reloadPluginsButton.setWidth(width / 2 - 6); + this.reloadPluginsButton.x = x + entryWidth / 2 - width / 2; + this.reloadPluginsButton.render(matrices, mouseX, mouseY, delta); + this.reloadSearchButton.active = this.isEditable() && !Argument.SEARCH_CACHE.isEmpty(); + this.reloadSearchButton.y = y; + this.reloadSearchButton.setWidth(width / 2 - 6); + this.reloadSearchButton.x = x + entryWidth / 2 + 3; + this.reloadSearchButton.render(matrices, mouseX, mouseY, delta); } @Override diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java index 8d0e8b291..28f6e2ca5 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java @@ -88,6 +88,7 @@ import org.apache.commons.lang3.mutable.MutableLong; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -508,26 +509,29 @@ public class EntryListWidget extends WidgetWithBounds implements OverlayListWidg if (ignoreLastSearch) searchManager.markDirty(); searchManager.updateFilter(searchTerm); if (searchManager.isDirty()) { - List> list = searchManager.get(); - EntryPanelOrdering ordering = ConfigObject.getInstance().getItemListOrdering(); - if (ordering == EntryPanelOrdering.NAME) - list.sort(ENTRY_NAME_COMPARER); - if (ordering == EntryPanelOrdering.GROUPS) - list.sort(ENTRY_GROUP_COMPARER); - if (!ConfigObject.getInstance().isItemListAscending()) { - Collections.reverse(list); - } - allStacks = list; + searchManager.getAsync(list -> { + list = new ArrayList<>(list); + EntryPanelOrdering ordering = ConfigObject.getInstance().getItemListOrdering(); + if (ordering == EntryPanelOrdering.NAME) + list.sort(ENTRY_NAME_COMPARER); + if (ordering == EntryPanelOrdering.GROUPS) + list.sort(ENTRY_GROUP_COMPARER); + if (!ConfigObject.getInstance().isItemListAscending()) { + Collections.reverse(list); + } + allStacks = list; + + if (ConfigObject.getInstance().doDebugSearchTimeRequired()) { + RoughlyEnoughItemsCore.LOGGER.info("Search Used: %s", stopwatch.stop().toString()); + } + updateEntriesPosition(); + }); } debugTime = ConfigObject.getInstance().doDebugRenderTimeRequired(); FavoritesListWidget favorites = ScreenOverlayImpl.getFavoritesListWidget(); if (favorites != null) { favorites.updateSearch(); } - if (ConfigObject.getInstance().doDebugSearchTimeRequired()) { - RoughlyEnoughItemsCore.LOGGER.info("Search Used: %s", stopwatch.stop().toString()); - } - updateEntriesPosition(); } public boolean matches(EntryStack stack) { 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 a20825496..3f9cfe4cc 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 @@ -59,10 +59,7 @@ import net.minecraft.util.Mth; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -230,6 +227,7 @@ public class EntryWidget extends Slot implements DraggableStackProviderWidget { @Override public EntryWidget entry(EntryStack stack) { + Objects.requireNonNull(stack); if (entryStacks.isEmpty()) { entryStacks = Collections.singletonList(stack); } else { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/AsyncSearchManager.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/AsyncSearchManager.java index 9e1a8934d..b1bc2ecd5 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/AsyncSearchManager.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/AsyncSearchManager.java @@ -38,10 +38,8 @@ import me.shedaniel.rei.api.common.util.EntryStacks; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; +import java.util.concurrent.*; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; import java.util.function.UnaryOperator; @@ -53,6 +51,8 @@ public class AsyncSearchManager { private Predicate> additionalPredicate; private SearchFilter filter; private boolean dirty = false; + private boolean filterDirty = false; + private CompletableFuture>> future; private List> last; public AsyncSearchManager(Supplier>> stacksProvider, Supplier>> additionalPredicateSupplier, UnaryOperator> transformer) { @@ -78,10 +78,15 @@ public class AsyncSearchManager { this.dirty = true; } + public void markFilterDirty() { + this.filterDirty = true; + } + public void updateFilter(String filter) { if (this.filter == null || !this.filter.getFilter().equals(filter)) { this.filter = SearchProvider.getInstance().createFilter(filter); markDirty(); + markFilterDirty(); } } @@ -89,6 +94,22 @@ public class AsyncSearchManager { return last == null || dirty; } + public boolean isFilterDirty() { + return filterDirty; + } + + public Future getAsync(Consumer>> consumer) { + if (future == null || future.isCancelled() || future.isDone() || future.isCompletedExceptionally()) { + if (future != null) future.cancel(true); + future = CompletableFuture.supplyAsync(this::get) + .exceptionally(throwable -> { + throwable.printStackTrace(); + return null; + }); + } + return future.thenAccept(consumer); + } + public List> get() { if (isDirty()) { this.additionalPredicate = additionalPredicateSupplier.get(); @@ -97,6 +118,11 @@ public class AsyncSearchManager { last = new ArrayList<>(); if (!stacks.isEmpty()) { + if (filterDirty) { + filter.prepareFilter(stacks); + filterDirty = false; + } + if (ConfigObject.getInstance().shouldAsyncSearch() && stacks.size() > searchPartitionSize * 4) { List>>> futures = Lists.newArrayList(); for (Iterable> partitionStacks : CollectionUtils.partition(stacks, searchPartitionSize)) { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/SearchProviderImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/SearchProviderImpl.java index f91005c45..9a3948a86 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/SearchProviderImpl.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/SearchProviderImpl.java @@ -26,14 +26,18 @@ package me.shedaniel.rei.impl.client.search; import me.shedaniel.rei.api.client.search.SearchFilter; import me.shedaniel.rei.api.client.search.SearchProvider; import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.impl.client.search.argument.AlternativeArgument; import me.shedaniel.rei.impl.client.search.argument.Argument; import me.shedaniel.rei.impl.client.search.argument.CompoundArgument; +import me.shedaniel.rei.impl.client.search.argument.type.ArgumentType; import me.shedaniel.rei.impl.client.util.CrashReportUtils; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; +import java.util.Collection; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; public class SearchProviderImpl implements SearchProvider { @Override @@ -49,10 +53,17 @@ public class SearchProviderImpl implements SearchProvider { public static class SearchFilterImpl implements SearchFilter { private final List arguments; private final String filter; + private final List> argumentTypes; public SearchFilterImpl(List arguments, String searchTerm) { this.arguments = arguments; this.filter = searchTerm; + this.argumentTypes = arguments.stream() + .flatMap(CompoundArgument::stream) + .flatMap(AlternativeArgument::stream) + .map(Argument::getArgument) + .distinct() + .collect(Collectors.toList()); } @Override @@ -71,6 +82,11 @@ public class SearchProviderImpl implements SearchProvider { } } + @Override + public void prepareFilter(Collection> stacks) { + Argument.prepareFilter(stacks, argumentTypes); + } + @Override public String getFilter() { return filter; diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/Argument.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/Argument.java index 7366f869a..0844257ad 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/Argument.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/Argument.java @@ -25,27 +25,33 @@ package me.shedaniel.rei.impl.client.search.argument; import com.google.common.base.MoreObjects; import com.google.common.collect.Lists; +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.ints.IntIntMutablePair; +import it.unimi.dsi.fastutil.ints.IntIntPair; +import it.unimi.dsi.fastutil.longs.Long2ObjectArrayMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMaps; import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; +import me.shedaniel.rei.api.client.config.ConfigObject; import me.shedaniel.rei.api.client.gui.config.SearchMode; import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.api.common.util.EntryStacks; import me.shedaniel.rei.impl.client.search.IntRange; import me.shedaniel.rei.impl.client.search.argument.type.AlwaysMatchingArgumentType; import me.shedaniel.rei.impl.client.search.argument.type.ArgumentType; import me.shedaniel.rei.impl.client.search.argument.type.ArgumentTypesRegistry; import me.shedaniel.rei.impl.client.search.result.ArgumentApplicableResult; +import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.util.Unit; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.mutable.Mutable; -import org.apache.commons.lang3.mutable.MutableObject; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -53,6 +59,10 @@ import java.util.Collection; import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -178,12 +188,11 @@ public class Argument { if (!Objects.equals(lastLanguage.getAndSet(newLanguage), newLanguage)) { SEARCH_CACHE.clear(); } - Mutable mutable = new MutableObject<>(); a: for (CompoundArgument arguments : compoundArguments) { for (AlternativeArgument argument : arguments) { - if (!matches(stack, argument, mutable)) { + if (!matches(stack, argument)) { continue a; } } @@ -194,12 +203,12 @@ public class Argument { return false; } - private static boolean matches(EntryStack stack, AlternativeArgument alternativeArgument, Mutable mutable) { + private static boolean matches(EntryStack stack, AlternativeArgument alternativeArgument) { if (alternativeArgument.isEmpty()) return true; long hashExact = EntryStacks.hashExact(stack); for (Argument argument : alternativeArgument) { - if (matches((short) argument.getArgument().getIndex(), argument.getArgument(), mutable, stack, hashExact, argument.getText(), argument.filterData) == argument.isRegular()) { + if (matches(argument.getArgument(), stack, hashExact, argument.getText(), argument.filterData) == argument.isRegular()) { return true; } } @@ -207,18 +216,98 @@ public class Argument { return false; } - private static boolean matches(short argumentIndex, ArgumentType argumentType, Mutable data, EntryStack stack, long hashExact, String filter, R filterData) { + private static Long2ObjectMap getSearchCache(ArgumentType argumentType) { + short argumentIndex = (short) argumentType.getIndex(); Long2ObjectMap map = SEARCH_CACHE.get(argumentIndex); if (map == null) { SEARCH_CACHE.put(argumentIndex, map = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>())); } - Z value = (Z) map.get(hashExact); - data.setValue(value); - boolean matches = argumentType.matches((Mutable) data, stack, filter, (T) filterData); + return map; + } + + private static boolean matches(ArgumentType argumentType, EntryStack stack, long hashExact, String filter, R filterData) { + Long2ObjectMap map = getSearchCache(argumentType); + B value = (B) map.get(hashExact); if (value == null) { - map.put(hashExact, data.getValue()); + value = argumentType.cacheData(stack); + map.put(hashExact, value); } - return matches; + return argumentType.matches(value, stack, filter, (T) filterData); + } + + public static Long prepareStart = null; + public static Collection> prepareStacks = null; + public static IntIntPair prepareStage = null; + public static IntIntPair[] currentStages = null; + + public static void prepareFilter(Collection> stacks, Collection> argumentTypes) { + if (prepareStage != null || currentStages != null) return; + prepareStart = Util.getEpochMillis(); + prepareStacks = stacks; + prepareStage = new IntIntMutablePair(0, argumentTypes.size()); + currentStages = new IntIntPair[argumentTypes.size()]; + List hashedStacks = CollectionUtils.map(stacks, HashedEntryStackWrapper::new); + int searchPartitionSize = ConfigObject.getInstance().getAsyncSearchPartitionSize(); + boolean async = ConfigObject.getInstance().shouldAsyncSearch() && stacks.size() > searchPartitionSize * 4; + List>> futures = Lists.newArrayList(); + List, CompletableFuture>>> pairs = Lists.newArrayList(); + + for (ArgumentType argumentType : argumentTypes) { + prepareStage.first(prepareStage.firstInt() + 1); + Long2ObjectMap map = getSearchCache(argumentType); + IntIntPair currentStage = currentStages[prepareStage.firstInt() - 1] = new IntIntMutablePair(0, hashedStacks.size()); + + if (async) { + for (Collection partitionStacks : CollectionUtils.partition(hashedStacks, searchPartitionSize)) { + CompletableFuture> future = CompletableFuture.supplyAsync(() -> { + Long2ObjectMap out = new Long2ObjectArrayMap<>(searchPartitionSize + 1); + for (HashedEntryStackWrapper stack : partitionStacks) { + if (map.get(stack.hashExact()) == null) { + Object data = argumentType.cacheData(stack.unwrap()); + + if (data != null) { + out.put(stack.hashExact(), data); + } + } + } + return out; + }).whenComplete((objectLong2ObjectMap, throwable) -> { + currentStage.first(currentStage.firstInt() + partitionStacks.size()); + }); + futures.add(future); + pairs.add(Pair.of(argumentType, future)); + } + } else { + for (HashedEntryStackWrapper stack : hashedStacks) { + currentStage.first(currentStage.firstInt() + 1); + + if (map.get(stack.hashExact()) == null) { + Object data = argumentType.cacheData(stack.unwrap()); + + if (data != null) { + map.put(stack.hashExact(), data); + } + } + } + } + } + + if (async) { + try { + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).get(10, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException e) { + e.printStackTrace(); + } + for (Pair, CompletableFuture>> pair : pairs) { + Long2ObjectMap now = pair.second().getNow(null); + if (now != null) getSearchCache(pair.left()).putAll(now); + } + } + + prepareStart = null; + prepareStacks = null; + prepareStage = null; + currentStages = null; } public ArgumentType getArgument() { diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/AlwaysMatchingArgumentType.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/AlwaysMatchingArgumentType.java index 5d498b95e..ee15b8496 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/AlwaysMatchingArgumentType.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/AlwaysMatchingArgumentType.java @@ -28,7 +28,6 @@ import me.shedaniel.rei.impl.client.search.result.ArgumentApplicableResult; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.util.Unit; -import org.apache.commons.lang3.mutable.Mutable; import org.jetbrains.annotations.ApiStatus; @ApiStatus.Internal @@ -42,7 +41,12 @@ public final class AlwaysMatchingArgumentType extends ArgumentType { } @Override - public boolean matches(Mutable data, EntryStack stack, String searchText, Unit filterData) { + public Unit cacheData(EntryStack stack) { + return null; + } + + @Override + public boolean matches(Unit data, EntryStack stack, String searchText, Unit filterData) { return true; } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/ArgumentType.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/ArgumentType.java index f196f8854..7e0e75348 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/ArgumentType.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/ArgumentType.java @@ -29,7 +29,6 @@ import me.shedaniel.rei.impl.client.search.result.ArgumentApplicableResult; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.network.chat.Style; -import org.apache.commons.lang3.mutable.Mutable; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -83,7 +82,9 @@ public abstract class ArgumentType { return ArgumentApplicableResult.notApplicable(); } - public abstract boolean matches(Mutable data, EntryStack stack, String searchText, T filterData); + public abstract R cacheData(EntryStack stack); + + public abstract boolean matches(R data, EntryStack stack, String searchText, T filterData); public abstract T prepareSearchFilter(String searchText); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/IdentifierArgumentType.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/IdentifierArgumentType.java index f490cac7a..350f3b6e6 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/IdentifierArgumentType.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/IdentifierArgumentType.java @@ -32,7 +32,6 @@ import net.minecraft.network.chat.Style; import net.minecraft.network.chat.TextColor; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Unit; -import org.apache.commons.lang3.mutable.Mutable; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -65,21 +64,19 @@ public final class IdentifierArgumentType extends ArgumentType { } @Override - public boolean matches(Mutable data, EntryStack stack, String searchText, Unit filterData) { - if (data.getValue() == null) { - ResourceLocation identifier = stack.getIdentifier(); - if (identifier == null) { - data.setValue(EMPTY); - } else { - String s = identifier.getPath(); - if (s.isEmpty()) { - data.setValue(EMPTY); - } else { - data.setValue(s); - } + public String cacheData(EntryStack stack) { + ResourceLocation identifier = stack.getIdentifier(); + if (identifier != null) { + String s = identifier.getPath(); + if (!s.isEmpty()) { + return s; } } - String identifier = data.getValue(); + return EMPTY; + } + + @Override + public boolean matches(String identifier, EntryStack stack, String searchText, Unit filterData) { return !identifier.isEmpty() && identifier.contains(searchText); } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/ModArgumentType.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/ModArgumentType.java index 458b5156b..06e8d35fc 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/ModArgumentType.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/ModArgumentType.java @@ -31,9 +31,7 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.network.chat.Style; import net.minecraft.network.chat.TextColor; -import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Unit; -import org.apache.commons.lang3.mutable.Mutable; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -62,15 +60,17 @@ public final class ModArgumentType extends ArgumentType data, EntryStack stack, String searchText, Unit filterData) { - if (data.getValue() == null) { - String containingNs = stack.getContainingNamespace(); - data.setValue(containingNs != null ? new ModInfoPair( - containingNs, - null - ) : ModInfoPair.EMPTY); - } - ModInfoPair pair = data.getValue(); + @Nullable + public ModInfoPair cacheData(EntryStack stack) { + String containingNs = stack.getContainingNamespace(); + return containingNs != null ? new ModInfoPair( + containingNs, + null + ) : ModInfoPair.EMPTY; + } + + @Override + public boolean matches(@Nullable ModInfoPair pair, EntryStack stack, String searchText, Unit filterData) { if (pair.modId == null || pair.modId.contains(searchText)) return true; if (pair.modName == null) { pair.modName = ClientHelper.getInstance().getModFromModId(pair.modId).toLowerCase(Locale.ROOT); diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/RegexArgumentType.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/RegexArgumentType.java index 136941fa3..a1833154b 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/RegexArgumentType.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/RegexArgumentType.java @@ -29,7 +29,6 @@ import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.network.chat.Style; import net.minecraft.network.chat.TextColor; -import org.apache.commons.lang3.mutable.Mutable; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -80,12 +79,14 @@ public final class RegexArgumentType extends ArgumentType<@Nullable Pattern, Str } @Override - public boolean matches(Mutable data, EntryStack stack, String searchText, @Nullable Pattern filterData) { + public String cacheData(EntryStack stack) { + return stack.asFormatStrippedText().getString(); + } + + @Override + public boolean matches(String data, EntryStack stack, String searchText, @Nullable Pattern filterData) { if (filterData == null) return false; - if (data.getValue() == null) { - data.setValue(stack.asFormatStrippedText().getString()); - } - Matcher matcher = filterData.matcher(data.getValue()); + Matcher matcher = filterData.matcher(data); return matcher != null && matcher.matches(); } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TagArgumentType.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TagArgumentType.java index e64ebff9b..fb74078b3 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TagArgumentType.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TagArgumentType.java @@ -32,7 +32,6 @@ import net.minecraft.network.chat.Style; import net.minecraft.network.chat.TextColor; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Unit; -import org.apache.commons.lang3.mutable.Mutable; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -67,22 +66,26 @@ public final class TagArgumentType extends ArgumentType { } @Override - public boolean matches(Mutable data, EntryStack stack, String searchText, Unit filterData) { - if (data.getValue() == null) { - Collection tags = stack.getTagsFor(); - if (tags.isEmpty()) { - data.setValue(EMPTY_ARRAY); - } else { - data.setValue(new String[tags.size()]); - int i = 0; - - for (ResourceLocation identifier : tags) { - data.getValue()[i] = identifier.toString(); - i++; - } + public String[] cacheData(EntryStack stack) { + Collection tags = stack.getTagsFor(); + if (tags.isEmpty()) { + return EMPTY_ARRAY; + } else { + String[] arr = new String[tags.size()]; + int i = 0; + + for (ResourceLocation identifier : tags) { + arr[i] = identifier.toString(); + i++; } + + return arr; } - for (String tag : data.getValue()) { + } + + @Override + public boolean matches(String[] data, EntryStack stack, String searchText, Unit filterData) { + for (String tag : data) { if (!tag.isEmpty() && tag.contains(searchText)) { return true; } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TextArgumentType.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TextArgumentType.java index e17f0dadb..a3ee560f4 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TextArgumentType.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TextArgumentType.java @@ -28,7 +28,6 @@ import me.shedaniel.rei.api.common.entry.EntryStack; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.util.Unit; -import org.apache.commons.lang3.mutable.Mutable; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -56,11 +55,12 @@ public final class TextArgumentType extends ArgumentType { } @Override - public boolean matches(Mutable data, EntryStack stack, String searchText, Unit filterData) { - if (data.getValue() == null) { - data.setValue(stack.asFormatStrippedText().getString().toLowerCase(Locale.ROOT)); - } - String value = data.getValue(); + public String cacheData(EntryStack stack) { + return stack.asFormatStrippedText().getString().toLowerCase(Locale.ROOT); + } + + @Override + public boolean matches(String value, EntryStack stack, String searchText, Unit filterData) { return !value.isEmpty() && value.contains(searchText); } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TooltipArgumentType.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TooltipArgumentType.java index dc8491615..372214b8a 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TooltipArgumentType.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TooltipArgumentType.java @@ -33,7 +33,6 @@ import net.fabricmc.api.Environment; import net.minecraft.network.chat.Style; import net.minecraft.network.chat.TextColor; import net.minecraft.util.Unit; -import org.apache.commons.lang3.mutable.Mutable; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -45,6 +44,7 @@ import java.util.StringJoiner; @Environment(EnvType.CLIENT) public final class TooltipArgumentType extends ArgumentType { public static final TooltipArgumentType INSTANCE = new TooltipArgumentType(); + public static final String INVALID = "INVALID_PIECE_OF_TOOLTIP_I_DONT_THINK_PEOPLE_WILL_EXACTLY_HAVE_THIS_REI_REI_REI"; private static final Style STYLE = Style.EMPTY.withColor(TextColor.fromRgb(0xffe0ad)); @Override @@ -69,13 +69,16 @@ public final class TooltipArgumentType extends ArgumentType { } @Override - public boolean matches(Mutable data, EntryStack stack, String searchText, Unit filterData) { - if (data.getValue() == null) { - String tooltip = tryGetEntryStackTooltip(stack, 0); - if (tooltip == null) return false; - data.setValue(tooltip.toLowerCase(Locale.ROOT)); - } - String tooltip = data.getValue(); + public String cacheData(EntryStack stack) { + String tooltip = tryGetEntryStackTooltip(stack, 0); + if (tooltip == null) return INVALID; + return tooltip.toLowerCase(Locale.ROOT); + } + + @Override + public boolean matches(String tooltip, EntryStack stack, String searchText, Unit filterData) { + //noinspection StringEquality + if (tooltip == INVALID) return false; return tooltip.isEmpty() || tooltip.contains(searchText); } @@ -84,13 +87,13 @@ public final class TooltipArgumentType extends ArgumentType { try { Tooltip tooltip = stack.getTooltip(new Point(), false); if (tooltip != null) { - StringJoiner joiner = new StringJoiner("\n"); - for (Tooltip.Entry entry : tooltip.entries()) { - if (entry.isText()) { - joiner.add(entry.getAsText().getString()); + StringJoiner joiner = new StringJoiner("\n"); + for (Tooltip.Entry entry : tooltip.entries()) { + if (entry.isText()) { + joiner.add(entry.getAsText().getString()); + } } - } - return joiner.toString(); + return joiner.toString(); } return ""; } catch (Throwable throwable) { diff --git a/runtime/src/main/java/me/shedaniel/rei/plugin/client/runtime/DefaultClientRuntimePlugin.java b/runtime/src/main/java/me/shedaniel/rei/plugin/client/runtime/DefaultClientRuntimePlugin.java index 9101f7648..41c37ce83 100644 --- a/runtime/src/main/java/me/shedaniel/rei/plugin/client/runtime/DefaultClientRuntimePlugin.java +++ b/runtime/src/main/java/me/shedaniel/rei/plugin/client/runtime/DefaultClientRuntimePlugin.java @@ -91,6 +91,7 @@ public class DefaultClientRuntimePlugin implements REIClientPlugin { } REIRuntimeImpl.getInstance().addHintProvider(watcher); REIRuntimeImpl.getInstance().addHintProvider(new SearchBarHighlightWatcher()); + REIRuntimeImpl.getInstance().addHintProvider(new SearchFilterPrepareWatcher()); } @Override diff --git a/runtime/src/main/java/me/shedaniel/rei/plugin/client/runtime/SearchFilterPrepareWatcher.java b/runtime/src/main/java/me/shedaniel/rei/plugin/client/runtime/SearchFilterPrepareWatcher.java new file mode 100644 index 000000000..436f41724 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/plugin/client/runtime/SearchFilterPrepareWatcher.java @@ -0,0 +1,80 @@ +/* + * 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.plugin.client.runtime; + +import com.google.common.collect.ImmutableList; +import it.unimi.dsi.fastutil.ints.IntIntPair; +import me.shedaniel.math.Color; +import me.shedaniel.math.Point; +import me.shedaniel.rei.api.client.gui.widgets.Tooltip; +import me.shedaniel.rei.impl.client.gui.hints.HintProvider; +import me.shedaniel.rei.impl.client.search.argument.Argument; +import net.minecraft.Util; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TranslatableComponent; +import org.apache.commons.lang3.ArrayUtils; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +public class SearchFilterPrepareWatcher implements HintProvider { + private Double lastProcess; + + @Override + public List provide() { + lastProcess = null; + if (Argument.prepareStage != null && Argument.currentStages != null && Argument.prepareStacks != null && Argument.prepareStacks.size() > 100) { + if (Util.getEpochMillis() - Argument.prepareStart < 500) return Collections.emptyList(); + int prepareStageCurrent = Argument.prepareStage.firstInt(); + int prepareStageTotal = Argument.prepareStage.secondInt(); + IntIntPair currentStage = ArrayUtils.get(Argument.currentStages, prepareStageCurrent - 1); + int currentStageCurrent = currentStage == null ? 0 : currentStage.firstInt(); + int currentStageTotal = currentStage == null ? 0 : currentStage.secondInt(); + double prepareStageProgress = prepareStageTotal == 0 ? 0 : prepareStageCurrent / (double) prepareStageTotal; + double currentStageProgress = currentStageTotal == 0 ? 0 : currentStageCurrent / (double) currentStageTotal; + lastProcess = prepareStageTotal == 0 ? 0 : Math.max(0, prepareStageProgress - (1 - currentStageProgress) / prepareStageTotal); + return ImmutableList.of(new TranslatableComponent("text.rei.caching.search"), + new TranslatableComponent("text.rei.caching.search.step", prepareStageCurrent, prepareStageTotal, Math.round(lastProcess * 100))); + } + return Collections.emptyList(); + } + + @Override + @Nullable + public Tooltip provideTooltip(Point mouse) { + return null; + } + + @Override + @Nullable + public Double getProgress() { + return lastProcess; + } + + @Override + public Color getColor() { + return Color.ofTransparent(0x50de38ff); + } +} -- cgit