aboutsummaryrefslogtreecommitdiff
path: root/runtime/src/main/java
diff options
context:
space:
mode:
authorshedaniel <daniel@shedaniel.me>2024-09-05 02:16:54 +0900
committershedaniel <daniel@shedaniel.me>2024-09-05 02:16:54 +0900
commit048eccb27ce9907b1028b7acc4f0f98321864750 (patch)
treeb47f508b1e82144c8daae10d60cd6cd888228426 /runtime/src/main/java
parent90af6e03bcf45b0c13d5b7af51126bd2fc7389a5 (diff)
parent8442364d7da83cda185dee99f0c980a46df55448 (diff)
downloadRoughlyEnoughItems-048eccb27ce9907b1028b7acc4f0f98321864750.tar.gz
RoughlyEnoughItems-048eccb27ce9907b1028b7acc4f0f98321864750.tar.bz2
RoughlyEnoughItems-048eccb27ce9907b1028b7acc4f0f98321864750.zip
Merge remote-tracking branch 'origin/14.x-1.20.4' into 15.x-1.20.5
Diffstat (limited to 'runtime/src/main/java')
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilterCalculator.java175
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/EntryListSearchManager.java12
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/search/AsyncSearchManager.java4
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java141
4 files changed, 189 insertions, 143 deletions
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilterCalculator.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilterCalculator.java
new file mode 100644
index 000000000..b23cc18bf
--- /dev/null
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilterCalculator.java
@@ -0,0 +1,175 @@
+/*
+ * 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.gui.craftable;
+
+import com.google.common.base.Suppliers;
+import it.unimi.dsi.fastutil.longs.*;
+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
+import me.shedaniel.rei.api.client.REIRuntime;
+import me.shedaniel.rei.api.client.registry.display.DisplayRegistry;
+import me.shedaniel.rei.api.client.registry.transfer.TransferHandler;
+import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerMeta;
+import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry;
+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.entry.comparison.ComparisonContext;
+import me.shedaniel.rei.api.common.entry.type.EntryDefinition;
+import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
+import me.shedaniel.rei.api.common.util.EntryStacks;
+import me.shedaniel.rei.impl.client.registry.display.DisplayCache;
+import me.shedaniel.rei.impl.client.registry.display.DisplayRegistryImpl;
+import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper;
+import me.shedaniel.rei.plugin.autocrafting.DefaultCategoryHandler;
+import net.minecraft.world.item.ItemStack;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+public class CraftableFilterCalculator implements Predicate<HashedEntryStackWrapper> {
+ private final Supplier<DisplayCache> displayCache = Suppliers.memoize(() -> ((DisplayRegistryImpl) DisplayRegistry.getInstance()).displaysHolder().cache());
+ private Set<Display> checkedCraftableDisplays = Collections.synchronizedSet(new ReferenceOpenHashSet<>());
+ private Set<Display> checkedUncraftableDisplays = Collections.synchronizedSet(new ReferenceOpenHashSet<>());
+ private LongSet checkedCraftableEntries = LongSets.synchronize(new LongOpenHashSet());
+
+ @Override
+ public boolean test(HashedEntryStackWrapper wrapper) {
+ EntryStack<?> stack = wrapper.unwrap();
+ if (stack.getType() != VanillaEntryTypes.ITEM || stack.isEmpty()) return false;
+ if (checkedCraftableEntries.contains(wrapper.hashExact())) return true;
+ DisplayCache cache = this.displayCache.get();
+ for (Display display : cache.getAllDisplaysByOutputs(List.of(stack))) {
+ if (checkCraftableCachedByResult(display)) return true;
+ }
+ return false;
+ }
+
+ private boolean checkCraftableCachedByDisplay(Display display) {
+ if (checkedCraftableDisplays.contains(display)) return true;
+ if (checkedUncraftableDisplays.contains(display)) return false;
+ boolean checkCraftable = checkCraftable(display);
+ if (checkCraftable) {
+ checkedCraftableDisplays.add(display);
+ } else {
+ checkedUncraftableDisplays.add(display);
+ }
+ return checkCraftable;
+ }
+
+ private boolean checkCraftableCachedByResult(Display display) {
+ boolean checkCraftable = checkCraftableCachedByDisplay(display);
+ if (checkCraftable) {
+ for (EntryIngredient ingredient : display.getOutputEntries()) {
+ for (EntryStack<?> stack : ingredient) {
+ if (stack.getType() != VanillaEntryTypes.ITEM || stack.isEmpty()) continue;
+ checkedCraftableEntries.add(EntryStacks.hashExact(stack));
+ }
+ }
+ }
+ return checkCraftable;
+ }
+
+ private boolean checkCraftable(Display display) {
+ @Nullable Long2LongMap ingredients = chooseHandler(display);
+ if (ingredients == null) {
+ return false;
+ }
+
+ List<EntryIngredient> requiredEntries = display.getRequiredEntries();
+ if (requiredEntries.isEmpty()) {
+ return false;
+ }
+
+ int slotsCraftable = 0;
+ boolean containsNonEmpty = false;
+
+ for (EntryIngredient slot : requiredEntries) {
+ if (slot.isEmpty()) {
+ slotsCraftable++;
+ continue;
+ }
+ for (EntryStack<?> slotPossible : slot) {
+ if (slotPossible.getType() != VanillaEntryTypes.ITEM) continue;
+ ItemStack stack = slotPossible.castValue();
+ long hashFuzzy = EntryStacks.hashFuzzy(slotPossible);
+ long availableAmount = ingredients.get(hashFuzzy);
+ if (availableAmount >= stack.getCount()) {
+ ingredients.put(hashFuzzy, availableAmount - stack.getCount());
+ containsNonEmpty = true;
+ slotsCraftable++;
+ break;
+ }
+ }
+ }
+
+ return slotsCraftable == requiredEntries.size() && containsNonEmpty;
+ }
+
+ @Nullable
+ public Long2LongMap chooseHandler(Display display) {
+ TransferHandler.Context transferContext = TransferHandler.Context.create(false, false, REIRuntime.getInstance().getPreviousContainerScreen(), display);
+ DefaultCategoryHandler legacyHandler = null;
+ for (TransferHandler handler : TransferHandlerRegistry.getInstance()) {
+ if (handler instanceof DefaultCategoryHandler) {
+ legacyHandler = (DefaultCategoryHandler) handler;
+ } else {
+ TransferHandler.ApplicabilityResult result = handler.checkApplicable(transferContext);
+ if (result.isSuccessful()) {
+ if (handler instanceof TransferHandlerMeta) {
+ return extractIngredients(((TransferHandlerMeta) handler).getAvailableIngredients(transferContext));
+ } else {
+ return CraftableFilter.INSTANCE.getInvStacks();
+ }
+ }
+ }
+ }
+
+ if (legacyHandler != null) {
+ TransferHandler.ApplicabilityResult result = legacyHandler.checkApplicable(transferContext);
+ if (result.isSuccessful()) {
+ return CraftableFilter.INSTANCE.getInvStacks();
+ }
+ }
+
+ return null;
+ }
+
+ private static Long2LongMap extractIngredients(Iterable<ItemStack> ingredients) {
+ EntryDefinition<ItemStack> definition = VanillaEntryTypes.ITEM.getDefinition();
+
+ Long2LongMap map = new Long2LongOpenHashMap();
+ for (ItemStack stack : ingredients) {
+ if (!stack.isEmpty()) {
+ long hash = definition.hash(null, stack, ComparisonContext.FUZZY);
+ long newCount = map.getOrDefault(hash, 0) + Math.max(0, stack.getCount());
+ map.put(hash, newCount);
+ }
+ }
+ return map;
+ }
+}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/EntryListSearchManager.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/EntryListSearchManager.java
index 3d262b001..78ee96b1a 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/EntryListSearchManager.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/EntryListSearchManager.java
@@ -23,10 +23,9 @@
package me.shedaniel.rei.impl.client.gui.widget.entrylist;
+import com.google.common.base.Predicates;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Iterators;
-import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
-import it.unimi.dsi.fastutil.longs.LongSet;
import me.shedaniel.rei.api.client.config.ConfigManager;
import me.shedaniel.rei.api.client.config.ConfigObject;
import me.shedaniel.rei.api.client.gui.config.EntryPanelOrdering;
@@ -39,6 +38,7 @@ import me.shedaniel.rei.api.common.util.EntryStacks;
import me.shedaniel.rei.impl.client.config.collapsible.CollapsibleConfigManager;
import me.shedaniel.rei.impl.client.search.AsyncSearchManager;
import me.shedaniel.rei.impl.client.search.collapsed.CollapsedEntriesCache;
+import me.shedaniel.rei.impl.client.view.ViewsImpl;
import me.shedaniel.rei.impl.common.InternalLogger;
import me.shedaniel.rei.impl.common.entry.type.EntryRegistryImpl;
import me.shedaniel.rei.impl.common.entry.type.collapsed.CollapsedStack;
@@ -62,13 +62,7 @@ public class EntryListSearchManager {
private final AsyncSearchManager searchManager = new AsyncSearchManager(EntryListSearchManager::getAllEntriesContextually, () -> {
boolean checkCraftable = ConfigManager.getInstance().isCraftableOnlyEnabled();
- LongSet workingItems = checkCraftable ? new LongOpenHashSet() : null;
- if (checkCraftable) {
- for (EntryStack<?> stack : Views.getInstance().findCraftableEntriesByMaterials()) {
- workingItems.add(EntryStacks.hashExact(stack));
- }
- }
- return checkCraftable ? stack -> workingItems.contains(stack.hashExact()) : stack -> true;
+ return checkCraftable ? ((ViewsImpl) Views.getInstance()).getCraftableEntriesPredicate() : Predicates.alwaysTrue();
}, HashedEntryStackWrapper::normalize);
private static List<HNEntryStackWrapper> getAllEntriesContextually(SearchFilter filter) {
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 381831c42..2d9f1c870 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
@@ -124,6 +124,10 @@ public class AsyncSearchManager {
.thenApply(entry -> {
this.last = entry;
return entry;
+ })
+ .exceptionally(throwable -> {
+ InternalLogger.getInstance().error("Error while searching", throwable);
+ return new AbstractMap.SimpleImmutableEntry<>(List.of(), filter);
});
}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java
index 79d27d9fd..2d2e6fada 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java
@@ -23,23 +23,18 @@
package me.shedaniel.rei.impl.client.view;
+import com.google.common.base.Predicates;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
-import it.unimi.dsi.fastutil.longs.Long2LongMap;
-import it.unimi.dsi.fastutil.longs.Long2LongMaps;
-import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
-import me.shedaniel.rei.api.client.REIRuntime;
import me.shedaniel.rei.api.client.config.ConfigObject;
import me.shedaniel.rei.api.client.registry.category.CategoryRegistry;
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.DynamicDisplayGenerator;
-import me.shedaniel.rei.api.client.registry.transfer.TransferHandler;
-import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry;
import me.shedaniel.rei.api.client.view.ViewSearchBuilder;
import me.shedaniel.rei.api.client.view.Views;
import me.shedaniel.rei.api.common.category.CategoryIdentifier;
@@ -47,40 +42,28 @@ import me.shedaniel.rei.api.common.display.Display;
import me.shedaniel.rei.api.common.display.DisplayMerger;
import me.shedaniel.rei.api.common.entry.EntryIngredient;
import me.shedaniel.rei.api.common.entry.EntryStack;
-import me.shedaniel.rei.api.common.entry.comparison.ComparisonContext;
-import me.shedaniel.rei.api.common.entry.type.EntryDefinition;
-import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
import me.shedaniel.rei.api.common.plugins.PluginManager;
-import me.shedaniel.rei.api.common.transfer.info.MenuInfo;
-import me.shedaniel.rei.api.common.transfer.info.MenuInfoRegistry;
-import me.shedaniel.rei.api.common.transfer.info.MenuSerializationContext;
-import me.shedaniel.rei.api.common.transfer.info.stack.SlotAccessor;
import me.shedaniel.rei.api.common.util.CollectionUtils;
import me.shedaniel.rei.api.common.util.EntryIngredients;
import me.shedaniel.rei.api.common.util.EntryStacks;
-import me.shedaniel.rei.impl.client.gui.craftable.CraftableFilter;
+import me.shedaniel.rei.impl.client.gui.craftable.CraftableFilterCalculator;
import me.shedaniel.rei.impl.client.gui.widget.AutoCraftingEvaluator;
import me.shedaniel.rei.impl.client.registry.display.DisplayRegistryImpl;
import me.shedaniel.rei.impl.client.registry.display.DisplaysHolder;
import me.shedaniel.rei.impl.client.util.CrashReportUtils;
import me.shedaniel.rei.impl.common.InternalLogger;
+import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper;
import me.shedaniel.rei.impl.display.DisplaySpec;
-import me.shedaniel.rei.plugin.autocrafting.DefaultCategoryHandler;
import net.minecraft.CrashReport;
import net.minecraft.ReportedException;
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
-import net.minecraft.client.player.LocalPlayer;
import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.inventory.AbstractContainerMenu;
-import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
-import java.util.stream.Collectors;
+import java.util.function.Predicate;
@ApiStatus.Internal
public class ViewsImpl implements Views {
@@ -411,122 +394,12 @@ public class ViewsImpl implements Views {
};
}
- @Override
- public Collection<EntryStack<?>> findCraftableEntriesByMaterials() {
+ public Predicate<HashedEntryStackWrapper> getCraftableEntriesPredicate() {
if (PluginManager.areAnyReloading()) {
- return Collections.emptySet();
- }
-
- AbstractContainerMenu menu = Minecraft.getInstance().player.containerMenu;
- Set<EntryStack<?>> craftables = new HashSet<>();
- AbstractContainerScreen<?> containerScreen = REIRuntime.getInstance().getPreviousContainerScreen();
-
- for (Map.Entry<CategoryIdentifier<?>, List<Display>> entry : DisplayRegistry.getInstance().getAll().entrySet()) {
- MenuSerializationContext<AbstractContainerMenu, LocalPlayer, Display> context = createLegacyContext(menu, entry.getKey().cast());
-
- List<Display> displays = entry.getValue();
- for (Display display : displays) {
- try {
- TransferHandler.Context transferContext = TransferHandler.Context.create(false, false, containerScreen, display);
- boolean successful = matchesLegacyRequirements(menu, context, display);
-
- if (!successful) {
- for (TransferHandler handler : TransferHandlerRegistry.getInstance()) {
- if (!(handler instanceof DefaultCategoryHandler) && handler.checkApplicable(transferContext).isSuccessful()) {
- if (handler.handle(transferContext).isSuccessful()) {
- successful = true;
- break;
- }
- }
- }
- }
-
- if (!successful) continue;
-
- display.getOutputEntries().stream().flatMap(Collection::stream)
- .collect(Collectors.toCollection(() -> craftables));
- } catch (Throwable t) {
- InternalLogger.getInstance().warn("Error while checking if display is craftable", t);
- }
- }
- }
- return craftables;
- }
-
- private static MenuSerializationContext<AbstractContainerMenu, LocalPlayer, Display> createLegacyContext(AbstractContainerMenu menu, CategoryIdentifier<Display> categoryIdentifier) {
- class InfoSerializationContext implements MenuSerializationContext<AbstractContainerMenu, LocalPlayer, Display> {
- @Override
- public AbstractContainerMenu getMenu() {
- return menu;
- }
-
- @Override
- public LocalPlayer getPlayerEntity() {
- return Minecraft.getInstance().player;
- }
-
- @Override
- public CategoryIdentifier<Display> getCategoryIdentifier() {
- return categoryIdentifier;
- }
- }
-
- return new InfoSerializationContext();
- }
-
- private static boolean matchesLegacyRequirements(AbstractContainerMenu menu,
- MenuSerializationContext<AbstractContainerMenu, LocalPlayer, Display> context,
- Display display) {
- MenuInfo<AbstractContainerMenu, Display> info = menu != null ?
- MenuInfoRegistry.getInstance().getClient(display, context, menu)
- : null;
-
- if (menu != null && info == null) {
- return false;
- }
-
- Iterable<SlotAccessor> inputSlots = info != null ? Iterables.concat(info.getInputSlots(context.withDisplay(display)), info.getInventorySlots(context.withDisplay(display))) : Collections.emptySet();
- int slotsCraftable = 0;
- boolean containsNonEmpty = false;
- List<EntryIngredient> requiredInput = display.getRequiredEntries();
- Long2LongMap invCount = new Long2LongOpenHashMap(info == null ? CraftableFilter.INSTANCE.getInvStacks() : Long2LongMaps.EMPTY_MAP);
- for (SlotAccessor inputSlot : inputSlots) {
- ItemStack stack = inputSlot.getItemStack();
-
- EntryDefinition<ItemStack> definition;
- try {
- definition = VanillaEntryTypes.ITEM.getDefinition();
- } catch (NullPointerException e) {
- break;
- }
-
- if (!stack.isEmpty()) {
- long hash = definition.hash(null, stack, ComparisonContext.FUZZY);
- long newCount = invCount.get(hash) + Math.max(0, stack.getCount());
- invCount.put(hash, newCount);
- }
- }
-
- for (EntryIngredient slot : requiredInput) {
- if (slot.isEmpty()) {
- slotsCraftable++;
- continue;
- }
- for (EntryStack<?> slotPossible : slot) {
- if (slotPossible.getType() != VanillaEntryTypes.ITEM) continue;
- ItemStack stack = slotPossible.castValue();
- long hashFuzzy = EntryStacks.hashFuzzy(slotPossible);
- long availableAmount = invCount.get(hashFuzzy);
- if (availableAmount >= stack.getCount()) {
- invCount.put(hashFuzzy, availableAmount - stack.getCount());
- containsNonEmpty = true;
- slotsCraftable++;
- break;
- }
- }
+ return Predicates.alwaysTrue();
}
- return slotsCraftable == display.getRequiredEntries().size() && containsNonEmpty;
+ return new CraftableFilterCalculator();
}
private static <T> boolean isStackWorkStationOfCategory(CategoryRegistry.CategoryConfiguration<?> category, EntryStack<T> stack) {