From db062b80d28c3873f54ed9dae5087e7a1a209051 Mon Sep 17 00:00:00 2001 From: derverdox Date: Sun, 3 Dec 2023 13:31:03 +0100 Subject: [PATCH 1/3] Adding PredicateChoice to Paper API --- ...-Adding-PredicateChoice-to-Paper-API.patch | 187 ++++++++++++++++ ...-Adding-PredicateChoice-to-Paper-API.patch | 210 ++++++++++++++++++ 2 files changed, 397 insertions(+) create mode 100644 patches/api/0449-Adding-PredicateChoice-to-Paper-API.patch create mode 100644 patches/server/1055-Adding-PredicateChoice-to-Paper-API.patch diff --git a/patches/api/0449-Adding-PredicateChoice-to-Paper-API.patch b/patches/api/0449-Adding-PredicateChoice-to-Paper-API.patch new file mode 100644 index 000000000000..fd6c7d4aba96 --- /dev/null +++ b/patches/api/0449-Adding-PredicateChoice-to-Paper-API.patch @@ -0,0 +1,187 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: derverdox +Date: Sun, 3 Dec 2023 02:39:02 +0100 +Subject: [PATCH] Adding PredicateChoice to Paper API + + +diff --git a/src/main/java/de/verdox/predicatechoice/ItemPredicate.java b/src/main/java/de/verdox/predicatechoice/ItemPredicate.java +new file mode 100644 +index 0000000000000000000000000000000000000000..cbddd2e8f5d8a1c19c56029af5608ed8f2e8cc6c +--- /dev/null ++++ b/src/main/java/de/verdox/predicatechoice/ItemPredicate.java +@@ -0,0 +1,95 @@ ++package de.verdox.predicatechoice; ++ ++import com.google.common.base.Preconditions; ++import org.bukkit.Material; ++import org.bukkit.Tag; ++import org.bukkit.inventory.ItemStack; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.List; ++import java.util.function.Predicate; ++import java.util.stream.Collectors; ++ ++public interface ItemPredicate extends Predicate { ++ List recipeBookExamples(); ++ ++ class MaterialPredicate implements ItemPredicate { ++ private final List choices; ++ ++ public MaterialPredicate(@NotNull List choices) { ++ Preconditions.checkArgument(choices != null, "choices"); ++ Preconditions.checkArgument(!choices.isEmpty(), "Must have at least one choice"); ++ for (Material choice : choices) { ++ Preconditions.checkArgument(choice != null, "Cannot have null choice"); ++ } ++ this.choices = choices; ++ } ++ ++ public MaterialPredicate(@NotNull Material choice) { ++ this(Arrays.asList(choice)); ++ } ++ ++ public MaterialPredicate(@NotNull Material... choices) { ++ this(Arrays.asList(choices)); ++ } ++ ++ public MaterialPredicate(@NotNull Tag choices) { ++ Preconditions.checkArgument(choices != null, "choices"); ++ this.choices = new ArrayList<>(choices.getValues()); ++ } ++ ++ @Override ++ public List recipeBookExamples() { ++ return choices.stream().map(ItemStack::new).collect(Collectors.toList()); ++ } ++ ++ @Override ++ public boolean test(final ItemStack stack) { ++ for (Material match : choices) { ++ if (stack.getType() == match) { ++ return true; ++ } ++ } ++ return false; ++ } ++ } ++ ++ class ExactItemPredicate implements ItemPredicate { ++ private List choices; ++ ++ public ExactItemPredicate(@NotNull ItemStack stack) { ++ this(Arrays.asList(stack)); ++ } ++ ++ public ExactItemPredicate(@NotNull ItemStack... stacks) { ++ this(Arrays.asList(stacks)); ++ } ++ ++ public ExactItemPredicate(@NotNull List choices) { ++ Preconditions.checkArgument(choices != null, "choices"); ++ Preconditions.checkArgument(!choices.isEmpty(), "Must have at least one choice"); ++ for (ItemStack choice : choices) { ++ Preconditions.checkArgument(choice != null, "Cannot have null choice"); ++ } ++ ++ this.choices = new ArrayList<>(choices); ++ } ++ ++ @Override ++ public List recipeBookExamples() { ++ return choices; ++ } ++ ++ @Override ++ public boolean test(final ItemStack stack) { ++ for (ItemStack match : choices) { ++ if (stack.isSimilar(match)) { ++ return true; ++ } ++ } ++ return false; ++ } ++ } ++} +diff --git a/src/main/java/de/verdox/predicatechoice/PredicateChoice.java b/src/main/java/de/verdox/predicatechoice/PredicateChoice.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6e83917aa79d9d5e3fd121dd961097a5a01d14ba +--- /dev/null ++++ b/src/main/java/de/verdox/predicatechoice/PredicateChoice.java +@@ -0,0 +1,50 @@ ++package de.verdox.predicatechoice; ++ ++import com.google.common.base.Preconditions; ++import org.bukkit.inventory.ItemStack; ++import org.bukkit.inventory.RecipeChoice; ++import org.jetbrains.annotations.NotNull; ++import java.util.Collections; ++import java.util.List; ++import java.util.Objects; ++ ++public record PredicateChoice(ItemPredicate itemPredicate) implements RecipeChoice { ++ public PredicateChoice { ++ Preconditions.checkArgument(itemPredicate != null, "itemPredicate"); ++ Preconditions.checkArgument(!itemPredicate.recipeBookExamples().isEmpty(), "Must have at least one template"); ++ } ++ ++ @Override ++ public final boolean test(final ItemStack stack) { ++ return itemPredicate.test(stack); ++ } ++ ++ @Override ++ public @NotNull ItemStack getItemStack() { ++ ItemStack stack = new ItemStack(itemPredicate.recipeBookExamples().get(0)); ++ // For compat ++ if (itemPredicate.recipeBookExamples().size() > 1) { ++ stack.setDurability(Short.MAX_VALUE); ++ } ++ ++ return stack; ++ } ++ ++ @Override ++ public PredicateChoice clone() { ++ return new PredicateChoice(itemPredicate); ++ } ++ ++ @NotNull ++ public List getRecipeBookTemplates() { ++ return Collections.unmodifiableList(itemPredicate.recipeBookExamples()); ++ } ++ ++ @Override ++ public boolean equals(final Object o) { ++ if (this == o) return true; ++ if (o == null || getClass() != o.getClass()) return false; ++ PredicateChoice that = (PredicateChoice) o; ++ return Objects.equals(itemPredicate, that.itemPredicate); ++ } ++} +diff --git a/src/main/java/org/bukkit/inventory/RecipeChoice.java b/src/main/java/org/bukkit/inventory/RecipeChoice.java +index 523818cbb0d6c90481ec97123e7fe0e2ff4eea14..62883850b9d1887708a56eca2d17959c98acc9fe 100644 +--- a/src/main/java/org/bukkit/inventory/RecipeChoice.java ++++ b/src/main/java/org/bukkit/inventory/RecipeChoice.java +@@ -37,7 +37,9 @@ public interface RecipeChoice extends Predicate, Cloneable { + + /** + * Represents a choice of multiple matching Materials. ++ * Deprecated - Use {@link de.verdox.predicatechoice.PredicateChoice instead} + */ ++ @Deprecated + public static class MaterialChoice implements RecipeChoice { + + private List choices; +@@ -146,7 +148,9 @@ public interface RecipeChoice extends Predicate, Cloneable { + /** + * Represents a choice that will be valid only one of the stacks is exactly + * matched (aside from stack size). ++ * Deprecated - Use {@link de.verdox.predicatechoice.PredicateChoice instead} + */ ++ @Deprecated + public static class ExactChoice implements RecipeChoice { + + private List choices; diff --git a/patches/server/1055-Adding-PredicateChoice-to-Paper-API.patch b/patches/server/1055-Adding-PredicateChoice-to-Paper-API.patch new file mode 100644 index 000000000000..cea55c23e57f --- /dev/null +++ b/patches/server/1055-Adding-PredicateChoice-to-Paper-API.patch @@ -0,0 +1,210 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: derverdox +Date: Sun, 3 Dec 2023 02:56:42 +0100 +Subject: [PATCH] Adding PredicateChoice to Paper API + + +diff --git a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java +index 63db0b843c5bd11f979e613ba6cfac9d9da956bb..f0758b3ee7da21f23132fcdf5b10c9554af3ef9a 100644 +--- a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java ++++ b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java +@@ -24,39 +24,52 @@ public final class StackedContentsExtraMap { + private final Int2ObjectMap idToExactChoice = new Int2ObjectOpenHashMap<>(); + private final StackedContents contents; + public final Map extraStackingIds = new IdentityHashMap<>(); ++ private Recipe recipe; + + public StackedContentsExtraMap(final StackedContents contents, final Recipe recipe) { + this.exactChoiceIds.defaultReturnValue(-1); + this.contents = contents; + this.initialize(recipe); + } ++ /** ++ * Instead of using the items an ingredient provides we choose a different approach. ++ * PredicateChoices do not have those "template items". That is why we use the items in the stacked contents as "template items" ++ * Whenever we account a stack we test if there is any ingredient that would return true on test(itemStack). ++ * Else we act as if the item was not in the inventory. ++ * As a result, there is no need to change the vanilla implementation of RecipePicker. ++ */ + + private void initialize(final Recipe recipe) { +- if (recipe.hasExactIngredients()) { +- for (final Ingredient ingredient : recipe.getIngredients()) { +- if (!ingredient.isEmpty() && ingredient.exact) { +- final net.minecraft.world.item.ItemStack[] items = ingredient.getItems(); +- final IntList idList = new IntArrayList(items.length); +- for (final ItemStack item : items) { +- idList.add(this.registerExact(item)); // I think not copying the stack here is safe because cb copies the stack when creating the ingredient +- if (!item.hasTag()) { +- // add regular index if it's a plain itemstack but still registered as exact +- idList.add(StackedContents.getStackingIndex(item)); +- } +- } +- idList.sort(IntComparators.NATURAL_COMPARATOR); +- this.extraStackingIds.put(ingredient, idList); +- } +- } ++ this.recipe = recipe; ++ for (final Ingredient ingredient : recipe.getIngredients()) { ++ if(ingredient.isEmpty()) ++ continue; ++ extraStackingIds.put(ingredient, new IntArrayList()); + } + } + + private int registerExact(final ItemStack exactChoice) { ++ if(exactChoice.isEmpty()) ++ return -1; + final int existing = this.exactChoiceIds.getInt(exactChoice); + if (existing > -1) { + return existing; + } + final int id = this.idCounter.getAndIncrement(); ++ boolean isPotentialIngredient = false; ++ for (final Ingredient ingredient : this.recipe.getIngredients()) { ++ if (ingredient.test(exactChoice)) { ++ isPotentialIngredient = true; ++ final IntList idList = extraStackingIds.computeIfAbsent(ingredient, ingredient1 -> new IntArrayList()); ++ idList.add(id); ++ if (!exactChoice.hasTag()) ++ idList.add(StackedContents.getStackingIndex(exactChoice)); ++ idList.sort(IntComparators.NATURAL_COMPARATOR); ++ } ++ } ++ ++ if(!isPotentialIngredient) ++ return -1; + this.exactChoiceIds.put(exactChoice, id); + this.idToExactChoice.put(id, exactChoice); + return id; +@@ -67,6 +80,7 @@ public final class StackedContentsExtraMap { + } + + public boolean accountStack(final ItemStack stack, final int count) { ++ registerExact(stack); + if (!this.exactChoiceIds.isEmpty()) { + final int id = this.exactChoiceIds.getInt(stack); + if (id >= 0) { +@@ -74,6 +88,7 @@ public final class StackedContentsExtraMap { + return true; + } + } +- return false; ++ // We always want to use extra map ++ return true; + } + } +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 26b236a764177ac16d53f5cbaf83d3e21d015ebc..82eda864013cbebf94a28e9de49efc23c562238d 100644 +--- a/src/main/java/net/minecraft/world/entity/player/StackedContents.java ++++ b/src/main/java/net/minecraft/world/entity/player/StackedContents.java +@@ -24,7 +24,8 @@ public class StackedContents { + @Nullable public io.papermc.paper.inventory.recipe.StackedContentsExtraMap extrasMap = null; // Paper + + public void accountSimpleStack(ItemStack stack) { +- if (this.extrasMap != null && stack.hasTag() && this.extrasMap.accountStack(stack, Math.min(64, stack.getCount()))) return; // Paper - max of 64 due to accountStack method below ++ // We always want to use extra map! ++ if (this.extrasMap != null /*&& stack.hasTag()*/ && this.extrasMap.accountStack(stack, Math.min(64, stack.getCount()))) return; // Paper - max of 64 due to accountStack method below + if (!stack.isDamaged() && !stack.isEnchanted() && !stack.hasCustomHoverName()) { + this.accountStack(stack); + } +@@ -39,7 +40,8 @@ public class StackedContents { + if (!stack.isEmpty()) { + int i = getStackingIndex(stack); + int j = Math.min(maxCount, stack.getCount()); +- if (this.extrasMap != null && stack.hasTag() && this.extrasMap.accountStack(stack, j)) return; // Paper - if an exact ingredient, don't include it ++ // We always want to use extra map! ++ if (this.extrasMap != null /*&& stack.hasTag()*/ && this.extrasMap.accountStack(stack, j)) return; // Paper - if an exact ingredient, don't include it + this.put(i, j); + } + +diff --git a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java +index 06fe5b056d78d42cdf78437eeabe1786d596b7f8..d9a0917a6f0291023042c01b33f5ca8793e441f0 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java ++++ b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java +@@ -7,6 +7,7 @@ import com.mojang.serialization.Codec; + import com.mojang.serialization.DataResult; + import com.mojang.serialization.JsonOps; + import com.mojang.serialization.codecs.RecordCodecBuilder; ++import de.verdox.predicatechoice.PredicateChoice; + import it.unimi.dsi.fastutil.ints.IntArrayList; + import it.unimi.dsi.fastutil.ints.IntComparators; + import it.unimi.dsi.fastutil.ints.IntList; +@@ -29,11 +30,13 @@ import net.minecraft.world.entity.player.StackedContents; + import net.minecraft.world.item.Item; + import net.minecraft.world.item.ItemStack; + import net.minecraft.world.level.ItemLike; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; + + public final class Ingredient implements Predicate { + + public static final Ingredient EMPTY = new Ingredient(Stream.empty()); + private final Ingredient.Value[] values; ++ private PredicateChoice predicateChoice; + @Nullable + public ItemStack[] itemStacks; + @Nullable +@@ -42,6 +45,11 @@ public final class Ingredient implements Predicate { + public static final Codec CODEC = Ingredient.codec(true); + public static final Codec CODEC_NONEMPTY = Ingredient.codec(false); + ++ public Ingredient(PredicateChoice predicateChoice) { ++ List bukkitChoices = predicateChoice.getRecipeBookTemplates(); ++ this.predicateChoice = predicateChoice; ++ this.values = bukkitChoices.stream().map(CraftItemStack::asNMSCopy).map(ItemValue::new).toArray(Value[]::new); ++ } + public Ingredient(Stream entries) { + this.values = (Ingredient.Value[]) entries.toArray((i) -> { + return new Ingredient.Value[i]; +@@ -70,6 +78,9 @@ public final class Ingredient implements Predicate { + } else if (this.isEmpty()) { + return itemstack.isEmpty(); + } else { ++ if (predicateChoice != null) { ++ return predicateChoice.test(itemstack.getBukkitStack()); ++ } + ItemStack[] aitemstack = this.getItems(); + int i = aitemstack.length; + +@@ -269,4 +280,7 @@ public final class Ingredient implements Predicate { + return Collections.singleton(this.item); + } + } ++ public @Nullable PredicateChoice getPredicateChoice() { ++ return predicateChoice; ++ } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java +index 13d25d118eb4d3ef35a4cdfb9bbde9ed83f6c04b..fb2ad3895989f68ca604dc1a7546842315f5dd49 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java +@@ -3,6 +3,7 @@ package org.bukkit.craftbukkit.inventory; + import com.google.common.base.Preconditions; + import java.util.ArrayList; + import java.util.List; ++import de.verdox.predicatechoice.PredicateChoice; + import net.minecraft.world.item.crafting.Ingredient; + import org.bukkit.craftbukkit.util.CraftMagicNumbers; + import org.bukkit.inventory.ItemStack; +@@ -30,7 +31,11 @@ public interface CraftRecipe extends Recipe { + } else if (bukkit instanceof RecipeChoice.ExactChoice) { + stack = new Ingredient(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> new net.minecraft.world.item.crafting.Ingredient.ItemValue(CraftItemStack.asNMSCopy(mat)))); + stack.exact = true; +- } else { ++ } ++ else if(bukkit instanceof PredicateChoice predicateChoice){ ++ stack = new Ingredient(predicateChoice); ++ } ++ else { + throw new IllegalArgumentException("Unknown recipe stack instance " + bukkit); + } + +@@ -49,6 +54,8 @@ public interface CraftRecipe extends Recipe { + return null; + } + ++ if(list.getPredicateChoice() != null) ++ return list.getPredicateChoice(); + if (list.exact) { + List choices = new ArrayList<>(list.itemStacks.length); + for (net.minecraft.world.item.ItemStack i : list.itemStacks) { From 904e97cad410371e500a679cb22f61fa6ed35063 Mon Sep 17 00:00:00 2001 From: derverdox Date: Mon, 4 Dec 2023 02:03:52 +0100 Subject: [PATCH 2/3] Better implementation of PredicateChoice for Paper API --- ...-Adding-PredicateChoice-to-Paper-API.patch | 187 --------------- ...-Adding-PredicateChoice-to-Paper-API.patch | 75 +++++++ ...-Adding-PredicateChoice-to-Paper-API.patch | 210 ----------------- ...-Adding-PredicateChoice-to-Paper-API.patch | 212 ++++++++++++++++++ 4 files changed, 287 insertions(+), 397 deletions(-) delete mode 100644 patches/api/0449-Adding-PredicateChoice-to-Paper-API.patch create mode 100644 patches/api/0462-Adding-PredicateChoice-to-Paper-API.patch delete mode 100644 patches/server/1055-Adding-PredicateChoice-to-Paper-API.patch create mode 100644 patches/server/1056-Adding-PredicateChoice-to-Paper-API.patch diff --git a/patches/api/0449-Adding-PredicateChoice-to-Paper-API.patch b/patches/api/0449-Adding-PredicateChoice-to-Paper-API.patch deleted file mode 100644 index fd6c7d4aba96..000000000000 --- a/patches/api/0449-Adding-PredicateChoice-to-Paper-API.patch +++ /dev/null @@ -1,187 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: derverdox -Date: Sun, 3 Dec 2023 02:39:02 +0100 -Subject: [PATCH] Adding PredicateChoice to Paper API - - -diff --git a/src/main/java/de/verdox/predicatechoice/ItemPredicate.java b/src/main/java/de/verdox/predicatechoice/ItemPredicate.java -new file mode 100644 -index 0000000000000000000000000000000000000000..cbddd2e8f5d8a1c19c56029af5608ed8f2e8cc6c ---- /dev/null -+++ b/src/main/java/de/verdox/predicatechoice/ItemPredicate.java -@@ -0,0 +1,95 @@ -+package de.verdox.predicatechoice; -+ -+import com.google.common.base.Preconditions; -+import org.bukkit.Material; -+import org.bukkit.Tag; -+import org.bukkit.inventory.ItemStack; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.ArrayList; -+import java.util.Arrays; -+import java.util.List; -+import java.util.function.Predicate; -+import java.util.stream.Collectors; -+ -+public interface ItemPredicate extends Predicate { -+ List recipeBookExamples(); -+ -+ class MaterialPredicate implements ItemPredicate { -+ private final List choices; -+ -+ public MaterialPredicate(@NotNull List choices) { -+ Preconditions.checkArgument(choices != null, "choices"); -+ Preconditions.checkArgument(!choices.isEmpty(), "Must have at least one choice"); -+ for (Material choice : choices) { -+ Preconditions.checkArgument(choice != null, "Cannot have null choice"); -+ } -+ this.choices = choices; -+ } -+ -+ public MaterialPredicate(@NotNull Material choice) { -+ this(Arrays.asList(choice)); -+ } -+ -+ public MaterialPredicate(@NotNull Material... choices) { -+ this(Arrays.asList(choices)); -+ } -+ -+ public MaterialPredicate(@NotNull Tag choices) { -+ Preconditions.checkArgument(choices != null, "choices"); -+ this.choices = new ArrayList<>(choices.getValues()); -+ } -+ -+ @Override -+ public List recipeBookExamples() { -+ return choices.stream().map(ItemStack::new).collect(Collectors.toList()); -+ } -+ -+ @Override -+ public boolean test(final ItemStack stack) { -+ for (Material match : choices) { -+ if (stack.getType() == match) { -+ return true; -+ } -+ } -+ return false; -+ } -+ } -+ -+ class ExactItemPredicate implements ItemPredicate { -+ private List choices; -+ -+ public ExactItemPredicate(@NotNull ItemStack stack) { -+ this(Arrays.asList(stack)); -+ } -+ -+ public ExactItemPredicate(@NotNull ItemStack... stacks) { -+ this(Arrays.asList(stacks)); -+ } -+ -+ public ExactItemPredicate(@NotNull List choices) { -+ Preconditions.checkArgument(choices != null, "choices"); -+ Preconditions.checkArgument(!choices.isEmpty(), "Must have at least one choice"); -+ for (ItemStack choice : choices) { -+ Preconditions.checkArgument(choice != null, "Cannot have null choice"); -+ } -+ -+ this.choices = new ArrayList<>(choices); -+ } -+ -+ @Override -+ public List recipeBookExamples() { -+ return choices; -+ } -+ -+ @Override -+ public boolean test(final ItemStack stack) { -+ for (ItemStack match : choices) { -+ if (stack.isSimilar(match)) { -+ return true; -+ } -+ } -+ return false; -+ } -+ } -+} -diff --git a/src/main/java/de/verdox/predicatechoice/PredicateChoice.java b/src/main/java/de/verdox/predicatechoice/PredicateChoice.java -new file mode 100644 -index 0000000000000000000000000000000000000000..6e83917aa79d9d5e3fd121dd961097a5a01d14ba ---- /dev/null -+++ b/src/main/java/de/verdox/predicatechoice/PredicateChoice.java -@@ -0,0 +1,50 @@ -+package de.verdox.predicatechoice; -+ -+import com.google.common.base.Preconditions; -+import org.bukkit.inventory.ItemStack; -+import org.bukkit.inventory.RecipeChoice; -+import org.jetbrains.annotations.NotNull; -+import java.util.Collections; -+import java.util.List; -+import java.util.Objects; -+ -+public record PredicateChoice(ItemPredicate itemPredicate) implements RecipeChoice { -+ public PredicateChoice { -+ Preconditions.checkArgument(itemPredicate != null, "itemPredicate"); -+ Preconditions.checkArgument(!itemPredicate.recipeBookExamples().isEmpty(), "Must have at least one template"); -+ } -+ -+ @Override -+ public final boolean test(final ItemStack stack) { -+ return itemPredicate.test(stack); -+ } -+ -+ @Override -+ public @NotNull ItemStack getItemStack() { -+ ItemStack stack = new ItemStack(itemPredicate.recipeBookExamples().get(0)); -+ // For compat -+ if (itemPredicate.recipeBookExamples().size() > 1) { -+ stack.setDurability(Short.MAX_VALUE); -+ } -+ -+ return stack; -+ } -+ -+ @Override -+ public PredicateChoice clone() { -+ return new PredicateChoice(itemPredicate); -+ } -+ -+ @NotNull -+ public List getRecipeBookTemplates() { -+ return Collections.unmodifiableList(itemPredicate.recipeBookExamples()); -+ } -+ -+ @Override -+ public boolean equals(final Object o) { -+ if (this == o) return true; -+ if (o == null || getClass() != o.getClass()) return false; -+ PredicateChoice that = (PredicateChoice) o; -+ return Objects.equals(itemPredicate, that.itemPredicate); -+ } -+} -diff --git a/src/main/java/org/bukkit/inventory/RecipeChoice.java b/src/main/java/org/bukkit/inventory/RecipeChoice.java -index 523818cbb0d6c90481ec97123e7fe0e2ff4eea14..62883850b9d1887708a56eca2d17959c98acc9fe 100644 ---- a/src/main/java/org/bukkit/inventory/RecipeChoice.java -+++ b/src/main/java/org/bukkit/inventory/RecipeChoice.java -@@ -37,7 +37,9 @@ public interface RecipeChoice extends Predicate, Cloneable { - - /** - * Represents a choice of multiple matching Materials. -+ * Deprecated - Use {@link de.verdox.predicatechoice.PredicateChoice instead} - */ -+ @Deprecated - public static class MaterialChoice implements RecipeChoice { - - private List choices; -@@ -146,7 +148,9 @@ public interface RecipeChoice extends Predicate, Cloneable { - /** - * Represents a choice that will be valid only one of the stacks is exactly - * matched (aside from stack size). -+ * Deprecated - Use {@link de.verdox.predicatechoice.PredicateChoice instead} - */ -+ @Deprecated - public static class ExactChoice implements RecipeChoice { - - private List choices; diff --git a/patches/api/0462-Adding-PredicateChoice-to-Paper-API.patch b/patches/api/0462-Adding-PredicateChoice-to-Paper-API.patch new file mode 100644 index 000000000000..b29a15784e43 --- /dev/null +++ b/patches/api/0462-Adding-PredicateChoice-to-Paper-API.patch @@ -0,0 +1,75 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: derverdox +Date: Mon, 4 Dec 2023 01:54:38 +0100 +Subject: [PATCH] Adding PredicateChoice to Paper API + + +diff --git a/src/main/java/org/bukkit/inventory/RecipeChoice.java b/src/main/java/org/bukkit/inventory/RecipeChoice.java +index 523818cbb0d6c90481ec97123e7fe0e2ff4eea14..6b9bbead57166d7622cf31b96c252d56fafc05fb 100644 +--- a/src/main/java/org/bukkit/inventory/RecipeChoice.java ++++ b/src/main/java/org/bukkit/inventory/RecipeChoice.java +@@ -233,4 +233,64 @@ public interface RecipeChoice extends Predicate, Cloneable { + return "ExactChoice{" + "choices=" + choices + '}'; + } + } ++ ++ /** ++ * Represents a choice that matches when the item predicate is fulfilled. ++ */ ++ public static class PredicateChoice implements RecipeChoice { ++ private final ItemPredicate itemPredicate; ++ ++ public PredicateChoice(ItemPredicate itemPredicate) { ++ Preconditions.checkArgument(itemPredicate != null, "itemPredicate"); ++ Preconditions.checkArgument(!itemPredicate.recipeBookExamples().isEmpty(), "Must have at least one template"); ++ this.itemPredicate = itemPredicate; ++ } ++ ++ @Override ++ public final boolean test(final ItemStack stack) { ++ return itemPredicate.test(stack); ++ } ++ ++ @Override ++ public @NotNull ItemStack getItemStack() { ++ ItemStack stack = new ItemStack(itemPredicate.recipeBookExamples().get(0)); ++ // For compat ++ if (itemPredicate.recipeBookExamples().size() > 1) { ++ stack.setDurability(Short.MAX_VALUE); ++ } ++ ++ return stack; ++ } ++ ++ @Override ++ public PredicateChoice clone() { ++ return new PredicateChoice(new ItemPredicate() { ++ @Override ++ public List recipeBookExamples() { ++ return List.copyOf(itemPredicate.recipeBookExamples()); ++ } ++ ++ @Override ++ public boolean test(final ItemStack stack) { ++ return itemPredicate.test(stack); ++ } ++ }); ++ } ++ ++ public ItemPredicate getItemPredicate() { ++ return itemPredicate; ++ } ++ ++ @Override ++ public boolean equals(final Object o) { ++ if (this == o) return true; ++ if (o == null || getClass() != o.getClass()) return false; ++ PredicateChoice that = (PredicateChoice) o; ++ return Objects.equals(itemPredicate, that.itemPredicate); ++ } ++ ++ public static interface ItemPredicate extends Predicate { ++ List recipeBookExamples(); ++ } ++ } + } diff --git a/patches/server/1055-Adding-PredicateChoice-to-Paper-API.patch b/patches/server/1055-Adding-PredicateChoice-to-Paper-API.patch deleted file mode 100644 index cea55c23e57f..000000000000 --- a/patches/server/1055-Adding-PredicateChoice-to-Paper-API.patch +++ /dev/null @@ -1,210 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: derverdox -Date: Sun, 3 Dec 2023 02:56:42 +0100 -Subject: [PATCH] Adding PredicateChoice to Paper API - - -diff --git a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java -index 63db0b843c5bd11f979e613ba6cfac9d9da956bb..f0758b3ee7da21f23132fcdf5b10c9554af3ef9a 100644 ---- a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java -+++ b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java -@@ -24,39 +24,52 @@ public final class StackedContentsExtraMap { - private final Int2ObjectMap idToExactChoice = new Int2ObjectOpenHashMap<>(); - private final StackedContents contents; - public final Map extraStackingIds = new IdentityHashMap<>(); -+ private Recipe recipe; - - public StackedContentsExtraMap(final StackedContents contents, final Recipe recipe) { - this.exactChoiceIds.defaultReturnValue(-1); - this.contents = contents; - this.initialize(recipe); - } -+ /** -+ * Instead of using the items an ingredient provides we choose a different approach. -+ * PredicateChoices do not have those "template items". That is why we use the items in the stacked contents as "template items" -+ * Whenever we account a stack we test if there is any ingredient that would return true on test(itemStack). -+ * Else we act as if the item was not in the inventory. -+ * As a result, there is no need to change the vanilla implementation of RecipePicker. -+ */ - - private void initialize(final Recipe recipe) { -- if (recipe.hasExactIngredients()) { -- for (final Ingredient ingredient : recipe.getIngredients()) { -- if (!ingredient.isEmpty() && ingredient.exact) { -- final net.minecraft.world.item.ItemStack[] items = ingredient.getItems(); -- final IntList idList = new IntArrayList(items.length); -- for (final ItemStack item : items) { -- idList.add(this.registerExact(item)); // I think not copying the stack here is safe because cb copies the stack when creating the ingredient -- if (!item.hasTag()) { -- // add regular index if it's a plain itemstack but still registered as exact -- idList.add(StackedContents.getStackingIndex(item)); -- } -- } -- idList.sort(IntComparators.NATURAL_COMPARATOR); -- this.extraStackingIds.put(ingredient, idList); -- } -- } -+ this.recipe = recipe; -+ for (final Ingredient ingredient : recipe.getIngredients()) { -+ if(ingredient.isEmpty()) -+ continue; -+ extraStackingIds.put(ingredient, new IntArrayList()); - } - } - - private int registerExact(final ItemStack exactChoice) { -+ if(exactChoice.isEmpty()) -+ return -1; - final int existing = this.exactChoiceIds.getInt(exactChoice); - if (existing > -1) { - return existing; - } - final int id = this.idCounter.getAndIncrement(); -+ boolean isPotentialIngredient = false; -+ for (final Ingredient ingredient : this.recipe.getIngredients()) { -+ if (ingredient.test(exactChoice)) { -+ isPotentialIngredient = true; -+ final IntList idList = extraStackingIds.computeIfAbsent(ingredient, ingredient1 -> new IntArrayList()); -+ idList.add(id); -+ if (!exactChoice.hasTag()) -+ idList.add(StackedContents.getStackingIndex(exactChoice)); -+ idList.sort(IntComparators.NATURAL_COMPARATOR); -+ } -+ } -+ -+ if(!isPotentialIngredient) -+ return -1; - this.exactChoiceIds.put(exactChoice, id); - this.idToExactChoice.put(id, exactChoice); - return id; -@@ -67,6 +80,7 @@ public final class StackedContentsExtraMap { - } - - public boolean accountStack(final ItemStack stack, final int count) { -+ registerExact(stack); - if (!this.exactChoiceIds.isEmpty()) { - final int id = this.exactChoiceIds.getInt(stack); - if (id >= 0) { -@@ -74,6 +88,7 @@ public final class StackedContentsExtraMap { - return true; - } - } -- return false; -+ // We always want to use extra map -+ return true; - } - } -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 26b236a764177ac16d53f5cbaf83d3e21d015ebc..82eda864013cbebf94a28e9de49efc23c562238d 100644 ---- a/src/main/java/net/minecraft/world/entity/player/StackedContents.java -+++ b/src/main/java/net/minecraft/world/entity/player/StackedContents.java -@@ -24,7 +24,8 @@ public class StackedContents { - @Nullable public io.papermc.paper.inventory.recipe.StackedContentsExtraMap extrasMap = null; // Paper - - public void accountSimpleStack(ItemStack stack) { -- if (this.extrasMap != null && stack.hasTag() && this.extrasMap.accountStack(stack, Math.min(64, stack.getCount()))) return; // Paper - max of 64 due to accountStack method below -+ // We always want to use extra map! -+ if (this.extrasMap != null /*&& stack.hasTag()*/ && this.extrasMap.accountStack(stack, Math.min(64, stack.getCount()))) return; // Paper - max of 64 due to accountStack method below - if (!stack.isDamaged() && !stack.isEnchanted() && !stack.hasCustomHoverName()) { - this.accountStack(stack); - } -@@ -39,7 +40,8 @@ public class StackedContents { - if (!stack.isEmpty()) { - int i = getStackingIndex(stack); - int j = Math.min(maxCount, stack.getCount()); -- if (this.extrasMap != null && stack.hasTag() && this.extrasMap.accountStack(stack, j)) return; // Paper - if an exact ingredient, don't include it -+ // We always want to use extra map! -+ if (this.extrasMap != null /*&& stack.hasTag()*/ && this.extrasMap.accountStack(stack, j)) return; // Paper - if an exact ingredient, don't include it - this.put(i, j); - } - -diff --git a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -index 06fe5b056d78d42cdf78437eeabe1786d596b7f8..d9a0917a6f0291023042c01b33f5ca8793e441f0 100644 ---- a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -+++ b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -@@ -7,6 +7,7 @@ import com.mojang.serialization.Codec; - import com.mojang.serialization.DataResult; - import com.mojang.serialization.JsonOps; - import com.mojang.serialization.codecs.RecordCodecBuilder; -+import de.verdox.predicatechoice.PredicateChoice; - import it.unimi.dsi.fastutil.ints.IntArrayList; - import it.unimi.dsi.fastutil.ints.IntComparators; - import it.unimi.dsi.fastutil.ints.IntList; -@@ -29,11 +30,13 @@ import net.minecraft.world.entity.player.StackedContents; - import net.minecraft.world.item.Item; - import net.minecraft.world.item.ItemStack; - import net.minecraft.world.level.ItemLike; -+import org.bukkit.craftbukkit.inventory.CraftItemStack; - - public final class Ingredient implements Predicate { - - public static final Ingredient EMPTY = new Ingredient(Stream.empty()); - private final Ingredient.Value[] values; -+ private PredicateChoice predicateChoice; - @Nullable - public ItemStack[] itemStacks; - @Nullable -@@ -42,6 +45,11 @@ public final class Ingredient implements Predicate { - public static final Codec CODEC = Ingredient.codec(true); - public static final Codec CODEC_NONEMPTY = Ingredient.codec(false); - -+ public Ingredient(PredicateChoice predicateChoice) { -+ List bukkitChoices = predicateChoice.getRecipeBookTemplates(); -+ this.predicateChoice = predicateChoice; -+ this.values = bukkitChoices.stream().map(CraftItemStack::asNMSCopy).map(ItemValue::new).toArray(Value[]::new); -+ } - public Ingredient(Stream entries) { - this.values = (Ingredient.Value[]) entries.toArray((i) -> { - return new Ingredient.Value[i]; -@@ -70,6 +78,9 @@ public final class Ingredient implements Predicate { - } else if (this.isEmpty()) { - return itemstack.isEmpty(); - } else { -+ if (predicateChoice != null) { -+ return predicateChoice.test(itemstack.getBukkitStack()); -+ } - ItemStack[] aitemstack = this.getItems(); - int i = aitemstack.length; - -@@ -269,4 +280,7 @@ public final class Ingredient implements Predicate { - return Collections.singleton(this.item); - } - } -+ public @Nullable PredicateChoice getPredicateChoice() { -+ return predicateChoice; -+ } - } -diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java -index 13d25d118eb4d3ef35a4cdfb9bbde9ed83f6c04b..fb2ad3895989f68ca604dc1a7546842315f5dd49 100644 ---- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java -+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java -@@ -3,6 +3,7 @@ package org.bukkit.craftbukkit.inventory; - import com.google.common.base.Preconditions; - import java.util.ArrayList; - import java.util.List; -+import de.verdox.predicatechoice.PredicateChoice; - import net.minecraft.world.item.crafting.Ingredient; - import org.bukkit.craftbukkit.util.CraftMagicNumbers; - import org.bukkit.inventory.ItemStack; -@@ -30,7 +31,11 @@ public interface CraftRecipe extends Recipe { - } else if (bukkit instanceof RecipeChoice.ExactChoice) { - stack = new Ingredient(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> new net.minecraft.world.item.crafting.Ingredient.ItemValue(CraftItemStack.asNMSCopy(mat)))); - stack.exact = true; -- } else { -+ } -+ else if(bukkit instanceof PredicateChoice predicateChoice){ -+ stack = new Ingredient(predicateChoice); -+ } -+ else { - throw new IllegalArgumentException("Unknown recipe stack instance " + bukkit); - } - -@@ -49,6 +54,8 @@ public interface CraftRecipe extends Recipe { - return null; - } - -+ if(list.getPredicateChoice() != null) -+ return list.getPredicateChoice(); - if (list.exact) { - List choices = new ArrayList<>(list.itemStacks.length); - for (net.minecraft.world.item.ItemStack i : list.itemStacks) { diff --git a/patches/server/1056-Adding-PredicateChoice-to-Paper-API.patch b/patches/server/1056-Adding-PredicateChoice-to-Paper-API.patch new file mode 100644 index 000000000000..c84359246a0f --- /dev/null +++ b/patches/server/1056-Adding-PredicateChoice-to-Paper-API.patch @@ -0,0 +1,212 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: derverdox +Date: Mon, 4 Dec 2023 01:54:37 +0100 +Subject: [PATCH] Adding PredicateChoice to Paper API + + +diff --git a/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java b/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java +index 2a2f8327a5bd3983a3a13fd663beb98906f27312..a4b4d8fe73a5d626906ed859abbdee9591263645 100644 +--- a/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java ++++ b/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java +@@ -6,25 +6,28 @@ import net.minecraft.world.item.crafting.Recipe; + + public abstract class RecipeBookExactChoiceRecipe implements Recipe { + +- private boolean hasExactIngredients; ++ private boolean hasSpecialIngredients; + + protected final void checkExactIngredients() { + // skip any special recipes + if (this.isSpecial()) { +- this.hasExactIngredients = false; ++ this.hasSpecialIngredients = false; + return; + } + for (final Ingredient ingredient : this.getIngredients()) { + if (!ingredient.isEmpty() && ingredient.exact) { +- this.hasExactIngredients = true; ++ this.hasSpecialIngredients = true; ++ return; ++ } else if (!ingredient.isEmpty() && ingredient.getItemPredicate() != null) { ++ this.hasSpecialIngredients = true; + return; + } + } +- this.hasExactIngredients = false; ++ this.hasSpecialIngredients = false; + } + + @Override +- public final boolean hasExactIngredients() { +- return this.hasExactIngredients; ++ public final boolean hasSpecialIngredients() { ++ return this.hasSpecialIngredients; + } + } +diff --git a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java +index 63db0b843c5bd11f979e613ba6cfac9d9da956bb..63a161ca3c228263d71d2da86b92970a13d28181 100644 +--- a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java ++++ b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java +@@ -7,7 +7,9 @@ import it.unimi.dsi.fastutil.ints.IntComparators; + import it.unimi.dsi.fastutil.ints.IntList; + import it.unimi.dsi.fastutil.objects.Object2IntMap; + import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; ++import java.util.ArrayList; + import java.util.IdentityHashMap; ++import java.util.List; + import java.util.Map; + import java.util.concurrent.atomic.AtomicInteger; + import net.minecraft.core.registries.BuiltInRegistries; +@@ -18,12 +20,12 @@ 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); + private final Int2ObjectMap idToExactChoice = new Int2ObjectOpenHashMap<>(); + private final StackedContents contents; + public final Map extraStackingIds = new IdentityHashMap<>(); ++ public final List predicateChoices = new ArrayList<>(); // Adding PredicateChoice + + public StackedContentsExtraMap(final StackedContents contents, final Recipe recipe) { + this.exactChoiceIds.defaultReturnValue(-1); +@@ -32,7 +34,7 @@ public final class StackedContentsExtraMap { + } + + private void initialize(final Recipe recipe) { +- if (recipe.hasExactIngredients()) { ++ if (recipe.hasSpecialIngredients()) { + for (final Ingredient ingredient : recipe.getIngredients()) { + if (!ingredient.isEmpty() && ingredient.exact) { + final net.minecraft.world.item.ItemStack[] items = ingredient.getItems(); +@@ -47,6 +49,11 @@ public final class StackedContentsExtraMap { + idList.sort(IntComparators.NATURAL_COMPARATOR); + this.extraStackingIds.put(ingredient, idList); + } ++ // Adding PredicateChoice ++ else if(!ingredient.isEmpty() && ingredient.getItemPredicate() != null){ ++ this.predicateChoices.add(ingredient); ++ this.extraStackingIds.put(ingredient, new IntArrayList()); // fill id list when accounting stacks ++ } + } + } + } +@@ -67,6 +74,16 @@ public final class StackedContentsExtraMap { + } + + public boolean accountStack(final ItemStack stack, final int count) { ++ // We are adding items that pass the predicate test. ++ for(final Ingredient predicateChoice : this.predicateChoices){ ++ if(predicateChoice.test(stack)){ ++ boolean isStackTypeRegistered = this.exactChoiceIds.getInt(stack) > -1; ++ final int id = this.registerExact(stack); ++ // We only want to add the stacking id to the list one time ++ if(!isStackTypeRegistered) ++ this.extraStackingIds.get(predicateChoice).add(id); ++ } ++ } + if (!this.exactChoiceIds.isEmpty()) { + final int id = this.exactChoiceIds.getInt(stack); + if (id >= 0) { +diff --git a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java +index 06fe5b056d78d42cdf78437eeabe1786d596b7f8..d98201d34c5863a9c1588d64aa0e72b1529a980c 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java ++++ b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java +@@ -29,11 +29,14 @@ import net.minecraft.world.entity.player.StackedContents; + import net.minecraft.world.item.Item; + import net.minecraft.world.item.ItemStack; + import net.minecraft.world.level.ItemLike; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.inventory.RecipeChoice; + + public final class Ingredient implements Predicate { + + public static final Ingredient EMPTY = new Ingredient(Stream.empty()); + private final Ingredient.Value[] values; ++ @Nullable private RecipeChoice.PredicateChoice.ItemPredicate itemPredicate; // Paper + @Nullable + public ItemStack[] itemStacks; + @Nullable +@@ -41,7 +44,13 @@ public final class Ingredient implements Predicate { + public boolean exact; // CraftBukkit + public static final Codec CODEC = Ingredient.codec(true); + public static final Codec CODEC_NONEMPTY = Ingredient.codec(false); +- ++ // Paper start - Adding PredicateChoice ++ public Ingredient(RecipeChoice.PredicateChoice.ItemPredicate itemPredicate) { ++ List bukkitChoices = itemPredicate.recipeBookExamples(); ++ this.itemPredicate = itemPredicate; ++ this.values = bukkitChoices.stream().map(CraftItemStack::asNMSCopy).map(ItemValue::new).toArray(Value[]::new); ++ } ++ // Paper end - Adding PredicateChoice + public Ingredient(Stream entries) { + this.values = (Ingredient.Value[]) entries.toArray((i) -> { + return new Ingredient.Value[i]; +@@ -70,6 +79,11 @@ public final class Ingredient implements Predicate { + } else if (this.isEmpty()) { + return itemstack.isEmpty(); + } else { ++ // Paper start - Adding PredicateChoice ++ if (itemPredicate != null) { ++ return itemPredicate.test(itemstack.getBukkitStack()); ++ } ++ // Paper end - Adding PredicateChoice + ItemStack[] aitemstack = this.getItems(); + int i = aitemstack.length; + +@@ -269,4 +283,10 @@ public final class Ingredient implements Predicate { + return Collections.singleton(this.item); + } + } ++ // Paper start - Adding PredicateChoice ++ @Nullable ++ public RecipeChoice.PredicateChoice.ItemPredicate getItemPredicate() { ++ return itemPredicate; ++ } ++ // Paper end - Adding PredicateChoice + } +diff --git a/src/main/java/net/minecraft/world/item/crafting/Recipe.java b/src/main/java/net/minecraft/world/item/crafting/Recipe.java +index 80387cd1bee2bd4c024073cee74222828f9f2c17..6fbbf255fe061943b72324d1484c7b9e885f8535 100644 +--- a/src/main/java/net/minecraft/world/item/crafting/Recipe.java ++++ b/src/main/java/net/minecraft/world/item/crafting/Recipe.java +@@ -67,7 +67,7 @@ public interface Recipe { + org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id); // CraftBukkit + + // Paper start - improved exact choice recipes +- default boolean hasExactIngredients() { ++ default boolean hasSpecialIngredients() { + return false; + } + // Paper end +diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java +index 13d25d118eb4d3ef35a4cdfb9bbde9ed83f6c04b..8b308da84b59c04e0b80d419d4eea602f01abcbf 100644 +--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java ++++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java +@@ -30,7 +30,13 @@ public interface CraftRecipe extends Recipe { + } else if (bukkit instanceof RecipeChoice.ExactChoice) { + stack = new Ingredient(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> new net.minecraft.world.item.crafting.Ingredient.ItemValue(CraftItemStack.asNMSCopy(mat)))); + stack.exact = true; +- } else { ++ } ++ // Paper start - Adding PredicateChoice ++ else if(bukkit instanceof RecipeChoice.PredicateChoice predicateChoice){ ++ stack = new Ingredient(predicateChoice.getItemPredicate()); ++ } ++ // Paper end - Adding PredicateChoice ++ else { + throw new IllegalArgumentException("Unknown recipe stack instance " + bukkit); + } + +@@ -48,7 +54,10 @@ public interface CraftRecipe extends Recipe { + if (list.itemStacks.length == 0) { + return null; + } +- ++ // Paper start - Adding PredicateChoice ++ if(list.getItemPredicate() != null) ++ return new RecipeChoice.PredicateChoice(list.getItemPredicate()); ++ // Paper end - Adding PredicateChoice + if (list.exact) { + List choices = new ArrayList<>(list.itemStacks.length); + for (net.minecraft.world.item.ItemStack i : list.itemStacks) { From 135b0d5cfa722beab152475ccc21b3c6b80b2966 Mon Sep 17 00:00:00 2001 From: derverdox Date: Thu, 21 Mar 2024 16:57:10 +0100 Subject: [PATCH 3/3] - Small code changes - Fixed RecipeBook click not working --- ...-Adding-PredicateChoice-to-Paper-API.patch | 75 ---------- patches/api/0469-Adding-PredicateChoice.patch | 94 ++++++++++++ ...atch => 1056-Adding-PredicateChoice.patch} | 134 +++++++----------- 3 files changed, 144 insertions(+), 159 deletions(-) delete mode 100644 patches/api/0462-Adding-PredicateChoice-to-Paper-API.patch create mode 100644 patches/api/0469-Adding-PredicateChoice.patch rename patches/server/{1056-Adding-PredicateChoice-to-Paper-API.patch => 1056-Adding-PredicateChoice.patch} (58%) diff --git a/patches/api/0462-Adding-PredicateChoice-to-Paper-API.patch b/patches/api/0462-Adding-PredicateChoice-to-Paper-API.patch deleted file mode 100644 index b29a15784e43..000000000000 --- a/patches/api/0462-Adding-PredicateChoice-to-Paper-API.patch +++ /dev/null @@ -1,75 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: derverdox -Date: Mon, 4 Dec 2023 01:54:38 +0100 -Subject: [PATCH] Adding PredicateChoice to Paper API - - -diff --git a/src/main/java/org/bukkit/inventory/RecipeChoice.java b/src/main/java/org/bukkit/inventory/RecipeChoice.java -index 523818cbb0d6c90481ec97123e7fe0e2ff4eea14..6b9bbead57166d7622cf31b96c252d56fafc05fb 100644 ---- a/src/main/java/org/bukkit/inventory/RecipeChoice.java -+++ b/src/main/java/org/bukkit/inventory/RecipeChoice.java -@@ -233,4 +233,64 @@ public interface RecipeChoice extends Predicate, Cloneable { - return "ExactChoice{" + "choices=" + choices + '}'; - } - } -+ -+ /** -+ * Represents a choice that matches when the item predicate is fulfilled. -+ */ -+ public static class PredicateChoice implements RecipeChoice { -+ private final ItemPredicate itemPredicate; -+ -+ public PredicateChoice(ItemPredicate itemPredicate) { -+ Preconditions.checkArgument(itemPredicate != null, "itemPredicate"); -+ Preconditions.checkArgument(!itemPredicate.recipeBookExamples().isEmpty(), "Must have at least one template"); -+ this.itemPredicate = itemPredicate; -+ } -+ -+ @Override -+ public final boolean test(final ItemStack stack) { -+ return itemPredicate.test(stack); -+ } -+ -+ @Override -+ public @NotNull ItemStack getItemStack() { -+ ItemStack stack = new ItemStack(itemPredicate.recipeBookExamples().get(0)); -+ // For compat -+ if (itemPredicate.recipeBookExamples().size() > 1) { -+ stack.setDurability(Short.MAX_VALUE); -+ } -+ -+ return stack; -+ } -+ -+ @Override -+ public PredicateChoice clone() { -+ return new PredicateChoice(new ItemPredicate() { -+ @Override -+ public List recipeBookExamples() { -+ return List.copyOf(itemPredicate.recipeBookExamples()); -+ } -+ -+ @Override -+ public boolean test(final ItemStack stack) { -+ return itemPredicate.test(stack); -+ } -+ }); -+ } -+ -+ public ItemPredicate getItemPredicate() { -+ return itemPredicate; -+ } -+ -+ @Override -+ public boolean equals(final Object o) { -+ if (this == o) return true; -+ if (o == null || getClass() != o.getClass()) return false; -+ PredicateChoice that = (PredicateChoice) o; -+ return Objects.equals(itemPredicate, that.itemPredicate); -+ } -+ -+ public static interface ItemPredicate extends Predicate { -+ List recipeBookExamples(); -+ } -+ } - } diff --git a/patches/api/0469-Adding-PredicateChoice.patch b/patches/api/0469-Adding-PredicateChoice.patch new file mode 100644 index 000000000000..270a2f57d309 --- /dev/null +++ b/patches/api/0469-Adding-PredicateChoice.patch @@ -0,0 +1,94 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: derverdox +Date: Thu, 21 Mar 2024 16:54:14 +0100 +Subject: [PATCH] - Adding PredicateChoice + + +diff --git a/src/main/java/org/bukkit/inventory/PredicateChoiceImpl.java b/src/main/java/org/bukkit/inventory/PredicateChoiceImpl.java +new file mode 100644 +index 0000000000000000000000000000000000000000..927917f9a875595e2ff0626cbda03a08b7e6e166 +--- /dev/null ++++ b/src/main/java/org/bukkit/inventory/PredicateChoiceImpl.java +@@ -0,0 +1,38 @@ ++package org.bukkit.inventory; ++ ++import org.jetbrains.annotations.NotNull; ++import java.util.List; ++import java.util.function.Predicate; ++ ++/** ++ * Package private implementation for {@link org.bukkit.inventory.RecipeChoice.PredicateChoice} ++ * @param predicate - The Item predicate ++ * @param choices - The recipe book choices ++ */ ++record PredicateChoiceImpl(Predicate predicate, List choices) implements RecipeChoice.PredicateChoice { ++ @Override ++ public @NotNull ItemStack getItemStack() { ++ ItemStack stack = new ItemStack(choices.get(0)); ++ // For compat ++ if (choices.size() > 1) { ++ stack.setDurability(Short.MAX_VALUE); ++ return stack; ++ } ++ return stack; ++ } ++ ++ @Override ++ public @NotNull RecipeChoice clone() { ++ return new PredicateChoiceImpl(predicate, recipeBookExamples()); ++ } ++ ++ @Override ++ public boolean test(@NotNull final ItemStack itemStack) { ++ return predicate.test(itemStack); ++ } ++ ++ @Override ++ public List recipeBookExamples() { ++ return List.copyOf(choices); ++ } ++} +diff --git a/src/main/java/org/bukkit/inventory/RecipeChoice.java b/src/main/java/org/bukkit/inventory/RecipeChoice.java +index db8bcc66bdc4bedfffb4705db6338eda4c0ad29a..e331040030c484ad63444a0ee4e42790d639115e 100644 +--- a/src/main/java/org/bukkit/inventory/RecipeChoice.java ++++ b/src/main/java/org/bukkit/inventory/RecipeChoice.java +@@ -233,4 +233,39 @@ public interface RecipeChoice extends Predicate, Cloneable { + return "ExactChoice{" + "choices=" + choices + '}'; + } + } ++ // Paper start - Adding PredicateChoice ++ /** ++ * Represents a choice that matches when the item predicate is fulfilled. ++ */ ++ ++ interface PredicateChoice extends RecipeChoice { ++ static PredicateChoice create(@NotNull Predicate predicate, ItemStack... recipeBookExamples){ ++ Objects.requireNonNull(predicate, "The item predicate cannot be null!"); ++ Objects.requireNonNull(predicate, "The mustHaveRecipeBookExample cannot be null!"); ++ if(recipeBookExamples.length == 0) ++ throw new IllegalArgumentException("Please provide at least one recipe book example item!"); ++ return new PredicateChoiceImpl(predicate, List.of(recipeBookExamples)); ++ } ++ ++ static PredicateChoice create(@NotNull Predicate predicate, java.util.Collection recipeBookExamples){ ++ Objects.requireNonNull(predicate, "The item predicate cannot be null!"); ++ Objects.requireNonNull(predicate, "The mustHaveRecipeBookExample cannot be null!"); ++ if(recipeBookExamples.isEmpty()) ++ throw new IllegalArgumentException("Please provide at least one recipe book example item!"); ++ return new PredicateChoiceImpl(predicate, List.copyOf(recipeBookExamples)); ++ } ++ ++ /** ++ * Returns the Item predicate ++ * @return - The item predicate ++ */ ++ Predicate predicate(); ++ ++ /** ++ * ++ * @return ++ */ ++ List recipeBookExamples(); ++ } ++ // Paper end - Adding PredicateChoice + } diff --git a/patches/server/1056-Adding-PredicateChoice-to-Paper-API.patch b/patches/server/1056-Adding-PredicateChoice.patch similarity index 58% rename from patches/server/1056-Adding-PredicateChoice-to-Paper-API.patch rename to patches/server/1056-Adding-PredicateChoice.patch index c84359246a0f..de1dce9621cc 100644 --- a/patches/server/1056-Adding-PredicateChoice-to-Paper-API.patch +++ b/patches/server/1056-Adding-PredicateChoice.patch @@ -1,11 +1,11 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: derverdox -Date: Mon, 4 Dec 2023 01:54:37 +0100 -Subject: [PATCH] Adding PredicateChoice to Paper API +Date: Thu, 21 Mar 2024 16:54:14 +0100 +Subject: [PATCH] - Adding PredicateChoice diff --git a/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java b/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java -index 2a2f8327a5bd3983a3a13fd663beb98906f27312..a4b4d8fe73a5d626906ed859abbdee9591263645 100644 +index 2a2f8327a5bd3983a3a13fd663beb98906f27312..684cbfa8d93dc9af8c3442c0ba4ac81f27899af1 100644 --- a/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java +++ b/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java @@ -6,25 +6,28 @@ import net.minecraft.world.item.crafting.Recipe; @@ -13,137 +13,107 @@ index 2a2f8327a5bd3983a3a13fd663beb98906f27312..a4b4d8fe73a5d626906ed859abbdee95 public abstract class RecipeBookExactChoiceRecipe implements Recipe { - private boolean hasExactIngredients; -+ private boolean hasSpecialIngredients; ++ private boolean hasSpecialIngredients; // Paper - Adding PredicateChoice protected final void checkExactIngredients() { // skip any special recipes if (this.isSpecial()) { - this.hasExactIngredients = false; -+ this.hasSpecialIngredients = false; ++ this.hasSpecialIngredients = false; // Paper - Adding PredicateChoice return; } for (final Ingredient ingredient : this.getIngredients()) { - if (!ingredient.isEmpty() && ingredient.exact) { +- if (!ingredient.isEmpty() && ingredient.exact) { - this.hasExactIngredients = true; ++ // Paper start - Adding PredicateChoice ++ if (!ingredient.isEmpty() && (ingredient.exact || ingredient.itemPredicate != null)) { + this.hasSpecialIngredients = true; -+ return; -+ } else if (!ingredient.isEmpty() && ingredient.getItemPredicate() != null) { -+ this.hasSpecialIngredients = true; ++ // Paper end - Adding PredicateChoice return; } } - this.hasExactIngredients = false; -+ this.hasSpecialIngredients = false; ++ this.hasSpecialIngredients = false; // Paper - Adding PredicateChoice } @Override - public final boolean hasExactIngredients() { - return this.hasExactIngredients; +- } ++ // Paper start - Adding PredicateChoice + public final boolean hasSpecialIngredients() { + return this.hasSpecialIngredients; - } ++ } // Paper end - Adding PredicateChoice } diff --git a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java -index 63db0b843c5bd11f979e613ba6cfac9d9da956bb..63a161ca3c228263d71d2da86b92970a13d28181 100644 +index 63db0b843c5bd11f979e613ba6cfac9d9da956bb..7c4780ab634a06ca3a362356443079f5d70558aa 100644 --- a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java +++ b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java -@@ -7,7 +7,9 @@ import it.unimi.dsi.fastutil.ints.IntComparators; - import it.unimi.dsi.fastutil.ints.IntList; - import it.unimi.dsi.fastutil.objects.Object2IntMap; - import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap; -+import java.util.ArrayList; - import java.util.IdentityHashMap; -+import java.util.List; - import java.util.Map; - import java.util.concurrent.atomic.AtomicInteger; - import net.minecraft.core.registries.BuiltInRegistries; -@@ -18,12 +20,12 @@ 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); +@@ -24,6 +24,7 @@ public final class StackedContentsExtraMap { private final Int2ObjectMap idToExactChoice = new Int2ObjectOpenHashMap<>(); private final StackedContents contents; public final Map extraStackingIds = new IdentityHashMap<>(); -+ public final List predicateChoices = new ArrayList<>(); // Adding PredicateChoice ++ public final java.util.List predicateChoices = new java.util.ArrayList<>(); // Paper - Adding PredicateChoice public StackedContentsExtraMap(final StackedContents contents, final Recipe recipe) { this.exactChoiceIds.defaultReturnValue(-1); -@@ -32,7 +34,7 @@ public final class StackedContentsExtraMap { +@@ -32,7 +33,7 @@ public final class StackedContentsExtraMap { } private void initialize(final Recipe recipe) { - if (recipe.hasExactIngredients()) { -+ if (recipe.hasSpecialIngredients()) { ++ if (recipe.hasSpecialIngredients()) { // Paper - Adding PredicateChoice for (final Ingredient ingredient : recipe.getIngredients()) { if (!ingredient.isEmpty() && ingredient.exact) { final net.minecraft.world.item.ItemStack[] items = ingredient.getItems(); -@@ -47,6 +49,11 @@ public final class StackedContentsExtraMap { +@@ -47,6 +48,12 @@ public final class StackedContentsExtraMap { idList.sort(IntComparators.NATURAL_COMPARATOR); this.extraStackingIds.put(ingredient, idList); } -+ // Adding PredicateChoice -+ else if(!ingredient.isEmpty() && ingredient.getItemPredicate() != null){ ++ // Paper start - Adding PredicateChoice ++ else if (!ingredient.isEmpty() && ingredient.itemPredicate != null) { + this.predicateChoices.add(ingredient); + this.extraStackingIds.put(ingredient, new IntArrayList()); // fill id list when accounting stacks + } ++ // Paper end - Adding PredicateChoice } } } -@@ -67,6 +74,16 @@ public final class StackedContentsExtraMap { +@@ -67,6 +74,18 @@ public final class StackedContentsExtraMap { } public boolean accountStack(final ItemStack stack, final int count) { ++ // Paper start - Adding PredicateChoice + // We are adding items that pass the predicate test. -+ for(final Ingredient predicateChoice : this.predicateChoices){ -+ if(predicateChoice.test(stack)){ -+ boolean isStackTypeRegistered = this.exactChoiceIds.getInt(stack) > -1; ++ for (final Ingredient predicateChoice : this.predicateChoices) { ++ if (predicateChoice.itemPredicate != null && predicateChoice.itemPredicate.test(stack.getBukkitStack())) { + final int id = this.registerExact(stack); + // We only want to add the stacking id to the list one time -+ if(!isStackTypeRegistered) ++ if (id != -1) { + this.extraStackingIds.get(predicateChoice).add(id); ++ } + } + } ++ // Paper end - Adding PredicateChoice if (!this.exactChoiceIds.isEmpty()) { final int id = this.exactChoiceIds.getInt(stack); if (id >= 0) { diff --git a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -index 06fe5b056d78d42cdf78437eeabe1786d596b7f8..d98201d34c5863a9c1588d64aa0e72b1529a980c 100644 +index 7c29750e534eae4266bf7a63c50e3827401d6569..6159c071cd6f104483df878b0968b5e6a17a69aa 100644 --- a/src/main/java/net/minecraft/world/item/crafting/Ingredient.java +++ b/src/main/java/net/minecraft/world/item/crafting/Ingredient.java -@@ -29,11 +29,14 @@ import net.minecraft.world.entity.player.StackedContents; - import net.minecraft.world.item.Item; - import net.minecraft.world.item.ItemStack; - import net.minecraft.world.level.ItemLike; -+import org.bukkit.craftbukkit.inventory.CraftItemStack; -+import org.bukkit.inventory.RecipeChoice; - - public final class Ingredient implements Predicate { - - public static final Ingredient EMPTY = new Ingredient(Stream.empty()); - private final Ingredient.Value[] values; -+ @Nullable private RecipeChoice.PredicateChoice.ItemPredicate itemPredicate; // Paper - @Nullable - public ItemStack[] itemStacks; +@@ -36,9 +36,9 @@ public final class Ingredient implements Predicate { @Nullable -@@ -41,7 +44,13 @@ public final class Ingredient implements Predicate { + private IntList stackingIds; public boolean exact; // CraftBukkit ++ @Nullable public Predicate itemPredicate; // Paper - Adding PredicateChoice public static final Codec CODEC = Ingredient.codec(true); public static final Codec CODEC_NONEMPTY = Ingredient.codec(false); - -+ // Paper start - Adding PredicateChoice -+ public Ingredient(RecipeChoice.PredicateChoice.ItemPredicate itemPredicate) { -+ List bukkitChoices = itemPredicate.recipeBookExamples(); -+ this.itemPredicate = itemPredicate; -+ this.values = bukkitChoices.stream().map(CraftItemStack::asNMSCopy).map(ItemValue::new).toArray(Value[]::new); -+ } -+ // Paper end - Adding PredicateChoice public Ingredient(Stream entries) { this.values = (Ingredient.Value[]) entries.toArray((i) -> { return new Ingredient.Value[i]; -@@ -70,6 +79,11 @@ public final class Ingredient implements Predicate { +@@ -67,6 +67,11 @@ public final class Ingredient implements Predicate { } else if (this.isEmpty()) { return itemstack.isEmpty(); } else { @@ -155,35 +125,24 @@ index 06fe5b056d78d42cdf78437eeabe1786d596b7f8..d98201d34c5863a9c1588d64aa0e72b1 ItemStack[] aitemstack = this.getItems(); int i = aitemstack.length; -@@ -269,4 +283,10 @@ public final class Ingredient implements Predicate { - return Collections.singleton(this.item); - } - } -+ // Paper start - Adding PredicateChoice -+ @Nullable -+ public RecipeChoice.PredicateChoice.ItemPredicate getItemPredicate() { -+ return itemPredicate; -+ } -+ // Paper end - Adding PredicateChoice - } diff --git a/src/main/java/net/minecraft/world/item/crafting/Recipe.java b/src/main/java/net/minecraft/world/item/crafting/Recipe.java -index 80387cd1bee2bd4c024073cee74222828f9f2c17..6fbbf255fe061943b72324d1484c7b9e885f8535 100644 +index e2d6c8ed586ef429cc712139e501df696ed10f6e..840ae57544e4f5c6e8a1bddd8cd1a09efb06625e 100644 --- a/src/main/java/net/minecraft/world/item/crafting/Recipe.java +++ b/src/main/java/net/minecraft/world/item/crafting/Recipe.java -@@ -67,7 +67,7 @@ public interface Recipe { +@@ -71,7 +71,7 @@ public interface Recipe { org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id); // CraftBukkit // Paper start - improved exact choice recipes - default boolean hasExactIngredients() { -+ default boolean hasSpecialIngredients() { ++ default boolean hasSpecialIngredients() { // Paper start - Adding PredicateChoice return false; } // Paper end diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java -index 13d25d118eb4d3ef35a4cdfb9bbde9ed83f6c04b..8b308da84b59c04e0b80d419d4eea602f01abcbf 100644 +index 6ba29875d78ede4aa7978ff689e588f7fed11528..b2bba76d8ebc0bd8a4404c19c8dc93cbb75e3142 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java -@@ -30,7 +30,13 @@ public interface CraftRecipe extends Recipe { +@@ -29,7 +29,15 @@ public interface CraftRecipe extends Recipe { } else if (bukkit instanceof RecipeChoice.ExactChoice) { stack = new Ingredient(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> new net.minecraft.world.item.crafting.Ingredient.ItemValue(CraftItemStack.asNMSCopy(mat)))); stack.exact = true; @@ -191,21 +150,28 @@ index 13d25d118eb4d3ef35a4cdfb9bbde9ed83f6c04b..8b308da84b59c04e0b80d419d4eea602 + } + // Paper start - Adding PredicateChoice + else if(bukkit instanceof RecipeChoice.PredicateChoice predicateChoice){ -+ stack = new Ingredient(predicateChoice.getItemPredicate()); ++ List bukkitChoices = predicateChoice.recipeBookExamples(); ++ stack = new Ingredient(bukkitChoices.stream().map(CraftItemStack::asNMSCopy).map(Ingredient.ItemValue::new)); ++ stack.itemPredicate = predicateChoice.predicate(); + } + // Paper end - Adding PredicateChoice + else { throw new IllegalArgumentException("Unknown recipe stack instance " + bukkit); } -@@ -48,7 +54,10 @@ public interface CraftRecipe extends Recipe { +@@ -47,7 +55,15 @@ public interface CraftRecipe extends Recipe { if (list.itemStacks.length == 0) { return null; } - + // Paper start - Adding PredicateChoice -+ if(list.getItemPredicate() != null) -+ return new RecipeChoice.PredicateChoice(list.getItemPredicate()); ++ if(list.itemPredicate != null) { ++ List choices = new ArrayList<>(list.itemStacks.length); ++ for (net.minecraft.world.item.ItemStack i : list.itemStacks) { ++ choices.add(CraftItemStack.asBukkitCopy(i)); ++ } ++ return RecipeChoice.PredicateChoice.create(list.itemPredicate, choices); ++ } + // Paper end - Adding PredicateChoice if (list.exact) { List choices = new ArrayList<>(list.itemStacks.length);