Эх сурвалжийг харах

Improve ContainerInfo to allow non-vanilla slots or custom behaviour

Signed-off-by: shedaniel <daniel@shedaniel.me>
shedaniel 4 жил өмнө
parent
commit
aef0f4f396

+ 39 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/server/ContainerContext.java

@@ -0,0 +1,39 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020 shedaniel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.shedaniel.rei.server;
+
+import net.minecraft.container.Container;
+import net.minecraft.entity.player.PlayerEntity;
+
+public interface ContainerContext<T extends Container> {
+    T getContainer();
+    
+    PlayerEntity getPlayerEntity();
+    
+    ContainerInfo<T> getContainerInfo();
+    
+    default StackAccessor getStack(int slotIndex) {
+        return getContainerInfo().getStack(this, slotIndex);
+    }
+}

+ 80 - 2
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/server/ContainerInfo.java

@@ -24,17 +24,95 @@
 package me.shedaniel.rei.server;
 
 import net.minecraft.container.Container;
+import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.util.collection.DefaultedList;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 public interface ContainerInfo<T extends Container> {
     Class<? extends Container> getContainerClass();
     
+    default StackAccessor getStack(ContainerContext<T> context, int slotIndex) {
+        return new SlotStackAccessor(context.getContainer().getSlot(slotIndex));
+    }
+    
+    default GridCleanHandler<T> getGridCleanHandler() {
+        return context -> {
+            T container = context.getContainer();
+            for (StackAccessor gridStack : getGridStacks(context)) {
+                GridCleanHandler.returnSlotToPlayerInventory(context, gridStack);
+            }
+
+            clearCraftingSlots(container);
+        };
+    }
+    
+    default DumpHandler<T> getDumpHandler() {
+        return (context, stackToInsert) -> {
+            List<StackAccessor> inventoryStacks = context.getContainerInfo().getInventoryStacks(context);
+            
+            StackAccessor nextSlot = DumpHandler.getOccupiedSlotWithRoomForStack(stackToInsert, inventoryStacks);
+            if (nextSlot == null) {
+                nextSlot = DumpHandler.getEmptySlot(inventoryStacks);
+            }
+            if (nextSlot == null) {
+                return false;
+            }
+            
+            ItemStack stack = stackToInsert.copy();
+            stack.setCount(nextSlot.getItemStack().getCount() + stack.getCount());
+            nextSlot.setItemStack(stack);
+            return true;
+        };
+    }
+    
+    default RecipeFinderPopulator<T> getRecipeFinderPopulator() {
+        return context -> recipeFinder -> {
+            for (StackAccessor inventoryStack : getInventoryStacks(context)) {
+                recipeFinder.addNormalItem(inventoryStack.getItemStack());
+            }
+            populateRecipeFinder(context.getContainer(), recipeFinder);
+        };
+    }
+    
+    default List<StackAccessor> getGridStacks(ContainerContext<T> context) {
+        return IntStream.range(0, getCraftingWidth(context.getContainer()) * getCraftingHeight(context.getContainer()) + 1)
+                .filter(value -> value != getCraftingResultSlotIndex(context.getContainer()))
+                .mapToObj(context::getStack)
+                .collect(Collectors.toList());
+    }
+    
+    default List<StackAccessor> getInventoryStacks(ContainerContext<T> context) {
+        PlayerInventory inventory = context.getPlayerEntity().inventory;
+        return IntStream.range(0, inventory.main.size())
+                .mapToObj(index -> (StackAccessor) new InventoryStackAccessor(inventory, index))
+                .collect(Collectors.toList());
+    }
+    
+    default void markDirty(ContainerContext<T> context) {
+        context.getPlayerEntity().inventory.markDirty();
+        context.getContainer().sendContentUpdates();
+        
+        DefaultedList<ItemStack> defaultedList = DefaultedList.of();
+        for (Slot slot : context.getPlayerEntity().container.slots) {
+            defaultedList.add(slot.getStack());
+        }
+        
+        ((ServerPlayerEntity) context.getPlayerEntity()).onContainerRegistered(context.getPlayerEntity().container, defaultedList);
+    }
+    
     int getCraftingResultSlotIndex(T container);
     
     int getCraftingWidth(T container);
     
     int getCraftingHeight(T container);
     
-    void clearCraftingSlots(T container);
+    default void clearCraftingSlots(T container) {}
     
-    void populateRecipeFinder(T container, RecipeFinder var1);
+    default void populateRecipeFinder(T container, RecipeFinder var1) {}
 }

+ 58 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/server/DumpHandler.java

@@ -0,0 +1,58 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020 shedaniel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.shedaniel.rei.server;
+
+import net.minecraft.container.Container;
+import net.minecraft.item.ItemStack;
+
+import java.util.List;
+
+@FunctionalInterface
+public interface DumpHandler<T extends Container> {
+    boolean dump(ContainerContext<T> context, ItemStack stackToInsert);
+    
+    static StackAccessor getOccupiedSlotWithRoomForStack(ItemStack stack, List<StackAccessor> inventoryStacks) {
+        for (StackAccessor inventoryStack : inventoryStacks) {
+            if (canStackAddMore(inventoryStack.getItemStack(), stack)) {
+                return inventoryStack;
+            }
+        }
+        
+        return null;
+    }
+    
+    static StackAccessor getEmptySlot(List<StackAccessor> inventoryStacks) {
+        for (StackAccessor inventoryStack : inventoryStacks) {
+            if (inventoryStack.getItemStack().isEmpty()) {
+                return inventoryStack;
+            }
+        }
+        
+        return null;
+    }
+    
+    static boolean canStackAddMore(ItemStack existingStack, ItemStack stack) {
+        return !existingStack.isEmpty() && ItemStack.areItemsEqual(existingStack, stack) && existingStack.isStackable() && existingStack.getCount() + stack.getCount() <= existingStack.getMaxCount();
+    }
+}

+ 50 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/server/GridCleanHandler.java

@@ -0,0 +1,50 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020 shedaniel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.shedaniel.rei.server;
+
+import net.minecraft.container.Container;
+import net.minecraft.item.ItemStack;
+
+@FunctionalInterface
+public interface GridCleanHandler<T extends Container> {
+    void clean(ContainerContext<T> context);
+    
+    static void error(String translationKey) {
+        throw new IllegalStateException(translationKey);
+    }
+    
+    static <T extends Container> void returnSlotToPlayerInventory(ContainerContext<T> context, StackAccessor stackAccessor) {
+        DumpHandler<T> dumpHandler = context.getContainerInfo().getDumpHandler();
+        ItemStack stackToReturn = stackAccessor.getItemStack();
+        if (!stackToReturn.isEmpty()) {
+            for (; stackToReturn.getCount() > 0; stackAccessor.takeStack(1)) {
+                ItemStack stackToInsert = stackToReturn.copy();
+                stackToInsert.setCount(1);
+                if (!dumpHandler.dump(context, stackToInsert)) {
+                    error("rei.rei.no.slot.in.inv");
+                }
+            }
+        }
+    }
+}

+ 31 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/server/GridStacksProvider.java

@@ -0,0 +1,31 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020 shedaniel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.shedaniel.rei.server;
+
+import net.minecraft.container.Container;
+
+@FunctionalInterface
+public interface GridStacksProvider<T extends Container> {
+    Iterable<StackAccessor> getStacks(ContainerContext<T> context);
+}

+ 52 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/server/InventoryStackAccessor.java

@@ -0,0 +1,52 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020 shedaniel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.shedaniel.rei.server;
+
+import net.minecraft.inventory.Inventory;
+import net.minecraft.item.ItemStack;
+
+public class InventoryStackAccessor implements StackAccessor {
+    protected Inventory inventory;
+    protected int index;
+    
+    public InventoryStackAccessor(Inventory inventory, int index) {
+        this.inventory = inventory;
+        this.index = index;
+    }
+    
+    @Override
+    public ItemStack getItemStack() {
+        return inventory.getInvStack(index);
+    }
+    
+    @Override
+    public void setItemStack(ItemStack stack) {
+        this.inventory.setInvStack(index, stack);
+    }
+    
+    @Override
+    public ItemStack takeStack(int amount) {
+        return this.inventory.takeInvStack(index, amount);
+    }
+}

+ 11 - 15
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/server/RecipeFinder.java

@@ -172,10 +172,10 @@ public class RecipeFinder {
                 
                 this.bitSet.clear(0, this.ingredientCount + this.usableIngredientSize + this.ingredientCount);
                 int int_5 = 0;
-                List<Ingredient> list_1 = ingredientsInput.stream().collect(Collectors.toList());
-                
-                for (int int_6 = 0; int_6 < list_1.size(); ++int_6) {
-                    if (boolean_2 && list_1.get(int_6).isEmpty()) {
+                List<Ingredient> list_1 = new ArrayList<>(ingredientsInput);
+    
+                for (Ingredient ingredient : list_1) {
+                    if (boolean_2 && ingredient.isEmpty()) {
                         intList_1.add(0);
                     } else {
                         for (int int_7 = 0; int_7 < this.usableIngredientSize; ++int_7) {
@@ -187,7 +187,7 @@ public class RecipeFinder {
                                 }
                             }
                         }
-                        
+            
                         ++int_5;
                     }
                 }
@@ -198,10 +198,8 @@ public class RecipeFinder {
         
         private int[] getUsableIngredientItemIds() {
             IntCollection intCollection_1 = new IntAVLTreeSet();
-            Iterator var2 = this.ingredients.iterator();
-            
-            while (var2.hasNext()) {
-                Ingredient ingredient_1 = (Ingredient) var2.next();
+    
+            for (Ingredient ingredient_1 : this.ingredients) {
                 intCollection_1.addAll(ingredient_1.getIds());
             }
             
@@ -325,17 +323,15 @@ public class RecipeFinder {
         @SuppressWarnings("deprecation")
         private int method_7415() {
             int int_1 = Integer.MAX_VALUE;
-            Iterator var2 = this.ingredients.iterator();
-            
-            while (var2.hasNext()) {
-                Ingredient ingredient_1 = (Ingredient) var2.next();
+    
+            for (Ingredient ingredient_1 : this.ingredients) {
                 int int_2 = 0;
-                
+        
                 int int_3;
                 for (IntListIterator var5 = ingredient_1.getIds().iterator(); var5.hasNext(); int_2 = Math.max(int_2, RecipeFinder.this.idToAmountMap.get(int_3))) {
                     int_3 = var5.next();
                 }
-                
+        
                 if (int_1 > 0) {
                     int_1 = Math.min(int_1, int_2);
                 }

+ 35 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/server/RecipeFinderPopulator.java

@@ -0,0 +1,35 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020 shedaniel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.shedaniel.rei.server;
+
+import net.minecraft.container.Container;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.function.Consumer;
+
+@FunctionalInterface
+public interface RecipeFinderPopulator<T extends Container> {
+    @NotNull
+    Consumer<RecipeFinder> populate(ContainerContext<T> context);
+}

+ 7 - 41
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/server/RecipeGridAligner.java

@@ -23,56 +23,22 @@
 
 package me.shedaniel.rei.server;
 
-import net.minecraft.recipe.Ingredient;
-import net.minecraft.util.collection.DefaultedList;
-import net.minecraft.util.math.MathHelper;
 import org.jetbrains.annotations.ApiStatus;
 
 import java.util.Iterator;
+import java.util.List;
 
 @ApiStatus.Internal
 public interface RecipeGridAligner<T> {
-    default void alignRecipeToGrid(int int_1, int int_2, int int_3, DefaultedList<Ingredient> recipe_1, Iterator<T> iterator_1, int int_4) {
-        int int_7 = 0;
-        
-        for (int int_8 = 0; int_8 < int_2; ++int_8) {
-            if (int_7 == int_3) {
-                ++int_7;
+    default void alignRecipeToGrid(List<StackAccessor> gridStacks, Iterator<T> iterator_1, int craftsAmount) {
+        for (StackAccessor gridStack : gridStacks) {
+            if (!iterator_1.hasNext()) {
+                return;
             }
             
-            boolean boolean_1 = (float) int_2 < (float) int_2 / 2.0F;
-            int int_9 = MathHelper.floor((float) int_2 / 2.0F - (float) int_2 / 2.0F);
-            if (boolean_1 && int_9 > int_8) {
-                int_7 += int_1;
-                ++int_8;
-            }
-            
-            for (int int_10 = 0; int_10 < int_1; ++int_10) {
-                if (!iterator_1.hasNext()) {
-                    return;
-                }
-                
-                boolean_1 = (float) int_1 < (float) int_1 / 2.0F;
-                int_9 = MathHelper.floor((float) int_1 / 2.0F - (float) int_1 / 2.0F);
-                int int_11 = int_1;
-                boolean boolean_2 = int_10 < int_1;
-                if (boolean_1) {
-                    int_11 = int_9 + int_1;
-                    boolean_2 = int_9 <= int_10 && int_10 < int_9 + int_1;
-                }
-                
-                if (boolean_2) {
-                    this.acceptAlignedInput(iterator_1, int_7, int_4, int_8, int_10);
-                } else if (int_11 == int_10) {
-                    int_7 += int_1 - int_10;
-                    break;
-                }
-                
-                ++int_7;
-            }
+            this.acceptAlignedInput(iterator_1, gridStack, craftsAmount);
         }
-        
     }
     
-    void acceptAlignedInput(Iterator<T> var1, int var2, int var3, int var4, int var5);
+    void acceptAlignedInput(Iterator<T> var1, StackAccessor gridSlot, int craftsAmount);
 }

+ 50 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/server/SlotStackAccessor.java

@@ -0,0 +1,50 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020 shedaniel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.shedaniel.rei.server;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.slot.Slot;
+
+public class SlotStackAccessor implements StackAccessor {
+    protected Slot slot;
+    
+    public SlotStackAccessor(Slot slot) {
+        this.slot = slot;
+    }
+    
+    @Override
+    public ItemStack getItemStack() {
+        return slot.getStack();
+    }
+    
+    @Override
+    public void setItemStack(ItemStack stack) {
+        this.slot.setStack(stack);
+    }
+    
+    @Override
+    public ItemStack takeStack(int amount) {
+        return slot.takeStack(amount);
+    }
+}

+ 34 - 0
RoughlyEnoughItems-api/src/main/java/me/shedaniel/rei/server/StackAccessor.java

@@ -0,0 +1,34 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Items.
+ * Copyright (c) 2018, 2019, 2020 shedaniel
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package me.shedaniel.rei.server;
+
+import net.minecraft.item.ItemStack;
+
+public interface StackAccessor {
+    ItemStack getItemStack();
+    
+    void setItemStack(ItemStack stack);
+    
+    ItemStack takeStack(int amount);
+}

+ 18 - 4
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/plugin/autocrafting/DefaultCategoryHandler.java

@@ -31,6 +31,7 @@ import me.shedaniel.rei.api.AutoTransferHandler;
 import me.shedaniel.rei.api.ClientHelper;
 import me.shedaniel.rei.api.EntryStack;
 import me.shedaniel.rei.api.TransferRecipeDisplay;
+import me.shedaniel.rei.server.ContainerContext;
 import me.shedaniel.rei.server.ContainerInfo;
 import me.shedaniel.rei.server.ContainerInfoHandler;
 import me.shedaniel.rei.server.RecipeFinder;
@@ -43,6 +44,7 @@ import net.minecraft.client.gui.screen.ingame.ContainerScreen;
 import net.minecraft.client.gui.screen.recipebook.RecipeBookProvider;
 import net.minecraft.client.resource.language.I18n;
 import net.minecraft.container.Container;
+import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.item.ItemStack;
 import net.minecraft.network.PacketByteBuf;
 import net.minecraft.util.registry.Registry;
@@ -104,10 +106,22 @@ public class DefaultCategoryHandler implements AutoTransferHandler {
     public IntList hasItems(Container container, ContainerInfo<Container> containerInfo, List<List<EntryStack>> inputs) {
         // Create a clone of player's inventory, and count
         RecipeFinder recipeFinder = new RecipeFinder();
-        containerInfo.populateRecipeFinder(container, recipeFinder);
-        for (ItemStack stack : MinecraftClient.getInstance().player.inventory.main) {
-            recipeFinder.addNormalItem(stack.copy());
-        }
+        containerInfo.getRecipeFinderPopulator().populate(new ContainerContext<Container>() {
+            @Override
+            public Container getContainer() {
+                return container;
+            }
+            
+            @Override
+            public PlayerEntity getPlayerEntity() {
+                return MinecraftClient.getInstance().player;
+            }
+            
+            @Override
+            public ContainerInfo<Container> getContainerInfo() {
+                return containerInfo;
+            }
+        }).accept(recipeFinder);
         IntList intList = new IntArrayList();
         for (int i = 0; i < inputs.size(); i++) {
             List<EntryStack> possibleStacks = inputs.get(i);

+ 79 - 128
RoughlyEnoughItems-runtime/src/main/java/me/shedaniel/rei/server/InputSlotCrafter.java

@@ -23,27 +23,27 @@
 
 package me.shedaniel.rei.server;
 
-import com.google.common.collect.Lists;
 import it.unimi.dsi.fastutil.ints.IntArrayList;
 import it.unimi.dsi.fastutil.ints.IntList;
 import net.minecraft.container.Container;
-import net.minecraft.entity.player.PlayerInventory;
+import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.inventory.Inventory;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.Ingredient;
-import net.minecraft.screen.slot.Slot;
 import net.minecraft.server.network.ServerPlayerEntity;
 import net.minecraft.util.Identifier;
 import net.minecraft.util.collection.DefaultedList;
 
 import java.util.*;
 
-public class InputSlotCrafter<C extends Inventory> implements RecipeGridAligner<Integer> {
+public class InputSlotCrafter<C extends Inventory> implements RecipeGridAligner<Integer>, ContainerContext {
     
     protected Container container;
     protected ContainerInfo containerInfo;
-    protected PlayerInventory inventory;
+    private List<StackAccessor> gridStacks;
+    private List<StackAccessor> inventoryStacks;
+    private ServerPlayerEntity player;
     
     private InputSlotCrafter(Container container, ContainerInfo<? extends Container> containerInfo) {
         this.container = container;
@@ -56,197 +56,148 @@ public class InputSlotCrafter<C extends Inventory> implements RecipeGridAligner<
     }
     
     private void fillInputSlots(ServerPlayerEntity player, Map<Integer, List<ItemStack>> map, boolean hasShift) {
-        this.inventory = player.inventory;
-        if (this.canReturnInputs() || player.isCreative()) {
+        this.player = player;
+        this.inventoryStacks = this.containerInfo.getInventoryStacks(this);
+        this.gridStacks = this.containerInfo.getGridStacks(this);
+        if (player.isCreative()) {
+            player.skipPacketSlotUpdates = true;
             // Return the already placed items on the grid
             this.returnInputs();
             
             RecipeFinder recipeFinder = new RecipeFinder();
-            recipeFinder.clear();
-            for (ItemStack stack : player.inventory.main) {
-                recipeFinder.addNormalItem(stack);
-            }
-            this.containerInfo.populateRecipeFinder(container, recipeFinder);
+            this.containerInfo.getRecipeFinderPopulator().populate(this).accept(recipeFinder);
             DefaultedList<Ingredient> ingredients = DefaultedList.of();
             map.entrySet().stream().sorted(Comparator.comparingInt(Map.Entry::getKey)).forEach(entry -> {
                 ingredients.add(Ingredient.ofItems(entry.getValue().stream().map(ItemStack::getItem).toArray(Item[]::new)));
             });
+            
             if (recipeFinder.findRecipe(ingredients, null)) {
                 this.fillInputSlots(recipeFinder, ingredients, hasShift);
             } else {
                 this.returnInputs();
-                player.inventory.markDirty();
+                player.skipPacketSlotUpdates = false;
+                this.containerInfo.markDirty(this);
                 throw new NotEnoughMaterialsException();
             }
             
-            player.inventory.markDirty();
+            player.skipPacketSlotUpdates = false;
+            this.containerInfo.markDirty(this);
         }
     }
     
     @Override
-    public void acceptAlignedInput(Iterator<Integer> iterator_1, int int_1, int int_2, int int_3, int int_4) {
-        Slot slot_1 = this.container.getSlot(int_1);
-        ItemStack itemStack_1 = RecipeFinder.getStackFromId(iterator_1.next());
-        if (!itemStack_1.isEmpty()) {
-            for (int int_5 = 0; int_5 < int_2; ++int_5) {
-                this.fillInputSlot(slot_1, itemStack_1);
+    public void acceptAlignedInput(Iterator<Integer> iterator_1, StackAccessor gridSlot, int craftsAmount) {
+        ItemStack toBeTakenStack = RecipeFinder.getStackFromId(iterator_1.next());
+        if (!toBeTakenStack.isEmpty()) {
+            for (int i = 0; i < craftsAmount; ++i) {
+                this.fillInputSlot(gridSlot, toBeTakenStack);
             }
         }
     }
     
-    protected void fillInputSlot(Slot slot_1, ItemStack itemStack_1) {
-        int int_1 = this.inventory.method_7371(itemStack_1);
-        if (int_1 != -1) {
-            ItemStack itemStack_2 = this.inventory.getInvStack(int_1).copy();
-            if (!itemStack_2.isEmpty()) {
-                if (itemStack_2.getCount() > 1) {
-                    this.inventory.takeInvStack(int_1, 1);
+    protected void fillInputSlot(StackAccessor slot, ItemStack toBeTakenStack) {
+        int takenSlotIndex = this.method_7371(toBeTakenStack);
+        if (takenSlotIndex != -1) {
+            ItemStack takenStack = this.inventoryStacks.get(takenSlotIndex).getItemStack().copy();
+            if (!takenStack.isEmpty()) {
+                if (takenStack.getCount() > 1) {
+                    this.inventoryStacks.get(takenSlotIndex).takeStack(1);
                 } else {
-                    this.inventory.removeInvStack(int_1);
+                    this.inventoryStacks.get(takenSlotIndex).setItemStack(ItemStack.EMPTY);
                 }
                 
-                itemStack_2.setCount(1);
-                if (slot_1.getStack().isEmpty()) {
-                    slot_1.setStack(itemStack_2);
+                takenStack.setCount(1);
+                if (slot.getItemStack().isEmpty()) {
+                    slot.setItemStack(takenStack);
                 } else {
-                    slot_1.getStack().increment(1);
+                    slot.getItemStack().increment(1);
                 }
-                
             }
         }
     }
     
     protected void fillInputSlots(RecipeFinder recipeFinder, DefaultedList<Ingredient> ingredients, boolean hasShift) {
-        //        boolean boolean_2 = this.craftingContainer.matches(recipe_1);
-        boolean boolean_2 = false;
-        int int_1 = recipeFinder.countRecipeCrafts(ingredients, null);
-        int int_2;
-        if (boolean_2) {
-            for (int_2 = 0; int_2 < this.containerInfo.getCraftingHeight(container) * this.containerInfo.getCraftingWidth(container) + 1; ++int_2) {
-                if (int_2 != this.containerInfo.getCraftingResultSlotIndex(container)) {
-                    ItemStack itemStack_1 = this.container.getSlot(int_2).getStack();
-                    if (!itemStack_1.isEmpty() && Math.min(int_1, itemStack_1.getMaxCount()) < itemStack_1.getCount() + 1) {
-                        return;
-                    }
-                }
-            }
-        }
-        
-        int_2 = this.getAmountToFill(hasShift, int_1, boolean_2);
+        int recipeCrafts = recipeFinder.countRecipeCrafts(ingredients, null);
+        int amountToFill = this.getAmountToFill(hasShift, recipeCrafts, false);
         IntList intList_1 = new IntArrayList();
-        if (recipeFinder.findRecipe(ingredients, intList_1, int_2)) {
-            int int_4 = int_2;
+        if (recipeFinder.findRecipe(ingredients, intList_1, amountToFill)) {
+            int finalCraftsAmount = amountToFill;
             
-            for (int int_5 : intList_1) {
-                int int_6 = RecipeFinder.getStackFromId(int_5).getMaxCount();
-                if (int_6 < int_4) {
-                    int_4 = int_6;
-                }
+            for (int itemId : intList_1) {
+                finalCraftsAmount = Math.min(finalCraftsAmount, RecipeFinder.getStackFromId(itemId).getMaxCount());
             }
             
-            if (recipeFinder.findRecipe(ingredients, intList_1, int_4)) {
+            if (recipeFinder.findRecipe(ingredients, intList_1, finalCraftsAmount)) {
                 this.returnInputs();
-                this.alignRecipeToGrid(this.containerInfo.getCraftingWidth(container), this.containerInfo.getCraftingHeight(container), this.containerInfo.getCraftingResultSlotIndex(container), ingredients, intList_1.iterator(), int_4);
+                this.alignRecipeToGrid(gridStacks, intList_1.iterator(), finalCraftsAmount);
             }
         }
         
     }
     
-    protected int getAmountToFill(boolean hasShift, int int_1, boolean boolean_2) {
-        int int_2 = 1;
+    protected int getAmountToFill(boolean hasShift, int recipeCrafts, boolean boolean_2) {
+        int amountToFill = 1;
         if (hasShift) {
-            int_2 = int_1;
+            amountToFill = recipeCrafts;
         } else if (boolean_2) {
-            int_2 = 64;
-            for (int int_3 = 0; int_3 < this.containerInfo.getCraftingWidth(container) * this.containerInfo.getCraftingHeight(container) + 1; ++int_3) {
-                if (int_3 != this.containerInfo.getCraftingResultSlotIndex(container)) {
-                    ItemStack itemStack_1 = this.container.getSlot(int_3).getStack();
-                    if (!itemStack_1.isEmpty() && int_2 > itemStack_1.getCount()) {
-                        int_2 = itemStack_1.getCount();
-                    }
+            amountToFill = 64;
+            for (StackAccessor stackAccessor : gridStacks) {
+                ItemStack itemStack = stackAccessor.getItemStack();
+                if (!itemStack.isEmpty() && amountToFill > itemStack.getCount()) {
+                    amountToFill = itemStack.getCount();
                 }
             }
-            if (int_2 < 64) {
-                ++int_2;
+            if (amountToFill < 64) {
+                ++amountToFill;
             }
         }
-        return int_2;
+        return amountToFill;
     }
     
     protected void returnInputs() {
-        for (int int_1 = 0; int_1 < this.containerInfo.getCraftingWidth(container) * this.containerInfo.getCraftingHeight(container) + 1; ++int_1) {
-            if (int_1 != this.containerInfo.getCraftingResultSlotIndex(container)) {
-                this.returnSlot(int_1);
-            }
-        }
-        
-        this.containerInfo.clearCraftingSlots(container);
+        this.containerInfo.getGridCleanHandler().clean(this);
     }
     
-    protected void returnSlot(int int_1) {
-        ItemStack itemStack_1 = this.container.getSlot(int_1).getStack();
-        if (!itemStack_1.isEmpty()) {
-            for (; itemStack_1.getCount() > 0; this.container.getSlot(int_1).takeStack(1)) {
-                int int_2 = this.inventory.getOccupiedSlotWithRoomForStack(itemStack_1);
-                if (int_2 == -1) {
-                    int_2 = this.inventory.getEmptySlot();
-                }
-                
-                ItemStack itemStack_2 = itemStack_1.copy();
-                itemStack_2.setCount(1);
-                if (!this.inventory.insertStack(int_2, itemStack_2)) {
-                    throw new IllegalStateException("rei.rei.no.slot.in.inv");
-                }
+    public int method_7371(ItemStack itemStack) {
+        for (int i = 0; i < inventoryStacks.size(); i++) {
+            ItemStack itemStack1 = this.inventoryStacks.get(i).getItemStack();
+            if (!itemStack1.isEmpty() && areItemsEqual(itemStack, itemStack1) && !itemStack1.isDamaged() && !itemStack1.hasEnchantments() && !itemStack1.hasCustomName()) {
+                return i;
             }
         }
+        
+        return -1;
     }
     
-    private boolean canReturnInputs() {
-        List<ItemStack> list_1 = Lists.newArrayList();
-        int int_1 = this.getFreeInventorySlots();
-        
-        for (int int_2 = 0; int_2 < this.containerInfo.getCraftingWidth(container) * this.containerInfo.getCraftingHeight(container) + 1; ++int_2) {
-            if (int_2 != this.containerInfo.getCraftingResultSlotIndex(container)) {
-                ItemStack itemStack_1 = this.container.getSlot(int_2).getStack().copy();
-                if (!itemStack_1.isEmpty()) {
-                    int int_3 = this.inventory.getOccupiedSlotWithRoomForStack(itemStack_1);
-                    if (int_3 == -1 && list_1.size() <= int_1) {
-                        
-                        for (ItemStack itemStack_2 : list_1) {
-                            if (itemStack_2.isItemEqualIgnoreDamage(itemStack_1) && itemStack_2.getCount() != itemStack_2.getMaxCount() && itemStack_2.getCount() + itemStack_1.getCount() <= itemStack_2.getMaxCount()) {
-                                itemStack_2.increment(itemStack_1.getCount());
-                                itemStack_1.setCount(0);
-                                break;
-                            }
-                        }
-                        
-                        if (!itemStack_1.isEmpty()) {
-                            if (list_1.size() >= int_1) {
-                                return false;
-                            }
-                            
-                            list_1.add(itemStack_1);
-                        }
-                    } else if (int_3 == -1) {
-                        return false;
-                    }
-                }
-            }
-        }
-        
-        return true;
+    private static boolean areItemsEqual(ItemStack stack1, ItemStack stack2) {
+        return stack1.getItem() == stack2.getItem() && ItemStack.areTagsEqual(stack1, stack2);
     }
     
     private int getFreeInventorySlots() {
         int int_1 = 0;
-        for (ItemStack itemStack_1 : this.inventory.main) {
-            if (itemStack_1.isEmpty()) {
+        for (StackAccessor inventoryStack : inventoryStacks) {
+            if (inventoryStack.getItemStack().isEmpty()) {
                 ++int_1;
             }
         }
         return int_1;
     }
     
+    @Override
+    public Container getContainer() {
+        return container;
+    }
+    
+    @Override
+    public PlayerEntity getPlayerEntity() {
+        return player;
+    }
+    
+    @Override
+    public ContainerInfo getContainerInfo() {
+        return containerInfo;
+    }
+    
     public static class NotEnoughMaterialsException extends RuntimeException {}
     
 }

+ 1 - 1
gradle.properties

@@ -1,5 +1,5 @@
 org.gradle.jvmargs=-Xmx3G
-mod_version=5.1.1
+mod_version=5.2.0
 supported_version=1.16.2
 minecraft_version=1.16.2-rc1
 yarn_version=1.16.2-rc1+build.4+legacy.20w09a+build.8