mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-03 10:39:57 -04:00
Resource Conditions Additions (#2821)
* Resource Conditions Additions - Add `registry_contains` condition. Closes #2548. - Make `fabric:load_conditions` appear first in generated JSON objects. - Uniformize implementation a bit. * Update fabric-resource-conditions-api-v1/src/main/java/net/fabricmc/fabric/mixin/resource/conditions/DataProviderMixin.java Co-authored-by: Juuz <6596629+Juuxel@users.noreply.github.com> * itemsLoaded -> itemsRegistered --------- Co-authored-by: Juuz <6596629+Juuxel@users.noreply.github.com>
This commit is contained in:
parent
2ff127f537
commit
e63306e015
6 changed files with 215 additions and 30 deletions
fabric-data-generation-api-v1/src/testmod/java/net/fabricmc/fabric/test/datagen
fabric-resource-conditions-api-v1/src/main
java/net/fabricmc/fabric
api/resource/conditions/v1
impl/resource/conditions
mixin/resource/conditions
resources
|
@ -37,6 +37,7 @@ import org.slf4j.LoggerFactory;
|
|||
import net.minecraft.advancement.Advancement;
|
||||
import net.minecraft.advancement.AdvancementFrame;
|
||||
import net.minecraft.advancement.criterion.OnKilledCriterion;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.data.client.BlockStateModelGenerator;
|
||||
import net.minecraft.data.client.ItemModelGenerator;
|
||||
|
@ -108,6 +109,13 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
|
|||
public void generate(Consumer<RecipeJsonProvider> exporter) {
|
||||
offerPlanksRecipe2(exporter, SIMPLE_BLOCK, ItemTags.ACACIA_LOGS, 1);
|
||||
|
||||
ShapelessRecipeJsonBuilder.create(RecipeCategory.MISC, Items.LEATHER, 4).input(Items.ITEM_FRAME)
|
||||
.criterion("has_frame", conditionsFromItem(Items.ITEM_FRAME))
|
||||
.offerTo(withConditions(exporter, DefaultResourceConditions.itemsRegistered(Blocks.DIAMOND_BLOCK)));
|
||||
ShapelessRecipeJsonBuilder.create(RecipeCategory.MISC, Items.LEATHER_BOOTS, 4).input(Items.ITEM_FRAME, 2)
|
||||
.criterion("has_frame", conditionsFromItem(Items.ITEM_FRAME))
|
||||
.offerTo(withConditions(exporter, DefaultResourceConditions.registryContains(BiomeKeys.PLAINS, BiomeKeys.BADLANDS)));
|
||||
|
||||
ShapelessRecipeJsonBuilder.create(RecipeCategory.MISC, Items.GOLD_INGOT).input(Items.DIRT).criterion("has_dirt", conditionsFromItem(Items.DIRT)).offerTo(withConditions(exporter, NEVER_LOADED));
|
||||
ShapelessRecipeJsonBuilder.create(RecipeCategory.MISC, Items.DIAMOND).input(Items.STICK).criterion("has_stick", conditionsFromItem(Items.STICK)).offerTo(withConditions(exporter, ALWAYS_LOADED));
|
||||
|
||||
|
|
|
@ -16,12 +16,20 @@
|
|||
|
||||
package net.fabricmc.fabric.api.resource.conditions.v1;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.fluid.Fluid;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemConvertible;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.tag.TagKey;
|
||||
import net.minecraft.resource.featuretoggle.FeatureFlag;
|
||||
|
@ -44,6 +52,7 @@ public final class DefaultResourceConditions {
|
|||
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");
|
||||
private static final Identifier REGISTRY_CONTAINS = new Identifier("fabric:registry_contains");
|
||||
|
||||
/**
|
||||
* Creates a NOT condition that returns true if its child condition is false, and false if its child is true.
|
||||
|
@ -159,6 +168,54 @@ public final class DefaultResourceConditions {
|
|||
return ResourceConditionsImpl.featuresEnabled(FEATURES_ENABLED, features);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that returns true if all the passed items are registered (in {@link Registries#ITEM}).
|
||||
*
|
||||
* @see #registryContains(RegistryKey, Identifier...)
|
||||
*/
|
||||
public static ConditionJsonProvider itemsRegistered(ItemConvertible... items) {
|
||||
return registryContains(Registries.ITEM, transform(items, ItemConvertible::asItem));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that returns true if the registry contains all the passed entries,
|
||||
* i.e. if all the passed registry entries are loaded.
|
||||
*
|
||||
* @see #registryContains(RegistryKey, Identifier...)
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <T> ConditionJsonProvider registryContains(Registry<T> registry, T... entries) {
|
||||
return registryContains(transform(entries, e -> {
|
||||
return registry.getKey(e).orElseThrow(() -> new IllegalArgumentException("Entry is not registered"));
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that returns true if all the passed registry entries are loaded.
|
||||
*
|
||||
* @see #registryContains(RegistryKey, Identifier...)
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static <T> ConditionJsonProvider registryContains(RegistryKey<T>... entries) {
|
||||
Preconditions.checkArgument(entries.length > 0, "Must register at least one entry.");
|
||||
|
||||
return registryContains(
|
||||
RegistryKey.ofRegistry(entries[0].getRegistry()),
|
||||
transform(entries, RegistryKey::getValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that returns true if all the passed registry entries are loaded.
|
||||
* Dynamic registries are supported for server resources.
|
||||
*
|
||||
* @apiNote This condition's ID is {@code fabric:registry_contains}, and takes up to two properties:
|
||||
* {@code values}, which is an array of string registry entry IDs, and {@code registry}, which is the ID of
|
||||
* the registry of the entries. If {@code registry} is not provided, it defaults to {@code minecraft:item}.
|
||||
*/
|
||||
public static <T> ConditionJsonProvider registryContains(RegistryKey<Registry<T>> registry, Identifier... entries) {
|
||||
return ResourceConditionsImpl.registryContains(REGISTRY_CONTAINS, registry.getValue(), entries);
|
||||
}
|
||||
|
||||
static void init() {
|
||||
// init static
|
||||
}
|
||||
|
@ -183,6 +240,19 @@ public final class DefaultResourceConditions {
|
|||
ResourceConditions.register(ITEM_TAGS_POPULATED, object -> ResourceConditionsImpl.tagsPopulatedMatch(object, RegistryKeys.ITEM));
|
||||
ResourceConditions.register(TAGS_POPULATED, ResourceConditionsImpl::tagsPopulatedMatch);
|
||||
ResourceConditions.register(FEATURES_ENABLED, ResourceConditionsImpl::featuresEnabledMatch);
|
||||
ResourceConditions.register(REGISTRY_CONTAINS, ResourceConditionsImpl::registryContainsMatch);
|
||||
}
|
||||
|
||||
// Slightly gross - the empty outputType vararg is used to capture the correct type for B[]
|
||||
@SafeVarargs
|
||||
private static <A, B> B[] transform(A[] input, Function<A, B> mapper, B... outputType) {
|
||||
B[] output = Arrays.copyOf(outputType, input.length);
|
||||
|
||||
for (int i = 0; i < input.length; i++) {
|
||||
output[i] = mapper.apply(input[i]);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private DefaultResourceConditions() {
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.util.Collection;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
|
@ -32,6 +33,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.minecraft.registry.DynamicRegistryManager;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
|
@ -126,6 +128,55 @@ public final class ResourceConditionsImpl {
|
|||
};
|
||||
}
|
||||
|
||||
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 ConditionJsonProvider registryContains(Identifier id, Identifier registry, Identifier... entries) {
|
||||
Preconditions.checkArgument(entries.length > 0, "Must register at least one entry.");
|
||||
|
||||
return new ConditionJsonProvider() {
|
||||
@Override
|
||||
public Identifier getConditionId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeParameters(JsonObject object) {
|
||||
JsonArray array = new JsonArray();
|
||||
|
||||
for (Identifier entry : entries) {
|
||||
array.add(entry.toString());
|
||||
}
|
||||
|
||||
object.add("values", array);
|
||||
|
||||
if (!RegistryKeys.ITEM.getValue().equals(registry)) {
|
||||
// Skip if this is the default (minecraft:item)
|
||||
object.addProperty("registry", registry.toString());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Condition implementations
|
||||
|
||||
public static boolean modsLoadedMatch(JsonObject object, boolean and) {
|
||||
|
@ -165,10 +216,6 @@ public final class ResourceConditionsImpl {
|
|||
LOADED_TAGS.set(tagMap);
|
||||
}
|
||||
|
||||
public static void clearTags() {
|
||||
LOADED_TAGS.remove();
|
||||
}
|
||||
|
||||
public static boolean tagsPopulatedMatch(JsonObject object) {
|
||||
String key = JsonHelper.getString(object, "registry", "minecraft:item");
|
||||
RegistryKey<? extends Registry<?>> registryRef = RegistryKey.ofRegistry(new Identifier(key));
|
||||
|
@ -208,29 +255,7 @@ 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 final ThreadLocal<FeatureSet> CURRENT_FEATURES = 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();
|
||||
|
@ -238,6 +263,45 @@ public final class ResourceConditionsImpl {
|
|||
throw new JsonParseException("Unknown feature flag: " + id);
|
||||
});
|
||||
|
||||
return set.isSubsetOf(currentFeature.get());
|
||||
return set.isSubsetOf(CURRENT_FEATURES.get());
|
||||
}
|
||||
|
||||
public static final ThreadLocal<DynamicRegistryManager.Immutable> CURRENT_REGISTRIES = new ThreadLocal<>();
|
||||
|
||||
public static boolean registryContainsMatch(JsonObject object) {
|
||||
String key = JsonHelper.getString(object, "registry", "minecraft:item");
|
||||
RegistryKey<? extends Registry<?>> registryRef = RegistryKey.ofRegistry(new Identifier(key));
|
||||
return registryContainsMatch(object, registryRef);
|
||||
}
|
||||
|
||||
private static <E> boolean registryContainsMatch(JsonObject object, RegistryKey<? extends Registry<? extends E>> registryRef) {
|
||||
JsonArray array = JsonHelper.getArray(object, "values");
|
||||
DynamicRegistryManager.Immutable registries = CURRENT_REGISTRIES.get();
|
||||
|
||||
if (registries == null) {
|
||||
LOGGER.warn("Can't retrieve current registries. Failing registry_contains resource condition check.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional<Registry<E>> registry = registries.getOptional(registryRef);
|
||||
|
||||
if (registry.isEmpty()) {
|
||||
// No such registry
|
||||
return array.isEmpty();
|
||||
}
|
||||
|
||||
for (JsonElement element : array) {
|
||||
if (element.isJsonPrimitive()) {
|
||||
Identifier id = new Identifier(element.getAsString());
|
||||
|
||||
if (!registry.get().containsId(id)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
throw new JsonParseException("Invalid registry entry id: " + element);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,8 @@ public class DataPackContentsMixin {
|
|||
at = @At("HEAD")
|
||||
)
|
||||
public void hookRefresh(DynamicRegistryManager dynamicRegistryManager, CallbackInfo ci) {
|
||||
ResourceConditionsImpl.clearTags();
|
||||
ResourceConditionsImpl.LOADED_TAGS.remove();
|
||||
ResourceConditionsImpl.CURRENT_REGISTRIES.remove();
|
||||
}
|
||||
|
||||
@Inject(
|
||||
|
@ -52,6 +53,7 @@ public class DataPackContentsMixin {
|
|||
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);
|
||||
ResourceConditionsImpl.CURRENT_FEATURES.set(enabledFeatures);
|
||||
ResourceConditionsImpl.CURRENT_REGISTRIES.set(dynamicRegistryManager);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.resource.conditions;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import org.spongepowered.asm.mixin.Dynamic;
|
||||
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 net.minecraft.data.DataProvider;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.ResourceConditions;
|
||||
|
||||
/**
|
||||
* Make the {@value ResourceConditions#CONDITIONS_KEY} appear first in generated JSON objects.
|
||||
*/
|
||||
@Mixin(DataProvider.class)
|
||||
public interface DataProviderMixin {
|
||||
@Dynamic("lambda method passed to Util.make")
|
||||
@Inject(method = "method_43808", at = @At("HEAD"))
|
||||
private static void fabric_injectResourceConditionsSortOrder(Object2IntOpenHashMap<String> map, CallbackInfo ci) {
|
||||
map.put(ResourceConditions.CONDITIONS_KEY, -100);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
"compatibilityLevel": "JAVA_16",
|
||||
"mixins": [
|
||||
"DataPackContentsMixin",
|
||||
"DataProviderMixin",
|
||||
"JsonDataLoaderMixin",
|
||||
"SinglePreparationResourceReloaderMixin",
|
||||
"TagManagerLoaderMixin"
|
||||
|
|
Loading…
Add table
Reference in a new issue