From 280be3abc9db0bd70cc116cd61af833454bd9120 Mon Sep 17 00:00:00 2001 From: apple502j <33279053+apple502j@users.noreply.github.com> Date: Sun, 27 Nov 2022 05:00:49 +0900 Subject: [PATCH] Resource Conditions: add feature_enabled (#2658) * Resource Conditions: add feature_enabled * Fix impl * Some refactors * Address reviews * Update testmod * Fix checkstyle * Move javadoc * Sort identifiers --- .../v1/DefaultResourceConditions.java | 16 ++++++- .../conditions/ResourceConditionsImpl.java | 44 +++++++++++++++++-- .../conditions/DataPackContentsMixin.java | 25 ++++++++--- .../conditions/ConditionalResourcesTest.java | 6 ++- .../recipes/features_enabled.json | 20 +++++++++ 5 files changed, 101 insertions(+), 10 deletions(-) create mode 100644 fabric-resource-conditions-api-v1/src/testmod/resources/data/fabric-resource-conditions-api-v1-testmod/recipes/features_enabled.json diff --git a/fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/api/resource/conditions/v1/DefaultResourceConditions.java b/fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/api/resource/conditions/v1/DefaultResourceConditions.java index b57d6a562..8e5c029df 100644 --- a/fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/api/resource/conditions/v1/DefaultResourceConditions.java +++ b/fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/api/resource/conditions/v1/DefaultResourceConditions.java @@ -20,10 +20,11 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import net.minecraft.block.Block; -import net.minecraft.registry.RegistryKeys; import net.minecraft.fluid.Fluid; import net.minecraft.item.Item; +import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.tag.TagKey; +import net.minecraft.resource.featuretoggle.FeatureFlag; import net.minecraft.util.Identifier; import net.minecraft.util.JsonHelper; @@ -42,6 +43,7 @@ public final class DefaultResourceConditions { private static final Identifier FLUID_TAGS_POPULATED = new Identifier("fabric:fluid_tags_populated"); private static final Identifier ITEM_TAGS_POPULATED = new Identifier("fabric:item_tags_populated"); private static final Identifier TAGS_POPULATED = new Identifier("fabric:tags_populated"); + private static final Identifier FEATURES_ENABLED = new Identifier("fabric:features_enabled"); /** * Creates a NOT condition that returns true if its child condition is false, and false if its child is true. @@ -146,6 +148,17 @@ public final class DefaultResourceConditions { return ResourceConditionsImpl.tagsPopulated(TAGS_POPULATED, true, tags); } + /** + * Creates a condition that returns true if all the passed features are enabled. + * @param features the features to check for + * + * @apiNote This condition's ID is {@code fabric:features_enabled}, and takes one property: + * {@code features}, which is the array of the IDs of the feature flag to check. + */ + public static ConditionJsonProvider featuresEnabled(FeatureFlag... features) { + return ResourceConditionsImpl.featuresEnabled(FEATURES_ENABLED, features); + } + static void init() { // init static } @@ -169,6 +182,7 @@ public final class DefaultResourceConditions { ResourceConditions.register(FLUID_TAGS_POPULATED, object -> ResourceConditionsImpl.tagsPopulatedMatch(object, RegistryKeys.FLUID)); ResourceConditions.register(ITEM_TAGS_POPULATED, object -> ResourceConditionsImpl.tagsPopulatedMatch(object, RegistryKeys.ITEM)); ResourceConditions.register(TAGS_POPULATED, ResourceConditionsImpl::tagsPopulatedMatch); + ResourceConditions.register(FEATURES_ENABLED, ResourceConditionsImpl::featuresEnabledMatch); } private DefaultResourceConditions() { diff --git a/fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/impl/resource/conditions/ResourceConditionsImpl.java b/fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/impl/resource/conditions/ResourceConditionsImpl.java index 0c51786e6..1525edfa7 100644 --- a/fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/impl/resource/conditions/ResourceConditionsImpl.java +++ b/fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/impl/resource/conditions/ResourceConditionsImpl.java @@ -20,6 +20,8 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.TreeSet; import com.google.common.base.Preconditions; import com.google.gson.JsonArray; @@ -31,14 +33,17 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; import net.minecraft.registry.RegistryKeys; +import net.minecraft.registry.entry.RegistryEntry; import net.minecraft.registry.tag.TagKey; import net.minecraft.registry.tag.TagManagerLoader; +import net.minecraft.resource.featuretoggle.FeatureFlag; +import net.minecraft.resource.featuretoggle.FeatureFlags; +import net.minecraft.resource.featuretoggle.FeatureSet; import net.minecraft.util.Identifier; import net.minecraft.util.JsonHelper; -import net.minecraft.registry.Registry; -import net.minecraft.registry.entry.RegistryEntry; -import net.minecraft.registry.RegistryKey; import net.fabricmc.fabric.api.resource.conditions.v1.ConditionJsonProvider; import net.fabricmc.loader.api.FabricLoader; @@ -204,4 +209,37 @@ public final class ResourceConditionsImpl { return true; } + + public static ConditionJsonProvider featuresEnabled(Identifier id, final FeatureFlag... features) { + final Set<Identifier> ids = new TreeSet<>(FeatureFlags.FEATURE_MANAGER.toId(FeatureFlags.FEATURE_MANAGER.featureSetOf(features))); + + return new ConditionJsonProvider() { + @Override + public Identifier getConditionId() { + return id; + } + + @Override + public void writeParameters(JsonObject object) { + JsonArray array = new JsonArray(); + + for (Identifier id : ids) { + array.add(id.toString()); + } + + object.add("features", array); + } + }; + } + + public static ThreadLocal<FeatureSet> currentFeature = ThreadLocal.withInitial(() -> FeatureFlags.DEFAULT_ENABLED_FEATURES); + + public static boolean featuresEnabledMatch(JsonObject object) { + List<Identifier> featureIds = JsonHelper.getArray(object, "features").asList().stream().map((element) -> new Identifier(element.getAsString())).toList(); + FeatureSet set = FeatureFlags.FEATURE_MANAGER.featureSetOf(featureIds, (id) -> { + throw new JsonParseException("Unknown feature flag: " + id); + }); + + return set.isSubsetOf(currentFeature.get()); + } } diff --git a/fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/mixin/resource/conditions/DataPackContentsMixin.java b/fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/mixin/resource/conditions/DataPackContentsMixin.java index 25e0bc25d..1eb18b40a 100644 --- a/fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/mixin/resource/conditions/DataPackContentsMixin.java +++ b/fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/mixin/resource/conditions/DataPackContentsMixin.java @@ -16,22 +16,29 @@ package net.fabricmc.fabric.mixin.resource.conditions; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import net.minecraft.server.DataPackContents; import net.minecraft.registry.DynamicRegistryManager; +import net.minecraft.resource.ResourceManager; +import net.minecraft.resource.featuretoggle.FeatureSet; +import net.minecraft.server.DataPackContents; +import net.minecraft.server.command.CommandManager; import net.fabricmc.fabric.impl.resource.conditions.ResourceConditionsImpl; -/** - * Clear the tags captured by {@link DataPackContentsMixin}. - * This must happen after the resource reload is complete, to ensure that the tags remain available throughout the entire "apply" phase. - */ @Mixin(DataPackContents.class) public class DataPackContentsMixin { + /** + * Clear the tags captured by {@link DataPackContentsMixin}. + * This must happen after the resource reload is complete, to ensure that the tags remain available throughout the entire "apply" phase. + */ @Inject( method = "refresh", at = @At("HEAD") @@ -39,4 +46,12 @@ public class DataPackContentsMixin { public void hookRefresh(DynamicRegistryManager dynamicRegistryManager, CallbackInfo ci) { ResourceConditionsImpl.clearTags(); } + + @Inject( + method = "reload", + at = @At("HEAD") + ) + private static void hookReload(ResourceManager manager, DynamicRegistryManager.Immutable dynamicRegistryManager, FeatureSet enabledFeatures, CommandManager.RegistrationEnvironment environment, int functionPermissionLevel, Executor prepareExecutor, Executor applyExecutor, CallbackInfoReturnable<CompletableFuture<DataPackContents>> cir) { + ResourceConditionsImpl.currentFeature.set(enabledFeatures); + } } diff --git a/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/ConditionalResourcesTest.java b/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/ConditionalResourcesTest.java index 3484953f9..707d704bc 100644 --- a/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/ConditionalResourcesTest.java +++ b/fabric-resource-conditions-api-v1/src/testmod/java/net/fabricmc/fabric/test/resource/conditions/ConditionalResourcesTest.java @@ -58,8 +58,12 @@ public class ConditionalResourcesTest { throw new AssertionError("tags_not_populated recipe should not have been loaded."); } + if (manager.get(id("features_enabled")).isEmpty()) { + throw new AssertionError("features_enabled recipe should have been loaded."); + } + long loadedRecipes = manager.values().stream().filter(r -> r.getId().getNamespace().equals(MOD_ID)).count(); - if (loadedRecipes != 4) throw new AssertionError("Unexpected loaded recipe count: " + loadedRecipes); + if (loadedRecipes != 5) throw new AssertionError("Unexpected loaded recipe count: " + loadedRecipes); context.complete(); } diff --git a/fabric-resource-conditions-api-v1/src/testmod/resources/data/fabric-resource-conditions-api-v1-testmod/recipes/features_enabled.json b/fabric-resource-conditions-api-v1/src/testmod/resources/data/fabric-resource-conditions-api-v1-testmod/recipes/features_enabled.json new file mode 100644 index 000000000..833b55006 --- /dev/null +++ b/fabric-resource-conditions-api-v1/src/testmod/resources/data/fabric-resource-conditions-api-v1-testmod/recipes/features_enabled.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { + "item": "minecraft:blue_dye" + } + ], + "result": { + "item": "minecraft:diamond" + }, + "fabric:load_conditions": [ + { + "condition": "fabric:features_enabled", + "features": [ + "minecraft:vanilla", + "minecraft:bundle" + ] + } + ] +}