Skip to content

Commit

Permalink
[1.21.3] Add condition to validate feature flags enabled state (#1712)
Browse files Browse the repository at this point in the history
Co-authored-by: Marc Hermans <[email protected]>
  • Loading branch information
ApexModder and marchermans authored Nov 27, 2024
1 parent cc64d63 commit c0dbe21
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 2 deletions.
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("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<? 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",
"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"
]
}
}
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",
"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"
}
}
Loading

0 comments on commit c0dbe21

Please sign in to comment.