Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.21.3] Add condition to validate feature flags enabled state #1712

Merged
merged 6 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
this.functionLibrary = new ServerFunctionLibrary(p_206859_, this.commands.getDispatcher());
+ // Neo: Store registries and create context object
+ this.registryLookup = p_361583_;
+ this.context = new net.neoforged.neoforge.common.conditions.ConditionContext(this.postponedTags);
+ this.context = new net.neoforged.neoforge.common.conditions.ConditionContext(this.postponedTags, p_250695_);
}

public ServerFunctionLibrary getFunctionLibrary() {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +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.ICondition;
import net.neoforged.neoforge.common.conditions.ItemExistsCondition;
import net.neoforged.neoforge.common.conditions.ModLoadedCondition;
Expand Down Expand Up @@ -385,6 +386,7 @@ 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);
public static final DeferredHolder<MapCodec<? extends ICondition>, MapCodec<FlagCondition>> FEATURE_FLAG_CONDITION = CONDITION_CODECS.register("feature_flags", () -> FlagCondition.CODEC);

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
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,30 @@
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.tags.TagKey;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.flag.FeatureFlags;
import org.jetbrains.annotations.ApiStatus;

public class ConditionContext implements ICondition.IContext {
private final Map<ResourceKey<? extends Registry<?>>, HolderLookup.RegistryLookup<?>> pendingTags;
private final FeatureFlagSet enabledFeatures;

public ConditionContext(List<Registry.PendingTags<?>> pendingTags) {
public ConditionContext(List<Registry.PendingTags<?>> pendingTags, FeatureFlagSet enabledFeatures) {
this.pendingTags = new IdentityHashMap<>();
this.enabledFeatures = enabledFeatures;

for (var tags : pendingTags) {
this.pendingTags.put(tags.key(), tags.lookup());
}
}

// Use FeatureFlagSet sensitive constructor
@ApiStatus.ScheduledForRemoval(inVersion = "1.21.4")
@Deprecated(forRemoval = true, since = "1.21.3")
public ConditionContext(List<Registry.PendingTags<?>> pendingTags) {
this(pendingTags, FeatureFlags.VANILLA_SET);
}

public void clear() {
this.pendingTags.clear();
}
Expand All @@ -33,4 +46,9 @@ public <T> boolean isTagLoaded(TagKey<T> key) {
var lookup = pendingTags.get(key.registry());
return lookup != null && lookup.get((TagKey) key).isPresent();
}

@Override
public FeatureFlagSet enabledFeatures() {
return enabledFeatures;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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}.
* <p>
* {@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<FlagCondition> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
FeatureFlags.CODEC.fieldOf("flags").forGetter(condition -> condition.requiredFeatures),
Codec.BOOL.lenientOptionalFieldOf("check_enabled", true).forGetter(condition -> condition.expectedResult)).apply(instance, FlagCondition::new));
ApexModder marked this conversation as resolved.
Show resolved Hide resolved

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<? extends ICondition> 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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
import net.minecraft.resources.RegistryOps;
import net.minecraft.tags.TagKey;
import net.minecraft.util.Unit;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.flag.FeatureFlags;
import net.neoforged.neoforge.registries.NeoForgeRegistries;
import net.neoforged.neoforge.server.ServerLifecycleHooks;

public interface ICondition {
Codec<ICondition> CODEC = NeoForgeRegistries.CONDITION_SERIALIZERS.byNameCodec()
Expand Down Expand Up @@ -91,5 +94,15 @@ public <T> boolean isTagLoaded(TagKey<T> key) {
* Returns {@code true} if the requested tag is available.
*/
<T> boolean isTagLoaded(TagKey<T> key);

default FeatureFlagSet enabledFeatures() {
// returning the vanilla set causes reports false positives for flags outside of vanilla
// return FeatureFlags.VANILLA_SET;

// lookup the active enabledFeatures from the current server
// if no server exists, delegating back to 'VANILLA_SET' should be fine (should rarely ever happen)
var server = ServerLifecycleHooks.getCurrentServer();
return server == null ? FeatureFlags.VANILLA_SET : server.getWorldData().enabledFeatures();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import java.util.List;
import net.minecraft.tags.TagKey;
import net.minecraft.world.flag.FeatureFlag;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.item.Item;

public interface IConditionBuilder {
Expand Down Expand Up @@ -41,4 +43,28 @@ default ICondition modLoaded(String modid) {
default ICondition tagEmpty(TagKey<Item> tag) {
return new TagEmptyCondition(tag.location());
}

default ICondition isFeatureEnabled(FeatureFlagSet requiredFeatures) {
return FlagCondition.isEnabled(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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"neoforge:conditions": [
{
"type": "neoforge:feature_flags",
"flags": [
"custom_feature_flags_pack_test:test_flag"
]
}
],
"parent": "minecraft:recipes/root",
"criteria": {
"has_dirt": {
"conditions": {
"items": [
{
"items": "#minecraft:dirt"
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "neotests_test_flag_condition:diamonds_from_dirt"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_the_recipe",
"has_dirt"
]
],
"rewards": {
"recipes": [
"neotests_test_flag_condition:diamonds_from_dirt"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"neoforge:conditions": [
{
"type": "neoforge:feature_flags",
"check_enabled": 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"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"neoforge:conditions": [
{
"type": "neoforge:feature_flags",
"flags": [
"custom_feature_flags_pack_test:test_flag"
]
}
],
"type": "minecraft:crafting_shapeless",
"category": "misc",
"ingredients": [
"#minecraft:dirt"
],
"result": {
"count": 1,
"id": "minecraft:diamond"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"neoforge:conditions": [
{
"type": "neoforge:feature_flags",
"check_enabled": 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"
}
}
Loading