aboutsummaryrefslogtreecommitdiff
path: root/runtime/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/src/main/java')
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/config/ConfigObjectImpl.java7
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java5
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringCache.java31
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringCacheImpl.java44
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringContextImpl.java42
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringRule.java6
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/ManualFilteringRule.java52
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/SearchFilteringRule.java3
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryStacksRegionWidget.java2
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/search/argument/type/TooltipArgumentType.java24
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/common/entry/type/EntryRegistryImpl.java15
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/plugin/client/FilteredStacksVisibilityHandler.java116
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/plugin/client/runtime/DefaultClientRuntimePlugin.java12
13 files changed, 330 insertions, 29 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 73521ae30..65997aae0 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
@@ -319,6 +319,12 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData {
return advanced.filtering.filteredStacks;
}
+ @ApiStatus.Experimental
+ @Override
+ public boolean shouldFilterDisplays() {
+ return advanced.filtering.shouldFilterDisplays;
+ }
+
@ApiStatus.Internal
public List<FilteringRule<?>> getFilteringRules() {
return advanced.filtering.filteringRules;
@@ -584,6 +590,7 @@ public class ConfigObjectImpl implements ConfigObject, ConfigData {
public static class Filtering {
@UseFilteringScreen private List<EntryStackProvider<?>> filteredStacks = new ArrayList<>();
+ public boolean shouldFilterDisplays = true;
@ConfigEntry.Gui.Excluded public List<FilteringRule<?>> filteringRules = new ArrayList<>();
}
}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java
index 9026127f3..2d70af051 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/config/entries/FilteringScreen.java
@@ -41,6 +41,7 @@ import me.shedaniel.rei.api.client.gui.widgets.Tooltip;
import me.shedaniel.rei.api.client.registry.entry.EntryRegistry;
import me.shedaniel.rei.api.client.search.SearchFilter;
import me.shedaniel.rei.api.client.search.SearchProvider;
+import me.shedaniel.rei.api.common.entry.EntrySerializer;
import me.shedaniel.rei.api.common.entry.EntryStack;
import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl;
import me.shedaniel.rei.impl.client.gui.widget.BatchedEntryRendererManager;
@@ -320,6 +321,10 @@ public class FilteringScreen extends Screen {
}
public boolean matches(EntryStack<?> stack) {
+ EntrySerializer<?> serializer = stack.getDefinition().getSerializer();
+ if (serializer == null || !serializer.supportReading() || !serializer.supportSaving()) {
+ return false;
+ }
return lastFilter.test(stack);
}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringCache.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringCache.java
new file mode 100644
index 000000000..d2779211a
--- /dev/null
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringCache.java
@@ -0,0 +1,31 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020, 2021 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.entry.filtering;
+
+import org.jetbrains.annotations.Nullable;
+
+public interface FilteringCache {
+ @Nullable
+ Object getCache(FilteringRule<?> rule);
+}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringCacheImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringCacheImpl.java
new file mode 100644
index 000000000..cd4030fec
--- /dev/null
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringCacheImpl.java
@@ -0,0 +1,44 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020, 2021 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.entry.filtering;
+
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class FilteringCacheImpl implements FilteringCache {
+ private final Map<FilteringRule<?>, Optional<?>> CACHE = new HashMap<>();
+
+ @Override
+ @Nullable
+ public Object getCache(FilteringRule<?> rule) {
+ return CACHE.getOrDefault(rule, Optional.empty()).orElse(null);
+ }
+
+ public void setCache(FilteringRule<?> rule, @Nullable Object value) {
+ CACHE.put(rule, Optional.ofNullable(value));
+ }
+}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringContextImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringContextImpl.java
index 1d52db2d9..adda65797 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringContextImpl.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringContextImpl.java
@@ -43,20 +43,28 @@ import java.util.concurrent.TimeoutException;
@Environment(EnvType.CLIENT)
public class FilteringContextImpl implements FilteringContext {
+ private final boolean async;
public final Map<FilteringContextType, Set<HashedEntryStackWrapper>> stacks;
private final Map<FilteringContextType, Collection<EntryStack<?>>> cachedStacks;
public FilteringContextImpl(Collection<EntryStack<?>> allStacks) {
+ this(true, allStacks);
+ }
+
+ public FilteringContextImpl(boolean async, Collection<EntryStack<?>> allStacks) {
+ this.async = async;
this.stacks = Maps.newHashMap();
this.cachedStacks = Maps.newHashMap();
for (FilteringContextType type : FilteringContextType.values()) {
this.stacks.computeIfAbsent(type, t -> Sets.newHashSet());
}
- this.stacks.get(FilteringContextType.DEFAULT).addAll(CollectionUtils.mapParallel(allStacks, HashedEntryStackWrapper::new));
+ this.stacks.get(FilteringContextType.DEFAULT).addAll(async ? CollectionUtils.mapParallel(allStacks, HashedEntryStackWrapper::new)
+ : CollectionUtils.map(allStacks, HashedEntryStackWrapper::new));
fillCache();
}
public FilteringContextImpl(Map<FilteringContextType, Set<HashedEntryStackWrapper>> stacks) {
+ this.async = false;
this.stacks = stacks;
this.cachedStacks = Maps.newHashMap();
for (FilteringContextType type : FilteringContextType.values()) {
@@ -81,24 +89,34 @@ public class FilteringContextImpl implements FilteringContext {
Collection<HashedEntryStackWrapper> hiddenStacks = result.getHiddenStacks();
Collection<HashedEntryStackWrapper> shownStacks = result.getShownStacks();
- List<CompletableFuture<Void>> completableFutures = Lists.newArrayList();
- completableFutures.add(CompletableFuture.runAsync(() -> {
+ if (async) {
+ List<CompletableFuture<Void>> completableFutures = Lists.newArrayList();
+ completableFutures.add(CompletableFuture.runAsync(() -> {
+ this.stacks.get(FilteringContextType.DEFAULT).removeAll(hiddenStacks);
+ this.stacks.get(FilteringContextType.DEFAULT).removeAll(shownStacks);
+ }));
+ completableFutures.add(CompletableFuture.runAsync(() -> {
+ this.stacks.get(FilteringContextType.SHOWN).removeAll(hiddenStacks);
+ this.stacks.get(FilteringContextType.SHOWN).addAll(shownStacks);
+ }));
+ completableFutures.add(CompletableFuture.runAsync(() -> {
+ this.stacks.get(FilteringContextType.HIDDEN).addAll(hiddenStacks);
+ this.stacks.get(FilteringContextType.HIDDEN).removeAll(shownStacks);
+ }));
+ try {
+ CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).get(20, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ e.printStackTrace();
+ }
+ } else {
this.stacks.get(FilteringContextType.DEFAULT).removeAll(hiddenStacks);
this.stacks.get(FilteringContextType.DEFAULT).removeAll(shownStacks);
- }));
- completableFutures.add(CompletableFuture.runAsync(() -> {
this.stacks.get(FilteringContextType.SHOWN).removeAll(hiddenStacks);
this.stacks.get(FilteringContextType.SHOWN).addAll(shownStacks);
- }));
- completableFutures.add(CompletableFuture.runAsync(() -> {
this.stacks.get(FilteringContextType.HIDDEN).addAll(hiddenStacks);
this.stacks.get(FilteringContextType.HIDDEN).removeAll(shownStacks);
- }));
- try {
- CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).get(20, TimeUnit.SECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- e.printStackTrace();
}
+
fillCache();
}
}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringRule.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringRule.java
index 122e2e3f7..40593dc66 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringRule.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/FilteringRule.java
@@ -66,7 +66,7 @@ public interface FilteringRule<T extends FilteringRule<?>> {
T createFromTag(CompoundTag tag);
- FilteringResult processFilteredStacks(FilteringContext context);
+ FilteringResult processFilteredStacks(FilteringContext context, FilteringCache cache, boolean async);
@ApiStatus.Internal
default Optional<BiFunction<FilteringEntry, Screen, Screen>> createEntryScreen() {
@@ -81,5 +81,9 @@ public interface FilteringRule<T extends FilteringRule<?>> {
return Component.nullToEmpty(null);
}
+ default Object prepareCache(boolean async) {
+ return null;
+ }
+
T createNew();
}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/ManualFilteringRule.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/ManualFilteringRule.java
index c8fb61998..8ba2e83fb 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/ManualFilteringRule.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/ManualFilteringRule.java
@@ -23,6 +23,7 @@
package me.shedaniel.rei.impl.client.entry.filtering.rules;
+import com.google.common.collect.Lists;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import me.shedaniel.rei.api.client.config.ConfigObject;
@@ -31,6 +32,7 @@ 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.entry.filtering.AbstractFilteringRule;
+import me.shedaniel.rei.impl.client.entry.filtering.FilteringCache;
import me.shedaniel.rei.impl.client.entry.filtering.FilteringContext;
import me.shedaniel.rei.impl.client.entry.filtering.FilteringResult;
import net.minecraft.nbt.CompoundTag;
@@ -38,6 +40,11 @@ import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import java.util.Collection;
+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.stream.Collectors;
public class ManualFilteringRule extends AbstractFilteringRule<ManualFilteringRule> {
@@ -52,16 +59,49 @@ public class ManualFilteringRule extends AbstractFilteringRule<ManualFilteringRu
}
@Override
- public FilteringResult processFilteredStacks(FilteringContext context) {
+ public Object prepareCache(boolean async) {
+ if (async) {
+ LongSet all = new LongOpenHashSet();
+ List<CompletableFuture<LongSet>> completableFutures = Lists.newArrayList();
+ for (Iterable<EntryStackProvider<?>> partitionStacks : CollectionUtils.partition(ConfigObject.getInstance().getFilteredStackProviders(), 100)) {
+ completableFutures.add(CompletableFuture.supplyAsync(() -> {
+ LongSet output = new LongOpenHashSet();
+ for (EntryStackProvider<?> provider : partitionStacks) {
+ if (provider != null && provider.isValid()) {
+ output.add(EntryStacks.hashExact(provider.provide()));
+ }
+ }
+ return output;
+ }));
+ }
+ try {
+ CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).get(10, TimeUnit.SECONDS);
+ } catch (InterruptedException | ExecutionException | TimeoutException e) {
+ e.printStackTrace();
+ }
+ for (CompletableFuture<LongSet> future : completableFutures) {
+ LongSet now = future.getNow(null);
+ if (now != null) {
+ all.addAll(now);
+ }
+ }
+ return all;
+ } else {
+ return ConfigObject.getInstance().getFilteredStackProviders().stream().filter(EntryStackProvider::isValid).map(provider -> EntryStacks.hashExact(provider.provide())).collect(Collectors.toCollection(LongOpenHashSet::new));
+ }
+ }
+
+ @Override
+ public FilteringResult processFilteredStacks(FilteringContext context, FilteringCache cache, boolean async) {
+ LongSet filteredStacks = (LongSet) cache.getCache(this);
FilteringResult result = FilteringResult.create();
- processList(context.getShownStacks(), result);
- processList(context.getUnsetStacks(), result);
+ processList(context.getShownStacks(), result, async, filteredStacks);
+ processList(context.getUnsetStacks(), result, async, filteredStacks);
return result;
}
- private void processList(Collection<EntryStack<?>> stacks, FilteringResult result) {
- LongSet filteredStacks = CollectionUtils.filterAndMapParallel(ConfigObject.getInstance().getFilteredStackProviders(), EntryStackProvider::isValid, provider -> EntryStacks.hashExact(provider.provide()), LongOpenHashSet::new);
- result.hide(stacks.parallelStream().filter(stack -> filteredStacks.contains(EntryStacks.hashExact(stack))).collect(Collectors.toList()));
+ private void processList(Collection<EntryStack<?>> stacks, FilteringResult result, boolean async, LongSet filteredStacks) {
+ result.hide((async ? stacks.parallelStream() : stacks.stream()).filter(stack -> filteredStacks.contains(EntryStacks.hashExact(stack))).collect(Collectors.toList()));
}
@Override
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/SearchFilteringRule.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/SearchFilteringRule.java
index 9e00dec60..b1e9b122f 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/SearchFilteringRule.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/entry/filtering/rules/SearchFilteringRule.java
@@ -31,6 +31,7 @@ import me.shedaniel.rei.api.common.util.CollectionUtils;
import me.shedaniel.rei.impl.client.config.entries.FilteringEntry;
import me.shedaniel.rei.impl.client.config.entries.FilteringRuleOptionsScreen;
import me.shedaniel.rei.impl.client.entry.filtering.AbstractFilteringRule;
+import me.shedaniel.rei.impl.client.entry.filtering.FilteringCache;
import me.shedaniel.rei.impl.client.entry.filtering.FilteringContext;
import me.shedaniel.rei.impl.client.entry.filtering.FilteringResult;
import net.fabricmc.api.EnvType;
@@ -79,7 +80,7 @@ public class SearchFilteringRule extends AbstractFilteringRule<SearchFilteringRu
}
@Override
- public FilteringResult processFilteredStacks(FilteringContext context) {
+ public FilteringResult processFilteredStacks(FilteringContext context, FilteringCache cache, boolean async) {
List<CompletableFuture<List<EntryStack<?>>>> completableFutures = Lists.newArrayList();
processList(context.getUnsetStacks(), completableFutures);
if (show) processList(context.getHiddenStacks(), completableFutures);
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryStacksRegionWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryStacksRegionWidget.java
index c68c7bb46..509293bb9 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryStacksRegionWidget.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryStacksRegionWidget.java
@@ -237,7 +237,7 @@ public class EntryStacksRegionWidget<T extends RegionEntry<T>> extends WidgetWit
public Optional<RealRegionEntry<T>> checkDraggedStacks(DraggingContext<Screen> context, DraggableStack stack) {
EntrySerializer<?> serializer = stack.getStack().getDefinition().getSerializer();
- if (stack instanceof RegionDraggableStack || (serializer.supportReading() && serializer.supportSaving())) {
+ if (serializer != null && (stack instanceof RegionDraggableStack || (serializer.supportReading() && serializer.supportSaving()))) {
try {
T regionEntry = stack instanceof RegionDraggableStack ? ((RegionDraggableStack<T>) stack).getEntry().getEntry().copy()
: listener.convertDraggableStack(context, stack);
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 aa5e35e6a..f5b6a12ab 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
@@ -39,6 +39,7 @@ import org.apache.commons.lang3.mutable.Mutable;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
+import java.util.ConcurrentModificationException;
import java.util.Locale;
@ApiStatus.Internal
@@ -71,18 +72,29 @@ public final class TooltipArgumentType extends ArgumentType<Unit, String> {
@Override
public boolean matches(Mutable<String> data, EntryStack<?> stack, String searchText, Unit filterData) {
if (data.getValue() == null) {
- data.setValue(tryGetEntryStackTooltip(stack).toLowerCase(Locale.ROOT));
+ String tooltip = tryGetEntryStackTooltip(stack, 0);
+ if (tooltip == null) return false;
+ data.setValue(tooltip.toLowerCase(Locale.ROOT));
}
String tooltip = data.getValue();
return tooltip.isEmpty() || tooltip.contains(searchText);
}
- public static String tryGetEntryStackTooltip(EntryStack<?> stack) {
- Tooltip tooltip = stack.getTooltip(new Point(), false);
- if (tooltip != null) {
- return CollectionUtils.mapAndJoinToString(tooltip.getText(), Component::getString, "\n");
+ @Nullable
+ public static String tryGetEntryStackTooltip(EntryStack<?> stack, int attempt) {
+ try {
+ Tooltip tooltip = stack.getTooltip(new Point(), false);
+ if (tooltip != null) {
+ return CollectionUtils.mapAndJoinToString(tooltip.getText(), Component::getString, "\n");
+ }
+ return "";
+ } catch (ConcurrentModificationException ignored) {
+ // yes, this is a hack
+ if (attempt < 10) {
+ return tryGetEntryStackTooltip(stack, attempt + 1);
+ }
+ return null;
}
- return "";
}
@Override
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 1c6e9d67f..346cbb8ea 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
@@ -40,6 +40,7 @@ import me.shedaniel.rei.api.common.util.CollectionUtils;
import me.shedaniel.rei.api.common.util.EntryStacks;
import me.shedaniel.rei.impl.client.REIRuntimeImpl;
import me.shedaniel.rei.impl.client.config.ConfigObjectImpl;
+import me.shedaniel.rei.impl.client.entry.filtering.FilteringCacheImpl;
import me.shedaniel.rei.impl.client.entry.filtering.FilteringContextImpl;
import me.shedaniel.rei.impl.client.entry.filtering.FilteringContextType;
import me.shedaniel.rei.impl.client.entry.filtering.FilteringRule;
@@ -65,6 +66,7 @@ import java.util.stream.Stream;
@ApiStatus.Internal
@Environment(EnvType.CLIENT)
public class EntryRegistryImpl implements EntryRegistry {
+ public List<Runnable> refilterListener = Lists.newCopyOnWriteArrayList();
private List<EntryStack<?>> preFilteredList = Lists.newCopyOnWriteArrayList();
private List<EntryStack<?>> entries = Lists.newCopyOnWriteArrayList();
@Nullable
@@ -83,6 +85,7 @@ public class EntryRegistryImpl implements EntryRegistry {
@Override
public void startReload() {
+ refilterListener.clear();
entries = Lists.newCopyOnWriteArrayList();
reloadingRegistry = Lists.newArrayListWithCapacity(Registry.ITEM.keySet().size() + 100);
preFilteredList = Lists.newCopyOnWriteArrayList();
@@ -131,12 +134,14 @@ public class EntryRegistryImpl implements EntryRegistry {
Stopwatch stopwatch = Stopwatch.createStarted();
FilteringContextImpl context = new FilteringContextImpl(entries);
+ FilteringCacheImpl cache = new FilteringCacheImpl();
List<FilteringRule<?>> rules = ((ConfigObjectImpl) ConfigObject.getInstance()).getFilteringRules();
Stopwatch innerStopwatch = Stopwatch.createStarted();
for (int i = rules.size() - 1; i >= 0; i--) {
innerStopwatch.reset().start();
FilteringRule<?> rule = rules.get(i);
- context.handleResult(rule.processFilteredStacks(context));
+ cache.setCache(rule, rule.prepareCache(true));
+ context.handleResult(rule.processFilteredStacks(context, cache, true));
RoughlyEnoughItemsCore.LOGGER.debug("Refiltered rule [%s] in %s.", FilteringRule.REGISTRY.getKey(rule).toString(), innerStopwatch.stop().toString());
}
@@ -152,6 +157,10 @@ public class EntryRegistryImpl implements EntryRegistry {
}
RoughlyEnoughItemsCore.LOGGER.debug("Refiltered %d entries with %d rules in %s.", entries.size() - preFilteredList.size(), rules.size(), stopwatch.stop().toString());
+
+ for (Runnable runnable : refilterListener) {
+ runnable.run();
+ }
}
private static <T> Predicate<T> not(Predicate<? super T> target) {
@@ -224,10 +233,12 @@ public class EntryRegistryImpl implements EntryRegistry {
}
FilteringContextImpl context = new FilteringContextImpl(entries);
+ FilteringCacheImpl cache = new FilteringCacheImpl();
List<FilteringRule<?>> rules = ((ConfigObjectImpl) ConfigObject.getInstance()).getFilteringRules();
for (int i = rules.size() - 1; i >= 0; i--) {
FilteringRule<?> rule = rules.get(i);
- context.handleResult(rule.processFilteredStacks(context));
+ cache.setCache(rule, rule.prepareCache(true));
+ context.handleResult(rule.processFilteredStacks(context, cache, true));
}
Set<HashedEntryStackWrapper> hiddenStacks = context.stacks.get(FilteringContextType.HIDDEN);
diff --git a/runtime/src/main/java/me/shedaniel/rei/plugin/client/FilteredStacksVisibilityHandler.java b/runtime/src/main/java/me/shedaniel/rei/plugin/client/FilteredStacksVisibilityHandler.java
new file mode 100644
index 000000000..50bc8f277
--- /dev/null
+++ b/runtime/src/main/java/me/shedaniel/rei/plugin/client/FilteredStacksVisibilityHandler.java
@@ -0,0 +1,116 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020, 2021 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;
+
+import com.google.common.base.Stopwatch;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanMap;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanMaps;
+import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap;
+import me.shedaniel.architectury.event.EventResult;
+import me.shedaniel.rei.RoughlyEnoughItemsCore;
+import me.shedaniel.rei.api.client.config.ConfigObject;
+import me.shedaniel.rei.api.client.registry.display.DisplayCategory;
+import me.shedaniel.rei.api.client.registry.display.DisplayRegistry;
+import me.shedaniel.rei.api.client.registry.display.visibility.DisplayVisibilityPredicate;
+import me.shedaniel.rei.api.common.display.Display;
+import me.shedaniel.rei.api.common.entry.EntryIngredient;
+import me.shedaniel.rei.impl.client.config.ConfigObjectImpl;
+import me.shedaniel.rei.impl.client.entry.filtering.*;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+public class FilteredStacksVisibilityHandler implements DisplayVisibilityPredicate {
+ private boolean checkHiddenStacks;
+ private Reference2BooleanMap<Display> visible = Reference2BooleanMaps.synchronize(new Reference2BooleanOpenHashMap<>());
+ private List<FilteringRule<?>> filteringRules;
+ private FilteringCacheImpl cache;
+ private final Predicate<Display> displayPredicate = this::checkHiddenStacks;
+
+ @Override
+ public EventResult handleDisplay(DisplayCategory<?> category, Display display) {
+ if (checkHiddenStacks) {
+ return visible.computeBooleanIfAbsent(display, displayPredicate) ? EventResult.pass() : EventResult.interruptFalse();
+ }
+
+ return EventResult.pass();
+ }
+
+ public void reset() {
+ checkHiddenStacks = ConfigObject.getInstance().shouldFilterDisplays();
+ visible = Reference2BooleanMaps.synchronize(new Reference2BooleanOpenHashMap<>());
+
+ if (checkHiddenStacks) {
+ filteringRules = ((ConfigObjectImpl) ConfigObject.getInstance()).getFilteringRules();
+ cache = new FilteringCacheImpl();
+ for (int i = filteringRules.size() - 1; i >= 0; i--) {
+ FilteringRule<?> rule = filteringRules.get(i);
+ cache.setCache(rule, rule.prepareCache(false));
+ }
+
+ cacheExisting();
+ } else {
+ filteringRules = null;
+ cache = null;
+ }
+ }
+
+ public void cacheExisting() {
+ Stopwatch stopwatch = Stopwatch.createStarted();
+ DisplayRegistry.getInstance().getAll().values().parallelStream().map(displays -> {
+ Reference2BooleanMap<Display> current = new Reference2BooleanOpenHashMap<>();
+ for (Display display : displays) {
+ current.put(display, checkHiddenStacks(display));
+ }
+ return current;
+ }).forEach(map -> {
+ visible.putAll(map);
+ });
+ RoughlyEnoughItemsCore.LOGGER.debug("Computed existing filtered displays with %d rules in %s", filteringRules.size(), stopwatch.stop());
+ }
+
+ private boolean checkHiddenStacks(Display display) {
+ for (EntryIngredient ingredient : display.getInputEntries()) {
+ if (isEntryIngredientAllHidden(ingredient, cache, filteringRules)) {
+ return false;
+ }
+ }
+ for (EntryIngredient ingredient : display.getOutputEntries()) {
+ if (isEntryIngredientAllHidden(ingredient, cache, filteringRules)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static boolean isEntryIngredientAllHidden(EntryIngredient ingredient, FilteringCache cache, List<FilteringRule<?>> rules) {
+ FilteringContextImpl context = new FilteringContextImpl(false, ingredient);
+ for (int i = rules.size() - 1; i >= 0; i--) {
+ FilteringRule<?> rule = rules.get(i);
+ context.handleResult(rule.processFilteredStacks(context, cache, false));
+ }
+ return context.stacks.get(FilteringContextType.SHOWN).isEmpty() && context.stacks.get(FilteringContextType.DEFAULT).isEmpty();
+ }
+}
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 3a174ffa4..260da518c 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
@@ -44,6 +44,7 @@ import me.shedaniel.rei.api.client.gui.widgets.Panel;
import me.shedaniel.rei.api.client.gui.widgets.Tooltip;
import me.shedaniel.rei.api.client.gui.widgets.Widgets;
import me.shedaniel.rei.api.client.plugins.REIClientPlugin;
+import me.shedaniel.rei.api.client.registry.display.DisplayRegistry;
import me.shedaniel.rei.api.client.registry.entry.Entr