diff --git a/fabric-api-base/src/testmodClient/java/net/fabricmc/fabric/test/base/client/FabricClientTestHelper.java b/fabric-api-base/src/testmodClient/java/net/fabricmc/fabric/test/base/client/FabricClientTestHelper.java
index 5f74a3f4a..39485c96c 100644
--- a/fabric-api-base/src/testmodClient/java/net/fabricmc/fabric/test/base/client/FabricClientTestHelper.java
+++ b/fabric-api-base/src/testmodClient/java/net/fabricmc/fabric/test/base/client/FabricClientTestHelper.java
@@ -143,7 +143,7 @@ public final class FabricClientTestHelper {
 
 	public static void enableDebugHud() {
 		submitAndWait(client -> {
-			client.inGameHud.method_53531().method_53539();
+			client.inGameHud.getDebugHud().toggleDebugHud();
 			return null;
 		});
 	}
diff --git a/fabric-api-lookup-api-v1/src/testmod/java/net/fabricmc/fabric/test/lookup/ChuteBlock.java b/fabric-api-lookup-api-v1/src/testmod/java/net/fabricmc/fabric/test/lookup/ChuteBlock.java
index b9fdd3718..c1c1b5b19 100644
--- a/fabric-api-lookup-api-v1/src/testmod/java/net/fabricmc/fabric/test/lookup/ChuteBlock.java
+++ b/fabric-api-lookup-api-v1/src/testmod/java/net/fabricmc/fabric/test/lookup/ChuteBlock.java
@@ -40,6 +40,6 @@ public class ChuteBlock extends BlockWithEntity {
 	@Nullable
 	@Override
 	public <T extends BlockEntity> BlockEntityTicker<T> getTicker(World world, BlockState state, BlockEntityType<T> type) {
-		return world.isClient ? null : checkType(type, FabricApiLookupTest.CHUTE_BLOCK_ENTITY_TYPE, ChuteBlockEntity::serverTick);
+		return world.isClient ? null : validateTicker(type, FabricApiLookupTest.CHUTE_BLOCK_ENTITY_TYPE, ChuteBlockEntity::serverTick);
 	}
 }
diff --git a/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricAdvancementProvider.java b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricAdvancementProvider.java
index 52030b804..56e028bdc 100644
--- a/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricAdvancementProvider.java
+++ b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricAdvancementProvider.java
@@ -28,6 +28,7 @@ import com.google.common.collect.Sets;
 import com.google.gson.JsonObject;
 
 import net.minecraft.advancement.Advancement;
+import net.minecraft.class_8779;
 import net.minecraft.data.DataOutput;
 import net.minecraft.data.DataProvider;
 import net.minecraft.data.DataWriter;
@@ -57,12 +58,12 @@ public abstract class FabricAdvancementProvider implements DataProvider {
 	 *
 	 * <p>Use {@link Advancement.Builder#build(Consumer, String)} to help build advancements.
 	 */
-	public abstract void generateAdvancement(Consumer<Advancement> consumer);
+	public abstract void generateAdvancement(Consumer<class_8779> consumer);
 
 	/**
 	 * Return a new exporter that applies the specified conditions to any advancement it receives.
 	 */
-	protected Consumer<Advancement> withConditions(Consumer<Advancement> exporter, ConditionJsonProvider... conditions) {
+	protected Consumer<class_8779> withConditions(Consumer<class_8779> exporter, ConditionJsonProvider... conditions) {
 		Preconditions.checkArgument(conditions.length > 0, "Must add at least one condition.");
 		return advancement -> {
 			FabricDataGenHelper.addConditions(advancement, conditions);
@@ -73,18 +74,18 @@ public abstract class FabricAdvancementProvider implements DataProvider {
 	@Override
 	public CompletableFuture<?> run(DataWriter writer) {
 		final Set<Identifier> identifiers = Sets.newHashSet();
-		final Set<Advancement> advancements = Sets.newHashSet();
+		final Set<class_8779> advancements = Sets.newHashSet();
 
 		generateAdvancement(advancements::add);
 
 		final List<CompletableFuture<?>> futures = new ArrayList<>();
 
-		for (Advancement advancement : advancements) {
-			if (!identifiers.add(advancement.getId())) {
-				throw new IllegalStateException("Duplicate advancement " + advancement.getId());
+		for (class_8779 advancement : advancements) {
+			if (!identifiers.add(advancement.comp_1919())) {
+				throw new IllegalStateException("Duplicate advancement " + advancement.comp_1919());
 			}
 
-			JsonObject advancementJson = advancement.createTask().toJson();
+			JsonObject advancementJson = advancement.comp_1920().method_53621();
 			ConditionJsonProvider.write(advancementJson, FabricDataGenHelper.consumeConditions(advancement));
 
 			futures.add(DataProvider.writeToPath(writer, advancementJson, getOutputPath(advancement)));
@@ -93,8 +94,8 @@ public abstract class FabricAdvancementProvider implements DataProvider {
 		return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new));
 	}
 
-	private Path getOutputPath(Advancement advancement) {
-		return pathResolver.resolveJson(advancement.getId());
+	private Path getOutputPath(class_8779 advancement) {
+		return pathResolver.resolveJson(advancement.comp_1919());
 	}
 
 	@Override
diff --git a/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricRecipeProvider.java b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricRecipeProvider.java
index 4a1dd5881..41d871fbf 100644
--- a/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricRecipeProvider.java
+++ b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricRecipeProvider.java
@@ -20,14 +20,17 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Sets;
 import com.google.gson.JsonObject;
 
+import net.minecraft.advancement.Advancement;
+import net.minecraft.class_8779;
+import net.minecraft.class_8790;
 import net.minecraft.data.DataProvider;
 import net.minecraft.data.DataWriter;
+import net.minecraft.data.server.recipe.CraftingRecipeJsonBuilder;
 import net.minecraft.data.server.recipe.RecipeJsonProvider;
 import net.minecraft.data.server.recipe.RecipeProvider;
 import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
@@ -56,16 +59,24 @@ public abstract class FabricRecipeProvider extends RecipeProvider {
 	 * Implement this method and then use the range of methods in {@link RecipeProvider} or from one of the recipe json factories such as {@link ShapedRecipeJsonBuilder} or {@link ShapelessRecipeJsonBuilder}.
 	 */
 	@Override
-	public abstract void generate(Consumer<RecipeJsonProvider> exporter);
+	public abstract void generate(class_8790 exporter);
 
 	/**
 	 * Return a new exporter that applies the specified conditions to any recipe json provider it receives.
 	 */
-	protected Consumer<RecipeJsonProvider> withConditions(Consumer<RecipeJsonProvider> exporter, ConditionJsonProvider... conditions) {
+	protected class_8790 withConditions(class_8790 exporter, ConditionJsonProvider... conditions) {
 		Preconditions.checkArgument(conditions.length > 0, "Must add at least one condition.");
-		return json -> {
-			FabricDataGenHelper.addConditions(json, conditions);
-			exporter.accept(json);
+		return new class_8790() {
+			@Override
+			public void method_53819(RecipeJsonProvider provider) {
+				FabricDataGenHelper.addConditions(provider, conditions);
+				exporter.method_53819(provider);
+			}
+
+			@Override
+			public Advancement.Builder method_53818() {
+				return exporter.method_53818();
+			}
 		};
 	}
 
@@ -73,23 +84,33 @@ public abstract class FabricRecipeProvider extends RecipeProvider {
 	public CompletableFuture<?> run(DataWriter writer) {
 		Set<Identifier> generatedRecipes = Sets.newHashSet();
 		List<CompletableFuture<?>> list = new ArrayList<>();
-		generate(provider -> {
-			Identifier identifier = getRecipeIdentifier(provider.getRecipeId());
+		generate(new class_8790() {
+			@Override
+			public void method_53819(RecipeJsonProvider provider) {
+				Identifier identifier = getRecipeIdentifier(provider.recipeId());
 
-			if (!generatedRecipes.add(identifier)) {
-				throw new IllegalStateException("Duplicate recipe " + identifier);
+				if (!generatedRecipes.add(identifier)) {
+					throw new IllegalStateException("Duplicate recipe " + identifier);
+				}
+
+				JsonObject recipeJson = provider.toJson();
+				ConditionJsonProvider[] conditions = FabricDataGenHelper.consumeConditions(provider);
+				ConditionJsonProvider.write(recipeJson, conditions);
+
+				list.add(DataProvider.writeToPath(writer, recipeJson, recipesPathResolver.resolveJson(identifier)));
+
+				class_8779 advancementBuilder = provider.advancementBuilder();
+
+				if (advancementBuilder != null) {
+					JsonObject advancementJson = advancementBuilder.comp_1920().method_53621();
+					ConditionJsonProvider.write(advancementJson, conditions);
+					list.add(DataProvider.writeToPath(writer, advancementJson, advancementsPathResolver.resolveJson(getRecipeIdentifier(advancementBuilder.comp_1919()))));
+				}
 			}
 
-			JsonObject recipeJson = provider.toJson();
-			ConditionJsonProvider[] conditions = FabricDataGenHelper.consumeConditions(provider);
-			ConditionJsonProvider.write(recipeJson, conditions);
-
-			list.add(DataProvider.writeToPath(writer, recipeJson, this.recipesPathResolver.resolveJson(identifier)));
-			JsonObject advancementJson = provider.toAdvancementJson();
-
-			if (advancementJson != null) {
-				ConditionJsonProvider.write(advancementJson, conditions);
-				list.add(DataProvider.writeToPath(writer, advancementJson, this.advancementsPathResolver.resolveJson(getRecipeIdentifier(provider.getAdvancementId()))));
+			@Override
+			public Advancement.Builder method_53818() {
+				return Advancement.Builder.createUntelemetered().parent(CraftingRecipeJsonBuilder.ROOT);
 			}
 		});
 		return CompletableFuture.allOf(list.toArray(CompletableFuture[]::new));
diff --git a/fabric-data-generation-api-v1/src/main/resources/fabric-data-generation-api-v1.accesswidener b/fabric-data-generation-api-v1/src/main/resources/fabric-data-generation-api-v1.accesswidener
index b632cee31..98a1d4278 100644
--- a/fabric-data-generation-api-v1/src/main/resources/fabric-data-generation-api-v1.accesswidener
+++ b/fabric-data-generation-api-v1/src/main/resources/fabric-data-generation-api-v1.accesswidener
@@ -64,75 +64,75 @@ transitive-accessible field net/minecraft/data/server/loottable/BlockLootTableGe
 
 ### Generated access wideners below
 
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	saveRecipeAdvancement	(Lnet/minecraft/data/DataWriter;Lnet/minecraft/util/Identifier;Lnet/minecraft/advancement/Advancement$Builder;)Ljava/util/concurrent/CompletableFuture;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	generate	(Ljava/util/function/Consumer;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	generateFamilies	(Ljava/util/function/Consumer;Lnet/minecraft/resource/featuretoggle/FeatureSet;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerSingleOutputShapelessRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;Ljava/lang/String;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerShapelessRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;Ljava/lang/String;I)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerSmelting	(Ljava/util/function/Consumer;Ljava/util/List;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;FILjava/lang/String;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerBlasting	(Ljava/util/function/Consumer;Ljava/util/List;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;FILjava/lang/String;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerMultipleOptions	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/RecipeSerializer;Ljava/util/List;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;FILjava/lang/String;Ljava/lang/String;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerNetheriteUpgradeRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/Item;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/Item;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerSmithingTrimRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/Item;Lnet/minecraft/util/Identifier;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offer2x2CompactingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerCompactingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;Ljava/lang/String;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerCompactingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerPlanksRecipe2	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/registry/tag/TagKey;I)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerPlanksRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/registry/tag/TagKey;I)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerBarkBlockRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerBoatRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerChestBoatRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	saveRecipeAdvancement	(Lnet/minecraft/data/DataWriter;Lnet/minecraft/class_8779;)Ljava/util/concurrent/CompletableFuture;
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	generate	(Lnet/minecraft/class_8790;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	generateFamilies	(Lnet/minecraft/class_8790;Lnet/minecraft/resource/featuretoggle/FeatureSet;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerSingleOutputShapelessRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;Ljava/lang/String;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerShapelessRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;Ljava/lang/String;I)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerSmelting	(Lnet/minecraft/class_8790;Ljava/util/List;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;FILjava/lang/String;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerBlasting	(Lnet/minecraft/class_8790;Ljava/util/List;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;FILjava/lang/String;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerMultipleOptions	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/RecipeSerializer;Ljava/util/List;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;FILjava/lang/String;Ljava/lang/String;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerNetheriteUpgradeRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/Item;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/Item;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerSmithingTrimRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/Item;Lnet/minecraft/util/Identifier;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offer2x2CompactingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerCompactingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;Ljava/lang/String;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerCompactingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerPlanksRecipe2	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/registry/tag/TagKey;I)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerPlanksRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/registry/tag/TagKey;I)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerBarkBlockRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerBoatRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerChestBoatRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	createTransmutationRecipe	(Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/CraftingRecipeJsonBuilder;
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	createDoorRecipe	(Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/CraftingRecipeJsonBuilder;
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	createFenceRecipe	(Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/CraftingRecipeJsonBuilder;
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	createFenceGateRecipe	(Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/CraftingRecipeJsonBuilder;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerPressurePlateRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerPressurePlateRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	createPressurePlateRecipe	(Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/CraftingRecipeJsonBuilder;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerSlabRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerSlabRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	createSlabRecipe	(Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/CraftingRecipeJsonBuilder;
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	createStairsRecipe	(Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/CraftingRecipeJsonBuilder;
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	createTrapdoorRecipe	(Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/CraftingRecipeJsonBuilder;
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	createSignRecipe	(Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/CraftingRecipeJsonBuilder;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerHangingSignRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerDyeableRecipes	(Ljava/util/function/Consumer;Ljava/util/List;Ljava/util/List;Ljava/lang/String;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerCarpetRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerBedRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerBannerRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerStainedGlassDyeingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerStainedGlassPaneRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerStainedGlassPaneDyeingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerTerracottaDyeingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerConcretePowderDyeingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerCandleDyeingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerWallRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerHangingSignRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerDyeableRecipes	(Lnet/minecraft/class_8790;Ljava/util/List;Ljava/util/List;Ljava/lang/String;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerCarpetRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerBedRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerBannerRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerStainedGlassDyeingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerStainedGlassPaneRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerStainedGlassPaneDyeingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerTerracottaDyeingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerConcretePowderDyeingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerCandleDyeingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerWallRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	getWallRecipe	(Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/CraftingRecipeJsonBuilder;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerPolishedStoneRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerPolishedStoneRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	createCondensingRecipe	(Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/CraftingRecipeJsonBuilder;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerCutCopperRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerCutCopperRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	createCutCopperRecipe	(Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/ShapedRecipeJsonBuilder;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerChiseledBlockRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerMosaicRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerChiseledBlockRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerMosaicRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	createChiseledBlockRecipe	(Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/Ingredient;)Lnet/minecraft/data/server/recipe/ShapedRecipeJsonBuilder;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerStonecuttingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerStonecuttingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;I)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerCrackingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerReversibleCompactingRecipes	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerReversibleCompactingRecipesWithCompactingRecipeGroup	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Ljava/lang/String;Ljava/lang/String;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerReversibleCompactingRecipesWithReverseRecipeGroup	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Ljava/lang/String;Ljava/lang/String;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerReversibleCompactingRecipes	(Ljava/util/function/Consumer;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerSmithingTemplateCopyingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/registry/tag/TagKey;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerSmithingTemplateCopyingRecipe	(Ljava/util/function/Consumer;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	generateCookingRecipes	(Ljava/util/function/Consumer;Ljava/lang/String;Lnet/minecraft/recipe/RecipeSerializer;I)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerFoodCookingRecipe	(Ljava/util/function/Consumer;Ljava/lang/String;Lnet/minecraft/recipe/RecipeSerializer;ILnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;F)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerWaxingRecipes	(Ljava/util/function/Consumer;)V
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	generateFamily	(Ljava/util/function/Consumer;Lnet/minecraft/data/family/BlockFamily;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerStonecuttingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerStonecuttingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;I)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerCrackingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerReversibleCompactingRecipes	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerReversibleCompactingRecipesWithCompactingRecipeGroup	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Ljava/lang/String;Ljava/lang/String;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerReversibleCompactingRecipesWithReverseRecipeGroup	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Ljava/lang/String;Ljava/lang/String;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerReversibleCompactingRecipes	(Lnet/minecraft/class_8790;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/recipe/book/RecipeCategory;Lnet/minecraft/item/ItemConvertible;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerSmithingTemplateCopyingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/registry/tag/TagKey;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerSmithingTemplateCopyingRecipe	(Lnet/minecraft/class_8790;Lnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	generateCookingRecipes	(Lnet/minecraft/class_8790;Ljava/lang/String;Lnet/minecraft/recipe/RecipeSerializer;I)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerFoodCookingRecipe	(Lnet/minecraft/class_8790;Ljava/lang/String;Lnet/minecraft/recipe/RecipeSerializer;ILnet/minecraft/item/ItemConvertible;Lnet/minecraft/item/ItemConvertible;F)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	offerWaxingRecipes	(Lnet/minecraft/class_8790;)V
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	generateFamily	(Lnet/minecraft/class_8790;Lnet/minecraft/data/family/BlockFamily;)V
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	getVariantRecipeInput	(Lnet/minecraft/data/family/BlockFamily;Lnet/minecraft/data/family/BlockFamily$Variant;)Lnet/minecraft/block/Block;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	requireEnteringFluid	(Lnet/minecraft/block/Block;)Lnet/minecraft/advancement/criterion/EnterBlockCriterion$Conditions;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	conditionsFromItem	(Lnet/minecraft/predicate/NumberRange$IntRange;Lnet/minecraft/item/ItemConvertible;)Lnet/minecraft/advancement/criterion/InventoryChangedCriterion$Conditions;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	conditionsFromItem	(Lnet/minecraft/item/ItemConvertible;)Lnet/minecraft/advancement/criterion/InventoryChangedCriterion$Conditions;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	conditionsFromTag	(Lnet/minecraft/registry/tag/TagKey;)Lnet/minecraft/advancement/criterion/InventoryChangedCriterion$Conditions;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	conditionsFromPredicates	([Lnet/minecraft/predicate/item/ItemPredicate$Builder;)Lnet/minecraft/advancement/criterion/InventoryChangedCriterion$Conditions;
-transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	conditionsFromItemPredicates	([Lnet/minecraft/predicate/item/ItemPredicate;)Lnet/minecraft/advancement/criterion/InventoryChangedCriterion$Conditions;
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	requireEnteringFluid	(Lnet/minecraft/block/Block;)Lnet/minecraft/advancement/AdvancementCriterion;
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	conditionsFromItem	(Lnet/minecraft/predicate/NumberRange$IntRange;Lnet/minecraft/item/ItemConvertible;)Lnet/minecraft/advancement/AdvancementCriterion;
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	conditionsFromItem	(Lnet/minecraft/item/ItemConvertible;)Lnet/minecraft/advancement/AdvancementCriterion;
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	conditionsFromTag	(Lnet/minecraft/registry/tag/TagKey;)Lnet/minecraft/advancement/AdvancementCriterion;
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	conditionsFromPredicates	([Lnet/minecraft/predicate/item/ItemPredicate$Builder;)Lnet/minecraft/advancement/AdvancementCriterion;
+transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	conditionsFromItemPredicates	([Lnet/minecraft/predicate/item/ItemPredicate;)Lnet/minecraft/advancement/AdvancementCriterion;
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	hasItem	(Lnet/minecraft/item/ItemConvertible;)Ljava/lang/String;
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	getItemPath	(Lnet/minecraft/item/ItemConvertible;)Ljava/lang/String;
 transitive-accessible	method	net/minecraft/data/server/recipe/RecipeProvider	getRecipeName	(Lnet/minecraft/item/ItemConvertible;)Ljava/lang/String;
diff --git a/fabric-data-generation-api-v1/src/testmod/java/net/fabricmc/fabric/test/datagen/DataGeneratorTestEntrypoint.java b/fabric-data-generation-api-v1/src/testmod/java/net/fabricmc/fabric/test/datagen/DataGeneratorTestEntrypoint.java
index 3254b60d7..b65569e64 100644
--- a/fabric-data-generation-api-v1/src/testmod/java/net/fabricmc/fabric/test/datagen/DataGeneratorTestEntrypoint.java
+++ b/fabric-data-generation-api-v1/src/testmod/java/net/fabricmc/fabric/test/datagen/DataGeneratorTestEntrypoint.java
@@ -41,9 +41,10 @@ import net.minecraft.advancement.Advancement;
 import net.minecraft.advancement.AdvancementFrame;
 import net.minecraft.advancement.criterion.OnKilledCriterion;
 import net.minecraft.block.Blocks;
+import net.minecraft.class_8779;
+import net.minecraft.class_8790;
 import net.minecraft.data.client.BlockStateModelGenerator;
 import net.minecraft.data.client.ItemModelGenerator;
-import net.minecraft.data.server.recipe.RecipeJsonProvider;
 import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
 import net.minecraft.entity.EntityType;
 import net.minecraft.entity.attribute.EntityAttributes;
@@ -138,7 +139,7 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
 		}
 
 		@Override
-		public void generate(Consumer<RecipeJsonProvider> exporter) {
+		public void generate(class_8790 exporter) {
 			offerPlanksRecipe2(exporter, SIMPLE_BLOCK, ItemTags.ACACIA_LOGS, 1);
 
 			ShapelessRecipeJsonBuilder.create(RecipeCategory.MISC, Items.LEATHER, 4).input(Items.ITEM_FRAME)
@@ -328,8 +329,8 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
 		}
 
 		@Override
-		public void generateAdvancement(Consumer<Advancement> consumer) {
-			Advancement root = Advancement.Builder.create()
+		public void generateAdvancement(Consumer<class_8779> consumer) {
+			class_8779 root = Advancement.Builder.create()
 					.display(
 							SIMPLE_BLOCK,
 							Text.translatable("advancements.test.root.title"),
@@ -339,7 +340,7 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
 							false, false, false)
 					.criterion("killed_something", OnKilledCriterion.Conditions.createPlayerKilledEntity())
 					.build(consumer, MOD_ID + ":test/root");
-			Advancement rootNotLoaded = Advancement.Builder.create()
+			class_8779 rootNotLoaded = Advancement.Builder.create()
 					.display(
 							SIMPLE_BLOCK,
 							Text.translatable("advancements.test.root_not_loaded.title"),
diff --git a/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/mixin/event/interaction/PlayerAdvancementTrackerMixin.java b/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/mixin/event/interaction/PlayerAdvancementTrackerMixin.java
index ddd507b97..6a6f1a759 100644
--- a/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/mixin/event/interaction/PlayerAdvancementTrackerMixin.java
+++ b/fabric-events-interaction-v0/src/main/java/net/fabricmc/fabric/mixin/event/interaction/PlayerAdvancementTrackerMixin.java
@@ -23,8 +23,8 @@ import org.spongepowered.asm.mixin.injection.Inject;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
 
-import net.minecraft.advancement.Advancement;
 import net.minecraft.advancement.PlayerAdvancementTracker;
+import net.minecraft.class_8779;
 import net.minecraft.server.network.ServerPlayerEntity;
 
 import net.fabricmc.fabric.api.entity.FakePlayer;
@@ -43,7 +43,7 @@ public class PlayerAdvancementTrackerMixin {
 	}
 
 	@Inject(method = "grantCriterion", at = @At("HEAD"), cancellable = true)
-	void preventGrantCriterion(Advancement advancement, String criterionName, CallbackInfoReturnable<Boolean> ci) {
+	void preventGrantCriterion(class_8779 advancement, String criterionName, CallbackInfoReturnable<Boolean> ci) {
 		if (owner instanceof FakePlayer) {
 			// Prevent granting advancements to fake players.
 			ci.setReturnValue(false);
diff --git a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/AbstractFurnaceBlockEntityMixin.java b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/AbstractFurnaceBlockEntityMixin.java
index b364b8abd..f0e903cea 100644
--- a/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/AbstractFurnaceBlockEntityMixin.java
+++ b/fabric-item-api-v1/src/main/java/net/fabricmc/fabric/mixin/item/AbstractFurnaceBlockEntityMixin.java
@@ -26,8 +26,8 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
 
 import net.minecraft.block.BlockState;
 import net.minecraft.block.entity.AbstractFurnaceBlockEntity;
+import net.minecraft.class_8786;
 import net.minecraft.item.ItemStack;
-import net.minecraft.recipe.Recipe;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.world.World;
 
@@ -37,7 +37,7 @@ public abstract class AbstractFurnaceBlockEntityMixin {
 	private static final ThreadLocal<ItemStack> REMAINDER_STACK = new ThreadLocal<>();
 
 	@Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;getItem()Lnet/minecraft/item/Item;"), locals = LocalCapture.CAPTURE_FAILHARD, allow = 1)
-	private static void getStackRemainder(World world, BlockPos pos, BlockState state, AbstractFurnaceBlockEntity blockEntity, CallbackInfo ci, boolean bl, boolean bl2, ItemStack itemStack, boolean bl3, boolean bl4, Recipe recipe, int i) {
+	private static void getStackRemainder(World world, BlockPos pos, BlockState state, AbstractFurnaceBlockEntity blockEntity, CallbackInfo ci, boolean bl, boolean bl2, ItemStack itemStack, boolean bl3, boolean bl4, class_8786<?> recipe, int i) {
 		REMAINDER_STACK.set(itemStack.getRecipeRemainder());
 	}
 
diff --git a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/RecipeGameTest.java b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/RecipeGameTest.java
index 799e6b23e..6f3eefe84 100644
--- a/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/RecipeGameTest.java
+++ b/fabric-item-api-v1/src/testmod/java/net/fabricmc/fabric/test/item/gametest/RecipeGameTest.java
@@ -28,7 +28,6 @@ import net.minecraft.registry.DynamicRegistryManager;
 import net.minecraft.test.GameTest;
 import net.minecraft.test.GameTestException;
 import net.minecraft.test.TestContext;
-import net.minecraft.util.Identifier;
 import net.minecraft.util.collection.DefaultedList;
 import net.minecraft.world.World;
 
@@ -96,11 +95,6 @@ public class RecipeGameTest implements FabricGameTest {
 				return null;
 			}
 
-			@Override
-			public Identifier getId() {
-				return null;
-			}
-
 			@Override
 			public RecipeSerializer<?> getSerializer() {
 				return null;
diff --git a/fabric-message-api-v1/src/main/java/net/fabricmc/fabric/api/message/v1/ServerMessageDecoratorEvent.java b/fabric-message-api-v1/src/main/java/net/fabricmc/fabric/api/message/v1/ServerMessageDecoratorEvent.java
index 0b0d9856d..8489de55c 100644
--- a/fabric-message-api-v1/src/main/java/net/fabricmc/fabric/api/message/v1/ServerMessageDecoratorEvent.java
+++ b/fabric-message-api-v1/src/main/java/net/fabricmc/fabric/api/message/v1/ServerMessageDecoratorEvent.java
@@ -17,10 +17,6 @@
 package net.fabricmc.fabric.api.message.v1;
 
 import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionException;
-
-import org.jetbrains.annotations.Nullable;
 
 import net.minecraft.network.message.MessageDecorator;
 import net.minecraft.text.Text;
@@ -79,27 +75,17 @@ public final class ServerMessageDecoratorEvent {
 	public static final Identifier STYLING_PHASE = new Identifier("fabric", "styling");
 
 	public static final Event<MessageDecorator> EVENT = EventFactory.createWithPhases(MessageDecorator.class, decorators -> (sender, message) -> {
-		CompletableFuture<Text> future = null;
+		Text decorated = message;
 
 		for (MessageDecorator decorator : decorators) {
-			if (future == null) {
-				future = decorator.decorate(sender, message).handle((decorated, throwable) -> handle(decorated, throwable, decorator));
-			} else {
-				future = future.thenCompose((decorated) -> decorator.decorate(sender, decorated).handle((newlyDecorated, throwable) -> handle(newlyDecorated, throwable, decorator)));
-			}
+			decorated = handle(decorator.decorate(sender, decorated), decorator);
 		}
 
-		return future == null ? CompletableFuture.completedFuture(message) : future;
+		return decorated;
 	}, CONTENT_PHASE, Event.DEFAULT_PHASE, STYLING_PHASE);
 
-	private static <T extends Text> T handle(T decorated, @Nullable Throwable throwable, MessageDecorator decorator) {
+	private static <T extends Text> T handle(T decorated, MessageDecorator decorator) {
 		String decoratorName = decorator.getClass().getName();
-
-		if (throwable != null) {
-			if (throwable instanceof CompletionException) throwable = throwable.getCause();
-			throw new CompletionException("message decorator %s failed".formatted(decoratorName), throwable);
-		}
-
 		return Objects.requireNonNull(decorated, "message decorator %s returned null".formatted(decoratorName));
 	}
 }
diff --git a/fabric-message-api-v1/src/main/java/net/fabricmc/fabric/mixin/message/MinecraftServerMixin.java b/fabric-message-api-v1/src/main/java/net/fabricmc/fabric/mixin/message/MinecraftServerMixin.java
index 9a7b415b3..a43ef4759 100644
--- a/fabric-message-api-v1/src/main/java/net/fabricmc/fabric/mixin/message/MinecraftServerMixin.java
+++ b/fabric-message-api-v1/src/main/java/net/fabricmc/fabric/mixin/message/MinecraftServerMixin.java
@@ -30,7 +30,6 @@ import net.fabricmc.fabric.api.message.v1.ServerMessageDecoratorEvent;
 public class MinecraftServerMixin {
 	@Inject(method = "getMessageDecorator", at = @At("RETURN"), cancellable = true)
 	private void onGetChatDecorator(CallbackInfoReturnable<MessageDecorator> cir) {
-		MessageDecorator originalDecorator = cir.getReturnValue();
-		cir.setReturnValue((sender, message) -> originalDecorator.decorate(sender, message).thenCompose((decorated) -> ServerMessageDecoratorEvent.EVENT.invoker().decorate(sender, decorated)));
+		cir.setReturnValue((sender, message) -> ServerMessageDecoratorEvent.EVENT.invoker().decorate(sender, message));
 	}
 }
diff --git a/fabric-message-api-v1/src/testmod/java/net/fabricmc/fabric/test/message/ChatTest.java b/fabric-message-api-v1/src/testmod/java/net/fabricmc/fabric/test/message/ChatTest.java
index 993028dd4..82044e29b 100644
--- a/fabric-message-api-v1/src/testmod/java/net/fabricmc/fabric/test/message/ChatTest.java
+++ b/fabric-message-api-v1/src/testmod/java/net/fabricmc/fabric/test/message/ChatTest.java
@@ -16,7 +16,6 @@
 
 package net.fabricmc.fabric.test.message;
 
-import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
 
 import org.slf4j.Logger;
@@ -41,45 +40,28 @@ public class ChatTest implements ModInitializer {
 		// Basic content phase testing
 		ServerMessageDecoratorEvent.EVENT.register(ServerMessageDecoratorEvent.CONTENT_PHASE, (sender, message) -> {
 			if (message.getString().contains("tater")) {
-				return CompletableFuture.completedFuture(message.copy().append(" :tiny_potato:"));
+				return message.copy().append(" :tiny_potato:");
 			}
 
-			return CompletableFuture.completedFuture(message);
+			return message;
 		});
 
 		// Content phase testing, with variable info
 		ServerMessageDecoratorEvent.EVENT.register(ServerMessageDecoratorEvent.CONTENT_PHASE, (sender, message) -> {
 			if (message.getString().contains("random")) {
-				return CompletableFuture.completedFuture(Text.of(String.valueOf(Random.create().nextBetween(0, 100))));
+				return Text.of(String.valueOf(Random.create().nextBetween(0, 100)));
 			}
 
-			return CompletableFuture.completedFuture(message);
+			return message;
 		});
 
 		// Basic styling phase testing
 		ServerMessageDecoratorEvent.EVENT.register(ServerMessageDecoratorEvent.STYLING_PHASE, (sender, message) -> {
 			if (sender != null && sender.getAbilities().creativeMode) {
-				return CompletableFuture.completedFuture(message.copy().styled(style -> style.withColor(0xFFA500)));
+				return message.copy().styled(style -> style.withColor(0xFFA500));
 			}
 
-			return CompletableFuture.completedFuture(message);
-		});
-
-		// Async testing
-		ServerMessageDecoratorEvent.EVENT.register(ServerMessageDecoratorEvent.CONTENT_PHASE, (sender, message) -> {
-			if (message.getString().contains("wait")) {
-				return CompletableFuture.supplyAsync(() -> {
-					try {
-						Thread.sleep(Random.create().nextBetween(500, 2000));
-					} catch (InterruptedException ignored) {
-						// Ignore interruption
-					}
-
-					return message;
-				}, ioWorkerExecutor);
-			}
-
-			return CompletableFuture.completedFuture(message);
+			return message;
 		});
 
 		// ServerMessageEvents
diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/advancement/CriterionRegistry.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/advancement/CriterionRegistry.java
deleted file mode 100644
index 5116adb4d..000000000
--- a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/api/object/builder/v1/advancement/CriterionRegistry.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package net.fabricmc.fabric.api.object.builder.v1.advancement;
-
-import net.minecraft.advancement.criterion.Criterion;
-import net.minecraft.util.Identifier;
-
-import net.fabricmc.fabric.mixin.object.builder.CriteriaAccessor;
-
-/**
- * Allows registering advancement criteria for triggers.
- *
- * <p>A registered criterion (trigger) can be retrieved through
- * {@link net.minecraft.advancement.criterion.Criteria#getById(Identifier)}.</p>
- *
- * @see net.minecraft.advancement.criterion.Criteria
- * @deprecated Replaced by access widener for {@link net.minecraft.advancement.criterion.Criteria#register(Criterion)}
- * in Fabric Transitive Access Wideners (v1).
- */
-@Deprecated
-public final class CriterionRegistry {
-	/**
-	 * Registers a criterion for a trigger for advancements.
-	 *
-	 * @param <T> the criterion's type
-	 * @param criterion the criterion registered
-	 * @return the criterion registered, for chaining
-	 * @throws IllegalArgumentException if a criterion with the same {@link
-	 *                                  Criterion#getId() id} exists
-	 */
-	public static <T extends Criterion<?>> T register(T criterion) {
-		CriteriaAccessor.callRegister(criterion);
-		return criterion;
-	}
-}
diff --git a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/CriteriaAccessor.java b/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/CriteriaAccessor.java
deleted file mode 100644
index 1684caf23..000000000
--- a/fabric-object-builder-api-v1/src/main/java/net/fabricmc/fabric/mixin/object/builder/CriteriaAccessor.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package net.fabricmc.fabric.mixin.object.builder;
-
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.gen.Invoker;
-
-import net.minecraft.advancement.criterion.Criteria;
-import net.minecraft.advancement.criterion.Criterion;
-
-@Mixin(Criteria.class)
-public interface CriteriaAccessor {
-	@Invoker
-	static <T extends Criterion<?>> T callRegister(T object) {
-		throw new AssertionError("Mixin dummy");
-	}
-}
diff --git a/fabric-object-builder-api-v1/src/main/resources/fabric-object-builder-v1.mixins.json b/fabric-object-builder-api-v1/src/main/resources/fabric-object-builder-v1.mixins.json
index 1f92b1196..7e4be4e72 100644
--- a/fabric-object-builder-api-v1/src/main/resources/fabric-object-builder-v1.mixins.json
+++ b/fabric-object-builder-api-v1/src/main/resources/fabric-object-builder-v1.mixins.json
@@ -5,7 +5,6 @@
   "mixins": [
     "AbstractBlockAccessor",
     "AbstractBlockSettingsAccessor",
-    "CriteriaAccessor",
     "DefaultAttributeRegistryAccessor",
     "DefaultAttributeRegistryMixin",
     "DetectorRailBlockMixin",
diff --git a/fabric-object-builder-api-v1/src/testmod/java/net/fabricmc/fabric/test/object/builder/CriterionRegistryTest.java b/fabric-object-builder-api-v1/src/testmod/java/net/fabricmc/fabric/test/object/builder/CriterionRegistryTest.java
deleted file mode 100644
index ef1517e40..000000000
--- a/fabric-object-builder-api-v1/src/testmod/java/net/fabricmc/fabric/test/object/builder/CriterionRegistryTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package net.fabricmc.fabric.test.object.builder;
-
-import com.google.gson.JsonObject;
-
-import net.minecraft.advancement.criterion.ImpossibleCriterion;
-import net.minecraft.predicate.entity.AdvancementEntityPredicateDeserializer;
-import net.minecraft.util.Identifier;
-
-import net.fabricmc.fabric.api.object.builder.v1.advancement.CriterionRegistry;
-
-public final class CriterionRegistryTest {
-	public static void init() {
-		CriterionRegistry.register(new CustomCriterion());
-	}
-
-	static class CustomCriterion extends ImpossibleCriterion {
-		static final Identifier ID = ObjectBuilderTestConstants.id("custom");
-
-		@Override
-		public Identifier getId() {
-			return ID;
-		}
-
-		@Override
-		public Conditions conditionsFromJson(JsonObject jsonObject, AdvancementEntityPredicateDeserializer advancementEntityPredicateDeserializer) {
-			ObjectBuilderTestConstants.LOGGER.info("Loading custom criterion in advancement!");
-			return super.conditionsFromJson(jsonObject, advancementEntityPredicateDeserializer);
-		}
-	}
-}
diff --git a/fabric-object-builder-api-v1/src/testmod/resources/data/fabric-object-builder-api-v1-testmod/advancements/criterion_registry_test.json b/fabric-object-builder-api-v1/src/testmod/resources/data/fabric-object-builder-api-v1-testmod/advancements/criterion_registry_test.json
deleted file mode 100644
index d96be7670..000000000
--- a/fabric-object-builder-api-v1/src/testmod/resources/data/fabric-object-builder-api-v1-testmod/advancements/criterion_registry_test.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  "display": {
-    "icon": {
-      "item": "minecraft:command_block_minecart"
-    },
-    "title": {
-      "text": "Criterion registry test advancement"
-    },
-    "description": {
-      "text": "Criterion registry test advancement description"
-    },
-    "frame": "task",
-    "show_toast": false,
-    "announce_to_chat": false,
-    "hidden": false,
-    "background": "minecraft:textures/gui/advancements/backgrounds/stone.png"
-  },
-  "criteria": {
-    "custom": {
-      "trigger": "fabric-object-builder-api-v1-testmod:custom"
-    }
-  },
-  "requirements": [
-    [
-      "custom"
-    ]
-  ]
-}
diff --git a/fabric-object-builder-api-v1/src/testmod/resources/fabric.mod.json b/fabric-object-builder-api-v1/src/testmod/resources/fabric.mod.json
index 4c1c66d85..875c2f6f1 100644
--- a/fabric-object-builder-api-v1/src/testmod/resources/fabric.mod.json
+++ b/fabric-object-builder-api-v1/src/testmod/resources/fabric.mod.json
@@ -29,7 +29,6 @@
   "entrypoints": {
     "main": [
       "net.fabricmc.fabric.test.object.builder.BlockEntityTypeBuilderTest",
-      "net.fabricmc.fabric.test.object.builder.CriterionRegistryTest::init",
       "net.fabricmc.fabric.test.object.builder.FabricBlockSettingsTest",
       "net.fabricmc.fabric.test.object.builder.VillagerTypeTest1",
       "net.fabricmc.fabric.test.object.builder.VillagerTypeTest2",
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/api/recipe/v1/ingredient/CustomIngredientSerializer.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/api/recipe/v1/ingredient/CustomIngredientSerializer.java
index 7f419bb7d..e41680b6f 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/api/recipe/v1/ingredient/CustomIngredientSerializer.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/api/recipe/v1/ingredient/CustomIngredientSerializer.java
@@ -16,8 +16,7 @@
 
 package net.fabricmc.fabric.api.recipe.v1.ingredient;
 
-import com.google.gson.JsonObject;
-import com.google.gson.JsonSyntaxException;
+import com.mojang.serialization.Codec;
 import org.jetbrains.annotations.Nullable;
 
 import net.minecraft.network.PacketByteBuf;
@@ -55,18 +54,7 @@ public interface CustomIngredientSerializer<T extends CustomIngredient> {
 	 */
 	Identifier getIdentifier();
 
-	/**
-	 * Deserializes the custom ingredient from a JSON object.
-	 *
-	 * @throws JsonSyntaxException if the JSON object does not match the format expected by the serializer
-	 * @throws IllegalArgumentException if the JSON object is invalid for some other reason
-	 */
-	T read(JsonObject json);
-
-	/**
-	 * Serializes the custom ingredient to a JSON object.
-	 */
-	void write(JsonObject json, T ingredient);
+	Codec<T> getCodec(boolean allowEmpty);
 
 	/**
 	 * Deserializes the custom ingredient from a packet buffer.
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/api/recipe/v1/ingredient/DefaultCustomIngredients.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/api/recipe/v1/ingredient/DefaultCustomIngredients.java
index a2051824b..6d9380e46 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/api/recipe/v1/ingredient/DefaultCustomIngredients.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/api/recipe/v1/ingredient/DefaultCustomIngredients.java
@@ -16,6 +16,7 @@
 
 package net.fabricmc.fabric.api.recipe.v1.ingredient;
 
+import java.util.List;
 import java.util.Objects;
 
 import org.jetbrains.annotations.Nullable;
@@ -54,7 +55,7 @@ public final class DefaultCustomIngredients {
 	public static Ingredient all(Ingredient... ingredients) {
 		for (Ingredient ing : ingredients) Objects.requireNonNull(ing, "Ingredient cannot be null");
 
-		return new AllIngredient(ingredients).toVanilla();
+		return new AllIngredient(List.of(ingredients)).toVanilla();
 	}
 
 	/**
@@ -77,7 +78,7 @@ public final class DefaultCustomIngredients {
 	public static Ingredient any(Ingredient... ingredients) {
 		for (Ingredient ing : ingredients) Objects.requireNonNull(ing, "Ingredient cannot be null");
 
-		return new AnyIngredient(ingredients).toVanilla();
+		return new AnyIngredient(List.of(ingredients)).toVanilla();
 	}
 
 	/**
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientImpl.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientImpl.java
index 7c0f86654..9e5e3e774 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientImpl.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/CustomIngredientImpl.java
@@ -18,12 +18,15 @@ package net.fabricmc.fabric.impl.recipe.ingredient;
 
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Stream;
 
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
+import com.mojang.datafixers.util.Pair;
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.DynamicOps;
 import org.jetbrains.annotations.Nullable;
 
 import net.minecraft.item.ItemStack;
@@ -46,6 +49,16 @@ public class CustomIngredientImpl extends Ingredient {
 
 	static final Map<Identifier, CustomIngredientSerializer<?>> REGISTERED_SERIALIZERS = new ConcurrentHashMap<>();
 
+	public static final Codec<CustomIngredientSerializer<?>> CODEC = Identifier.CODEC.flatXmap(identifier ->
+					Optional.ofNullable(REGISTERED_SERIALIZERS.get(identifier))
+					.map(DataResult::success)
+					.orElseGet(() -> DataResult.error(() -> "Unknown custom ingredient serializer: " + identifier)),
+			serializer -> DataResult.success(serializer.getIdentifier())
+	);
+
+	public static final Codec<CustomIngredient> ALLOW_EMPTY_INGREDIENT_CODECS = CODEC.dispatch(TYPE_KEY, CustomIngredient::getSerializer, serializer -> serializer.getCodec(true));
+	public static final Codec<CustomIngredient> DISALLOW_EMPTY_INGREDIENT_CODECS = CODEC.dispatch(TYPE_KEY, CustomIngredient::getSerializer, serializer -> serializer.getCodec(false));
+
 	public static void registerSerializer(CustomIngredientSerializer<?> serializer) {
 		Objects.requireNonNull(serializer.getIdentifier(), "CustomIngredientSerializer identifier may not be null.");
 
@@ -113,14 +126,6 @@ public class CustomIngredientImpl extends Ingredient {
 		}
 	}
 
-	@Override
-	public JsonElement toJson() {
-		JsonObject json = new JsonObject();
-		json.addProperty(TYPE_KEY, customIngredient.getSerializer().getIdentifier().toString());
-		customIngredient.getSerializer().write(json, coerceIngredient());
-		return json;
-	}
-
 	@Override
 	public boolean isEmpty() {
 		// We don't want to resolve the matching stacks,
@@ -132,4 +137,33 @@ public class CustomIngredientImpl extends Ingredient {
 	private <T> T coerceIngredient() {
 		return (T) customIngredient;
 	}
+
+	public static <T> Codec<T> first(Codec<T> first, Codec<T> second) {
+		return new First<>(first, second);
+	}
+
+	// Decode/encode the first codec, if that fails return the result of the second.
+	record First<T>(Codec<T> first, Codec<T> second) implements Codec<T> {
+		@Override
+		public <T1> DataResult<Pair<T, T1>> decode(DynamicOps<T1> ops, T1 input) {
+			DataResult<Pair<T, T1>> firstResult = first.decode(ops, input);
+
+			if (firstResult.result().isPresent()) {
+				return firstResult;
+			}
+
+			return second.decode(ops, input);
+		}
+
+		@Override
+		public <T1> DataResult<T1> encode(T input, DynamicOps<T1> ops, T1 prefix) {
+			DataResult<T1> firstResult = first.encode(input, ops, prefix);
+
+			if (firstResult.result().isPresent()) {
+				return firstResult;
+			}
+
+			return second.encode(input, ops, prefix);
+		}
+	}
 }
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/AllIngredient.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/AllIngredient.java
index 387009f42..c5fc5f0ee 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/AllIngredient.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/AllIngredient.java
@@ -20,6 +20,8 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
+import com.mojang.serialization.Codec;
+
 import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.Ingredient;
 import net.minecraft.util.Identifier;
@@ -27,10 +29,21 @@ import net.minecraft.util.Identifier;
 import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredientSerializer;
 
 public class AllIngredient extends CombinedIngredient {
-	public static final CustomIngredientSerializer<AllIngredient> SERIALIZER =
-			new Serializer<>(new Identifier("fabric", "all"), AllIngredient::new);
+	private static final Codec<AllIngredient> ALLOW_EMPTY_CODEC = createCodec(Ingredient.field_46095);
+	private static final Codec<AllIngredient> DISALLOW_EMPTY_CODEC = createCodec(Ingredient.field_46096);
 
-	public AllIngredient(Ingredient[] ingredients) {
+	private static Codec<AllIngredient> createCodec(Codec<Ingredient> ingredientCodec) {
+		return ingredientCodec
+				.listOf()
+				.fieldOf("ingredients")
+				.xmap(AllIngredient::new, AllIngredient::getIngredients)
+				.codec();
+	}
+
+	public static final CustomIngredientSerializer<AllIngredient> SERIALIZER =
+			new Serializer<>(new Identifier("fabric", "all"), AllIngredient::new, ALLOW_EMPTY_CODEC, DISALLOW_EMPTY_CODEC);
+
+	public AllIngredient(List<Ingredient> ingredients) {
 		super(ingredients);
 	}
 
@@ -48,10 +61,10 @@ public class AllIngredient extends CombinedIngredient {
 	@Override
 	public List<ItemStack> getMatchingStacks() {
 		// There's always at least one sub ingredient, so accessing ingredients[0] is safe.
-		List<ItemStack> previewStacks = new ArrayList<>(Arrays.asList(ingredients[0].getMatchingStacks()));
+		List<ItemStack> previewStacks = new ArrayList<>(Arrays.asList(ingredients.get(0).getMatchingStacks()));
 
-		for (int i = 1; i < ingredients.length; ++i) {
-			Ingredient ing = ingredients[i];
+		for (int i = 1; i < ingredients.size(); ++i) {
+			Ingredient ing = ingredients.get(i);
 			previewStacks.removeIf(stack -> !ing.test(stack));
 		}
 
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/AnyIngredient.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/AnyIngredient.java
index 93b2b4045..0ae919a67 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/AnyIngredient.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/AnyIngredient.java
@@ -20,6 +20,8 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
+import com.mojang.serialization.Codec;
+
 import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.Ingredient;
 import net.minecraft.util.Identifier;
@@ -27,10 +29,21 @@ import net.minecraft.util.Identifier;
 import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredientSerializer;
 
 public class AnyIngredient extends CombinedIngredient {
-	public static final CustomIngredientSerializer<AnyIngredient> SERIALIZER =
-			new CombinedIngredient.Serializer<>(new Identifier("fabric", "any"), AnyIngredient::new);
+	private static final Codec<AnyIngredient> ALLOW_EMPTY_CODEC = createCodec(Ingredient.field_46095);
+	private static final Codec<AnyIngredient> DISALLOW_EMPTY_CODEC = createCodec(Ingredient.field_46096);
 
-	public AnyIngredient(Ingredient[] ingredients) {
+	private static Codec<AnyIngredient> createCodec(Codec<Ingredient> ingredientCodec) {
+		return ingredientCodec
+				.listOf()
+				.fieldOf("ingredients")
+				.xmap(AnyIngredient::new, AnyIngredient::getIngredients)
+				.codec();
+	}
+
+	public static final CustomIngredientSerializer<AnyIngredient> SERIALIZER =
+			new CombinedIngredient.Serializer<>(new Identifier("fabric", "any"), AnyIngredient::new, ALLOW_EMPTY_CODEC, DISALLOW_EMPTY_CODEC);
+
+	public AnyIngredient(List<Ingredient> ingredients) {
 		super(ingredients);
 	}
 
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/CombinedIngredient.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/CombinedIngredient.java
index 375d80503..6f768d427 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/CombinedIngredient.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/CombinedIngredient.java
@@ -16,15 +16,16 @@
 
 package net.fabricmc.fabric.impl.recipe.ingredient.builtin;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.function.Function;
 
-import com.google.gson.JsonArray;
-import com.google.gson.JsonObject;
+import com.mojang.serialization.Codec;
 
 import net.minecraft.network.PacketByteBuf;
 import net.minecraft.recipe.Ingredient;
 import net.minecraft.util.Identifier;
-import net.minecraft.util.JsonHelper;
 
 import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredient;
 import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredientSerializer;
@@ -33,10 +34,10 @@ import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredientSerializer;
  * Base class for ALL and ANY ingredients.
  */
 abstract class CombinedIngredient implements CustomIngredient {
-	protected final Ingredient[] ingredients;
+	protected final List<Ingredient> ingredients;
 
-	protected CombinedIngredient(Ingredient[] ingredients) {
-		if (ingredients.length == 0) {
+	protected CombinedIngredient(List<Ingredient> ingredients) {
+		if (ingredients.isEmpty()) {
 			throw new IllegalArgumentException("ALL or ANY ingredient must have at least one sub-ingredient");
 		}
 
@@ -54,13 +55,21 @@ abstract class CombinedIngredient implements CustomIngredient {
 		return false;
 	}
 
+	List<Ingredient> getIngredients() {
+		return ingredients;
+	}
+
 	static class Serializer<I extends CombinedIngredient> implements CustomIngredientSerializer<I> {
 		private final Identifier identifier;
-		private final Function<Ingredient[], I> factory;
+		private final Function<List<Ingredient>, I> factory;
+		private final Codec<I> allowEmptyCodec;
+		private final Codec<I> disallowEmptyCodec;
 
-		Serializer(Identifier identifier, Function<Ingredient[], I> factory) {
+		Serializer(Identifier identifier, Function<List<Ingredient>, I> factory, Codec<I> allowEmptyCodec, Codec<I> disallowEmptyCodec) {
 			this.identifier = identifier;
 			this.factory = factory;
+			this.allowEmptyCodec = allowEmptyCodec;
+			this.disallowEmptyCodec = disallowEmptyCodec;
 		}
 
 		@Override
@@ -69,43 +78,25 @@ abstract class CombinedIngredient implements CustomIngredient {
 		}
 
 		@Override
-		public I read(JsonObject json) {
-			JsonArray values = JsonHelper.getArray(json, "ingredients");
-			Ingredient[] ingredients = new Ingredient[values.size()];
-
-			for (int i = 0; i < values.size(); i++) {
-				ingredients[i] = Ingredient.fromJson(values.get(i));
-			}
-
-			return factory.apply(ingredients);
-		}
-
-		@Override
-		public void write(JsonObject json, I ingredient) {
-			JsonArray values = new JsonArray();
-
-			for (Ingredient value : ingredient.ingredients) {
-				values.add(value.toJson());
-			}
-
-			json.add("ingredients", values);
+		public Codec<I> getCodec(boolean allowEmpty) {
+			return allowEmpty ? allowEmptyCodec : disallowEmptyCodec;
 		}
 
 		@Override
 		public I read(PacketByteBuf buf) {
 			int size = buf.readVarInt();
-			Ingredient[] ingredients = new Ingredient[size];
+			List<Ingredient> ingredients = new ArrayList<>(size);
 
 			for (int i = 0; i < size; i++) {
-				ingredients[i] = Ingredient.fromPacket(buf);
+				ingredients.add(Ingredient.fromPacket(buf));
 			}
 
-			return factory.apply(ingredients);
+			return factory.apply(Collections.unmodifiableList(ingredients));
 		}
 
 		@Override
 		public void write(PacketByteBuf buf, I ingredient) {
-			buf.writeVarInt(ingredient.ingredients.length);
+			buf.writeVarInt(ingredient.ingredients.size());
 
 			for (Ingredient value : ingredient.ingredients) {
 				value.write(buf);
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/DifferenceIngredient.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/DifferenceIngredient.java
index ec07b4f61..4acec2ca2 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/DifferenceIngredient.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/DifferenceIngredient.java
@@ -19,7 +19,8 @@ package net.fabricmc.fabric.impl.recipe.ingredient.builtin;
 import java.util.ArrayList;
 import java.util.List;
 
-import com.google.gson.JsonObject;
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.codecs.RecordCodecBuilder;
 
 import net.minecraft.item.ItemStack;
 import net.minecraft.network.PacketByteBuf;
@@ -62,25 +63,36 @@ public class DifferenceIngredient implements CustomIngredient {
 		return SERIALIZER;
 	}
 
+	private Ingredient getBase() {
+		return base;
+	}
+
+	private Ingredient getSubtracted() {
+		return subtracted;
+	}
+
 	private static class Serializer implements CustomIngredientSerializer<DifferenceIngredient> {
-		private final Identifier id = new Identifier("fabric", "difference");
+		private static final Identifier ID = new Identifier("fabric", "difference");
+		private static final Codec<DifferenceIngredient> ALLOW_EMPTY_CODEC = createCodec(Ingredient.field_46095);
+		private static final Codec<DifferenceIngredient> DISALLOW_EMPTY_CODEC = createCodec(Ingredient.field_46096);
+
+		private static Codec<DifferenceIngredient> createCodec(Codec<Ingredient> ingredientCodec) {
+			return RecordCodecBuilder.create(instance ->
+					instance.group(
+							ingredientCodec.fieldOf("base").forGetter(DifferenceIngredient::getBase),
+							ingredientCodec.fieldOf("subtracted").forGetter(DifferenceIngredient::getSubtracted)
+					).apply(instance, DifferenceIngredient::new)
+			);
+		}
 
 		@Override
 		public Identifier getIdentifier() {
-			return id;
+			return ID;
 		}
 
 		@Override
-		public DifferenceIngredient read(JsonObject json) {
-			Ingredient base = Ingredient.fromJson(json.get("base"));
-			Ingredient subtracted = Ingredient.fromJson(json.get("subtracted"));
-			return new DifferenceIngredient(base, subtracted);
-		}
-
-		@Override
-		public void write(JsonObject json, DifferenceIngredient ingredient) {
-			json.add("base", ingredient.base.toJson());
-			json.add("subtracted", ingredient.subtracted.toJson());
+		public Codec<DifferenceIngredient> getCodec(boolean allowEmpty) {
+			return allowEmpty ? ALLOW_EMPTY_CODEC : DISALLOW_EMPTY_CODEC;
 		}
 
 		@Override
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/NbtIngredient.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/NbtIngredient.java
index 99e5841b2..bee73e867 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/NbtIngredient.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/impl/recipe/ingredient/builtin/NbtIngredient.java
@@ -21,24 +21,20 @@ import java.util.List;
 import java.util.Objects;
 
 import com.mojang.brigadier.exceptions.CommandSyntaxException;
-import com.mojang.serialization.JsonOps;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonSyntaxException;
+import com.mojang.datafixers.util.Either;
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.DataResult;
+import com.mojang.serialization.codecs.RecordCodecBuilder;
 import org.jetbrains.annotations.Nullable;
 
 import net.minecraft.item.ItemStack;
 import net.minecraft.nbt.NbtCompound;
 import net.minecraft.nbt.NbtHelper;
-import net.minecraft.nbt.NbtOps;
 import net.minecraft.nbt.StringNbtReader;
 import net.minecraft.network.PacketByteBuf;
-import net.minecraft.predicate.NbtPredicate;
 import net.minecraft.recipe.Ingredient;
 import net.minecraft.util.Identifier;
-import net.minecraft.util.JsonHelper;
+import net.minecraft.util.dynamic.Codecs;
 
 import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredient;
 import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredientSerializer;
@@ -98,55 +94,53 @@ public class NbtIngredient implements CustomIngredient {
 		return SERIALIZER;
 	}
 
+	private Ingredient getBase() {
+		return base;
+	}
+
+	private NbtCompound getNbt() {
+		return nbt;
+	}
+
+	private boolean isStrict() {
+		return strict;
+	}
+
 	private static class Serializer implements CustomIngredientSerializer<NbtIngredient> {
-		private final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
-		private final Identifier id = new Identifier("fabric", "nbt");
+		private static final Identifier ID = new Identifier("fabric", "nbt");
+
+		// Supports decoding the NBT as a string as well as the object.
+		private static final Codec<NbtCompound> NBT_CODEC = Codecs.xor(
+				Codec.STRING, NbtCompound.CODEC
+		).flatXmap(either -> either.map(s -> {
+			try {
+				return DataResult.success(StringNbtReader.parse(s));
+			} catch (CommandSyntaxException e) {
+				return DataResult.error(e::getMessage);
+			}
+		}, DataResult::success), nbtCompound -> DataResult.success(Either.left(nbtCompound.asString())));
+
+		private static final Codec<NbtIngredient> ALLOW_EMPTY_CODEC = createCodec(Ingredient.field_46095);
+		private static final Codec<NbtIngredient> DISALLOW_EMPTY_CODEC = createCodec(Ingredient.field_46096);
+
+		private static Codec<NbtIngredient> createCodec(Codec<Ingredient> ingredientCodec) {
+			return RecordCodecBuilder.create(instance ->
+					instance.group(
+							ingredientCodec.fieldOf("base").forGetter(NbtIngredient::getBase),
+							NBT_CODEC.optionalFieldOf("nbt", null).forGetter(NbtIngredient::getNbt),
+							Codec.BOOL.optionalFieldOf("strict", false).forGetter(NbtIngredient::isStrict)
+					).apply(instance, NbtIngredient::new)
+			);
+		}
 
 		@Override
 		public Identifier getIdentifier() {
-			return id;
+			return ID;
 		}
 
 		@Override
-		public NbtIngredient read(JsonObject json) {
-			Ingredient base = Ingredient.fromJson(json.get("base"));
-			NbtCompound nbt = readNbt(json.get("nbt"));
-			boolean strict = JsonHelper.getBoolean(json, "strict", false);
-			return new NbtIngredient(base, nbt, strict);
-		}
-
-		/**
-		 * Inspiration taken from {@link NbtPredicate#fromJson}.
-		 */
-		@Nullable
-		private static NbtCompound readNbt(@Nullable JsonElement json) {
-			// Process null
-			if (json == null || json.isJsonNull()) {
-				return null;
-			}
-
-			try {
-				if (json.isJsonObject()) {
-					// We use a normal .toString() to convert the json to string, and read it as SNBT.
-					// Using DynamicOps would mess with the type of integers and cause things like damage comparisons to fail...
-					return StringNbtReader.parse(json.toString());
-				} else {
-					// Assume it's a string representation of the NBT
-					return StringNbtReader.parse(JsonHelper.asString(json, "nbt"));
-				}
-			} catch (CommandSyntaxException commandSyntaxException) {
-				throw new JsonSyntaxException("Invalid nbt tag: " + commandSyntaxException.getMessage());
-			}
-		}
-
-		@Override
-		public void write(JsonObject json, NbtIngredient ingredient) {
-			json.add("base", ingredient.base.toJson());
-			json.addProperty("strict", ingredient.strict);
-
-			if (ingredient.nbt != null) {
-				json.add("nbt", NbtOps.INSTANCE.convertTo(JsonOps.INSTANCE, ingredient.nbt));
-			}
+		public Codec<NbtIngredient> getCodec(boolean allowEmpty) {
+			return allowEmpty ? ALLOW_EMPTY_CODEC : DISALLOW_EMPTY_CODEC;
 		}
 
 		@Override
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/IngredientMixin.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/IngredientMixin.java
index 18cf5f514..a718b8418 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/IngredientMixin.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/IngredientMixin.java
@@ -16,8 +16,7 @@
 
 package net.fabricmc.fabric.mixin.recipe.ingredient;
 
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
+import com.mojang.serialization.Codec;
 import org.spongepowered.asm.mixin.Mixin;
 import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Inject;
@@ -26,51 +25,19 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
 import net.minecraft.network.PacketByteBuf;
 import net.minecraft.recipe.Ingredient;
 import net.minecraft.util.Identifier;
-import net.minecraft.util.JsonHelper;
 
+import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredient;
 import net.fabricmc.fabric.api.recipe.v1.ingredient.CustomIngredientSerializer;
 import net.fabricmc.fabric.api.recipe.v1.ingredient.FabricIngredient;
 import net.fabricmc.fabric.impl.recipe.ingredient.CustomIngredientImpl;
-import net.fabricmc.fabric.impl.recipe.ingredient.builtin.AnyIngredient;
 
 @Mixin(Ingredient.class)
 public class IngredientMixin implements FabricIngredient {
-	/**
-	 * Inject right when vanilla detected a json object and check for our custom key.
-	 */
-	@Inject(
-			at = @At(
-					value = "INVOKE",
-					target = "net/minecraft/recipe/Ingredient.entryFromJson(Lcom/google/gson/JsonObject;)Lnet/minecraft/recipe/Ingredient$Entry;",
-					ordinal = 0
-			),
-			method = "fromJson(Lcom/google/gson/JsonElement;Z)Lnet/minecraft/recipe/Ingredient;",
-			cancellable = true
-	)
-	private static void injectFromJson(JsonElement json, boolean requireNotEmpty, CallbackInfoReturnable<Ingredient> cir) {
-		JsonObject obj = json.getAsJsonObject();
-
-		if (obj.has(CustomIngredientImpl.TYPE_KEY)) {
-			Identifier id = new Identifier(JsonHelper.getString(obj, CustomIngredientImpl.TYPE_KEY));
-			CustomIngredientSerializer<?> serializer = CustomIngredientSerializer.get(id);
-
-			if (serializer != null) {
-				cir.setReturnValue(serializer.read(obj).toVanilla());
-			} else {
-				throw new IllegalArgumentException("Unknown custom ingredient type: " + id);
-			}
-		}
-	}
-
-	/**
-	 * Throw exception when someone attempts to use our custom key inside an array ingredient.
-	 * The {@link AnyIngredient} should be used instead.
-	 */
-	@Inject(at = @At("HEAD"), method = "entryFromJson")
-	private static void injectEntryFromJson(JsonObject obj, CallbackInfoReturnable<?> cir) {
-		if (obj.has(CustomIngredientImpl.TYPE_KEY)) {
-			throw new IllegalArgumentException("Custom ingredient cannot be used inside an array ingredient. You can replace the array by a fabric:any ingredient.");
-		}
+	@Inject(method = "method_53725", at = @At("RETURN"), cancellable = true)
+	private static void injectCodec(boolean allowEmpty, CallbackInfoReturnable<Codec<Ingredient>> cir) {
+		final Codec<CustomIngredient> customIngredientCodec = allowEmpty ? CustomIngredientImpl.ALLOW_EMPTY_INGREDIENT_CODECS : CustomIngredientImpl.DISALLOW_EMPTY_INGREDIENT_CODECS;
+		Codec<Ingredient> ingredientCodec = customIngredientCodec.xmap(CustomIngredient::toVanilla, FabricIngredient::getCustomIngredient);
+		cir.setReturnValue(CustomIngredientImpl.first(cir.getReturnValue(), ingredientCodec));
 	}
 
 	@Inject(
diff --git a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/ShapelessRecipeMixin.java b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/ShapelessRecipeMixin.java
index b7eefab42..69459be92 100644
--- a/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/ShapelessRecipeMixin.java
+++ b/fabric-recipe-api-v1/src/main/java/net/fabricmc/fabric/mixin/recipe/ingredient/ShapelessRecipeMixin.java
@@ -33,7 +33,6 @@ import net.minecraft.item.ItemStack;
 import net.minecraft.recipe.Ingredient;
 import net.minecraft.recipe.ShapelessRecipe;
 import net.minecraft.recipe.book.CraftingRecipeCategory;
-import net.minecraft.util.Identifier;
 import net.minecraft.util.collection.DefaultedList;
 import net.minecraft.world.World;
 
@@ -48,7 +47,7 @@ public class ShapelessRecipeMixin {
 	private boolean fabric_requiresTesting = false;
 
 	@Inject(at = @At("RETURN"), method = "<init>")
-	private void cacheRequiresTesting(Identifier id, String group, CraftingRecipeCategory category, ItemStack output, DefaultedList<Ingredient> input, CallbackInfo ci) {
+	private void cacheRequiresTesting(String group, CraftingRecipeCategory category, ItemStack output, DefaultedList<Ingredient> input, CallbackInfo ci) {
 		for (Ingredient ingredient : input) {
 			if (ingredient.requiresTesting()) {
 				fabric_requiresTesting = true;
diff --git a/fabric-recipe-api-v1/src/testmod/java/net/fabricmc/fabric/test/recipe/ingredient/SerializationTests.java b/fabric-recipe-api-v1/src/testmod/java/net/fabricmc/fabric/test/recipe/ingredient/SerializationTests.java
index bdebebf7a..5c8805633 100644
--- a/fabric-recipe-api-v1/src/testmod/java/net/fabricmc/fabric/test/recipe/ingredient/SerializationTests.java
+++ b/fabric-recipe-api-v1/src/testmod/java/net/fabricmc/fabric/test/recipe/ingredient/SerializationTests.java
@@ -16,15 +16,22 @@
 
 package net.fabricmc.fabric.test.recipe.ingredient;
 
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParser;
+import java.util.List;
 
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+import com.mojang.serialization.JsonOps;
+
+import net.minecraft.item.Items;
 import net.minecraft.recipe.Ingredient;
 import net.minecraft.test.GameTest;
 import net.minecraft.test.GameTestException;
 import net.minecraft.test.TestContext;
+import net.minecraft.util.Util;
 
 import net.fabricmc.fabric.api.gametest.v1.FabricGameTest;
+import net.fabricmc.fabric.impl.recipe.ingredient.builtin.AllIngredient;
 
 public class SerializationTests {
 	/**
@@ -49,10 +56,27 @@ public class SerializationTests {
 		JsonElement json = JsonParser.parseString(ingredientJson);
 
 		try {
-			Ingredient.fromJson(json);
+			Util.getResult(Ingredient.field_46096.parse(JsonOps.INSTANCE, json), JsonParseException::new);
 			throw new GameTestException("Using a custom ingredient inside an array ingredient should have failed.");
-		} catch (IllegalArgumentException e) {
+		} catch (JsonParseException e) {
 			context.complete();
 		}
 	}
+
+	/**
+	 * Check that we can serialise a custom ingredient.
+	 */
+	@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
+	public void testCustomIngredientSerialization(TestContext context) {
+		String ingredientJson = """
+				{"ingredients":[{"item":"minecraft:stone"}],"fabric:type":"fabric:all"}
+				""".trim();
+
+		var ingredient = new AllIngredient(List.of(
+				Ingredient.ofItems(Items.STONE)
+		));
+		String json = ingredient.toVanilla().toJson(false).toString();
+		context.assertTrue(json.equals(ingredientJson), "Unexpected json: " + json);
+		context.complete();
+	}
 }
diff --git a/fabric-recipe-api-v1/src/testmod/java/net/fabricmc/fabric/test/recipe/ingredient/ShapelessRecipeMatchTests.java b/fabric-recipe-api-v1/src/testmod/java/net/fabricmc/fabric/test/recipe/ingredient/ShapelessRecipeMatchTests.java
index 96ac83abd..c3fb1dd62 100644
--- a/fabric-recipe-api-v1/src/testmod/java/net/fabricmc/fabric/test/recipe/ingredient/ShapelessRecipeMatchTests.java
+++ b/fabric-recipe-api-v1/src/testmod/java/net/fabricmc/fabric/test/recipe/ingredient/ShapelessRecipeMatchTests.java
@@ -36,7 +36,7 @@ public class ShapelessRecipeMatchTests {
 	@GameTest(templateName = FabricGameTest.EMPTY_STRUCTURE)
 	public void testShapelessMatch(TestContext context) {
 		Identifier recipeId = new Identifier("fabric-recipe-api-v1-testmod", "test_shapeless_match");
-		ShapelessRecipe recipe = (ShapelessRecipe) context.getWorld().getRecipeManager().get(recipeId).get();
+		ShapelessRecipe recipe = (ShapelessRecipe) context.getWorld().getRecipeManager().get(recipeId).get().comp_1933();
 
 		ItemStack undamagedPickaxe = new ItemStack(Items.DIAMOND_PICKAXE);
 		ItemStack damagedPickaxe = new ItemStack(Items.DIAMOND_PICKAXE);
diff --git a/fabric-recipe-api-v1/src/testmod/resources/data/fabric-recipe-api-v1-testmod/recipes/test_shapeless_match.json b/fabric-recipe-api-v1/src/testmod/resources/data/fabric-recipe-api-v1-testmod/recipes/test_shapeless_match.json
index 754bf94aa..ba924fb41 100644
--- a/fabric-recipe-api-v1/src/testmod/resources/data/fabric-recipe-api-v1-testmod/recipes/test_shapeless_match.json
+++ b/fabric-recipe-api-v1/src/testmod/resources/data/fabric-recipe-api-v1-testmod/recipes/test_shapeless_match.json
@@ -18,9 +18,7 @@
       "base": {
         "item": "minecraft:diamond_pickaxe"
       },
-      "nbt": {
-        "Damage": 0
-      },
+      "nbt": "{Damage:0}",
       "strict": false
     },
     {
diff --git a/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/ConditionalResourcesTest.java b/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/ConditionalResourcesTest.java
index d86924d2f..6adb0c3e1 100644
--- a/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/ConditionalResourcesTest.java
+++ b/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/ConditionalResourcesTest.java
@@ -64,7 +64,7 @@ public class ConditionalResourcesTest {
 			throw new AssertionError("features_enabled recipe should have been loaded.");
 		}
 
-		long loadedRecipes = manager.values().stream().filter(r -> r.getId().getNamespace().equals(MOD_ID)).count();
+		long loadedRecipes = manager.values().stream().filter(r -> r.comp_1932().getNamespace().equals(MOD_ID)).count();
 		if (loadedRecipes != 5) throw new AssertionError("Unexpected loaded recipe count: " + loadedRecipes);
 
 		context.complete();
diff --git a/fabric-transfer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/transfer/ingame/client/FluidVariantRenderTest.java b/fabric-transfer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/transfer/ingame/client/FluidVariantRenderTest.java
index a8c0029cd..bcac9e2b9 100644
--- a/fabric-transfer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/transfer/ingame/client/FluidVariantRenderTest.java
+++ b/fabric-transfer-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/transfer/ingame/client/FluidVariantRenderTest.java
@@ -54,7 +54,7 @@ public class FluidVariantRenderTest implements ClientModInitializer {
 			PlayerEntity player = MinecraftClient.getInstance().player;
 			if (player == null) return;
 
-			if (MinecraftClient.getInstance().inGameHud.method_53531().method_53536()) return;
+			if (MinecraftClient.getInstance().inGameHud.getDebugHud().shouldShowDebugHud()) return;
 
 			int renderY = 0;
 			List<FluidVariant> variants = List.of(FluidVariant.of(Fluids.WATER), FluidVariant.of(Fluids.LAVA));
diff --git a/fabric-transitive-access-wideners-v1/src/main/resources/fabric-transitive-access-wideners-v1.accesswidener b/fabric-transitive-access-wideners-v1/src/main/resources/fabric-transitive-access-wideners-v1.accesswidener
index be4f66ea5..df0f4a66e 100644
--- a/fabric-transitive-access-wideners-v1/src/main/resources/fabric-transitive-access-wideners-v1.accesswidener
+++ b/fabric-transitive-access-wideners-v1/src/main/resources/fabric-transitive-access-wideners-v1.accesswidener
@@ -9,7 +9,7 @@ transitive-accessible method net/minecraft/client/item/ModelPredicateProviderReg
 transitive-accessible method net/minecraft/client/item/ModelPredicateProviderRegistry registerCustomModelData (Lnet/minecraft/client/item/ModelPredicateProvider;)V
 
 # Registering custom advancement criteria
-transitive-accessible method net/minecraft/advancement/criterion/Criteria register (Lnet/minecraft/advancement/criterion/Criterion;)Lnet/minecraft/advancement/criterion/Criterion;
+transitive-accessible method net/minecraft/advancement/criterion/Criteria register (Ljava/lang/String;Lnet/minecraft/advancement/criterion/Criterion;)Lnet/minecraft/advancement/criterion/Criterion;
 
 # Creating custom screen handler types
 transitive-accessible class net/minecraft/screen/ScreenHandlerType$Factory
diff --git a/fabric-transitive-access-wideners-v1/template.accesswidener b/fabric-transitive-access-wideners-v1/template.accesswidener
index 326b00d63..feab2c8c4 100644
--- a/fabric-transitive-access-wideners-v1/template.accesswidener
+++ b/fabric-transitive-access-wideners-v1/template.accesswidener
@@ -4,7 +4,7 @@ transitive-accessible method net/minecraft/client/item/ModelPredicateProviderReg
 transitive-accessible method net/minecraft/client/item/ModelPredicateProviderRegistry registerCustomModelData (Lnet/minecraft/client/item/ModelPredicateProvider;)V
 
 # Registering custom advancement criteria
-transitive-accessible method net/minecraft/advancement/criterion/Criteria register (Lnet/minecraft/advancement/criterion/Criterion;)Lnet/minecraft/advancement/criterion/Criterion;
+transitive-accessible method net/minecraft/advancement/criterion/Criteria register (Ljava/lang/String;Lnet/minecraft/advancement/criterion/Criterion;)Lnet/minecraft/advancement/criterion/Criterion;
 
 # Creating custom screen handler types
 transitive-accessible class net/minecraft/screen/ScreenHandlerType$Factory
diff --git a/gradle.properties b/gradle.properties
index 30b03f3e0..dc9186f00 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,9 +2,9 @@ org.gradle.jvmargs=-Xmx2560M
 org.gradle.parallel=true
 fabric.loom.multiProjectOptimisation=true
 
-version=0.87.1
-minecraft_version=23w33a
-yarn_version=+build.1
+version=0.87.2
+minecraft_version=23w35a
+yarn_version=+build.4
 loader_version=0.14.22
 installer_version=0.11.1
 
@@ -13,38 +13,38 @@ curseforge_minecraft_version=1.20.2-Snapshot
 
 # Do not manually update, use the bumpversions task:
 fabric-api-base-version=0.4.32
-fabric-api-lookup-api-v1-version=1.6.37
+fabric-api-lookup-api-v1-version=1.6.38
 fabric-biome-api-v1-version=13.0.11
 fabric-block-api-v1-version=1.0.10
 fabric-blockrenderlayer-v1-version=1.1.42
 fabric-command-api-v1-version=1.2.35
 fabric-command-api-v2-version=2.2.14
 fabric-commands-v0-version=0.2.52
-fabric-containers-v0-version=0.1.66
+fabric-containers-v0-version=0.1.67
 fabric-content-registries-v0-version=5.0.1
 fabric-crash-report-info-v1-version=0.2.19
-fabric-data-generation-api-v1-version=12.2.6
+fabric-data-generation-api-v1-version=13.0.0
 fabric-dimensions-v1-version=2.1.55
 fabric-entity-events-v1-version=1.5.24
-fabric-events-interaction-v0-version=0.6.4
-fabric-events-lifecycle-v0-version=0.2.64
+fabric-events-interaction-v0-version=0.6.5
+fabric-events-lifecycle-v0-version=0.2.65
 fabric-game-rule-api-v1-version=1.0.39
 fabric-gametest-api-v1-version=1.2.14
-fabric-item-api-v1-version=2.1.29
+fabric-item-api-v1-version=2.1.30
 fabric-item-group-api-v1-version=4.0.12
 fabric-key-binding-api-v1-version=1.0.37
 fabric-keybindings-v0-version=0.2.35
 fabric-lifecycle-events-v1-version=2.2.23
 fabric-loot-api-v2-version=2.0.1
-fabric-message-api-v1-version=5.1.9
+fabric-message-api-v1-version=6.0.0
 fabric-mining-level-api-v1-version=2.1.51
 fabric-model-loading-api-v1-version=1.0.4
 fabric-models-v0-version=0.4.3
-fabric-networking-api-v1-version=3.0.2
-fabric-object-builder-api-v1-version=11.1.3
+fabric-networking-api-v1-version=3.0.3
+fabric-object-builder-api-v1-version=12.0.0
 fabric-particles-v1-version=1.1.3
-fabric-recipe-api-v1-version=1.0.23
-fabric-registry-sync-v0-version=3.0.3
+fabric-recipe-api-v1-version=2.0.0
+fabric-registry-sync-v0-version=3.0.4
 fabric-renderer-api-v1-version=3.1.4
 fabric-renderer-indigo-version=1.4.4
 fabric-renderer-registries-v1-version=3.2.47
@@ -52,12 +52,12 @@ fabric-rendering-data-attachment-v1-version=0.3.36
 fabric-rendering-fluids-v1-version=3.0.29
 fabric-rendering-v0-version=1.1.50
 fabric-rendering-v1-version=3.0.9
-fabric-resource-conditions-api-v1-version=2.3.6
+fabric-resource-conditions-api-v1-version=2.3.7
 fabric-resource-loader-v0-version=0.11.10
 fabric-screen-api-v1-version=2.0.10
-fabric-screen-handler-api-v1-version=1.3.32
+fabric-screen-handler-api-v1-version=1.3.33
 fabric-sound-api-v1-version=1.0.13
-fabric-transfer-api-v1-version=3.3.2
-fabric-transitive-access-wideners-v1-version=5.0.1
+fabric-transfer-api-v1-version=3.3.3
+fabric-transitive-access-wideners-v1-version=5.0.2
 fabric-convention-tags-v1-version=1.5.6
 fabric-client-tags-api-v1-version=1.1.3