package net.minecraft.recipebook; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.List; import java.util.OptionalInt; 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.PlacementInfo; 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)) { OptionalInt optionalInt = list.stream().mapToInt(holder -> ((Item)holder.value()).getDefaultMaxStackSize()).min(); if (optionalInt.isPresent()) { j = Math.min(j, optionalInt.getAsInt()); } list.clear(); if (stackedItemContents.canCraft(recipe.value(), j, list::add)) { this.clearGrid(); int k = j; PlaceRecipeHelper.placeRecipe(this.gridWidth, this.gridHeight, recipe.value(), recipe.value().placementInfo().slotInfo(), (optional, jx, kx, l) -> { if (!optional.isEmpty()) { Slot slotx = (Slot)this.inputGridSlots.get(jx); int m = ((PlacementInfo.SlotInfo)optional.get()).placerOutputPosition(); int n = k; while (n > 0) { Holder holder = (Holder)list.get(m); n = this.moveItemToGrid(slotx, holder, n); if (n == -1) { return; } } } }); } } } 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) { int i = this.inventory.findSlotMatchingCraftingIngredient(item); if (i == -1) { return -1; } else { ItemStack itemStack = this.inventory.getItem(i); int j; if (count < itemStack.getCount()) { this.inventory.removeItem(i, count); j = count; } else { this.inventory.removeItemNoUpdate(i); j = itemStack.getCount(); } if (slot.getItem().isEmpty()) { slot.set(itemStack.copyWithCount(j)); } else { slot.getItem().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.items) { if (itemStack.isEmpty()) { i++; } } return i; } public interface CraftingMenuAccess> { void fillCraftSlotsStackedContents(StackedItemContents stackedItemContents); void clearCraftingContent(); boolean recipeMatches(RecipeHolder recipe); } }