Selaa lähdekoodia

These days are great days to completely scrap an api

Unknown 5 vuotta sitten
vanhempi
sitoutus
7219715426

+ 0 - 25
src/main/java/me/shedaniel/rei/api/AutoCraftingHandler.java

@@ -1,25 +0,0 @@
-/*
- * Roughly Enough Items by Danielshe.
- * Licensed under the MIT License.
- */
-
-package me.shedaniel.rei.api;
-
-import me.shedaniel.rei.gui.ContainerScreenOverlay;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
-
-import java.util.function.Supplier;
-
-public interface AutoCraftingHandler {
-    
-    default double getPriority() {
-        return 0d;
-    }
-    
-    boolean handle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay);
-    
-    boolean canHandle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay);
-    
-}

+ 131 - 0
src/main/java/me/shedaniel/rei/api/AutoTransferHandler.java

@@ -0,0 +1,131 @@
+/*
+ * Roughly Enough Items by Danielshe.
+ * Licensed under the MIT License.
+ */
+
+package me.shedaniel.rei.api;
+
+import me.shedaniel.rei.client.ScreenHelper;
+import me.shedaniel.rei.gui.ContainerScreenOverlay;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
+import net.minecraft.container.Container;
+
+import java.util.function.Supplier;
+
+public interface AutoTransferHandler {
+    
+    default double getPriority() {
+        return 0d;
+    }
+    
+    Result handle(Context context);
+    
+    public interface Result {
+        static Result createSuccessful() {
+            return new ResultImpl();
+        }
+        
+        static Result createNotApplicable() {
+            return new ResultImpl(false);
+        }
+        
+        static Result createFailed(String errorKey) {
+            return new ResultImpl(errorKey);
+        }
+        
+        boolean isSuccessful();
+        
+        boolean isApplicable();
+        
+        String getErrorKey();
+    }
+    
+    public interface Context {
+        static Context create(boolean actuallyCrafting, AbstractContainerScreen<?> containerScreen, RecipeDisplay recipeDisplay) {
+            return new ContextImpl(actuallyCrafting, containerScreen, () -> recipeDisplay);
+        }
+        
+        default MinecraftClient getMinecraft() {
+            return MinecraftClient.getInstance();
+        }
+        
+        boolean isActuallyCrafting();
+        
+        AbstractContainerScreen<?> getContainerScreen();
+        
+        RecipeDisplay getRecipe();
+        
+        default Container getContainer() {
+            return getContainerScreen().getContainer();
+        }
+        
+        default ContainerScreenOverlay getOverlay() {
+            return ScreenHelper.getLastOverlay();
+        }
+    }
+    
+    public final class ResultImpl implements Result {
+        private boolean successful, applicable;
+        private String errorKey;
+        
+        private ResultImpl() {
+            this.successful = true;
+            this.applicable = true;
+        }
+        
+        public ResultImpl(boolean applicable) {
+            this.successful = false;
+            this.applicable = applicable;
+        }
+        
+        public ResultImpl(String errorKey) {
+            this.successful = false;
+            this.applicable = true;
+            this.errorKey = errorKey;
+        }
+        
+        @Override
+        public boolean isSuccessful() {
+            return successful;
+        }
+    
+        @Override
+        public boolean isApplicable() {
+            return applicable;
+        }
+    
+        @Override
+        public String getErrorKey() {
+            return errorKey;
+        }
+    }
+    
+    public final class ContextImpl implements Context {
+        boolean actuallyCrafting;
+        AbstractContainerScreen<?> containerScreen;
+        Supplier<RecipeDisplay> recipeDisplaySupplier;
+        
+        private ContextImpl(boolean actuallyCrafting, AbstractContainerScreen<?> containerScreen, Supplier<RecipeDisplay> recipeDisplaySupplier) {
+            this.actuallyCrafting = actuallyCrafting;
+            this.containerScreen = containerScreen;
+            this.recipeDisplaySupplier = recipeDisplaySupplier;
+        }
+        
+        @Override
+        public boolean isActuallyCrafting() {
+            return actuallyCrafting;
+        }
+        
+        @Override
+        public AbstractContainerScreen<?> getContainerScreen() {
+            return containerScreen;
+        }
+        
+        @Override
+        public RecipeDisplay getRecipe() {
+            return recipeDisplaySupplier.get();
+        }
+    }
+    
+}

+ 2 - 2
src/main/java/me/shedaniel/rei/api/RecipeHelper.java

@@ -29,9 +29,9 @@ public interface RecipeHelper {
         return RoughlyEnoughItemsCore.getRecipeHelper();
     }
     
-    AutoCraftingHandler registerAutoCraftingHandler(AutoCraftingHandler handler);
+    AutoTransferHandler registerAutoCraftingHandler(AutoTransferHandler handler);
     
-    List<AutoCraftingHandler> getSortedAutoCraftingHandler();
+    List<AutoTransferHandler> getSortedAutoCraftingHandler();
     
     /**
      * Gets the total recipe count registered

+ 2 - 0
src/main/java/me/shedaniel/rei/client/ConfigObject.java

@@ -50,6 +50,8 @@ public class ConfigObject {
     @Comment("Disable Recipe Book")
     public boolean disableRecipeBook = false;
     
+    public boolean clickableRecipeArrows = true;
+    
     public ItemCheatingMode itemCheatingMode = ItemCheatingMode.REI_LIKE;
     
     public boolean lightGrayRecipeBorder = false;

+ 6 - 6
src/main/java/me/shedaniel/rei/client/RecipeHelperImpl.java

@@ -43,7 +43,7 @@ public class RecipeHelperImpl implements RecipeHelper {
         VISIBILITY_HANDLER_COMPARATOR = comparator.reversed();
     }
     
-    private final List<AutoCraftingHandler> autoCraftingHandlers = Lists.newArrayList();
+    private final List<AutoTransferHandler> autoTransferHandlers = Lists.newArrayList();
     private final List<RecipeFunction> recipeFunctions = Lists.newArrayList();
     private final List<ScreenClickArea> screenClickAreas = Lists.newArrayList();
     private final AtomicInteger recipeCount = new AtomicInteger();
@@ -229,7 +229,7 @@ public class RecipeHelperImpl implements RecipeHelper {
         this.recipeFunctions.clear();
         this.displayVisibilityHandlers.clear();
         this.liveRecipeGenerators.clear();
-        this.autoCraftingHandlers.clear();
+        this.autoTransferHandlers.clear();
         ((DisplayHelperImpl) RoughlyEnoughItemsCore.getDisplayHelper()).resetCache();
         BaseBoundsHandler baseBoundsHandler = new BaseBoundsHandlerImpl();
         RoughlyEnoughItemsCore.getDisplayHelper().registerBoundsHandler(baseBoundsHandler);
@@ -303,14 +303,14 @@ public class RecipeHelperImpl implements RecipeHelper {
     }
     
     @Override
-    public AutoCraftingHandler registerAutoCraftingHandler(AutoCraftingHandler handler) {
-        autoCraftingHandlers.add(handler);
+    public AutoTransferHandler registerAutoCraftingHandler(AutoTransferHandler handler) {
+        autoTransferHandlers.add(handler);
         return handler;
     }
     
     @Override
-    public List<AutoCraftingHandler> getSortedAutoCraftingHandler() {
-        return autoCraftingHandlers.stream().sorted(Comparator.comparingDouble(AutoCraftingHandler::getPriority).reversed()).collect(Collectors.toList());
+    public List<AutoTransferHandler> getSortedAutoCraftingHandler() {
+        return autoTransferHandlers.stream().sorted(Comparator.comparingDouble(AutoTransferHandler::getPriority).reversed()).collect(Collectors.toList());
     }
     
     @Override

+ 2 - 2
src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java

@@ -373,7 +373,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
         GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
         GuiLighting.disable();
         this.renderWidgets(mouseX, mouseY, delta);
-        if (MinecraftClient.getInstance().currentScreen instanceof AbstractContainerScreen) {
+        if (MinecraftClient.getInstance().currentScreen instanceof AbstractContainerScreen && RoughlyEnoughItemsCore.getConfigManager().getConfig().clickableRecipeArrows) {
             ContainerScreenHooks hooks = (ContainerScreenHooks) MinecraftClient.getInstance().currentScreen;
             for (RecipeHelperImpl.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas())
                 if (area.getScreenClass().equals(MinecraftClient.getInstance().currentScreen.getClass()))
@@ -532,7 +532,7 @@ public class ContainerScreenOverlay extends AbstractParentElement implements Dra
     public boolean mouseClicked(double double_1, double double_2, int int_1) {
         if (!ScreenHelper.isOverlayVisible())
             return false;
-        if (MinecraftClient.getInstance().currentScreen instanceof AbstractContainerScreen) {
+        if (MinecraftClient.getInstance().currentScreen instanceof AbstractContainerScreen && RoughlyEnoughItemsCore.getConfigManager().getConfig().clickableRecipeArrows) {
             ContainerScreenHooks hooks = (ContainerScreenHooks) MinecraftClient.getInstance().currentScreen;
             for (RecipeHelperImpl.ScreenClickArea area : RecipeHelper.getInstance().getScreenClickAreas())
                 if (area.getScreenClass().equals(MinecraftClient.getInstance().currentScreen.getClass()))

+ 25 - 15
src/main/java/me/shedaniel/rei/gui/widget/AutoCraftingButtonWidget.java

@@ -5,7 +5,7 @@
 
 package me.shedaniel.rei.gui.widget;
 
-import me.shedaniel.rei.api.AutoCraftingHandler;
+import me.shedaniel.rei.api.AutoTransferHandler;
 import me.shedaniel.rei.api.RecipeDisplay;
 import me.shedaniel.rei.api.RecipeHelper;
 import me.shedaniel.rei.client.ScreenHelper;
@@ -22,6 +22,7 @@ public class AutoCraftingButtonWidget extends ButtonWidget {
     
     private final Supplier<RecipeDisplay> displaySupplier;
     private String extraTooltip;
+    private String errorTooltip;
     private AbstractContainerScreen<?> containerScreen;
     
     public AutoCraftingButtonWidget(Rectangle rectangle, String text, Supplier<RecipeDisplay> displaySupplier) {
@@ -34,14 +35,15 @@ public class AutoCraftingButtonWidget extends ButtonWidget {
     
     @Override
     public void onPressed() {
-        for (AutoCraftingHandler autoCraftingHandler : RecipeHelper.getInstance().getSortedAutoCraftingHandler())
-            if (autoCraftingHandler.canHandle(displaySupplier, minecraft, minecraft.currentScreen, containerScreen, ScreenHelper.getLastOverlay()))
-                try {
-                    if (autoCraftingHandler.handle(displaySupplier, minecraft, minecraft.currentScreen, containerScreen, ScreenHelper.getLastOverlay()))
-                        return;
-                } catch (Exception e) {
-                    e.printStackTrace();
-                }
+        AutoTransferHandler.Context context = AutoTransferHandler.Context.create(true, containerScreen, displaySupplier.get());
+        for (AutoTransferHandler autoTransferHandler : RecipeHelper.getInstance().getSortedAutoCraftingHandler())
+            try {
+                AutoTransferHandler.Result result = autoTransferHandler.handle(context);
+                if (result.isSuccessful())
+                    return;
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
         minecraft.openScreen(containerScreen);
         ScreenHelper.getLastOverlay().init();
     }
@@ -49,24 +51,32 @@ public class AutoCraftingButtonWidget extends ButtonWidget {
     @Override
     public void render(int mouseX, int mouseY, float delta) {
         this.enabled = false;
-        for (AutoCraftingHandler autoCraftingHandler : RecipeHelper.getInstance().getSortedAutoCraftingHandler())
-            if (autoCraftingHandler.canHandle(displaySupplier, minecraft, minecraft.currentScreen, containerScreen, ScreenHelper.getLastOverlay())) {
+        String error = null;
+        AutoTransferHandler.Context context = AutoTransferHandler.Context.create(false, containerScreen, displaySupplier.get());
+        for (AutoTransferHandler autoTransferHandler : RecipeHelper.getInstance().getSortedAutoCraftingHandler()) {
+            AutoTransferHandler.Result result = autoTransferHandler.handle(context);
+            if (result.isSuccessful()) {
                 enabled = true;
+                error = null;
                 break;
+            } else if (error == null) {
+                error = result.getErrorKey();
             }
+        }
+        errorTooltip = error;
         super.render(mouseX, mouseY, delta);
     }
     
     @Override
     public Optional<String> getTooltips() {
         if (this.minecraft.options.advancedItemTooltips)
-            if (enabled)
+            if (errorTooltip == null)
                 return Optional.ofNullable(I18n.translate("text.auto_craft.move_items") + extraTooltip);
             else
-                return Optional.ofNullable(I18n.translate("text.auto_craft.failed_move_items") + extraTooltip);
-        if (enabled)
+                return Optional.ofNullable("§c" + I18n.translate(errorTooltip) + extraTooltip);
+        if (errorTooltip == null)
             return Optional.ofNullable(I18n.translate("text.auto_craft.move_items"));
         else
-            return Optional.ofNullable(I18n.translate("text.auto_craft.failed_move_items"));
+            return Optional.ofNullable("§c" + I18n.translate(errorTooltip));
     }
 }

+ 15 - 19
src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoBlastingBookHandler.java

@@ -5,35 +5,31 @@
 
 package me.shedaniel.rei.plugin.autocrafting;
 
-import me.shedaniel.rei.api.AutoCraftingHandler;
-import me.shedaniel.rei.api.RecipeDisplay;
+import me.shedaniel.rei.api.AutoTransferHandler;
 import me.shedaniel.rei.client.ScreenHelper;
-import me.shedaniel.rei.gui.ContainerScreenOverlay;
 import me.shedaniel.rei.listeners.RecipeBookGuiHooks;
 import me.shedaniel.rei.plugin.blasting.DefaultBlastingDisplay;
-import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
 import net.minecraft.client.gui.screen.ingame.BlastFurnaceScreen;
 import net.minecraft.container.BlastFurnaceContainer;
 
-import java.util.function.Supplier;
-
-public class AutoBlastingBookHandler implements AutoCraftingHandler {
+public class AutoBlastingBookHandler implements AutoTransferHandler {
     @Override
-    public boolean handle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay) {
-        DefaultBlastingDisplay display = (DefaultBlastingDisplay) displaySupplier.get();
-        BlastFurnaceScreen furnaceScreen = (BlastFurnaceScreen) parentScreen;
-        minecraft.openScreen(furnaceScreen);
+    public Result handle(Context context) {
+        if (!(context.getContainerScreen() instanceof BlastFurnaceScreen) || !(context.getRecipe() instanceof DefaultBlastingDisplay))
+            return Result.createNotApplicable();
+        if (!((DefaultBlastingDisplay) context.getRecipe()).getOptionalRecipe().isPresent() || !context.getMinecraft().player.getRecipeBook().contains(((DefaultBlastingDisplay) context.getRecipe()).getOptionalRecipe().get()))
+            return Result.createNotApplicable();
+        if (!context.isActuallyCrafting())
+            return Result.createSuccessful();
+        
+        DefaultBlastingDisplay display = (DefaultBlastingDisplay) context.getRecipe();
+        BlastFurnaceScreen furnaceScreen = (BlastFurnaceScreen) context.getContainerScreen();
+        context.getMinecraft().openScreen(furnaceScreen);
         ((RecipeBookGuiHooks) furnaceScreen.getRecipeBookGui()).rei_getGhostSlots().reset();
         BlastFurnaceContainer container = furnaceScreen.getContainer();
-        minecraft.interactionManager.clickRecipe(container.syncId, display.getOptionalRecipe().get(), Screen.hasShiftDown());
+        context.getMinecraft().interactionManager.clickRecipe(container.syncId, display.getOptionalRecipe().get(), Screen.hasShiftDown());
         ScreenHelper.getLastOverlay().init();
-        return true;
-    }
-    
-    @Override
-    public boolean canHandle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay) {
-        return parentScreen instanceof BlastFurnaceScreen && displaySupplier.get() instanceof DefaultBlastingDisplay && ((DefaultBlastingDisplay) displaySupplier.get()).getOptionalRecipe().isPresent() && minecraft.player.getRecipeBook().contains(((DefaultBlastingDisplay) displaySupplier.get()).getOptionalRecipe().get());
+        return Result.createSuccessful();
     }
 }

+ 15 - 19
src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoCraftingTableBookHandler.java

@@ -5,35 +5,31 @@
 
 package me.shedaniel.rei.plugin.autocrafting;
 
-import me.shedaniel.rei.api.AutoCraftingHandler;
-import me.shedaniel.rei.api.RecipeDisplay;
+import me.shedaniel.rei.api.AutoTransferHandler;
 import me.shedaniel.rei.client.ScreenHelper;
-import me.shedaniel.rei.gui.ContainerScreenOverlay;
 import me.shedaniel.rei.listeners.RecipeBookGuiHooks;
 import me.shedaniel.rei.plugin.crafting.DefaultCraftingDisplay;
-import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
 import net.minecraft.client.gui.screen.ingame.CraftingTableScreen;
 import net.minecraft.container.CraftingTableContainer;
 
-import java.util.function.Supplier;
-
-public class AutoCraftingTableBookHandler implements AutoCraftingHandler {
+public class AutoCraftingTableBookHandler implements AutoTransferHandler {
     @Override
-    public boolean handle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay) {
-        DefaultCraftingDisplay display = (DefaultCraftingDisplay) displaySupplier.get();
-        CraftingTableScreen craftingTableScreen = (CraftingTableScreen) parentScreen;
-        minecraft.openScreen(craftingTableScreen);
+    public Result handle(Context context) {
+        if (!(context.getContainerScreen() instanceof CraftingTableScreen) || !(context.getRecipe() instanceof DefaultCraftingDisplay))
+            return Result.createNotApplicable();
+        if (!((DefaultCraftingDisplay) context.getRecipe()).getOptionalRecipe().isPresent() || !context.getMinecraft().player.getRecipeBook().contains(((DefaultCraftingDisplay) context.getRecipe()).getOptionalRecipe().get()))
+            return Result.createNotApplicable();
+        if (!context.isActuallyCrafting())
+            return Result.createSuccessful();
+        
+        DefaultCraftingDisplay display = (DefaultCraftingDisplay) context.getRecipe();
+        CraftingTableScreen craftingTableScreen = (CraftingTableScreen) context.getContainerScreen();
+        context.getMinecraft().openScreen(craftingTableScreen);
         ((RecipeBookGuiHooks) craftingTableScreen.getRecipeBookGui()).rei_getGhostSlots().reset();
         CraftingTableContainer container = craftingTableScreen.getContainer();
-        minecraft.interactionManager.clickRecipe(container.syncId, display.getOptionalRecipe().get(), Screen.hasShiftDown());
+        context.getMinecraft().interactionManager.clickRecipe(container.syncId, display.getOptionalRecipe().get(), Screen.hasShiftDown());
         ScreenHelper.getLastOverlay().init();
-        return true;
-    }
-    
-    @Override
-    public boolean canHandle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay) {
-        return parentScreen instanceof CraftingTableScreen && displaySupplier.get() instanceof DefaultCraftingDisplay && ((DefaultCraftingDisplay) displaySupplier.get()).getOptionalRecipe().isPresent() && minecraft.player.getRecipeBook().contains(((DefaultCraftingDisplay) displaySupplier.get()).getOptionalRecipe().get());
+        return Result.createSuccessful();
     }
 }

+ 21 - 20
src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoCraftingTableHandler.java

@@ -8,9 +8,7 @@ package me.shedaniel.rei.plugin.autocrafting;
 import com.google.common.collect.Lists;
 import io.netty.buffer.Unpooled;
 import me.shedaniel.rei.RoughlyEnoughItemsNetwork;
-import me.shedaniel.rei.api.AutoCraftingHandler;
-import me.shedaniel.rei.api.RecipeDisplay;
-import me.shedaniel.rei.gui.ContainerScreenOverlay;
+import me.shedaniel.rei.api.AutoTransferHandler;
 import me.shedaniel.rei.listeners.RecipeBookGuiHooks;
 import me.shedaniel.rei.plugin.crafting.DefaultCraftingCategory;
 import me.shedaniel.rei.plugin.crafting.DefaultCraftingDisplay;
@@ -18,7 +16,6 @@ import me.shedaniel.rei.plugin.crafting.DefaultShapedDisplay;
 import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
 import net.minecraft.client.gui.screen.ingame.CraftingTableScreen;
 import net.minecraft.client.gui.screen.ingame.InventoryScreen;
 import net.minecraft.client.gui.screen.recipebook.RecipeBookProvider;
@@ -28,18 +25,30 @@ import net.minecraft.util.DefaultedList;
 import net.minecraft.util.PacketByteBuf;
 
 import java.util.List;
-import java.util.function.Supplier;
 
-public class AutoCraftingTableHandler implements AutoCraftingHandler {
+public class AutoCraftingTableHandler implements AutoTransferHandler {
     @Override
-    public boolean handle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay) {
-        minecraft.openScreen(parentScreen);
-        ((RecipeBookGuiHooks) ((RecipeBookProvider) parentScreen).getRecipeBookGui()).rei_getGhostSlots().reset();
-        DefaultCraftingDisplay display = (DefaultCraftingDisplay) displaySupplier.get();
+    public Result handle(Context context) {
+        if (!(context.getRecipe() instanceof DefaultCraftingDisplay))
+            return Result.createNotApplicable();
+        if (!(context.getContainerScreen() instanceof CraftingTableScreen) && !(context.getContainerScreen() instanceof InventoryScreen))
+            return Result.createNotApplicable();
+        if (context.getContainerScreen() instanceof InventoryScreen && (((DefaultCraftingDisplay) context.getRecipe()).getWidth() > 2 || ((DefaultCraftingDisplay) context.getRecipe()).getHeight() > 2))
+            return Result.createFailed("error.rei.transfer.too_small");
+        if (!canUseMovePackets())
+            return Result.createFailed("error.rei.not.on.server");
+        if (!hasItems(context.getRecipe().getInput()))
+            return Result.createFailed("error.rei.not.enough.materials");
+        if (!context.isActuallyCrafting())
+            return Result.createSuccessful();
+        
+        context.getMinecraft().openScreen(context.getContainerScreen());
+        ((RecipeBookGuiHooks) ((RecipeBookProvider) context.getContainerScreen()).getRecipeBookGui()).rei_getGhostSlots().reset();
+        DefaultCraftingDisplay display = (DefaultCraftingDisplay) context.getRecipe();
         PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
         buf.writeUuid(RoughlyEnoughItemsNetwork.CRAFTING_TABLE_MOVE);
         buf.writeBoolean(Screen.hasShiftDown());
-        CraftingContainer craftingContainer = (CraftingContainer) parentScreen.getContainer();
+        CraftingContainer craftingContainer = (CraftingContainer) context.getContainer();
         
         List<List<ItemStack>> ogInput = display.getInput();
         List<List<ItemStack>> input = Lists.newArrayListWithCapacity(craftingContainer.getCraftingWidth() * craftingContainer.getCraftingHeight());
@@ -63,15 +72,7 @@ public class AutoCraftingTableHandler implements AutoCraftingHandler {
             }
         }
         ClientSidePacketRegistry.INSTANCE.sendToServer(RoughlyEnoughItemsNetwork.MOVE_ITEMS_PACKET, buf);
-        return true;
-    }
-    
-    @Override
-    public boolean canHandle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay) {
-        if (displaySupplier.get() instanceof DefaultCraftingDisplay && (parentScreen instanceof CraftingTableScreen || (parentScreen instanceof InventoryScreen && ((DefaultCraftingDisplay) displaySupplier.get()).getWidth() <= 2 && ((DefaultCraftingDisplay) displaySupplier.get()).getHeight() <= 2)) && canUseMovePackets()) {
-            return hasItems(displaySupplier.get().getInput());
-        }
-        return false;
+        return Result.createSuccessful();
     }
     
     @Override

+ 15 - 19
src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoFurnaceBookHandler.java

@@ -5,35 +5,31 @@
 
 package me.shedaniel.rei.plugin.autocrafting;
 
-import me.shedaniel.rei.api.AutoCraftingHandler;
-import me.shedaniel.rei.api.RecipeDisplay;
+import me.shedaniel.rei.api.AutoTransferHandler;
 import me.shedaniel.rei.client.ScreenHelper;
-import me.shedaniel.rei.gui.ContainerScreenOverlay;
 import me.shedaniel.rei.listeners.RecipeBookGuiHooks;
 import me.shedaniel.rei.plugin.smelting.DefaultSmeltingDisplay;
-import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
 import net.minecraft.client.gui.screen.ingame.FurnaceScreen;
 import net.minecraft.container.FurnaceContainer;
 
-import java.util.function.Supplier;
-
-public class AutoFurnaceBookHandler implements AutoCraftingHandler {
+public class AutoFurnaceBookHandler implements AutoTransferHandler {
     @Override
-    public boolean handle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay) {
-        DefaultSmeltingDisplay display = (DefaultSmeltingDisplay) displaySupplier.get();
-        FurnaceScreen furnaceScreen = (FurnaceScreen) parentScreen;
-        minecraft.openScreen(furnaceScreen);
+    public Result handle(Context context) {
+        if (!(context.getContainerScreen() instanceof FurnaceScreen) || !(context.getRecipe() instanceof DefaultSmeltingDisplay))
+            return Result.createNotApplicable();
+        if (!((DefaultSmeltingDisplay) context.getRecipe()).getOptionalRecipe().isPresent() || !context.getMinecraft().player.getRecipeBook().contains(((DefaultSmeltingDisplay) context.getRecipe()).getOptionalRecipe().get()))
+            return Result.createNotApplicable();
+        if (!context.isActuallyCrafting())
+            return Result.createSuccessful();
+        
+        DefaultSmeltingDisplay display = (DefaultSmeltingDisplay) context.getRecipe();
+        FurnaceScreen furnaceScreen = (FurnaceScreen) context.getContainerScreen();
+        context.getMinecraft().openScreen(furnaceScreen);
         ((RecipeBookGuiHooks) furnaceScreen.getRecipeBookGui()).rei_getGhostSlots().reset();
         FurnaceContainer container = furnaceScreen.getContainer();
-        minecraft.interactionManager.clickRecipe(container.syncId, display.getOptionalRecipe().get(), Screen.hasShiftDown());
+        context.getMinecraft().interactionManager.clickRecipe(container.syncId, display.getOptionalRecipe().get(), Screen.hasShiftDown());
         ScreenHelper.getLastOverlay().init();
-        return true;
-    }
-    
-    @Override
-    public boolean canHandle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay) {
-        return parentScreen instanceof FurnaceScreen && displaySupplier.get() instanceof DefaultSmeltingDisplay && ((DefaultSmeltingDisplay) displaySupplier.get()).getOptionalRecipe().isPresent() && minecraft.player.getRecipeBook().contains(((DefaultSmeltingDisplay) displaySupplier.get()).getOptionalRecipe().get());
+        return Result.createSuccessful();
     }
 }

+ 17 - 19
src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoInventoryBookHandler.java

@@ -5,35 +5,33 @@
 
 package me.shedaniel.rei.plugin.autocrafting;
 
-import me.shedaniel.rei.api.AutoCraftingHandler;
-import me.shedaniel.rei.api.RecipeDisplay;
+import me.shedaniel.rei.api.AutoTransferHandler;
 import me.shedaniel.rei.client.ScreenHelper;
-import me.shedaniel.rei.gui.ContainerScreenOverlay;
 import me.shedaniel.rei.listeners.RecipeBookGuiHooks;
 import me.shedaniel.rei.plugin.crafting.DefaultCraftingDisplay;
-import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
 import net.minecraft.client.gui.screen.ingame.InventoryScreen;
 import net.minecraft.container.PlayerContainer;
 
-import java.util.function.Supplier;
-
-public class AutoInventoryBookHandler implements AutoCraftingHandler {
+public class AutoInventoryBookHandler implements AutoTransferHandler {
     @Override
-    public boolean handle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay) {
-        DefaultCraftingDisplay display = (DefaultCraftingDisplay) displaySupplier.get();
-        InventoryScreen inventoryScreen = (InventoryScreen) parentScreen;
-        minecraft.openScreen(inventoryScreen);
+    public Result handle(Context context) {
+        if (!(context.getContainerScreen() instanceof InventoryScreen) || !(context.getRecipe() instanceof DefaultCraftingDisplay))
+            return Result.createNotApplicable();
+        if (!((DefaultCraftingDisplay) context.getRecipe()).getOptionalRecipe().isPresent() || !context.getMinecraft().player.getRecipeBook().contains(((DefaultCraftingDisplay) context.getRecipe()).getOptionalRecipe().get()))
+            return Result.createNotApplicable();
+        if (((DefaultCraftingDisplay) context.getRecipe()).getWidth() > 2 || ((DefaultCraftingDisplay) context.getRecipe()).getHeight() > 2)
+            return Result.createFailed("error.rei.transfer.too_small");
+        if (!context.isActuallyCrafting())
+            return Result.createSuccessful();
+        
+        DefaultCraftingDisplay display = (DefaultCraftingDisplay) context.getRecipe();
+        InventoryScreen inventoryScreen = (InventoryScreen) context.getContainerScreen();
+        context.getMinecraft().openScreen(inventoryScreen);
         ((RecipeBookGuiHooks) inventoryScreen.getRecipeBookGui()).rei_getGhostSlots().reset();
         PlayerContainer container = inventoryScreen.getContainer();
-        minecraft.interactionManager.clickRecipe(container.syncId, display.getOptionalRecipe().get(), Screen.hasShiftDown());
+        context.getMinecraft().interactionManager.clickRecipe(container.syncId, display.getOptionalRecipe().get(), Screen.hasShiftDown());
         ScreenHelper.getLastOverlay().init();
-        return true;
-    }
-    
-    @Override
-    public boolean canHandle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay) {
-        return parentScreen instanceof InventoryScreen && displaySupplier.get() instanceof DefaultCraftingDisplay && ((DefaultCraftingDisplay) displaySupplier.get()).getOptionalRecipe().isPresent() && minecraft.player.getRecipeBook().contains(((DefaultCraftingDisplay) displaySupplier.get()).getOptionalRecipe().get()) && ((DefaultCraftingDisplay) displaySupplier.get()).getWidth() <= 2 && ((DefaultCraftingDisplay) displaySupplier.get()).getHeight() <= 2;
+        return Result.createSuccessful();
     }
 }

+ 15 - 19
src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoSmokerBookHandler.java

@@ -5,35 +5,31 @@
 
 package me.shedaniel.rei.plugin.autocrafting;
 
-import me.shedaniel.rei.api.AutoCraftingHandler;
-import me.shedaniel.rei.api.RecipeDisplay;
+import me.shedaniel.rei.api.AutoTransferHandler;
 import me.shedaniel.rei.client.ScreenHelper;
-import me.shedaniel.rei.gui.ContainerScreenOverlay;
 import me.shedaniel.rei.listeners.RecipeBookGuiHooks;
 import me.shedaniel.rei.plugin.smoking.DefaultSmokingDisplay;
-import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
 import net.minecraft.client.gui.screen.ingame.SmokerScreen;
 import net.minecraft.container.SmokerContainer;
 
-import java.util.function.Supplier;
-
-public class AutoSmokerBookHandler implements AutoCraftingHandler {
+public class AutoSmokerBookHandler implements AutoTransferHandler {
     @Override
-    public boolean handle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay) {
-        DefaultSmokingDisplay display = (DefaultSmokingDisplay) displaySupplier.get();
-        SmokerScreen smokerScreen = (SmokerScreen) parentScreen;
-        minecraft.openScreen(smokerScreen);
+    public Result handle(Context context) {
+        if (!(context.getContainerScreen() instanceof SmokerScreen) || !(context.getRecipe() instanceof DefaultSmokingDisplay))
+            return Result.createNotApplicable();
+        if (!((DefaultSmokingDisplay) context.getRecipe()).getOptionalRecipe().isPresent() || !context.getMinecraft().player.getRecipeBook().contains(((DefaultSmokingDisplay) context.getRecipe()).getOptionalRecipe().get()))
+            return Result.createNotApplicable();
+        if (!context.isActuallyCrafting())
+            return Result.createSuccessful();
+        
+        DefaultSmokingDisplay display = (DefaultSmokingDisplay) context.getRecipe();
+        SmokerScreen smokerScreen = (SmokerScreen) context.getContainerScreen();
+        context.getMinecraft().openScreen(smokerScreen);
         ((RecipeBookGuiHooks) smokerScreen.getRecipeBookGui()).rei_getGhostSlots().reset();
         SmokerContainer container = smokerScreen.getContainer();
-        minecraft.interactionManager.clickRecipe(container.syncId, display.getOptionalRecipe().get(), Screen.hasShiftDown());
+        context.getMinecraft().interactionManager.clickRecipe(container.syncId, display.getOptionalRecipe().get(), Screen.hasShiftDown());
         ScreenHelper.getLastOverlay().init();
-        return true;
-    }
-    
-    @Override
-    public boolean canHandle(Supplier<RecipeDisplay> displaySupplier, MinecraftClient minecraft, Screen recipeViewingScreen, AbstractContainerScreen<?> parentScreen, ContainerScreenOverlay overlay) {
-        return parentScreen instanceof SmokerScreen && displaySupplier.get() instanceof DefaultSmokingDisplay && ((DefaultSmokingDisplay) displaySupplier.get()).getOptionalRecipe().isPresent() && minecraft.player.getRecipeBook().contains(((DefaultSmokingDisplay) displaySupplier.get()).getOptionalRecipe().get());
+        return Result.createSuccessful();
     }
 }

+ 47 - 33
src/main/java/me/shedaniel/rei/server/InputSlotCrafter.java

@@ -9,7 +9,6 @@ import com.google.common.collect.Lists;
 import net.minecraft.container.CraftingContainer;
 import net.minecraft.container.CraftingTableContainer;
 import net.minecraft.container.PlayerContainer;
-import net.minecraft.container.Slot;
 import net.minecraft.entity.player.PlayerInventory;
 import net.minecraft.inventory.Inventory;
 import net.minecraft.item.ItemStack;
@@ -17,7 +16,6 @@ import net.minecraft.recipe.RecipeFinder;
 import net.minecraft.server.network.ServerPlayerEntity;
 import net.minecraft.util.DefaultedList;
 
-import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -37,49 +35,65 @@ public class InputSlotCrafter<C extends Inventory> {
     }
     
     private void fillInputSlots(ServerPlayerEntity player, Map<Integer, List<ItemStack>> map, boolean hasShift) {
+        /*
+         * Steps:
+         * Return the already placed items on the grid
+         * Check if the player have the enough resource to even craft one
+         * Calculate how many items the player is going to craft
+         * Move the best suited items for the player to use
+         * Send container updates to the player
+         * Profit??
+         */
         this.inventory = player.inventory;
         if (this.canReturnInputs() || player.isCreative()) {
             // Return the already placed items on the grid
             this.returnInputs();
             
+            // Check if the player have the enough resource to even craft one
             if (!isPossibleToCraft(map)) {
                 craftingContainer.sendContentUpdates();
                 player.inventory.markDirty();
                 throw new NullPointerException();
             }
             
+            // Calculate how many items the player is going to craft
+            int amountCrafting = hasShift ? 0 : 1;
+            if (hasShift) {
+            
+            }
+            
             // TODO: Rewrite most parts of this
-            map.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getKey)).forEach(entry -> {
-                int id = entry.getKey().intValue();
-                List<ItemStack> possibleStacks = entry.getValue();
-                boolean done = false;
-                for (ItemStack possibleStack : possibleStacks) {
-                    int requiredCount = possibleStack.getCount();
-                    int invCount = 0;
-                    for (ItemStack stack : inventory.main) {
-                        if (ItemStack.areItemsEqualIgnoreDamage(possibleStack, stack))
-                            invCount += stack.getCount();
-                    }
-                    if (invCount >= requiredCount) {
-                        for (ItemStack stack : inventory.main) {
-                            if (ItemStack.areItemsEqualIgnoreDamage(possibleStack, stack)) {
-                                Slot containerSlot = craftingContainer.getSlot(id + craftingContainer.getCraftingResultSlotIndex() + 1);
-                                while (containerSlot.getStack().getCount() < requiredCount && !stack.isEmpty()) {
-                                    if (containerSlot.getStack().isEmpty()) {
-                                        containerSlot.setStack(new ItemStack(stack.getItem(), 1));
-                                    } else {
-                                        containerSlot.getStack().setCount(containerSlot.getStack().getCount() + 1);
-                                    }
-                                    stack.setCount(stack.getCount() - 1);
-                                }
-                                if (containerSlot.getStack().getCount() >= requiredCount)
-                                    break;
-                            }
-                        }
-                        break;
-                    }
-                }
-            });
+            //            map.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getKey)).forEach(entry -> {
+            //                int id = entry.getKey().intValue();
+            //                List<ItemStack> possibleStacks = entry.getValue();
+            //                boolean done = false;
+            //                for (ItemStack possibleStack : possibleStacks) {
+            //                    int requiredCount = possibleStack.getCount();
+            //                    int invCount = 0;
+            //                    for (ItemStack stack : inventory.main) {
+            //                        if (ItemStack.areItemsEqualIgnoreDamage(possibleStack, stack))
+            //                            invCount += stack.getCount();
+            //                    }
+            //                    if (invCount >= requiredCount) {
+            //                        for (ItemStack stack : inventory.main) {
+            //                            if (ItemStack.areItemsEqualIgnoreDamage(possibleStack, stack)) {
+            //                                Slot containerSlot = craftingContainer.getSlot(id + craftingContainer.getCraftingResultSlotIndex() + 1);
+            //                                while (containerSlot.getStack().getCount() < requiredCount && !stack.isEmpty()) {
+            //                                    if (containerSlot.getStack().isEmpty()) {
+            //                                        containerSlot.setStack(new ItemStack(stack.getItem(), 1));
+            //                                    } else {
+            //                                        containerSlot.getStack().setCount(containerSlot.getStack().getCount() + 1);
+            //                                    }
+            //                                    stack.setCount(stack.getCount() - 1);
+            //                                }
+            //                                if (containerSlot.getStack().getCount() >= requiredCount)
+            //                                    break;
+            //                            }
+            //                        }
+            //                        break;
+            //                    }
+            //                }
+            //            });
             
             craftingContainer.sendContentUpdates();
             player.inventory.markDirty();

+ 61 - 0
src/main/java/me/shedaniel/rei/server/RecipeGridAligner.java

@@ -0,0 +1,61 @@
+package me.shedaniel.rei.server;
+
+import net.minecraft.recipe.Recipe;
+import net.minecraft.recipe.ShapedRecipe;
+import net.minecraft.util.math.MathHelper;
+
+import java.util.Iterator;
+
+public interface RecipeGridAligner<T> {
+    default void alignRecipeToGrid(int craftingGridWidth, int craftingGridHeight, int resultSlotIndex, Recipe<?> recipe, Iterator<T> iterator_1, int int_4) {
+        int recipeWidth = craftingGridWidth;
+        int recipeHeight = craftingGridHeight;
+        if (recipe instanceof ShapedRecipe) {
+            ShapedRecipe shapedRecipe = (ShapedRecipe) recipe;
+            recipeWidth = shapedRecipe.getWidth();
+            recipeHeight = shapedRecipe.getHeight();
+        }
+        
+        int slotId = 0;
+        
+        for (int int_8 = 0; int_8 < craftingGridHeight; ++int_8) {
+            if (slotId == resultSlotIndex) {
+                ++slotId;
+            }
+            
+            boolean boolean_1 = (float) recipeHeight < (float) craftingGridHeight / 2.0F;
+            int int_9 = MathHelper.floor((float) craftingGridHeight / 2.0F - (float) recipeHeight / 2.0F);
+            if (boolean_1 && int_9 > int_8) {
+                slotId += craftingGridWidth;
+                ++int_8;
+            }
+            
+            for (int int_10 = 0; int_10 < craftingGridWidth; ++int_10) {
+                if (!iterator_1.hasNext()) {
+                    return;
+                }
+                
+                boolean_1 = (float) recipeWidth < (float) craftingGridWidth / 2.0F;
+                int_9 = MathHelper.floor((float) craftingGridWidth / 2.0F - (float) recipeWidth / 2.0F);
+                int int_11 = recipeWidth;
+                boolean boolean_2 = int_10 < recipeWidth;
+                if (boolean_1) {
+                    int_11 = int_9 + recipeWidth;
+                    boolean_2 = int_9 <= int_10 && int_10 < int_9 + recipeWidth;
+                }
+                
+                if (boolean_2) {
+                    this.acceptAlignedInput(iterator_1, slotId, int_4, int_8, int_10);
+                } else if (int_11 == int_10) {
+                    slotId += craftingGridWidth - int_10;
+                    break;
+                }
+                
+                ++slotId;
+            }
+        }
+        
+    }
+    
+    void acceptAlignedInput(Iterator<T> var1, int var2, int var3, int var4, int var5);
+}

+ 6 - 0
src/main/java/me/shedaniel/rei/utils/ClothScreenRegistry.java

@@ -129,6 +129,12 @@ public class ClothScreenRegistry {
                 .setSaveConsumer(s -> configManager.getConfig().weatherCommand = s)
                 .setTooltip(getConfigTooltip("weather_command"))
                 .build());
+        action.addEntry(eb.startBooleanToggle("text.rei.config.clickable_recipe_arrows", configManager.getConfig().clickableRecipeArrows)
+                .setDefaultValue(true)
+                .setYesNoTextSupplier(bool -> I18n.translate("text.rei.config.text." + bool))
+                .setSaveConsumer(bool -> configManager.getConfig().clickableRecipeArrows = bool)
+                .setTooltip(getConfigTooltip("clickable_recipe_arrows"))
+                .build());
         ConfigCategory modules = builder.getOrCreateCategory("text.rei.config.modules");
         modules.addEntry(eb.startBooleanToggle("text.rei.config.craftable_only", configManager.getConfig().enableCraftableOnlyButton)
                 .setDefaultValue(false)

+ 4 - 1
src/main/resources/assets/roughlyenoughitems/lang/en_us.json

@@ -46,9 +46,12 @@
   "ordering.rei.registry": "Registry",
   "ordering.rei.name": "Name",
   "ordering.rei.item_groups": "Item Groups",
-  "text.auto_craft.failed_move_items": "§cCan't move items!",
   "text.auto_craft.move_items": "Move Items",
+  "error.rei.transfer.too_small": "Unable to move items to a 2x2 grid.",
+  "error.rei.not.on.server": "REI is not on the server.",
+  "error.rei.not.enough.materials": "Not Enough Materials.",
   "text.rei.config.craftable_only": "Craftable Filter: ",
+  "text.rei.config.clickable_recipe_arrows": "Clickable Recipe Arrows: ",
   "text.rei.config.text.true": "Enabled",
   "text.rei.config.text.false": "Disabled",
   "text.rei.showing_craftable": "Showing Craftable",