From 7bfe069e511d8c19b2616383c0b815eb8d50566a Mon Sep 17 00:00:00 2001 From: Sakura Ryoko Date: Mon, 11 Nov 2024 23:12:48 -0500 Subject: [PATCH] Fix Mass Crafting via Recipe Book Protocol with verified recipe input matching. --- gradle.properties | 2 +- .../masa/itemscroller/event/InputHandler.java | 1 - .../itemscroller/event/KeybindCallbacks.java | 1 - .../itemscroller/recipes/RecipePattern.java | 317 ++++++++---------- .../itemscroller/recipes/RecipeStorage.java | 34 +- .../itemscroller/recipes/RecipeUtils.java | 134 ++++++++ .../itemscroller/util/InventoryUtils.java | 14 - 7 files changed, 289 insertions(+), 214 deletions(-) create mode 100644 src/main/java/fi/dy/masa/itemscroller/recipes/RecipeUtils.java diff --git a/gradle.properties b/gradle.properties index 1f26a2345..99065a1b4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ author = masa mod_file_name = itemscroller-fabric # Current mod version -mod_version = 0.25.0-sakura.4 +mod_version = 0.25.0-sakura.5 # Required malilib version malilib_version = 0.22.0-sakura.5 diff --git a/src/main/java/fi/dy/masa/itemscroller/event/InputHandler.java b/src/main/java/fi/dy/masa/itemscroller/event/InputHandler.java index 518fdaf01..136793d7f 100644 --- a/src/main/java/fi/dy/masa/itemscroller/event/InputHandler.java +++ b/src/main/java/fi/dy/masa/itemscroller/event/InputHandler.java @@ -202,7 +202,6 @@ else if (isPickBlock && InputUtils.isRecipeViewOpen() && InventoryUtils.isCrafti { recipes.storeCraftingRecipeToCurrentSelection(slot, gui, true, false, mc); InventoryUtils.clearFirstCraftingGridOfAllItems(gui); - //InventoryUtils.clearCraftingGridCursorStack(gui, mc); cancel = true; } } diff --git a/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java b/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java index b4b8c9e24..c8eef5ddc 100644 --- a/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java +++ b/src/main/java/fi/dy/masa/itemscroller/event/KeybindCallbacks.java @@ -151,7 +151,6 @@ else if (key == Hotkeys.STORE_RECIPE.getKeybind()) { recipes.storeCraftingRecipeToCurrentSelection(slot, gui, true, true, mc); InventoryUtils.clearFirstCraftingGridOfAllItems(gui); - //InventoryUtils.clearCraftingGridCursorStack(gui, mc); return true; } } diff --git a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java index 6a8114296..160046b33 100644 --- a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java +++ b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipePattern.java @@ -1,7 +1,9 @@ package fi.dy.masa.itemscroller.recipes; -import java.util.*; -import java.util.concurrent.atomic.AtomicReference; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.llamalad7.mixinextras.lib.apache.commons.tuple.Pair; @@ -15,6 +17,7 @@ import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtList; import net.minecraft.recipe.*; +import net.minecraft.recipe.book.RecipeBookCategory; import net.minecraft.recipe.display.SlotDisplayContexts; import net.minecraft.recipe.input.CraftingRecipeInput; import net.minecraft.recipe.input.RecipeInput; @@ -39,10 +42,8 @@ public class RecipePattern private ItemStack[] recipe = new ItemStack[9]; private RecipeEntry vanillaRecipe; private NetworkRecipeId networkRecipeId; - private NetworkRecipeId ghostNetworkRecipeId; private RecipeDisplayEntry displayEntry; - private long lastMassCraft; - private long altTimeoutMillis = 6000L; + private RecipeBookCategory category; public RecipePattern() { @@ -63,9 +64,8 @@ public void clearRecipe() this.result = InventoryUtils.EMPTY_STACK; this.vanillaRecipe = null; this.networkRecipeId = null; - this.ghostNetworkRecipeId = null; this.displayEntry = null; - this.lastMassCraft = -1; + this.category = null; } public void ensureRecipeSizeAndClearRecipe(int size) @@ -138,73 +138,59 @@ public void storeIdFromClientRecipeBook(MinecraftClient mc) return; } - this.storeNetworkRecipeId(pair.getLeft(), false); + this.storeNetworkRecipeId(pair.getLeft()); + this.storeRecipeCategory(pair.getRight().category()); this.storeRecipeDisplayEntry(pair.getRight()); } - public void storeNetworkRecipeId(NetworkRecipeId id, boolean saveGhost) + public void storeNetworkRecipeId(NetworkRecipeId id) { - if (this.networkRecipeId != null && id.index() != this.networkRecipeId.index() && saveGhost) - { - this.storeGhostNetworkRecipeId(this.networkRecipeId); - } - else - { - this.ghostNetworkRecipeId = null; - } - this.networkRecipeId = id; } - public void storeGhostNetworkRecipeId(NetworkRecipeId id) + public void storeRecipeDisplayEntry(RecipeDisplayEntry entry) { - this.ghostNetworkRecipeId = id; + this.displayEntry = entry; } - public void swapGhostNetworkRecipeId() + public void storeRecipeCategory(RecipeBookCategory category) { - // The 'Ghost' RecipeId is usually a higher index() number than the real recipe, or the "Inverse" recipe of the one being crafted. - // We just need to be able to handle the swap at the correct time. - if (this.getGhostNetworkRecipeId() != null) - { - NetworkRecipeId last = this.getNetworkRecipeId(); - this.storeNetworkRecipeId(this.getGhostNetworkRecipeId(), false); - this.storeGhostNetworkRecipeId(last); - } + this.category = category; } - public void storeRecipeDisplayEntry(RecipeDisplayEntry entry) + public @Nullable NetworkRecipeId getNetworkRecipeId() { - this.displayEntry = entry; + return this.networkRecipeId; } - public @Nullable NetworkRecipeId getNetworkRecipeId() + public @Nullable RecipeDisplayEntry getRecipeDisplayEntry() { - return this.networkRecipeId; + return this.displayEntry; } - public @Nullable NetworkRecipeId getGhostNetworkRecipeId() + public @Nullable RecipeBookCategory getRecipeCategory() { - return this.ghostNetworkRecipeId; + return this.category; } - public @Nullable RecipeDisplayEntry getRecipeDisplayEntry() + public boolean matchRecipeCategory(RecipeBookCategory category) { - return this.displayEntry; + return this.getRecipeCategory() != null && this.getRecipeCategory().equals(category); } public @Nullable Pair matchClientRecipeBook(MinecraftClient mc) { - Pair pair = null; + Pair pair; - if (mc.player == null || mc.world == null) + if (mc.player == null || mc.world == null || this.isEmpty()) { return null; } + ClientRecipeBook recipeBook = mc.player.getRecipeBook(); ContextParameterMap ctx = SlotDisplayContexts.createParameters(mc.world); Map recipeMap = ((IMixinClientRecipeBook) recipeBook).itemscroller_getRecipeMap(); - List recipeStacks = this.combineStacks(Arrays.stream(this.getRecipeItems()).toList(), 3); + //List recipeStacks = Arrays.stream(this.getRecipeItems()).toList(); if (recipeMap.size() < 1) { @@ -213,165 +199,52 @@ public void storeRecipeDisplayEntry(RecipeDisplayEntry entry) for (NetworkRecipeId id : recipeMap.keySet()) { - List stacks = recipeMap.get(id).getStacks(ctx); - - if (this.compareRecipeStacks(recipeStacks, this.combineStacks(stacks, 3))) - { - pair = Pair.of(id, recipeMap.get(id)); - return pair; - } - } - - return null; - } - - public static List combineStacks(List stacks, int iterations) - { - if (iterations > 3 || iterations < 1) - { - iterations = 3; - } - - List list = new ArrayList<>(stacks); - int i = 0; - - while (i < iterations) - { - list = combineStacksEach(list); - i++; - } - - return list; - } + RecipeDisplayEntry entry = recipeMap.get(id); - private static List combineStacksEach(List stacks) - { - List list = new ArrayList<>(); - ItemStack previous = ItemStack.EMPTY; - ItemStack entry = ItemStack.EMPTY; - - for (int i = 0; i < stacks.size(); i++) - { - entry = stacks.get(i); - - if (!previous.isEmpty() && ItemStack.areItemsAndComponentsEqual(previous, entry)) + if (entry != null) { - int prevCount = previous.getCount(); - int maxCount = previous.getMaxCount(); - int entryCount = entry.getCount(); - - if ((prevCount + entryCount) <= maxCount) - { - previous.setCount(prevCount + entryCount); - entry = ItemStack.EMPTY; - } - else + if (this.getRecipeCategory() != null && !this.matchRecipeCategory(entry.category())) { - previous.setCount(maxCount); - entry.setCount((prevCount + entryCount) - maxCount); - list.add(previous.copy()); - previous = entry.copy(); - entry = ItemStack.EMPTY; + continue; } - } - else - { - if (!previous.isEmpty()) + + List stacks = entry.getStacks(ctx); + + //if (RecipeUtils.compareRecipeStacks(recipeStacks, stacks)) + if (ItemStack.areItemsEqual(this.getResult(), stacks.getFirst())) { - list.add(previous.copy()); + pair = Pair.of(id, entry); + return pair; } - - previous = entry.copy(); - entry = ItemStack.EMPTY; } } - if (!previous.isEmpty()) - { - list.add(previous.copy()); - } - if (!entry.isEmpty()) - { - list.add(entry.copy()); - } - - return list; + return null; } - private boolean compareRecipeStacks(List left, List right) + public boolean matchClientRecipeBookEntry(RecipeDisplayEntry entry, MinecraftClient mc) { - if (left.size() != right.size()) + if (mc.world == null || this.isEmpty()) { return false; } - for (int i = 0; i < left.size(); i++) + if (this.getRecipeCategory() != null && !entry.category().equals(this.getRecipeCategory())) { - ItemStack l = left.get(i); - ItemStack r = right.get(i); - - //System.out.printf("compare() [%d] left [%s] / right [%s] --> ", i, l.toString(), r.toString()); - - if (ItemStack.areItemsEqual(l, r) == false) - { - //System.out.print(" not equal\n"); - return false; - } - else if (l.getCount() != r.getCount()) - { - //System.out.print(" count not equal\n"); - return false; - } + return false; } + List recipeStacks = Arrays.stream(this.getRecipeItems()).toList(); + List stacks = entry.getStacks(SlotDisplayContexts.createParameters(mc.world)); - //System.out.print(" PASS\n"); - return true; - } + //System.out.printf("matchClientRecipeBookEntry() --> [%s] vs [%s]\n", this.getResult().toString(), stacks.getFirst().toString()); - public boolean verifyClientRecipeBook(MinecraftClient mc, @Nullable NetworkRecipeId id) - { - if (id != null) + if (ItemStack.areItemsEqual(this.getResult(), stacks.getFirst())) { - if (id.equals(this.getNetworkRecipeId())) + if (entry.craftingRequirements().isPresent()) { - return true; + return RecipeUtils.compareStacksAndIngredients(recipeStacks, entry.craftingRequirements().get(), this.countRecipeItems()); } - return false; - } - - Pair pair = this.matchClientRecipeBook(mc); - - if (pair == null || pair.getLeft() == null) - { - return false; - } - - if (this.getNetworkRecipeId() != null && pair.getLeft().index() == this.getNetworkRecipeId().index()) - { - return true; - } - else if (this.getGhostNetworkRecipeId() != null && pair.getLeft().index() == this.getGhostNetworkRecipeId().index()) - { - return true; - } - - return false; - } - - public boolean matchClientRecipeBookEntry(RecipeDisplayEntry entry, MinecraftClient mc) - { - if (mc.world == null) - { - return false; - } - - // Compact the stacks so that they equal - List recipeStacks = this.combineStacks(Arrays.stream(this.getRecipeItems()).toList(), 3); - List stacks = this.combineStacks(entry.getStacks(SlotDisplayContexts.createParameters(mc.world)), 3); - - if (this.compareRecipeStacks(recipeStacks, stacks)) - { return true; } @@ -401,6 +274,7 @@ public void storeCraftingRecipe(Slot slot, HandledScreen { MinecraftClient mc = MinecraftClient.getInstance(); - if (gui instanceof RecipeBookScreen rbs) + if (mc.world == null || mc.player == null) + { + return; + } + if (gui instanceof RecipeBookScreen rbs) { RecipeBookWidget widget = ((IMixinRecipeBookScreen) rbs).itemscroller_getRecipeBookWidget(); @@ -429,15 +307,68 @@ public void storeSelectedRecipeIdFromGui(HandledScreen ClientRecipeBook recipeBook = mc.player.getRecipeBook(); Map recipeMap = ((IMixinClientRecipeBook) recipeBook).itemscroller_getRecipeMap(); - this.storeNetworkRecipeId(id, false); - if (recipeMap.containsKey(id)) { - this.storeRecipeDisplayEntry(recipeMap.get(id)); + RecipeDisplayEntry entry = recipeMap.get(id); + ItemStack result = entry.getStacks(SlotDisplayContexts.createParameters(mc.world)).getFirst(); + + if (ItemStack.areItemsEqual(this.getResult(), result)) + { + if (entry.craftingRequirements().isPresent()) + { + if (RecipeUtils.compareStacksAndIngredients(Arrays.asList(this.getRecipeItems()), entry.craftingRequirements().get(), this.countRecipeItems())) + { + ItemScroller.printDebug("storeSelectedRecipeIdFromGui(): Matched Ingredients for result stack [{}] networkId [{}]", this.getResult().toString(), id.index()); + this.storeNetworkRecipeId(id); + this.storeRecipeCategory(entry.category()); + this.storeRecipeDisplayEntry(entry); + } + else + { + ItemScroller.logger.warn("storeSelectedRecipeIdFromGui(): failed to match Ingredients for result stack [{}] networkId [{}]", this.getResult().toString(), id.index()); + } + } + else + { + ItemScroller.printDebug("storeSelectedRecipeIdFromGui(): No craftingRequirements present, Saving Blindly for result stack [{}] networkId [{}]", this.getResult().toString(), id.index()); + this.storeNetworkRecipeId(id); + this.storeRecipeCategory(entry.category()); + this.storeRecipeDisplayEntry(entry); + } + } + else + { + // Go for broke, and iterate it. + Pair pair = this.matchClientRecipeBook(mc); + + if (pair != null) + { + ItemScroller.printDebug("storeSelectedRecipeIdFromGui(): matching pair for result stack [{}] networkId [{}]", this.getResult().toString(), pair.getLeft().index()); + this.storeNetworkRecipeId(pair.getLeft()); + this.storeRecipeCategory(pair.getRight().category()); + this.storeRecipeDisplayEntry(pair.getRight()); + } + else + { + // Sometimes the result gets de-sync to like an Iron Nugget, just copy it and try one last time (It should work) + this.result = result.copy(); + pair = this.matchClientRecipeBook(mc); + + if (pair != null) + { + ItemScroller.printDebug("storeSelectedRecipeIdFromGui(): RE-matching pair results stack [{}] networkId [{}]", this.getResult().toString(), pair.getLeft().index()); + this.storeNetworkRecipeId(pair.getLeft()); + this.storeRecipeCategory(pair.getRight().category()); + this.storeRecipeDisplayEntry(pair.getRight()); + } + else + { + ItemScroller.logger.error("storeSelectedRecipeIdFromGui(): Final Exception matching results stack [{}] versus [{}] --> Clearing Recipe", this.getResult().toString(), result.toString()); + this.clearRecipe(); + } + } + } } - - // Clear crafting grid - InventoryUtils.clearFirstCraftingGridOfAllItems(gui); } } } @@ -459,6 +390,7 @@ public void copyRecipeFrom(RecipePattern other) this.vanillaRecipe = other.vanillaRecipe; this.networkRecipeId = other.networkRecipeId; this.displayEntry = other.displayEntry; + this.category = other.category; } public void readFromNBT(@Nonnull NbtCompound nbt, @Nonnull DynamicRegistryManager registryManager) @@ -543,19 +475,34 @@ public ItemStack[] getRecipeItems() return this.recipe; } - public boolean hasRecipeItems() + public boolean isEmpty() { boolean empty = true; for (int i = 0; i < this.getRecipeLength(); i++) { - if (this.getRecipeItems()[i].isEmpty() == false) + if (!this.getRecipeItems()[i].isEmpty()) { empty = false; } } - return empty; + return empty || this.getResult().isEmpty(); + } + + public int countRecipeItems() + { + int count = 0; + + for (ItemStack itemStack : this.recipe) + { + if (!itemStack.isEmpty()) + { + count++; + } + } + + return count; } public boolean isValid() diff --git a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeStorage.java b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeStorage.java index e9d73e7d0..82e1608f4 100644 --- a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeStorage.java +++ b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeStorage.java @@ -150,11 +150,15 @@ public void onAddToRecipeBook(RecipeDisplayEntry entry) for (RecipePattern recipe : this.recipes) { - if (recipe.matchClientRecipeBookEntry(entry, mc)) + if (!recipe.isEmpty()) { - recipe.storeNetworkRecipeId(entry.id(), true); - recipe.swapGhostNetworkRecipeId(); - recipe.storeRecipeDisplayEntry(entry); + if (recipe.matchClientRecipeBookEntry(entry, mc)) + { + ItemScroller.printDebug("onAddToRecipeBook(): Positive Match for result stack: [{}] networkId [{}]", recipe.getResult().toString(), entry.id().index()); + recipe.storeNetworkRecipeId(entry.id()); + recipe.storeRecipeCategory(entry.category()); + recipe.storeRecipeDisplayEntry(entry); + } } } } @@ -184,13 +188,13 @@ private void readFromNBT(NbtCompound nbt, @Nonnull DynamicRegistryManager regist { this.recipes[index].readFromNBT(tag, registryManager); - if (tag.contains("LastNetworkId")) + if (tag.contains("RecipeCategory", Constants.NBT.TAG_STRING)) { - this.recipes[index].storeNetworkRecipeId(new NetworkRecipeId(tag.getInt("LastNetworkId")), false); + this.recipes[index].storeRecipeCategory(RecipeUtils.getRecipeCategoryFromId(tag.getString("RecipeCategory"))); } - if (tag.contains("GhostNetworkId")) + if (tag.contains("LastNetworkId")) { - this.recipes[index].storeGhostNetworkRecipeId(new NetworkRecipeId(tag.getInt("GhostNetworkId"))); + this.recipes[index].storeNetworkRecipeId(new NetworkRecipeId(tag.getInt("LastNetworkId"))); } } } @@ -211,14 +215,20 @@ private NbtCompound writeToNBT(@Nonnull DynamicRegistryManager registryManager) NbtCompound tag = entry.writeToNBT(registryManager); tag.putByte("RecipeIndex", (byte) i); - if (entry.getNetworkRecipeId() != null) + if (entry.getRecipeCategory() != null) { - tag.putInt("LastNetworkId", entry.getNetworkRecipeId().index()); + String id = RecipeUtils.getRecipeCategoryId(entry.getRecipeCategory()); + + if (!id.isEmpty()) + { + tag.putString("RecipeCategory", id); + } } - if (entry.getGhostNetworkRecipeId() != null) + if (entry.getNetworkRecipeId() != null) { - tag.putInt("GhostNetworkId", entry.getGhostNetworkRecipeId().index()); + tag.putInt("LastNetworkId", entry.getNetworkRecipeId().index()); } + tagRecipes.add(tag); } } diff --git a/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeUtils.java b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeUtils.java new file mode 100644 index 000000000..0019cfb1b --- /dev/null +++ b/src/main/java/fi/dy/masa/itemscroller/recipes/RecipeUtils.java @@ -0,0 +1,134 @@ +package fi.dy.masa.itemscroller.recipes; + +import java.util.List; +import javax.annotation.Nullable; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.recipe.Ingredient; +import net.minecraft.recipe.book.RecipeBookCategory; +import net.minecraft.registry.Registries; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.util.Identifier; + +public class RecipeUtils +{ + public static String getRecipeCategoryId(RecipeBookCategory category) + { + RegistryKey key = Registries.RECIPE_BOOK_CATEGORY.getKey(category).orElse(null); + + if (key != null) + { + return key.getValue().toString(); + } + + return ""; + } + + public static @Nullable RecipeBookCategory getRecipeCategoryFromId(String id) + { + RegistryEntry.Reference catReference = Registries.RECIPE_BOOK_CATEGORY.getEntry(Identifier.tryParse(id)).orElse(null); + + if (catReference != null && catReference.hasKeyAndValue()) + { + return catReference.value(); + } + + return null; + } + + public static boolean compareStacksAndIngredients(List left, List right, int count) + { + if (left.isEmpty() || right.isEmpty()) + { + //System.out.print("compare() --> EMPTY!!!\n"); + return false; + } + /* + if (left.size() != right.size()) + { + //System.out.printf("compare() --> size left [%d] != right [%d]\n", left.size(), right.size()); + return false; + } + */ + + //System.out.printf("compare() --> size left [%d], right [%d]\n", left.size(), right.size()); + //dumpStacks(left, "LF"); + //dumpIngs(right, "RT"); + + for (int i = 0; i < left.size(); i++) + { + ItemStack l = left.get(i); + + if (i >= right.size()) + { + if (count > i) + { + //System.out.print(" count not valid (ran out of right entries!)\n"); + return false; + } + else if (l.isEmpty() && i <= count) + { + //System.out.print(" PASS (end of right side)\n"); + return true; + } + + //System.out.print(" not valid (ran out of right entries!)\n"); + return false; + } + Ingredient ri = right.get(i); + RegistryEntry r = ri.getMatchingItems().getFirst(); + + //System.out.printf("compare() [%d] left [%s] / right [%s]\n", i, l.toString(), r.getIdAsString()); + + if (!ri.test(l)) + { + //System.out.print(" not valid (Test test)\n"); + return false; + } + else if (!ItemStack.areItemsEqual(l, new ItemStack(r))) + { + //System.out.print(" not valid (Stack test)\n"); + return false; + } + + /* + else if (l.getCount() != r.getCount()) + { + //System.out.print(" count not equal\n"); + return false; + } + */ + } + + //System.out.print(" PASS\n"); + return true; + } + + private static void dumpStacks(List stacks, String side) + { + int i = 0; + + //System.out.printf("DUMP [%s] -->\n", side); + for (ItemStack stack : stacks) + { + //System.out.printf("%s[%d] // [%s]\n", side, i, stack.toString()); + i++; + } + //System.out.printf("DUMP END [%s]\n", side); + } + + private static void dumpIngs(List ings, String side) + { + int i = 0; + + //System.out.printf("DUMP [%s] -->\n", side); + for (Ingredient ing : ings) + { + //System.out.printf("%s[%d] // [%s]\n", side, i, ing.getMatchingItems().getFirst().getIdAsString()); + i++; + } + //System.out.printf("DUMP END [%s]\n", side); + } +} diff --git a/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java b/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java index e68ea1e55..08fea7bb8 100644 --- a/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java +++ b/src/main/java/fi/dy/masa/itemscroller/util/InventoryUtils.java @@ -1422,20 +1422,6 @@ private static boolean clearCraftingGridOfAllItems(HandledScreen gui, MinecraftClient mc) - { - ItemStack stack = gui.getScreenHandler().getCursorStack(); - PlayerEntity player = mc.player; - - if (stack.isEmpty() == false && player != null) - { - ((IMixinScreenHandler) gui).itemscroller_offerOrDropStack(player, stack); - gui.getScreenHandler().setCursorStack(ItemStack.EMPTY); - } - } - */ - private static boolean tryMoveItemsToCraftingGridSlots(RecipePattern recipe, Slot slot, HandledScreen gui,