====== Thinking With Resource Conditions (DRAFT) ======
===== Introduction =====
Sometimes you want a recipe to only exist when a certain mod is loaded, or maybe have something happen when an empty tag actually has some items. This is where resource conditions come into play.
Resource Conditions allow you to define things like these in a non-breaking way.
===== Usage in JSON =====
Resource Conditions are placed in a list, often at the top of the file for simplicity.\\
Example:
{
"fabric:load_conditions": [
{
"condition": ...
},
{
...
}
],
...
}
===== Types of Conditions =====
There are 6 basic types, and 3 'modifier' types.
==== TrueResourceCondition ====
The TrueResourceCondition returns true. That's all it does.\\
Example:
{
"fabric:load_conditions": [
{
"condition": "fabric:true"
}
],
...
}
==== AllModsLoadedResourceCondition ====
This one returns true if all mods(defined by id) listed in it are loaded(running in the current environment).\\
Example:
{
"fabric:load_conditions": [
{
"condition": "fabric:all_mods_loaded"
"values": [
"aether",
"create"
]
}
],
...
}
==== AnyModsLoadedResourceCondition ====
Returns true if any mods listed in it are loaded.\\
Example:
{
"fabric:load_conditions": [
{
"condition": "fabric:any_mods_loaded"
"values": [
"energizedpower",
"iljatech"
]
}
],
...
}
==== TagsPopulatedResourceCondition ====
Returns true if all tags listed in it have members(not empty).\\
Example:
{
"fabric:load_conditions": [
{
"condition": "fabric:tags_populated",
"registry": "minecraft:item",
"values": [
"minecraft:logs",
"minecraft:planks"
]
}
],
...
}
==== FeaturesEnabledResourceCondition ====
Returns true if all features listed in it are enabled.\\
Example:
{
"fabric:load_conditions": [
{
"condition": "fabric:features_enabled"
"features": [
"minecraft:redstone_experiments"
]
}
],
...
}
==== RegistryContainsResourceCondition ====
Returns true if the registry has the identifiers listed in it as entries.\\
Example:
{
"fabric:load_conditions": [
{
"condition": "fabric:registry_contains"
"registry": "minecraft:item"
"values": [
"minecraft:coal",
"minecraft:diamond"
]
}
],
...
}
==== NotResourceCondition ====
Returns true if the resource condition nested inside is false.\\
Example:
{
"fabric:load_conditions": [
{
"condition": "fabric:not"
"value": {
"condition": "fabric:true"
}
}
],
...
}
==== OrResourceCondition ====
Returns true if any condition inside it is true.\\
Example:
{
"fabric:load_conditions": [
{
"condition": "fabric:or"
"values": [
{
"condition": "fabric:tags_populated",
"registry": "minecraft:item",
"values": [
"minecraft:buttons"
]
},
{
"condition": "fabric:features_enabled",
"features": [
"minecraft:redstone_experiments"
]
},
{
"condition": "fabric:true"
}
]
}
],
...
}
==== AndResourceCondition ====
Returns true if all conditions are true.\\
Example:
{
"fabric:load_conditions": [
{
"condition": "fabric:and"
"values": [
{
"condition": "fabric:tags_populated",
"registry": "minecraft:item",
"values": [
"minecraft:eggs"
]
},
{
"condition": "fabric:features_enabled",
"features": [
"minecraft:minecart_improvements"
]
},
{
"condition": "fabric:true"
}
]
}
],
...
}
===== Usage in Datagen =====
This section will assume you have already set up a basic datagen setup. It will use recipes as an example.
We will create a recipe that creates a minecart with copper if the minecart improvements experiment is enabled.
Next, we can go into our recipe provider and begin.
public class ModRecipeProvider extends FabricRecipeProvider {
...
@Override
protected RecipeProvider createRecipeProvider(HolderLookup.Provider provider, RecipeOutput output) {
return new RecipeProvider(provider, output) {
@Override
public void buildRecipes() {
shaped(RecipeCategory.MISC, Items.MINECART)
.pattern("c c")
.pattern("ccc")
.define('c', Items.COPPER_INGOT)
.unlockedBy(getHasName(Items.COPPER_INGOT), has(Items.COPPER_INGOT))
.save(withConditions(output, new FeaturesEnabledResourceCondition(Identifier.withDefaultNamespace("minecart_improvements");
};
}
}
The important part of this is when we call the save function. We can wrap our RecipeOutput with the withConditions method provided by FabricRecipeProvider, which allows us to place as many conditions as we want.
The result will be this:
{
"fabric:load_conditions": [
{
"condition": "fabric:features_enabled",
"features": [
"minecraft:minecart_improvements"
]
}
],
"type": "minecraft:crafting_shaped",
"category": "misc",
"key": {
"c": "minecraft:copper_ingot",
},
"pattern": [
"c c",
"ccc"
],
"result": {
"count": 1,
"id": "minecraft:minecart"
}
}
===== Making A Custom Condition =====
There are two things you need to do to make a custom condition: 1. Create the class and logic for it. 2. Register it.
We are going to make a FalseResourceCondition.
To begin, we must have our class implement ResourceCondition.
We can make a codec through MapCodec.unit, and we will return to the getType method.
The test method is where all the logic happens. It passes in a RegistryInfoLookup, which can get you a RegistryInfo from a ResourceKey.
For our use, we do not need it. We can just return false.
Your class should currently look something like this:
public class FalseResourceCondition implements ResourceCondition {
public static final MapCodec CODEC = MapCodec.unit(FalseResourceCondition::new);
public ResourceConditionType> getType() {
return TYPE_HERE;
}
public boolean test(RegistryOps.@Nullable RegistryInfoLookup registryInfo) {
return false;
}
}
Now, we must register the condition. You can refer to DefaultResourceConditionTypes to see how they are done.
A register method will look something like this:
protected static ResourceConditionType createResourceConditionType(String name, MapCodec codec) {
return ResourceConditionType.create(Identifier.fromNamespaceAndPath("tutorial", name), codec);
}
For name, we can pass in "false", and codec is our ''FalseResourceCondition.CODEC''.
In your ''ModInitializer'', do something like this:
public static ResourceConditionType FALSE;
@Override
public void onInitialize() {
FALSE = createResourceConditionType("false", FalseResourceCondition.CODEC)
// ...
}
protected static ResourceConditionType createResourceConditionType(String name, MapCodec codec) {
return ResourceConditionType.create(Identifier.fromNamespaceAndPath("tutorial", name), codec);
}
// ...
Now, replace TYPE_HERE with the new condition type.