Skip to content

Commit 7b5ed40

Browse files
committed
Merge remote-tracking branch 'origin/9.x-1.19' into 11.x-1.19.4
# Conflicts: # runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/EntryListSearchManager.java
2 parents 6da90a5 + 3cf87de commit 7b5ed40

File tree

8 files changed

+261
-146
lines changed

8 files changed

+261
-146
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* This file is licensed under the MIT License, part of Roughly Enough Items.
3+
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in all
13+
* copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
* SOFTWARE.
22+
*/
23+
24+
package me.shedaniel.rei.api.client.registry.transfer;
25+
26+
import net.minecraft.world.item.ItemStack;
27+
import org.jetbrains.annotations.ApiStatus;
28+
29+
import java.util.List;
30+
31+
/**
32+
* A meta interface for {@link TransferHandler}.
33+
*/
34+
@ApiStatus.Experimental
35+
public interface TransferHandlerMeta {
36+
/**
37+
* Returns the available ingredients for the transfer handler.
38+
* This is used in the craftable filter, and the quick transfer tooltip to quickly determine if the handler can handle the recipe.
39+
* <p>
40+
* If this interface is not implemented, REI will assume stacks only from the player's inventory are available.
41+
*
42+
* @param context the context
43+
* @return the available ingredients
44+
*/
45+
default Iterable<ItemStack> getAvailableIngredients(TransferHandler.Context context) {
46+
return List.of();
47+
}
48+
}

api/src/main/java/me/shedaniel/rei/api/client/registry/transfer/simple/SimpleTransferHandler.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,17 @@
2323

2424
package me.shedaniel.rei.api.client.registry.transfer.simple;
2525

26+
import com.google.common.collect.Iterables;
2627
import com.mojang.blaze3d.vertex.PoseStack;
2728
import it.unimi.dsi.fastutil.ints.IntSet;
2829
import me.shedaniel.math.Rectangle;
2930
import me.shedaniel.rei.api.client.gui.widgets.Slot;
3031
import me.shedaniel.rei.api.client.gui.widgets.Widget;
3132
import me.shedaniel.rei.api.client.registry.transfer.TransferHandler;
33+
import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerMeta;
3234
import me.shedaniel.rei.api.common.category.CategoryIdentifier;
3335
import me.shedaniel.rei.api.common.display.Display;
36+
import me.shedaniel.rei.api.common.entry.EntryStack;
3437
import me.shedaniel.rei.api.common.entry.InputIngredient;
3538
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
3639
import me.shedaniel.rei.api.common.transfer.info.stack.SlotAccessor;
@@ -44,13 +47,14 @@
4447
import net.minecraft.world.item.ItemStack;
4548
import org.jetbrains.annotations.ApiStatus;
4649

50+
import java.util.Collection;
4751
import java.util.Collections;
4852
import java.util.List;
4953
import java.util.stream.Collectors;
5054
import java.util.stream.IntStream;
5155

5256
@ApiStatus.Experimental
53-
public interface SimpleTransferHandler extends TransferHandler {
57+
public interface SimpleTransferHandler extends TransferHandler, TransferHandlerMeta {
5458
static <C extends AbstractContainerMenu, D extends Display> SimpleTransferHandler create(Class<? extends C> containerClass,
5559
CategoryIdentifier<D> categoryIdentifier,
5660
IntRange inputSlots) {
@@ -147,6 +151,11 @@ default List<InputIngredient<ItemStack>> getInputsIndexed(Context context) {
147151
InputIngredient.withType(entry, VanillaEntryTypes.ITEM));
148152
}
149153

154+
@Override
155+
default Iterable<ItemStack> getAvailableIngredients(Context context) {
156+
return Iterables.transform(Iterables.concat(getInputSlots(context), getInventorySlots(context)), SlotAccessor::getItemStack);
157+
}
158+
150159
/**
151160
* Renders the missing ingredients of the transfer.
152161
*

api/src/main/java/me/shedaniel/rei/api/client/view/Views.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.jetbrains.annotations.Nullable;
3232

3333
import java.util.Collection;
34+
import java.util.List;
3435

3536
public interface Views extends Reloadable<REIClientPlugin> {
3637
/**
@@ -49,6 +50,10 @@ static Views getInstance() {
4950
* Returns all craftable items from materials.
5051
*
5152
* @return the list of craftable entries
53+
* @deprecated This is no longer exposed in the API due to the lack of use cases and how this API is very specific to a type of implementation.
5254
*/
53-
Collection<EntryStack<?>> findCraftableEntriesByMaterials();
55+
@Deprecated(forRemoval = true)
56+
default Collection<EntryStack<?>> findCraftableEntriesByMaterials() {
57+
return List.of();
58+
}
5459
}

default-plugin/src/main/java/me/shedaniel/rei/plugin/autocrafting/InventoryCraftingTransferHandler.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,19 @@
2424
package me.shedaniel.rei.plugin.autocrafting;
2525

2626
import me.shedaniel.rei.api.client.registry.transfer.TransferHandler;
27+
import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerMeta;
2728
import me.shedaniel.rei.api.client.registry.transfer.simple.SimpleTransferHandler;
2829
import me.shedaniel.rei.api.common.entry.EntryStack;
2930
import me.shedaniel.rei.api.common.entry.InputIngredient;
3031
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
3132
import me.shedaniel.rei.api.common.util.CollectionUtils;
3233
import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCraftingDisplay;
3334
import net.minecraft.network.chat.Component;
35+
import net.minecraft.world.item.ItemStack;
3436

3537
import java.util.List;
3638

37-
public class InventoryCraftingTransferHandler implements TransferHandler {
39+
public class InventoryCraftingTransferHandler implements TransferHandler, TransferHandlerMeta {
3840
private final SimpleTransferHandler parent;
3941

4042
public InventoryCraftingTransferHandler(SimpleTransferHandler parent) {
@@ -61,4 +63,9 @@ public Result handle(Context context) {
6163
CollectionUtils.map(inputs, entry -> InputIngredient.withType(entry, VanillaEntryTypes.ITEM)),
6264
parent.getInputSlots(context), parent.getInventorySlots(context));
6365
}
66+
67+
@Override
68+
public Iterable<ItemStack> getAvailableIngredients(Context context) {
69+
return parent.getAvailableIngredients(context);
70+
}
6471
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* This file is licensed under the MIT License, part of Roughly Enough Items.
3+
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in all
13+
* copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
* SOFTWARE.
22+
*/
23+
24+
package me.shedaniel.rei.impl.client.gui.craftable;
25+
26+
import com.google.common.base.Suppliers;
27+
import it.unimi.dsi.fastutil.longs.*;
28+
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
29+
import me.shedaniel.rei.api.client.REIRuntime;
30+
import me.shedaniel.rei.api.client.registry.display.DisplayRegistry;
31+
import me.shedaniel.rei.api.client.registry.transfer.TransferHandler;
32+
import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerMeta;
33+
import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry;
34+
import me.shedaniel.rei.api.common.display.Display;
35+
import me.shedaniel.rei.api.common.entry.EntryIngredient;
36+
import me.shedaniel.rei.api.common.entry.EntryStack;
37+
import me.shedaniel.rei.api.common.entry.comparison.ComparisonContext;
38+
import me.shedaniel.rei.api.common.entry.type.EntryDefinition;
39+
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
40+
import me.shedaniel.rei.api.common.util.EntryStacks;
41+
import me.shedaniel.rei.impl.client.registry.display.DisplayCache;
42+
import me.shedaniel.rei.impl.client.registry.display.DisplayRegistryImpl;
43+
import me.shedaniel.rei.impl.common.util.HashedEntryStackWrapper;
44+
import me.shedaniel.rei.plugin.autocrafting.DefaultCategoryHandler;
45+
import net.minecraft.world.item.ItemStack;
46+
import org.jetbrains.annotations.Nullable;
47+
48+
import java.util.Collections;
49+
import java.util.List;
50+
import java.util.Set;
51+
import java.util.function.Predicate;
52+
import java.util.function.Supplier;
53+
54+
public class CraftableFilterCalculator implements Predicate<HashedEntryStackWrapper> {
55+
private final Supplier<DisplayCache> displayCache = Suppliers.memoize(() -> ((DisplayRegistryImpl) DisplayRegistry.getInstance()).displaysHolder().cache());
56+
private Set<Display> checkedCraftableDisplays = Collections.synchronizedSet(new ReferenceOpenHashSet<>());
57+
private Set<Display> checkedUncraftableDisplays = Collections.synchronizedSet(new ReferenceOpenHashSet<>());
58+
private LongSet checkedCraftableEntries = LongSets.synchronize(new LongOpenHashSet());
59+
60+
@Override
61+
public boolean test(HashedEntryStackWrapper wrapper) {
62+
EntryStack<?> stack = wrapper.unwrap();
63+
if (stack.getType() != VanillaEntryTypes.ITEM || stack.isEmpty()) return false;
64+
if (checkedCraftableEntries.contains(wrapper.hashExact())) return true;
65+
DisplayCache cache = this.displayCache.get();
66+
for (Display display : cache.getAllDisplaysByOutputs(List.of(stack))) {
67+
if (checkCraftableCachedByResult(display)) return true;
68+
}
69+
return false;
70+
}
71+
72+
private boolean checkCraftableCachedByDisplay(Display display) {
73+
if (checkedCraftableDisplays.contains(display)) return true;
74+
if (checkedUncraftableDisplays.contains(display)) return false;
75+
boolean checkCraftable = checkCraftable(display);
76+
if (checkCraftable) {
77+
checkedCraftableDisplays.add(display);
78+
} else {
79+
checkedUncraftableDisplays.add(display);
80+
}
81+
return checkCraftable;
82+
}
83+
84+
private boolean checkCraftableCachedByResult(Display display) {
85+
boolean checkCraftable = checkCraftableCachedByDisplay(display);
86+
if (checkCraftable) {
87+
for (EntryIngredient ingredient : display.getOutputEntries()) {
88+
for (EntryStack<?> stack : ingredient) {
89+
if (stack.getType() != VanillaEntryTypes.ITEM || stack.isEmpty()) continue;
90+
checkedCraftableEntries.add(EntryStacks.hashExact(stack));
91+
}
92+
}
93+
}
94+
return checkCraftable;
95+
}
96+
97+
private boolean checkCraftable(Display display) {
98+
@Nullable Long2LongMap ingredients = chooseHandler(display);
99+
if (ingredients == null) {
100+
return false;
101+
}
102+
103+
List<EntryIngredient> requiredEntries = display.getRequiredEntries();
104+
if (requiredEntries.isEmpty()) {
105+
return false;
106+
}
107+
108+
int slotsCraftable = 0;
109+
boolean containsNonEmpty = false;
110+
111+
for (EntryIngredient slot : requiredEntries) {
112+
if (slot.isEmpty()) {
113+
slotsCraftable++;
114+
continue;
115+
}
116+
for (EntryStack<?> slotPossible : slot) {
117+
if (slotPossible.getType() != VanillaEntryTypes.ITEM) continue;
118+
ItemStack stack = slotPossible.castValue();
119+
long hashFuzzy = EntryStacks.hashFuzzy(slotPossible);
120+
long availableAmount = ingredients.get(hashFuzzy);
121+
if (availableAmount >= stack.getCount()) {
122+
ingredients.put(hashFuzzy, availableAmount - stack.getCount());
123+
containsNonEmpty = true;
124+
slotsCraftable++;
125+
break;
126+
}
127+
}
128+
}
129+
130+
return slotsCraftable == requiredEntries.size() && containsNonEmpty;
131+
}
132+
133+
@Nullable
134+
public Long2LongMap chooseHandler(Display display) {
135+
TransferHandler.Context transferContext = TransferHandler.Context.create(false, false, REIRuntime.getInstance().getPreviousContainerScreen(), display);
136+
DefaultCategoryHandler legacyHandler = null;
137+
for (TransferHandler handler : TransferHandlerRegistry.getInstance()) {
138+
if (handler instanceof DefaultCategoryHandler) {
139+
legacyHandler = (DefaultCategoryHandler) handler;
140+
} else {
141+
TransferHandler.ApplicabilityResult result = handler.checkApplicable(transferContext);
142+
if (result.isSuccessful()) {
143+
if (handler instanceof TransferHandlerMeta) {
144+
return extractIngredients(((TransferHandlerMeta) handler).getAvailableIngredients(transferContext));
145+
} else {
146+
return CraftableFilter.INSTANCE.getInvStacks();
147+
}
148+
}
149+
}
150+
}
151+
152+
if (legacyHandler != null) {
153+
TransferHandler.ApplicabilityResult result = legacyHandler.checkApplicable(transferContext);
154+
if (result.isSuccessful()) {
155+
return CraftableFilter.INSTANCE.getInvStacks();
156+
}
157+
}
158+
159+
return null;
160+
}
161+
162+
private static Long2LongMap extractIngredients(Iterable<ItemStack> ingredients) {
163+
EntryDefinition<ItemStack> definition = VanillaEntryTypes.ITEM.getDefinition();
164+
165+
Long2LongMap map = new Long2LongOpenHashMap();
166+
for (ItemStack stack : ingredients) {
167+
if (!stack.isEmpty()) {
168+
long hash = definition.hash(null, stack, ComparisonContext.FUZZY);
169+
long newCount = map.getOrDefault(hash, 0) + Math.max(0, stack.getCount());
170+
map.put(hash, newCount);
171+
}
172+
}
173+
return map;
174+
}
175+
}

runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/EntryListSearchManager.java

+3-9
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@
2323

2424
package me.shedaniel.rei.impl.client.gui.widget.entrylist;
2525

26+
import com.google.common.base.Predicates;
2627
import com.google.common.base.Stopwatch;
2728
import com.google.common.collect.Iterators;
28-
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
29-
import it.unimi.dsi.fastutil.longs.LongSet;
3029
import me.shedaniel.rei.api.client.config.ConfigManager;
3130
import me.shedaniel.rei.api.client.config.ConfigObject;
3231
import me.shedaniel.rei.api.client.gui.config.EntryPanelOrdering;
@@ -39,6 +38,7 @@
3938
import me.shedaniel.rei.impl.client.config.collapsible.CollapsibleConfigManager;
4039
import me.shedaniel.rei.impl.client.search.AsyncSearchManager;
4140
import me.shedaniel.rei.impl.client.search.collapsed.CollapsedEntriesCache;
41+
import me.shedaniel.rei.impl.client.view.ViewsImpl;
4242
import me.shedaniel.rei.impl.common.InternalLogger;
4343
import me.shedaniel.rei.impl.common.entry.type.EntryRegistryImpl;
4444
import me.shedaniel.rei.impl.common.entry.type.collapsed.CollapsedStack;
@@ -62,13 +62,7 @@ public class EntryListSearchManager {
6262

6363
private final AsyncSearchManager searchManager = new AsyncSearchManager(EntryListSearchManager::getAllEntriesContextually, () -> {
6464
boolean checkCraftable = ConfigManager.getInstance().isCraftableOnlyEnabled();
65-
LongSet workingItems = checkCraftable ? new LongOpenHashSet() : null;
66-
if (checkCraftable) {
67-
for (EntryStack<?> stack : Views.getInstance().findCraftableEntriesByMaterials()) {
68-
workingItems.add(EntryStacks.hashExact(stack));
69-
}
70-
}
71-
return checkCraftable ? stack -> workingItems.contains(stack.hashExact()) : stack -> true;
65+
return checkCraftable ? ((ViewsImpl) Views.getInstance()).getCraftableEntriesPredicate() : Predicates.alwaysTrue();
7266
}, HashedEntryStackWrapper::normalize);
7367

7468
private static List<HNEntryStackWrapper> getAllEntriesContextually(SearchFilter filter) {

runtime/src/main/java/me/shedaniel/rei/impl/client/search/AsyncSearchManager.java

+4
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ public CompletableFuture<Map.Entry<List<HashedEntryStackWrapper>, SearchFilter>>
124124
.thenApply(entry -> {
125125
this.last = entry;
126126
return entry;
127+
})
128+
.exceptionally(throwable -> {
129+
InternalLogger.getInstance().error("Error while searching", throwable);
130+
return new AbstractMap.SimpleImmutableEntry<>(List.of(), filter);
127131
});
128132
}
129133

0 commit comments

Comments
 (0)