diff options
| author | shedaniel <daniel@shedaniel.me> | 2023-08-28 13:16:57 +0800 |
|---|---|---|
| committer | shedaniel <daniel@shedaniel.me> | 2023-09-01 19:48:57 +0800 |
| commit | 6a8bc6a8c34af1e3ff15fe8a802ef5ece3c417d2 (patch) | |
| tree | a41c145c1273ab063c725bf44c1f39ca6e0bd5bd /runtime/src/main/java/me/shedaniel/rei/impl | |
| parent | f8bea2079764219f68070be9ae45ffd8d517de5d (diff) | |
| download | RoughlyEnoughItems-6a8bc6a8c34af1e3ff15fe8a802ef5ece3c417d2.tar.gz RoughlyEnoughItems-6a8bc6a8c34af1e3ff15fe8a802ef5ece3c417d2.tar.bz2 RoughlyEnoughItems-6a8bc6a8c34af1e3ff15fe8a802ef5ece3c417d2.zip | |
Reworked the transfer api
See https://www.craft.me/s/TVL01jO3OZarPE for the documentation of the new experimental simple transfer handle
Diffstat (limited to 'runtime/src/main/java/me/shedaniel/rei/impl')
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 777b44d06..f5369814c 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 @@ -54,6 +54,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; @@ -613,7 +614,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 143c81783..7cbf4ebcd 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 stack1.getItem() == stack2.getItem() && ItemStack.tagMatches(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.comm |
