Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/11.x-1.19.4' into 12.x-1.20
Browse files Browse the repository at this point in the history
# Conflicts:
#	api/src/main/java/me/shedaniel/rei/api/client/registry/transfer/simple/SimpleTransferHandler.java
  • Loading branch information
shedaniel committed Sep 4, 2024
2 parents 63cb335 + 7b5ed40 commit 97adf5d
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 146 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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.api.client.registry.transfer;

import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.ApiStatus;

import java.util.List;

/**
* A meta interface for {@link TransferHandler}.
*/
@ApiStatus.Experimental
public interface TransferHandlerMeta {
/**
* Returns the available ingredients for the transfer handler.
* This is used in the craftable filter, and the quick transfer tooltip to quickly determine if the handler can handle the recipe.
* <p>
* If this interface is not implemented, REI will assume stacks only from the player's inventory are available.
*
* @param context the context
* @return the available ingredients
*/
default Iterable<ItemStack> getAvailableIngredients(TransferHandler.Context context) {
return List.of();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@

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

import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.ints.IntSet;
import me.shedaniel.math.Rectangle;
import me.shedaniel.rei.api.client.gui.widgets.Slot;
import me.shedaniel.rei.api.client.gui.widgets.Widget;
import me.shedaniel.rei.api.client.registry.transfer.TransferHandler;
import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerMeta;
import me.shedaniel.rei.api.common.category.CategoryIdentifier;
import me.shedaniel.rei.api.common.display.Display;
import me.shedaniel.rei.api.common.entry.EntryStack;
import me.shedaniel.rei.api.common.entry.InputIngredient;
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
import me.shedaniel.rei.api.common.transfer.info.stack.SlotAccessor;
Expand All @@ -43,13 +46,14 @@
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.ApiStatus;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@ApiStatus.Experimental
public interface SimpleTransferHandler extends TransferHandler {
public interface SimpleTransferHandler extends TransferHandler, TransferHandlerMeta {
static <C extends AbstractContainerMenu, D extends Display> SimpleTransferHandler create(Class<? extends C> containerClass,
CategoryIdentifier<D> categoryIdentifier,
IntRange inputSlots) {
Expand Down Expand Up @@ -146,6 +150,11 @@ default List<InputIngredient<ItemStack>> getInputsIndexed(Context context) {
InputIngredient.withType(entry, VanillaEntryTypes.ITEM));
}

@Override
default Iterable<ItemStack> getAvailableIngredients(Context context) {
return Iterables.transform(Iterables.concat(getInputSlots(context), getInventorySlots(context)), SlotAccessor::getItemStack);
}

/**
* Renders the missing ingredients of the transfer.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.jetbrains.annotations.Nullable;

import java.util.Collection;
import java.util.List;

public interface Views extends Reloadable<REIClientPlugin> {
/**
Expand All @@ -49,6 +50,10 @@ static Views getInstance() {
* Returns all craftable items from materials.
*
* @return the list of craftable entries
* @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.
*/
Collection<EntryStack<?>> findCraftableEntriesByMaterials();
@Deprecated(forRemoval = true)
default Collection<EntryStack<?>> findCraftableEntriesByMaterials() {
return List.of();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,19 @@
package me.shedaniel.rei.plugin.autocrafting;

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.simple.SimpleTransferHandler;
import me.shedaniel.rei.api.common.entry.EntryStack;
import me.shedaniel.rei.api.common.entry.InputIngredient;
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
import me.shedaniel.rei.api.common.util.CollectionUtils;
import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCraftingDisplay;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemStack;

import java.util.List;

public class InventoryCraftingTransferHandler implements TransferHandler {
public class InventoryCraftingTransferHandler implements TransferHandler, TransferHandlerMeta {
private final SimpleTransferHandler parent;

public InventoryCraftingTransferHandler(SimpleTransferHandler parent) {
Expand All @@ -61,4 +63,9 @@ public Result handle(Context context) {
CollectionUtils.map(inputs, entry -> InputIngredient.withType(entry, VanillaEntryTypes.ITEM)),
parent.getInputSlots(context), parent.getInventorySlots(context));
}

@Override
public Iterable<ItemStack> getAvailableIngredients(Context context) {
return parent.getAvailableIngredients(context);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -39,6 +38,7 @@
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;
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ public CompletableFuture<Map.Entry<List<HashedEntryStackWrapper>, SearchFilter>>
.thenApply(entry -> {
this.last = entry;
return entry;
})
.exceptionally(throwable -> {
InternalLogger.getInstance().error("Error while searching", throwable);
return new AbstractMap.SimpleImmutableEntry<>(List.of(), filter);
});
}

Expand Down
Loading

0 comments on commit 97adf5d

Please sign in to comment.