package net.minecraft.recipebook; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.List; import net.minecraft.core.Holder; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.StackedItemContents; import net.minecraft.world.inventory.RecipeBookMenu; import net.minecraft.world.inventory.Slot; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.item.crafting.RecipeInput; public class ServerPlaceRecipe> { private static final int ITEM_NOT_FOUND = -1; private final Inventory inventory; private final ServerPlaceRecipe.CraftingMenuAccess menu; private final boolean useMaxItems; private final int gridWidth; private final int gridHeight; private final List inputGridSlots; private final List slotsToClear; public static > RecipeBookMenu.PostPlaceAction placeRecipe( ServerPlaceRecipe.CraftingMenuAccess menu, int gridWidth, int gridHeight, List inputGridSlots, List slotsToClear, Inventory inventory, RecipeHolder recipe, boolean useMaxItems, boolean isCreative ) { ServerPlaceRecipe serverPlaceRecipe = new ServerPlaceRecipe<>(menu, inventory, useMaxItems, gridWidth, gridHeight, inputGridSlots, slotsToClear); if (!isCreative && !serverPlaceRecipe.testClearGrid()) { return RecipeBookMenu.PostPlaceAction.NOTHING; } else { StackedItemContents stackedItemContents = new StackedItemContents(); inventory.fillStackedContents(stackedItemContents); menu.fillCraftSlotsStackedContents(stackedItemContents); return serverPlaceRecipe.tryPlaceRecipe(recipe, stackedItemContents); } } private ServerPlaceRecipe( ServerPlaceRecipe.CraftingMenuAccess menu, Inventory inventory, boolean useMaxItems, int gridWidth, int gridHeight, List inputGridSlots, List slotsToClear ) { this.menu = menu; this.inventory = inventory; this.useMaxItems = useMaxItems; this.gridWidth = gridWidth; this.gridHeight = gridHeight; this.inputGridSlots = inputGridSlots; this.slotsToClear = slotsToClear; } private RecipeBookMenu.PostPlaceAction tryPlaceRecipe(RecipeHolder recipe, StackedItemContents stackedItemContents) { if (stackedItemContents.canCraft(recipe.value(), null)) { this.placeRecipe(recipe, stackedItemContents); this.inventory.setChanged(); return RecipeBookMenu.PostPlaceAction.NOTHING; } else { this.clearGrid(); this.inventory.setChanged(); return RecipeBookMenu.PostPlaceAction.PLACE_GHOST_RECIPE; } } private void clearGrid() { for (Slot slot : this.slotsToClear) { ItemStack itemStack = slot.getItem().copy(); this.inventory.placeItemBackInInventory(itemStack, false); slot.set(itemStack); } this.menu.clearCraftingContent(); } private void placeRecipe(RecipeHolder recipe, StackedItemContents stackedItemContents) { boolean bl = this.menu.recipeMatches(recipe); int i = stackedItemContents.getBiggestCraftableStack(recipe.value(), null); if (bl) { for (Slot slot : this.inputGridSlots) { ItemStack itemStack = slot.getItem(); if (!itemStack.isEmpty() && Math.min(i, itemStack.getMaxStackSize()) < itemStack.getCount() + 1) { return; } } } int j = this.calculateAmountToCraft(i, bl); List> list = new ArrayList(); if (stackedItemContents.canCraft(recipe.value(), j, list::add)) { int k = clampToMaxStackSize(j, list); if (k != j) { list.clear(); if (!stackedItemContents.canCraft(recipe.value(), k, list::add)) { return; } } this.clearGrid(); PlaceRecipeHelper.placeRecipe( this.gridWidth, this.gridHeight, recipe.value(), recipe.value().placementInfo().slotsToIngredientIndex(), (integer, jx, kx, l) -> { if (integer != -1) { Slot slotx = (Slot)this.inputGridSlots.get(jx); Holder holder = (Holder)list.get(integer); int m = k; while (m > 0) { m = this.moveItemToGrid(slotx, holder, m); if (m == -1) { return; } } } } ); } } private static int clampToMaxStackSize(int amount, List> items) { for (Holder holder : items) { amount = Math.min(amount, holder.value().getDefaultMaxStackSize()); } return amount; } private int calculateAmountToCraft(int max, boolean recipeMatches) { if (this.useMaxItems) { return max; } else if (recipeMatches) { int i = Integer.MAX_VALUE; for (Slot slot : this.inputGridSlots) { ItemStack itemStack = slot.getItem(); if (!itemStack.isEmpty() && i > itemStack.getCount()) { i = itemStack.getCount(); } } if (i != Integer.MAX_VALUE) { i++; } return i; } else { return 1; } } private int moveItemToGrid(Slot slot, Holder item, int count) { ItemStack itemStack = slot.getItem(); int i = this.inventory.findSlotMatchingCraftingIngredient(item, itemStack); if (i == -1) { return -1; } else { ItemStack itemStack2 = this.inventory.getItem(i); ItemStack itemStack3; if (count < itemStack2.getCount()) { itemStack3 = this.inventory.removeItem(i, count); } else { itemStack3 = this.inventory.removeItemNoUpdate(i); } int j = itemStack3.getCount(); if (itemStack.isEmpty()) { slot.set(itemStack3); } else { itemStack.grow(j); } return count - j; } } /** * Places the output of the recipe into the player's inventory. */ private boolean testClearGrid() { List list = Lists.newArrayList(); int i = this.getAmountOfFreeSlotsInInventory(); for (Slot slot : this.inputGridSlots) { ItemStack itemStack = slot.getItem().copy(); if (!itemStack.isEmpty()) { int j = this.inventory.getSlotWithRemainingSpace(itemStack); if (j == -1 && list.size() <= i) { for (ItemStack itemStack2 : list) { if (ItemStack.isSameItem(itemStack2, itemStack) && itemStack2.getCount() != itemStack2.getMaxStackSize() && itemStack2.getCount() + itemStack.getCount() <= itemStack2.getMaxStackSize()) { itemStack2.grow(itemStack.getCount()); itemStack.setCount(0); break; } } if (!itemStack.isEmpty()) { if (list.size() >= i) { return false; } list.add(itemStack); } } else if (j == -1) { return false; } } } return true; } private int getAmountOfFreeSlotsInInventory() { int i = 0; for (ItemStack itemStack : this.inventory.getNonEquipmentItems()) { if (itemStack.isEmpty()) { i++; } } return i; } public interface CraftingMenuAccess> { void fillCraftSlotsStackedContents(StackedItemContents stackedItemContents); void clearCraftingContent(); boolean recipeMatches(RecipeHolder recipe); } }