mirror of
https://github.com/FabricMC/fabric.git
synced 2025-07-29 15:39:55 -04:00
Fabric Resource Conditions (#1656)
* First completed draft * Update PR, should be close to ready now * Add fluid/item_tags_populated conditions * Log processed resource with trace log level * Use debug instead of trace log level * Apply suggestions from code review Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com> * Code review suggestions * writeAdditional -> writeParameters * Apply suggestions from code review Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com> * CONDITION_ID -> CONDITION_ID_KEY
This commit is contained in:
parent
eb6d303987
commit
12540453db
21 changed files with 871 additions and 8 deletions
fabric-data-generation-api-v1
build.gradle
src
main/java/net/fabricmc/fabric
api/datagen/v1/provider
impl/datagen
testmod/java/net/fabricmc/fabric/test/datagen
fabric-resource-conditions-api-v1
build.gradle
gradle.propertiessettings.gradlesrc
main
java/net/fabricmc/fabric
api/resource/conditions/v1
impl/resource/conditions
mixin/resource/conditions
resources
assets/fabric-resource-conditions-api-v1
fabric-resource-conditions-api-v1.mixins.jsonfabric.mod.jsontestmod
java/net/fabricmc/fabric/test/resource/conditions
resources
|
@ -4,7 +4,8 @@ version = getSubprojectVersion(project)
|
|||
moduleDependencies(project, [
|
||||
'fabric-api-base',
|
||||
'fabric-registry-sync-v0',
|
||||
'fabric-networking-api-v1'
|
||||
'fabric-networking-api-v1',
|
||||
'fabric-resource-conditions-api-v1'
|
||||
])
|
||||
|
||||
dependencies {
|
||||
|
|
|
@ -21,9 +21,11 @@ import java.nio.file.Path;
|
|||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import net.minecraft.advancement.Advancement;
|
||||
import net.minecraft.data.DataCache;
|
||||
|
@ -31,6 +33,8 @@ import net.minecraft.data.DataProvider;
|
|||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.ConditionJsonProvider;
|
||||
import net.fabricmc.fabric.impl.datagen.FabricDataGenHelper;
|
||||
|
||||
/**
|
||||
* Extend this class and implement {@link FabricAdvancementsProvider#generateAdvancement}.
|
||||
|
@ -53,6 +57,17 @@ public abstract class FabricAdvancementsProvider implements DataProvider {
|
|||
*/
|
||||
public abstract void generateAdvancement(Consumer<Advancement> consumer);
|
||||
|
||||
/**
|
||||
* Return a new exporter that applies the specified conditions to any advancement it receives.
|
||||
*/
|
||||
protected Consumer<Advancement> withConditions(Consumer<Advancement> exporter, ConditionJsonProvider... conditions) {
|
||||
Preconditions.checkArgument(conditions.length > 0, "Must add at least one condition.");
|
||||
return advancement -> {
|
||||
FabricDataGenHelper.addConditions(advancement, conditions);
|
||||
exporter.accept(advancement);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(DataCache cache) throws IOException {
|
||||
final Set<Identifier> identifiers = Sets.newHashSet();
|
||||
|
@ -65,7 +80,10 @@ public abstract class FabricAdvancementsProvider implements DataProvider {
|
|||
throw new IllegalStateException("Duplicate advancement " + advancement.getId());
|
||||
}
|
||||
|
||||
DataProvider.writeToPath(GSON, cache, advancement.createTask().toJson(), getOutputPath(advancement));
|
||||
JsonObject advancementJson = advancement.createTask().toJson();
|
||||
ConditionJsonProvider.write(advancementJson, FabricDataGenHelper.consumeConditions(advancement));
|
||||
|
||||
DataProvider.writeToPath(GSON, cache, advancementJson, getOutputPath(advancement));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,9 +23,11 @@ import java.util.Map;
|
|||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import net.minecraft.data.DataCache;
|
||||
|
@ -36,6 +38,8 @@ import net.minecraft.loot.context.LootContextType;
|
|||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.ConditionJsonProvider;
|
||||
import net.fabricmc.fabric.impl.datagen.FabricDataGenHelper;
|
||||
|
||||
/**
|
||||
* A base interface for Loot table providers. You should not implement this class directly.
|
||||
|
@ -52,19 +56,37 @@ public interface FabricLootTableProvider extends Consumer<BiConsumer<Identifier,
|
|||
|
||||
FabricDataGenerator getFabricDataGenerator();
|
||||
|
||||
/**
|
||||
* Return a new exporter that applies the specified conditions to any loot table it receives.
|
||||
*/
|
||||
default BiConsumer<Identifier, LootTable.Builder> withConditions(BiConsumer<Identifier, LootTable.Builder> exporter, ConditionJsonProvider... conditions) {
|
||||
Preconditions.checkArgument(conditions.length > 0, "Must add at least one condition.");
|
||||
return (id, table) -> {
|
||||
FabricDataGenHelper.addConditions(table, conditions);
|
||||
exporter.accept(id, table);
|
||||
};
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Override
|
||||
default void run(DataCache cache) throws IOException {
|
||||
HashMap<Identifier, LootTable> builders = Maps.newHashMap();
|
||||
HashMap<Identifier, ConditionJsonProvider[]> conditionMap = new HashMap<>();
|
||||
|
||||
accept((identifier, builder) -> {
|
||||
ConditionJsonProvider[] conditions = FabricDataGenHelper.consumeConditions(builder);
|
||||
conditionMap.put(identifier, conditions);
|
||||
|
||||
if (builders.put(identifier, builder.type(getLootContextType()).build()) != null) {
|
||||
throw new IllegalStateException("Duplicate loot table " + identifier);
|
||||
}
|
||||
});
|
||||
|
||||
for (Map.Entry<Identifier, LootTable> entry : builders.entrySet()) {
|
||||
DataProvider.writeToPath(GSON, cache, LootManager.toJson(entry.getValue()), getOutputPath(entry.getKey()));
|
||||
JsonObject tableJson = (JsonObject) LootManager.toJson(entry.getValue());
|
||||
ConditionJsonProvider.write(tableJson, conditionMap.remove(entry.getKey()));
|
||||
|
||||
DataProvider.writeToPath(GSON, cache, tableJson, getOutputPath(entry.getKey()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.nio.file.Path;
|
|||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
|
@ -31,6 +32,8 @@ import net.minecraft.data.server.recipe.ShapelessRecipeJsonFactory;
|
|||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.ConditionJsonProvider;
|
||||
import net.fabricmc.fabric.impl.datagen.FabricDataGenHelper;
|
||||
|
||||
/**
|
||||
* Extend this class and implement {@link FabricRecipesProvider#generateRecipes}.
|
||||
|
@ -50,6 +53,17 @@ public abstract class FabricRecipesProvider extends RecipesProvider {
|
|||
*/
|
||||
protected abstract void generateRecipes(Consumer<RecipeJsonProvider> exporter);
|
||||
|
||||
/**
|
||||
* Return a new exporter that applies the specified conditions to any recipe json provider it receives.
|
||||
*/
|
||||
protected Consumer<RecipeJsonProvider> withConditions(Consumer<RecipeJsonProvider> exporter, ConditionJsonProvider... conditions) {
|
||||
Preconditions.checkArgument(conditions.length > 0, "Must add at least one condition.");
|
||||
return json -> {
|
||||
FabricDataGenHelper.addConditions(json, conditions);
|
||||
exporter.accept(json);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(DataCache cache) {
|
||||
Path path = this.root.getOutput();
|
||||
|
@ -61,11 +75,16 @@ public abstract class FabricRecipesProvider extends RecipesProvider {
|
|||
throw new IllegalStateException("Duplicate recipe " + identifier);
|
||||
}
|
||||
|
||||
saveRecipe(cache, provider.toJson(), path.resolve("data/" + identifier.getNamespace() + "/recipes/" + identifier.getPath() + ".json"));
|
||||
JsonObject jsonObject = provider.toAdvancementJson();
|
||||
JsonObject recipeJson = provider.toJson();
|
||||
ConditionJsonProvider[] conditions = FabricDataGenHelper.consumeConditions(provider);
|
||||
ConditionJsonProvider.write(recipeJson, conditions);
|
||||
|
||||
if (jsonObject != null) {
|
||||
saveRecipeAdvancement(cache, jsonObject, path.resolve("data/" + identifier.getNamespace() + "/advancements/" + provider.getAdvancementId().getPath() + ".json"));
|
||||
saveRecipe(cache, recipeJson, path.resolve("data/" + identifier.getNamespace() + "/recipes/" + identifier.getPath() + ".json"));
|
||||
JsonObject advancementJson = provider.toAdvancementJson();
|
||||
|
||||
if (advancementJson != null) {
|
||||
ConditionJsonProvider.write(advancementJson, conditions);
|
||||
saveRecipeAdvancement(cache, advancementJson, path.resolve("data/" + identifier.getNamespace() + "/advancements/" + provider.getAdvancementId().getPath() + ".json"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -19,10 +19,13 @@ package net.fabricmc.fabric.impl.datagen;
|
|||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
@ -37,6 +40,7 @@ import net.minecraft.util.registry.SimpleRegistry;
|
|||
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider.DynamicRegistryTagProvider;
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.ConditionJsonProvider;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.entrypoint.EntrypointContainer;
|
||||
|
||||
|
@ -117,4 +121,18 @@ public final class FabricDataGenHelper {
|
|||
public static <T> Registry<T> getFakeDynamicRegistry() {
|
||||
return FAKE_DYNAMIC_REGISTRY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to keep track of conditions associated to generated objects.
|
||||
*/
|
||||
private static final Map<Object, ConditionJsonProvider[]> CONDITIONS_MAP = new IdentityHashMap<>();
|
||||
|
||||
public static void addConditions(Object object, ConditionJsonProvider[] conditions) {
|
||||
CONDITIONS_MAP.merge(object, conditions, ArrayUtils::addAll);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ConditionJsonProvider[] consumeConditions(Object object) {
|
||||
return CONDITIONS_MAP.remove(object);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ import net.minecraft.advancement.criterion.OnKilledCriterion;
|
|||
import net.minecraft.data.client.ItemModelGenerator;
|
||||
import net.minecraft.data.client.model.BlockStateModelGenerator;
|
||||
import net.minecraft.data.server.recipe.RecipeJsonProvider;
|
||||
import net.minecraft.data.server.recipe.ShapelessRecipeJsonFactory;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.loot.LootPool;
|
||||
import net.minecraft.loot.LootTable;
|
||||
import net.minecraft.loot.LootTables;
|
||||
|
@ -53,12 +55,18 @@ import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockStateDefinitionPro
|
|||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipesProvider;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.SimpleFabricLootTableProvider;
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.ConditionJsonProvider;
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.DefaultResourceConditions;
|
||||
import net.fabricmc.fabric.api.tag.TagFactory;
|
||||
|
||||
public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
|
||||
private static final ConditionJsonProvider NEVER_LOADED = DefaultResourceConditions.allModsLoaded("a");
|
||||
private static final ConditionJsonProvider ALWAYS_LOADED = DefaultResourceConditions.not(NEVER_LOADED);
|
||||
|
||||
@Override
|
||||
public void onInitializeDataGenerator(FabricDataGenerator dataGenerator) {
|
||||
dataGenerator.addProvider(TestRecipeProvider::new);
|
||||
dataGenerator.addProvider(TestConditionalRecipeProvider::new);
|
||||
dataGenerator.addProvider(TestBlockStateDefinitionProvider::new);
|
||||
dataGenerator.addProvider(TestAdvancementsProvider::new);
|
||||
dataGenerator.addProvider(TestBlockLootTablesProvider::new);
|
||||
|
@ -102,6 +110,18 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
|
|||
}
|
||||
}
|
||||
|
||||
private static class TestConditionalRecipeProvider extends FabricRecipesProvider {
|
||||
private TestConditionalRecipeProvider(FabricDataGenerator dataGenerator) {
|
||||
super(dataGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void generateRecipes(Consumer<RecipeJsonProvider> exporter) {
|
||||
ShapelessRecipeJsonFactory.create(Items.GOLD_INGOT).input(Items.DIRT).criterion("has_dirt", conditionsFromItem(Items.DIRT)).offerTo(withConditions(exporter, NEVER_LOADED));
|
||||
ShapelessRecipeJsonFactory.create(Items.DIAMOND).input(Items.STICK).criterion("has_stick", conditionsFromItem(Items.STICK)).offerTo(withConditions(exporter, ALWAYS_LOADED));
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestBlockStateDefinitionProvider extends FabricBlockStateDefinitionProvider {
|
||||
private TestBlockStateDefinitionProvider(FabricDataGenerator generator) {
|
||||
super(generator);
|
||||
|
@ -180,6 +200,16 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
|
|||
false, false, false)
|
||||
.criterion("killed_something", OnKilledCriterion.Conditions.createPlayerKilledEntity())
|
||||
.build(consumer, MOD_ID + ":test/root");
|
||||
Advancement rootNotLoaded = Advancement.Task.create()
|
||||
.display(
|
||||
SIMPLE_BLOCK,
|
||||
new TranslatableText("advancements.test.root_not_loaded.title"),
|
||||
new TranslatableText("advancements.test.root_not_loaded.description"),
|
||||
new Identifier("textures/gui/advancements/backgrounds/end.png"),
|
||||
AdvancementFrame.TASK,
|
||||
false, false, false)
|
||||
.criterion("killed_something", OnKilledCriterion.Conditions.createPlayerKilledEntity())
|
||||
.build(withConditions(consumer, NEVER_LOADED), MOD_ID + ":test/root_not_loaded");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,7 +232,7 @@ public class DataGeneratorTestEntrypoint implements DataGeneratorEntrypoint {
|
|||
|
||||
@Override
|
||||
public void accept(BiConsumer<Identifier, LootTable.Builder> consumer) {
|
||||
consumer.accept(
|
||||
withConditions(consumer, ALWAYS_LOADED).accept(
|
||||
LootTables.PIGLIN_BARTERING_GAMEPLAY,
|
||||
LootTable.builder().pool(
|
||||
LootPool.builder().rolls(ConstantLootNumberProvider.create(1.0F)).with(ItemEntry.builder(SIMPLE_BLOCK))
|
||||
|
|
6
fabric-resource-conditions-api-v1/build.gradle
Normal file
6
fabric-resource-conditions-api-v1/build.gradle
Normal file
|
@ -0,0 +1,6 @@
|
|||
archivesBaseName = "fabric-resource-conditions-api-v1"
|
||||
version = getSubprojectVersion(project)
|
||||
|
||||
dependencies {
|
||||
testmodImplementation project(path: ':fabric-gametest-api-v1', configuration: 'namedElements')
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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.api.resource.conditions.v1;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
/**
|
||||
* A resource condition and its parameters that can be serialized to JSON, meant for use in data generators.
|
||||
*/
|
||||
public interface ConditionJsonProvider {
|
||||
/**
|
||||
* Write the passed conditions to a JSON object in the {@value ResourceConditions#CONDITIONS_KEY} array.
|
||||
*
|
||||
* @throws IllegalArgumentException if the JSON object already contains that array
|
||||
*/
|
||||
static void write(JsonObject conditionalObject, ConditionJsonProvider @Nullable... conditions) {
|
||||
if (conditions == null) { // no condition -> skip
|
||||
return;
|
||||
}
|
||||
|
||||
Preconditions.checkArgument(conditions.length > 0, "Must write at least one condition."); // probably a programmer error
|
||||
if (conditionalObject.has(ResourceConditions.CONDITIONS_KEY)) throw new IllegalArgumentException("Object already has a condition entry: " + conditionalObject);
|
||||
|
||||
JsonArray array = new JsonArray();
|
||||
|
||||
for (ConditionJsonProvider condition : conditions) {
|
||||
array.add(condition.toJson());
|
||||
}
|
||||
|
||||
conditionalObject.add(ResourceConditions.CONDITIONS_KEY, array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize this condition and its parameters to a new JSON object.
|
||||
*/
|
||||
default JsonObject toJson() {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty(ResourceConditions.CONDITION_ID_KEY, getConditionId().toString());
|
||||
this.writeParameters(jsonObject);
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the identifier of this condition} This is only for use by {@link #toJson()} to write it.
|
||||
*/
|
||||
Identifier getConditionId();
|
||||
|
||||
/**
|
||||
* Write the condition parameters (everything except the {@code "condition": ...} entry). This is only for use by {@link #toJson()}.
|
||||
*/
|
||||
void writeParameters(JsonObject object);
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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.api.resource.conditions.v1;
|
||||
|
||||
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.tag.BlockTags;
|
||||
import net.minecraft.tag.FluidTags;
|
||||
import net.minecraft.tag.ItemTags;
|
||||
import net.minecraft.tag.Tag;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.JsonHelper;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.conditions.ResourceConditionsImpl;
|
||||
|
||||
/**
|
||||
* Contains {@link ConditionJsonProvider}s for resource conditions provided by fabric itself.
|
||||
*/
|
||||
public final class DefaultResourceConditions {
|
||||
private static final Identifier NOT = new Identifier("fabric:not");
|
||||
private static final Identifier AND = new Identifier("fabric:and");
|
||||
private static final Identifier OR = new Identifier("fabric:or");
|
||||
private static final Identifier ALL_MODS_LOADED = new Identifier("fabric:all_mods_loaded");
|
||||
private static final Identifier ANY_MOD_LOADED = new Identifier("fabric:any_mod_loaded");
|
||||
private static final Identifier BLOCK_TAGS_POPULATED = new Identifier("fabric:block_tags_populated");
|
||||
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");
|
||||
|
||||
/**
|
||||
* Create a NOT condition: returns true if its child condition is false, and false if its child is true.
|
||||
*/
|
||||
public static ConditionJsonProvider not(ConditionJsonProvider value) {
|
||||
return new ConditionJsonProvider() {
|
||||
@Override
|
||||
public void writeParameters(JsonObject object) {
|
||||
object.add("value", value.toJson());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getConditionId() {
|
||||
return NOT;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a condition that returns true if all of its child conditions are true.
|
||||
*/
|
||||
public static ConditionJsonProvider and(ConditionJsonProvider... values) {
|
||||
return ResourceConditionsImpl.array(AND, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a condition that returns true if at least one of its child conditions is true.
|
||||
*/
|
||||
public static ConditionJsonProvider or(ConditionJsonProvider... values) {
|
||||
return ResourceConditionsImpl.array(OR, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a condition that returns true if all the passed mod ids correspond to a loaded mod.
|
||||
*/
|
||||
public static ConditionJsonProvider allModsLoaded(String... modIds) {
|
||||
return ResourceConditionsImpl.mods(ALL_MODS_LOADED, modIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a condition that returns true if at least one of the passed mod ids corresponds to a loaded mod.
|
||||
*/
|
||||
public static ConditionJsonProvider anyModLoaded(String... modIds) {
|
||||
return ResourceConditionsImpl.mods(ANY_MOD_LOADED, modIds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a condition that returns true if each of the passed block tags exists and has at least one element.
|
||||
*/
|
||||
public static ConditionJsonProvider blockTagsPopulated(Tag.Identified<Block>... tags) {
|
||||
return ResourceConditionsImpl.tagsPopulated(BLOCK_TAGS_POPULATED, tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a condition that returns true if each of the passed fluid tags exists and has at least one element.
|
||||
*/
|
||||
public static ConditionJsonProvider fluidTagsPopulated(Tag.Identified<Fluid>... tags) {
|
||||
return ResourceConditionsImpl.tagsPopulated(FLUID_TAGS_POPULATED, tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a condition that returns true if each of the passed item tags exists and has at least one element.
|
||||
*/
|
||||
public static ConditionJsonProvider itemTagsPopulated(Tag.Identified<Item>... tags) {
|
||||
return ResourceConditionsImpl.tagsPopulated(ITEM_TAGS_POPULATED, tags);
|
||||
}
|
||||
|
||||
static void init() {
|
||||
// init static
|
||||
}
|
||||
|
||||
static {
|
||||
ResourceConditions.register(NOT, object -> {
|
||||
JsonObject condition = JsonHelper.getObject(object, "value");
|
||||
return !ResourceConditions.conditionMatches(condition);
|
||||
});
|
||||
ResourceConditions.register(AND, object -> {
|
||||
JsonArray array = JsonHelper.getArray(object, "values");
|
||||
return ResourceConditions.conditionsMatch(array, true);
|
||||
});
|
||||
ResourceConditions.register(OR, object -> {
|
||||
JsonArray array = JsonHelper.getArray(object, "values");
|
||||
return ResourceConditions.conditionsMatch(array, false);
|
||||
});
|
||||
ResourceConditions.register(ALL_MODS_LOADED, object -> ResourceConditionsImpl.modsLoadedMatch(object, true));
|
||||
ResourceConditions.register(ANY_MOD_LOADED, object -> ResourceConditionsImpl.modsLoadedMatch(object, false));
|
||||
ResourceConditions.register(BLOCK_TAGS_POPULATED, object -> ResourceConditionsImpl.tagsPopulatedMatch(object, BlockTags.getTagGroup()));
|
||||
ResourceConditions.register(FLUID_TAGS_POPULATED, object -> ResourceConditionsImpl.tagsPopulatedMatch(object, FluidTags.getTagGroup()));
|
||||
ResourceConditions.register(ITEM_TAGS_POPULATED, object -> ResourceConditionsImpl.tagsPopulatedMatch(object, ItemTags.getTagGroup()));
|
||||
}
|
||||
|
||||
private DefaultResourceConditions() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* 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.api.resource.conditions.v1;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.resource.JsonDataLoader;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.JsonHelper;
|
||||
|
||||
import net.fabricmc.fabric.impl.resource.conditions.ResourceConditionsImpl;
|
||||
|
||||
/**
|
||||
* Registration and access to resource loading conditions.
|
||||
* A resource condition is an identified {@code Predicate<JsonObject>} that can decide whether a resource should be loaded or not.
|
||||
* <ul>
|
||||
* <li>A JSON object that may contain a condition can be parsed with {@link #objectMatchesConditions}.
|
||||
* This is the preferred way of implementing conditional objects, as it handles the details of the format (see below) and catches and logs thrown exceptions.</li>
|
||||
* <li>The lower-level {@link #conditionsMatch} and {@link #conditionMatches} may be useful when implementing conditions.</li>
|
||||
* <li>Conditions are registered with {@link #register} and queried with {@link #get}.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>At the moment, Fabric only recognizes conditions for resources loaded by subclasses of {@link JsonDataLoader}.
|
||||
* This means: recipes, advancements, loot tables, loot functions and loot conditions.
|
||||
*
|
||||
* <p>Fabric provides some conditions, which can be generated using the helper methods in {@link DefaultResourceConditions}.
|
||||
*
|
||||
* <h3>Details of the format</h3>
|
||||
*
|
||||
* <p>A conditional JSON object must have a {@link #CONDITIONS_KEY} entry, containing an array of condition objects.
|
||||
* The conditions in the array must be satisfied to load the resource.
|
||||
* Each condition object must contain a {@link #CONDITION_ID_KEY} entry with the identifier of the condition,
|
||||
* and it may also contain additional data for the condition.
|
||||
* Here is an example of a resource that is only loaded if no mod with id {@code a} is loaded:
|
||||
* <pre>{@code
|
||||
* {
|
||||
* ... // normal contents of the resource
|
||||
* "fabric:load_conditions": [ // array of condition objects
|
||||
* { // a condition object
|
||||
* // the identifier of the condition... the "fabric:not" condition inverts the condition in its "value" field
|
||||
* "condition": "fabric:not",
|
||||
* // additional data, for "fabric:not", the "value" field is required to be another condition object
|
||||
* "value": {
|
||||
* // the identifier of the condition
|
||||
* "condition": "fabric:all_mods_loaded",
|
||||
* // additional data, for "fabric:all_mods_loaded"
|
||||
* "values": [
|
||||
* "a"
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
public final class ResourceConditions {
|
||||
private static final Map<Identifier, Predicate<JsonObject>> REGISTERED_CONDITIONS = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* The key ({@value}) Fabric uses to identify resource conditions in a JSON object.
|
||||
*/
|
||||
public static final String CONDITIONS_KEY = "fabric:load_conditions";
|
||||
/**
|
||||
* The key ({@value}) identifying the resource condition's identifier inside a condition object.
|
||||
*/
|
||||
public static final String CONDITION_ID_KEY = "condition";
|
||||
|
||||
/**
|
||||
* Register a new resource condition.
|
||||
*
|
||||
* @throws IllegalArgumentException If a resource condition is already registered with the same name.
|
||||
*/
|
||||
public static void register(Identifier identifier, Predicate<JsonObject> condition) {
|
||||
Objects.requireNonNull(identifier, "Identifier may not be null.");
|
||||
Objects.requireNonNull(condition, "Condition may not be null.");
|
||||
|
||||
if (REGISTERED_CONDITIONS.put(identifier, condition) != null) {
|
||||
throw new IllegalArgumentException("Duplicate JSON condition registration with id " + identifier);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource condition with the passed name, or {@code null} if none is registered (yet).
|
||||
*/
|
||||
@Nullable
|
||||
public static Predicate<JsonObject> get(Identifier identifier) {
|
||||
return REGISTERED_CONDITIONS.get(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the passed JSON object either has no {@code fabric:conditions} tag, or all of its conditions match.
|
||||
* This should be called for objects that may contain a conditions entry.
|
||||
*
|
||||
* <p>If an exception is thrown during condition testing, it will be caught and logged, and false will be returned.
|
||||
*/
|
||||
public static boolean objectMatchesConditions(JsonObject object) {
|
||||
try {
|
||||
JsonArray conditions = JsonHelper.getArray(object, CONDITIONS_KEY, null);
|
||||
|
||||
if (conditions == null) {
|
||||
return true; // no conditions
|
||||
} else {
|
||||
return conditionsMatch(conditions, true);
|
||||
}
|
||||
} catch (RuntimeException exception) {
|
||||
ResourceConditionsImpl.LOGGER.warn("Skipping object %s. Failed to parse resource conditions".formatted(object), exception);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code and} is true, check if all the passed conditions match.
|
||||
* If it is false, check if at least one of the passed conditions matches.
|
||||
*
|
||||
* @throws RuntimeException If some condition failed to parse.
|
||||
*/
|
||||
public static boolean conditionsMatch(JsonArray conditions, boolean and) throws RuntimeException {
|
||||
for (JsonElement element : conditions) {
|
||||
if (element.isJsonObject()) {
|
||||
if (conditionMatches(element.getAsJsonObject()) != and) {
|
||||
return !and;
|
||||
}
|
||||
} else {
|
||||
throw new JsonParseException("Invalid condition entry: " + element);
|
||||
}
|
||||
}
|
||||
|
||||
return and;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the passed condition object matches.
|
||||
*
|
||||
* @throws RuntimeException If some condition failed to parse.
|
||||
*/
|
||||
public static boolean conditionMatches(JsonObject condition) throws RuntimeException {
|
||||
Identifier conditionId = new Identifier(JsonHelper.getString(condition, CONDITION_ID_KEY));
|
||||
Predicate<JsonObject> jrc = get(conditionId);
|
||||
|
||||
if (jrc == null) {
|
||||
throw new JsonParseException("Unknown recipe condition: " + conditionId);
|
||||
} else {
|
||||
return jrc.test(condition);
|
||||
}
|
||||
}
|
||||
|
||||
private ResourceConditions() {
|
||||
}
|
||||
|
||||
static {
|
||||
// Load Fabric-provided conditions.
|
||||
DefaultResourceConditions.init();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.impl.resource.conditions;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import net.minecraft.tag.Tag;
|
||||
import net.minecraft.tag.TagGroup;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.JsonHelper;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.ConditionJsonProvider;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public class ResourceConditionsImpl {
|
||||
public static final Logger LOGGER = LogManager.getLogger("Fabric Resource Conditions");
|
||||
|
||||
// Providers
|
||||
|
||||
public static ConditionJsonProvider array(Identifier id, ConditionJsonProvider... values) {
|
||||
Preconditions.checkArgument(values.length > 0, "Must register at least one value.");
|
||||
|
||||
return new ConditionJsonProvider() {
|
||||
@Override
|
||||
public Identifier getConditionId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeParameters(JsonObject object) {
|
||||
JsonArray array = new JsonArray();
|
||||
|
||||
for (ConditionJsonProvider provider : values) {
|
||||
array.add(provider.toJson());
|
||||
}
|
||||
|
||||
object.add("values", array);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static ConditionJsonProvider mods(Identifier id, String... modIds) {
|
||||
Preconditions.checkArgument(modIds.length > 0, "Must register at least one mod id.");
|
||||
|
||||
return new ConditionJsonProvider() {
|
||||
@Override
|
||||
public Identifier getConditionId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeParameters(JsonObject object) {
|
||||
JsonArray array = new JsonArray();
|
||||
|
||||
for (String modId : modIds) {
|
||||
array.add(modId);
|
||||
}
|
||||
|
||||
object.add("values", array);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> ConditionJsonProvider tagsPopulated(Identifier id, Tag.Identified<T>... tags) {
|
||||
Preconditions.checkArgument(tags.length > 0, "Must register at least one tag.");
|
||||
|
||||
return new ConditionJsonProvider() {
|
||||
@Override
|
||||
public Identifier getConditionId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeParameters(JsonObject object) {
|
||||
JsonArray array = new JsonArray();
|
||||
|
||||
for (Tag.Identified<T> tag : tags) {
|
||||
array.add(tag.getId().toString());
|
||||
}
|
||||
|
||||
object.add("values", array);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Condition implementations
|
||||
|
||||
public static boolean modsLoadedMatch(JsonObject object, boolean and) {
|
||||
JsonArray array = JsonHelper.getArray(object, "values");
|
||||
|
||||
for (JsonElement element : array) {
|
||||
if (element.isJsonPrimitive()) {
|
||||
if (FabricLoader.getInstance().isModLoaded(element.getAsString()) != and) {
|
||||
return !and;
|
||||
}
|
||||
} else {
|
||||
throw new JsonParseException("Invalid mod id entry: " + element);
|
||||
}
|
||||
}
|
||||
|
||||
return and;
|
||||
}
|
||||
|
||||
public static <T> boolean tagsPopulatedMatch(JsonObject object, TagGroup<T> tagGroup) {
|
||||
JsonArray array = JsonHelper.getArray(object, "values");
|
||||
|
||||
for (JsonElement element : array) {
|
||||
if (element.isJsonPrimitive()) {
|
||||
Identifier id = new Identifier(element.getAsString());
|
||||
Tag<T> tag = tagGroup.getTag(id);
|
||||
|
||||
if (tag == null || tag.values().size() == 0) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
throw new JsonParseException("Invalid tag id entry: " + element);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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 java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.resource.JsonDataLoader;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.profiler.Profiler;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.conditions.v1.ResourceConditions;
|
||||
import net.fabricmc.fabric.impl.resource.conditions.ResourceConditionsImpl;
|
||||
|
||||
@Mixin(JsonDataLoader.class)
|
||||
public class JsonDataLoaderMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private String dataType;
|
||||
|
||||
@Inject(at = @At("RETURN"), method = "prepare")
|
||||
public void applyResourceConditions(ResourceManager resourceManager, Profiler profiler, CallbackInfoReturnable<Map<Identifier, JsonElement>> cir) {
|
||||
profiler.push("Fabric resource conditions: %s".formatted(dataType));
|
||||
|
||||
Iterator<Map.Entry<Identifier, JsonElement>> it = cir.getReturnValue().entrySet().iterator();
|
||||
boolean debugLogEnabled = ResourceConditionsImpl.LOGGER.isDebugEnabled();
|
||||
|
||||
while (it.hasNext()) {
|
||||
Map.Entry<Identifier, JsonElement> entry = it.next();
|
||||
JsonElement resourceData = entry.getValue();
|
||||
|
||||
if (resourceData.isJsonObject()) {
|
||||
JsonObject obj = resourceData.getAsJsonObject();
|
||||
|
||||
if (obj.has(ResourceConditions.CONDITIONS_KEY)) {
|
||||
boolean matched = ResourceConditions.objectMatchesConditions(obj);
|
||||
|
||||
if (!matched) {
|
||||
it.remove();
|
||||
}
|
||||
|
||||
if (debugLogEnabled) {
|
||||
String verdict = matched ? "Allowed" : "Rejected";
|
||||
ResourceConditionsImpl.LOGGER.debug("{} resource of type {} with id {}", verdict, dataType, entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
profiler.pop();
|
||||
}
|
||||
}
|
Binary file not shown.
After ![]() (image error) Size: 1.5 KiB |
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"required": true,
|
||||
"package": "net.fabricmc.fabric.mixin.resource.conditions",
|
||||
"compatibilityLevel": "JAVA_16",
|
||||
"mixins": [
|
||||
"JsonDataLoaderMixin"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "fabric-resource-conditions-api-v1",
|
||||
"name": "Fabric Resource Conditions API (v1)",
|
||||
"version": "${version}",
|
||||
"environment": "*",
|
||||
"license": "Apache-2.0",
|
||||
"icon": "assets/fabric-resource-conditions-api-v1/icon.png",
|
||||
"contact": {
|
||||
"homepage": "https://fabricmc.net",
|
||||
"irc": "irc://irc.esper.net:6667/fabric",
|
||||
"issues": "https://github.com/FabricMC/fabric/issues",
|
||||
"sources": "https://github.com/FabricMC/fabric"
|
||||
},
|
||||
"authors": [
|
||||
"FabricMC"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.9.2"
|
||||
},
|
||||
"description": "Allows conditionally loading resources.",
|
||||
"mixins": [
|
||||
"fabric-resource-conditions-api-v1.mixins.json"
|
||||
],
|
||||
"custom": {
|
||||
"fabric-api:module-lifecycle": "experimental"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.test.resource.conditions;
|
||||
|
||||
import net.minecraft.recipe.RecipeManager;
|
||||
import net.minecraft.test.GameTest;
|
||||
import net.minecraft.test.TestContext;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.gametest.v1.FabricGameTest;
|
||||
|
||||
public class ConditionalResourcesTest {
|
||||
private static final String MOD_ID = "fabric-resource-conditions-api-v1-testmod";
|
||||
|
||||
private static Identifier id(String path) {
|
||||
return new Identifier(MOD_ID, path);
|
||||
}
|
||||
|
||||
@GameTest(structureName = FabricGameTest.EMPTY_STRUCTURE)
|
||||
public void conditionalRecipes(TestContext context) {
|
||||
RecipeManager manager = context.getWorld().getRecipeManager();
|
||||
|
||||
if (manager.get(id("not_loaded")).isPresent()) {
|
||||
throw new AssertionError("not_loaded recipe should not have been loaded.");
|
||||
}
|
||||
|
||||
if (manager.get(id("loaded")).isEmpty()) {
|
||||
throw new AssertionError("loaded recipe should have been loaded.");
|
||||
}
|
||||
|
||||
long loadedRecipes = manager.values().stream().filter(r -> r.getId().getNamespace().equals(MOD_ID)).count();
|
||||
if (loadedRecipes != 1) throw new AssertionError("Unexpected loaded recipe count: " + loadedRecipes);
|
||||
|
||||
context.complete();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"type": "minecraft:crafting_shapeless",
|
||||
"ingredients": [
|
||||
{
|
||||
"item": "minecraft:stick"
|
||||
}
|
||||
],
|
||||
"result": {
|
||||
"item": "minecraft:diamond"
|
||||
},
|
||||
"fabric:load_conditions": [
|
||||
{
|
||||
"condition": "fabric:all_mods_loaded",
|
||||
"values": [
|
||||
"fabric-resource-conditions-api-v1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"__comment": "this is going to be skipped before any check, so /shrug.",
|
||||
"fabric:load_conditions": [
|
||||
{
|
||||
"condition": "fabric:not",
|
||||
"value": {
|
||||
"condition": "fabric:all_mods_loaded",
|
||||
"values": [
|
||||
"fabric-resource-conditions-api-v1"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"condition": "fabric:all_mods_loaded",
|
||||
"values": [
|
||||
"fabric-resource-conditions-api-v1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "fabric-resource-conditions-api-v1-testmod",
|
||||
"name": "Fabric Resource Conditions API (v1) Test Mod",
|
||||
"version": "1.0.0",
|
||||
"environment": "*",
|
||||
"license": "Apache-2.0",
|
||||
"depends": {
|
||||
"fabric-api-lookup-api-v1": "*"
|
||||
},
|
||||
"entrypoints": {
|
||||
"fabric-gametest": [
|
||||
"net.fabricmc.fabric.test.resource.conditions.ConditionalResourcesTest"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -46,6 +46,7 @@ fabric-rendering-data-attachment-v1-version=0.3.4
|
|||
fabric-rendering-fluids-v1-version=0.1.18
|
||||
fabric-rendering-v0-version=1.1.9
|
||||
fabric-rendering-v1-version=1.10.3
|
||||
fabric-resource-conditions-api-v1-version=1.0.0
|
||||
fabric-resource-loader-v0-version=0.4.11
|
||||
fabric-screen-api-v1-version=1.0.7
|
||||
fabric-screen-handler-api-v1-version=1.1.11
|
||||
|
|
|
@ -50,6 +50,7 @@ include 'fabric-rendering-v0'
|
|||
include 'fabric-rendering-v1'
|
||||
include 'fabric-rendering-data-attachment-v1'
|
||||
include 'fabric-rendering-fluids-v1'
|
||||
include 'fabric-resource-conditions-api-v1'
|
||||
include 'fabric-resource-loader-v0'
|
||||
include 'fabric-screen-api-v1'
|
||||
include 'fabric-screen-handler-api-v1'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue