From 33f761a92c41bdfd44f27d1cab8648f17187b1c9 Mon Sep 17 00:00:00 2001 From: CraftBukkit/Spigot Date: Mon, 6 Nov 2023 20:37:34 +1100 Subject: [PATCH] #992: Add API to get full result of crafting items By: md_5 --- .../org/bukkit/craftbukkit/CraftServer.java | 83 ++++++++++++++++--- .../inventory/CraftItemCraftResult.java | 44 ++++++++++ 2 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemCraftResult.java diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java index b77e1e0781..827bfd3576 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -24,7 +24,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetAddress; -import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -45,7 +44,6 @@ import java.util.logging.Logger; import java.util.stream.Collectors; import javax.imageio.ImageIO; import jline.console.ConsoleReader; -import net.minecraft.advancements.Advancement; import net.minecraft.advancements.AdvancementHolder; import net.minecraft.commands.CommandDispatcher; import net.minecraft.commands.CommandListenerWrapper; @@ -53,6 +51,7 @@ import net.minecraft.commands.arguments.ArgumentEntity; import net.minecraft.core.BlockPosition; import net.minecraft.core.HolderLookup; import net.minecraft.core.IRegistry; +import net.minecraft.core.NonNullList; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.DynamicOpsNBT; @@ -94,7 +93,6 @@ import net.minecraft.world.inventory.InventoryCrafting; import net.minecraft.world.inventory.TransientCraftingContainer; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemWorldMap; -import net.minecraft.world.item.crafting.IRecipe; import net.minecraft.world.item.crafting.RecipeCrafting; import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.item.crafting.RecipeRepair; @@ -170,6 +168,7 @@ import org.bukkit.craftbukkit.help.SimpleHelpMap; import org.bukkit.craftbukkit.inventory.CraftBlastingRecipe; import org.bukkit.craftbukkit.inventory.CraftCampfireRecipe; import org.bukkit.craftbukkit.inventory.CraftFurnaceRecipe; +import org.bukkit.craftbukkit.inventory.CraftItemCraftResult; import org.bukkit.craftbukkit.inventory.CraftItemFactory; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.craftbukkit.inventory.CraftMerchantCustom; @@ -228,6 +227,7 @@ import org.bukkit.inventory.FurnaceRecipe; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.ItemCraftResult; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.Merchant; import org.bukkit.inventory.Recipe; @@ -1325,8 +1325,7 @@ public final class CraftServer implements Server { return getServer().getRecipeManager().byKey(CraftNamespacedKey.toMinecraft(recipeKey)).map(RecipeHolder::toBukkitRecipe).orElse(null); } - @Override - public Recipe getCraftingRecipe(ItemStack[] craftingMatrix, World world) { + private InventoryCrafting createInventoryCrafting() { // Create a players Crafting Inventory Container container = new Container(null, -1) { @Override @@ -1345,12 +1344,21 @@ public final class CraftServer implements Server { } }; InventoryCrafting inventoryCrafting = new TransientCraftingContainer(container, 3, 3); + return inventoryCrafting; + } - return getNMSRecipe(craftingMatrix, inventoryCrafting, (CraftWorld) world).map(RecipeHolder::toBukkitRecipe).orElse(null); + @Override + public Recipe getCraftingRecipe(ItemStack[] craftingMatrix, World world) { + return getNMSRecipe(craftingMatrix, createInventoryCrafting(), (CraftWorld) world).map(RecipeHolder::toBukkitRecipe).orElse(null); } @Override public ItemStack craftItem(ItemStack[] craftingMatrix, World world, Player player) { + return craftItemResult(craftingMatrix, world, player).getResult(); + } + + @Override + public ItemCraftResult craftItemResult(ItemStack[] craftingMatrix, World world, Player player) { Preconditions.checkArgument(world != null, "world cannot be null"); Preconditions.checkArgument(player != null, "player cannot be null"); @@ -1377,13 +1385,66 @@ public final class CraftServer implements Server { // Call Bukkit event to check for matrix/result changes. net.minecraft.world.item.ItemStack result = CraftEventFactory.callPreCraftEvent(inventoryCrafting, craftResult, itemstack, container.getBukkitView(), recipe.map(RecipeHolder::toBukkitRecipe).orElse(null) instanceof RecipeRepair); - // Set the resulting matrix items - for (int i = 0; i < craftingMatrix.length; i++) { - Item remaining = inventoryCrafting.getContents().get(i).getItem().getCraftingRemainingItem(); - craftingMatrix[i] = (remaining != null) ? CraftItemStack.asBukkitCopy(remaining.getDefaultInstance()) : null; + return createItemCraftResult(CraftItemStack.asBukkitCopy(result), inventoryCrafting, craftWorld.getHandle()); + } + + @Override + public ItemStack craftItem(ItemStack[] craftingMatrix, World world) { + return craftItemResult(craftingMatrix, world).getResult(); + } + + @Override + public ItemCraftResult craftItemResult(ItemStack[] craftingMatrix, World world) { + Preconditions.checkArgument(world != null, "world must not be null"); + + CraftWorld craftWorld = (CraftWorld) world; + + // Create a players Crafting Inventory and get the recipe + InventoryCrafting inventoryCrafting = createInventoryCrafting(); + + Optional> recipe = getNMSRecipe(craftingMatrix, inventoryCrafting, craftWorld); + + // Generate the resulting ItemStack from the Crafting Matrix + net.minecraft.world.item.ItemStack itemStack = net.minecraft.world.item.ItemStack.EMPTY; + + if (recipe.isPresent()) { + itemStack = recipe.get().value().assemble(inventoryCrafting, craftWorld.getHandle().registryAccess()); } - return CraftItemStack.asBukkitCopy(result); + return createItemCraftResult(CraftItemStack.asBukkitCopy(itemStack), inventoryCrafting, craftWorld.getHandle()); + } + + private CraftItemCraftResult createItemCraftResult(ItemStack itemStack, InventoryCrafting inventoryCrafting, WorldServer worldServer) { + CraftItemCraftResult craftItemResult = new CraftItemCraftResult(itemStack); + NonNullList remainingItems = getServer().getRecipeManager().getRemainingItemsFor(Recipes.CRAFTING, inventoryCrafting, worldServer); + + // Set the resulting matrix items and overflow items + for (int i = 0; i < remainingItems.size(); ++i) { + net.minecraft.world.item.ItemStack itemstack1 = inventoryCrafting.getItem(i); + net.minecraft.world.item.ItemStack itemstack2 = (net.minecraft.world.item.ItemStack) remainingItems.get(i); + + if (!itemstack1.isEmpty()) { + inventoryCrafting.removeItem(i, 1); + itemstack1 = inventoryCrafting.getItem(i); + } + + if (!itemstack2.isEmpty()) { + if (itemstack1.isEmpty()) { + inventoryCrafting.setItem(i, itemstack2); + } else if (net.minecraft.world.item.ItemStack.isSameItemSameTags(itemstack1, itemstack2)) { + itemstack2.grow(itemstack1.getCount()); + inventoryCrafting.setItem(i, itemstack2); + } else { + craftItemResult.getOverflowItems().add(CraftItemStack.asBukkitCopy(itemstack2)); + } + } + } + + for (int i = 0; i < inventoryCrafting.getContents().size(); i++) { + craftItemResult.setResultMatrix(i, CraftItemStack.asBukkitCopy(inventoryCrafting.getItem(i))); + } + + return craftItemResult; } private Optional> getNMSRecipe(ItemStack[] craftingMatrix, InventoryCrafting inventoryCrafting, CraftWorld world) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemCraftResult.java b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemCraftResult.java new file mode 100644 index 0000000000..44a919a45a --- /dev/null +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemCraftResult.java @@ -0,0 +1,44 @@ +package org.bukkit.craftbukkit.inventory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.bukkit.Material; +import org.bukkit.inventory.ItemCraftResult; +import org.bukkit.inventory.ItemStack; + +public final class CraftItemCraftResult implements ItemCraftResult { + + private final ItemStack result; + private final ItemStack[] resultMatrix; + private final List overflowItems; + + public CraftItemCraftResult(ItemStack result) { + this.result = Objects.requireNonNullElseGet(result, () -> new ItemStack(Material.AIR)); + this.resultMatrix = new ItemStack[9]; + this.overflowItems = new ArrayList<>(); + + for (int i = 0; i < resultMatrix.length; i++) { + resultMatrix[i] = new ItemStack(Material.AIR); + } + } + + @Override + public ItemStack getResult() { + return result; + } + + @Override + public ItemStack[] getResultingMatrix() { + return resultMatrix; + } + + @Override + public List getOverflowItems() { + return overflowItems; + } + + public void setResultMatrix(int i, ItemStack itemStack) { + resultMatrix[i] = Objects.requireNonNullElseGet(itemStack, () -> new ItemStack(Material.AIR)); + } +}