diff --git a/patches/minecraft/net/minecraft/resources/RegistryLoader.java.patch b/patches/minecraft/net/minecraft/resources/RegistryLoader.java.patch index a89190ef28..521f1b0cc5 100644 --- a/patches/minecraft/net/minecraft/resources/RegistryLoader.java.patch +++ b/patches/minecraft/net/minecraft/resources/RegistryLoader.java.patch @@ -1,5 +1,14 @@ --- a/net/minecraft/resources/RegistryLoader.java +++ b/net/minecraft/resources/RegistryLoader.java +@@ -26,7 +_,7 @@ + Map, RegistryResourceAccess.EntryThunk> map = this.f_206750_.m_214030_(p_206764_); + DataResult> dataresult = DataResult.success(p_206763_, Lifecycle.stable()); + +- for(Map.Entry, RegistryResourceAccess.EntryThunk> entry : map.entrySet()) { ++ for(Map.Entry, RegistryResourceAccess.EntryThunk> entry : net.minecraftforge.common.ForgeHooks.filterThunks(map)) { + dataresult = dataresult.flatMap((p_214227_) -> { + return this.m_214228_(p_214227_, p_206764_, p_206765_, entry.getKey(), Optional.of(entry.getValue()), p_206766_).map((p_206761_) -> { + return p_214227_; @@ -48,7 +_,12 @@ if (dataresult != null) { return dataresult; diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index bafd67a49d..81f14903f5 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -8,6 +8,7 @@ package net.minecraftforge.common; import java.net.URI; import java.net.URISyntaxException; import java.util.*; +import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; @@ -27,6 +28,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; import com.mojang.serialization.Lifecycle; import it.unimi.dsi.fastutil.objects.ObjectArrayList; @@ -107,6 +109,7 @@ import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.BiomeSpecialEffects; import net.minecraft.world.level.biome.BiomeGenerationSettings; import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraftforge.common.crafting.conditions.ICondition; import net.minecraftforge.common.loot.IGlobalLootModifier; import net.minecraftforge.common.loot.LootModifierManager; import net.minecraftforge.common.loot.LootTableIdCondition; @@ -171,8 +174,10 @@ import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; import net.minecraft.ChatFormatting; +import net.minecraft.resources.RegistryResourceAccess; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.RegistryResourceAccess.EntryThunk; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; @@ -1660,4 +1665,12 @@ public class ForgeHooks } return false; } -} + + @SuppressWarnings("unchecked") + public static Collection, EntryThunk>> filterThunks(Map, RegistryResourceAccess.EntryThunk> map) + { + return map.entrySet().stream().filter(e -> + ((EntryThunk)e.getValue()).parseElement(JsonOps.INSTANCE, ICondition.DECODER) + .get().left().get().value()).toList(); // Validity of this .get() call is enforced by the above Decoder object, which only returns DataResult.success. + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java b/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java index 393ddcfdcb..f380bece11 100644 --- a/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java +++ b/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java @@ -234,4 +234,14 @@ public class CraftingHelper throw new JsonSyntaxException("Unknown condition type: " + condition.getID().toString()); return serializer.getJson(condition); } + + public static JsonArray serialize(ICondition... conditions) + { + JsonArray arr = new JsonArray(); + for(ICondition iCond : conditions) + { + arr.add(serialize(iCond)); + } + return arr; + } } diff --git a/src/main/java/net/minecraftforge/common/crafting/conditions/ICondition.java b/src/main/java/net/minecraftforge/common/crafting/conditions/ICondition.java index d3f3f11b50..1483af722e 100644 --- a/src/main/java/net/minecraftforge/common/crafting/conditions/ICondition.java +++ b/src/main/java/net/minecraftforge/common/crafting/conditions/ICondition.java @@ -5,11 +5,17 @@ package net.minecraftforge.common.crafting.conditions; +import com.google.gson.JsonObject; +import com.mojang.datafixers.util.Pair; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.Decoder; +import com.mojang.serialization.DynamicOps; import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.TagKey; +import net.minecraftforge.common.crafting.CraftingHelper; import java.util.Collection; import java.util.Collections; @@ -18,6 +24,28 @@ import java.util.Set; public interface ICondition { + + Decoder DECODER = new Decoder<>() { + @Override + public DataResult> decode(DynamicOps ops, T input) + { + if(input instanceof JsonObject obj && obj.has("forge:conditions")) + { + try + { + boolean result = CraftingHelper.processConditions(obj, "forge:conditions", IContext.TAGS_INVALID); + return DataResult.success(Pair.of(result, input)); + } + catch(Exception ex) + { + ex.printStackTrace(); + return DataResult.success(Pair.of(false, input)); // Print stacktrace and do not load errored conditions. + } + } + return DataResult.success(Pair.of(true, input)); + } + }; + ResourceLocation getID(); boolean test(IContext context); @@ -33,6 +61,15 @@ public interface ICondition } }; + IContext TAGS_INVALID = new IContext() + { + @Override + public Map>> getAllTags(ResourceKey> registry) + { + throw new UnsupportedOperationException("Usage of tag-based conditions is not permitted in this context!"); + } + }; + /** * Return the requested tag if available, or an empty tag otherwise. */ diff --git a/src/main/java/net/minecraftforge/common/data/JsonCodecProvider.java b/src/main/java/net/minecraftforge/common/data/JsonCodecProvider.java index f36c122b51..9451351205 100644 --- a/src/main/java/net/minecraftforge/common/data/JsonCodecProvider.java +++ b/src/main/java/net/minecraftforge/common/data/JsonCodecProvider.java @@ -8,9 +8,11 @@ package net.minecraftforge.common.data; import com.mojang.logging.LogUtils; import java.io.IOException; import java.nio.file.Path; +import java.util.Collections; import java.util.Map; import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.mojang.serialization.Codec; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.JsonOps; @@ -26,6 +28,8 @@ import net.minecraft.resources.RegistryOps; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.PackType; +import net.minecraftforge.common.crafting.CraftingHelper; +import net.minecraftforge.common.crafting.conditions.ICondition; import net.minecraftforge.common.data.ExistingFileHelper.ResourceType; import org.slf4j.Logger; @@ -49,6 +53,7 @@ public class JsonCodecProvider implements DataProvider protected final String directory; protected final Codec codec; protected final Map entries; + protected Map conditions = Collections.emptyMap(); /** * @param dataGenerator DataGenerator provided by {@link GatherDataEvent}. @@ -112,6 +117,18 @@ public class JsonCodecProvider implements DataProvider final Path path = outputFolder.resolve(String.join("/", dataFolder, id.getNamespace(), this.directory, id.getPath() + ".json")); JsonElement encoded = this.codec.encodeStart(this.dynamicOps, value) .getOrThrow(false, msg -> LOGGER.error("Failed to encode {}: {}", path, msg)); + ICondition[] conditions = this.conditions.get(id); + if (conditions != null && conditions.length > 0) + { + if(encoded instanceof JsonObject obj) + { + obj.add("forge:conditions", CraftingHelper.serialize(conditions)); + } + else + { + LOGGER.error("Attempted to apply conditions to a type that is not a JsonObject! - Path: {}", path); + } + } DataProvider.saveStable(cache, encoded, path); })); } @@ -126,4 +143,16 @@ public class JsonCodecProvider implements DataProvider { return String.format("%s generator for %s", this.directory, this.modid); } + + /** + * Applies a condition map to this provider. + * These conditions will be applied to the created JsonElements with the matching names. + * Null or empty arrays will not be written, and if the top-level json type is not JsonObject, attempting to add conditions will error. + * @param conditions The name->condition map to apply. + */ + public JsonCodecProvider setConditions(Map conditions) + { + this.conditions = conditions; + return this; + } } \ No newline at end of file diff --git a/src/test/java/net/minecraftforge/debug/world/BiomeModifierTest.java b/src/test/java/net/minecraftforge/debug/world/BiomeModifierTest.java index da28c87694..e9de7bf70d 100644 --- a/src/test/java/net/minecraftforge/debug/world/BiomeModifierTest.java +++ b/src/test/java/net/minecraftforge/debug/world/BiomeModifierTest.java @@ -33,6 +33,8 @@ import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; import net.minecraft.world.level.levelgen.placement.BiomeFilter; import net.minecraft.world.level.levelgen.placement.CountOnEveryLayerPlacement; import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import net.minecraftforge.common.crafting.conditions.ICondition; +import net.minecraftforge.common.crafting.conditions.ModLoadedCondition; import net.minecraftforge.common.data.ExistingFileHelper; import net.minecraftforge.common.data.JsonCodecProvider; import net.minecraftforge.common.world.BiomeModifier; @@ -150,7 +152,7 @@ public class BiomeModifierTest // Create and add dataproviders. generator.addProvider(event.includeServer(), JsonCodecProvider.forDatapackRegistry( generator, existingFileHelper, MODID, ops, Registry.PLACED_FEATURE_REGISTRY, Map.of( - LARGE_BASALT_COLUMNS_RL, basaltFeature))); + LARGE_BASALT_COLUMNS_RL, basaltFeature)).setConditions(Map.of(LARGE_BASALT_COLUMNS_RL, new ICondition[] { new ModLoadedCondition("forge") }))); generator.addProvider(event.includeServer(), JsonCodecProvider.forDatapackRegistry( generator, existingFileHelper, MODID, ops, ForgeRegistries.Keys.BIOME_MODIFIERS, Map.of( diff --git a/src/test/resources/data/forge/forge/biome_modifier/test_datapackreg_conditions.json b/src/test/resources/data/forge/forge/biome_modifier/test_datapackreg_conditions.json new file mode 100644 index 0000000000..a5b217496f --- /dev/null +++ b/src/test/resources/data/forge/forge/biome_modifier/test_datapackreg_conditions.json @@ -0,0 +1,7 @@ +{ + "forge:conditions": [{ + "type": "forge:mod_loaded", + "modid": "not_forge" + }], + "type": "irrelevant:wont_be_loaded" +} \ No newline at end of file diff --git a/src/test/resources/data/forge/forge/biome_modifier/test_datapackreg_conditions_error.json b/src/test/resources/data/forge/forge/biome_modifier/test_datapackreg_conditions_error.json new file mode 100644 index 0000000000..fb44ef1f09 --- /dev/null +++ b/src/test/resources/data/forge/forge/biome_modifier/test_datapackreg_conditions_error.json @@ -0,0 +1,6 @@ +{ + "forge:conditions": [{ + "type": "junk:not_real_type" + }], + "type": "irrelevant:wont_be_loaded" +} \ No newline at end of file