From 2961a0449da999a5b8198bd043b6bad26879667d Mon Sep 17 00:00:00 2001 From: shedaniel Date: Thu, 27 Jan 2022 09:51:57 +0800 Subject: Close #724 - Sort displays so craftable ones are on the top - Add Ctrl+Click shortcut to move items immediately - Improve TransferHandler API to allow renderers even when it is "successful" --- .../client/gui/widget/AutoCraftingEvaluator.java | 177 +++++++++++++++++++++ .../impl/client/gui/widget/EntryListWidget.java | 67 +++++++- .../impl/client/gui/widget/InternalWidgets.java | 141 +++------------- .../shedaniel/rei/impl/client/view/ViewsImpl.java | 87 +++++++--- .../autocrafting/DefaultCategoryHandler.java | 40 +---- 5 files changed, 337 insertions(+), 175 deletions(-) create mode 100644 runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/AutoCraftingEvaluator.java (limited to 'runtime/src/main/java') 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 new file mode 100644 index 000000000..7cd3e3a93 --- /dev/null +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/AutoCraftingEvaluator.java @@ -0,0 +1,177 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022 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.widget; + +import me.shedaniel.math.Point; +import me.shedaniel.rei.api.client.REIRuntime; +import me.shedaniel.rei.api.client.gui.widgets.Tooltip; +import me.shedaniel.rei.api.client.overlay.ScreenOverlay; +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.util.CollectionUtils; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.resources.language.I18n; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.resources.ResourceLocation; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class AutoCraftingEvaluator { + public static class AutoCraftingResult { + public int tint = 0; + public boolean successful = false; + public TransferHandler successfulHandler; + public boolean hasApplicable = false; + public TransferHandlerRenderer renderer; + public BiConsumer> tooltipRenderer; + } + + public static AutoCraftingResult evaluateAutoCrafting(boolean actuallyCrafting, boolean stackedCrafting, Display display, Supplier> idsSupplier) { + AbstractContainerScreen containerScreen = REIRuntime.getInstance().getPreviousContainerScreen(); + AutoCraftingResult result = new AutoCraftingResult(); + final List errorTooltip = new ArrayList<>(); + result.tooltipRenderer = (pos, sink) -> { + List str = new ArrayList<>(errorTooltip); + + if (Minecraft.getInstance().options.advancedItemTooltips && idsSupplier != null) { + Collection locations = idsSupplier.get(); + if (!locations.isEmpty()) { + str.add(new TextComponent(" ")); + for (ResourceLocation location : locations) { + String t = I18n.get("text.rei.recipe_id", "", location.toString()); + if (t.startsWith("\n")) { + t = t.substring("\n".length()); + } + str.add(new TextComponent(t).withStyle(ChatFormatting.GRAY)); + } + } + } + + sink.accept(Tooltip.create(pos, str)); + }; + + if (containerScreen == null) { + errorTooltip.add(new TranslatableComponent("error.rei.not.supported.move.items").withStyle(ChatFormatting.RED)); + return result; + } + + List errors = new ArrayList<>(); + TransferHandler.Context context = TransferHandler.Context.create(actuallyCrafting, stackedCrafting, containerScreen, display); + + for (TransferHandler transferHandler : TransferHandlerRegistry.getInstance()) { + try { + TransferHandler.Result transferResult = transferHandler.handle(context); + + if (transferResult.isBlocking() && actuallyCrafting) { + if (transferResult.isReturningToScreen()) { + Minecraft.getInstance().setScreen(containerScreen); + REIRuntime.getInstance().getOverlay().ifPresent(ScreenOverlay::queueReloadOverlay); + } + + break; + } + + if (transferResult.isApplicable()) { + result.hasApplicable = true; + result.tint = transferResult.getColor(); + + TransferHandlerRenderer transferHandlerRenderer = transferResult.getRenderer(transferHandler, context); + if (transferHandlerRenderer != null) { + result.renderer = transferHandlerRenderer; + } + + if (transferResult.getTooltipRenderer() != null) { + BiConsumer tooltipRenderer = transferResult.getTooltipRenderer(); + result.tooltipRenderer = (point, tooltipConsumer) -> tooltipRenderer.accept(point, tooltipConsumer::accept); + } + + if (transferResult.isSuccessful()) { + errors.clear(); + result.successful = true; + result.successfulHandler = transferHandler; + result.renderer = null; + break; + } + + errors.add(transferResult); + + if (transferResult.isBlocking()) { + break; + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + if (!result.hasApplicable) { + errorTooltip.clear(); + errorTooltip.add(new TranslatableComponent("error.rei.not.supported.move.items").withStyle(ChatFormatting.RED)); + return result; + } + + if (errors.isEmpty()) { + errorTooltip.clear(); + errorTooltip.add(new TranslatableComponent("text.auto_craft.move_items")); + } else { + errorTooltip.clear(); + List tooltipsFilled = new ArrayList<>(); + for (TransferHandler.Result error : errors) { + error.fillTooltip(tooltipsFilled); + } + + if (errors.size() == 1) { + for (Component tooltipFilled : tooltipsFilled) { + MutableComponent colored = tooltipFilled.copy().withStyle(ChatFormatting.RED); + if (!CollectionUtils.anyMatch(errorTooltip, ss -> ss.getString().equalsIgnoreCase(tooltipFilled.getString()))) { + errorTooltip.add(colored); + } + } + } else { + errorTooltip.add(new TranslatableComponent("error.rei.multi.errors").withStyle(ChatFormatting.RED)); + for (Component tooltipFilled : tooltipsFilled) { + MutableComponent colored = new TextComponent("- ").withStyle(ChatFormatting.RED) + .append(tooltipFilled.copy().withStyle(ChatFormatting.RED)); + if (!CollectionUtils.anyMatch(errorTooltip, ss -> ss.getString().equalsIgnoreCase(colored.getString()))) { + errorTooltip.add(colored); + } + } + } + } + + return result; + } +} diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java index f8e58ed33..53029ff4b 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryListWidget.java @@ -52,8 +52,11 @@ import me.shedaniel.rei.api.client.gui.widgets.Widget; import me.shedaniel.rei.api.client.gui.widgets.WidgetWithBounds; import me.shedaniel.rei.api.client.overlay.OverlayListWidget; import me.shedaniel.rei.api.client.overlay.ScreenOverlay; +import me.shedaniel.rei.api.client.registry.display.DisplayRegistry; import me.shedaniel.rei.api.client.registry.screen.OverlayDecider; import me.shedaniel.rei.api.client.registry.screen.ScreenRegistry; +import me.shedaniel.rei.api.client.registry.transfer.TransferHandler; +import me.shedaniel.rei.api.common.display.Display; import me.shedaniel.rei.api.common.entry.EntryStack; import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes; import me.shedaniel.rei.api.common.util.EntryStacks; @@ -62,8 +65,11 @@ import me.shedaniel.rei.impl.client.config.ConfigManagerImpl; import me.shedaniel.rei.impl.client.config.ConfigObjectImpl; import me.shedaniel.rei.impl.client.gui.ScreenOverlayImpl; import me.shedaniel.rei.impl.client.search.AsyncSearchManager; +import me.shedaniel.rei.impl.client.view.ViewsImpl; +import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.network.chat.Component; @@ -76,10 +82,12 @@ import net.minecraft.world.item.ItemStack; import org.apache.commons.lang3.mutable.MutableInt; import org.apache.commons.lang3.mutable.MutableLong; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -257,7 +265,7 @@ public class EntryListWidget extends WidgetWithBounds implements OverlayListWidg @Override public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { if (!hasSpace()) return; - + MutableInt size = new MutableInt(); MutableLong time = new MutableLong(); long totalTimeStart = debugTime ? System.nanoTime() : 0; @@ -559,6 +567,9 @@ public class EntryListWidget extends WidgetWithBounds implements OverlayListWidg } private class EntryListEntry extends EntryListEntryWidget { + private Display display; + private Optional transferHandler; + private EntryListEntry(int x, int y, int entrySize) { super(new Point(x, y), entrySize); } @@ -567,5 +578,59 @@ public class EntryListWidget extends WidgetWithBounds implements OverlayListWidg public boolean containsMouse(double mouseX, double mouseY) { return super.containsMouse(mouseX, mouseY) && containsChecked(mouseX, mouseY, true); } + + public TransferHandler getTransferHandler() { + for (List displays : DisplayRegistry.getInstance().getAll().values()) { + for (Display display : displays) { + if (ViewsImpl.isRecipesFor(getEntries(), display)) { + AutoCraftingEvaluator.AutoCraftingResult result = AutoCraftingEvaluator.evaluateAutoCrafting(false, false, display, null); + if (result.successful) { + this.display = display; + return result.successfulHandler; + } + } + } + } + + return null; + } + + @Override + @Nullable + public Tooltip getCurrentTooltip(Point point) { + Tooltip tooltip = super.getCurrentTooltip(point); + + if (tooltip != null && getTransferHandler() != null) { + tooltip.add(new TranslatableComponent("text.auto_craft.move_items.tooltip").withStyle(ChatFormatting.YELLOW)); + } + + return tooltip; + } + + @Override + protected boolean doAction(double mouseX, double mouseY, int button) { + if (!ClientHelper.getInstance().isCheating() && !(Minecraft.getInstance().screen instanceof DisplayScreen) && Screen.hasControlDown()) { + try { + TransferHandler handler = getTransferHandler(); + + 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); + + if (transferResult.isBlocking()) { + if (transferResult.isReturningToScreen() && Minecraft.getInstance().screen != containerScreen) { + Minecraft.getInstance().setScreen(containerScreen); + REIRuntime.getInstance().getOverlay().ifPresent(ScreenOverlay::queueReloadOverlay); + } + return true; + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + return super.doAction(mouseX, mouseY, button); + } } } diff --git a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java index ce4f7d463..e39b48fc5 100644 --- a/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java +++ b/runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/InternalWidgets.java @@ -28,33 +28,25 @@ import com.mojang.blaze3d.vertex.PoseStack; import me.shedaniel.math.Point; import me.shedaniel.math.Rectangle; import me.shedaniel.math.impl.PointHelper; -import me.shedaniel.rei.api.client.REIRuntime; import me.shedaniel.rei.api.client.config.ConfigObject; import me.shedaniel.rei.api.client.gui.DrawableConsumer; import me.shedaniel.rei.api.client.gui.widgets.*; import me.shedaniel.rei.api.client.registry.display.DisplayCategory; -import me.shedaniel.rei.api.client.registry.transfer.TransferHandler; -import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerErrorRenderer; -import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry; import me.shedaniel.rei.api.common.display.Display; -import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.impl.ClientInternals; import me.shedaniel.rei.impl.client.gui.toast.CopyRecipeIdentifierToast; import me.shedaniel.rei.impl.client.gui.widget.basewidgets.*; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.resources.language.I18n; -import net.minecraft.network.chat.*; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.FormattedText; import net.minecraft.resources.ResourceLocation; -import org.apache.commons.lang3.mutable.Mutable; -import org.apache.commons.lang3.mutable.MutableObject; import org.jetbrains.annotations.ApiStatus; -import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; @@ -66,44 +58,10 @@ public final class InternalWidgets { private InternalWidgets() {} public static Widget createAutoCraftingButtonWidget(Rectangle displayBounds, Rectangle rectangle, Component text, Supplier displaySupplier, Supplier> idsSupplier, List setupDisplay, DisplayCategory category) { - AbstractContainerScreen containerScreen = REIRuntime.getInstance().getPreviousContainerScreen(); - Mutable> errorTooltip = new MutableObject<>(new ArrayList<>()); Button autoCraftingButton = Widgets.createButton(rectangle, text) .focusable(false) .onClick(button -> { - TransferHandler.Context context = TransferHandler.Context.create(true, containerScreen, displaySupplier.get()); - for (TransferHandler autoTransferHandler : TransferHandlerRegistry.getInstance()) - try { - TransferHandler.Result result = autoTransferHandler.handle(context); - if (result.isBlocking()) { - if (result.isReturningToScreen()) { - break; - } - return; - } - } catch (Exception e) { - e.printStackTrace(); - } - Minecraft.getInstance().setScreen(containerScreen); - REIRuntime.getInstance().getOverlay().get().queueReloadOverlay(); - }) - .tooltipSupplier(button -> { - List str = new ArrayList<>(errorTooltip.getValue()); - - if (Minecraft.getInstance().options.advancedItemTooltips) { - Collection locations = idsSupplier.get(); - if (!locations.isEmpty()) { - str.add(new TextComponent(" ")); - for (ResourceLocation location : locations) { - String t = I18n.get("text.rei.recipe_id", "", location.toString()); - if (t.startsWith("\n")) { - t = t.substring("\n".length()); - } - str.add(new TextComponent(t).withStyle(ChatFormatting.GRAY)); - } - } - } - return str.toArray(new Component[0]); + AutoCraftingEvaluator.evaluateAutoCrafting(true, Screen.hasShiftDown(), displaySupplier.get(), idsSupplier); }); return new DelegateWidget(autoCraftingButton) { boolean didJustRender = false; @@ -111,85 +69,32 @@ public final class InternalWidgets { @Override public void render(PoseStack poses, int mouseX, int mouseY, float delta) { didJustRender = false; - autoCraftingButton.setEnabled(false); - autoCraftingButton.setTint(0); - if (containerScreen == null) { - errorTooltip.setValue(Lists.newArrayList(new TranslatableComponent("error.rei.not.supported.move.items").withStyle(ChatFormatting.RED))); - renderIf(false, poses, mouseX, mouseY, delta); - return; - } + AutoCraftingEvaluator.AutoCraftingResult result = AutoCraftingEvaluator.evaluateAutoCrafting(false, false, displaySupplier.get(), idsSupplier); - List errors = new ArrayList<>(); - boolean hasApplicable = false; - TransferHandlerErrorRenderer errorRenderer = null; - TransferHandler.Context context = TransferHandler.Context.create(false, containerScreen, displaySupplier.get()); - for (TransferHandler transferHandler : TransferHandlerRegistry.getInstance()) { - try { - TransferHandler.Result result = transferHandler.handle(context); - if (result.isApplicable()) { - hasApplicable = true; - autoCraftingButton.setTint(result.getColor()); - - if (result.isSuccessful()) { - errors.clear(); - autoCraftingButton.setEnabled(true); - errorRenderer = null; - break; - } - - errors.add(result); - TransferHandlerErrorRenderer transferHandlerErrorRenderer = result.getErrorRenderer(transferHandler, context); - if (transferHandlerErrorRenderer != null) { - errorRenderer = transferHandlerErrorRenderer; - } - - if (result.isBlocking()) { - break; - } - } - } catch (Throwable e) { - e.printStackTrace(); - } - } + autoCraftingButton.setEnabled(result.successful); + autoCraftingButton.setTint(result.tint); - if (!hasApplicable) { - errorTooltip.setValue(Lists.newArrayList(new TranslatableComponent("error.rei.not.supported.move.items").withStyle(ChatFormatting.RED))); - renderIf(false, poses, mouseX, mouseY, delta); - return; + if (result.hasApplicable && (containsMouse(mouseX, mouseY) || autoCraftingButton.isFocused()) && result.renderer != null) { + result.renderer.render(poses, mouseX, mouseY, delta, setupDisplay, displayBounds, displaySupplier.get()); } - if ((containsMouse(mouseX, mouseY) || autoCraftingButton.isFocused()) && errorRenderer != null) { - errorRenderer.render(poses, mouseX, mouseY, delta, setupDisplay, displayBounds, displaySupplier.get()); - } - if (errors.isEmpty()) { - errorTooltip.setValue(Lists.newArrayList(new TranslatableComponent("text.auto_craft.move_items"))); - } else { - errorTooltip.setValue(Lists.newArrayList()); - List tooltipsFilled = new ArrayList<>(); - for (TransferHandler.Result error : errors) { - error.fillTooltip(tooltipsFilled); - } - - if (errors.size() == 1) { - for (Component tooltipFilled : tooltipsFilled) { - MutableComponent colored = tooltipFilled.copy().withStyle(ChatFormatting.RED); - if (!CollectionUtils.anyMatch(errorTooltip.getValue(), ss -> ss.getString().equalsIgnoreCase(tooltipFilled.getString()))) { - errorTooltip.getValue().add(colored); - } - } - } else { - errorTooltip.getValue().add(new TranslatableComponent("error.rei.multi.errors").withStyle(ChatFormatting.RED)); - for (Component tooltipFilled : tooltipsFilled) { - MutableComponent colored = new TextComponent("- ").withStyle(ChatFormatting.RED) - .append(tooltipFilled.copy().withStyle(ChatFormatting.RED)); - if (!CollectionUtils.anyMatch(errorTooltip.getValue(), ss -> ss.getString().equalsIgnoreCase(colored.getString()))) { - errorTooltip.getValue().add(colored); - } - } + renderIf(result.hasApplicable, poses, mouseX, mouseY, delta); + + if (didJustRender) { + if (!autoCraftingButton.isFocused() && containsMouse(mouseX, mouseY)) { + tryTooltip(result, new Point(mouseX, mouseY)); + } else if (autoCraftingButton.isFocused()) { + Rectangle bounds = autoCraftingButton.getBounds(); + tryTooltip(result, new Point(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2)); } } - renderIf(true, poses, mouseX, mouseY, delta); + } + + private void tryTooltip(AutoCraftingEvaluator.AutoCraftingResult result, Point point) { + if (result.tooltipRenderer != null) { + result.tooltipRenderer.accept(point, Tooltip::queue); + } } private void renderIf(boolean should, PoseStack poseStack, int mouseX, int mouseY, float delta) { 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 cf5f765d3..fcfc17807 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 @@ -24,6 +24,8 @@ package me.shedaniel.rei.impl.client.view; import com.google.common.base.Stopwatch; +import com.google.common.collect.AbstractIterator; +import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import me.shedaniel.rei.RoughlyEnoughItemsCore; @@ -48,6 +50,7 @@ import me.shedaniel.rei.api.common.util.CollectionUtils; import me.shedaniel.rei.api.common.util.EntryIngredients; import me.shedaniel.rei.api.common.util.EntryStacks; import me.shedaniel.rei.impl.client.gui.craftable.CraftableFilter; +import me.shedaniel.rei.impl.client.gui.widget.AutoCraftingEvaluator; import me.shedaniel.rei.impl.display.DisplaySpec; import net.minecraft.client.Minecraft; import net.minecraft.client.player.LocalPlayer; @@ -102,29 +105,15 @@ public class ViewsImpl implements Views { for (Display display : allRecipesFromCategory) { if (!displayRegistry.isDisplayVisible(display)) continue; if (!recipesForStacks.isEmpty()) { - back: - for (List> results : display.getOutputEntries()) { - for (EntryStack otherEntry : results) { - for (EntryStack recipesFor : recipesForStacks) { - if (EntryStacks.equalsFuzzy(otherEntry, recipesFor)) { - set.add(display); - break back; - } - } - } + if (isRecipesFor(recipesForStacks, display)) { + set.add(display); + break; } } if (!usagesForStacks.isEmpty()) { - back: - for (List> input : display.getInputEntries()) { - for (EntryStack otherEntry : input) { - for (EntryStack usagesFor : usagesForStacks) { - if (EntryStacks.equalsFuzzy(otherEntry, usagesFor)) { - set.add(display); - break back; - } - } - } + if (isUsagesFor(usagesForStacks, display)) { + set.add(display); + break; } } } @@ -220,7 +209,7 @@ public class ViewsImpl implements Views { Map wrappedSet = new LinkedHashMap<>(); List wrappeds = new ArrayList<>(); - for (Display display : entry.getValue()) { + for (Display display : sortAutoCrafting(entry.getValue())) { Wrapped wrapped = new Wrapped(display); if (wrappedSet.containsKey(wrapped)) { wrappedSet.get(wrapped).add(display); @@ -245,6 +234,62 @@ public class ViewsImpl implements Views { return resultSpeced; } + public static boolean isRecipesFor(List> stacks, Display display) { + return checkUsages(stacks, display, display.getOutputEntries()); + } + + public static boolean isUsagesFor(List> stacks, Display display) { + return checkUsages(stacks, display, display.getInputEntries()); + } + + private static boolean checkUsages(List> stacks, Display display, List entries) { + for (List> results : entries) { + for (EntryStack otherEntry : results) { + for (EntryStack recipesFor : stacks) { + if (EntryStacks.equalsFuzzy(otherEntry, recipesFor)) { + return true; + } + } + } + } + + return false; + } + + private static Iterable sortAutoCrafting(List displays) { + Set successfulDisplays = new LinkedHashSet<>(); + Set applicableDisplays = new LinkedHashSet<>(); + + for (Display display : displays) { + AutoCraftingEvaluator.AutoCraftingResult result = AutoCraftingEvaluator.evaluateAutoCrafting(false, false, display, null); + + if (result.successful) { + successfulDisplays.add(display); + } else if (result.hasApplicable) { + applicableDisplays.add(display); + } + } + + return Iterables.concat(successfulDisplays, applicableDisplays, () -> new AbstractIterator() { + Iterator iterator = displays.iterator(); + + @Override + protected Display computeNext() { + while (iterator.hasNext()) { + Display next = iterator.next(); + + if (successfulDisplays.contains(next) || applicableDisplays.contains(next)) { + continue; + } + + return next; + } + + return endOfData(); + } + }); + } + private static void generateLiveDisplays(DisplayRegistry displayRegistry, DynamicDisplayGenerator generator, ViewSearchBuilder builder, Consumer displayConsumer) { for (EntryStack stack : builder.getRecipesFor()) { Optional> recipeForDisplays = generator.getRecipeFor(stack); diff --git a/runtime/src/main/java/me/shedaniel/rei/plugin/autocrafting/DefaultCategoryHandler.java b/runtime/src/main/java/me/shedaniel/rei/plugin/autocrafting/DefaultCategoryHandler.java index 3441b32f2..371509d34 100644 --- a/runtime/src/main/java/me/shedaniel/rei/plugin/autocrafting/DefaultCategoryHandler.java +++ b/runtime/src/main/java/me/shedaniel/rei/plugin/autocrafting/DefaultCategoryHandler.java @@ -30,7 +30,6 @@ import it.unimi.dsi.fastutil.ints.IntList; 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.TransferHandlerErrorRenderer; import me.shedaniel.rei.api.common.category.CategoryIdentifier; import me.shedaniel.rei.api.common.display.Display; import me.shedaniel.rei.api.common.transfer.RecipeFinder; @@ -49,26 +48,11 @@ import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.ItemStack; -import org.jetbrains.annotations.Nullable; import java.util.List; @Environment(EnvType.CLIENT) public class DefaultCategoryHandler implements TransferHandler { - private static class ErrorData { - private MenuInfoContext menuInfoContext; - private MenuInfo menuInfo; - private List> inputs; - private IntList intList; - - public ErrorData(MenuInfoContext menuInfoContext, MenuInfo menuInfo, List> inputs, IntList intList) { - this.menuInfoContext = menuInfoContext; - this.menuInfo = menuInfo; - this.inputs = inputs; - this.intList = intList; - } - } - @Override public Result handle(Context context) { Display display = context.getDisplay(); @@ -94,7 +78,10 @@ public class DefaultCategoryHandler implements TransferHandler { List> input = menuInfo.getInputs(menuInfoContext, false); IntList intList = hasItems(menuInfoContext, menu, menuInfo, display, input); if (!intList.isEmpty()) { - return Result.createFailed(new TranslatableComponent("error.rei.not.enough.materials")).errorRenderer(new ErrorData(menuInfoContext, menuInfo, input, intList)); + return Result.createFailed(new TranslatableComponent("error.rei.not.enough.materials")) + .renderer((matrices, mouseX, mouseY, delta, widgets, bounds, d) -> { + menuInfo.renderMissingInput(menuInfoContext, input, intList, matrices, mouseX, mouseY, delta, widgets, bounds); + }); } if (!ClientHelper.getInstance().canUseMovePackets()) { return Result.createFailed(new TranslatableComponent("error.rei.not.on.server")); @@ -109,30 +96,13 @@ public class DefaultCategoryHandler implements TransferHandler { } FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); buf.writeResourceLocation(display.getCategoryIdentifier().getIdentifier()); - buf.writeBoolean(Screen.hasShiftDown()); + buf.writeBoolean(context.isStackedCrafting()); buf.writeNbt(menuInfo.save(menuInfoContext, display)); NetworkManager.sendToServer(RoughlyEnoughItemsNetwork.MOVE_ITEMS_PACKET, buf); return Result.createSuccessful(); } - @Override - @Environment(EnvType.CLIENT) - @Nullable - public TransferHandlerErrorRenderer provideErrorRenderer(Context context, Object data) { - if (data instanceof ErrorData) { - MenuInfoContext menuInfoContext = ((ErrorData) data).menuInfoContext; - MenuInfo menuInfo = ((ErrorData) data).menuInfo; - List> inputs = ((ErrorData) data).inputs; - IntList intList = ((ErrorData) data).intList; - return (matrices, mouseX, mouseY, delta, widgets, bounds, display) -> { - menuInfo.renderMissingInput(menuInfoContext, inputs, intList, matrices, mouseX, mouseY, delta, widgets, bounds); - }; - } - - return null; - } - @Override public double getPriority() { return -10; -- cgit