From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 12 May 2024 15:49:36 -0700
Subject: [PATCH] Fix issues with Recipe API


diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
+++ b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
@@ -0,0 +0,0 @@ public class ShapedRecipe implements CraftingRecipe {
         char c = 'a';
         for (Optional<Ingredient> list : this.pattern.ingredients()) {
             RecipeChoice choice = CraftRecipe.toBukkit(list);
-            if (choice != null) {
+            if (choice != RecipeChoice.empty()) { // Paper
                 recipe.setIngredient(c, choice);
             }
 
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
@@ -0,0 +0,0 @@ public interface CraftRecipe extends Recipe {
             stack = Ingredient.of(((RecipeChoice.MaterialChoice) bukkit).getChoices().stream().map((mat) -> CraftItemType.bukkitToMinecraft(mat)));
         } else if (bukkit instanceof RecipeChoice.ExactChoice) {
             stack = Ingredient.ofStacks(((RecipeChoice.ExactChoice) bukkit).getChoices().stream().map((mat) -> CraftItemStack.asNMSCopy(mat)).toList());
+            // Paper start - support "empty" choices
+        } else if (bukkit == RecipeChoice.empty()) {
+            stack = Ingredient.of();
+            // Paper end
         } else {
             throw new IllegalArgumentException("Unknown recipe stack instance " + bukkit);
         }
@@ -0,0 +0,0 @@ public interface CraftRecipe extends Recipe {
     }
 
     public static RecipeChoice toBukkit(Optional<Ingredient> list) {
-        return list.map(CraftRecipe::toBukkit).orElse(null);
+        return list.map(CraftRecipe::toBukkit).orElse(RecipeChoice.empty()); // Paper - fix issue with recipe API
     }
 
     public static RecipeChoice toBukkit(Ingredient list) {
         List<Holder<Item>> items = list.items();
 
         if (items.isEmpty()) {
-            return null;
+            return RecipeChoice.empty(); // Paper - null breaks API contracts
         }
 
         if (list.isExact()) {
diff --git a/src/test/java/io/papermc/paper/inventory/recipe/TestRecipeChoice.java b/src/test/java/io/papermc/paper/inventory/recipe/TestRecipeChoice.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/test/java/io/papermc/paper/inventory/recipe/TestRecipeChoice.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.inventory.recipe;
+
+import java.util.Iterator;
+import org.bukkit.Bukkit;
+import org.bukkit.inventory.Recipe;
+import org.bukkit.support.environment.AllFeatures;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+@AllFeatures
+class TestRecipeChoice {
+
+    @Test
+    void testRecipeChoices() {
+        final Iterator<Recipe> iter = Bukkit.recipeIterator();
+        boolean foundRecipes = false;
+        while (iter.hasNext()) {
+            foundRecipes = true;
+            assertDoesNotThrow(iter::next, "Failed to convert a recipe to Bukkit recipe!");
+        }
+        assertTrue(foundRecipes, "No recipes found!");
+    }
+}
diff --git a/src/test/java/org/bukkit/support/DummyServerHelper.java b/src/test/java/org/bukkit/support/DummyServerHelper.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/test/java/org/bukkit/support/DummyServerHelper.java
+++ b/src/test/java/org/bukkit/support/DummyServerHelper.java
@@ -0,0 +0,0 @@ public final class DummyServerHelper {
         // Paper end - testing additions
 
         io.papermc.paper.configuration.GlobalConfigTestingBase.setupGlobalConfigForTest(); // Paper - configuration files - setup global configuration test base
+
+        // Paper start - add test for recipe conversion
+        when(instance.recipeIterator()).thenAnswer(ignored ->
+            com.google.common.collect.Iterators.transform(
+                RegistryHelper.getDataPack().getRecipeManager().recipes.byType.entries().iterator(),
+                input -> input.getValue().toBukkitRecipe()
+            )
+        );
+        // Paper end - add test for recipe conversion
         return instance;
     }
 }
