Skip to content

Commit

Permalink
Add RecipeChoice.ItemTypeChoice
Browse files Browse the repository at this point in the history
  • Loading branch information
Machine-Maker committed Jan 31, 2025
1 parent d4a9578 commit 350514c
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.bukkit.inventory;

import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.keys.ItemTypeKeys;
import io.papermc.paper.registry.set.RegistryKeySet;
import org.jspecify.annotations.NullMarked;

@NullMarked
record ItemTypeRecipeChoiceImpl(RegistryKeySet<ItemType> itemTypes) implements RecipeChoice.ItemTypeChoice {

ItemTypeRecipeChoiceImpl {
Preconditions.checkArgument(!itemTypes.isEmpty(), "RecipeChoice.ItemTypeChoice cannot be empty");
Preconditions.checkArgument(!(itemTypes.contains(ItemTypeKeys.AIR)), "RecipeChoice.ItemTypeChoice cannot contain minecraft:air");
}

@Override
public ItemStack getItemStack() {
throw new UnsupportedOperationException("ItemTypeRecipeChoice does not support this");
}

@Override
public RecipeChoice clone() {
return new ItemTypeRecipeChoiceImpl(this.itemTypes);
}

@Override
public boolean test(final ItemStack itemStack) {
return this.itemTypes.contains(RegistryKey.ITEM.typedKey(itemStack.getType().key()));
}

@Override
public RecipeChoice validate(final boolean allowEmptyRecipes) {
Preconditions.checkArgument(!(this.itemTypes.contains(ItemTypeKeys.AIR)), "RecipeChoice.ItemTypeChoice cannot contain minecraft:air");
return this;
}
}
83 changes: 56 additions & 27 deletions paper-api/src/main/java/org/bukkit/inventory/RecipeChoice.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package org.bukkit.inventory;

import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.TypedKey;
import io.papermc.paper.registry.set.RegistryKeySet;
import io.papermc.paper.registry.tag.TagKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand All @@ -11,7 +15,9 @@
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.material.MaterialData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jspecify.annotations.NullMarked;

/**
* Represents a potential item match within a recipe. All choices within a
Expand All @@ -20,20 +26,34 @@
*
* <b>This class is not legal for implementation by plugins!</b>
*/
@NullMarked
@ApiStatus.NonExtendable
public interface RecipeChoice extends Predicate<ItemStack>, Cloneable {

// Paper start - add "empty" choice
/**
* An "empty" recipe choice. Only valid as a recipe choice in
* specific places. Check the javadocs of a method before using it
* to be sure it's valid for that recipe and ingredient type.
*
* @return the empty recipe choice
*/
static @NotNull RecipeChoice empty() {
static RecipeChoice empty() {
return EmptyRecipeChoice.INSTANCE;
}
// Paper end

/**
* Creates a new recipe choice based on a {@link RegistryKeySet} of item types.
* Can either be created via {@link RegistryKeySet#keySet(RegistryKey, TypedKey[])}
* or obtained from {@link org.bukkit.Registry#getTag(TagKey)}.
*
* @param itemTypes the item types to match
* @return a new recipe choice
*/
@Contract(pure = true, value = "_ -> new")
@ApiStatus.Experimental
static ItemTypeChoice itemType(final RegistryKeySet<ItemType> itemTypes) {
return new ItemTypeRecipeChoiceImpl(itemTypes);
}

/**
* Gets a single item stack representative of this stack choice.
Expand All @@ -42,34 +62,32 @@ public interface RecipeChoice extends Predicate<ItemStack>, Cloneable {
* @deprecated for compatibility only
*/
@Deprecated(since = "1.13.1")
@NotNull
ItemStack getItemStack();

@NotNull
RecipeChoice clone();

@Override
boolean test(@NotNull ItemStack itemStack);
boolean test(ItemStack itemStack);

// Paper start - check valid ingredients
@org.jetbrains.annotations.ApiStatus.Internal
default @NotNull RecipeChoice validate(final boolean allowEmptyRecipes) {
default RecipeChoice validate(final boolean allowEmptyRecipes) {
return this;
}
// Paper end - check valid ingredients

/**
* Represents a choice of multiple matching Materials.
*/
public static class MaterialChoice implements RecipeChoice {
final class MaterialChoice implements RecipeChoice {

private List<Material> choices;

public MaterialChoice(@NotNull Material choice) {
public MaterialChoice(Material choice) {
this(Arrays.asList(choice));
}

public MaterialChoice(@NotNull Material... choices) {
public MaterialChoice(Material... choices) {
this(Arrays.asList(choices));
}

Expand All @@ -79,11 +97,11 @@ public MaterialChoice(@NotNull Material... choices) {
*
* @param choices the tag
*/
public MaterialChoice(@NotNull Tag<Material> choices) {
public MaterialChoice(Tag<Material> choices) {
this(new ArrayList<>(java.util.Objects.requireNonNull(choices, "Cannot create a material choice with null tag").getValues())); // Paper - delegate to list ctor to make sure all checks are called
}

public MaterialChoice(@NotNull List<Material> choices) {
public MaterialChoice(List<Material> choices) {
Preconditions.checkArgument(choices != null, "choices");
Preconditions.checkArgument(!choices.isEmpty(), "Must have at least one choice");

Expand All @@ -103,7 +121,7 @@ public MaterialChoice(@NotNull List<Material> choices) {
}

@Override
public boolean test(@NotNull ItemStack t) {
public boolean test(ItemStack t) {
for (Material match : choices) {
if (t.getType() == match) {
return true;
Expand All @@ -113,7 +131,6 @@ public boolean test(@NotNull ItemStack t) {
return false;
}

@NotNull
@Override
public ItemStack getItemStack() {
ItemStack stack = new ItemStack(choices.get(0));
Expand All @@ -126,12 +143,10 @@ public ItemStack getItemStack() {
return stack;
}

@NotNull
public List<Material> getChoices() {
return Collections.unmodifiableList(choices);
}

@NotNull
@Override
public MaterialChoice clone() {
try {
Expand Down Expand Up @@ -175,7 +190,7 @@ public String toString() {

// Paper start - check valid ingredients
@Override
public @NotNull RecipeChoice validate(final boolean allowEmptyRecipes) {
public RecipeChoice validate(final boolean allowEmptyRecipes) {
if (this.choices.stream().anyMatch(Material::isAir)) {
throw new IllegalArgumentException("RecipeChoice.MaterialChoice cannot contain air");
}
Expand All @@ -188,19 +203,19 @@ public String toString() {
* Represents a choice that will be valid only if one of the stacks is
* exactly matched (aside from stack size).
*/
public static class ExactChoice implements RecipeChoice {
final class ExactChoice implements RecipeChoice {

private List<ItemStack> choices;

public ExactChoice(@NotNull ItemStack stack) {
public ExactChoice(ItemStack stack) {
this(Arrays.asList(stack));
}

public ExactChoice(@NotNull ItemStack... stacks) {
public ExactChoice(ItemStack... stacks) {
this(Arrays.asList(stacks));
}

public ExactChoice(@NotNull List<ItemStack> choices) {
public ExactChoice(List<ItemStack> choices) {
Preconditions.checkArgument(choices != null, "choices");
Preconditions.checkArgument(!choices.isEmpty(), "Must have at least one choice");
for (ItemStack choice : choices) {
Expand All @@ -211,18 +226,15 @@ public ExactChoice(@NotNull List<ItemStack> choices) {
this.choices = new ArrayList<>(choices);
}

@NotNull
@Override
public ItemStack getItemStack() {
return choices.get(0).clone();
}

@NotNull
public List<ItemStack> getChoices() {
return Collections.unmodifiableList(choices);
}

@NotNull
@Override
public ExactChoice clone() {
try {
Expand All @@ -240,7 +252,7 @@ public ExactChoice clone() {
}

@Override
public boolean test(@NotNull ItemStack t) {
public boolean test(ItemStack t) {
for (ItemStack match : choices) {
if (t.isSimilar(match)) {
return true;
Expand Down Expand Up @@ -282,12 +294,29 @@ public String toString() {

// Paper start - check valid ingredients
@Override
public @NotNull RecipeChoice validate(final boolean allowEmptyRecipes) {
public RecipeChoice validate(final boolean allowEmptyRecipes) {
if (this.choices.stream().anyMatch(s -> s.getType().isAir())) {
throw new IllegalArgumentException("RecipeChoice.ExactChoice cannot contain air");
}
return this;
}
// Paper end - check valid ingredients
}

/**
* Represents a choice that will be valid if the {@link ItemStack#getType()}
* matches any of the item types in the set.
* @see #itemType(RegistryKeySet)
*/
@ApiStatus.Experimental
@ApiStatus.NonExtendable
interface ItemTypeChoice extends RecipeChoice {

/**
* Gets the set of item types that this choice will match.
*
* @return the set of item types
*/
RegistryKeySet<ItemType> itemTypes();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ index 6bbe2e51ef71d193e0a5d3cace2b0ad1760ce759..83ccde54c625d40dc595e000c533f60a
}

diff --git a/net/minecraft/world/item/crafting/Ingredient.java b/net/minecraft/world/item/crafting/Ingredient.java
index e43641650d66a62b5b7b58c43833ce504970ab1e..879c8fe1f20decc793cfa39e686b61d521bd76ba 100644
index ac4520afe510df9b1dd256d13dcb0768c83edb70..da95f40503f3696c8daa8fd0e947508fe43060ba 100644
--- a/net/minecraft/world/item/crafting/Ingredient.java
+++ b/net/minecraft/world/item/crafting/Ingredient.java
@@ -21,7 +21,7 @@ import net.minecraft.world.item.Items;
Expand All @@ -354,7 +354,7 @@ index e43641650d66a62b5b7b58c43833ce504970ab1e..879c8fe1f20decc793cfa39e686b61d5
.map(Ingredient::new, ingredient -> ingredient.values);
public static final StreamCodec<RegistryFriendlyByteBuf, Optional<Ingredient>> OPTIONAL_CONTENTS_STREAM_CODEC = ByteBufCodecs.holderSet(Registries.ITEM)
@@ -35,20 +35,24 @@ public final class Ingredient implements StackedContents.IngredientInfo<Holder<I
private final HolderSet<Item> values;
public final HolderSet<Item> values;
// CraftBukkit start
@javax.annotation.Nullable
- private java.util.List<ItemStack> itemStacks;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--- a/net/minecraft/core/HolderSet.java
+++ b/net/minecraft/core/HolderSet.java
@@ -166,6 +_,8 @@
private final TagKey<T> key;
@Nullable
private List<Holder<T>> contents;
+ @Nullable
+ private Set<io.papermc.paper.registry.TypedKey<?>> typedKeys; // Paper - cache typed key set for constant contains calls

Named(HolderOwner<T> owner, TagKey<T> key) {
this.owner = owner;
@@ -174,6 +_,7 @@

void bind(List<Holder<T>> contents) {
this.contents = List.copyOf(contents);
+ this.typedKeys = null; // Paper - reset if tag is re-bound
}

public TagKey<T> key() {
@@ -218,5 +_,15 @@
public boolean canSerializeIn(HolderOwner<T> owner) {
return this.owner.canSerializeIn(owner);
}
+
+ // Paper start - cache typed key set for constant contains calls
+ public boolean contains(io.papermc.paper.registry.TypedKey<?> key) {
+ if (this.typedKeys == null) {
+ this.typedKeys = this.contents().stream().map(h -> io.papermc.paper.registry.PaperRegistries.fromNms(h.unwrapKey().orElseThrow())).collect(java.util.stream.Collectors.toUnmodifiableSet());
+ }
+
+ return this.typedKeys.contains(key);
+ }
+ // Paper end
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
--- a/net/minecraft/world/item/crafting/Ingredient.java
+++ b/net/minecraft/world/item/crafting/Ingredient.java
@@ -33,6 +_,25 @@
@@ -32,7 +_,26 @@
public static final Codec<HolderSet<Item>> NON_AIR_HOLDER_SET_CODEC = HolderSetCodec.create(Registries.ITEM, Item.CODEC, false);
public static final Codec<Ingredient> CODEC = ExtraCodecs.nonEmptyHolderSet(NON_AIR_HOLDER_SET_CODEC)
.xmap(Ingredient::new, ingredient -> ingredient.values);
private final HolderSet<Item> values;
- private final HolderSet<Item> values;
+ public final HolderSet<Item> values;
+ // CraftBukkit start
+ @javax.annotation.Nullable
+ private java.util.List<ItemStack> itemStacks;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ public RegistryKey<T> registryKey() {

@Override
public boolean contains(final TypedKey<T> valueKey) {
return Iterables.any(this.namedSet, h -> {
return PaperRegistries.fromNms(((Holder.Reference<?>) h).key()).equals(valueKey);
});
return this.namedSet.contains(valueKey);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package org.bukkit.craftbukkit.inventory;

import com.google.common.base.Preconditions;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.util.Conversions;
import io.papermc.paper.registry.set.PaperRegistrySets;
import io.papermc.paper.registry.set.RegistryKeySet;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
Expand All @@ -9,6 +13,7 @@
import net.minecraft.world.item.crafting.Ingredient;
import org.bukkit.NamespacedKey;
import org.bukkit.craftbukkit.util.CraftNamespacedKey;
import org.bukkit.inventory.ItemType;
import org.bukkit.inventory.Recipe;
import org.bukkit.inventory.RecipeChoice;
import org.bukkit.inventory.recipe.CookingBookCategory;
Expand All @@ -32,6 +37,8 @@ static Ingredient toIngredient(RecipeChoice bukkit, boolean requireNotEmpty) {

if (bukkit == null) {
stack = Ingredient.of();
} else if (bukkit instanceof final RecipeChoice.ItemTypeChoice itemTypeChoice) {
stack = Ingredient.of(PaperRegistrySets.convertToNms(Registries.ITEM, Conversions.global().lookup(), itemTypeChoice.itemTypes()));
} else if (bukkit instanceof RecipeChoice.MaterialChoice) {
stack = Ingredient.of(((RecipeChoice.MaterialChoice) bukkit).getChoices().stream().map((mat) -> CraftItemType.bukkitToMinecraft(mat)));
} else if (bukkit instanceof RecipeChoice.ExactChoice) {
Expand Down Expand Up @@ -70,6 +77,9 @@ public static RecipeChoice toBukkit(Ingredient list) {
}

return new RecipeChoice.ExactChoice(choices);
// } else { // TODO change over to this when it's not experimental
// RegistryKeySet<ItemType> itemTypes = PaperRegistrySets.convertToApi(RegistryKey.ITEM, list.values);
// return RecipeChoice.itemType(itemTypes);
} else {
List<org.bukkit.Material> choices = list.items().map((i) -> CraftItemType.minecraftToBukkit(i.value())).toList();

Expand Down

0 comments on commit 350514c

Please sign in to comment.