aboutsummaryrefslogtreecommitdiff
path: root/runtime/src/main/java/me/shedaniel/rei/impl
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/src/main/java/me/shedaniel/rei/impl')
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilter.java11
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/AutoCraftingEvaluator.java16
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java11
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/transfer/SimpleTransferHandlerImpl.java152
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/client/view/ViewsImpl.java167
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/common/transfer/InputSlotCrafter.java80
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/common/transfer/LegacyInputSlotCrafter.java113
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/common/transfer/NewInputSlotCrafter.java134
-rw-r--r--runtime/src/main/java/me/shedaniel/rei/impl/common/transfer/SlotAccessorRegistryImpl.java105
9 files changed, 657 insertions, 132 deletions
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilter.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilter.java
index 56dd69632..41063cdf4 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilter.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/craftable/CraftableFilter.java
@@ -27,12 +27,15 @@ import it.unimi.dsi.fastutil.longs.Long2LongMap;
import it.unimi.dsi.fastutil.longs.Long2LongMaps;
import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
import me.shedaniel.rei.impl.client.ClientHelperImpl;
+import net.minecraft.client.Minecraft;
+import net.minecraft.world.inventory.AbstractContainerMenu;
public class CraftableFilter {
public static final CraftableFilter INSTANCE = new CraftableFilter();
private boolean dirty = false;
private Long2LongMap invStacks = new Long2LongOpenHashMap();
private Long2LongMap containerStacks = new Long2LongOpenHashMap();
+ private long menuId = -2;
public void markDirty() {
dirty = true;
@@ -49,6 +52,14 @@ public class CraftableFilter {
public void tick() {
if (dirty) return;
+ AbstractContainerMenu menu = Minecraft.getInstance().player.containerMenu;
+ long currentMenuId = menu == null ? -1 : menu.containerId;
+ if (currentMenuId != menuId) {
+ menuId = currentMenuId;
+ markDirty();
+ }
+ if (dirty) return;
+
Long2LongMap currentStacks;
try {
currentStacks = ClientHelperImpl.getInstance()._getInventoryItemsTypes();
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/AutoCraftingEvaluator.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/AutoCraftingEvaluator.java
index ba1379b02..665420cfb 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/AutoCraftingEvaluator.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/AutoCraftingEvaluator.java
@@ -32,6 +32,7 @@ import me.shedaniel.rei.api.client.registry.transfer.TransferHandler;
import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry;
import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRenderer;
import me.shedaniel.rei.api.common.display.Display;
+import me.shedaniel.rei.api.common.transfer.info.MenuTransferException;
import me.shedaniel.rei.api.common.util.CollectionUtils;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
@@ -41,10 +42,7 @@ import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.resources.ResourceLocation;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
+import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -99,7 +97,15 @@ public class AutoCraftingEvaluator {
for (TransferHandler transferHandler : TransferHandlerRegistry.getInstance()) {
try {
- TransferHandler.Result transferResult = transferHandler.handle(context);
+ TransferHandler.ApplicabilityResult applicabilityResult = transferHandler.checkApplicable(context);
+ if (!applicabilityResult.isApplicable()) continue;
+ TransferHandler.Result transferResult;
+
+ if (applicabilityResult.isSuccessful()) {
+ transferResult = transferHandler.handle(context);
+ } else {
+ transferResult = applicabilityResult.getError();
+ }
if (transferResult.isBlocking() && actuallyCrafting) {
if (transferResult.isReturningToScreen()) {
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 259f7367f..38157049c 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
@@ -53,6 +53,7 @@ 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.plugins.PluginManager;
+import me.shedaniel.rei.api.common.transfer.info.MenuTransferException;
import me.shedaniel.rei.api.common.util.FormattingUtils;
import me.shedaniel.rei.impl.client.REIRuntimeImpl;
import me.shedaniel.rei.impl.client.gui.InternalTextures;
@@ -615,7 +616,15 @@ public class EntryWidget extends Slot implements DraggableStackProviderWidget {
if (handler != null) {
AbstractContainerScreen<?> containerScreen = REIRuntime.getInstance().getPreviousContainerScreen();
TransferHandler.Context context = TransferHandler.Context.create(true, Screen.hasShiftDown() || button == 1, containerScreen, display);
- TransferHandler.Result transferResult = handler.handle(context);
+ TransferHandler.ApplicabilityResult applicabilityResult = handler.checkApplicable(context);
+ if (!applicabilityResult.isApplicable()) return false;
+ TransferHandler.Result transferResult;
+
+ if (applicabilityResult.isSuccessful()) {
+ transferResult = handler.handle(context);
+ } else {
+ transferResult = applicabilityResult.getError();
+ }
if (transferResult.isBlocking()) {
minecraft.getSoundManager().play(SimpleSoundInstance.forUI(SoundEvents.UI_BUTTON_CLICK, 1.0F));
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/transfer/SimpleTransferHandlerImpl.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/transfer/SimpleTransferHandlerImpl.java
new file mode 100644
index 000000000..ef7d59c79
--- /dev/null
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/transfer/SimpleTransferHandlerImpl.java
@@ -0,0 +1,152 @@
+/*
+ * 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.transfer;
+
+import dev.architectury.networking.NetworkManager;
+import io.netty.buffer.Unpooled;
+import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
+import it.unimi.dsi.fastutil.ints.IntSet;
+import me.shedaniel.rei.RoughlyEnoughItemsNetwork;
+import me.shedaniel.rei.api.client.ClientHelper;
+import me.shedaniel.rei.api.client.registry.transfer.TransferHandler;
+import me.shedaniel.rei.api.client.registry.transfer.simple.SimpleTransferHandler;
+import me.shedaniel.rei.api.common.entry.InputIngredient;
+import me.shedaniel.rei.api.common.transfer.RecipeFinder;
+import me.shedaniel.rei.api.common.transfer.info.stack.SlotAccessor;
+import me.shedaniel.rei.api.common.transfer.info.stack.SlotAccessorRegistry;
+import me.shedaniel.rei.api.common.util.CollectionUtils;
+import me.shedaniel.rei.api.common.util.EntryIngredients;
+import me.shedaniel.rei.impl.ClientInternals;
+import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
+import net.minecraft.client.gui.screens.recipebook.RecipeUpdateListener;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.ListTag;
+import net.minecraft.nbt.Tag;
+import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.item.ItemStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public enum SimpleTransferHandlerImpl implements ClientInternals.SimpleTransferHandler {
+ INSTANCE;
+
+ @Override
+ public TransferHandler.Result handle(TransferHandler.Context context, SimpleTransferHandler.MissingInputRenderer missingInputRenderer, List<InputIngredient<ItemStack>> inputs, Iterable<SlotAccessor> inputSlots, Iterable<SlotAccessor> inventorySlots) {
+ AbstractContainerScreen<?> containerScreen = context.getContainerScreen();
+ List<InputIngredient<ItemStack>> missing = SimpleTransferHandlerImpl.hasItemsIndexed(context, inventorySlots, inputs);
+
+ if (!missing.isEmpty()) {
+ IntSet missingIndices = new IntLinkedOpenHashSet(missing.size());
+ for (InputIngredient<ItemStack> ingredient : missing) {
+ missingIndices.add(ingredient.getDisplayIndex());
+ }
+ return TransferHandler.Result.createFailed(Component.translatable("error.rei.not.enough.materials"))
+ .renderer((matrices, mouseX, mouseY, delta, widgets, bounds, d) -> {
+ missingInputRenderer.renderMissingInput(context, inputs, missing, missingIndices, matrices, mouseX, mouseY, delta, widgets, bounds);
+ })
+ .tooltipMissing(CollectionUtils.map(missing, ingredient -> EntryIngredients.ofItemStacks(ingredient.get())));
+ }
+
+ if (!ClientHelper.getInstance().canUseMovePackets()) {
+ return TransferHandler.Result.createFailed(Component.translatable("error.rei.not.on.server"));
+ }
+
+ if (!context.isActuallyCrafting()) {
+ return TransferHandler.Result.createSuccessful();
+ }
+
+ context.getMinecraft().setScreen(containerScreen);
+ if (containerScreen instanceof RecipeUpdateListener listener) {
+ listener.getRecipeBookComponent().ghostRecipe.clear();
+ }
+
+ FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
+ buf.writeResourceLocation(context.getDisplay().getCategoryIdentifier().getIdentifier());
+ buf.writeBoolean(context.isStackedCrafting());
+
+ buf.writeNbt(save(context, inputs, inputSlots, inventorySlots));
+ NetworkManager.sendToServer(RoughlyEnoughItemsNetwork.MOVE_ITEMS_NEW_PACKET, buf);
+ return TransferHandler.Result.createSuccessful();
+ }
+
+ private CompoundTag save(TransferHandler.Context context, List<InputIngredient<ItemStack>> inputs, Iterable<SlotAccessor> inputSlots, Iterable<SlotAccessor> inventorySlots) {
+ CompoundTag tag = new CompoundTag();
+ tag.put("Inputs", saveInputs(inputs));
+ tag.put("InventorySlots", saveSlots(context,inventorySlots));
+ tag.put("InputSlots", saveSlots(context, inputSlots));
+ return tag;
+ }
+
+ private Tag saveSlots(TransferHandler.Context context, Iterable<SlotAccessor> slots) {
+ ListTag tag = new ListTag();
+
+ for (SlotAccessor slot : slots) {
+ tag.add(SlotAccessorRegistry.getInstance().save(context.getMenu(), context.getMinecraft().player, slot));
+ }
+
+ return tag;
+ }
+
+ private Tag saveInputs(List<InputIngredient<ItemStack>> inputs) {
+ CompoundTag tag = new CompoundTag();
+
+ for (InputIngredient<ItemStack> input : inputs) {
+ tag.put(String.valueOf(input.getIndex()), EntryIngredients.ofItemStacks(input.get()).saveIngredient());
+ }
+
+ return tag;
+ }
+
+ public static List<InputIngredient<ItemStack>> hasItemsIndexed(TransferHandler.Context context, Iterable<SlotAccessor> inventorySlots, List<InputIngredient<ItemStack>> inputs) {
+ // Create a clone of player's inventory, and count
+ RecipeFinder recipeFinder = new RecipeFinder();
+ for (SlotAccessor slot : inventorySlots) {
+ recipeFinder.addNormalItem(slot.getItemStack());
+ }
+ List<InputIngredient<ItemStack>> missing = new ArrayList<>();
+ for (InputIngredient<ItemStack> possibleStacks : inputs) {
+ boolean done = possibleStacks.get().isEmpty();
+ for (ItemStack possibleStack : possibleStacks.get()) {
+ if (!done) {
+ int invRequiredCount = possibleStack.getCount();
+ int key = RecipeFinder.getItemId(possibleStack);
+ while (invRequiredCount > 0 && recipeFinder.contains(key)) {
+ invRequiredCount--;
+ recipeFinder.take(key, 1);
+ }
+ if (invRequiredCount <= 0) {
+ done = true;
+ break;
+ }
+ }
+ }
+ if (!done) {
+ missing.add(possibleStacks);
+ }
+ }
+ return missing;
+ }
+}
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 36c84a396..54fe8ba5f 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
@@ -30,11 +30,14 @@ import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.longs.Long2LongMap;
import it.unimi.dsi.fastutil.longs.Long2LongMaps;
import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
+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;
@@ -60,9 +63,11 @@ 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.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;
@@ -429,86 +434,32 @@ public class ViewsImpl implements Views {
AbstractContainerMenu menu = Minecraft.getInstance().player.containerMenu;
Set<EntryStack<?>> craftables = new HashSet<>();
- boolean onlyIncludeHasMenu = false;
+ AbstractContainerScreen<?> containerScreen = REIRuntime.getInstance().getPreviousContainerScreen();
for (Map.Entry<CategoryIdentifier<?>, List<Display>> entry : DisplayRegistry.getInstance().getAll().entrySet()) {
- 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<Display>) entry.getKey();
- }
- }
-
- InfoSerializationContext context = new InfoSerializationContext();
+ MenuSerializationContext<AbstractContainerMenu, LocalPlayer, Display> context = createLegacyContext(menu, entry.getKey().cast());
List<Display> displays = entry.getValue();
for (Display display : displays) {
try {
- MenuInfo<AbstractContainerMenu, Display> info = menu != null ?
- MenuInfoRegistry.getInstance().getClient(display, context, menu)
- : null;
+ TransferHandler.Context transferContext = TransferHandler.Context.create(false, false, containerScreen, display);
+ boolean successful = matchesLegacyRequirements(menu, context, display);
- if (onlyIncludeHasMenu && info == null) {
- continue;
- }
-
- 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;
+ if (!successful) {
+ for (TransferHandler handler : TransferHandlerRegistry.getInstance()) {
+ if (!(handler instanceof DefaultCategoryHandler) && handler.checkApplicable(transferContext).isSuccessful()) {
+ if (handler.handle(transferContext).isSuccessful()) {
+ successful = true;
+ break;
+ }
}
}
}
- if (slotsCraftable == display.getRequiredEntries().size() && containsNonEmpty) {
- if (info != null && !onlyIncludeHasMenu) {
- onlyIncludeHasMenu = true;
- craftables.clear();
- }
-
- display.getOutputEntries().stream().flatMap(Collection::stream).collect(Collectors.toCollection(() -> craftables));
- }
+
+ 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);
}
@@ -517,6 +468,82 @@ public class ViewsImpl implements Views {
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 slotsCraftable == display.getRequiredEntries().size() && containsNonEmpty;
+ }
+
private static <T> boolean isStackWorkStationOfCategory(CategoryRegistry.CategoryConfiguration<?> category, EntryStack<T> stack) {
for (EntryIngredient ingredient : category.getWorkstations()) {
if (EntryIngredients.testFuzzy(ingredient, stack)) {
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/common/transfer/InputSlotCrafter.java b/runtime/src/main/java/me/shedaniel/rei/impl/common/transfer/InputSlotCrafter.java
index a4130307d..5de24ce18 100644
--- a/runtime/src/main/java/me/shedaniel/rei/impl/common/transfer/InputSlotCrafter.java
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/common/transfer/InputSlotCrafter.java
@@ -25,16 +25,11 @@ package me.shedaniel.rei.impl.common.transfer;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
-import me.shedaniel.rei.api.common.category.CategoryIdentifier;
-import me.shedaniel.rei.api.common.display.Display;
+import me.shedaniel.rei.api.common.entry.InputIngredient;
import me.shedaniel.rei.api.common.transfer.RecipeFinder;
-import me.shedaniel.rei.api.common.transfer.info.MenuInfo;
-import me.shedaniel.rei.api.common.transfer.info.MenuInfoContext;
-import me.shedaniel.rei.api.common.transfer.info.MenuInfoRegistry;
import me.shedaniel.rei.api.common.transfer.info.stack.SlotAccessor;
import me.shedaniel.rei.api.common.util.CollectionUtils;
import net.minecraft.core.NonNullList;
-import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.inventory.AbstractContainerMenu;
@@ -44,59 +39,53 @@ import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.List;
-import java.util.Objects;
-public class InputSlotCrafter<T extends AbstractContainerMenu, C extends Container, D extends Display> implements MenuInfoContext<T, ServerPlayer, D> {
- protected CategoryIdentifier<D> category;
+public abstract class InputSlotCrafter<T extends AbstractContainerMenu, C extends Container> {
protected T container;
- protected MenuInfo<T, D> menuInfo;
private Iterable<SlotAccessor> inputStacks;
private Iterable<SlotAccessor> inventoryStacks;
- private ServerPlayer player;
+ protected ServerPlayer player;
- private InputSlotCrafter(CategoryIdentifier<D> category, T container) {
- this.category = category;
+ protected InputSlotCrafter(T container) {
this.container = container;
}
- public void setMenuInfo(MenuInfo<T, D> menuInfo) {
- this.menuInfo = menuInfo;
- }
-
- public static <T extends AbstractContainerMenu, C extends Container, D extends Display> InputSlotCrafter<T, C, D> start(CategoryIdentifier<D> category, T menu, ServerPlayer player, CompoundTag display, boolean hasShift) {
- InputSlotCrafter<T, C, D> crafter = new InputSlotCrafter<>(category, menu);
- MenuInfo<T, D> menuInfo = Objects.requireNonNull(MenuInfoRegistry.getInstance().get(category, menu, crafter, display), "Container Info does not exist on the server!");
- crafter.setMenuInfo(menuInfo);
- crafter.fillInputSlots(player, hasShift);
- return crafter;
- }
-
- private void fillInputSlots(ServerPlayer player, boolean hasShift) {
+ public void fillInputSlots(ServerPlayer player, boolean hasShift) {
this.player = player;
- this.inventoryStacks = this.menuInfo.getInventorySlots(this);
- this.inputStacks = this.menuInfo.getInputSlots(this);
+ this.inventoryStacks = this.getInventorySlots();
+ this.inputStacks = this.getInputSlots();
// Return the already placed items on the grid
this.cleanInputs();
RecipeFinder recipeFinder = new RecipeFinder();
- this.menuInfo.getRecipeFinderPopulator().populate(this, recipeFinder);
+ this.populateRecipeFinder(recipeFinder);
NonNullList<Ingredient> ingredients = NonNullList.create();
- for (List<ItemStack> itemStacks : this.menuInfo.getInputs(this, true)) {
- ingredients.add(CollectionUtils.toIngredient(itemStacks));
+ for (InputIngredient<ItemStack> itemStacks : this.getInputs()) {
+ ingredients.add(CollectionUtils.toIngredient(itemStacks.get()));
}
if (recipeFinder.findRecipe(ingredients, null)) {
this.fillInputSlots(recipeFinder, ingredients, hasShift);
} else {
this.cleanInputs();
- this.menuInfo.markDirty(this);
+ this.markDirty();
throw new NotEnoughMaterialsException();
}
- this.menuInfo.markDirty(this);
+ this.markDirty();
}
+ protected abstract Iterable<SlotAccessor> getInputSlots();
+
+ protected abstract Iterable<SlotAccessor> getInventorySlots();
+
+ protected abstract List<InputIngredient<ItemStack>> getInputs();
+
+ protected abstract void populateRecipeFinder(RecipeFinder recipeFinder);
+
+ protected abstract void markDirty();
+
public void alignRecipeToGrid(Iterable<SlotAccessor> inputStacks, Iterator<Integer> recipeItemIds, int craftsAmount) {
for (SlotAccessor inputStack : inputStacks) {
if (!recipeItemIds.hasNext()) {
@@ -155,9 +144,7 @@ public class InputSlotCrafter<T extends AbstractContainerMenu, C extends Contain
}
}
- protected void cleanInputs() {
- this.menuInfo.getInputCleanHandler().clean(this);
- }
+ protected abstract void cleanInputs();
@Nullable
public SlotAccessor takeInventoryStack(ItemStack itemStack) {
@@ -175,25 +162,6 @@ public class InputSlotCrafter<T extends AbstractContainerMenu, C extends Contain
return ItemStack.isSameItemSameTags(stack1, stack2);
}
- @Override
- public T getMenu() {
- return container;
- }
-
- @Override
- public ServerPlayer getPlayerEntity() {
- return player;
- }
-
- @Override
- public D getDisplay() {
- return menuInfo.getDisplay();
- }
-
- @Override
- public CategoryIdentifier<D> getCategoryIdentifier() {
- return category;
+ public static class NotEnoughMaterialsException extends RuntimeException {
}
-
- public static class NotEnoughMaterialsException extends RuntimeException {}
}
diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/common/transfer/LegacyInputSlotCrafter.java b/runtime/src/main/java/me/shedaniel/rei/impl/common/transfer/LegacyInputSlotCrafter.java
new file mode 100644
index 000000000..d05256111
--- /dev/null
+++ b/runtime/src/main/java/me/shedaniel/rei/impl/common/transfer/LegacyInputSlotCrafter.java
@@ -0,0 +1,113 @@
+/*
+ * 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.common.transfer;
+
+import me.shedaniel.rei.api.common.category.CategoryIdentifier;
+import me.shedaniel.rei.api.common.display.Display;
+import me.shedaniel.rei.api.common.entry.InputIngredient;
+import me.shedaniel.rei.api.common.transfer.RecipeFinder;
+import me.shedaniel.rei.api.common.transfer.info.MenuInfo;
+import me.shedaniel.rei.api.common.transfer.info.MenuInfoContext;
+import me.shedaniel.rei.api.common.transfer.info.MenuInfoRegistry;
+import me.shedaniel.rei.api.common.transfer.info.stack.SlotAccessor;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.Container;
+import net.minecraft.world.inventory.AbstractContainerMenu;
+import net.minecraft.world.item.ItemStack;
+
+import java.util.List;
+import java.util.Objects;
+
+public class LegacyInputSlotCrafter<T extends AbstractContainerMenu, C extends Container, D extends Display> extends InputSlotCrafter<T, C> implements MenuInfoContext<T, ServerPlayer, D> {
+ protected CategoryIdentifier<D> category;
+ protected MenuInfo<T, D> menuInfo;
+
+ protected LegacyInputSlotCrafter(CategoryIdentifier<D> category, T container) {
+ super(container);
+ this.category = category;
+ }
+
+ public void setMenuInfo(MenuInfo<T, D> menuInfo) {
+ this.menuInfo = menuInfo;
+ }
+
+ public static <T extends AbstractContainerMenu, C extends Container, D extends Display> LegacyInputSlotCrafter<T, C, D> start(CategoryIdentifier<D> category, T menu, ServerPlayer player, CompoundTag display, boolean hasShift) {
+ LegacyInputSlotCrafter<T, C, D> crafter = new LegacyInputSlotCrafter<>(category, menu);
+ MenuInfo<T, D> menuInfo = Objects.requireNonNull(MenuInfoRegistry.getInstance().get(category, menu, crafter, display), "Container Info does not exist on the server!");
+ crafter.setMenuInfo(menuInfo);
+ crafter.fillInputSlots(player, hasShift);
+ return crafter;