Skip to content

Commit

Permalink
Ingredient Rework (#793)
Browse files Browse the repository at this point in the history
  • Loading branch information
Technici4n committed Apr 23, 2024
1 parent f4d8dec commit 7b6ac97
Show file tree
Hide file tree
Showing 26 changed files with 900 additions and 422 deletions.
253 changes: 142 additions & 111 deletions patches/net/minecraft/world/item/crafting/Ingredient.java.patch

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@
default boolean isIncomplete() {
NonNullList<Ingredient> nonnulllist = this.getIngredients();
- return nonnulllist.isEmpty() || nonnulllist.stream().anyMatch(p_151268_ -> p_151268_.getItems().length == 0);
+ return nonnulllist.isEmpty() || nonnulllist.stream().anyMatch(net.neoforged.neoforge.common.CommonHooks::hasNoElements);
+ return nonnulllist.isEmpty() || nonnulllist.stream().anyMatch(Ingredient::hasNoItems);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
public boolean isIncomplete() {
NonNullList<Ingredient> nonnulllist = this.getIngredients();
- return nonnulllist.isEmpty() || nonnulllist.stream().filter(p_151277_ -> !p_151277_.isEmpty()).anyMatch(p_151273_ -> p_151273_.getItems().length == 0);
+ return nonnulllist.isEmpty() || nonnulllist.stream().filter(p_151277_ -> !p_151277_.isEmpty()).anyMatch(net.neoforged.neoforge.common.CommonHooks::hasNoElements);
+ return nonnulllist.isEmpty() || nonnulllist.stream().filter(p_151277_ -> !p_151277_.isEmpty()).anyMatch(Ingredient::hasNoItems);
}

public static class Serializer implements RecipeSerializer<ShapedRecipe> {
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
@Override
public boolean isIncomplete() {
- return Stream.of(this.template, this.base, this.addition).anyMatch(Ingredient::isEmpty);
+ return Stream.of(this.template, this.base, this.addition).anyMatch(net.neoforged.neoforge.common.CommonHooks::hasNoElements);
+ return Stream.of(this.template, this.base, this.addition).anyMatch(Ingredient::hasNoItems);
}

public static class Serializer implements RecipeSerializer<SmithingTransformRecipe> {
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
@Override
public boolean isIncomplete() {
- return Stream.of(this.template, this.base, this.addition).anyMatch(Ingredient::isEmpty);
+ return Stream.of(this.template, this.base, this.addition).anyMatch(net.neoforged.neoforge.common.CommonHooks::hasNoElements);
+ return Stream.of(this.template, this.base, this.addition).anyMatch(Ingredient::hasNoItems);
}

public static class Serializer implements RecipeSerializer<SmithingTrimRecipe> {
14 changes: 0 additions & 14 deletions src/main/java/net/neoforged/neoforge/common/CommonHooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,13 @@
import net.minecraft.world.item.EnchantedBookItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.PotionItem;
import net.minecraft.world.item.SpawnEggItem;
import net.minecraft.world.item.Tiers;
import net.minecraft.world.item.TippedArrowItem;
import net.minecraft.world.item.alchemy.Potion;
import net.minecraft.world.item.alchemy.PotionContents;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeType;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
Expand Down Expand Up @@ -897,18 +895,6 @@ public static int onNoteChange(Level level, BlockPos pos, BlockState state, int
return event.getVanillaNoteId();
}

public static boolean hasNoElements(Ingredient ingredient) {
ItemStack[] items = ingredient.getItems();
if (items.length == 0)
return true;
if (items.length == 1) {
// If we potentially added a barrier due to the ingredient being an empty tag, try and check if it is the stack we added
ItemStack item = items[0];
return item.getItem() == Items.BARRIER && item.getHoverName() instanceof MutableComponent hoverName && hoverName.getString().startsWith("Empty Tag: ");
}
return false;
}

@Deprecated(forRemoval = true, since = "1.20.1") // Tags use a codec now This was never used in 1.20
public static <T> void deserializeTagAdditions(List<TagEntry> list, JsonObject json, List<TagEntry> allList) {}

Expand Down
14 changes: 4 additions & 10 deletions src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.GameRules;
Expand Down Expand Up @@ -367,10 +366,10 @@ public class NeoForgeMod {

private static final DeferredRegister<IngredientType<?>> INGREDIENT_TYPES = DeferredRegister.create(NeoForgeRegistries.Keys.INGREDIENT_TYPES, NeoForgeVersion.MOD_ID);

public static final DeferredHolder<IngredientType<?>, IngredientType<CompoundIngredient>> COMPOUND_INGREDIENT_TYPE = INGREDIENT_TYPES.register("compound", () -> new IngredientType<>(CompoundIngredient.CODEC, CompoundIngredient.CODEC_NONEMPTY));
public static final DeferredHolder<IngredientType<?>, IngredientType<DataComponentIngredient>> NBT_INGREDIENT_TYPE = INGREDIENT_TYPES.register("components", () -> new IngredientType<>(DataComponentIngredient.CODEC, DataComponentIngredient.CODEC_NONEMPTY));
public static final DeferredHolder<IngredientType<?>, IngredientType<DifferenceIngredient>> DIFFERENCE_INGREDIENT_TYPE = INGREDIENT_TYPES.register("difference", () -> new IngredientType<>(DifferenceIngredient.CODEC, DifferenceIngredient.CODEC_NONEMPTY));
public static final DeferredHolder<IngredientType<?>, IngredientType<IntersectionIngredient>> INTERSECTION_INGREDIENT_TYPE = INGREDIENT_TYPES.register("intersection", () -> new IngredientType<>(IntersectionIngredient.CODEC, IntersectionIngredient.CODEC_NONEMPTY));
public static final DeferredHolder<IngredientType<?>, IngredientType<CompoundIngredient>> COMPOUND_INGREDIENT_TYPE = INGREDIENT_TYPES.register("compound", () -> new IngredientType<>(CompoundIngredient.CODEC));
public static final DeferredHolder<IngredientType<?>, IngredientType<DataComponentIngredient>> DATA_COMPONENT_INGREDIENT_TYPE = INGREDIENT_TYPES.register("components", () -> new IngredientType<>(DataComponentIngredient.CODEC));
public static final DeferredHolder<IngredientType<?>, IngredientType<DifferenceIngredient>> DIFFERENCE_INGREDIENT_TYPE = INGREDIENT_TYPES.register("difference", () -> new IngredientType<>(DifferenceIngredient.CODEC));
public static final DeferredHolder<IngredientType<?>, IngredientType<IntersectionIngredient>> INTERSECTION_INGREDIENT_TYPE = INGREDIENT_TYPES.register("intersection", () -> new IngredientType<>(IntersectionIngredient.CODEC));

private static final DeferredRegister<MapCodec<? extends ICondition>> CONDITION_CODECS = DeferredRegister.create(NeoForgeRegistries.Keys.CONDITION_CODECS, NeoForgeVersion.MOD_ID);
public static final DeferredHolder<MapCodec<? extends ICondition>, MapCodec<AndCondition>> AND_CONDITION = CONDITION_CODECS.register("and", () -> AndCondition.CODEC);
Expand All @@ -381,10 +380,6 @@ public class NeoForgeMod {
public static final DeferredHolder<MapCodec<? extends ICondition>, MapCodec<OrCondition>> OR_CONDITION = CONDITION_CODECS.register("or", () -> OrCondition.CODEC);
public static final DeferredHolder<MapCodec<? extends ICondition>, MapCodec<TagEmptyCondition>> TAG_EMPTY_CONDITION = CONDITION_CODECS.register("tag_empty", () -> TagEmptyCondition.CODEC);
public static final DeferredHolder<MapCodec<? extends ICondition>, MapCodec<TrueCondition>> TRUE_CONDITION = CONDITION_CODECS.register("true", () -> TrueCondition.CODEC);
private static final DeferredRegister<IngredientType<?>> VANILLA_INGREDIENT_TYPES = DeferredRegister.create(NeoForgeRegistries.Keys.INGREDIENT_TYPES, "minecraft");

// TODO 1.20.5: this will be gone with the custom ingredient cleanup
public static final DeferredHolder<IngredientType<?>, IngredientType<Ingredient>> VANILLA_INGREDIENT_TYPE = VANILLA_INGREDIENT_TYPES.register("item", () -> new IngredientType<>(Ingredient.VANILLA_CODEC.fieldOf("value"), Ingredient.VANILLA_CODEC_NONEMPTY.fieldOf("value")));

private static final DeferredRegister<MapCodec<? extends EntitySubPredicate>> ENTITY_PREDICATE_CODECS = DeferredRegister.create(Registries.ENTITY_SUB_PREDICATE_TYPE, NeoForgeVersion.MOD_ID);
public static final DeferredHolder<MapCodec<? extends EntitySubPredicate>, MapCodec<PiglinNeutralArmorEntityPredicate>> PIGLIN_NEUTRAL_ARMOR_PREDICATE = ENTITY_PREDICATE_CODECS.register("piglin_neutral_armor", () -> PiglinNeutralArmorEntityPredicate.CODEC);
Expand Down Expand Up @@ -583,7 +578,6 @@ public NeoForgeMod(IEventBus modEventBus, Dist dist, ModContainer container) {
STRUCTURE_MODIFIER_SERIALIZERS.register(modEventBus);
HOLDER_SET_TYPES.register(modEventBus);
VANILLA_FLUID_TYPES.register(modEventBus);
VANILLA_INGREDIENT_TYPES.register(modEventBus);
ENTITY_PREDICATE_CODECS.register(modEventBus);
ITEM_SUB_PREDICATES.register(modEventBus);
INGREDIENT_TYPES.register(modEventBus);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) Forge Development LLC and contributors
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

Expand All @@ -8,50 +8,56 @@
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.neoforged.neoforge.common.NeoForgeMod;
import net.neoforged.neoforge.common.util.NeoForgeExtraCodecs;
import org.jetbrains.annotations.Nullable;

/** Ingredient that matches if any of the child ingredients match */
public class CompoundIngredient extends ChildBasedIngredient {
public static final MapCodec<CompoundIngredient> CODEC = NeoForgeExtraCodecs.aliasedFieldOf(Ingredient.LIST_CODEC, "children", "ingredients").xmap(CompoundIngredient::new, ChildBasedIngredient::getChildren);
public static final Codec<CompoundIngredient> DIRECT_CODEC = Ingredient.LIST_CODEC.xmap(CompoundIngredient::new, ChildBasedIngredient::getChildren);
public static final MapCodec<CompoundIngredient> CODEC_NONEMPTY = NeoForgeExtraCodecs.aliasedFieldOf(Ingredient.LIST_CODEC_NONEMPTY, "children", "ingredients").xmap(CompoundIngredient::new, ChildBasedIngredient::getChildren);
public static final Codec<CompoundIngredient> DIRECT_CODEC_NONEMPTY = Ingredient.LIST_CODEC_NONEMPTY.xmap(CompoundIngredient::new, ChildBasedIngredient::getChildren);

protected CompoundIngredient(List<Ingredient> children) {
super(children.stream().map(Value::new), NeoForgeMod.COMPOUND_INGREDIENT_TYPE, children);
}
public record CompoundIngredient(List<Ingredient> children) implements ICustomIngredient {
public static final MapCodec<CompoundIngredient> CODEC = NeoForgeExtraCodecs.aliasedFieldOf(Ingredient.LIST_CODEC_NONEMPTY, "children", "ingredients").xmap(CompoundIngredient::new, CompoundIngredient::children);
public static final Codec<CompoundIngredient> DIRECT_CODEC = Ingredient.LIST_CODEC.xmap(CompoundIngredient::new, CompoundIngredient::children);
public static final Codec<CompoundIngredient> DIRECT_CODEC_NONEMPTY = Ingredient.LIST_CODEC_NONEMPTY.xmap(CompoundIngredient::new, CompoundIngredient::children);

/** Creates a compound ingredient from the given list of ingredients */
public static Ingredient of(Ingredient... children) {
if (children.length == 0)
return of();
return Ingredient.EMPTY;
if (children.length == 1)
return children[0];

return new CompoundIngredient(List.of(children));
return new CompoundIngredient(List.of(children)).toVanilla();
}

@Override
protected Stream<ItemStack> generateMatchingStacks() {
public Stream<ItemStack> getItems() {
return children.stream().flatMap(child -> Arrays.stream(child.getItems()));
}

@Override
protected boolean testComplex(@Nullable ItemStack stack) {
return children.stream().anyMatch(i -> i.test(stack));
public boolean test(ItemStack stack) {
for (var child : children) {
if (child.test(stack)) {
return true;
}
}
return false;
}

private record Value(Ingredient inner) implements Ingredient.Value {
@Override
public Collection<ItemStack> getItems() {
return List.of(inner.getItems());
@Override
public boolean isSimple() {
for (var child : children) {
if (!child.isSimple()) {
return false;
}
}
return true;
}

@Override
public IngredientType<?> getType() {
return NeoForgeMod.COMPOUND_INGREDIENT_TYPE.get();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,60 @@
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import java.util.function.Function;
import com.mojang.serialization.MapCodec;
import java.util.stream.Stream;
import net.minecraft.world.item.crafting.Ingredient;
import net.neoforged.neoforge.common.NeoForgeMod;
import net.neoforged.neoforge.common.util.NeoForgeExtraCodecs;
import net.neoforged.neoforge.registries.NeoForgeRegistries;
import org.jetbrains.annotations.ApiStatus;

@ApiStatus.Internal
public class CraftingHelper {
@ApiStatus.Internal
public static Codec<Ingredient> makeIngredientCodec(boolean allowEmpty, Codec<Ingredient> vanillaCodec) {
public static Codec<Ingredient> makeIngredientCodec(boolean allowEmpty) {
var compoundIngredientCodec = Codec.lazyInitialized(() -> allowEmpty ? CompoundIngredient.DIRECT_CODEC : CompoundIngredient.DIRECT_CODEC_NONEMPTY);
return NeoForgeExtraCodecs.withAlternative(
// Compound ingredient handling
compoundIngredientCodec.flatComapMap(
Function.identity(),
i -> i instanceof CompoundIngredient c ? DataResult.success(c) : DataResult.error(() -> "Not a compound ingredient")),
// Otherwise choose between custom and vanilla
makeIngredientCodec0(allowEmpty, vanillaCodec));
return Codec.either(compoundIngredientCodec, makeIngredientMapCodec().codec())
.xmap(either -> either.map(ICustomIngredient::toVanilla, i -> i), ingredient -> {
if (convertToCompoundIngredient(ingredient).getCustomIngredient() instanceof CompoundIngredient compound) {
// Use [] syntax for vanilla array ingredients and CompoundIngredients.
return Either.left(compound);
} else {
// Else use {} syntax.
return Either.right(ingredient);
}
});
}

// Choose between dispatch codec for custom ingredients and vanilla codec
private static Codec<Ingredient> makeIngredientCodec0(boolean allowEmpty, Codec<Ingredient> vanillaCodec) {
// Dispatch codec for custom ingredient types:
Codec<Ingredient> dispatchCodec = NeoForgeRegistries.INGREDIENT_TYPES.byNameCodec()
.dispatch(Ingredient::getType, ingredientType -> ingredientType.codec(allowEmpty));
// Either codec to combine with the vanilla ingredient codec:
Codec<Either<Ingredient, Ingredient>> eitherCodec = Codec.either(
dispatchCodec,
vanillaCodec);
return eitherCodec.xmap(either -> either.map(i -> i, i -> i), ingredient -> {
// Prefer writing without the "type" field if possible:
if (ingredient.getType() == NeoForgeMod.VANILLA_INGREDIENT_TYPE.get()) {
return Either.right(ingredient);
} else {
return Either.left(ingredient);
}
});
/**
* Converts vanilla "array ingredients" to {@link CompoundIngredient}s.
*/
private static Ingredient convertToCompoundIngredient(Ingredient ingredient) {
if (!ingredient.isCustom() && ingredient.getValues().length != 1) {
// Convert vanilla ingredient to custom CompoundIngredient
return CompoundIngredient.of(Stream.of(ingredient.getValues()).map(v -> Ingredient.fromValues(Stream.of(v))).toArray(Ingredient[]::new));
}
return ingredient;
}

public static MapCodec<Ingredient> makeIngredientMapCodec() {
// Dispatch codec for custom ingredient types, else fallback to vanilla ingredient codec.
return NeoForgeExtraCodecs.<IngredientType<?>, ICustomIngredient, Ingredient.Value>dispatchMapOrElse(
NeoForgeRegistries.INGREDIENT_TYPES.byNameCodec(),
ICustomIngredient::getType,
IngredientType::codec,
Ingredient.Value.MAP_CODEC)
.xmap(either -> either.map(ICustomIngredient::toVanilla, v -> Ingredient.fromValues(Stream.of(v))), ingredient -> {
var customIngredient = convertToCompoundIngredient(ingredient).getCustomIngredient();
if (customIngredient == null) {
return Either.right(ingredient.getValues()[0]);
} else {
return Either.left(customIngredient);
}
})
.validate(ingredient -> {
if (!ingredient.isCustom() && ingredient.getValues().length == 0) {
return DataResult.error(() -> "Cannot serialize empty ingredient using the map codec");
}
return DataResult.success(ingredient);
});
}
}
Loading

0 comments on commit 7b6ac97

Please sign in to comment.