Browse Source

Finishing the auto crafting API

Unknown 5 years ago
parent
commit
a0a014eaa2
22 changed files with 418 additions and 140 deletions
  1. 40 35
      src/main/java/me/shedaniel/rei/RoughlyEnoughItemsNetwork.java
  2. 21 4
      src/main/java/me/shedaniel/rei/api/AutoTransferHandler.java
  3. 17 0
      src/main/java/me/shedaniel/rei/api/TransferRecipeDisplay.java
  4. 4 6
      src/main/java/me/shedaniel/rei/gui/ContainerScreenOverlay.java
  5. 3 2
      src/main/java/me/shedaniel/rei/gui/RecipeViewingScreen.java
  6. 4 3
      src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java
  7. 68 9
      src/main/java/me/shedaniel/rei/gui/widget/AutoCraftingButtonWidget.java
  8. 2 2
      src/main/java/me/shedaniel/rei/gui/widget/EntryListWidget.java
  9. 5 9
      src/main/java/me/shedaniel/rei/plugin/DefaultAutoCraftingPlugin.java
  10. 17 0
      src/main/java/me/shedaniel/rei/plugin/DefaultServerContainerPlugin.java
  11. 38 38
      src/main/java/me/shedaniel/rei/plugin/autocrafting/DefaultCategoryHandler.java
  12. 18 1
      src/main/java/me/shedaniel/rei/plugin/blasting/DefaultBlastingDisplay.java
  13. 50 0
      src/main/java/me/shedaniel/rei/plugin/containers/CraftingContainerInfoWrapper.java
  14. 25 2
      src/main/java/me/shedaniel/rei/plugin/crafting/DefaultCraftingDisplay.java
  15. 19 2
      src/main/java/me/shedaniel/rei/plugin/smelting/DefaultSmeltingDisplay.java
  16. 19 1
      src/main/java/me/shedaniel/rei/plugin/smoking/DefaultSmokingDisplay.java
  17. 17 0
      src/main/java/me/shedaniel/rei/server/ContainerInfo.java
  18. 25 0
      src/main/java/me/shedaniel/rei/server/ContainerInfoHandler.java
  19. 21 24
      src/main/java/me/shedaniel/rei/server/InputSlotCrafter.java
  20. 1 1
      src/main/resources/assets/roughlyenoughitems/lang/en_us.json
  21. 1 1
      src/main/resources/assets/roughlyenoughitems/lang/zh_tw.json
  22. 3 0
      src/main/resources/fabric.mod.json

+ 40 - 35
src/main/java/me/shedaniel/rei/RoughlyEnoughItemsNetwork.java

@@ -11,6 +11,8 @@ import io.netty.buffer.Unpooled;
 import me.shedaniel.rei.server.InputSlotCrafter;
 import net.fabricmc.api.ModInitializer;
 import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.container.Container;
 import net.minecraft.container.CraftingContainer;
 import net.minecraft.container.PlayerContainer;
 import net.minecraft.item.ItemStack;
@@ -23,7 +25,6 @@ import net.minecraft.util.PacketByteBuf;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
 
 public class RoughlyEnoughItemsNetwork implements ModInitializer {
     
@@ -32,7 +33,7 @@ public class RoughlyEnoughItemsNetwork implements ModInitializer {
     public static final Identifier CREATE_ITEMS_MESSAGE_PACKET = new Identifier("roughlyenoughitems", "ci_msg");
     public static final Identifier MOVE_ITEMS_PACKET = new Identifier("roughlyenoughitems", "move_items");
     public static final Identifier NOT_ENOUGH_ITEMS_PACKET = new Identifier("roughlyenoughitems", "og_not_enough");
-    public static final UUID CRAFTING_TABLE_MOVE = UUID.fromString("190c2b2d-d1f6-4149-a4a8-62860189403e");
+    //    public static final UUID CRAFTING_TABLE_MOVE = UUID.fromString("190c2b2d-d1f6-4149-a4a8-62860189403e");
     
     @Override
     public void onInitialize() {
@@ -58,49 +59,53 @@ public class RoughlyEnoughItemsNetwork implements ModInitializer {
                 player.addChatMessage(new TranslatableText("text.rei.failed_cheat_items"), false);
         });
         ServerSidePacketRegistry.INSTANCE.register(MOVE_ITEMS_PACKET, (packetContext, packetByteBuf) -> {
-            UUID type = packetByteBuf.readUuid();
+            //            UUID type = packetByteBuf.readUuid();
+            Identifier category = packetByteBuf.readIdentifier();
             ServerPlayerEntity player = (ServerPlayerEntity) packetContext.getPlayer();
-            CraftingContainer container = (CraftingContainer) player.container;
+            Container container = player.container;
             PlayerContainer playerContainer = player.playerContainer;
-            if (type.equals(CRAFTING_TABLE_MOVE)) {
-                try {
-                    boolean shift = packetByteBuf.readBoolean();
-                    Map<Integer, List<ItemStack>> input = Maps.newHashMap();
-                    int mapSize = packetByteBuf.readInt();
-                    for (int i = 0; i < mapSize; i++) {
-                        List<ItemStack> list = Lists.newArrayList();
-                        int count = packetByteBuf.readInt();
-                        for (int j = 0; j < count; j++) {
-                            list.add(packetByteBuf.readItemStack());
-                        }
-                        input.put(i, list);
+            //            if (type.equals(CRAFTING_TABLE_MOVE)) {
+            try {
+                boolean shift = packetByteBuf.readBoolean();
+                Map<Integer, List<ItemStack>> input = Maps.newHashMap();
+                int mapSize = packetByteBuf.readInt();
+                for (int i = 0; i < mapSize; i++) {
+                    List<ItemStack> list = Lists.newArrayList();
+                    int count = packetByteBuf.readInt();
+                    for (int j = 0; j < count; j++) {
+                        list.add(packetByteBuf.readItemStack());
                     }
-                    try {
-                        InputSlotCrafter.start(container, player, input, shift);
-                    } catch (NullPointerException e) {
-                        PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
-                        buf.writeInt(input.size());
-                        input.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getKey)).forEach(entry -> {
-                            List<ItemStack> stacks = entry.getValue();
-                            buf.writeInt(stacks.size());
-                            for (ItemStack stack : stacks) {
-                                buf.writeItemStack(stack);
-                            }
-                        });
-                        if (ServerSidePacketRegistry.INSTANCE.canPlayerReceive(player, NOT_ENOUGH_ITEMS_PACKET)) {
-                            ServerSidePacketRegistry.INSTANCE.sendToPlayer(player, NOT_ENOUGH_ITEMS_PACKET, buf);
+                    input.put(i, list);
+                }
+                try {
+                    InputSlotCrafter.start(category, container, player, input, shift);
+                } catch (NullPointerException e) {
+                    if (!(container instanceof CraftingContainer))
+                        return;
+                    PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
+                    buf.writeInt(input.size());
+                    input.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getKey)).forEach(entry -> {
+                        List<ItemStack> stacks = entry.getValue();
+                        buf.writeInt(stacks.size());
+                        for (ItemStack stack : stacks) {
+                            buf.writeItemStack(stack);
                         }
-                    } catch (IllegalStateException e) {
-                        player.sendMessage(new TranslatableText(e.getMessage()).formatted(Formatting.RED));
-                    } catch (Exception e) {
-                        player.sendMessage(new TranslatableText("error.rei.internal.error",e.getMessage()).formatted(Formatting.RED));
-                        e.printStackTrace();
+                    });
+                    if (ServerSidePacketRegistry.INSTANCE.canPlayerReceive(player, NOT_ENOUGH_ITEMS_PACKET)) {
+                        ServerSidePacketRegistry.INSTANCE.sendToPlayer(player, NOT_ENOUGH_ITEMS_PACKET, buf);
                     }
+                } catch (IllegalStateException e) {
+                    player.sendMessage(new TranslatableText(e.getMessage()).formatted(Formatting.RED));
                 } catch (Exception e) {
+                    player.sendMessage(new TranslatableText("error.rei.internal.error", e.getMessage()).formatted(Formatting.RED));
                     e.printStackTrace();
                 }
+            } catch (Exception e) {
+                e.printStackTrace();
             }
+            //            }
         });
+        FabricLoader.getInstance().getEntrypoints("rei_containers", Runnable.class).forEach(Runnable::run);
     }
     
 }

+ 21 - 4
src/main/java/me/shedaniel/rei/api/AutoTransferHandler.java

@@ -7,8 +7,8 @@ package me.shedaniel.rei.api;
 
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import it.unimi.dsi.fastutil.ints.IntList;
-import me.shedaniel.rei.impl.ScreenHelper;
 import me.shedaniel.rei.gui.ContainerScreenOverlay;
+import me.shedaniel.rei.impl.ScreenHelper;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
 import net.minecraft.container.Container;
@@ -33,13 +33,23 @@ public interface AutoTransferHandler {
         }
         
         static Result createFailed(String errorKey) {
-            return new ResultImpl(errorKey, new IntArrayList());
+            return new ResultImpl(errorKey, new IntArrayList(), 1744764928);
+        }
+        
+        static Result createFailedCustomButtonColor(String errorKey, int color) {
+            return new ResultImpl(errorKey, new IntArrayList(), color);
         }
         
         static Result createFailed(String errorKey, IntList redSlots) {
-            return new ResultImpl(errorKey, redSlots);
+            return new ResultImpl(errorKey, redSlots, 1744764928);
+        }
+        
+        static Result createFailedCustomButtonColor(String errorKey, IntList redSlots, int color) {
+            return new ResultImpl(errorKey, redSlots, color);
         }
         
+        int getColor();
+        
         boolean isSuccessful();
         
         boolean isApplicable();
@@ -75,6 +85,7 @@ public interface AutoTransferHandler {
         private boolean successful, applicable;
         private String errorKey;
         private IntList integers = new IntArrayList();
+        private int color;
         
         private ResultImpl() {
             this.successful = true;
@@ -86,12 +97,18 @@ public interface AutoTransferHandler {
             this.applicable = applicable;
         }
         
-        public ResultImpl(String errorKey, IntList integers) {
+        public ResultImpl(String errorKey, IntList integers, int color) {
             this.successful = false;
             this.applicable = true;
             this.errorKey = errorKey;
             if (integers != null)
                 this.integers = integers;
+            this.color = color;
+        }
+        
+        @Override
+        public int getColor() {
+            return color;
         }
         
         @Override

+ 17 - 0
src/main/java/me/shedaniel/rei/api/TransferRecipeDisplay.java

@@ -0,0 +1,17 @@
+package me.shedaniel.rei.api;
+
+import me.shedaniel.rei.server.ContainerInfo;
+import net.minecraft.container.Container;
+import net.minecraft.item.ItemStack;
+
+import java.util.List;
+
+public interface TransferRecipeDisplay extends RecipeDisplay {
+    
+    int getWidth();
+    
+    int getHeight();
+    
+    List<List<ItemStack>> getOrganisedInput(ContainerInfo<Container> containerInfo, Container container);
+    
+}

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

@@ -8,7 +8,6 @@ package me.shedaniel.rei.gui;
 import com.google.common.collect.Lists;
 import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.cloth.api.ClientUtils;
-import me.shedaniel.clothconfig2.api.MouseUtils;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.ClientHelper;
 import me.shedaniel.rei.api.DisplayHelper;
@@ -500,7 +499,7 @@ public class ContainerScreenOverlay extends Widget {
     
     @Override
     public boolean keyPressed(int int_1, int int_2, int int_3) {
-        if (ScreenHelper.isOverlayVisible() && isInside(MouseUtils.getMouseLocation()))
+        if (ScreenHelper.isOverlayVisible())
             for (Element listener : widgets)
                 if (listener.keyPressed(int_1, int_2, int_3))
                     return true;
@@ -532,10 +531,9 @@ public class ContainerScreenOverlay extends Widget {
     public boolean charTyped(char char_1, int int_1) {
         if (!ScreenHelper.isOverlayVisible())
             return false;
-        if (isInside(MouseUtils.getMouseLocation()))
-            for (Element listener : widgets)
-                if (listener.charTyped(char_1, int_1))
-                    return true;
+        for (Element listener : widgets)
+            if (listener.charTyped(char_1, int_1))
+                return true;
         return false;
     }
     

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

@@ -278,9 +278,10 @@ public class RecipeViewingScreen extends Screen {
             final Supplier<RecipeDisplay> displaySupplier = () -> currentDisplayed.get(finalI);
             int displayWidth = selectedCategory.getDisplayWidth(displaySupplier.get());
             final Rectangle displayBounds = new Rectangle((int) getBounds().getCenterX() - displayWidth / 2, getBounds().y + 40 + recipeHeight * i + 7 * i, displayWidth, recipeHeight);
-            widgets.addAll(selectedCategory.setupDisplay(displaySupplier, displayBounds));
+            List<Widget> setupDisplay = selectedCategory.setupDisplay(displaySupplier, displayBounds);
+            this.widgets.addAll(setupDisplay);
             if (supplier.isPresent() && supplier.get().get(displayBounds) != null)
-                widgets.add(new AutoCraftingButtonWidget(supplier.get().get(displayBounds), supplier.get().getButtonText(), displaySupplier));
+                this.widgets.add(new AutoCraftingButtonWidget(supplier.get().get(displayBounds), supplier.get().getButtonText(), displaySupplier, setupDisplay));
         }
         if (choosePageActivated)
             recipeChoosePageWidget = new RecipeChoosePageWidget(this, page, getTotalPages(selectedCategory));

+ 4 - 3
src/main/java/me/shedaniel/rei/gui/VillagerRecipeViewingScreen.java

@@ -12,9 +12,9 @@ import com.zeitheron.hammercore.client.utils.Scissors;
 import me.shedaniel.cloth.api.ClientUtils;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.*;
-import me.shedaniel.rei.impl.ScreenHelper;
 import me.shedaniel.rei.gui.renderers.RecipeRenderer;
 import me.shedaniel.rei.gui.widget.*;
+import me.shedaniel.rei.impl.ScreenHelper;
 import net.minecraft.client.MinecraftClient;
 import net.minecraft.client.gui.Element;
 import net.minecraft.client.gui.screen.Screen;
@@ -128,10 +128,11 @@ public class VillagerRecipeViewingScreen extends Screen {
         this.widgets.add(new SlotBaseWidget(scrollListBounds));
         
         Rectangle recipeBounds = new Rectangle(bounds.x + 100 + (guiWidth - 100) / 2 - category.getDisplayWidth(display) / 2, bounds.y + bounds.height / 2 - category.getDisplayHeight() / 2, category.getDisplayWidth(display), category.getDisplayHeight());
-        this.widgets.addAll(category.setupDisplay(() -> display, recipeBounds));
+        List<Widget> setupDisplay = category.setupDisplay(() -> display, recipeBounds);
+        this.widgets.addAll(setupDisplay);
         Optional<ButtonAreaSupplier> supplier = RecipeHelper.getInstance().getSpeedCraftButtonArea(category);
         if (supplier.isPresent() && supplier.get().get(recipeBounds) != null)
-            this.widgets.add(new AutoCraftingButtonWidget(supplier.get().get(recipeBounds), supplier.get().getButtonText(), () -> display));
+            this.widgets.add(new AutoCraftingButtonWidget(supplier.get().get(recipeBounds), supplier.get().getButtonText(), () -> display, setupDisplay));
         
         int index = 0;
         for (RecipeDisplay recipeDisplay : categoryMap.get(category)) {

+ 68 - 9
src/main/java/me/shedaniel/rei/gui/widget/AutoCraftingButtonWidget.java

@@ -5,6 +5,7 @@
 
 package me.shedaniel.rei.gui.widget;
 
+import com.mojang.blaze3d.platform.GlStateManager;
 import me.shedaniel.rei.api.AutoTransferHandler;
 import me.shedaniel.rei.api.RecipeDisplay;
 import me.shedaniel.rei.api.RecipeHelper;
@@ -13,8 +14,10 @@ import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.util.Formatting;
 import net.minecraft.util.Identifier;
+import net.minecraft.util.math.MathHelper;
 
 import java.awt.*;
+import java.util.List;
 import java.util.Optional;
 import java.util.function.Supplier;
 
@@ -23,14 +26,17 @@ public class AutoCraftingButtonWidget extends ButtonWidget {
     private final Supplier<RecipeDisplay> displaySupplier;
     private String extraTooltip;
     private String errorTooltip;
+    private List<Widget> setupDisplay;
     private AbstractContainerScreen<?> containerScreen;
+    private  boolean visible = false;
     
-    public AutoCraftingButtonWidget(Rectangle rectangle, String text, Supplier<RecipeDisplay> displaySupplier) {
+    public AutoCraftingButtonWidget(Rectangle rectangle, String text, Supplier<RecipeDisplay> displaySupplier, List<Widget> setupDisplay) {
         super(rectangle, text);
         this.displaySupplier = () -> displaySupplier.get();
         Optional<Identifier> recipe = displaySupplier.get().getRecipeLocation();
         extraTooltip = recipe.isPresent() ? I18n.translate("text.rei.recipe_id", Formatting.GRAY.toString(), recipe.get().toString()) : "";
         this.containerScreen = ScreenHelper.getLastContainerScreen();
+        this.setupDisplay = setupDisplay;
     }
     
     @Override
@@ -52,19 +58,72 @@ public class AutoCraftingButtonWidget extends ButtonWidget {
     public void render(int mouseX, int mouseY, float delta) {
         this.enabled = false;
         String error = null;
+        int color = 0;
+        visible = false;
         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();
+            try {
+                AutoTransferHandler.Result result = autoTransferHandler.handle(context);
+                if (result.isApplicable())
+                    visible = true;
+                if (result.isSuccessful()) {
+                    enabled = true;
+                    error = null;
+                    break;
+                } else if (error == null) {
+                    error = result.getErrorKey();
+                    color = result.getColor();
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
             }
         }
+        if (!visible)
+            enabled = false;
         errorTooltip = error;
-        super.render(mouseX, mouseY, delta);
+        int x = getBounds().x, y = getBounds().y, width = getBounds().width, height = getBounds().height;
+        minecraft.getTextureManager().bindTexture(ScreenHelper.isDarkModeEnabled() ? BUTTON_LOCATION_DARK : BUTTON_LOCATION);
+        GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+        int textureOffset = this.getTextureId(isHovered(mouseX, mouseY));
+        GlStateManager.enableBlend();
+        GlStateManager.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
+        GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
+        //Four Corners
+        blit(x, y, 0, textureOffset * 80, 4, 4);
+        blit(x + width - 4, y, 252, textureOffset * 80, 4, 4);
+        blit(x, y + height - 4, 0, textureOffset * 80 + 76, 4, 4);
+        blit(x + width - 4, y + height - 4, 252, textureOffset * 80 + 76, 4, 4);
+        
+        //Sides
+        blit(x + 4, y, 4, textureOffset * 80, MathHelper.ceil((width - 8) / 2f), 4);
+        blit(x + 4, y + height - 4, 4, textureOffset * 80 + 76, MathHelper.ceil((width - 8) / 2f), 4);
+        blit(x + 4 + MathHelper.ceil((width - 8) / 2f), y + height - 4, 252 - MathHelper.floor((width - 8) / 2f), textureOffset * 80 + 76, MathHelper.floor((width - 8) / 2f), 4);
+        blit(x + 4 + MathHelper.ceil((width - 8) / 2f), y, 252 - MathHelper.floor((width - 8) / 2f), textureOffset * 80, MathHelper.floor((width - 8) / 2f), 4);
+        for (int i = y + 4; i < y + height - 4; i += 76) {
+            blit(x, i, 0, 4 + textureOffset * 80, MathHelper.ceil(width / 2f), MathHelper.clamp(y + height - 4 - i, 0, 76));
+            blit(x + MathHelper.ceil(width / 2f), i, 256 - MathHelper.floor(width / 2f), 4 + textureOffset * 80, MathHelper.floor(width / 2f), MathHelper.clamp(y + height - 4 - i, 0, 76));
+        }
+        
+        int colour = 14737632;
+        if (!this.visible) {
+            colour = 10526880;
+        } else  if (enabled && isHovered(mouseX, mouseY)) {
+            colour = 16777120;
+        }
+        
+        fillGradient(x, y, x + width, y + height, color, color);
+        this.drawCenteredString(font, text, x + width / 2, y + (height - 8) / 2, colour);
+        
+        if (getTooltips().isPresent())
+            if (!focused && containsMouse(mouseX, mouseY))
+                ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(getTooltips().get().split("\n")));
+            else if (focused)
+                ScreenHelper.getLastOverlay().addTooltip(QueuedTooltip.create(new Point(x + width / 2, y + height / 2), getTooltips().get().split("\n")));
+    }
+    
+    @Override
+    protected int getTextureId(boolean boolean_1) {
+        return !visible ? 0 :boolean_1 && enabled ? 2 : 1;
     }
     
     @Override

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

@@ -421,13 +421,13 @@ public class EntryListWidget extends Widget {
         if (!RoughlyEnoughItemsCore.getConfigManager().isCraftableOnlyEnabled() || stacks.isEmpty() || inventoryItems.isEmpty())
             return Collections.unmodifiableList(stacks);
         List<ItemStack> workingItems = RecipeHelper.getInstance().findCraftableByItems(inventoryItems);
-        List<Entry> newList = Lists.newArrayList();
+        List<Entry> newList = Lists.newLinkedList();
         for (ItemStack workingItem : workingItems) {
             Optional<Entry> any = stacks.stream().filter(i -> i.getItemStack() != null && i.getItemStack().isItemEqualIgnoreDamage(workingItem)).findAny();
             if (any.isPresent())
                 newList.add(any.get());
         }
-        return newList;
+        return Collections.unmodifiableList(newList);
     }
     
     public List<SearchArgument[]> getLastSearchArgument() {

+ 5 - 9
src/main/java/me/shedaniel/rei/plugin/DefaultAutoCraftingPlugin.java

@@ -8,10 +8,7 @@ package me.shedaniel.rei.plugin;
 import me.shedaniel.rei.RoughlyEnoughItemsCore;
 import me.shedaniel.rei.api.RecipeHelper;
 import me.shedaniel.rei.api.plugins.REIPluginV0;
-import me.shedaniel.rei.plugin.autocrafting.AutoBlastingBookHandler;
-import me.shedaniel.rei.plugin.autocrafting.AutoCraftingHandler;
-import me.shedaniel.rei.plugin.autocrafting.AutoFurnaceBookHandler;
-import me.shedaniel.rei.plugin.autocrafting.AutoSmokerBookHandler;
+import me.shedaniel.rei.plugin.autocrafting.DefaultCategoryHandler;
 import net.fabricmc.loader.api.SemanticVersion;
 import net.fabricmc.loader.util.version.VersionParsingException;
 import net.minecraft.util.Identifier;
@@ -37,10 +34,9 @@ public class DefaultAutoCraftingPlugin implements REIPluginV0 {
         }
         //        recipeHelper.registerAutoCraftingHandler(new AutoCraftingTableBookHandler());
         //        recipeHelper.registerAutoCraftingHandler(new AutoInventoryBookHandler());
-        recipeHelper.registerAutoCraftingHandler(new AutoFurnaceBookHandler());
-        recipeHelper.registerAutoCraftingHandler(new AutoSmokerBookHandler());
-        recipeHelper.registerAutoCraftingHandler(new AutoBlastingBookHandler());
-        recipeHelper.registerAutoCraftingHandler(new AutoCraftingHandler());
+        //        recipeHelper.registerAutoCraftingHandler(new AutoFurnaceBookHandler());
+        //        recipeHelper.registerAutoCraftingHandler(new AutoSmokerBookHandler());
+        //        recipeHelper.registerAutoCraftingHandler(new AutoBlastingBookHandler());
+        recipeHelper.registerAutoCraftingHandler(new DefaultCategoryHandler());
     }
-    
 }

+ 17 - 0
src/main/java/me/shedaniel/rei/plugin/DefaultServerContainerPlugin.java

@@ -0,0 +1,17 @@
+package me.shedaniel.rei.plugin;
+
+import me.shedaniel.rei.plugin.containers.CraftingContainerInfoWrapper;
+import me.shedaniel.rei.server.ContainerInfoHandler;
+import net.minecraft.container.*;
+import net.minecraft.util.Identifier;
+
+public class DefaultServerContainerPlugin implements Runnable {
+    @Override
+    public void run() {
+        ContainerInfoHandler.registerContainerInfo(new Identifier("minecraft", "plugins/crafting"), new CraftingContainerInfoWrapper(CraftingTableContainer.class));
+        ContainerInfoHandler.registerContainerInfo(new Identifier("minecraft", "plugins/crafting"), new CraftingContainerInfoWrapper(PlayerContainer.class));
+        ContainerInfoHandler.registerContainerInfo(new Identifier("minecraft", "plugins/smelting"), new CraftingContainerInfoWrapper(FurnaceContainer.class));
+        ContainerInfoHandler.registerContainerInfo(new Identifier("minecraft", "plugins/smoking"), new CraftingContainerInfoWrapper(SmokerContainer.class));
+        ContainerInfoHandler.registerContainerInfo(new Identifier("minecraft", "plugins/blasting"), new CraftingContainerInfoWrapper(BlastFurnaceContainer.class));
+    }
+}

+ 38 - 38
src/main/java/me/shedaniel/rei/plugin/autocrafting/AutoCraftingHandler.java → src/main/java/me/shedaniel/rei/plugin/autocrafting/DefaultCategoryHandler.java

@@ -5,64 +5,60 @@
 
 package me.shedaniel.rei.plugin.autocrafting;
 
-import com.google.common.collect.Lists;
 import io.netty.buffer.Unpooled;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
 import me.shedaniel.rei.RoughlyEnoughItemsNetwork;
 import me.shedaniel.rei.api.AutoTransferHandler;
+import me.shedaniel.rei.api.TransferRecipeDisplay;
 import me.shedaniel.rei.listeners.RecipeBookGuiHooks;
-import me.shedaniel.rei.plugin.crafting.DefaultCraftingCategory;
-import me.shedaniel.rei.plugin.crafting.DefaultCraftingDisplay;
-import me.shedaniel.rei.plugin.crafting.DefaultShapedDisplay;
+import me.shedaniel.rei.server.ContainerInfo;
+import me.shedaniel.rei.server.ContainerInfoHandler;
 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.CraftingTableScreen;
-import net.minecraft.client.gui.screen.ingame.InventoryScreen;
+import net.minecraft.client.gui.screen.ingame.AbstractContainerScreen;
 import net.minecraft.client.gui.screen.recipebook.RecipeBookProvider;
-import net.minecraft.container.CraftingContainer;
+import net.minecraft.client.resource.language.I18n;
+import net.minecraft.container.Container;
 import net.minecraft.item.ItemStack;
 import net.minecraft.util.DefaultedList;
 import net.minecraft.util.PacketByteBuf;
 
 import java.util.List;
 
-public class AutoCraftingHandler implements AutoTransferHandler {
+public class DefaultCategoryHandler implements AutoTransferHandler {
+    
     @Override
     public Result handle(Context context) {
-        if (!(context.getRecipe() instanceof DefaultCraftingDisplay))
+        if (!(context.getRecipe() instanceof TransferRecipeDisplay))
+            return Result.createNotApplicable();
+        TransferRecipeDisplay recipe = (TransferRecipeDisplay) context.getRecipe();
+        if (!ContainerInfoHandler.isCategoryHandled(recipe.getRecipeCategory()))
             return Result.createNotApplicable();
-        if (!(context.getContainerScreen() instanceof CraftingTableScreen) && !(context.getContainerScreen() instanceof InventoryScreen))
+        AbstractContainerScreen<?> containerScreen = context.getContainerScreen();
+        Container container = containerScreen.getContainer();
+        ContainerInfo containerInfo = ContainerInfoHandler.getContainerInfo(recipe.getRecipeCategory(), container.getClass());
+        if (containerInfo == null)
             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 (recipe.getHeight() > containerInfo.getCraftingHeight(container) || recipe.getWidth() > containerInfo.getCraftingWidth(container))
+            return Result.createFailed(I18n.translate("error.rei.transfer.too_small", containerInfo.getCraftingWidth(container), containerInfo.getCraftingHeight(container)));
         if (!canUseMovePackets())
             return Result.createFailed("error.rei.not.on.server");
-        if (!hasItems(context.getRecipe().getInput()))
-            return Result.createFailed("error.rei.not.enough.materials");
+        List<List<ItemStack>> input = recipe.getOrganisedInput(containerInfo, container);
+        IntList intList = hasItems(input);
+        if (!intList.isEmpty())
+            return Result.createFailed("error.rei.not.enough.materials", intList);
         if (!context.isActuallyCrafting())
             return Result.createSuccessful();
         
-        context.getMinecraft().openScreen(context.getContainerScreen());
-        ((RecipeBookGuiHooks) ((RecipeBookProvider) context.getContainerScreen()).getRecipeBookGui()).rei_getGhostSlots().reset();
-        DefaultCraftingDisplay display = (DefaultCraftingDisplay) context.getRecipe();
+        context.getMinecraft().openScreen(containerScreen);
+        if (containerScreen instanceof RecipeBookProvider)
+            ((RecipeBookGuiHooks) ((RecipeBookProvider) containerScreen).getRecipeBookGui()).rei_getGhostSlots().reset();
         PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
-        buf.writeUuid(RoughlyEnoughItemsNetwork.CRAFTING_TABLE_MOVE);
+        //        buf.writeUuid(RoughlyEnoughItemsNetwork.CRAFTING_TABLE_MOVE);
+        buf.writeIdentifier(recipe.getRecipeCategory());
         buf.writeBoolean(Screen.hasShiftDown());
-        CraftingContainer craftingContainer = (CraftingContainer) context.getContainer();
-        
-        List<List<ItemStack>> ogInput = display.getInput();
-        List<List<ItemStack>> input = Lists.newArrayListWithCapacity(craftingContainer.getCraftingWidth() * craftingContainer.getCraftingHeight());
-        for (int i = 0; i < craftingContainer.getCraftingWidth() * craftingContainer.getCraftingHeight(); i++) {
-            input.add(Lists.newArrayList());
-        }
-        for (int i = 0; i < ogInput.size(); i++) {
-            List<ItemStack> ogStacks = ogInput.get(i);
-            if (display instanceof DefaultShapedDisplay) {
-                if (!ogInput.get(i).isEmpty())
-                    input.set(DefaultCraftingCategory.getSlotWithSize(display, i), ogInput.get(i));
-            } else if (!ogInput.get(i).isEmpty())
-                input.set(i, ogInput.get(i));
-        }
         
         buf.writeInt(input.size());
         for (List<ItemStack> stacks : input) {
@@ -84,13 +80,15 @@ public class AutoCraftingHandler implements AutoTransferHandler {
         return ClientSidePacketRegistry.INSTANCE.canServerReceive(RoughlyEnoughItemsNetwork.MOVE_ITEMS_PACKET);
     }
     
-    public boolean hasItems(List<List<ItemStack>> inputs) {
+    public IntList hasItems(List<List<ItemStack>> inputs) {
         // Create a clone of player's inventory, and count
         DefaultedList<ItemStack> copyMain = DefaultedList.create();
         for (ItemStack stack : MinecraftClient.getInstance().player.inventory.main) {
             copyMain.add(stack.copy());
         }
-        for (List<ItemStack> possibleStacks : inputs) {
+        IntList intList = new IntArrayList();
+        for (int i = 0; i < inputs.size(); i++) {
+            List<ItemStack> possibleStacks = inputs.get(i);
             boolean done = possibleStacks.isEmpty();
             for (ItemStack possibleStack : possibleStacks) {
                 if (!done) {
@@ -105,12 +103,14 @@ public class AutoCraftingHandler implements AutoTransferHandler {
                     }
                     if (invRequiredCount <= 0) {
                         done = true;
+                        break;
                     }
                 }
             }
-            if (!done)
-                return false;
+            if (!done) {
+                intList.add(i);
+            }
         }
-        return true;
+        return intList;
     }
 }

+ 18 - 1
src/main/java/me/shedaniel/rei/plugin/blasting/DefaultBlastingDisplay.java

@@ -6,8 +6,11 @@
 package me.shedaniel.rei.plugin.blasting;
 
 import me.shedaniel.rei.api.RecipeDisplay;
+import me.shedaniel.rei.api.TransferRecipeDisplay;
 import me.shedaniel.rei.plugin.DefaultPlugin;
+import me.shedaniel.rei.server.ContainerInfo;
 import net.minecraft.block.entity.FurnaceBlockEntity;
+import net.minecraft.container.Container;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.AbstractCookingRecipe;
@@ -20,7 +23,7 @@ import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
-public class DefaultBlastingDisplay implements RecipeDisplay {
+public class DefaultBlastingDisplay implements TransferRecipeDisplay {
     
     private BlastingRecipe display;
     private List<List<ItemStack>> input;
@@ -66,4 +69,18 @@ public class DefaultBlastingDisplay implements RecipeDisplay {
         return Optional.ofNullable(display);
     }
     
+    @Override
+    public int getWidth() {
+        return 1;
+    }
+    
+    @Override
+    public int getHeight() {
+        return 1;
+    }
+    
+    @Override
+    public List<List<ItemStack>> getOrganisedInput(ContainerInfo<Container> containerInfo, Container container) {
+        return display.getPreviewInputs().stream().map(i -> Arrays.asList(i.getStackArray())).collect(Collectors.toList());
+    }
 }

+ 50 - 0
src/main/java/me/shedaniel/rei/plugin/containers/CraftingContainerInfoWrapper.java

@@ -0,0 +1,50 @@
+package me.shedaniel.rei.plugin.containers;
+
+import me.shedaniel.rei.server.ContainerInfo;
+import me.shedaniel.rei.server.RecipeFinder;
+import net.minecraft.container.Container;
+import net.minecraft.container.CraftingContainer;
+import net.minecraft.item.ItemStack;
+
+public class CraftingContainerInfoWrapper<T extends CraftingContainer<?>> implements ContainerInfo<T> {
+    private Class<? extends CraftingContainer<?>> containerClass;
+    
+    public <T extends CraftingContainer<?>> CraftingContainerInfoWrapper(Class<T> containerClass) {
+        this.containerClass = containerClass;
+    }
+    
+    @Override
+    public Class<? extends Container> getContainerClass() {
+        return containerClass;
+    }
+    
+    @Override
+    public int getCraftingResultSlotIndex(T container) {
+        return container.getCraftingResultSlotIndex();
+    }
+    
+    @Override
+    public int getCraftingWidth(T container) {
+        return container.getCraftingWidth();
+    }
+    
+    @Override
+    public int getCraftingHeight(T container) {
+        return container.getCraftingHeight();
+    }
+    
+    @Override
+    public void clearCraftingSlots(T container) {
+        container.clearCraftingSlots();
+    }
+    
+    @Override
+    public void populateRecipeFinder(T container, RecipeFinder var1) {
+        container.populateRecipeFinder(new net.minecraft.recipe.RecipeFinder() {
+            @Override
+            public void addNormalItem(ItemStack itemStack_1) {
+                var1.addNormalItem(itemStack_1);
+            }
+        });
+    }
+}

+ 25 - 2
src/main/java/me/shedaniel/rei/plugin/crafting/DefaultCraftingDisplay.java

@@ -5,28 +5,51 @@
 
 package me.shedaniel.rei.plugin.crafting;
 
-import me.shedaniel.rei.api.RecipeDisplay;
+import com.google.common.collect.Lists;
+import me.shedaniel.rei.api.TransferRecipeDisplay;
 import me.shedaniel.rei.plugin.DefaultPlugin;
+import me.shedaniel.rei.server.ContainerInfo;
+import net.minecraft.container.Container;
+import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.Recipe;
 import net.minecraft.util.Identifier;
 
+import java.util.List;
 import java.util.Optional;
 
-public interface DefaultCraftingDisplay extends RecipeDisplay {
+public interface DefaultCraftingDisplay extends TransferRecipeDisplay {
     
     @Override
     default Identifier getRecipeCategory() {
         return DefaultPlugin.CRAFTING;
     }
     
+    @Override
     default public int getWidth() {
         return 2;
     }
     
+    @Override
     default public int getHeight() {
         return 2;
     }
     
     Optional<Recipe<?>> getOptionalRecipe();
     
+    @Override
+    default List<List<ItemStack>> getOrganisedInput(ContainerInfo<Container> containerInfo, Container container) {
+        List<List<ItemStack>> list = Lists.newArrayListWithCapacity(containerInfo.getCraftingWidth(container) * containerInfo.getCraftingHeight(container));
+        for (int i = 0; i < containerInfo.getCraftingWidth(container) * containerInfo.getCraftingHeight(container); i++) {
+            list.add(Lists.newArrayList());
+        }
+        for (int i = 0; i < getInput().size(); i++) {
+            List<ItemStack> stacks = getInput().get(i);
+            if (this instanceof DefaultShapedDisplay) {
+                if (!stacks.isEmpty())
+                    list.set(DefaultCraftingCategory.getSlotWithSize(this, i), stacks);
+            } else if (!stacks.isEmpty())
+                list.set(i, stacks);
+        }
+        return list;
+    }
 }

+ 19 - 2
src/main/java/me/shedaniel/rei/plugin/smelting/DefaultSmeltingDisplay.java

@@ -5,9 +5,11 @@
 
 package me.shedaniel.rei.plugin.smelting;
 
-import me.shedaniel.rei.api.RecipeDisplay;
+import me.shedaniel.rei.api.TransferRecipeDisplay;
 import me.shedaniel.rei.plugin.DefaultPlugin;
+import me.shedaniel.rei.server.ContainerInfo;
 import net.minecraft.block.entity.FurnaceBlockEntity;
+import net.minecraft.container.Container;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.AbstractCookingRecipe;
@@ -20,7 +22,7 @@ import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
-public class DefaultSmeltingDisplay implements RecipeDisplay {
+public class DefaultSmeltingDisplay implements TransferRecipeDisplay {
     
     private SmeltingRecipe display;
     private List<List<ItemStack>> input;
@@ -65,4 +67,19 @@ public class DefaultSmeltingDisplay implements RecipeDisplay {
     public Optional<SmeltingRecipe> getOptionalRecipe() {
         return Optional.ofNullable(display);
     }
+    
+    @Override
+    public int getWidth() {
+        return 1;
+    }
+    
+    @Override
+    public int getHeight() {
+        return 1;
+    }
+    
+    @Override
+    public List<List<ItemStack>> getOrganisedInput(ContainerInfo<Container> containerInfo, Container container) {
+        return display.getPreviewInputs().stream().map(i -> Arrays.asList(i.getStackArray())).collect(Collectors.toList());
+    }
 }

+ 19 - 1
src/main/java/me/shedaniel/rei/plugin/smoking/DefaultSmokingDisplay.java

@@ -6,8 +6,11 @@
 package me.shedaniel.rei.plugin.smoking;
 
 import me.shedaniel.rei.api.RecipeDisplay;
+import me.shedaniel.rei.api.TransferRecipeDisplay;
 import me.shedaniel.rei.plugin.DefaultPlugin;
+import me.shedaniel.rei.server.ContainerInfo;
 import net.minecraft.block.entity.FurnaceBlockEntity;
+import net.minecraft.container.Container;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.AbstractCookingRecipe;
@@ -20,7 +23,7 @@ import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
-public class DefaultSmokingDisplay implements RecipeDisplay {
+public class DefaultSmokingDisplay implements TransferRecipeDisplay {
     
     private SmokingRecipe display;
     private List<List<ItemStack>> input;
@@ -66,4 +69,19 @@ public class DefaultSmokingDisplay implements RecipeDisplay {
         return Optional.ofNullable(display);
     }
     
+    @Override
+    public int getWidth() {
+        return 1;
+    }
+    
+    @Override
+    public int getHeight() {
+        return 1;
+    }
+    
+    @Override
+    public List<List<ItemStack>> getOrganisedInput(ContainerInfo<Container> containerInfo, Container container) {
+        return display.getPreviewInputs().stream().map(i -> Arrays.asList(i.getStackArray())).collect(Collectors.toList());
+    }
+    
 }

+ 17 - 0
src/main/java/me/shedaniel/rei/server/ContainerInfo.java

@@ -0,0 +1,17 @@
+package me.shedaniel.rei.server;
+
+import net.minecraft.container.Container;
+
+public interface ContainerInfo<T extends Container> {
+    Class<? extends Container> getContainerClass();
+    
+    int getCraftingResultSlotIndex(T container);
+    
+    int getCraftingWidth(T container);
+    
+    int getCraftingHeight(T container);
+    
+    void clearCraftingSlots(T container);
+    
+    void populateRecipeFinder(T container, RecipeFinder var1);
+}

+ 25 - 0
src/main/java/me/shedaniel/rei/server/ContainerInfoHandler.java

@@ -0,0 +1,25 @@
+package me.shedaniel.rei.server;
+
+import com.google.common.collect.Maps;
+import net.minecraft.container.Container;
+import net.minecraft.util.Identifier;
+
+import java.util.Map;
+
+public class ContainerInfoHandler {
+    private static final Map<Identifier, Map<Class<? extends Container>, ContainerInfo>> containerInfoMap = Maps.newHashMap();
+    
+    public static void registerContainerInfo(Identifier category, ContainerInfo containerInfo) {
+        if (!containerInfoMap.containsKey(category))
+            containerInfoMap.put(category, Maps.newHashMap());
+        containerInfoMap.get(category).put(containerInfo.getContainerClass(), containerInfo);
+    }
+    
+    public static boolean isCategoryHandled(Identifier category) {
+        return containerInfoMap.containsKey(category) && !containerInfoMap.get(category).isEmpty();
+    }
+    
+    public static ContainerInfo getContainerInfo(Identifier category, Class<?> containerClass) {
+        return containerInfoMap.get(category).get(containerClass);
+    }
+}

+ 21 - 24
src/main/java/me/shedaniel/rei/server/InputSlotCrafter.java

@@ -9,9 +9,7 @@ import com.google.common.collect.Lists;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import it.unimi.dsi.fastutil.ints.IntList;
 import it.unimi.dsi.fastutil.ints.IntListIterator;
-import net.minecraft.container.CraftingContainer;
-import net.minecraft.container.CraftingTableContainer;
-import net.minecraft.container.PlayerContainer;
+import net.minecraft.container.Container;
 import net.minecraft.container.Slot;
 import net.minecraft.entity.player.PlayerInventory;
 import net.minecraft.inventory.Inventory;
@@ -19,6 +17,7 @@ import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.Ingredient;
 import net.minecraft.server.network.ServerPlayerEntity;
 import net.minecraft.util.DefaultedList;
+import net.minecraft.util.Identifier;
 
 import java.util.Comparator;
 import java.util.Iterator;
@@ -27,15 +26,18 @@ import java.util.Map;
 
 public class InputSlotCrafter<C extends Inventory> implements RecipeGridAligner<Integer> {
     
-    protected CraftingContainer<C> craftingContainer;
+    protected Container craftingContainer;
+    protected ContainerInfo containerInfo;
     protected PlayerInventory inventory;
     
-    private InputSlotCrafter(CraftingContainer<C> craftingContainer_1) {
-        this.craftingContainer = craftingContainer_1;
+    private InputSlotCrafter(Container craftingContainer, ContainerInfo containerInfo) {
+        this.craftingContainer = craftingContainer;
+        this.containerInfo = containerInfo;
     }
     
-    public static <C extends Inventory> void start(CraftingContainer<C> craftingContainer_1, ServerPlayerEntity player, Map<Integer, List<ItemStack>> map, boolean hasShift) {
-        new InputSlotCrafter<C>(craftingContainer_1).fillInputSlots(player, map, hasShift);
+    public static <C extends Inventory> void start(Identifier category, Container craftingContainer_1, ServerPlayerEntity player, Map<Integer, List<ItemStack>> map, boolean hasShift) {
+        ContainerInfo containerInfo = ContainerInfoHandler.getContainerInfo(category, craftingContainer_1.getClass());
+        new InputSlotCrafter<C>(craftingContainer_1, containerInfo).fillInputSlots(player, map, hasShift);
     }
     
     private void fillInputSlots(ServerPlayerEntity player, Map<Integer, List<ItemStack>> map, boolean hasShift) {
@@ -49,12 +51,7 @@ public class InputSlotCrafter<C extends Inventory> implements RecipeGridAligner<
             for (ItemStack stack : player.inventory.main) {
                 recipeFinder.addNormalItem(stack);
             }
-            this.craftingContainer.populateRecipeFinder(new net.minecraft.recipe.RecipeFinder() {
-                @Override
-                public void addNormalItem(ItemStack itemStack_1) {
-                    recipeFinder.addNormalItem(itemStack_1);
-                }
-            });
+            this.containerInfo.populateRecipeFinder(craftingContainer, recipeFinder);
             DefaultedList<Ingredient> ingredients = DefaultedList.create();
             map.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getKey)).forEach(entry -> {
                 ingredients.add(Ingredient.ofStacks(entry.getValue().toArray(new ItemStack[0])));
@@ -112,8 +109,8 @@ public class InputSlotCrafter<C extends Inventory> implements RecipeGridAligner<
         int int_1 = recipeFinder.countRecipeCrafts(ingredients, (IntList) null);
         int int_2;
         if (boolean_2) {
-            for (int_2 = 0; int_2 < this.craftingContainer.getCraftingHeight() * this.craftingContainer.getCraftingWidth() + 1; ++int_2) {
-                if (int_2 != this.craftingContainer.getCraftingResultSlotIndex()) {
+            for (int_2 = 0; int_2 < this.containerInfo.getCraftingHeight(craftingContainer) * this.containerInfo.getCraftingWidth(craftingContainer) + 1; ++int_2) {
+                if (int_2 != this.containerInfo.getCraftingResultSlotIndex(craftingContainer)) {
                     ItemStack itemStack_1 = this.craftingContainer.getSlot(int_2).getStack();
                     if (!itemStack_1.isEmpty() && Math.min(int_1, itemStack_1.getMaxCount()) < itemStack_1.getCount() + 1) {
                         return;
@@ -138,7 +135,7 @@ public class InputSlotCrafter<C extends Inventory> implements RecipeGridAligner<
             
             if (recipeFinder.findRecipe(ingredients, intList_1, int_4)) {
                 this.returnInputs();
-                this.alignRecipeToGrid(this.craftingContainer.getCraftingWidth(), this.craftingContainer.getCraftingHeight(), this.craftingContainer.getCraftingResultSlotIndex(), ingredients, intList_1.iterator(), int_4);
+                this.alignRecipeToGrid(this.containerInfo.getCraftingWidth(craftingContainer), this.containerInfo.getCraftingHeight(craftingContainer), this.containerInfo.getCraftingResultSlotIndex(craftingContainer), ingredients, intList_1.iterator(), int_4);
             }
         }
         
@@ -150,8 +147,8 @@ public class InputSlotCrafter<C extends Inventory> implements RecipeGridAligner<
             int_2 = int_1;
         } else if (boolean_2) {
             int_2 = 64;
-            for (int int_3 = 0; int_3 < this.craftingContainer.getCraftingWidth() * this.craftingContainer.getCraftingHeight() + 1; ++int_3) {
-                if (int_3 != this.craftingContainer.getCraftingResultSlotIndex()) {
+            for (int int_3 = 0; int_3 < this.containerInfo.getCraftingWidth(craftingContainer) * this.containerInfo.getCraftingHeight(craftingContainer) + 1; ++int_3) {
+                if (int_3 != this.containerInfo.getCraftingResultSlotIndex(craftingContainer)) {
                     ItemStack itemStack_1 = this.craftingContainer.getSlot(int_3).getStack();
                     if (!itemStack_1.isEmpty() && int_2 > itemStack_1.getCount()) {
                         int_2 = itemStack_1.getCount();
@@ -166,13 +163,13 @@ public class InputSlotCrafter<C extends Inventory> implements RecipeGridAligner<
     }
     
     protected void returnInputs() {
-        for (int int_1 = 0; int_1 < this.craftingContainer.getCraftingWidth() * this.craftingContainer.getCraftingHeight() + 1; ++int_1) {
-            if (int_1 != this.craftingContainer.getCraftingResultSlotIndex() || !(this.craftingContainer instanceof CraftingTableContainer) && !(this.craftingContainer instanceof PlayerContainer)) {
+        for (int int_1 = 0; int_1 < this.containerInfo.getCraftingWidth(craftingContainer) * this.containerInfo.getCraftingHeight(craftingContainer) + 1; ++int_1) {
+            if (int_1 != this.containerInfo.getCraftingResultSlotIndex(craftingContainer)) {
                 this.returnSlot(int_1);
             }
         }
         
-        this.craftingContainer.clearCraftingSlots();
+        this.containerInfo.clearCraftingSlots(craftingContainer);
     }
     
     protected void returnSlot(int int_1) {
@@ -197,8 +194,8 @@ public class InputSlotCrafter<C extends Inventory> implements RecipeGridAligner<
         List<ItemStack> list_1 = Lists.newArrayList();
         int int_1 = this.getFreeInventorySlots();
         
-        for (int int_2 = 0; int_2 < this.craftingContainer.getCraftingWidth() * this.craftingContainer.getCraftingHeight() + 1; ++int_2) {
-            if (int_2 != this.craftingContainer.getCraftingResultSlotIndex()) {
+        for (int int_2 = 0; int_2 < this.containerInfo.getCraftingWidth(craftingContainer) * this.containerInfo.getCraftingHeight(craftingContainer) + 1; ++int_2) {
+            if (int_2 != this.containerInfo.getCraftingResultSlotIndex(craftingContainer)) {
                 ItemStack itemStack_1 = this.craftingContainer.getSlot(int_2).getStack().copy();
                 if (!itemStack_1.isEmpty()) {
                     int int_3 = this.inventory.getOccupiedSlotWithRoomForStack(itemStack_1);

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

@@ -38,7 +38,7 @@
   "ordering.rei.name": "Name",
   "ordering.rei.item_groups": "Item Groups",
   "text.auto_craft.move_items": "Move Items",
-  "error.rei.transfer.too_small": "Unable to move items to a 2x2 grid.",
+  "error.rei.transfer.too_small": "Unable to move items to a %dx%d grid.",
   "error.rei.not.on.server": "REI is not on the server.",
   "error.rei.not.enough.materials": "Not Enough Materials.",
   "error.rei.internal.error": "Internal Error: %s",

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

@@ -38,7 +38,7 @@
   "ordering.rei.name": "名稱",
   "ordering.rei.item_groups": "物品分類",
   "text.auto_craft.move_items": "移動物品",
-  "error.rei.transfer.too_small": "無法移動物品至 2x2 的方格.",
+  "error.rei.transfer.too_small": "無法移動物品至 %dx%d 的方格.",
   "error.rei.not.on.server": "REI 不在伺服器上.",
   "error.rei.not.enough.materials": "不夠材料.",
   "error.rei.internal.error": "內部錯誤: %s",

+ 3 - 0
src/main/resources/fabric.mod.json

@@ -29,6 +29,9 @@
     "rei_plugins": [
       "me.shedaniel.rei.plugin.DefaultPlugin",
       "me.shedaniel.rei.plugin.DefaultAutoCraftingPlugin"
+    ],
+    "rei_containers": [
+      "me.shedaniel.rei.plugin.DefaultServerContainerPlugin"
     ]
   },
   "depends": {