diff --git a/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java b/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java index bbdffc2b55..d9ad69fcf7 100644 --- a/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java +++ b/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java @@ -87,7 +87,7 @@ import net.neoforged.neoforge.common.advancements.critereon.SnowBootsEntityPredicate; import net.neoforged.neoforge.common.conditions.AndCondition; import net.neoforged.neoforge.common.conditions.FalseCondition; -import net.neoforged.neoforge.common.conditions.FlagCondition; +import net.neoforged.neoforge.common.conditions.FeatureFlagsEnabledCondition; import net.neoforged.neoforge.common.conditions.ICondition; import net.neoforged.neoforge.common.conditions.ItemExistsCondition; import net.neoforged.neoforge.common.conditions.ModLoadedCondition; @@ -386,7 +386,7 @@ public class NeoForgeMod { public static final DeferredHolder, MapCodec> OR_CONDITION = CONDITION_CODECS.register("or", () -> OrCondition.CODEC); public static final DeferredHolder, MapCodec> TAG_EMPTY_CONDITION = CONDITION_CODECS.register("tag_empty", () -> TagEmptyCondition.CODEC); public static final DeferredHolder, MapCodec> TRUE_CONDITION = CONDITION_CODECS.register("true", () -> TrueCondition.CODEC); - public static final DeferredHolder, MapCodec> FEATURE_FLAG_CONDITION = CONDITION_CODECS.register("feature_flags", () -> FlagCondition.CODEC); + public static final DeferredHolder, MapCodec> FEATURE_FLAGS_ENABLED_CONDITION = CONDITION_CODECS.register("feature_flags_enabled", () -> FeatureFlagsEnabledCondition.CODEC); private static final DeferredRegister> ENTITY_PREDICATE_CODECS = DeferredRegister.create(Registries.ENTITY_SUB_PREDICATE_TYPE, NeoForgeVersion.MOD_ID); public static final DeferredHolder, MapCodec> PIGLIN_NEUTRAL_ARMOR_PREDICATE = ENTITY_PREDICATE_CODECS.register("piglin_neutral_armor", () -> PiglinNeutralArmorEntityPredicate.CODEC); diff --git a/src/main/java/net/neoforged/neoforge/common/conditions/FeatureFlagsEnabledCondition.java b/src/main/java/net/neoforged/neoforge/common/conditions/FeatureFlagsEnabledCondition.java new file mode 100644 index 0000000000..6eb0aef8ab --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/conditions/FeatureFlagsEnabledCondition.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.common.conditions; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.world.flag.FeatureFlag; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.flag.FeatureFlags; + +/** + * Condition checking that a set of {@link FeatureFlag feature flags} are enabled. + * + * @apiNote Mainly to be used when flagged content is not contained within the same feature pack which also enables said {@link FeatureFlag feature flags}. + */ +public record FeatureFlagsEnabledCondition(FeatureFlagSet flags) implements ICondition { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + FeatureFlags.CODEC.fieldOf("flags").forGetter(condition -> condition.flags)).apply(instance, FeatureFlagsEnabledCondition::new)); + + public FeatureFlagsEnabledCondition { + if (flags.isEmpty()) { + throw new IllegalArgumentException("FeatureFlagsEnabledCondition requires a non-empty feature flag set"); + } + } + + @Override + public boolean test(IContext context) { + return flags.isSubsetOf(context.enabledFeatures()); + } + + @Override + public MapCodec codec() { + return CODEC; + } +} diff --git a/src/main/java/net/neoforged/neoforge/common/conditions/FlagCondition.java b/src/main/java/net/neoforged/neoforge/common/conditions/FlagCondition.java deleted file mode 100644 index 2b59ed1584..0000000000 --- a/src/main/java/net/neoforged/neoforge/common/conditions/FlagCondition.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) NeoForged and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.neoforged.neoforge.common.conditions; - -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.world.flag.FeatureFlag; -import net.minecraft.world.flag.FeatureFlagSet; -import net.minecraft.world.flag.FeatureFlags; - -/** - * Condition checking for the enabled state of a given {@link FeatureFlagSet}. - *

- * {@code requiredFeatures} - {@link FeatureFlagSet} containing all {@link FeatureFlag feature flags} to be validated. - * {@code expectedResult} - Validates that all given {@link FeatureFlag feature flags} are enabled when {@code true} or disabled when {@code false}. - * - * @apiNote Mainly to be used when flagged content is not contained within the same feature pack which also enables said {@link FeatureFlag feature flags}. - */ -public final class FlagCondition implements ICondition { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - FeatureFlags.CODEC.fieldOf("flags").forGetter(condition -> condition.requiredFeatures), - Codec.BOOL.lenientOptionalFieldOf("expected_result", true).forGetter(condition -> condition.expectedResult)).apply(instance, FlagCondition::new)); - - private final FeatureFlagSet requiredFeatures; - private final boolean expectedResult; - - private FlagCondition(FeatureFlagSet requiredFeatures, boolean expectedResult) { - this.requiredFeatures = requiredFeatures; - this.expectedResult = expectedResult; - } - - @Override - public boolean test(IContext context) { - var flagsEnabled = requiredFeatures.isSubsetOf(context.enabledFeatures()); - // true if: 'expectedResult' is true nd all given flags are enabled - // false if: `enabledEnabled' is false and all given flags are disabled - return flagsEnabled == expectedResult; - } - - @Override - public MapCodec codec() { - return CODEC; - } - - public static ICondition isEnabled(FeatureFlagSet requiredFeatures) { - return new FlagCondition(requiredFeatures, true); - } - - public static ICondition isEnabled(FeatureFlag requiredFlag) { - return isEnabled(FeatureFlagSet.of(requiredFlag)); - } - - public static ICondition isEnabled(FeatureFlag requiredFlag, FeatureFlag... requiredFlags) { - return isEnabled(FeatureFlagSet.of(requiredFlag, requiredFlags)); - } - - public static ICondition isDisabled(FeatureFlagSet requiredFeatures) { - return new FlagCondition(requiredFeatures, false); - } - - public static ICondition isDisabled(FeatureFlag requiredFlag) { - return isDisabled(FeatureFlagSet.of(requiredFlag)); - } - - public static ICondition isDisabled(FeatureFlag requiredFlag, FeatureFlag... requiredFlags) { - return isDisabled(FeatureFlagSet.of(requiredFlag, requiredFlags)); - } -} diff --git a/src/main/java/net/neoforged/neoforge/common/conditions/IConditionBuilder.java b/src/main/java/net/neoforged/neoforge/common/conditions/IConditionBuilder.java index f0a532ac16..ef10000e02 100644 --- a/src/main/java/net/neoforged/neoforge/common/conditions/IConditionBuilder.java +++ b/src/main/java/net/neoforged/neoforge/common/conditions/IConditionBuilder.java @@ -10,6 +10,7 @@ import net.minecraft.world.flag.FeatureFlag; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.item.Item; +import org.apache.commons.lang3.ArrayUtils; public interface IConditionBuilder { default ICondition and(ICondition... values) { @@ -44,27 +45,18 @@ default ICondition tagEmpty(TagKey tag) { return new TagEmptyCondition(tag.location()); } - default ICondition isFeatureEnabled(FeatureFlagSet requiredFeatures) { - return FlagCondition.isEnabled(requiredFeatures); + default ICondition featureFlagsEnabled(FeatureFlagSet requiredFeatures) { + return new FeatureFlagsEnabledCondition(requiredFeatures); } - default ICondition isFeatureEnabled(FeatureFlag requiredFlag) { - return FlagCondition.isEnabled(requiredFlag); - } - - default ICondition isFeatureEnabled(FeatureFlag requiredFlag, FeatureFlag... requiredFlags) { - return FlagCondition.isEnabled(requiredFlag, requiredFlags); - } - - default ICondition isFeatureDisabled(FeatureFlagSet requiredFeatures) { - return FlagCondition.isDisabled(requiredFeatures); - } - - default ICondition isFeatureDisabled(FeatureFlag requiredFlag) { - return FlagCondition.isDisabled(requiredFlag); - } - - default ICondition isFeatureDisabled(FeatureFlag requiredFlag, FeatureFlag... requiredFlags) { - return FlagCondition.isDisabled(requiredFlag, requiredFlags); + default ICondition featureFlagsEnabled(FeatureFlag... requiredFlags) { + if (requiredFlags.length == 0) { + throw new IllegalArgumentException("FeatureFlagsEnabledCondition requires at least one feature flag."); + } + if (requiredFlags.length == 1) { + return new FeatureFlagsEnabledCondition(FeatureFlagSet.of(requiredFlags[0])); + } else { + return new FeatureFlagsEnabledCondition(FeatureFlagSet.of(requiredFlags[0], ArrayUtils.remove(requiredFlags, 0))); + } } } diff --git a/tests/src/generated/resources/data/neotests_test_flag_condition/advancement/recipes/misc/diamonds_from_dirt.json b/tests/src/generated/resources/data/neotests_test_flag_condition/advancement/recipes/misc/diamonds_from_dirt.json index 2228d43a8b..e0d40e6e34 100644 --- a/tests/src/generated/resources/data/neotests_test_flag_condition/advancement/recipes/misc/diamonds_from_dirt.json +++ b/tests/src/generated/resources/data/neotests_test_flag_condition/advancement/recipes/misc/diamonds_from_dirt.json @@ -1,7 +1,7 @@ { "neoforge:conditions": [ { - "type": "neoforge:feature_flags", + "type": "neoforge:feature_flags_enabled", "flags": [ "custom_feature_flags_pack_test:test_flag" ] diff --git a/tests/src/generated/resources/data/neotests_test_flag_condition/advancement/recipes/misc/dirt_from_diamonds.json b/tests/src/generated/resources/data/neotests_test_flag_condition/advancement/recipes/misc/dirt_from_diamonds.json deleted file mode 100644 index bde8245ef5..0000000000 --- a/tests/src/generated/resources/data/neotests_test_flag_condition/advancement/recipes/misc/dirt_from_diamonds.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "neoforge:conditions": [ - { - "type": "neoforge:feature_flags", - "expected_result": false, - "flags": [ - "custom_feature_flags_pack_test:test_flag" - ] - } - ], - "parent": "minecraft:recipes/root", - "criteria": { - "has_diamond": { - "conditions": { - "items": [ - { - "items": "#c:gems/diamond" - } - ] - }, - "trigger": "minecraft:inventory_changed" - }, - "has_the_recipe": { - "conditions": { - "recipe": "neotests_test_flag_condition:dirt_from_diamonds" - }, - "trigger": "minecraft:recipe_unlocked" - } - }, - "requirements": [ - [ - "has_the_recipe", - "has_diamond" - ] - ], - "rewards": { - "recipes": [ - "neotests_test_flag_condition:dirt_from_diamonds" - ] - } -} \ No newline at end of file diff --git a/tests/src/generated/resources/data/neotests_test_flag_condition/recipe/diamonds_from_dirt.json b/tests/src/generated/resources/data/neotests_test_flag_condition/recipe/diamonds_from_dirt.json index 51550166a3..5accc57d95 100644 --- a/tests/src/generated/resources/data/neotests_test_flag_condition/recipe/diamonds_from_dirt.json +++ b/tests/src/generated/resources/data/neotests_test_flag_condition/recipe/diamonds_from_dirt.json @@ -1,7 +1,7 @@ { "neoforge:conditions": [ { - "type": "neoforge:feature_flags", + "type": "neoforge:feature_flags_enabled", "flags": [ "custom_feature_flags_pack_test:test_flag" ] diff --git a/tests/src/generated/resources/data/neotests_test_flag_condition/recipe/dirt_from_diamonds.json b/tests/src/generated/resources/data/neotests_test_flag_condition/recipe/dirt_from_diamonds.json deleted file mode 100644 index d1984cf97c..0000000000 --- a/tests/src/generated/resources/data/neotests_test_flag_condition/recipe/dirt_from_diamonds.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "neoforge:conditions": [ - { - "type": "neoforge:feature_flags", - "expected_result": false, - "flags": [ - "custom_feature_flags_pack_test:test_flag" - ] - } - ], - "type": "minecraft:crafting_shapeless", - "category": "misc", - "ingredients": [ - "#c:gems/diamond" - ], - "result": { - "count": 1, - "id": "minecraft:dirt" - } -} \ No newline at end of file diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/data/CustomFeatureFlagsTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/data/CustomFeatureFlagsTests.java index 0e852b9840..10419da329 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/data/CustomFeatureFlagsTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/data/CustomFeatureFlagsTests.java @@ -23,8 +23,7 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; -import net.neoforged.neoforge.common.Tags; -import net.neoforged.neoforge.common.conditions.FlagCondition; +import net.neoforged.neoforge.common.conditions.IConditionBuilder; import net.neoforged.neoforge.event.AddPackFindersEvent; import net.neoforged.neoforge.event.server.ServerStartedEvent; import net.neoforged.neoforge.registries.DeferredItem; @@ -110,27 +109,25 @@ static void testFlagCondition(DynamicTest test, RegistrationHelper reg) { var modId = reg.modId(); var enabledRecipeName = ResourceKey.create(Registries.RECIPE, ResourceLocation.fromNamespaceAndPath(modId, "diamonds_from_dirt")); - var disabledRecipeName = ResourceKey.create(Registries.RECIPE, ResourceLocation.fromNamespaceAndPath(modId, "dirt_from_diamonds")); reg.addProvider(event -> new RecipeProvider.Runner(event.getGenerator().getPackOutput(), event.getLookupProvider()) { @Override protected RecipeProvider createRecipeProvider(HolderLookup.Provider registries, RecipeOutput output) { - return new RecipeProvider(registries, output) { + class Provider extends RecipeProvider implements IConditionBuilder { + protected Provider(HolderLookup.Provider p_360573_, RecipeOutput p_360872_) { + super(p_360573_, p_360872_); + } + @Override protected void buildRecipes() { // recipe available when above flag is enabled shapeless(RecipeCategory.MISC, Items.DIAMOND) .requires(ItemTags.DIRT) .unlockedBy("has_dirt", has(ItemTags.DIRT)) - .save(output.withConditions(FlagCondition.isEnabled(flag)), enabledRecipeName); - - // recipe available when above flag is disabled - shapeless(RecipeCategory.MISC, Items.DIRT) - .requires(Tags.Items.GEMS_DIAMOND) - .unlockedBy("has_diamond", has(Tags.Items.GEMS_DIAMOND)) - .save(output.withConditions(FlagCondition.isDisabled(flag)), disabledRecipeName); + .save(output.withConditions(featureFlagsEnabled(flag)), enabledRecipeName); } - }; + } + return new Provider(registries, output); } @Override @@ -144,22 +141,15 @@ public String getName() { var isFlagEnabled = server.getWorldData().enabledFeatures().contains(flag); var recipeMap = server.getRecipeManager().recipeMap(); var hasEnabledRecipe = recipeMap.byKey(enabledRecipeName) != null; - var hasDisabledRecipe = recipeMap.byKey(disabledRecipeName) != null; if (isFlagEnabled) { if (!hasEnabledRecipe) { test.fail("Missing recipe '" + enabledRecipeName.location() + "', This should be enabled due to our flag '" + flagName + "' being enabled"); } - if (hasDisabledRecipe) { - test.fail("Found recipe '" + disabledRecipeName.location() + "', This should be disabled due to our flag '" + flagName + "' being disabled"); - } } else { if (hasEnabledRecipe) { test.fail("Found recipe '" + enabledRecipeName.location() + "', This should be disabled due to our flag '" + flagName + "' being enabled"); } - if (!hasDisabledRecipe) { - test.fail("Missing recipe '" + disabledRecipeName.location() + "', This should be enabled due to our flag '" + flagName + "' being disabled"); - } } test.pass();