diff --git a/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java b/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java index 0476a2610e7..0e0134d0f24 100644 --- a/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java +++ b/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java @@ -535,6 +535,7 @@ public NeoForgeMod(IEventBus modEventBus, Dist dist, ModContainer container) { ITEM_SUB_PREDICATES.register(modEventBus); SLOT_DISPLAY_TYPES.register(modEventBus); INGREDIENT_TYPES.register(modEventBus); + FLUID_INGREDIENT_TYPES.register(modEventBus); CONDITION_CODECS.register(modEventBus); GLOBAL_LOOT_MODIFIER_SERIALIZERS.register(modEventBus); NeoForge.EVENT_BUS.addListener(this::serverStopping); diff --git a/src/main/java/net/neoforged/neoforge/fluids/crafting/FluidIngredientCodecs.java b/src/main/java/net/neoforged/neoforge/fluids/crafting/FluidIngredientCodecs.java index 07f35907394..f785d57e955 100644 --- a/src/main/java/net/neoforged/neoforge/fluids/crafting/FluidIngredientCodecs.java +++ b/src/main/java/net/neoforged/neoforge/fluids/crafting/FluidIngredientCodecs.java @@ -24,7 +24,9 @@ public class FluidIngredientCodecs { static Codec codec() { return Codec.xor( NeoForgeRegistries.FLUID_INGREDIENT_TYPES.byNameCodec().dispatch("neoforge:ingredient_type", FluidIngredient::getType, FluidIngredientType::codec), - SimpleFluidIngredient.CODEC).xmap(either -> either.map(i -> i, i -> i), ingredient -> switch (ingredient) { + // Use lazy: SimpleFluidIngredient.CODEC is initialized after FluidIngredient.CODEC which lives in a superclass. + Codec.lazyInitialized(() -> SimpleFluidIngredient.CODEC)) + .xmap(either -> either.map(i -> i, i -> i), ingredient -> switch (ingredient) { case SimpleFluidIngredient simple -> Either.right(simple); default -> Either.left(ingredient); }); diff --git a/tests/src/junit/java/net/neoforged/neoforge/unittest/FluidIngredientTests.java b/tests/src/junit/java/net/neoforged/neoforge/unittest/FluidIngredientTests.java index ab8d78ab508..30e66adcc2f 100644 --- a/tests/src/junit/java/net/neoforged/neoforge/unittest/FluidIngredientTests.java +++ b/tests/src/junit/java/net/neoforged/neoforge/unittest/FluidIngredientTests.java @@ -5,6 +5,7 @@ package net.neoforged.neoforge.unittest; +import com.mojang.serialization.JsonOps; import java.util.List; import java.util.stream.Stream; import net.minecraft.core.component.DataComponentPatch; @@ -17,6 +18,7 @@ import net.neoforged.neoforge.fluids.crafting.CompoundFluidIngredient; import net.neoforged.neoforge.fluids.crafting.DataComponentFluidIngredient; import net.neoforged.neoforge.fluids.crafting.FluidIngredient; +import net.neoforged.neoforge.fluids.crafting.IntersectionFluidIngredient; import net.neoforged.testframework.junit.EphemeralTestServerProvider; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; @@ -74,6 +76,7 @@ void fluidIngredientComponentMatchingWorks(boolean strict, MinecraftServer serve .isEqualTo(!strict); } + @Test void singleFluidIngredientIgnoresSizeAndData(MinecraftServer server) { var ingredient = FluidIngredient.of(Fluids.WATER); @@ -85,4 +88,30 @@ void singleFluidIngredientIgnoresSizeAndData(MinecraftServer server) { .withFailMessage("Single fluid ingredient should match regardless of fluid data!") .isTrue(); } + + @Test + void customFluidIngredientSerialization(MinecraftServer server) { + var ingredient1 = FluidIngredient.of(Fluids.WATER, Fluids.LAVA); + var ingredient2 = FluidIngredient.of(Fluids.LAVA); + var intersection = IntersectionFluidIngredient.of(ingredient1, ingredient2); + + var ops = server.registryAccess().createSerializationContext(JsonOps.INSTANCE); + var result = FluidIngredient.CODEC.encodeStart(ops, intersection) + .getOrThrow(error -> new RuntimeException("Failed to serialize ingredient: " + error)); + + var deserialized = FluidIngredient.CODEC.decode(ops, result) + .getOrThrow(error -> new RuntimeException("Failed to deserialize ingredient: " + error)) + .getFirst(); + + if (!(deserialized instanceof IntersectionFluidIngredient)) { + throw new AssertionError("Deserialized ingredient is not an IntersectionFluidIngredient! Got " + deserialized); + } + + if (deserialized.test(new FluidStack(Fluids.WATER, 1))) { + throw new AssertionError("Deserialized ingredient should not match water!"); + } + if (!deserialized.test(new FluidStack(Fluids.LAVA, 1))) { + throw new AssertionError("Deserialized ingredient should match lava!"); + } + } }