diff --git a/patches/server/0278-Improve-exact-choice-recipe-ingredients.patch b/patches/server/0278-Improve-exact-choice-recipe-ingredients.patch index 3f924dd8b8..9c0b7b8275 100644 --- a/patches/server/0278-Improve-exact-choice-recipe-ingredients.patch +++ b/patches/server/0278-Improve-exact-choice-recipe-ingredients.patch @@ -9,6 +9,7 @@ and shapeless recipes. == AT == public net.minecraft.world.item.ItemStackLinkedSet TYPE_AND_TAG public net.minecraft.world.entity.player.StackedContents put(II)V +public net.minecraft.world.entity.player.StackedContents take(II)I diff --git a/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java b/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java new file mode 100644 @@ -48,12 +49,15 @@ index 0000000000000000000000000000000000000000..ef68600f6b59674ddea6c77f7e412902 +} diff --git a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java new file mode 100644 -index 0000000000000000000000000000000000000000..2258d4556a1c608e2b0ece38471350646718eb19 +index 0000000000000000000000000000000000000000..568ba6aed2e74b8d84f4e82c1e785ef1587e2617 --- /dev/null +++ b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java -@@ -0,0 +1,79 @@ +@@ -0,0 +1,109 @@ +package io.papermc.paper.inventory.recipe; + ++import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; ++import it.unimi.dsi.fastutil.ints.Int2IntMap; ++import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntArrayList; @@ -68,13 +72,14 @@ index 0000000000000000000000000000000000000000..2258d4556a1c608e2b0ece3847135064 +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.ItemStackLinkedSet; ++import net.minecraft.world.item.crafting.CraftingInput; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.Recipe; + +public final class StackedContentsExtraMap { + + private final AtomicInteger idCounter = new AtomicInteger(BuiltInRegistries.ITEM.size()); // start at max vanilla stacked contents idx -+ private final Object2IntMap exactChoiceIds = new Object2IntOpenCustomHashMap<>(ItemStackLinkedSet.TYPE_AND_TAG); ++ public final Object2IntMap exactChoiceIds = new Object2IntOpenCustomHashMap<>(ItemStackLinkedSet.TYPE_AND_TAG); + private final Int2ObjectMap idToExactChoice = new Int2ObjectOpenHashMap<>(); + private final StackedContents contents; + public final Map extraStackingIds = new IdentityHashMap<>(); @@ -120,6 +125,32 @@ index 0000000000000000000000000000000000000000..2258d4556a1c608e2b0ece3847135064 + return this.idToExactChoice.get(id); + } + ++ public Int2IntMap regularRemoved = new Int2IntArrayMap(); ++ public void accountInput(final CraftingInput input) { ++ // similar logic to the CraftingInput constructor ++ for (final ItemStack item : input.items()) { ++ if (!item.isEmpty()) { ++ if (this.accountStack(item, 1)) { ++ // remove one of the items if it was added to the contents as a non-extra item ++ final int plainStackIdx = StackedContents.getStackingIndex(item); ++ if (this.contents.take(plainStackIdx, 1) == plainStackIdx) { ++ this.regularRemoved.put(plainStackIdx, 1); ++ } ++ } ++ } ++ } ++ } ++ ++ public void resetExtras() { ++ // clear previous extra ids ++ for (final int extraId : this.exactChoiceIds.values()) { ++ this.contents.contents.remove(extraId); ++ } ++ for (final Int2IntMap.Entry entry : this.regularRemoved.int2IntEntrySet()) { ++ this.contents.put(entry.getIntKey(), entry.getIntValue()); ++ } ++ } ++ + public boolean accountStack(final ItemStack stack, final int count) { + if (!this.exactChoiceIds.isEmpty()) { + final int id = this.exactChoiceIds.getInt(stack); @@ -143,14 +174,14 @@ index 0000000000000000000000000000000000000000..413dfa52760db393ad6a8b5341200ee7 +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; diff --git a/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java b/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java -index 0bd749af8014dd437229594ef6981a2ead803990..25acc13ba1adcc31a83f9cf29563760285f2ba7a 100644 +index 0bd749af8014dd437229594ef6981a2ead803990..6d1f9c15dc99917a2ac966ea38ef1970f4f0289c 100644 --- a/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java +++ b/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java @@ -31,6 +31,7 @@ public class ServerPlaceRecipe> imple this.inventory = entity.getInventory(); if (this.testClearGrid() || entity.isCreative()) { this.stackedContents.clear(); -+ this.stackedContents.initialize(recipe.value()); // Paper - Improve exact choice recipe ingredients ++ this.stackedContents.initializeExtras(recipe.value(), null); // Paper - Improve exact choice recipe ingredients entity.getInventory().fillStackedContents(this.stackedContents); this.menu.fillCraftSlotsStackedContents(this.stackedContents); if (this.stackedContents.canCraft(recipe.value(), null)) { @@ -206,7 +237,7 @@ index 0bd749af8014dd437229594ef6981a2ead803990..25acc13ba1adcc31a83f9cf295637602 return -1; } else { diff --git a/src/main/java/net/minecraft/world/entity/player/StackedContents.java b/src/main/java/net/minecraft/world/entity/player/StackedContents.java -index b11121e0846183ceeb7f4ad536aab2ee89ea9d26..0a58698dcd62adf3dc06a8c7dc782aada50409f5 100644 +index fa5576e41baec4b52c7ebb877924eb91d3775a2d..fcabf630ce1e4949d00f485a5bff66dd1e54a277 100644 --- a/src/main/java/net/minecraft/world/entity/player/StackedContents.java +++ b/src/main/java/net/minecraft/world/entity/player/StackedContents.java @@ -22,8 +22,10 @@ import net.minecraft.world.item.crafting.RecipeHolder; @@ -228,13 +259,21 @@ index b11121e0846183ceeb7f4ad536aab2ee89ea9d26..0a58698dcd62adf3dc06a8c7dc782aad this.put(i, j); } } -@@ -83,6 +86,23 @@ public class StackedContents { +@@ -83,6 +86,31 @@ public class StackedContents { return itemId == 0 ? ItemStack.EMPTY : new ItemStack(Item.byId(itemId)); } + // Paper start - Improve exact choice recipe ingredients -+ public void initialize(final Recipe recipe) { ++ public void initializeExtras(final Recipe recipe, @Nullable final net.minecraft.world.item.crafting.CraftingInput input) { + this.extrasMap = new io.papermc.paper.inventory.recipe.StackedContentsExtraMap(this, recipe); ++ if (input != null) this.extrasMap.accountInput(input); ++ } ++ ++ public void resetExtras() { ++ if (this.extrasMap != null && !this.contents.isEmpty()) { ++ this.extrasMap.resetExtras(); ++ } ++ this.extrasMap = null; + } + + public static ItemStack fromStackingIndexWithExtras(final int itemId, @Nullable final StackedContents contents) { @@ -252,7 +291,7 @@ index b11121e0846183ceeb7f4ad536aab2ee89ea9d26..0a58698dcd62adf3dc06a8c7dc782aad public void clear() { this.contents.clear(); } -@@ -106,7 +126,7 @@ public class StackedContents { +@@ -106,7 +134,7 @@ public class StackedContents { this.data = new BitSet(this.ingredientCount + this.itemCount + this.ingredientCount + this.ingredientCount * this.itemCount); for (int i = 0; i < this.ingredients.size(); i++) { @@ -261,7 +300,7 @@ index b11121e0846183ceeb7f4ad536aab2ee89ea9d26..0a58698dcd62adf3dc06a8c7dc782aad for (int j = 0; j < this.itemCount; j++) { if (intList.contains(this.items[j])) { -@@ -169,7 +189,7 @@ public class StackedContents { +@@ -169,7 +197,7 @@ public class StackedContents { IntCollection intCollection = new IntAVLTreeSet(); for (Ingredient ingredient : this.ingredients) { @@ -270,7 +309,7 @@ index b11121e0846183ceeb7f4ad536aab2ee89ea9d26..0a58698dcd62adf3dc06a8c7dc782aad } IntIterator intIterator = intCollection.iterator(); -@@ -298,7 +318,7 @@ public class StackedContents { +@@ -298,7 +326,7 @@ public class StackedContents { for (Ingredient ingredient : this.ingredients) { int j = 0; @@ -279,7 +318,7 @@ index b11121e0846183ceeb7f4ad536aab2ee89ea9d26..0a58698dcd62adf3dc06a8c7dc782aad j = Math.max(j, StackedContents.this.contents.get(k)); } -@@ -309,5 +329,17 @@ public class StackedContents { +@@ -309,5 +337,17 @@ public class StackedContents { return i; } @@ -355,7 +394,7 @@ index 59372daacd6fef45373c0557ccebb6ff5f16f174..63cf2b66f51df68aa3f6d98c69368ce4 public ShapedRecipe(String group, CraftingBookCategory category, ShapedRecipePattern raw, ItemStack result) { diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java -index 62401d045245ec7e303ec526c09b5e6fa4c9f17b..5740296b55827f11c0029e89a86eaab1a24f560c 100644 +index 62401d045245ec7e303ec526c09b5e6fa4c9f17b..213ee4aa988dd4c2a5a7be99b1d13f67338e5209 100644 --- a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java +++ b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java @@ -19,7 +19,7 @@ import org.bukkit.craftbukkit.inventory.CraftRecipe; @@ -375,7 +414,7 @@ index 62401d045245ec7e303ec526c09b5e6fa4c9f17b..5740296b55827f11c0029e89a86eaab1 } // CraftBukkit start -@@ -75,7 +76,16 @@ public class ShapelessRecipe implements CraftingRecipe { +@@ -75,7 +76,18 @@ public class ShapelessRecipe implements CraftingRecipe { } public boolean matches(CraftingInput input, Level world) { @@ -387,8 +426,10 @@ index 62401d045245ec7e303ec526c09b5e6fa4c9f17b..5740296b55827f11c0029e89a86eaab1 + if (input.size() == 1 && this.ingredients.size() == 1) { + return this.ingredients.getFirst().test(input.getItem(0)); + } -+ input.stackedContents().initialize(this); // setup stacked contents for this recipe -+ return input.stackedContents().canCraft(this, null); ++ input.stackedContents().initializeExtras(this, input); // setup stacked contents for this recipe ++ final boolean canCraft = input.stackedContents().canCraft(this, null); ++ input.stackedContents().resetExtras(); ++ return canCraft; + // Paper end - unwrap ternary & better exact choice recipes }