diff --git a/patches/minecraft/net/minecraft/nbt/CompoundTag.java.patch b/patches/minecraft/net/minecraft/nbt/CompoundTag.java.patch index 5f158056c2..4ff53ed0ec 100644 --- a/patches/minecraft/net/minecraft/nbt/CompoundTag.java.patch +++ b/patches/minecraft/net/minecraft/nbt/CompoundTag.java.patch @@ -1,5 +1,16 @@ --- a/net/minecraft/nbt/CompoundTag.java +++ b/net/minecraft/nbt/CompoundTag.java +@@ -19,6 +_,10 @@ + import net.minecraft.ReportedException; + + public class CompoundTag implements Tag { ++ public static net.minecraftforge.common.util.INBTBuilder.Builder builder() { ++ return (new net.minecraftforge.common.util.INBTBuilder(){}).nbt(); ++ } ++ + public static final Codec CODEC = Codec.PASSTHROUGH.comapFlatMap((p_274781_) -> { + Tag tag = p_274781_.convert(NbtOps.INSTANCE).getValue(); + return tag instanceof CompoundTag ? DataResult.success((CompoundTag)tag) : DataResult.error(() -> { @@ -204,6 +_,7 @@ @Nullable diff --git a/patches/minecraft/net/minecraft/world/item/crafting/Ingredient.java.patch b/patches/minecraft/net/minecraft/world/item/crafting/Ingredient.java.patch index 7c9565aa74..6c0f04b68c 100644 --- a/patches/minecraft/net/minecraft/world/item/crafting/Ingredient.java.patch +++ b/patches/minecraft/net/minecraft/world/item/crafting/Ingredient.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/item/crafting/Ingredient.java +++ b/net/minecraft/world/item/crafting/Ingredient.java -@@ -30,6 +_,12 @@ +@@ -30,14 +_,24 @@ import net.minecraft.world.level.ItemLike; public class Ingredient implements Predicate { @@ -13,10 +13,16 @@ public static final Ingredient EMPTY = new Ingredient(Stream.empty()); private final Ingredient.Value[] values; @Nullable -@@ -38,6 +_,7 @@ + private ItemStack[] itemStacks; + @Nullable private IntList stackingIds; - public static final Codec CODEC = codec(true); - public static final Codec CODEC_NONEMPTY = codec(false); +- public static final Codec CODEC = codec(true); +- public static final Codec CODEC_NONEMPTY = codec(false); ++ /** The vanilla codec that doesn't support custom Ingredient types. */ ++ public static final Codec VANILLA_CODEC = codec(true); ++ public static final Codec CODEC = net.minecraftforge.common.ForgeHooks.enhanceIngredientCodec(VANILLA_CODEC); ++ private static final Codec VANILLA_CODEC_NONEMPTY = codec(false); ++ public static final Codec CODEC_NONEMPTY = net.minecraftforge.common.ForgeHooks.enhanceIngredientCodec(VANILLA_CODEC_NONEMPTY); + private int invalidationCounter; protected Ingredient(Stream p_43907_) { @@ -31,20 +37,15 @@ ItemStack[] aitemstack = this.getItems(); this.stackingIds = new IntArrayList(aitemstack.length); -@@ -93,6 +_,12 @@ +@@ -93,6 +_,7 @@ } public final void toNetwork(FriendlyByteBuf p_43924_) { -+ /* -+ if (!this.isVanilla()) { -+ net.minecraftforge.common.crafting.CraftingHelper.write(p_43924_, this); -+ return; -+ } -+ */ ++ if (net.minecraftforge.common.ForgeHooks.ingredientToNetwork(p_43924_, this)) p_43924_.writeCollection(Arrays.asList(this.getItems()), FriendlyByteBuf::writeItem); } -@@ -113,6 +_,40 @@ +@@ -113,6 +_,38 @@ } } @@ -72,15 +73,13 @@ + + private final boolean isVanilla = this.getClass() == Ingredient.class; + public final boolean isVanilla() { -+ return isVanilla; ++ return isVanilla; + } + -+ /* -+ public net.minecraftforge.common.crafting.IIngredientSerializer getSerializer() { -+ if (!isVanilla()) throw new IllegalStateException("Modders must implement Ingredient.getSerializer in their custom Ingredients: " + this); -+ return net.minecraftforge.common.crafting.VanillaIngredientSerializer.INSTANCE; ++ public net.minecraftforge.common.crafting.ingredients.IIngredientSerializer serializer() { ++ if (!isVanilla()) throw new IllegalStateException("Modders must implement Ingredient.codec in their custom Ingredients: " + getClass()); ++ return net.minecraftforge.common.crafting.ingredients.IIngredientSerializer.VANILLA; + } -+ */ + public static Ingredient fromValues(Stream p_43939_) { Ingredient ingredient = new Ingredient(p_43939_); @@ -91,24 +90,11 @@ public static Ingredient fromNetwork(FriendlyByteBuf p_43941_) { - return fromValues(p_43941_.readList(FriendlyByteBuf::readItem).stream().map(Ingredient.ItemValue::new)); + var size = p_43941_.readVarInt(); -+ //if (size == -1) return net.minecraftforge.common.crafting.CraftingHelper.getIngredient(p_43941_.readResourceLocation(), p_43941_); ++ if (size == -1) return net.minecraftforge.common.ForgeHooks.ingredientFromNetwork(p_43941_); + return fromValues(Stream.generate(() -> new Ingredient.ItemValue(p_43941_.readItem())).limit(size)); } private static Codec codec(boolean p_298496_) { -@@ -185,6 +_,12 @@ - } - } - -+ //Merges several vanilla Ingredients together. As a quirk of how the json is structured, we can't tell if its a single Ingredient type or multiple so we split per item and re-merge here. -+ //Only public for internal use, so we can access a private field in here. -+ public static Ingredient merge(Collection parts) { -+ return fromValues(parts.stream().flatMap(i -> Arrays.stream(i.values))); -+ } -+ - public static record TagValue(TagKey tag) implements Ingredient.Value { - static final Codec CODEC = RecordCodecBuilder.create((p_300241_) -> { - return p_300241_.group(TagKey.codec(Registries.ITEM).fieldOf("tag").forGetter((p_301340_) -> { @@ -207,6 +_,9 @@ list.add(new ItemStack(holder)); } diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/compound_ingredient.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/compound_ingredient.json new file mode 100644 index 0000000000..066b4c7066 --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/compound_ingredient.json @@ -0,0 +1,35 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_dirt": { + "conditions": { + "items": [ + { + "items": [ + "minecraft:dirt" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "custom_ingredients:compound_ingredient" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_dirt" + ] + ], + "rewards": { + "recipes": [ + "custom_ingredients:compound_ingredient" + ] + }, + "sends_telemetry_event": false +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/difference_ingredient.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/difference_ingredient.json new file mode 100644 index 0000000000..828e01ac5a --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/difference_ingredient.json @@ -0,0 +1,35 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_dirt": { + "conditions": { + "items": [ + { + "items": [ + "minecraft:dirt" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "custom_ingredients:difference_ingredient" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_dirt" + ] + ], + "rewards": { + "recipes": [ + "custom_ingredients:difference_ingredient" + ] + }, + "sends_telemetry_event": false +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/intersection_ingredient.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/intersection_ingredient.json new file mode 100644 index 0000000000..d9c072b190 --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/intersection_ingredient.json @@ -0,0 +1,35 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_dirt": { + "conditions": { + "items": [ + { + "items": [ + "minecraft:dirt" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "custom_ingredients:intersection_ingredient" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_dirt" + ] + ], + "rewards": { + "recipes": [ + "custom_ingredients:intersection_ingredient" + ] + }, + "sends_telemetry_event": false +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/partial_nbt_damage_only.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/partial_nbt_damage_only.json new file mode 100644 index 0000000000..d05f48e4c7 --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/partial_nbt_damage_only.json @@ -0,0 +1,35 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_dirt": { + "conditions": { + "items": [ + { + "items": [ + "minecraft:dirt" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "custom_ingredients:partial_nbt_damage_only" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_dirt" + ] + ], + "rewards": { + "recipes": [ + "custom_ingredients:partial_nbt_damage_only" + ] + }, + "sends_telemetry_event": false +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/partial_nbt_name_only.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/partial_nbt_name_only.json new file mode 100644 index 0000000000..dd7e945ea9 --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/partial_nbt_name_only.json @@ -0,0 +1,35 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_dirt": { + "conditions": { + "items": [ + { + "items": [ + "minecraft:dirt" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "custom_ingredients:partial_nbt_name_only" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_dirt" + ] + ], + "rewards": { + "recipes": [ + "custom_ingredients:partial_nbt_name_only" + ] + }, + "sends_telemetry_event": false +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/strict_nbt_ingredient.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/strict_nbt_ingredient.json new file mode 100644 index 0000000000..a11793597f --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/advancements/recipes/misc/strict_nbt_ingredient.json @@ -0,0 +1,35 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "has_dirt": { + "conditions": { + "items": [ + { + "items": [ + "minecraft:dirt" + ] + } + ] + }, + "trigger": "minecraft:inventory_changed" + }, + "has_the_recipe": { + "conditions": { + "recipe": "custom_ingredients:strict_nbt_ingredient" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "has_dirt" + ] + ], + "rewards": { + "recipes": [ + "custom_ingredients:strict_nbt_ingredient" + ] + }, + "sends_telemetry_event": false +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/compound_ingredient.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/compound_ingredient.json new file mode 100644 index 0000000000..441f7c19aa --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/compound_ingredient.json @@ -0,0 +1,39 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "X": { + "type": "forge:compound", + "children": [ + { + "type": "forge:nbt", + "stack": { + "Count": 1, + "id": "minecraft:dirt", + "tag": { + "Name": "{\"text\":\"Test Item\"}" + } + } + }, + { + "type": "forge:nbt", + "stack": { + "Count": 1, + "id": "minecraft:oak_log", + "tag": { + "Name": "{\"text\":\"Test Item\"}" + } + } + } + ] + } + }, + "pattern": [ + "XXX", + " X " + ], + "result": { + "item": "minecraft:dirt" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/difference_ingredient.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/difference_ingredient.json new file mode 100644 index 0000000000..896646d69f --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/difference_ingredient.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "X": { + "type": "forge:difference", + "base": { + "tag": "custom_ingredients:left" + }, + "subtracted": { + "tag": "custom_ingredients:right" + } + } + }, + "pattern": [ + " X ", + "XXX", + " X " + ], + "result": { + "item": "minecraft:dirt" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/intersection_ingredient.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/intersection_ingredient.json new file mode 100644 index 0000000000..444742073e --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/intersection_ingredient.json @@ -0,0 +1,26 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "X": { + "type": "forge:intersection", + "children": [ + { + "tag": "custom_ingredients:left" + }, + { + "tag": "custom_ingredients:right" + } + ] + } + }, + "pattern": [ + "XXX", + "XXX", + " X " + ], + "result": { + "item": "minecraft:dirt" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/partial_nbt_damage_only.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/partial_nbt_damage_only.json new file mode 100644 index 0000000000..f20ccc51e5 --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/partial_nbt_damage_only.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "X": { + "type": "forge:partial_nbt", + "items": [ + "minecraft:wooden_pickaxe", + "minecraft:stone_pickaxe", + "minecraft:iron_pickaxe" + ], + "nbt": "{Damage:3}" + } + }, + "pattern": [ + "XXX", + "XXX" + ], + "result": { + "item": "minecraft:dirt" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/partial_nbt_name_only.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/partial_nbt_name_only.json new file mode 100644 index 0000000000..505101648e --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/partial_nbt_name_only.json @@ -0,0 +1,22 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "X": { + "type": "forge:partial_nbt", + "items": [ + "minecraft:wooden_pickaxe", + "minecraft:stone_pickaxe", + "minecraft:iron_pickaxe" + ], + "nbt": "{Name:'{\"text\":\"Test Item\"}'}" + } + }, + "pattern": [ + "XXX" + ], + "result": { + "item": "minecraft:dirt" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/strict_nbt_ingredient.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/strict_nbt_ingredient.json new file mode 100644 index 0000000000..db8a4465d9 --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/recipes/strict_nbt_ingredient.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "X": { + "type": "forge:nbt", + "stack": { + "Count": 1, + "id": "minecraft:iron_pickaxe", + "tag": { + "Damage": 3 + } + } + } + }, + "pattern": [ + "XXX" + ], + "result": { + "item": "minecraft:dirt" + }, + "show_notification": true +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/tags/items/left.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/tags/items/left.json new file mode 100644 index 0000000000..e78172bf67 --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/tags/items/left.json @@ -0,0 +1,6 @@ +{ + "values": [ + "minecraft:dirt", + "minecraft:stone" + ] +} \ No newline at end of file diff --git a/src/generated_test/resources/custom_ingredients/data/custom_ingredients/tags/items/right.json b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/tags/items/right.json new file mode 100644 index 0000000000..8302cf6d69 --- /dev/null +++ b/src/generated_test/resources/custom_ingredients/data/custom_ingredients/tags/items/right.json @@ -0,0 +1,6 @@ +{ + "values": [ + "minecraft:stone", + "minecraft:gravel" + ] +} \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/client/model/ForgeFaceData.java b/src/main/java/net/minecraftforge/client/model/ForgeFaceData.java index c41005202f..ca0b046817 100644 --- a/src/main/java/net/minecraftforge/client/model/ForgeFaceData.java +++ b/src/main/java/net/minecraftforge/client/model/ForgeFaceData.java @@ -23,7 +23,7 @@ import net.minecraft.util.ExtraCodecs; /** * Holds extra data that may be injected into a face.

* Used by {@link ItemLayerModel}, {@link BlockElement} and {@link BlockElementFace} - * + * * @param color Color in ARGB format * @param blockLight Block Light for this face from 0-15 (inclusive) * @param skyLight Sky Light for this face from 0-15 (inclusive) @@ -39,7 +39,7 @@ public record ForgeFaceData(int color, int blockLight, int skyLight, boolean amb public static final ForgeFaceData DEFAULT = new ForgeFaceData(0xFFFFFFFF, 0, 0, true, false); - public static final Codec COLOR = new ExtraCodecs.EitherCodec<>(Codec.INT, Codec.STRING).xmap( + public static final Codec COLOR = ExtraCodecs.either(Codec.INT, Codec.STRING).xmap( either -> either.map(Function.identity(), str -> (int) Long.parseLong(str, 16)), color -> Either.right(Integer.toHexString(color))); diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index 6f11f62fea..b9634d103b 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -25,6 +25,7 @@ import com.google.common.collect.Multimap; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonSyntaxException; +import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; @@ -34,6 +35,7 @@ import com.mojang.serialization.DynamicOps; import com.mojang.serialization.JsonOps; import com.mojang.serialization.Lifecycle; +import io.netty.handler.codec.DecoderException; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; @@ -46,6 +48,7 @@ import net.minecraft.core.HolderSet; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.registries.Registries; import net.minecraft.network.Connection; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; @@ -55,6 +58,7 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.BlockTags; import net.minecraft.tags.TagKey; import net.minecraft.util.CrudeIncrementalIntIdentityHashBiMap; +import net.minecraft.util.ExtraCodecs; import net.minecraft.util.GsonHelper; import net.minecraft.util.Mth; import net.minecraft.util.datafix.fixes.StructuresBecomeConfiguredFix; @@ -118,6 +122,7 @@ 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.crafting.ingredients.IIngredientSerializer; import net.minecraftforge.common.util.BlockSnapshot; import net.minecraftforge.common.util.BrainBuilder; import net.minecraftforge.common.util.Lazy; @@ -856,7 +861,7 @@ public class ForgeHooks { EntityDataSerializer serializer = vanilla.byId(id); if (serializer == null) { // ForgeRegistries.DATA_SERIALIZERS is a deferred register now, so if this method is called too early, the registry will be null - ForgeRegistry> registry = (ForgeRegistry>) ForgeRegistries.ENTITY_DATA_SERIALIZERS.get(); + var registry = (ForgeRegistry>)ForgeRegistries.ENTITY_DATA_SERIALIZERS.get(); if (registry != null) serializer = registry.getValue(id); } @@ -867,7 +872,7 @@ public class ForgeHooks { int id = vanilla.getId(serializer); if (id < 0) { // ForgeRegistries.DATA_SERIALIZERS is a deferred register now, so if this method is called too early, the registry will be null - ForgeRegistry> registry = (ForgeRegistry>) ForgeRegistries.ENTITY_DATA_SERIALIZERS.get(); + var registry = (ForgeRegistry>)ForgeRegistries.ENTITY_DATA_SERIALIZERS.get(); if (registry != null) id = registry.getID(serializer); } @@ -1313,4 +1318,44 @@ public class ForgeHooks { return null; } + + @ApiStatus.Internal + public static Codec enhanceIngredientCodec(Codec vanilla) { + return ExtraCodecs.lazyInitializedCodec(() -> + ExtraCodecs.either( + ForgeRegistries.INGREDIENT_SERIALIZERS.get().getCodec().dispatch(Ingredient::serializer, IIngredientSerializer::codec), + vanilla + ) + .flatComapMap( + i -> i.left().isPresent() ? i.left().get() : i.right().get(), + i -> DataResult.success(i.isVanilla() ? Either.right(i) : Either.left(i)) + ) + ); + } + + @ApiStatus.Internal + public static boolean ingredientToNetwork(FriendlyByteBuf buffer, T ingredient) { + if (ingredient.isVanilla()) + return true; + + @SuppressWarnings("unchecked") + var serializer = (IIngredientSerializer)ingredient.serializer(); + var key = ForgeRegistries.INGREDIENT_SERIALIZERS.get().getKey(ingredient.serializer()); + if (key == null) + throw new IllegalArgumentException("Tried to write unregistered Ingredient to network: " + ingredient); + + buffer.writeVarInt(-1); // Our Marker + buffer.writeResourceLocation(key); + serializer.write(buffer, ingredient); + return false; + } + + public static Ingredient ingredientFromNetwork(FriendlyByteBuf buffer) { + // We have already read our marker + var key = buffer.readResourceLocation(); + var serializer = ForgeRegistries.INGREDIENT_SERIALIZERS.get().getValue(key); + if (serializer == null) + throw new DecoderException("Could not read ingredient of type: " + key); + return serializer.read(buffer); + } } \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/ForgeMod.java b/src/main/java/net/minecraftforge/common/ForgeMod.java index f027467780..2b5a345186 100644 --- a/src/main/java/net/minecraftforge/common/ForgeMod.java +++ b/src/main/java/net/minecraftforge/common/ForgeMod.java @@ -13,6 +13,7 @@ import net.minecraft.commands.synchronization.ArgumentTypeInfos; import net.minecraft.commands.synchronization.SingletonArgumentInfo; import net.minecraft.core.BlockPos; import net.minecraft.core.HolderLookup; +import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; import net.minecraft.data.PackOutput; import net.minecraft.data.metadata.PackMetadataGenerator; @@ -31,15 +32,9 @@ import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; -import net.minecraft.world.level.levelgen.GenerationStep.Decoration; -import net.minecraft.world.level.levelgen.placement.PlacedFeature; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.item.Items; import net.minecraft.sounds.SoundEvent; -import net.minecraft.util.ExtraCodecs; -import net.minecraft.core.RegistryCodecs; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.pathfinder.BlockPathTypes; import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType; @@ -93,6 +88,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import net.minecraft.data.DataGenerator; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.common.crafting.ConditionalRecipe; import net.minecraftforge.common.crafting.conditions.AndCondition; @@ -104,6 +100,12 @@ import net.minecraftforge.common.crafting.conditions.NotCondition; import net.minecraftforge.common.crafting.conditions.OrCondition; import net.minecraftforge.common.crafting.conditions.TagEmptyCondition; import net.minecraftforge.common.crafting.conditions.TrueCondition; +import net.minecraftforge.common.crafting.ingredients.CompoundIngredient; +import net.minecraftforge.common.crafting.ingredients.DifferenceIngredient; +import net.minecraftforge.common.crafting.ingredients.IIngredientSerializer; +import net.minecraftforge.common.crafting.ingredients.IntersectionIngredient; +import net.minecraftforge.common.crafting.ingredients.PartialNBTIngredient; +import net.minecraftforge.common.crafting.ingredients.StrictNBTIngredient; import net.minecraftforge.common.data.ForgeBlockTagsProvider; import net.minecraftforge.common.data.ForgeEntityTypeTagsProvider; import net.minecraftforge.common.data.ForgeItemTagsProvider; @@ -112,15 +114,12 @@ import net.minecraftforge.fml.common.Mod; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; -import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; -import com.mojang.serialization.codecs.RecordCodecBuilder; import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import java.util.function.Function; import java.util.function.Predicate; @Mod("forge") @@ -129,20 +128,24 @@ public class ForgeMod { private static final Logger LOGGER = LogManager.getLogger(); private static final Marker FORGEMOD = MarkerManager.getMarker("FORGEMOD"); - private static final DeferredRegister ATTRIBUTES = DeferredRegister.create(ForgeRegistries.Keys.ATTRIBUTES, "forge"); - private static final DeferredRegister> COMMAND_ARGUMENT_TYPES = DeferredRegister.create(Registries.COMMAND_ARGUMENT_TYPE, "forge"); - private static final DeferredRegister> BIOME_MODIFIER_SERIALIZERS = DeferredRegister.create(ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, "forge"); - private static final DeferredRegister> STRUCTURE_MODIFIER_SERIALIZERS = DeferredRegister.create(ForgeRegistries.Keys.STRUCTURE_MODIFIER_SERIALIZERS, "forge"); - private static final DeferredRegister HOLDER_SET_TYPES = DeferredRegister.create(ForgeRegistries.Keys.HOLDER_SET_TYPES, "forge"); + private static final List> registries = new ArrayList<>(); + private static DeferredRegister deferred(ResourceKey> key) { + return deferred(key, "forge"); + } + private static DeferredRegister deferred(ResourceKey> key, String modid) { + var ret = DeferredRegister.create(key, modid); + registries.add(ret); + return ret; + } - @SuppressWarnings({ "unchecked", "rawtypes", "unused" }) - private static final RegistryObject ENUM_COMMAND_ARGUMENT_TYPE = COMMAND_ARGUMENT_TYPES.register("enum", () -> - ArgumentTypeInfos.registerByClass(EnumArgument.class, new EnumArgument.Info())); - @SuppressWarnings("unused") - private static final RegistryObject> MODID_COMMAND_ARGUMENT_TYPE = COMMAND_ARGUMENT_TYPES.register("modid", () -> - ArgumentTypeInfos.registerByClass(ModIdArgument.class, - SingletonArgumentInfo.contextFree(ModIdArgument::modIdArgument))); + private static final DeferredRegister> COMMAND_ARGUMENT_TYPES = deferred(Registries.COMMAND_ARGUMENT_TYPE); + static { + @SuppressWarnings({ "unchecked", "rawtypes", "unused" }) + var v = COMMAND_ARGUMENT_TYPES.register("enum", () -> ArgumentTypeInfos.registerByClass(EnumArgument.class, new EnumArgument.Info())); + COMMAND_ARGUMENT_TYPES.register("modid", () -> ArgumentTypeInfos.registerByClass(ModIdArgument.class, SingletonArgumentInfo.contextFree(ModIdArgument::modIdArgument))); + } + private static final DeferredRegister ATTRIBUTES = deferred(ForgeRegistries.Keys.ATTRIBUTES); public static final RegistryObject SWIM_SPEED = ATTRIBUTES.register("swim_speed", () -> new RangedAttribute("forge.swim_speed", 1.0D, 0.0D, 1024.0D).setSyncable(true)); public static final RegistryObject NAMETAG_DISTANCE = ATTRIBUTES.register("nametag_distance", () -> new RangedAttribute("forge.name_tag_distance", 64.0D, 0.0D, 64.0).setSyncable(true)); public static final RegistryObject ENTITY_GRAVITY = ATTRIBUTES.register("entity_gravity", () -> new RangedAttribute("forge.entity_gravity", 0.08D, -8.0D, 8.0D).setSyncable(true)); @@ -169,64 +172,22 @@ public class ForgeMod { */ public static final RegistryObject STEP_HEIGHT_ADDITION = ATTRIBUTES.register("step_height_addition", () -> new RangedAttribute("forge.step_height", 0.0D, -512.0D, 512.0D).setSyncable(true)); - /** - * Noop biome modifier. Can be used in a biome modifier json with "type": "forge:none". - */ - public static final RegistryObject> NONE_BIOME_MODIFIER_TYPE = BIOME_MODIFIER_SERIALIZERS.register("none", () -> Codec.unit(NoneBiomeModifier.INSTANCE)); - /** - * Stock biome modifier for adding features to biomes. - */ - public static final RegistryObject> ADD_FEATURES_BIOME_MODIFIER_TYPE = BIOME_MODIFIER_SERIALIZERS.register("add_features", () -> - RecordCodecBuilder.create(builder -> builder.group( - Biome.LIST_CODEC.fieldOf("biomes").forGetter(AddFeaturesBiomeModifier::biomes), - PlacedFeature.LIST_CODEC.fieldOf("features").forGetter(AddFeaturesBiomeModifier::features), - Decoration.CODEC.fieldOf("step").forGetter(AddFeaturesBiomeModifier::step) - ).apply(builder, AddFeaturesBiomeModifier::new)) - ); + private static final DeferredRegister> BIOME_MODIFIER_SERIALIZERS = deferred(ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS); + static { + BIOME_MODIFIER_SERIALIZERS.register("none", () -> NoneBiomeModifier.CODEC); + BIOME_MODIFIER_SERIALIZERS.register("add_features", () -> AddFeaturesBiomeModifier.CODEC); + BIOME_MODIFIER_SERIALIZERS.register("remove_features", () -> RemoveFeaturesBiomeModifier.CODEC); + BIOME_MODIFIER_SERIALIZERS.register("add_spawns", () -> AddSpawnsBiomeModifier.CODEC); + BIOME_MODIFIER_SERIALIZERS.register("remove_spawns", () -> RemoveSpawnsBiomeModifier.CODEC); + } - /** - * Stock biome modifier for removing features from biomes. - */ - public static final RegistryObject> REMOVE_FEATURES_BIOME_MODIFIER_TYPE = BIOME_MODIFIER_SERIALIZERS.register("remove_features", () -> - RecordCodecBuilder.create(builder -> builder.group( - Biome.LIST_CODEC.fieldOf("biomes").forGetter(RemoveFeaturesBiomeModifier::biomes), - PlacedFeature.LIST_CODEC.fieldOf("features").forGetter(RemoveFeaturesBiomeModifier::features), - new ExtraCodecs.EitherCodec, Decoration>(Decoration.CODEC.listOf(), Decoration.CODEC).>xmap( - either -> either.map(Set::copyOf, Set::of), // convert list/singleton to set when decoding - set -> set.size() == 1 ? Either.right(set.toArray(Decoration[]::new)[0]) : Either.left(List.copyOf(set)) - ).optionalFieldOf("steps", EnumSet.allOf(Decoration.class)).forGetter(RemoveFeaturesBiomeModifier::steps) - ).apply(builder, RemoveFeaturesBiomeModifier::new)) - ); + private static final DeferredRegister> STRUCTURE_MODIFIER_SERIALIZERS = deferred(ForgeRegistries.Keys.STRUCTURE_MODIFIER_SERIALIZERS); + static { + STRUCTURE_MODIFIER_SERIALIZERS.register("none", () -> NoneStructureModifier.CODEC); + } - /** - * Stock biome modifier for adding mob spawns to biomes. - */ - public static final RegistryObject> ADD_SPAWNS_BIOME_MODIFIER_TYPE = BIOME_MODIFIER_SERIALIZERS.register("add_spawns", () -> - RecordCodecBuilder.create(builder -> builder.group( - Biome.LIST_CODEC.fieldOf("biomes").forGetter(AddSpawnsBiomeModifier::biomes), - // Allow either a list or single spawner, attempting to decode the list format first. - // Uses the better EitherCodec that logs both errors if both formats fail to parse. - new ExtraCodecs.EitherCodec<>(SpawnerData.CODEC.listOf(), SpawnerData.CODEC).xmap( - either -> either.map(Function.identity(), List::of), // convert list/singleton to list when decoding - list -> list.size() == 1 ? Either.right(list.get(0)) : Either.left(list) // convert list to singleton/list when encoding - ).fieldOf("spawners").forGetter(AddSpawnsBiomeModifier::spawners) - ).apply(builder, AddSpawnsBiomeModifier::new)) - ); - - /** - * Stock biome modifier for removing mob spawns from biomes. - */ - public static final RegistryObject> REMOVE_SPAWNS_BIOME_MODIFIER_TYPE = BIOME_MODIFIER_SERIALIZERS.register("remove_spawns", () -> - RecordCodecBuilder.create(builder -> builder.group( - Biome.LIST_CODEC.fieldOf("biomes").forGetter(RemoveSpawnsBiomeModifier::biomes), - RegistryCodecs.homogeneousList(ForgeRegistries.Keys.ENTITY_TYPES).fieldOf("entity_types").forGetter(RemoveSpawnsBiomeModifier::entityTypes) - ).apply(builder, RemoveSpawnsBiomeModifier::new)) - ); - /** - * Noop structure modifier. Can be used in a structure modifier json with "type": "forge:none". - */ - public static final RegistryObject> NONE_STRUCTURE_MODIFIER_TYPE = STRUCTURE_MODIFIER_SERIALIZERS.register("none", () -> Codec.unit(NoneStructureModifier.INSTANCE)); + private static final DeferredRegister HOLDER_SET_TYPES = deferred(ForgeRegistries.Keys.HOLDER_SET_TYPES); /** * Stock holder set type that represents any/all values in a registry. Can be used in a holderset object with {@code { "type": "forge:any" }} @@ -249,7 +210,7 @@ public class ForgeMod { */ public static final RegistryObject NOT_HOLDER_SET = HOLDER_SET_TYPES.register("not", () -> NotHolderSet::codec); - private static final DeferredRegister VANILLA_FLUID_TYPES = DeferredRegister.create(ForgeRegistries.Keys.FLUID_TYPES, "minecraft"); + private static final DeferredRegister VANILLA_FLUID_TYPES = deferred(ForgeRegistries.Keys.FLUID_TYPES, "minecraft"); public static final RegistryObject EMPTY_TYPE = VANILLA_FLUID_TYPES.register("empty", () -> new FluidType(FluidType.Properties.create() @@ -374,13 +335,13 @@ public class ForgeMod { } }); - private static final DeferredRegister LOOT_CONDITION_TYPES = DeferredRegister.create(Registries.LOOT_CONDITION_TYPE, "forge"); + private static final DeferredRegister LOOT_CONDITION_TYPES = deferred(Registries.LOOT_CONDITION_TYPE); static { LOOT_CONDITION_TYPES.register("loot_table_id", () -> LootTableIdCondition.TYPE); LOOT_CONDITION_TYPES.register("can_tool_perform_action", () -> CanToolPerformAction.TYPE); } - private static final DeferredRegister> CONDITION_SERIALIZERS = DeferredRegister.create(ForgeRegistries.Keys.CONDITION_SERIALIZERS, "forge"); + private static final DeferredRegister> CONDITION_SERIALIZERS = deferred(ForgeRegistries.Keys.CONDITION_SERIALIZERS); static { CONDITION_SERIALIZERS.register("and", () -> AndCondition.CODEC); CONDITION_SERIALIZERS.register("false", () -> FalseCondition.CODEC); @@ -392,11 +353,20 @@ public class ForgeMod { CONDITION_SERIALIZERS.register("tag_empty", () -> TagEmptyCondition.CODEC); } - private static final DeferredRegister> RECIPE_SERIALIZERS = DeferredRegister.create(ForgeRegistries.Keys.RECIPE_SERIALIZERS, "forge"); + private static final DeferredRegister> RECIPE_SERIALIZERS = deferred(ForgeRegistries.Keys.RECIPE_SERIALIZERS); static { RECIPE_SERIALIZERS.register("conditional", () -> ConditionalRecipe.SERIALZIER); } + private static final DeferredRegister> INGREDIENT_SERIALIZERS = deferred(ForgeRegistries.Keys.INGREDIENT_SERIALIZERS); + static { + INGREDIENT_SERIALIZERS.register("compound", () -> CompoundIngredient.SERIALIZER); + INGREDIENT_SERIALIZERS.register("nbt", () -> StrictNBTIngredient.SERIALIZER); + INGREDIENT_SERIALIZERS.register("partial_nbt", () -> PartialNBTIngredient.SERIALIZER); + INGREDIENT_SERIALIZERS.register("difference", () -> DifferenceIngredient.SERIALIZER); + INGREDIENT_SERIALIZERS.register("intersection", () -> IntersectionIngredient.SERIALIZER); + } + private static boolean enableMilkFluid = false; public static final RegistryObject BUCKET_EMPTY_MILK = RegistryObject.create(new ResourceLocation("item.bucket.empty_milk"), ForgeRegistries.SOUND_EVENTS); @@ -444,17 +414,9 @@ public class ForgeMod { modEventBus.addListener(this::loadComplete); modEventBus.addListener(this::registerFluids); modEventBus.addListener(this::registerVanillaDisplayContexts); - modEventBus.addListener(this::registerRecipeSerializers); modEventBus.register(this); - ATTRIBUTES.register(modEventBus); - COMMAND_ARGUMENT_TYPES.register(modEventBus); - BIOME_MODIFIER_SERIALIZERS.register(modEventBus); - STRUCTURE_MODIFIER_SERIALIZERS.register(modEventBus); - HOLDER_SET_TYPES.register(modEventBus); - VANILLA_FLUID_TYPES.register(modEventBus); - LOOT_CONDITION_TYPES.register(modEventBus); - CONDITION_SERIALIZERS.register(modEventBus); - RECIPE_SERIALIZERS.register(modEventBus); + registries.forEach(r -> r.register(modEventBus)); + MinecraftForge.EVENT_BUS.addListener(this::serverStopping); ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, ForgeConfig.clientSpec); ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, ForgeConfig.serverSpec); @@ -577,22 +539,6 @@ public class ForgeMod { } } - public void registerRecipeSerializers(RegisterEvent event) { - if (event.getRegistryKey().equals(ForgeRegistries.Keys.RECIPE_SERIALIZERS)) { - - /* - CraftingHelper.register(new ResourceLocation("forge", "compound"), CompoundIngredient.Serializer.INSTANCE); - CraftingHelper.register(new ResourceLocation("forge", "nbt"), StrictNBTIngredient.Serializer.INSTANCE); - CraftingHelper.register(new ResourceLocation("forge", "partial_nbt"), PartialNBTIngredient.Serializer.INSTANCE); - CraftingHelper.register(new ResourceLocation("forge", "difference"), DifferenceIngredient.Serializer.INSTANCE); - CraftingHelper.register(new ResourceLocation("forge", "intersection"), IntersectionIngredient.Serializer.INSTANCE); - CraftingHelper.register(new ResourceLocation("minecraft", "item"), VanillaIngredientSerializer.INSTANCE); - */ - - //event.register(ForgeRegistries.Keys.RECIPE_SERIALIZERS, new ResourceLocation("forge", "conditional"), ConditionalRecipe.Serializer::new); - } - } - public static final PermissionNode USE_SELECTORS_PERMISSION = new PermissionNode<>("forge", "use_entity_selectors", PermissionTypes.BOOLEAN, (player, uuid, contexts) -> player != null && player.hasPermissions(Commands.LEVEL_GAMEMASTERS)); diff --git a/src/main/java/net/minecraftforge/common/IExtensibleEnum.java b/src/main/java/net/minecraftforge/common/IExtensibleEnum.java index 5f09c4a878..1f5338fdf5 100644 --- a/src/main/java/net/minecraftforge/common/IExtensibleEnum.java +++ b/src/main/java/net/minecraftforge/common/IExtensibleEnum.java @@ -8,7 +8,6 @@ package net.minecraftforge.common; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; -import net.minecraft.util.ExtraCodecs; import net.minecraft.util.StringRepresentable; import java.util.function.Function; @@ -34,8 +33,7 @@ import java.util.function.Supplier; * {@code create} methods can be defined as long as each * matches a constructor. */ -public interface IExtensibleEnum -{ +public interface IExtensibleEnum { /** * Called by generated factory code to do any post-constructor setup required by * the enum. Should not be called manually. diff --git a/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java b/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java index a35fdcbd0f..a5d8254aa8 100644 --- a/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java +++ b/src/main/java/net/minecraftforge/common/crafting/CraftingHelper.java @@ -5,128 +5,25 @@ package net.minecraftforge.common.crafting; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; import com.google.gson.JsonSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.serialization.Codec; -import com.mojang.serialization.JsonOps; - import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; -import net.minecraftforge.common.crafting.conditions.ICondition; import net.minecraft.nbt.TagParser; -import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.GsonHelper; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.registries.ForgeRegistries; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.MarkerManager; - public class CraftingHelper { - @SuppressWarnings("unused") - private static final Logger LOGGER = LogManager.getLogger(); - @SuppressWarnings("unused") - private static final Marker CRAFTHELPER = MarkerManager.getMarker("CRAFTHELPER"); private static Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); - /* TODO: Custom Ingredients - private static final BiMap> ingredients = HashBiMap.create(); - - - public static IIngredientSerializer register(ResourceLocation key, IIngredientSerializer serializer) { - if (ingredients.containsKey(key)) - throw new IllegalStateException("Duplicate recipe ingredient serializer: " + key); - if (ingredients.containsValue(serializer)) - throw new IllegalStateException("Duplicate recipe ingredient serializer: " + key + " " + serializer); - ingredients.put(key, serializer); - return serializer; - } - - @Nullable - public static ResourceLocation getID(IIngredientSerializer serializer) { - return ingredients.inverse().get(serializer); - } - - public static void write(FriendlyByteBuf buffer, T ingredient) { - @SuppressWarnings("unchecked") //I wonder if there is a better way generic wise... - IIngredientSerializer serializer = (IIngredientSerializer)ingredient.getSerializer(); - ResourceLocation key = ingredients.inverse().get(serializer); - if (key == null) - throw new IllegalArgumentException("Tried to serialize unregistered Ingredient: " + ingredient + " " + serializer); - if (serializer != VanillaIngredientSerializer.INSTANCE) { - buffer.writeVarInt(-1); //Marker to know there is a custom ingredient - buffer.writeResourceLocation(key); - } - serializer.write(buffer, ingredient); - } - - public static Ingredient getIngredient(ResourceLocation type, FriendlyByteBuf buffer) { - IIngredientSerializer serializer = ingredients.get(type); - if (serializer == null) - throw new IllegalArgumentException("Can not deserialize unknown Ingredient type: " + type); - return serializer.parse(buffer); - } - - public static Ingredient getIngredient(JsonElement json, boolean allowEmpty) { - if (json == null || json.isJsonNull()) - throw new JsonSyntaxException("Json cannot be null"); - - if (json.isJsonArray()) { - List ingredients = Lists.newArrayList(); - List vanilla = Lists.newArrayList(); - json.getAsJsonArray().forEach((ele) -> { - Ingredient ing = CraftingHelper.getIngredient(ele, allowEmpty); - - if (ing.getClass() == Ingredient.class) //Vanilla, Due to how we read it splits each itemstack, so we pull out to re-merge later - vanilla.add(ing); - else - ingredients.add(ing); - }); - - if (!vanilla.isEmpty()) - ingredients.add(Ingredient.merge(vanilla)); - - if (ingredients.size() == 0) { - if (allowEmpty) - return Ingredient.EMPTY; - throw new JsonSyntaxException("Item array cannot be empty, at least one item must be defined"); - } - - if (ingredients.size() == 1) - return ingredients.get(0); - - return new CompoundIngredient(ingredients); - } - - if (!json.isJsonObject()) - throw new JsonSyntaxException("Expcted ingredient to be a object or array of objects"); - - JsonObject obj = (JsonObject)json; - - String type = GsonHelper.getAsString(obj, "type", "minecraft:item"); - if (type.isEmpty()) - throw new JsonSyntaxException("Ingredient type can not be an empty string"); - - IIngredientSerializer serializer = ingredients.get(new ResourceLocation(type)); - if (serializer == null) - throw new JsonSyntaxException("Unknown ingredient type: " + type); - - return serializer.parse(obj); - } - */ public static ItemStack getItemStack(JsonObject json, boolean readNBT) { return getItemStack(json, readNBT, false); diff --git a/src/main/java/net/minecraftforge/common/crafting/IntersectionIngredient.java b/src/main/java/net/minecraftforge/common/crafting/IntersectionIngredient.java deleted file mode 100644 index 6ede3869dc..0000000000 --- a/src/main/java/net/minecraftforge/common/crafting/IntersectionIngredient.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.minecraftforge.common.crafting; -/* -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonSyntaxException; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntComparators; -import it.unimi.dsi.fastutil.ints.IntList; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.util.GsonHelper; -import net.minecraft.world.entity.player.StackedContents; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.Ingredient; -import org.jetbrains.annotations.Nullable; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -/** Ingredient that matches if all child ingredients match * / -public class IntersectionIngredient extends AbstractIngredient -{ - private final List children; - private final boolean isSimple; - private ItemStack[] intersectedMatchingStacks = null; - private IntList packedMatchingStacks = null; - protected IntersectionIngredient(List children) - { - if (children.size() < 2) - throw new IllegalArgumentException("Cannot create an IntersectionIngredient with one or no children"); - this.children = Collections.unmodifiableList(children); - this.isSimple = children.stream().allMatch(Ingredient::isSimple); - } - - /** - * Gets an intersection ingredient - * @param ingredients List of ingredients to match - * @return Ingredient that only matches if all the passed ingredients match - * / - public static Ingredient of(Ingredient... ingredients) - { - if (ingredients.length == 0) - throw new IllegalArgumentException("Cannot create an IntersectionIngredient with no children, use Ingredient.of() to create an empty ingredient"); - if (ingredients.length == 1) - return ingredients[0]; - - return new IntersectionIngredient(Arrays.asList(ingredients)); - } - - @Override - public boolean test(@Nullable ItemStack stack) - { - if (stack == null || stack.isEmpty()) - return false; - - for (Ingredient ingredient : children) - if (!ingredient.test(stack)) - return false; - - return true; - } - - @Override - public ItemStack[] getItems() - { - if (this.intersectedMatchingStacks == null) - { - this.intersectedMatchingStacks = Arrays - .stream(children.get(0).getItems()) - .filter(stack -> - { - // the first ingredient is treated as a base, filtered by the second onwards - for (int i = 1; i < children.size(); i++) - if (!children.get(i).test(stack)) - return false; - return true; - }) - .toArray(ItemStack[]::new); - } - return intersectedMatchingStacks; - } - - @Override - public boolean isEmpty() - { - return children.stream().anyMatch(Ingredient::isEmpty); - } - - @Override - public boolean isSimple() - { - return isSimple; - } - - @Override - protected void invalidate() - { - super.invalidate(); - this.intersectedMatchingStacks = null; - this.packedMatchingStacks = null; - } - - @Override - public IntList getStackingIds() - { - if (this.packedMatchingStacks == null || checkInvalidation()) - { - markValid(); - ItemStack[] matchingStacks = getItems(); - this.packedMatchingStacks = new IntArrayList(matchingStacks.length); - for(ItemStack stack : matchingStacks) - this.packedMatchingStacks.add(StackedContents.getStackingIndex(stack)); - this.packedMatchingStacks.sort(IntComparators.NATURAL_COMPARATOR); - } - return packedMatchingStacks; - } - - @Override - public JsonElement toJson() - { - JsonObject json = new JsonObject(); - json.addProperty("type", CraftingHelper.getID(Serializer.INSTANCE).toString()); - JsonArray array = new JsonArray(); - for (Ingredient ingredient : children) - array.add(ingredient.toJson()); - - json.add("children", array); - return json; - } - - @Override - public IIngredientSerializer getSerializer() - { - return Serializer.INSTANCE; - } - - public static class Serializer implements IIngredientSerializer - { - public static final IIngredientSerializer INSTANCE = new Serializer(); - - @Override - public IntersectionIngredient parse(JsonObject json) - { - JsonArray children = GsonHelper.getAsJsonArray(json, "children"); - if (children.size() < 2) - throw new JsonSyntaxException("Must have at least two children for an intersection ingredient"); - - return new IntersectionIngredient( - IntStream.range(0, children.size()) - .mapToObj(i -> Ingredient.fromJson(children.get(i), false)) - .toList() - ); - } - - @Override - public IntersectionIngredient parse(FriendlyByteBuf buffer) - { - return new IntersectionIngredient(Stream.generate(() -> Ingredient.fromNetwork(buffer)).limit(buffer.readVarInt()).toList()); - } - - @Override - public void write(FriendlyByteBuf buffer, IntersectionIngredient intersection) - { - buffer.writeVarInt(intersection.children.size()); - for (Ingredient ingredient : intersection.children) - ingredient.toNetwork(buffer); - } - } -} -*/ diff --git a/src/main/java/net/minecraftforge/common/crafting/PartialNBTIngredient.java b/src/main/java/net/minecraftforge/common/crafting/PartialNBTIngredient.java deleted file mode 100644 index 6f0d18af57..0000000000 --- a/src/main/java/net/minecraftforge/common/crafting/PartialNBTIngredient.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) Forge Development LLC and contributors - * SPDX-License-Identifier: LGPL-2.1-only - */ - -package net.minecraftforge.common.crafting; -/* -import com.google.common.collect.ImmutableSet; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonSyntaxException; -import net.minecraft.advancements.critereon.NbtPredicate; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.util.GsonHelper; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.Ingredient; -import net.minecraft.world.level.ItemLike; -import net.minecraftforge.registries.ForgeRegistries; -import org.jetbrains.annotations.Nullable; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** Ingredient that matches the given items, performing a partial NBT match. Use {@link StrictNBTIngredient} if you want exact match on NBT * / -public class PartialNBTIngredient extends AbstractIngredient -{ - private final Set items; - private final CompoundTag nbt; - private final NbtPredicate predicate; - protected PartialNBTIngredient(Set items, CompoundTag nbt) - { - super(items.stream().map(item -> - { - ItemStack stack = new ItemStack(item); - // copy NBT to prevent the stack from modifying the original, as capabilities or vanilla item durability will modify the tag - stack.setTag(nbt.copy()); - return new Ingredient.ItemValue(stack); - })); - if (items.isEmpty()) - { - throw new IllegalArgumentException("Cannot create a PartialNBTIngredient with no items"); - } - this.items = Collections.unmodifiableSet(items); - this.nbt = nbt; - this.predicate = new NbtPredicate(nbt); - } - - /** Creates a new ingredient matching any item from the list, containing the given NBT * / - public static PartialNBTIngredient of(CompoundTag nbt, ItemLike... items) - { - return new PartialNBTIngredient(Arrays.stream(items).map(ItemLike::asItem).collect(Collectors.toSet()), nbt); - } - - /** Creates a new ingredient matching the given item, containing the given NBT * / - public static PartialNBTIngredient of(ItemLike item, CompoundTag nbt) - { - return new PartialNBTIngredient(Set.of(item.asItem()), nbt); - } - - @Override - public boolean test(@Nullable ItemStack input) - { - if (input == null) - return false; - return items.contains(input.getItem()) && predicate.matches(input.getShareTag()); - } - - @Override - public boolean isSimple() - { - return false; - } - - @Override - public IIngredientSerializer getSerializer() - { - return Serializer.INSTANCE; - } - - @Override - public JsonElement toJson() - { - JsonObject json = new JsonObject(); - json.addProperty("type", CraftingHelper.getID(Serializer.INSTANCE).toString()); - if (items.size() == 1) - { - json.addProperty("item", ForgeRegistries.ITEMS.getKey(items.iterator().next()).toString()); - } - else - { - JsonArray items = new JsonArray(); - // ensure the order of items in the set is deterministic when saved to JSON - this.items.stream().map(ForgeRegistries.ITEMS::getKey).sorted().forEach(name -> items.add(name.toString())); - json.add("items", items); - } - json.addProperty("nbt", nbt.toString()); - return json; - } - - public static class Serializer implements IIngredientSerializer - { - public static final Serializer INSTANCE = new Serializer(); - - @Override - public PartialNBTIngredient parse(JsonObject json) - { - // parse items - Set items; - if (json.has("item")) - items = Set.of(CraftingHelper.getItem(GsonHelper.getAsString(json, "item"), true)); - else if (json.has("items")) - { - ImmutableSet.Builder builder = ImmutableSet.builder(); - JsonArray itemArray = GsonHelper.getAsJsonArray(json, "items"); - for (int i = 0; i < itemArray.size(); i++) - { - builder.add(CraftingHelper.getItem(GsonHelper.convertToString(itemArray.get(i), "items[" + i + ']'), true)); - } - items = builder.build(); - } - else - throw new JsonSyntaxException("Must set either 'item' or 'items'"); - - // parse NBT - if (!json.has("nbt")) - throw new JsonSyntaxException("Missing nbt, expected to find a String or JsonObject"); - CompoundTag nbt = CraftingHelper.getNBT(json.get("nbt")); - - return new PartialNBTIngredient(items, nbt); - } - - @Override - public PartialNBTIngredient parse(FriendlyByteBuf buffer) - { - Set items = Stream.generate(() -> buffer.readRegistryIdUnsafe(ForgeRegistries.ITEMS)).limit(buffer.readVarInt()).collect(Collectors.toSet()); - CompoundTag nbt = buffer.readNbt(); - return new PartialNBTIngredient(items, Objects.requireNonNull(nbt)); - } - - @Override - public void write(FriendlyByteBuf buffer, PartialNBTIngredient ingredient) - { - buffer.writeVarInt(ingredient.items.size()); - for (Item item : ingredient.items) - buffer.writeRegistryIdUnsafe(ForgeRegistries.ITEMS, item); - buffer.writeNbt(ingredient.nbt); - } - } -} -*/ diff --git a/src/main/java/net/minecraftforge/common/crafting/ingredients/AbstractIngredient.java b/src/main/java/net/minecraftforge/common/crafting/ingredients/AbstractIngredient.java new file mode 100644 index 0000000000..e57581a3fc --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/ingredients/AbstractIngredient.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.common.crafting.ingredients; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.ItemLike; +import java.util.stream.Stream; + +/** + * Extension of {@link Ingredient} which makes most methods custom ingredients need to implement abstract, and removes the static constructors + * Mods are encouraged to extend this class for their custom ingredients + */ +public abstract class AbstractIngredient extends Ingredient { + /** Empty constructor, for the sake of dynamic ingredients */ + protected AbstractIngredient() { + super(Stream.of()); + } + + /** Value constructor, for ingredients that have some vanilla representation */ + protected AbstractIngredient(Stream values) { + super(values); + } + + @Override + public abstract boolean isSimple(); + + @Override + public abstract IIngredientSerializer serializer(); + + + /* Hide vanilla ingredient static constructors to reduce errors with constructing custom ingredients */ + + /** @deprecated use {@link Ingredient#fromValues(Stream)} */ + @Deprecated + public static Ingredient fromValues(Stream values) { + throw new UnsupportedOperationException("Use Ingredient.fromValues()"); + } + + /** @deprecated use {@link Ingredient#of()} */ + @Deprecated + public static Ingredient of() { + throw new UnsupportedOperationException("Use Ingredient.of()"); + } + + /** @deprecated use {@link Ingredient#of(ItemLike...)} (Stream)} */ + @Deprecated + public static Ingredient of(ItemLike... items) { + throw new UnsupportedOperationException("Use Ingredient.of()"); + } + + /** @deprecated use {@link Ingredient#of(ItemStack...)} (Stream)} */ + @Deprecated + public static Ingredient of(ItemStack... stacks) { + throw new UnsupportedOperationException("Use Ingredient.of()"); + } + + /** @deprecated use {@link Ingredient#of(Stream)} (Stream)} */ + @Deprecated + public static Ingredient of(Stream stacks) { + throw new UnsupportedOperationException("Use Ingredient.of()"); + } + + /** @deprecated use {@link Ingredient#of(TagKey)} (Stream)} */ + @Deprecated + public static Ingredient of(TagKey tag) { + throw new UnsupportedOperationException("Use Ingredient.of()"); + } + + /** @deprecated use {@link Ingredient#fromNetwork(FriendlyByteBuf)} */ + @Deprecated + public static Ingredient fromNetwork(FriendlyByteBuf buffer) { + throw new UnsupportedOperationException("Use Ingredient.fromNetwork()"); + } +} diff --git a/src/main/java/net/minecraftforge/common/crafting/CompoundIngredient.java b/src/main/java/net/minecraftforge/common/crafting/ingredients/CompoundIngredient.java similarity index 61% rename from src/main/java/net/minecraftforge/common/crafting/CompoundIngredient.java rename to src/main/java/net/minecraftforge/common/crafting/ingredients/CompoundIngredient.java index 10aa0273f8..4ec9f374a6 100644 --- a/src/main/java/net/minecraftforge/common/crafting/CompoundIngredient.java +++ b/src/main/java/net/minecraftforge/common/crafting/ingredients/CompoundIngredient.java @@ -3,20 +3,15 @@ * SPDX-License-Identifier: LGPL-2.1-only */ -package net.minecraftforge.common.crafting; -/* +package net.minecraftforge.common.crafting.ingredients; + import java.util.ArrayList; -import java.util.Collection; +import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import com.google.common.collect.Lists; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonSyntaxException; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntComparators; @@ -28,45 +23,27 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** Ingredient that matches if any of the child ingredients match */ -/* -public class CompoundIngredient extends Ingredient { +public class CompoundIngredient extends AbstractIngredient { + public static Ingredient of(Ingredient... children) { + if (children.length == 0) + throw new IllegalArgumentException("Cannot create a compound ingredient with no children, use Ingredient.of() to create an empty ingredient"); + + if (children.length == 1) + return children[0]; + + return new CompoundIngredient(Arrays.asList(children)); + } + private List children; private ItemStack[] stacks; private IntList itemIds; private final boolean isSimple; - protected CompoundIngredient(List children) - { + private CompoundIngredient(List children) { this.children = Collections.unmodifiableList(children); this.isSimple = children.stream().allMatch(Ingredient::isSimple); } - /** Creates a compound ingredient from the given list of ingredients */ -/* - public static Ingredient of(Ingredient... children) - { - // if 0 or 1 ingredient, can save effort - if (children.length == 0) - throw new IllegalArgumentException("Cannot create a compound ingredient with no children, use Ingredient.of() to create an empty ingredient"); - if (children.length == 1) - return children[0]; - - // need to merge vanilla ingredients, as otherwise the JSON produced by this ingredient could be invalid - List vanillaIngredients = new ArrayList<>(); - List allIngredients = new ArrayList<>(); - for (Ingredient child : children) { - if (child.getSerializer() == VanillaIngredientSerializer.INSTANCE) - vanillaIngredients.add(child); - else - allIngredients.add(child); - } - if (!vanillaIngredients.isEmpty()) - allIngredients.add(merge(vanillaIngredients)); - if (allIngredients.size() == 1) - return allIngredients.get(0); - return new CompoundIngredient(allIngredients); - } - @Override @NotNull public ItemStack[] getItems() { @@ -110,14 +87,42 @@ public class CompoundIngredient extends Ingredient { return isSimple; } - @NotNull - public Collection getChildren() { - return this.children; - } - @Override public boolean isEmpty() { return children.stream().allMatch(Ingredient::isEmpty); } + + @Override + public IIngredientSerializer serializer() { + return SERIALIZER; + } + + public static final Codec CODEC = RecordCodecBuilder.create(builder -> + builder.group( + Ingredient.CODEC_NONEMPTY.listOf().fieldOf("children").forGetter(i -> i.children) + ).apply(builder, CompoundIngredient::new) + ); + + public static final IIngredientSerializer SERIALIZER = new IIngredientSerializer<>() { + @Override + public Codec codec() { + return CODEC; + } + + @Override + public void write(FriendlyByteBuf buffer, CompoundIngredient value) { + buffer.writeVarInt(value.children.size()); + for (var child : value.children) + child.toNetwork(buffer); + } + + @Override + public CompoundIngredient read(FriendlyByteBuf buffer) { + int size = buffer.readVarInt(); + var children = new ArrayList(size); + for (int x = 0; x < size; x++) + children.add(Ingredient.fromNetwork(buffer)); + return new CompoundIngredient(children); + } + }; } -*/ diff --git a/src/main/java/net/minecraftforge/common/crafting/DifferenceIngredient.java b/src/main/java/net/minecraftforge/common/crafting/ingredients/DifferenceIngredient.java similarity index 59% rename from src/main/java/net/minecraftforge/common/crafting/DifferenceIngredient.java rename to src/main/java/net/minecraftforge/common/crafting/ingredients/DifferenceIngredient.java index 4d8a7b6e4d..c79af58629 100644 --- a/src/main/java/net/minecraftforge/common/crafting/DifferenceIngredient.java +++ b/src/main/java/net/minecraftforge/common/crafting/ingredients/DifferenceIngredient.java @@ -3,10 +3,11 @@ * SPDX-License-Identifier: LGPL-2.1-only */ -package net.minecraftforge.common.crafting; -/* -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; +package net.minecraftforge.common.crafting.ingredients; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntComparators; import it.unimi.dsi.fastutil.ints.IntList; @@ -18,42 +19,37 @@ import org.jetbrains.annotations.Nullable; import java.util.Arrays; -/** Ingredient that matches everything from the first ingredient that is not included in the second ingredient * / -public class DifferenceIngredient extends AbstractIngredient -{ +/** Ingredient that matches everything from the first ingredient that is not included in the second ingredient */ +public class DifferenceIngredient extends AbstractIngredient { + /** + * Gets the difference from the two ingredients + * @param base Ingredient the item must match + * @param subtracted Ingredient the item must not match + * @return Ingredient that matches anything in {@code base} that is not in {@code subtracted} + */ + public static DifferenceIngredient of(Ingredient base, Ingredient subtracted) { + return new DifferenceIngredient(base, subtracted); + } + private final Ingredient base; private final Ingredient subtracted; private ItemStack[] filteredMatchingStacks; private IntList packedMatchingStacks; - protected DifferenceIngredient(Ingredient base, Ingredient subtracted) - { + private DifferenceIngredient(Ingredient base, Ingredient subtracted) { this.base = base; this.subtracted = subtracted; } - /** - * Gets the difference from the two ingredients - * @param base Ingredient the item must match - * @param subtracted Ingredient the item must not match - * @return Ingredient that {@code base} anything in base that is not in {@code subtracted} - * / - public static DifferenceIngredient of(Ingredient base, Ingredient subtracted) - { - return new DifferenceIngredient(base, subtracted); - } - @Override - public boolean test(@Nullable ItemStack stack) - { + public boolean test(@Nullable ItemStack stack) { if (stack == null || stack.isEmpty()) return false; return base.test(stack) && !subtracted.test(stack); } @Override - public ItemStack[] getItems() - { + public ItemStack[] getItems() { if (this.filteredMatchingStacks == null) this.filteredMatchingStacks = Arrays.stream(base.getItems()) .filter(stack -> !subtracted.test(stack)) @@ -62,34 +58,29 @@ public class DifferenceIngredient extends AbstractIngredient } @Override - public boolean isEmpty() - { + public boolean isEmpty() { return base.isEmpty(); } @Override - public boolean isSimple() - { + public boolean isSimple() { return base.isSimple() && subtracted.isSimple(); } @Override - protected void invalidate() - { + protected void invalidate() { super.invalidate(); this.filteredMatchingStacks = null; this.packedMatchingStacks = null; } @Override - public IntList getStackingIds() - { - if (this.packedMatchingStacks == null || checkInvalidation()) - { + public IntList getStackingIds() { + if (this.packedMatchingStacks == null || checkInvalidation()) { markValid(); - ItemStack[] matchingStacks = getItems(); + var matchingStacks = getItems(); this.packedMatchingStacks = new IntArrayList(matchingStacks.length); - for (ItemStack stack : matchingStacks) + for (var stack : matchingStacks) this.packedMatchingStacks.add(StackedContents.getStackingIndex(stack)); this.packedMatchingStacks.sort(IntComparators.NATURAL_COMPARATOR); @@ -98,46 +89,34 @@ public class DifferenceIngredient extends AbstractIngredient } @Override - public JsonElement toJson(boolean allowEmpty) - { - JsonObject json = new JsonObject(); - json.addProperty("type", CraftingHelper.getID(Serializer.INSTANCE).toString()); - json.add("base", base.toJson(allowEmpty)); - json.add("subtracted", subtracted.toJson(allowEmpty)); - return json; + public IIngredientSerializer serializer() { + return SERIALIZER; } - @Override - public IIngredientSerializer getSerializer() - { - return Serializer.INSTANCE; - } - - public static class Serializer implements IIngredientSerializer { - public static final IIngredientSerializer INSTANCE = new Serializer(); + public static final Codec CODEC = RecordCodecBuilder.create(builder -> + builder.group( + Ingredient.CODEC.fieldOf("base").forGetter(i -> i.base), + Ingredient.CODEC.fieldOf("subtracted").forGetter(i -> i.subtracted) + ).apply(builder, DifferenceIngredient::new) + ); + public static final IIngredientSerializer SERIALIZER = new IIngredientSerializer<>() { @Override - public DifferenceIngredient parse(JsonObject json) - { - Ingredient base = Ingredient.fromJson(json.get("base"), false); - Ingredient without = Ingredient.fromJson(json.get("subtracted"), false); - return new DifferenceIngredient(base, without); + public Codec codec() { + return CODEC; } @Override - public DifferenceIngredient parse(FriendlyByteBuf buffer) - { + public DifferenceIngredient read(FriendlyByteBuf buffer) { Ingredient base = Ingredient.fromNetwork(buffer); Ingredient without = Ingredient.fromNetwork(buffer); return new DifferenceIngredient(base, without); } @Override - public void write(FriendlyByteBuf buffer, DifferenceIngredient ingredient) - { + public void write(FriendlyByteBuf buffer, DifferenceIngredient ingredient) { ingredient.base.toNetwork(buffer); ingredient.subtracted.toNetwork(buffer); } - } + }; } -*/ diff --git a/src/main/java/net/minecraftforge/common/crafting/ingredients/IIngredientBuilder.java b/src/main/java/net/minecraftforge/common/crafting/ingredients/IIngredientBuilder.java new file mode 100644 index 0000000000..8e99c7125e --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/ingredients/IIngredientBuilder.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.common.crafting.ingredients; + +import java.util.Arrays; + +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; + +public interface IIngredientBuilder { + default PartialNBTIngredient.Builder partialNbt() { + return PartialNBTIngredient.builder(); + } + + default Ingredient strictNbt(ItemStack value) { + return StrictNBTIngredient.of(value); + } + + default Ingredient compound(Ingredient... values) { + return CompoundIngredient.of(values); + } + + default Ingredient intersection(@SuppressWarnings("rawtypes") TagKey... values) { + return intersection(Arrays.stream(values).map(Ingredient::of).toArray(Ingredient[]::new)); + } + + default Ingredient intersection(Ingredient... values) { + return IntersectionIngredient.of(values); + } + + default Ingredient difference(TagKey base, TagKey subtracted) { + return difference(Ingredient.of(base), Ingredient.of(subtracted)); + } + + default Ingredient difference(Ingredient base, Ingredient subtracted) { + return DifferenceIngredient.of(base, subtracted); + } +} diff --git a/src/main/java/net/minecraftforge/common/crafting/ingredients/IIngredientSerializer.java b/src/main/java/net/minecraftforge/common/crafting/ingredients/IIngredientSerializer.java new file mode 100644 index 0000000000..6375cca443 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/ingredients/IIngredientSerializer.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.common.crafting.ingredients; + +import com.mojang.serialization.Codec; + +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.network.FriendlyByteBuf; + +public interface IIngredientSerializer { + Codec codec(); + void write(FriendlyByteBuf buffer, T value); + T read(FriendlyByteBuf buffer); + + IIngredientSerializer VANILLA = new IIngredientSerializer<>() { + @Override + public Codec codec() { + return Ingredient.VANILLA_CODEC; + } + + @Override + public void write(FriendlyByteBuf buffer, Ingredient value) { + value.toNetwork(buffer); + } + + @Override + public Ingredient read(FriendlyByteBuf buffer) { + return Ingredient.fromNetwork(buffer); + } + }; +} diff --git a/src/main/java/net/minecraftforge/common/crafting/ingredients/IntersectionIngredient.java b/src/main/java/net/minecraftforge/common/crafting/ingredients/IntersectionIngredient.java new file mode 100644 index 0000000000..897122b391 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/ingredients/IntersectionIngredient.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.common.crafting.ingredients; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntComparators; +import it.unimi.dsi.fastutil.ints.IntList; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.entity.player.StackedContents; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** Ingredient that matches if all child ingredients match */ +public class IntersectionIngredient extends AbstractIngredient { + /** + * Gets an intersection ingredient + * @param ingredients List of ingredients to match + * @return Ingredient that only matches if all the passed ingredients match + */ + public static Ingredient of(Ingredient... ingredients) { + if (ingredients.length == 0) + throw new IllegalArgumentException("Cannot create an IntersectionIngredient with no children, use Ingredient.of() to create an empty ingredient"); + if (ingredients.length == 1) + return ingredients[0]; + + return new IntersectionIngredient(Arrays.asList(ingredients)); + } + + private final List children; + private final boolean isSimple; + private ItemStack[] intersectedMatchingStacks = null; + private IntList packedMatchingStacks = null; + + private IntersectionIngredient(List children) { + if (children.size() < 2) + throw new IllegalArgumentException("Cannot create an IntersectionIngredient with one or no children"); + this.children = Collections.unmodifiableList(children); + this.isSimple = children.stream().allMatch(Ingredient::isSimple); + } + + @Override + public boolean test(@Nullable ItemStack stack) { + if (stack == null || stack.isEmpty()) + return false; + + for (Ingredient ingredient : children) + if (!ingredient.test(stack)) + return false; + + return true; + } + + @Override + public ItemStack[] getItems() { + if (this.intersectedMatchingStacks == null) { + this.intersectedMatchingStacks = Arrays + .stream(children.get(0).getItems()) + .filter(stack -> { + // the first ingredient is treated as a base, filtered by the second onwards + for (int i = 1; i < children.size(); i++) + if (!children.get(i).test(stack)) + return false; + return true; + }) + .toArray(ItemStack[]::new); + } + return intersectedMatchingStacks; + } + + @Override + public boolean isEmpty() { + return children.stream().anyMatch(Ingredient::isEmpty); + } + + @Override + public boolean isSimple() { + return isSimple; + } + + @Override + protected void invalidate() { + super.invalidate(); + this.intersectedMatchingStacks = null; + this.packedMatchingStacks = null; + } + + @Override + public IntList getStackingIds() { + if (this.packedMatchingStacks == null || checkInvalidation()) { + markValid(); + var matchingStacks = getItems(); + this.packedMatchingStacks = new IntArrayList(matchingStacks.length); + for (var stack : matchingStacks) + this.packedMatchingStacks.add(StackedContents.getStackingIndex(stack)); + this.packedMatchingStacks.sort(IntComparators.NATURAL_COMPARATOR); + } + return packedMatchingStacks; + } + + @Override + public IIngredientSerializer serializer() { + return SERIALIZER; + } + + public static final Codec CODEC = RecordCodecBuilder.create(builder -> + builder.group( + Ingredient.CODEC.listOf().fieldOf("children").forGetter(i -> i.children) + ) + .apply(builder, IntersectionIngredient::new) + ); + + public static final IIngredientSerializer SERIALIZER = new IIngredientSerializer<>() { + @Override + public Codec codec() { + return CODEC; + } + + @Override + public IntersectionIngredient read(FriendlyByteBuf buffer) { + return new IntersectionIngredient(buffer.readList(Ingredient::fromNetwork)); + } + + @Override + public void write(FriendlyByteBuf buffer, IntersectionIngredient value) { + buffer.writeCollection(value.children, (b, i) -> i.toNetwork(b)); + } + }; +} diff --git a/src/main/java/net/minecraftforge/common/crafting/ingredients/PartialNBTIngredient.java b/src/main/java/net/minecraftforge/common/crafting/ingredients/PartialNBTIngredient.java new file mode 100644 index 0000000000..a04cfaefb2 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/crafting/ingredients/PartialNBTIngredient.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.common.crafting.ingredients; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.advancements.critereon.NbtPredicate; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.TagParser; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.level.ItemLike; +import net.minecraftforge.registries.ForgeRegistries; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** Ingredient that matches the given items, performing a partial NBT match. Use {@link StrictNBTIngredient} if you want exact match on NBT */ +public class PartialNBTIngredient extends AbstractIngredient { + public static Builder builder() { + return new Builder(); + } + + private final List items; + private final CompoundTag nbt; + private final NbtPredicate predicate; + + private PartialNBTIngredient(List items, CompoundTag nbt) { + super(items.stream().map(item -> { + var stack = new ItemStack(item); + // copy NBT to prevent the stack from modifying the original, as capabilities or vanilla item durability will modify the tag + stack.setTag(nbt.copy()); + return new Ingredient.ItemValue(stack); + })); + + if (items.isEmpty()) + throw new IllegalArgumentException("Cannot create a PartialNBTIngredient with no items"); + + this.items = Collections.unmodifiableList(items); + this.nbt = nbt; + this.predicate = new NbtPredicate(nbt); + } + + /** Creates a new ingredient matching any item from the list, containing the given NBT */ + public static PartialNBTIngredient of(CompoundTag nbt, ItemLike... items) { + return new PartialNBTIngredient(Arrays.stream(items).map(ItemLike::asItem).toList(), nbt); + } + + /** Creates a new ingredient matching the given item, containing the given NBT */ + public static PartialNBTIngredient of(ItemLike item, CompoundTag nbt) { + return new PartialNBTIngredient(List.of(item.asItem()), nbt); + } + + @Override + public boolean test(@Nullable ItemStack input) { + if (input == null) + return false; + return items.contains(input.getItem()) && predicate.matches(input.getShareTag()); + } + + @Override + public boolean isSimple() { + return false; + } + + @Override + public IIngredientSerializer serializer() { + return SERIALIZER; + } + + public static final Codec CODEC = RecordCodecBuilder.create(builder -> + builder.group( + ForgeRegistries.ITEMS.getCodec().listOf().fieldOf("items").forGetter(i -> i.items), + TagParser.AS_CODEC.fieldOf("nbt").forGetter(i -> i.nbt) + ).apply(builder, PartialNBTIngredient::new) + ); + + public static final IIngredientSerializer SERIALIZER = new IIngredientSerializer<>() { + @Override + public Codec codec() { + return CODEC; + } + + @Override + public PartialNBTIngredient read(FriendlyByteBuf buffer) { + var items = buffer.readList(b -> b.readRegistryIdUnsafe(ForgeRegistries.ITEMS)); + var nbt = buffer.readNbt(); + return new PartialNBTIngredient(items, Objects.requireNonNull(nbt)); + } + + @Override + public void write(FriendlyByteBuf buffer, PartialNBTIngredient value) { + buffer.writeCollection(value.items, (b, item) -> b.writeRegistryIdUnsafe(ForgeRegistries.ITEMS, item)); + buffer.writeNbt(value.nbt); + } + }; + + public static class Builder { + private List items = new ArrayList<>(); + private CompoundTag nbt; + + public Builder nbt(CompoundTag value) { + if (this.nbt != null) + throw new IllegalStateException("NBT Tag already set"); + this.nbt = value; + return this; + } + + public Builder item(ItemLike item) { + this.items.add(item); + return this; + } + + public Builder items(ItemLike... values) { + for (var item : values) + this.items.add(item); + return this; + } + + public PartialNBTIngredient build() { + if (nbt == null) + throw new IllegalStateException("NBT Data not set"); + if (items.isEmpty()) + throw new IllegalStateException("No items added"); + + return PartialNBTIngredient.of(nbt, items.stream().toArray(ItemLike[]::new)); + } + } +} diff --git a/src/main/java/net/minecraftforge/common/crafting/StrictNBTIngredient.java b/src/main/java/net/minecraftforge/common/crafting/ingredients/StrictNBTIngredient.java similarity index 61% rename from src/main/java/net/minecraftforge/common/crafting/StrictNBTIngredient.java rename to src/main/java/net/minecraftforge/common/crafting/ingredients/StrictNBTIngredient.java index 7cf7705503..9691e61f00 100644 --- a/src/main/java/net/minecraftforge/common/crafting/StrictNBTIngredient.java +++ b/src/main/java/net/minecraftforge/common/crafting/ingredients/StrictNBTIngredient.java @@ -3,10 +3,11 @@ * SPDX-License-Identifier: LGPL-2.1-only */ -package net.minecraftforge.common.crafting; +package net.minecraftforge.common.crafting.ingredients; import java.util.stream.Stream; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.Ingredient; import org.jetbrains.annotations.Nullable; @@ -15,22 +16,17 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; /** Ingredient that matches the given stack, performing an exact NBT match. Use {@link PartialNBTIngredient} if you need partial match. */ -public class StrictNBTIngredient extends Ingredient { - public static final Codec CODEC = RecordCodecBuilder.create(b -> b.group( - ItemStack.CODEC.fieldOf("stack").forGetter(i -> i.stack) - ).apply(b, StrictNBTIngredient::new)); - - private final ItemStack stack; - protected StrictNBTIngredient(ItemStack stack) { - super(Stream.of(new Ingredient.ItemValue(stack))); - this.stack = stack; - } - - /** Creates a new ingredient matching the given stack and tag */ +public class StrictNBTIngredient extends AbstractIngredient { public static StrictNBTIngredient of(ItemStack stack) { return new StrictNBTIngredient(stack); } + private final ItemStack stack; + private StrictNBTIngredient(ItemStack stack) { + super(Stream.of(new Ingredient.ItemValue(stack))); + this.stack = stack; + } + @Override public boolean test(@Nullable ItemStack input) { if (input == null) @@ -43,4 +39,30 @@ public class StrictNBTIngredient extends Ingredient { public boolean isSimple() { return false; } + + @Override + public IIngredientSerializer serializer() { + return SERIALIZER; + } + + public static final Codec CODEC = RecordCodecBuilder.create(b -> b.group( + ItemStack.CODEC.fieldOf("stack").forGetter(i -> i.stack) + ).apply(b, StrictNBTIngredient::new)); + + public static final IIngredientSerializer SERIALIZER = new IIngredientSerializer<>() { + @Override + public Codec codec() { + return CODEC; + } + + @Override + public void write(FriendlyByteBuf buffer, StrictNBTIngredient value) { + buffer.writeItem(value.stack); + } + + @Override + public StrictNBTIngredient read(FriendlyByteBuf buffer) { + return new StrictNBTIngredient(buffer.readItem()); + } + }; } diff --git a/src/main/java/net/minecraftforge/common/data/ForgeItemTagsProvider.java b/src/main/java/net/minecraftforge/common/data/ForgeItemTagsProvider.java index 115aa476d3..728df58d67 100644 --- a/src/main/java/net/minecraftforge/common/data/ForgeItemTagsProvider.java +++ b/src/main/java/net/minecraftforge/common/data/ForgeItemTagsProvider.java @@ -8,7 +8,6 @@ package net.minecraftforge.common.data; import net.minecraft.core.HolderLookup; import net.minecraft.data.PackOutput; import net.minecraft.data.tags.ItemTagsProvider; -import net.minecraft.data.tags.TagsProvider; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.ItemTags; import net.minecraft.tags.TagKey; diff --git a/src/main/java/net/minecraftforge/common/extensions/IForgeItem.java b/src/main/java/net/minecraftforge/common/extensions/IForgeItem.java index 984eaa19a7..3d38e22362 100644 --- a/src/main/java/net/minecraftforge/common/extensions/IForgeItem.java +++ b/src/main/java/net/minecraftforge/common/extensions/IForgeItem.java @@ -437,7 +437,7 @@ public interface IForgeItem { * @param damage the new damage value */ default void setDamage(ItemStack stack, int damage) { - stack.getOrCreateTag().putInt("Damage", Math.max(0, damage)); + stack.getOrCreateTag().putInt(ItemStack.TAG_DAMAGE, Math.max(0, damage)); } /** diff --git a/src/main/java/net/minecraftforge/common/util/INBTBuilder.java b/src/main/java/net/minecraftforge/common/util/INBTBuilder.java new file mode 100644 index 0000000000..cf52502ce6 --- /dev/null +++ b/src/main/java/net/minecraftforge/common/util/INBTBuilder.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.common.util; + +import java.util.List; +import java.util.UUID; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; + +public interface INBTBuilder { + default Builder nbt() { + return new Builder(); + } + + public static class Builder implements INBTBuilder { + private final CompoundTag tag = new CompoundTag(); + + public CompoundTag build() { + return tag; + } + + public Builder tag(String key, Tag value) { + tag.put(key, value); + return this; + } + + public Builder putByte(String key, byte value) { + tag.putByte(key, value); + return this; + } + + public Builder putShort(String key, short value) { + tag.putShort(key, value); + return this; + } + + public Builder putInt(String key, int value) { + tag.putInt(key, value); + return this; + } + + public Builder putLong(String key, long value) { + tag.putLong(key, value); + return this; + } + + public Builder putFloat(String key, float value) { + tag.putFloat(key, value); + return this; + } + + public Builder putDouble(String key, double value) { + tag.putDouble(key, value); + return this; + } + + public Builder putByteArray(String key, byte... value) { + tag.putByteArray(key, value); + return this; + } + + public Builder putByteArray(String key, List value) { + tag.putByteArray(key, value); + return this; + } + + public Builder putIntArray(String key, int... value) { + tag.putIntArray(key, value); + return this; + } + + public Builder putIntArray(String key, List value) { + tag.putIntArray(key, value); + return this; + } + + public Builder putLongArray(String key, long... value) { + tag.putLongArray(key, value); + return this; + } + + public Builder putLongArray(String key, List value) { + tag.putLongArray(key, value); + return this; + } + + public Builder put(String key, boolean value) { + tag.putBoolean(key, value); + return this; + } + + public Builder put(String key, String value) { + tag.putString(key, value); + return this; + } + + public Builder put(String key, UUID value) { + tag.putUUID(key, value); + return this; + } + } +} diff --git a/src/main/java/net/minecraftforge/common/util/SortedProperties.java b/src/main/java/net/minecraftforge/common/util/SortedProperties.java index c87ab5400b..7e1ace3c0f 100644 --- a/src/main/java/net/minecraftforge/common/util/SortedProperties.java +++ b/src/main/java/net/minecraftforge/common/util/SortedProperties.java @@ -6,7 +6,6 @@ package net.minecraftforge.common.util; import java.io.IOException; -import java.io.OutputStream; import java.io.Writer; import java.util.Collections; import java.util.Enumeration; @@ -21,32 +20,27 @@ import java.util.TreeSet; * This is implemented very basically, and thus is not a speedy system. * This is not recommended for used in high traffic areas, and is mainly intended for writing to disc. */ -public class SortedProperties extends Properties -{ +public class SortedProperties extends Properties { private static final long serialVersionUID = -8913480931455982442L; @Override - public Set> entrySet() - { + public Set> entrySet() { Set> ret = new TreeSet<>((left, right) -> left.getKey().toString().compareTo(right.getKey().toString())); ret.addAll(super.entrySet()); return ret; } @Override - public Set keySet() - { + public Set keySet() { return new TreeSet<>(super.keySet()); } @Override - public synchronized Enumeration keys() - { + public synchronized Enumeration keys() { return Collections.enumeration(new TreeSet<>(super.keySet())); } - public static void store(Properties props, Writer stream, String comment) throws IOException - { + public static void store(Properties props, Writer stream, String comment) throws IOException { SortedProperties sorted = new SortedProperties(); sorted.putAll(props); sorted.store(stream, comment); diff --git a/src/main/java/net/minecraftforge/common/world/ForgeBiomeModifiers.java b/src/main/java/net/minecraftforge/common/world/ForgeBiomeModifiers.java index 8497acfe3f..c14ccab1a1 100644 --- a/src/main/java/net/minecraftforge/common/world/ForgeBiomeModifiers.java +++ b/src/main/java/net/minecraftforge/common/world/ForgeBiomeModifiers.java @@ -8,23 +8,26 @@ package net.minecraftforge.common.world; import java.util.EnumSet; import java.util.List; import java.util.Set; +import java.util.function.Function; +import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; +import net.minecraft.core.RegistryCodecs; +import net.minecraft.util.ExtraCodecs; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; import net.minecraft.world.level.levelgen.GenerationStep.Decoration; import net.minecraft.world.level.levelgen.placement.PlacedFeature; -import net.minecraftforge.common.ForgeMod; import net.minecraftforge.common.world.ModifiableBiomeInfo.BiomeInfo.Builder; import net.minecraftforge.registries.ForgeRegistries; -public final class ForgeBiomeModifiers -{ +public final class ForgeBiomeModifiers { private ForgeBiomeModifiers() {} // Utility class. /** @@ -43,22 +46,24 @@ public final class ForgeBiomeModifiers * @param features PlacedFeatures to add to biomes. * @param step Decoration step to run features in. */ - public static record AddFeaturesBiomeModifier(HolderSet biomes, HolderSet features, Decoration step) implements BiomeModifier - { + public static record AddFeaturesBiomeModifier(HolderSet biomes, HolderSet features, Decoration step) implements BiomeModifier { + public static final Codec CODEC = RecordCodecBuilder.create(builder -> builder.group( + Biome.LIST_CODEC.fieldOf("biomes").forGetter(AddFeaturesBiomeModifier::biomes), + PlacedFeature.LIST_CODEC.fieldOf("features").forGetter(AddFeaturesBiomeModifier::features), + Decoration.CODEC.fieldOf("step").forGetter(AddFeaturesBiomeModifier::step) + ).apply(builder, AddFeaturesBiomeModifier::new)); + @Override - public void modify(Holder biome, Phase phase, Builder builder) - { - if (phase == Phase.ADD && this.biomes.contains(biome)) - { + public void modify(Holder biome, Phase phase, Builder builder) { + if (phase == Phase.ADD && this.biomes.contains(biome)) { BiomeGenerationSettingsBuilder generationSettings = builder.getGenerationSettings(); this.features.forEach(holder -> generationSettings.addFeature(this.step, holder)); } } @Override - public Codec codec() - { - return ForgeMod.ADD_FEATURES_BIOME_MODIFIER_TYPE.get(); + public Codec codec() { + return CODEC; } } @@ -77,35 +82,38 @@ public final class ForgeBiomeModifiers * @param features PlacedFeatures to remove from biomes. * @param steps Decoration steps to remove features from. */ - public static record RemoveFeaturesBiomeModifier(HolderSet biomes, HolderSet features, Set steps) implements BiomeModifier - { + public static record RemoveFeaturesBiomeModifier(HolderSet biomes, HolderSet features, Set steps) implements BiomeModifier { + public static final Codec CODEC = RecordCodecBuilder.create(builder -> + builder.group( + Biome.LIST_CODEC.fieldOf("biomes").forGetter(RemoveFeaturesBiomeModifier::biomes), + PlacedFeature.LIST_CODEC.fieldOf("features").forGetter(RemoveFeaturesBiomeModifier::features), + ExtraCodecs.either(Decoration.CODEC.listOf(), Decoration.CODEC).>xmap( + either -> either.map(Set::copyOf, Set::of), // convert list/singleton to set when decoding + set -> set.size() == 1 ? Either.right(set.toArray(Decoration[]::new)[0]) : Either.left(List.copyOf(set)) + ).optionalFieldOf("steps", EnumSet.allOf(Decoration.class)).forGetter(RemoveFeaturesBiomeModifier::steps) + ).apply(builder, RemoveFeaturesBiomeModifier::new)); + /** * Creates a modifier that removes the given features from all decoration steps in the given biomes. * @param biomes Biomes to remove features from. * @param features PlacedFeatures to remove from biomes. */ - public static RemoveFeaturesBiomeModifier allSteps(HolderSet biomes, HolderSet features) - { + public static RemoveFeaturesBiomeModifier allSteps(HolderSet biomes, HolderSet features) { return new RemoveFeaturesBiomeModifier(biomes, features, EnumSet.allOf(Decoration.class)); } @Override - public void modify(Holder biome, Phase phase, Builder builder) - { - if (phase == Phase.REMOVE && this.biomes.contains(biome)) - { + public void modify(Holder biome, Phase phase, Builder builder) { + if (phase == Phase.REMOVE && this.biomes.contains(biome)) { BiomeGenerationSettingsBuilder generationSettings = builder.getGenerationSettings(); for (Decoration step : this.steps) - { generationSettings.getFeatures(step).removeIf(this.features::contains); - } } } @Override - public Codec codec() - { - return ForgeMod.REMOVE_FEATURES_BIOME_MODIFIER_TYPE.get(); + public Codec codec() { + return CODEC; } } @@ -147,37 +155,39 @@ public final class ForgeBiomeModifiers * @param biomes Biomes to add mob spawns to. * @param spawners List of SpawnerDatas specifying EntityType, weight, and pack size. */ - public record AddSpawnsBiomeModifier(HolderSet biomes, List spawners) implements BiomeModifier - { + public record AddSpawnsBiomeModifier(HolderSet biomes, List spawners) implements BiomeModifier { + public static final Codec CODEC = RecordCodecBuilder.create(builder -> + builder.group( + Biome.LIST_CODEC.fieldOf("biomes").forGetter(AddSpawnsBiomeModifier::biomes), + // Allow either a list or single spawner, attempting to decode the list format first. + ExtraCodecs.either(SpawnerData.CODEC.listOf(), SpawnerData.CODEC).xmap( + either -> either.map(Function.identity(), List::of), // convert list/singleton to list when decoding + list -> list.size() == 1 ? Either.right(list.get(0)) : Either.left(list) // convert list to singleton/list when encoding + ).fieldOf("spawners").forGetter(AddSpawnsBiomeModifier::spawners) + ).apply(builder, AddSpawnsBiomeModifier::new)); + /** * Convenience method for using a single spawn data. * @param biomes Biomes to add mob spawns to. * @param spawner SpawnerData specifying EntityTYpe, weight, and pack size. * @return AddSpawnsBiomeModifier that adds a single spawn entry to the specified biomes. */ - public static AddSpawnsBiomeModifier singleSpawn(HolderSet biomes, SpawnerData spawner) - { + public static AddSpawnsBiomeModifier singleSpawn(HolderSet biomes, SpawnerData spawner) { return new AddSpawnsBiomeModifier(biomes, List.of(spawner)); } @Override - public void modify(Holder biome, Phase phase, Builder builder) - { - if (phase == Phase.ADD && this.biomes.contains(biome)) - { - MobSpawnSettingsBuilder spawns = builder.getMobSpawnSettings(); - for (SpawnerData spawner : this.spawners) - { - EntityType type = spawner.type; - spawns.addSpawn(type.getCategory(), spawner); - } + public void modify(Holder biome, Phase phase, Builder builder) { + if (phase == Phase.ADD && this.biomes.contains(biome)) { + var spawns = builder.getMobSpawnSettings(); + for (var spawner : this.spawners) + spawns.addSpawn(spawner.type.getCategory(), spawner); } } @Override - public Codec codec() - { - return ForgeMod.ADD_SPAWNS_BIOME_MODIFIER_TYPE.get(); + public Codec codec() { + return CODEC; } } @@ -194,26 +204,25 @@ public final class ForgeBiomeModifiers * @param biomes Biomes to add mob spawns to. * @param entityTypes EntityTypes to remove from spawn lists. */ - public record RemoveSpawnsBiomeModifier(HolderSet biomes, HolderSet> entityTypes) implements BiomeModifier - { + public record RemoveSpawnsBiomeModifier(HolderSet biomes, HolderSet> entityTypes) implements BiomeModifier { + public static final Codec CODEC = RecordCodecBuilder.create(builder -> + builder.group( + Biome.LIST_CODEC.fieldOf("biomes").forGetter(RemoveSpawnsBiomeModifier::biomes), + RegistryCodecs.homogeneousList(ForgeRegistries.Keys.ENTITY_TYPES).fieldOf("entity_types").forGetter(RemoveSpawnsBiomeModifier::entityTypes) + ).apply(builder, RemoveSpawnsBiomeModifier::new)); + @Override - public void modify(Holder biome, Phase phase, Builder builder) - { - if (phase == Phase.REMOVE && this.biomes.contains(biome)) - { - MobSpawnSettingsBuilder spawnBuilder = builder.getMobSpawnSettings(); - for (MobCategory category : MobCategory.values()) - { - List spawns = spawnBuilder.getSpawner(category); - spawns.removeIf(spawnerData -> this.entityTypes.contains(ForgeRegistries.ENTITY_TYPES.getHolder(spawnerData.type).get())); - } + public void modify(Holder biome, Phase phase, Builder builder) { + if (phase == Phase.REMOVE && this.biomes.contains(biome)) { + var spawns = builder.getMobSpawnSettings(); + for (var category : MobCategory.values()) + spawns.getSpawner(category).removeIf(data -> this.entityTypes.contains(ForgeRegistries.ENTITY_TYPES.getHolder(data.type).get())); } } @Override - public Codec codec() - { - return ForgeMod.REMOVE_SPAWNS_BIOME_MODIFIER_TYPE.get(); + public Codec codec() { + return CODEC; } } } diff --git a/src/main/java/net/minecraftforge/common/world/NoneBiomeModifier.java b/src/main/java/net/minecraftforge/common/world/NoneBiomeModifier.java index 0a6c70abcc..fd14659045 100644 --- a/src/main/java/net/minecraftforge/common/world/NoneBiomeModifier.java +++ b/src/main/java/net/minecraftforge/common/world/NoneBiomeModifier.java @@ -8,22 +8,21 @@ package net.minecraftforge.common.world; import com.mojang.serialization.Codec; import net.minecraft.core.Holder; import net.minecraft.world.level.biome.Biome; -import net.minecraftforge.common.ForgeMod; import net.minecraftforge.common.world.ModifiableBiomeInfo.BiomeInfo.Builder; -public class NoneBiomeModifier implements BiomeModifier -{ +/** + * Noop biome modifier. Can be used in a biome modifier json with "type": "forge:none". + */ +public class NoneBiomeModifier implements BiomeModifier { public static final NoneBiomeModifier INSTANCE = new NoneBiomeModifier(); + public static final Codec CODEC = Codec.unit(NoneBiomeModifier.INSTANCE); @Override - public void modify(Holder biome, Phase phase, Builder builder) - { - // NOOP - intended for datapack makers who want to disable a biome modifier + public void modify(Holder biome, Phase phase, Builder builder) { } @Override - public Codec codec() - { - return ForgeMod.NONE_BIOME_MODIFIER_TYPE.get(); + public Codec codec() { + return CODEC; } } diff --git a/src/main/java/net/minecraftforge/common/world/NoneStructureModifier.java b/src/main/java/net/minecraftforge/common/world/NoneStructureModifier.java index 89c404a810..79e02f14b6 100644 --- a/src/main/java/net/minecraftforge/common/world/NoneStructureModifier.java +++ b/src/main/java/net/minecraftforge/common/world/NoneStructureModifier.java @@ -8,22 +8,21 @@ package net.minecraftforge.common.world; import com.mojang.serialization.Codec; import net.minecraft.core.Holder; import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraftforge.common.ForgeMod; import net.minecraftforge.common.world.ModifiableStructureInfo.StructureInfo.Builder; -public class NoneStructureModifier implements StructureModifier -{ +/** + * Noop structure modifier. Can be used in a structure modifier json with "type": "forge:none". + * intended for datapack makers who want to disable a structure modifier + */ +public class NoneStructureModifier implements StructureModifier { public static final NoneStructureModifier INSTANCE = new NoneStructureModifier(); + public static final Codec CODEC = Codec.unit(NoneStructureModifier.INSTANCE); @Override - public void modify(Holder structure, Phase phase, Builder builder) - { - // NOOP - intended for datapack makers who want to disable a structure modifier - } + public void modify(Holder structure, Phase phase, Builder builder) {} @Override - public Codec codec() - { - return ForgeMod.NONE_STRUCTURE_MODIFIER_TYPE.get(); + public Codec codec() { + return CODEC; } } diff --git a/src/main/java/net/minecraftforge/registries/DeferredRegister.java b/src/main/java/net/minecraftforge/registries/DeferredRegister.java index 4c1b229421..cc5bb1ac40 100644 --- a/src/main/java/net/minecraftforge/registries/DeferredRegister.java +++ b/src/main/java/net/minecraftforge/registries/DeferredRegister.java @@ -15,6 +15,7 @@ import net.minecraft.tags.TagKey; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.registries.tags.ITagManager; + import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -24,7 +25,6 @@ import java.util.HashSet; import java.util.IdentityHashMap; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.function.Supplier; @@ -50,8 +50,7 @@ import java.util.function.Supplier; * * @param The base registry type */ -public class DeferredRegister -{ +public class DeferredRegister { /** * DeferredRegister factory for forge registries that exist before this DeferredRegister is created. *

@@ -63,11 +62,24 @@ public class DeferredRegister * @see #create(ResourceKey, String) * @see #create(ResourceLocation, String) */ - public static DeferredRegister create(IForgeRegistry reg, String modid) - { + public static DeferredRegister create(IForgeRegistry reg, String modid) { return new DeferredRegister<>(reg, modid); } + /** + * DeferredRegister factory for forge registries that exist after this DeferredRegister is created. + * + * @param reg the forge registry to wrap + * @param modid the namespace for all objects registered to this DeferredRegister + * @see #create(ResourceKey, String) + * @see #create(ResourceLocation, String) + */ + public static DeferredRegister create(@Deprecated(forRemoval = true/* change to RegistryHolder */, since = "1.20.2") Supplier> reg, String modid) { + if (reg instanceof RegistryHolder holder) + return create(holder.registryKey, modid); + throw new IllegalArgumentException("Registry argument was not made by DefferredRegister.makeRegistry. Use another create method. This method will be changed to use the hard type in1 1.20.3, but I don't wanna break everything in 1.20.2 so we have this error"); + } + /** * DeferredRegister factory for custom forge registries or {@link BuiltInRegistries vanilla registries} * to lookup based on the provided registry key. Supports both registries that already exist or do not exist yet. @@ -81,8 +93,7 @@ public class DeferredRegister * @see #create(IForgeRegistry, String) * @see #create(ResourceLocation, String) */ - public static DeferredRegister create(ResourceKey> key, String modid) - { + public static DeferredRegister create(ResourceKey> key, String modid) { return new DeferredRegister<>(key, modid, false); } @@ -99,8 +110,7 @@ public class DeferredRegister * @see #create(IForgeRegistry, String) * @see #create(ResourceLocation, String) */ - public static DeferredRegister createOptional(ResourceKey> key, String modid) - { + public static DeferredRegister createOptional(ResourceKey> key, String modid) { return new DeferredRegister<>(key, modid, true); } @@ -117,8 +127,7 @@ public class DeferredRegister * @see #create(IForgeRegistry, String) * @see #create(ResourceKey, String) */ - public static DeferredRegister create(ResourceLocation registryName, String modid) - { + public static DeferredRegister create(ResourceLocation registryName, String modid) { return new DeferredRegister<>(ResourceKey.createRegistryKey(registryName), modid, false); } @@ -135,8 +144,7 @@ public class DeferredRegister * @see #create(IForgeRegistry, String) * @see #create(ResourceKey, String) */ - public static DeferredRegister createOptional(ResourceLocation registryName, String modid) - { + public static DeferredRegister createOptional(ResourceLocation registryName, String modid) { return new DeferredRegister<>(ResourceKey.createRegistryKey(registryName), modid, true); } @@ -152,15 +160,13 @@ public class DeferredRegister private SetMultimap, Supplier> optionalTags; private boolean seenRegisterEvent = false; - private DeferredRegister(ResourceKey> registryKey, String modid, boolean optionalRegistry) - { + private DeferredRegister(ResourceKey> registryKey, String modid, boolean optionalRegistry) { this.registryKey = registryKey; this.modid = modid; this.optionalRegistry = optionalRegistry; } - private DeferredRegister(IForgeRegistry reg, String modid) - { + private DeferredRegister(IForgeRegistry reg, String modid) { this(reg.getRegistryKey(), modid, false); } @@ -171,26 +177,26 @@ public class DeferredRegister * @param sup A factory for the new entry, it should return a new instance every time it is called. * @return A RegistryObject that will be updated with when the entries in the registry change. */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public RegistryObject register(final String name, final Supplier sup) - { + @SuppressWarnings("unchecked") + public RegistryObject register(final String name, final Supplier sup) { if (seenRegisterEvent) throw new IllegalStateException("Cannot register new entries to DeferredRegister after RegisterEvent has been fired."); + Objects.requireNonNull(name); Objects.requireNonNull(sup); final ResourceLocation key = new ResourceLocation(modid, name); RegistryObject ret; - if (this.registryKey != null) - ret = this.optionalRegistry - ? RegistryObject.createOptional(key, this.registryKey, this.modid) - : RegistryObject.create(key, this.registryKey, this.modid); - else + if (this.registryKey != null) { + if (this.optionalRegistry) + ret = RegistryObject.createOptional(key, this.registryKey, this.modid); + else + ret = RegistryObject.create(key, this.registryKey, this.modid); + } else throw new IllegalStateException("Could not create RegistryObject in DeferredRegister"); - if (entries.putIfAbsent((RegistryObject) ret, sup) != null) { + if (entries.putIfAbsent((RegistryObject) ret, sup) != null) throw new IllegalArgumentException("Duplicate registration " + name); - } return ret; } @@ -204,8 +210,7 @@ public class DeferredRegister * @return A supplier of the {@link IForgeRegistry} created by the builder. * Will always return null until after the {@link NewRegistryEvent} event fires. */ - public Supplier> makeRegistry(final Supplier> sup) - { + public Supplier> makeRegistry(final Supplier> sup) { return makeRegistry(this.registryKey.location(), sup); } @@ -219,8 +224,7 @@ public class DeferredRegister * @see #createOptionalTagKey(String, Set) */ @NotNull - public TagKey createTagKey(@NotNull String path) - { + public TagKey createTagKey(@NotNull String path) { Objects.requireNonNull(path); return createTagKey(new ResourceLocation(this.modid, path)); } @@ -235,8 +239,7 @@ public class DeferredRegister * @see #createOptionalTagKey(ResourceLocation, Set) */ @NotNull - public TagKey createTagKey(@NotNull ResourceLocation location) - { + public TagKey createTagKey(@NotNull ResourceLocation location) { if (this.registryKey == null) throw new IllegalStateException("The registry name was not set, cannot create a tag key"); Objects.requireNonNull(location); @@ -256,8 +259,7 @@ public class DeferredRegister * @see #addOptionalTagDefaults(TagKey, Set) */ @NotNull - public TagKey createOptionalTagKey(@NotNull String path, @NotNull Set> defaults) - { + public TagKey createOptionalTagKey(@NotNull String path, @NotNull Set> defaults) { Objects.requireNonNull(path); return createOptionalTagKey(new ResourceLocation(this.modid, path), defaults); } @@ -275,8 +277,7 @@ public class DeferredRegister * @see #addOptionalTagDefaults(TagKey, Set) */ @NotNull - public TagKey createOptionalTagKey(@NotNull ResourceLocation location, @NotNull Set> defaults) - { + public TagKey createOptionalTagKey(@NotNull ResourceLocation location, @NotNull Set> defaults) { TagKey tagKey = createTagKey(location); addOptionalTagDefaults(tagKey, defaults); @@ -294,8 +295,7 @@ public class DeferredRegister * @see #createOptionalTagKey(String, Set) * @see #createOptionalTagKey(ResourceLocation, Set) */ - public void addOptionalTagDefaults(@NotNull TagKey name, @NotNull Set> defaults) - { + public void addOptionalTagDefaults(@NotNull TagKey name, @NotNull Set> defaults) { Objects.requireNonNull(defaults); if (optionalTags == null) optionalTags = Multimaps.newSetMultimap(new IdentityHashMap<>(), HashSet::new); @@ -309,38 +309,21 @@ public class DeferredRegister * * @param bus The Mod Specific event bus. */ - public void register(IEventBus bus) - { - bus.register(new EventDispatcher(this)); - if (this.registryFactory != null) { - bus.addListener(this::createRegistry); - } + public void register(IEventBus bus) { + bus.register(new EventDispatcher()); } - public static class EventDispatcher { - private final DeferredRegister register; - public EventDispatcher(final DeferredRegister register) { - this.register = register; - } - - @SubscribeEvent - public void handleEvent(RegisterEvent event) { - register.addEntries(event); - } - } /** * @return The unmodifiable view of registered entries. Useful for bulk operations on all values. */ - public Collection> getEntries() - { + public Collection> getEntries() { return entriesView; } /** * @return The registry key stored in this deferred register. Useful for creating new deferred registers based on an existing one. */ - public ResourceKey> getRegistryKey() - { + public ResourceKey> getRegistryKey() { return this.registryKey; } @@ -348,8 +331,7 @@ public class DeferredRegister * @return The registry name stored in this deferred register. Useful for creating new deferred registers based on an existing one. */ @NotNull - public ResourceLocation getRegistryName() - { + public ResourceLocation getRegistryName() { return Objects.requireNonNull(this.registryKey).location(); } @@ -364,8 +346,7 @@ public class DeferredRegister } @SuppressWarnings("unchecked") - private void onFill(IForgeRegistry registry) - { + private void onFill(IForgeRegistry registry) { if (this.optionalTags == null) return; @@ -376,37 +357,40 @@ public class DeferredRegister Multimaps.asMap(this.optionalTags).forEach(tagManager::addOptionalTagDefaults); } - private void addEntries(RegisterEvent event) - { - if (event.getRegistryKey().equals(this.registryKey)) - { - this.seenRegisterEvent = true; - for (Entry, Supplier> e : entries.entrySet()) - { - event.register(this.registryKey, e.getKey().getId(), () -> e.getValue().get()); - e.getKey().updateReference(event); + private class EventDispatcher { + @SubscribeEvent + public void handleEvent(RegisterEvent event) { + if (event.getRegistryKey().equals(registryKey)) { + seenRegisterEvent = true; + for (var e : entries.entrySet()) { + event.register(registryKey, e.getKey().getId(), () -> e.getValue().get()); + e.getKey().updateReference(event); + } } } + + @SubscribeEvent + public void createRegistry(NewRegistryEvent event) { + if (registryFactory != null) + event.create(registryFactory.get(), DeferredRegister.this::onFill); + } } - private void createRegistry(NewRegistryEvent event) - { - event.create(this.registryFactory.get(), this::onFill); - } - - private static class RegistryHolder implements Supplier> - { + /* + * Make this public API and make `makeRegistry` return this. Thus allowing us to make a new create method that takes this in for a simpler API + * for custom Forge registries. But it requires breaking the core api of makeRegistry and the fields we store forge's custom registries in. + */ + @Deprecated(forRemoval = true, since = "1.20.2") + private static class RegistryHolder implements Supplier> { private final ResourceKey> registryKey; private IForgeRegistry registry = null; - private RegistryHolder(ResourceKey> registryKey) - { + private RegistryHolder(ResourceKey> registryKey) { this.registryKey = registryKey; } @Override - public IForgeRegistry get() - { + public IForgeRegistry get() { // Keep looking up the registry until it's not null if (this.registry == null) this.registry = RegistryManager.ACTIVE.getRegistry(this.registryKey); diff --git a/src/main/java/net/minecraftforge/registries/ForgeDeferredRegistriesSetup.java b/src/main/java/net/minecraftforge/registries/ForgeDeferredRegistriesSetup.java index 91fdebd658..090250dcf6 100644 --- a/src/main/java/net/minecraftforge/registries/ForgeDeferredRegistriesSetup.java +++ b/src/main/java/net/minecraftforge/registries/ForgeDeferredRegistriesSetup.java @@ -5,32 +5,26 @@ package net.minecraftforge.registries; +import org.jetbrains.annotations.ApiStatus; + import net.minecraftforge.eventbus.api.IEventBus; -public class ForgeDeferredRegistriesSetup -{ +@ApiStatus.Internal +public class ForgeDeferredRegistriesSetup { private static boolean setup = false; /** * Internal forge method. Modders do not call. */ - public static void setup(IEventBus modEventBus) - { - synchronized (ForgeDeferredRegistriesSetup.class) - { + public static void setup(IEventBus modEventBus) { + synchronized (ForgeDeferredRegistriesSetup.class) { if (setup) throw new IllegalStateException("Setup has already been called!"); setup = true; } - ForgeRegistries.DEFERRED_ENTITY_DATA_SERIALIZERS.register(modEventBus); - ForgeRegistries.DEFERRED_GLOBAL_LOOT_MODIFIER_SERIALIZERS.register(modEventBus); - ForgeRegistries.DEFERRED_BIOME_MODIFIER_SERIALIZERS.register(modEventBus); - ForgeRegistries.DEFERRED_FLUID_TYPES.register(modEventBus); - ForgeRegistries.DEFERRED_STRUCTURE_MODIFIER_SERIALIZERS.register(modEventBus); - ForgeRegistries.DEFERRED_HOLDER_SET_TYPES.register(modEventBus); - ForgeRegistries.DEFERRED_DISPLAY_CONTEXTS.register(modEventBus); - ForgeRegistries.DEFERRED_CONDITION_CODECS.register(modEventBus); + for (var reg : ForgeRegistries.registries) + reg.register(modEventBus); } } diff --git a/src/main/java/net/minecraftforge/registries/ForgeRegistries.java b/src/main/java/net/minecraftforge/registries/ForgeRegistries.java index ee87775a51..b3b9709922 100644 --- a/src/main/java/net/minecraftforge/registries/ForgeRegistries.java +++ b/src/main/java/net/minecraftforge/registries/ForgeRegistries.java @@ -44,112 +44,82 @@ import net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacerTy import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecoratorType; import net.minecraftforge.common.Tags; import net.minecraftforge.common.crafting.conditions.ICondition; +import net.minecraftforge.common.crafting.ingredients.IIngredientSerializer; import net.minecraftforge.common.loot.IGlobalLootModifier; import net.minecraftforge.common.world.BiomeModifier; import net.minecraftforge.fluids.FluidType; import net.minecraftforge.registries.holdersets.HolderSetType; +import java.util.ArrayList; +import java.util.List; import java.util.function.Supplier; import net.minecraftforge.common.world.StructureModifier; /** * A class that exposes static references to all vanilla and Forge registries. * Created to have a central place to access the registries directly if modders need. - * It is still advised that if you are registering things to use {@link RegisterEvent} or {@link net.minecraftforge.registries.DeferredRegister}, but queries and iterations can use this. + * It is still advised that if you are registering things to use {@link RegisterEvent} or {@link DeferredRegister}, but queries and iterations can use this. */ -public class ForgeRegistries -{ +public class ForgeRegistries { static { init(); } // This must be above the fields so we guarantee it's run before getRegistry is called. Yay static initializers + private static IForgeRegistry active(ResourceKey> key) { + return RegistryManager.ACTIVE.getRegistry(key); + } + static final List> registries = new ArrayList<>(); + private static final Supplier> registry(ResourceKey> key, Supplier> factory) { + var dr = DeferredRegister.create(key, key.location().getNamespace()); + registries.add(dr); + return dr.makeRegistry(factory); + } // Game objects - public static final IForgeRegistry BLOCKS = RegistryManager.ACTIVE.getRegistry(Keys.BLOCKS); - public static final IForgeRegistry FLUIDS = RegistryManager.ACTIVE.getRegistry(Keys.FLUIDS); - public static final IForgeRegistry ITEMS = RegistryManager.ACTIVE.getRegistry(Keys.ITEMS); - public static final IForgeRegistry MOB_EFFECTS = RegistryManager.ACTIVE.getRegistry(Keys.MOB_EFFECTS); - public static final IForgeRegistry SOUND_EVENTS = RegistryManager.ACTIVE.getRegistry(Keys.SOUND_EVENTS); - public static final IForgeRegistry POTIONS = RegistryManager.ACTIVE.getRegistry(Keys.POTIONS); - public static final IForgeRegistry ENCHANTMENTS = RegistryManager.ACTIVE.getRegistry(Keys.ENCHANTMENTS); - public static final IForgeRegistry> ENTITY_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.ENTITY_TYPES); - public static final IForgeRegistry> BLOCK_ENTITY_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.BLOCK_ENTITY_TYPES); - public static final IForgeRegistry> PARTICLE_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.PARTICLE_TYPES); - public static final IForgeRegistry> MENU_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.MENU_TYPES); - public static final IForgeRegistry PAINTING_VARIANTS = RegistryManager.ACTIVE.getRegistry(Keys.PAINTING_VARIANTS); - public static final IForgeRegistry> RECIPE_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.RECIPE_TYPES); - public static final IForgeRegistry> RECIPE_SERIALIZERS = RegistryManager.ACTIVE.getRegistry(Keys.RECIPE_SERIALIZERS); - public static final IForgeRegistry ATTRIBUTES = RegistryManager.ACTIVE.getRegistry(Keys.ATTRIBUTES); - public static final IForgeRegistry> STAT_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.STAT_TYPES); - public static final IForgeRegistry> COMMAND_ARGUMENT_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.COMMAND_ARGUMENT_TYPES); + public static final IForgeRegistry BLOCKS = active(Keys.BLOCKS); + public static final IForgeRegistry FLUIDS = active(Keys.FLUIDS); + public static final IForgeRegistry ITEMS = active(Keys.ITEMS); + public static final IForgeRegistry MOB_EFFECTS = active(Keys.MOB_EFFECTS); + public static final IForgeRegistry SOUND_EVENTS = active(Keys.SOUND_EVENTS); + public static final IForgeRegistry POTIONS = active(Keys.POTIONS); + public static final IForgeRegistry ENCHANTMENTS = active(Keys.ENCHANTMENTS); + public static final IForgeRegistry> ENTITY_TYPES = active(Keys.ENTITY_TYPES); + public static final IForgeRegistry> BLOCK_ENTITY_TYPES = active(Keys.BLOCK_ENTITY_TYPES); + public static final IForgeRegistry> PARTICLE_TYPES = active(Keys.PARTICLE_TYPES); + public static final IForgeRegistry> MENU_TYPES = active(Keys.MENU_TYPES); + public static final IForgeRegistry PAINTING_VARIANTS = active(Keys.PAINTING_VARIANTS); + public static final IForgeRegistry> RECIPE_TYPES = active(Keys.RECIPE_TYPES); + public static final IForgeRegistry> RECIPE_SERIALIZERS = active(Keys.RECIPE_SERIALIZERS); + public static final IForgeRegistry ATTRIBUTES = active(Keys.ATTRIBUTES); + public static final IForgeRegistry> STAT_TYPES = active(Keys.STAT_TYPES); + public static final IForgeRegistry> COMMAND_ARGUMENT_TYPES = active(Keys.COMMAND_ARGUMENT_TYPES); // Villages - public static final IForgeRegistry VILLAGER_PROFESSIONS = RegistryManager.ACTIVE.getRegistry(Keys.VILLAGER_PROFESSIONS); - public static final IForgeRegistry POI_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.POI_TYPES); - public static final IForgeRegistry> MEMORY_MODULE_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.MEMORY_MODULE_TYPES); - public static final IForgeRegistry> SENSOR_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.SENSOR_TYPES); - public static final IForgeRegistry SCHEDULES = RegistryManager.ACTIVE.getRegistry(Keys.SCHEDULES); - public static final IForgeRegistry ACTIVITIES = RegistryManager.ACTIVE.getRegistry(Keys.ACTIVITIES); + public static final IForgeRegistry VILLAGER_PROFESSIONS = active(Keys.VILLAGER_PROFESSIONS); + public static final IForgeRegistry POI_TYPES = active(Keys.POI_TYPES); + public static final IForgeRegistry> MEMORY_MODULE_TYPES = active(Keys.MEMORY_MODULE_TYPES); + public static final IForgeRegistry> SENSOR_TYPES = active(Keys.SENSOR_TYPES); + public static final IForgeRegistry SCHEDULES = active(Keys.SCHEDULES); + public static final IForgeRegistry ACTIVITIES = active(Keys.ACTIVITIES); // Worldgen - public static final IForgeRegistry> WORLD_CARVERS = RegistryManager.ACTIVE.getRegistry(Keys.WORLD_CARVERS); - public static final IForgeRegistry> FEATURES = RegistryManager.ACTIVE.getRegistry(Keys.FEATURES); - public static final IForgeRegistry CHUNK_STATUS = RegistryManager.ACTIVE.getRegistry(Keys.CHUNK_STATUS); - public static final IForgeRegistry> BLOCK_STATE_PROVIDER_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.BLOCK_STATE_PROVIDER_TYPES); - public static final IForgeRegistry> FOLIAGE_PLACER_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.FOLIAGE_PLACER_TYPES); - public static final IForgeRegistry> TREE_DECORATOR_TYPES = RegistryManager.ACTIVE.getRegistry(Keys.TREE_DECORATOR_TYPES); + public static final IForgeRegistry> WORLD_CARVERS = active(Keys.WORLD_CARVERS); + public static final IForgeRegistry> FEATURES = active(Keys.FEATURES); + public static final IForgeRegistry CHUNK_STATUS = active(Keys.CHUNK_STATUS); + public static final IForgeRegistry> BLOCK_STATE_PROVIDER_TYPES = active(Keys.BLOCK_STATE_PROVIDER_TYPES); + public static final IForgeRegistry> FOLIAGE_PLACER_TYPES = active(Keys.FOLIAGE_PLACER_TYPES); + public static final IForgeRegistry> TREE_DECORATOR_TYPES = active(Keys.TREE_DECORATOR_TYPES); // Dynamic/Data driven. - public static final IForgeRegistry BIOMES = RegistryManager.ACTIVE.getRegistry(Keys.BIOMES); + public static final IForgeRegistry BIOMES = active(Keys.BIOMES); // Custom forge registries - static final DeferredRegister> DEFERRED_ENTITY_DATA_SERIALIZERS = DeferredRegister.create(Keys.ENTITY_DATA_SERIALIZERS, Keys.ENTITY_DATA_SERIALIZERS.location().getNamespace()); - /** - * Calling {@link Supplier#get()} before {@link NewRegistryEvent} is fired will result in a null registry returned. - * Use {@link Keys#ENTITY_DATA_SERIALIZERS} to create a {@link DeferredRegister}. - */ - public static final Supplier>> ENTITY_DATA_SERIALIZERS = DEFERRED_ENTITY_DATA_SERIALIZERS.makeRegistry(GameData::getDataSerializersRegistryBuilder); - static final DeferredRegister> DEFERRED_GLOBAL_LOOT_MODIFIER_SERIALIZERS = DeferredRegister.create(Keys.GLOBAL_LOOT_MODIFIER_SERIALIZERS, Keys.GLOBAL_LOOT_MODIFIER_SERIALIZERS.location().getNamespace()); - /** - * Calling {@link Supplier#get()} before {@link NewRegistryEvent} is fired will result in a null registry returned. - * Use {@link Keys#GLOBAL_LOOT_MODIFIER_SERIALIZERS} to create a {@link DeferredRegister}. - */ - public static final Supplier>> GLOBAL_LOOT_MODIFIER_SERIALIZERS = DEFERRED_GLOBAL_LOOT_MODIFIER_SERIALIZERS.makeRegistry(GameData::getGLMSerializersRegistryBuilder); - static final DeferredRegister> DEFERRED_BIOME_MODIFIER_SERIALIZERS = DeferredRegister.create(Keys.BIOME_MODIFIER_SERIALIZERS, Keys.BIOME_MODIFIER_SERIALIZERS.location().getNamespace()); - /** - * Calling {@link Supplier#get()} before {@link NewRegistryEvent} is fired will result in a null registry returned. - * Use {@link Keys#BIOME_MODIFIER_SERIALIZERS} to create a {@link DeferredRegister}. - */ - public static final Supplier>> BIOME_MODIFIER_SERIALIZERS = DEFERRED_BIOME_MODIFIER_SERIALIZERS.makeRegistry(GameData::getBiomeModifierSerializersRegistryBuilder); - static final DeferredRegister> DEFERRED_STRUCTURE_MODIFIER_SERIALIZERS = DeferredRegister.create(Keys.STRUCTURE_MODIFIER_SERIALIZERS, Keys.STRUCTURE_MODIFIER_SERIALIZERS.location().getNamespace()); - /** - * Calling {@link Supplier#get()} before {@link NewRegistryEvent} is fired will result in a null registry returned. - * Use {@link Keys#STRUCTURE_MODIFIER_SERIALIZERS} to create a {@link DeferredRegister}. - */ - public static final Supplier>> STRUCTURE_MODIFIER_SERIALIZERS = DEFERRED_STRUCTURE_MODIFIER_SERIALIZERS.makeRegistry(GameData::getStructureModifierSerializersRegistryBuilder); - static final DeferredRegister DEFERRED_FLUID_TYPES = DeferredRegister.create(Keys.FLUID_TYPES, Keys.FLUID_TYPES.location().getNamespace()); - /** - * Calling {@link Supplier#get()} before {@link NewRegistryEvent} is fired will result in a null registry returned. - * Use {@link Keys#FLUID_TYPES} to create a {@link DeferredRegister}. - */ - public static final Supplier> FLUID_TYPES = DEFERRED_FLUID_TYPES.makeRegistry(GameData::getFluidTypeRegistryBuilder); - static final DeferredRegister DEFERRED_HOLDER_SET_TYPES = DeferredRegister.create(Keys.HOLDER_SET_TYPES, "forge"); - /** - * Calling {@link Supplier#get()} before {@link NewRegistryEvent} is fired will result in a null registry returned. - * Use {@link Keys#HOLDER_SET_TYPES} to create a {@link DeferredRegister}. - */ - public static final Supplier> HOLDER_SET_TYPES = DEFERRED_HOLDER_SET_TYPES.makeRegistry(GameData::getHolderSetTypeRegistryBuilder); - - static final DeferredRegister DEFERRED_DISPLAY_CONTEXTS = DeferredRegister.create(Keys.DISPLAY_CONTEXTS, "forge"); - /** - * Calling {@link Supplier#get()} before {@link NewRegistryEvent} is fired will result in a null registry returned. - * Use {@link Keys#DISPLAY_CONTEXTS} to create a {@link DeferredRegister}. - */ - public static final Supplier> DISPLAY_CONTEXTS = DEFERRED_DISPLAY_CONTEXTS.makeRegistry(GameData::getItemDisplayContextRegistryBuilder); - - static final DeferredRegister> DEFERRED_CONDITION_CODECS = DeferredRegister.create(Keys.CONDITION_SERIALIZERS, Keys.CONDITION_SERIALIZERS.location().getNamespace()); - /** - * Calling {@link Supplier#get()} before {@link NewRegistryEvent} is fired will result in a null registry returned. - * Use {@link Keys#CONDITION_SERIALIZERS} to create a {@link DeferredRegister}. - */ - public static final Supplier>> CONDITION_SERIALIZERS = DEFERRED_CONDITION_CODECS.makeRegistry(GameData::getConditionCodecRegistryBuilder); + public static final Supplier>> ENTITY_DATA_SERIALIZERS = registry(Keys.ENTITY_DATA_SERIALIZERS, GameData::getDataSerializersRegistryBuilder); + public static final Supplier>> GLOBAL_LOOT_MODIFIER_SERIALIZERS = registry(Keys.GLOBAL_LOOT_MODIFIER_SERIALIZERS, GameData::getGLMSerializersRegistryBuilder); + public static final Supplier>> BIOME_MODIFIER_SERIALIZERS = registry(Keys.BIOME_MODIFIER_SERIALIZERS, GameData::makeUnsavedAndUnsynced); + public static final Supplier>> STRUCTURE_MODIFIER_SERIALIZERS = registry(Keys.STRUCTURE_MODIFIER_SERIALIZERS, GameData::makeUnsavedAndUnsynced); + public static final Supplier> FLUID_TYPES = registry(Keys.FLUID_TYPES, GameData::getFluidTypeRegistryBuilder); + public static final Supplier> HOLDER_SET_TYPES = registry(Keys.HOLDER_SET_TYPES, GameData::makeUnsavedAndUnsynced); + public static final Supplier> DISPLAY_CONTEXTS = registry(Keys.DISPLAY_CONTEXTS, GameData::getItemDisplayContextRegistryBuilder); + public static final Supplier>> CONDITION_SERIALIZERS = registry(Keys.CONDITION_SERIALIZERS, GameData::makeUnsavedAndUnsynced); + public static final Supplier>> INGREDIENT_SERIALIZERS = registry(Keys.INGREDIENT_SERIALIZERS, GameData::makeUnsavedAndUnsynced); public static final class Keys { //Vanilla @@ -195,23 +165,23 @@ public class ForgeRegistries public static final ResourceKey> HOLDER_SET_TYPES = key("forge:holder_set_type"); public static final ResourceKey> DISPLAY_CONTEXTS = key("forge:display_contexts"); public static final ResourceKey>> CONDITION_SERIALIZERS = key("forge:condition_codecs"); + public static final ResourceKey>> INGREDIENT_SERIALIZERS = key("forge:ingredient_serializers"); // Forge Dynamic public static final ResourceKey> BIOME_MODIFIERS = key("forge:biome_modifier"); public static final ResourceKey> STRUCTURE_MODIFIERS = key("forge:structure_modifier"); - private static ResourceKey> key(String name) - { + private static ResourceKey> key(String name) { return ResourceKey.createRegistryKey(new ResourceLocation(name)); } + private static void init() {} } /** * This function is just to make sure static initializers in other classes have run and setup their registries before we query them. */ - private static void init() - { + private static void init() { Keys.init(); GameData.init(); Bootstrap.bootStrap(); diff --git a/src/main/java/net/minecraftforge/registries/ForgeRegistry.java b/src/main/java/net/minecraftforge/registries/ForgeRegistry.java index 7a3548b6a6..b0c53982dc 100644 --- a/src/main/java/net/minecraftforge/registries/ForgeRegistry.java +++ b/src/main/java/net/minecraftforge/registries/ForgeRegistry.java @@ -5,9 +5,22 @@ package net.minecraftforge.registries; -import java.util.*; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.Map.Entry; -import java.util.function.IntFunction; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; @@ -16,7 +29,6 @@ import com.mojang.serialization.DynamicOps; import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.tags.TagKey; -import net.minecraft.world.level.biome.Biome; import net.minecraftforge.common.util.LogMessageAdapter; import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.registries.tags.ITagManager; @@ -26,15 +38,11 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; import com.google.common.collect.Multimap; -import com.google.common.collect.Sets; import io.netty.buffer.Unpooled; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.StringTag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; @@ -47,11 +55,9 @@ import org.apache.logging.log4j.MarkerManager; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.VisibleForTesting; @ApiStatus.Internal -public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegistryModifiable -{ +public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegistryModifiable { public static Marker REGISTRIES = MarkerManager.getMarker("REGISTRIES"); private static final Marker REGISTRYDUMP = MarkerManager.getMarker("REGISTRYDUMP"); private static final Logger LOGGER = LogManager.getLogger(); @@ -59,10 +65,10 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist private final BiMap ids = HashBiMap.create(); private final BiMap names = HashBiMap.create(); private final BiMap, V> keys = HashBiMap.create(); - private final Map aliases = Maps.newHashMap(); - final Map slaves = Maps.newHashMap(); + private final Map aliases = new HashMap<>(); + final Map slaves = new HashMap<>(); private final ResourceLocation defaultKey; - private final ResourceKey defaultResourceKey; + //private final ResourceKey defaultResourceKey; private final CreateCallback create; private final AddCallback add; private final ClearCallback clear; @@ -70,10 +76,10 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist private final BakeCallback bake; private final MissingFactory missing; private final BitSet availabilityMap; - private final Set blocked = Sets.newHashSet(); + private final Set blocked = new HashSet<>(); private final Multimap overrides = ArrayListMultimap.create(); - private final Map> delegatesByName = Maps.newHashMap(); - private final Map> delegatesByValue = Maps.newHashMap(); + private final Map> delegatesByName = new HashMap<>(); + private final Map> delegatesByValue = new HashMap<>(); private final BiMap, V> owners = HashBiMap.create(); private final ForgeRegistryTagManager tagManager; private final int min; @@ -91,15 +97,13 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist private final Codec codec = new RegistryCodec(); - @SuppressWarnings("unchecked") - ForgeRegistry(RegistryManager stage, ResourceLocation name, RegistryBuilder builder) - { + ForgeRegistry(RegistryManager stage, ResourceLocation name, RegistryBuilder builder) { this.name = name; this.key = ResourceKey.createRegistryKey(name); this.builder = builder; this.stage = stage; this.defaultKey = builder.getDefault(); - this.defaultResourceKey = ResourceKey.create(key, defaultKey); + //this.defaultResourceKey = ResourceKey.create(key, defaultKey); this.min = builder.getMinId(); this.max = builder.getMaxId(); this.availabilityMap = new BitSet(Math.min(max + 1, 0x0FFF)); @@ -118,34 +122,29 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist } @Override - public void register(String key, V value) - { + public void register(String key, V value) { register(GameData.checkPrefix(key, true), value); } @Override - public void register(ResourceLocation key, V value) - { + public void register(ResourceLocation key, V value) { add(-1, key, value); } @Override public Iterator iterator() { - return new Iterator() - { + return new Iterator() { int cur = -1; V next = null; { next(); } @Override - public boolean hasNext() - { + public boolean hasNext() { return next != null; } @Override - public V next() - { + public V next() { V ret = next; do { cur = availabilityMap.nextSetBit(cur + 1); @@ -158,28 +157,23 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist } @Override - public ResourceLocation getRegistryName() - { + public ResourceLocation getRegistryName() { return this.name; } @Override - public ResourceKey> getRegistryKey() - { + public ResourceKey> getRegistryKey() { return this.key; } @NotNull - public Codec getCodec() - { + public Codec getCodec() { return this.codec; } @Override - public boolean containsKey(ResourceLocation key) - { - while (key != null) - { + public boolean containsKey(ResourceLocation key) { + while (key != null) { if (this.names.containsKey(key)) return true; key = this.aliases.get(key); @@ -188,29 +182,24 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist } @Override - public boolean containsValue(V value) - { + public boolean containsValue(V value) { return this.names.containsValue(value); } @Override - public boolean isEmpty() - { + public boolean isEmpty() { return this.names.isEmpty(); } - int size() - { + int size() { return this.names.size(); } @Override - public V getValue(ResourceLocation key) - { + public V getValue(ResourceLocation key) { V ret = this.names.get(key); key = this.aliases.get(key); - while (ret == null && key != null) - { + while (ret == null && key != null) { ret = this.names.get(key); key = this.aliases.get(key); } @@ -218,23 +207,20 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist } @Override - public ResourceLocation getKey(V value) - { + public ResourceLocation getKey(V value) { return getResourceKey(value).map(ResourceKey::location).orElse(this.defaultKey); } @NotNull @Override - public Optional> getResourceKey(V value) - { + public Optional> getResourceKey(V value) { // We use 'owners' here because we want to return the key for the inactive overridden items, not just the active set. return Optional.ofNullable(this.owners.inverse().get(value)).map(OverrideOwner::key); } @SuppressWarnings("unchecked") @Nullable - NamespacedWrapper getWrapper() - { + NamespacedWrapper getWrapper() { if (!this.hasWrapper) return null; @@ -244,8 +230,7 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist } @NotNull - NamespacedWrapper getWrapperOrThrow() - { + NamespacedWrapper getWrapperOrThrow() { NamespacedWrapper wrapper = getWrapper(); if (wrapper == null) @@ -254,131 +239,113 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist return wrapper; } - void onBindTags(Map, HolderSet.Named> tags, Set> defaultedTags) - { + void onBindTags(Map, HolderSet.Named> tags, Set> defaultedTags) { if (this.tagManager != null) this.tagManager.bind(tags, defaultedTags); } @NotNull @Override - public Optional> getHolder(ResourceKey key) - { + public Optional> getHolder(ResourceKey key) { return Optional.ofNullable(this.getWrapper()).flatMap(wrapper -> wrapper.getHolder(key)); } @NotNull @Override - public Optional> getHolder(ResourceLocation location) - { + public Optional> getHolder(ResourceLocation location) { return Optional.ofNullable(this.getWrapper()).flatMap(wrapper -> wrapper.getHolder(location)); } @NotNull @Override - public Optional> getHolder(V value) - { + public Optional> getHolder(V value) { return Optional.ofNullable(this.getWrapper()).flatMap(wrapper -> wrapper.getHolder(value)); } @Nullable @Override - public ITagManager tags() - { + public ITagManager tags() { return this.tagManager; } @NotNull @Override - public Set getKeys() - { + public Set getKeys() { return Collections.unmodifiableSet(this.names.keySet()); } @NotNull - Set> getResourceKeys() - { + Set> getResourceKeys() { return Collections.unmodifiableSet(this.keys.keySet()); } @NotNull @Override - public Collection getValues() - { + public Collection getValues() { return Collections.unmodifiableSet(this.names.values()); } @NotNull @Override - public Set, V>> getEntries() - { + public Set, V>> getEntries() { return Collections.unmodifiableSet(this.keys.entrySet()); } @SuppressWarnings("unchecked") @Override - public T getSlaveMap(ResourceLocation name, Class type) - { + public T getSlaveMap(ResourceLocation name, Class type) { return (T)this.slaves.get(name); } @SuppressWarnings("unchecked") @Override - public void setSlaveMap(ResourceLocation name, Object obj) - { + public void setSlaveMap(ResourceLocation name, Object obj) { ((Map)this.slaves).put(name, obj); } - public int getID(V value) - { + public int getID(V value) { Integer ret = this.ids.inverse().get(value); if (ret == null && this.defaultValue != null) ret = this.ids.inverse().get(this.defaultValue); return ret == null ? -1 : ret.intValue(); } - public int getID(ResourceLocation name) - { + public int getID(ResourceLocation name) { return getID(this.names.get(name)); } - private int getIDRaw(V value) - { + + private int getIDRaw(V value) { Integer ret = this.ids.inverse().get(value); return ret == null ? -1 : ret.intValue(); } - private int getIDRaw(ResourceLocation name) - { + + private int getIDRaw(ResourceLocation name) { return getIDRaw(this.names.get(name)); } @Override - public V getValue(int id) - { + public V getValue(int id) { V ret = this.ids.get(id); return ret == null ? this.defaultValue : ret; } @Nullable - public ResourceKey getKey(int id) - { + public ResourceKey getKey(int id) { V value = getValue(id); return this.keys.inverse().get(value); } - void validateKey() - { + void validateKey() { if (this.defaultKey != null) Validate.notNull(this.defaultValue, "Missing default of ForgeRegistry: " + this.defaultKey + " Name: " + this.name); } @Nullable - public ResourceLocation getDefaultKey() - { + public ResourceLocation getDefaultKey() { return this.defaultKey; } - ForgeRegistry copy(RegistryManager stage) - { + ForgeRegistry copy(RegistryManager stage) { return new ForgeRegistry<>(stage, name, builder); } @@ -387,14 +354,12 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist add(id, key, value, key.getNamespace()); } - int add(int id, ResourceLocation key, V value) - { + int add(int id, ResourceLocation key, V value) { final String owner = ModLoadingContext.get().getActiveNamespace(); return add(id, key, value, owner); } - int add(int id, ResourceLocation key, V value, String owner) - { + int add(int id, ResourceLocation key, V value, String owner) { Preconditions.checkNotNull(key, "Can't use a null-name for the registry, object %s.", value); Preconditions.checkNotNull(value, "Can't add null-object to the registry, name %s.", key); @@ -406,13 +371,12 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist throw new RuntimeException(String.format(Locale.ENGLISH, "Invalid id %d - maximum id range exceeded.", idToUse)); V oldEntry = getRaw(key); - if (oldEntry == value) // already registered, return prev registration's id - { + if (oldEntry == value) { // already registered, return prev registration's id LOGGER.warn(REGISTRIES,"Registry {}: The object {} has been registered twice for the same name {}.", this.name, value, key); return this.getID(value); } - if (oldEntry != null) // duplicate name - { + + if (oldEntry != null) { // duplicate name if (!this.allowOverrides) throw new IllegalArgumentException(String.format(Locale.ENGLISH, "The name %s has been registered twice, for %s and %s.", key, getRaw(key), value)); if (owner == null) @@ -422,8 +386,7 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist } Integer foundId = this.ids.inverse().get(value); //Is this ever possible to trigger with otherThing being different? - if (foundId != null) - { + if (foundId != null) { V otherThing = this.ids.get(foundId); throw new IllegalArgumentException(String.format(Locale.ENGLISH, "The object %s{%x} has been registered twice, using the names %s and %s. (Other object at this id is %s{%x})", value, System.identityHashCode(value), getKey(value), key, otherThing, System.identityHashCode(otherThing))); } @@ -431,8 +394,7 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist if (isLocked()) throw new IllegalStateException(String.format(Locale.ENGLISH, "The object %s (name %s) is being added too late.", value, key)); - if (defaultKey != null && defaultKey.equals(key)) - { + if (defaultKey != null && defaultKey.equals(key)) { if (this.defaultValue != null) throw new IllegalStateException(String.format(Locale.ENGLISH, "Attemped to override already set default value. This is not allowed: The object %s (name %s)", value, key)); this.defaultValue = value; @@ -445,11 +407,9 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist this.availabilityMap.set(idToUse); this.owners.put(new OverrideOwner(owner == null ? key.getNamespace() : owner, rkey), value); - if (hasWrapper) - { - Holder.Reference delegate = bindDelegate(rkey, value); - if (oldEntry != null) - { + if (hasWrapper) { + bindDelegate(rkey, value); + if (oldEntry != null) { if (!this.overrides.get(key).contains(oldEntry)) this.overrides.put(key, oldEntry); this.overrides.get(key).remove(value); @@ -464,12 +424,10 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist return idToUse; } - public V getRaw(ResourceLocation key) - { + public V getRaw(ResourceLocation key) { V ret = this.names.get(key); key = this.aliases.get(key); - while (ret == null && key != null) - { + while (ret == null && key != null) { ret = this.names.get(key); key = this.aliases.get(key); } @@ -484,13 +442,11 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist * * TODO: Add as public API in IForgeRegistry and DeferredRegister. */ - public void addAlias(ResourceLocation src, ResourceLocation dst) - { + public void addAlias(ResourceLocation src, ResourceLocation dst) { if (this.isLocked()) throw new IllegalStateException(String.format(Locale.ENGLISH, "Attempted to register the alias %s -> %s too late", src, dst)); - if (src.equals(dst)) - { + if (src.equals(dst)) { LOGGER.warn(REGISTRIES, "Registry {} Ignoring invalid alias: {} -> {}", this.name, src, dst); return; } @@ -501,48 +457,41 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist @NotNull @Override - public Optional> getDelegate(ResourceKey rkey) - { + public Optional> getDelegate(ResourceKey rkey) { return Optional.ofNullable(delegatesByName.get(rkey.location())); } @NotNull @Override - public Holder.Reference getDelegateOrThrow(ResourceKey rkey) - { + public Holder.Reference getDelegateOrThrow(ResourceKey rkey) { return getDelegate(rkey).orElseThrow(() -> new IllegalArgumentException(String.format(Locale.ENGLISH, "No delegate exists for key %s", rkey))); } @NotNull @Override - public Optional> getDelegate(ResourceLocation key) - { + public Optional> getDelegate(ResourceLocation key) { return Optional.ofNullable(delegatesByName.get(key)); } @NotNull @Override - public Holder.Reference getDelegateOrThrow(ResourceLocation key) - { + public Holder.Reference getDelegateOrThrow(ResourceLocation key) { return getDelegate(key).orElseThrow(() -> new IllegalArgumentException(String.format(Locale.ENGLISH, "No delegate exists for key %s", key))); } @NotNull @Override - public Optional> getDelegate(V value) - { + public Optional> getDelegate(V value) { return Optional.ofNullable(delegatesByValue.get(value)); } @NotNull @Override - public Holder.Reference getDelegateOrThrow(V value) - { + public Holder.Reference getDelegateOrThrow(V value) { return getDelegate(value).orElseThrow(() -> new IllegalArgumentException(String.format(Locale.ENGLISH, "No delegate exists for value %s", value))); } - private Holder.Reference bindDelegate(ResourceKey rkey, V value) - { + private Holder.Reference bindDelegate(ResourceKey rkey, V value) { Holder.Reference delegate = delegatesByName.computeIfAbsent(rkey.location(), k -> Holder.Reference.createStandAlone(this.getWrapperOrThrow().holderOwner(), rkey)); delegate.bindKey(rkey); delegate.bindValue(value); @@ -550,8 +499,7 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist return delegate; } - void resetDelegates() - { + void resetDelegates() { if (!this.hasWrapper) return; @@ -562,16 +510,12 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist bindDelegate(ResourceKey.create(this.key, entry.getKey()), entry.getValue()); } - V getDefault() - { + V getDefault() { return this.defaultValue; } - - void validateContent(ResourceLocation registryName) - { - for (V obj : this) - { + void validateContent(ResourceLocation registryName) { + for (V obj : this) { int id = getID(obj); ResourceLocation name = getKey(obj); @@ -607,14 +551,12 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist } } - public void bake() - { + public void bake() { if (this.bake != null) this.bake.onBake(this, this.stage); } - void sync(ResourceLocation name, ForgeRegistry from) - { + void sync(ResourceLocation name, ForgeRegistry from) { LOGGER.debug(REGISTRIES,"Registry {} Sync: {} -> {}", this.name, this.stage.getName(), from.stage.getName()); if (this == from) throw new IllegalArgumentException("WTF We are the same!?!?!"); @@ -630,12 +572,9 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist this.min = from.min; */ // Aliases from the active registry take priority over those from the staged (loaded) registry. - for (var entry : from.aliases.entrySet()) - { + for (var entry : from.aliases.entrySet()) { if (!this.aliases.containsKey(entry.getKey())) - { this.aliases.put(entry.getKey(), entry.getValue()); - } } this.ids.clear(); @@ -648,34 +587,27 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist boolean errored = false; - for (Entry entry : from.names.entrySet()) - { - List overrides = Lists.newArrayList(from.overrides.get(entry.getKey())); + for (Entry entry : from.names.entrySet()) { + List overrides = new ArrayList<>(from.overrides.get(entry.getKey())); int id = from.getID(entry.getKey()); - if (overrides.isEmpty()) - { + if (overrides.isEmpty()) { int realId = add(id, entry.getKey(), entry.getValue()); - if (id != realId && id != -1) - { + if (id != realId && id != -1) { LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.name, entry.getKey(), id, realId); errored = true; } - } - else - { + } else { overrides.add(entry.getValue()); - for (V value : overrides) - { + for (V value : overrides) { OverrideOwner owner = from.owners.inverse().get(value); - if (owner == null) - { + if (owner == null) { LOGGER.warn(REGISTRIES,"Registry {}: Override did not have an associated owner object. Name: {} Value: {}", this.name, entry.getKey(), value); errored = true; continue; } + int realId = add(id, entry.getKey(), value, owner.owner); - if (id != realId && id != -1) - { + if (id != realId && id != -1) { LOGGER.warn(REGISTRIES,"Registry {}: Object did not get ID it asked for. Name: {} Expected: {} Got: {}", this.name, entry.getKey(), id, realId); errored = true; } @@ -688,8 +620,7 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist } @Override - public void clear() - { + public void clear() { if (!this.isModifiable) throw new UnsupportedOperationException("Attempted to clear a non-modifiable Forge Registry"); @@ -709,8 +640,7 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist } @Override - public V remove(ResourceLocation key) - { + public V remove(ResourceLocation key) { if (!this.isModifiable) throw new UnsupportedOperationException("Attempted to remove from a non-modifiable Forge Registry"); @@ -718,8 +648,7 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist throw new IllegalStateException("Attempted to remove from the registry to late."); V value = this.names.remove(key); - if (value != null) - { + if (value != null) { ResourceKey rkey = this.keys.inverse().remove(value); if (rkey == null) throw new IllegalStateException("Removed a entry that did not have an associated RegistryKey: " + key + " " + value.toString() + " This should never happen unless hackery!"); @@ -734,15 +663,13 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist return value; } - void block(int id) - { + void block(int id) { this.blocked.add(id); this.availabilityMap.set(id); } @Override - public boolean isLocked() - { + public boolean isLocked() { return this.isFrozen; } @@ -750,34 +677,29 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist * Used to control the times where people can modify this registry. * Users should only ever register things in the {@literal Register} events! */ - public void freeze() - { + public void freeze() { this.isFrozen = true; } - public void unfreeze() - { + public void unfreeze() { this.isFrozen = false; } - void dump(ResourceLocation name) - { + void dump(ResourceLocation name) { // Building a good looking table is not cheap, so only do it if the debug logger is enabled. - if (LOGGER.isDebugEnabled(REGISTRYDUMP)) - { + if (LOGGER.isDebugEnabled(REGISTRYDUMP)) { TablePrinter tab = new TablePrinter() .header("ID", r -> r.id) .header("Key", r -> r.key) .header("Value", r -> r.value); - LOGGER.debug(REGISTRYDUMP, ()-> LogMessageAdapter.adapt(sb -> - { + LOGGER.debug(REGISTRYDUMP, ()-> LogMessageAdapter.adapt(sb -> { sb.append("Registry Name: ").append(name).append('\n'); tab.clearRows(); getKeys().stream().map(this::getID).sorted().map(id -> { V val = getValue(id); - ResourceLocation key = getKey(val); - return new DumpRow(Integer.toString(id), getKey(val).toString(), val.toString()); + var key = getKey(val); + return new DumpRow(Integer.toString(id), key.toString(), val.toString()); }).forEach(tab::add); tab.build(sb); })); @@ -786,24 +708,19 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist private record DumpRow(String id, String key, String value) {} - public void loadIds(Map ids, Map overrides, Map missing, Map remapped, ForgeRegistry old, ResourceLocation name) - { - Map ovs = Maps.newHashMap(overrides); - for (Map.Entry entry : ids.entrySet()) - { + public void loadIds(Map ids, Map overrides, Map missing, Map remapped, ForgeRegistry old, ResourceLocation name) { + Map ovs = new HashMap<>(overrides); + for (Map.Entry entry : ids.entrySet()) { ResourceLocation itemName = entry.getKey(); int newId = entry.getValue(); int currId = old.getIDRaw(itemName); - if (currId == -1) - { + if (currId == -1) { LOGGER.info(REGISTRIES,"Registry {}: Found a missing id from the world {}", this.name, itemName); missing.put(itemName, newId); continue; // no block/item -> nothing to add - } - else if (currId != newId) - { + } else if (currId != newId) { LOGGER.debug(REGISTRIES,"Registry {}: Fixed {} id mismatch {}: {} (init) -> {} (map).", this.name, name, itemName, currId, newId); remapped.put(itemName, new IdMappingEvent.IdRemapping(currId, newId)); } @@ -811,25 +728,20 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist V obj = old.getRaw(itemName); Preconditions.checkState(obj != null, "objectKey has an ID but no object. Reflection/ASM hackery? Registry bug?"); - List lst = Lists.newArrayList(old.overrides.get(itemName)); + List lst = new ArrayList<>(old.overrides.get(itemName)); String primaryName = null; - if (old.overrides.containsKey(itemName)) - { - if (!overrides.containsKey(itemName)) - { + if (old.overrides.containsKey(itemName)) { + if (!overrides.containsKey(itemName)) { lst.add(obj); obj = old.overrides.get(itemName).iterator().next(); //Get the first one in the list, Which should be the first one registered primaryName = old.owners.inverse().get(obj).owner; - } - else + } else primaryName = overrides.get(itemName); } - for (V value : lst) - { + for (V value : lst) { OverrideOwner owner = old.owners.inverse().get(value); - if (owner == null) - { + if (owner == null) { LOGGER.warn(REGISTRIES,"Registry {}: Override did not have an associated owner object. Name: {} Value: {}", this.name, entry.getKey(), value); continue; } @@ -848,16 +760,13 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist ovs.remove(itemName); } - for (Map.Entry entry : ovs.entrySet()) - { + for (Map.Entry entry : ovs.entrySet()) { ResourceLocation itemName = entry.getKey(); String owner = entry.getValue(); String current = this.owners.inverse().get(this.getRaw(itemName)).owner; - if (!owner.equals(current)) - { + if (!owner.equals(current)) { V _new = this.owners.get(new OverrideOwner(owner, ResourceKey.create(this.key, itemName))); - if (_new == null) - { + if (_new == null) { LOGGER.warn(REGISTRIES,"Registry {}: Skipping override for {}, Unknown owner {}", this.name, itemName, owner); continue; } @@ -873,8 +782,7 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist } //Public for tests - public Snapshot makeSnapshot() - { + public Snapshot makeSnapshot() { Snapshot ret = new Snapshot(); this.ids.forEach((id, value) -> ret.ids.put(getKey(value), id)); ret.aliases.putAll(this.aliases); @@ -883,11 +791,9 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist return ret; } - Map getOverrideOwners() - { - Map ret = Maps.newHashMap(); - for (ResourceLocation key : this.overrides.keySet()) - { + Map getOverrideOwners() { + Map ret = new HashMap<>(); + for (ResourceLocation key : this.overrides.keySet()) { V obj = this.names.get(key); OverrideOwner owner = this.owners.inverse().get(obj); if (owner == null) @@ -897,26 +803,18 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist return ret; } - private class RegistryCodec implements Codec - { + private class RegistryCodec implements Codec { @Override - public DataResult> decode(DynamicOps ops, T input) - { - if (ops.compressMaps()) - { - return ops.getNumberValue(input).flatMap(n -> - { + public DataResult> decode(DynamicOps ops, T input) { + if (ops.compressMaps()) { + return ops.getNumberValue(input).flatMap(n -> { int id = n.intValue(); if (ids.get(id) == null) - { return DataResult.error(() -> "Unknown registry id in " + ForgeRegistry.this.key + ": " + n); - } V val = ForgeRegistry.this.getValue(id); return DataResult.success(val); }).map(v -> Pair.of(v, ops.empty())); - } - else - { + } else { return ResourceLocation.CODEC.decode(ops, input).flatMap(keyValuePair -> !ForgeRegistry.this.containsKey(keyValuePair.getFirst()) ? DataResult.error(() -> "Unknown registry key in " + ForgeRegistry.this.key + ": " + keyValuePair.getFirst()) : DataResult.success(keyValuePair.mapFirst(ForgeRegistry.this::getValue))); @@ -924,13 +822,10 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist } @Override - public DataResult encode(V input, DynamicOps ops, T prefix) - { + public DataResult encode(V input, DynamicOps ops, T prefix) { ResourceLocation key = getKey(input); if (key == null) - { return DataResult.error(() -> "Unknown registry element in " + ForgeRegistry.this.key + ": " + input); - } T toMerge = ops.compressMaps() ? ops.createInt(getID(input)) : ops.createString(key.toString()); return ops.mergeToPrimitive(prefix, toMerge); } @@ -938,10 +833,10 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist public static class Snapshot { private static final Comparator sorter = (a,b) -> a.compareNamespaced(b); - public final Map ids = Maps.newTreeMap(sorter); - public final Map aliases = Maps.newTreeMap(sorter); - public final Set blocked = Sets.newTreeSet(); - public final Map overrides = Maps.newTreeMap(sorter); + public final Map ids = new TreeMap<>(sorter); + public final Map aliases = new TreeMap<>(sorter); + public final Set blocked = new TreeSet<>(); + public final Map overrides = new TreeMap<>(sorter); private FriendlyByteBuf binary = null; public CompoundTag write() { @@ -1037,25 +932,21 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist } @SuppressWarnings("unchecked") - public MissingMappingsEvent getMissingEvent(ResourceLocation name, Map map) - { - List> lst = Lists.newArrayList(); + public MissingMappingsEvent getMissingEvent(ResourceLocation name, Map map) { + List> lst = new ArrayList<>(); ForgeRegistry pool = RegistryManager.ACTIVE.getRegistry(name); map.forEach((rl, id) -> lst.add(new MissingMappingsEvent.Mapping<>(this, pool, rl, id))); return new MissingMappingsEvent(ResourceKey.createRegistryKey(name), this, (Collection>) (Collection) lst); } - void processMissingEvent(ResourceLocation name, ForgeRegistry pool, List> mappings, Map missing, Map remaps, Collection defaulted, Collection failed, boolean injectNetworkDummies) - { + void processMissingEvent(ResourceLocation name, ForgeRegistry pool, List> mappings, Map missing, Map remaps, Collection defaulted, Collection failed, boolean injectNetworkDummies) { LOGGER.debug(REGISTRIES,"Processing missing event for {}:", name); int ignored = 0; - for (MissingMappingsEvent.Mapping remap : mappings) - { + for (MissingMappingsEvent.Mapping remap : mappings) { MissingMappingsEvent.Action action = remap.action; - if (action == MissingMappingsEvent.Action.REMAP) - { + if (action == MissingMappingsEvent.Action.REMAP) { // entry re-mapped, finish the registration with the new name/object, but the old id int currId = getID(remap.target); ResourceLocation newName = pool.getKey(remap.target); @@ -1069,35 +960,25 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist this.addAlias(remap.key, newName); - if (currId != realId) - { + if (currId != realId) { LOGGER.info(REGISTRIES,"Fixed id mismatch {}: {} (init) -> {} (map).", newName, currId, realId); remaps.put(newName, new IdMappingEvent.IdRemapping(currId, realId)); } - } - else - { + } else { // block item missing, warn as requested and block the id - if (action == MissingMappingsEvent.Action.DEFAULT) - { + if (action == MissingMappingsEvent.Action.DEFAULT) { V m = this.missing == null ? null : this.missing.createMissing(remap.key, injectNetworkDummies); if (m == null) defaulted.add(remap.key); else this.add(remap.id, remap.key, m, remap.key.getNamespace()); - } - else if (action == MissingMappingsEvent.Action.IGNORE) - { + } else if (action == MissingMappingsEvent.Action.IGNORE) { LOGGER.debug(REGISTRIES,"Ignoring {}", remap.key); ignored++; - } - else if (action == MissingMappingsEvent.Action.FAIL) - { + } else if (action == MissingMappingsEvent.Action.FAIL) { LOGGER.debug(REGISTRIES,"Failing {}!", remap.key); failed.add(remap.key); - } - else if (action == MissingMappingsEvent.Action.WARN) - { + } else if (action == MissingMappingsEvent.Action.WARN) { LOGGER.warn(REGISTRIES,"{} may cause world breakage!", remap.key); } this.block(remap.id); @@ -1108,8 +989,7 @@ public class ForgeRegistry implements IForgeRegistryInternal, IForgeRegist LOGGER.debug(REGISTRIES,"There were {} missing mappings that have been ignored", ignored); } - RegistryBuilder getBuilder() - { + RegistryBuilder getBuilder() { return this.builder; } diff --git a/src/main/java/net/minecraftforge/registries/GameData.java b/src/main/java/net/minecraftforge/registries/GameData.java index 20bdfc7578..c564bdf9af 100644 --- a/src/main/java/net/minecraftforge/registries/GameData.java +++ b/src/main/java/net/minecraftforge/registries/GameData.java @@ -50,11 +50,8 @@ import net.minecraft.world.level.material.Fluid; import net.minecraftforge.common.CreativeModeTabRegistry; import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.common.crafting.conditions.ICondition; import net.minecraftforge.common.loot.IGlobalLootModifier; import net.minecraftforge.common.util.LogMessageAdapter; -import net.minecraftforge.common.world.BiomeModifier; -import net.minecraftforge.common.world.StructureModifier; import net.minecraftforge.fluids.FluidType; import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.ModLoadingContext; @@ -62,7 +59,6 @@ import net.minecraftforge.fml.StartupMessageManager; import net.minecraftforge.fml.util.EnhancedRuntimeException; import net.minecraftforge.fml.util.thread.EffectiveSide; import net.minecraftforge.registries.ForgeRegistries.Keys; -import net.minecraftforge.registries.holdersets.HolderSetType; import org.apache.commons.lang3.Validate; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -154,29 +150,13 @@ public class GameData return makeRegistry(Keys.GLOBAL_LOOT_MODIFIER_SERIALIZERS).disableSaving().disableSync(); } - static RegistryBuilder> getBiomeModifierSerializersRegistryBuilder() - { - return new RegistryBuilder>().disableSaving().disableSync(); - } - - static RegistryBuilder> getStructureModifierSerializersRegistryBuilder() - { - return new RegistryBuilder>().disableSaving().disableSync(); - } - static RegistryBuilder getFluidTypeRegistryBuilder() { return makeRegistry(Keys.FLUID_TYPES).disableSaving(); } - static RegistryBuilder getHolderSetTypeRegistryBuilder() - { - return new RegistryBuilder().disableSaving().disableSync(); - } - - static RegistryBuilder> getConditionCodecRegistryBuilder() - { - return new RegistryBuilder>().disableSaving().disableSync(); + static RegistryBuilder makeUnsavedAndUnsynced() { + return RegistryBuilder.of().disableSaving().disableSync(); } static RegistryBuilder getItemDisplayContextRegistryBuilder() diff --git a/src/main/java/net/minecraftforge/registries/RegistryBuilder.java b/src/main/java/net/minecraftforge/registries/RegistryBuilder.java index f4e0b153a4..323114892f 100644 --- a/src/main/java/net/minecraftforge/registries/RegistryBuilder.java +++ b/src/main/java/net/minecraftforge/registries/RegistryBuilder.java @@ -22,6 +22,18 @@ import java.util.function.Function; public class RegistryBuilder { + public static RegistryBuilder of() { + return new RegistryBuilder(); + } + + public static RegistryBuilder of(String name) { + return of(new ResourceLocation(name)); + } + + public static RegistryBuilder of(ResourceLocation name) { + return new RegistryBuilder().setName(name); + } + private static final int MAX_ID = Integer.MAX_VALUE - 1; private ResourceLocation registryName; diff --git a/src/main/java/net/minecraftforge/registries/holdersets/HolderSetType.java b/src/main/java/net/minecraftforge/registries/holdersets/HolderSetType.java index b3e0882f54..80df39e494 100644 --- a/src/main/java/net/minecraftforge/registries/holdersets/HolderSetType.java +++ b/src/main/java/net/minecraftforge/registries/holdersets/HolderSetType.java @@ -12,7 +12,6 @@ import net.minecraft.core.Registry; import net.minecraft.resources.ResourceKey; @FunctionalInterface -public interface HolderSetType -{ - Codec> makeCodec(ResourceKey> registryKey, Codec> holderCodec, boolean forceList); +public interface HolderSetType { + Codec> makeCodec(ResourceKey> registryKey, Codec> holderCodec, boolean forceList); } diff --git a/src/test/java/net/minecraftforge/debug/gameplay/crafting/ConditionalRecipeTest.java b/src/test/java/net/minecraftforge/debug/gameplay/crafting/ConditionalRecipeTest.java index 9bf0eeb958..1e1b0c045f 100644 --- a/src/test/java/net/minecraftforge/debug/gameplay/crafting/ConditionalRecipeTest.java +++ b/src/test/java/net/minecraftforge/debug/gameplay/crafting/ConditionalRecipeTest.java @@ -271,82 +271,6 @@ public class ConditionalRecipeTest extends BaseTestMod { ::save ) .save(out, rl("conditional_doesnt_load_empty")); - - - /* - - // ingredient tests - // strict NBT match - should match an unnamed iron pickaxe that lost 3 durability - Ingredient nbtIngredient = StrictNBTIngredient.of(Util.make(() -> - { - ItemStack stack = new ItemStack(Items.IRON_PICKAXE); - stack.setDamageValue(3); - return stack; - })); - ShapelessRecipeBuilder.shapeless(RecipeCategory.TOOLS, Items.GOLDEN_PICKAXE) - .requires(nbtIngredient) - .unlockedBy("has_pickaxe", has(Items.IRON_PICKAXE)) - .save(consumer, new ResourceLocation("data_gen_test", "exact_nbt_ingredient")); - - // ingredient tests - // contains NBT match - should match a stone pickaxe that lost 3 durability, regardless of setting its name - ShapelessRecipeBuilder.shapeless(RecipeCategory.TOOLS, Items.IRON_PICKAXE) - .requires(PartialNBTIngredient.of(Items.STONE_PICKAXE, Util.make(() -> - { - CompoundTag nbt = new CompoundTag(); - nbt.putInt(ItemStack.TAG_DAMAGE, 3); - return nbt; - }))) - .unlockedBy("has_pickaxe", has(Items.STONE_PICKAXE)) - .save(consumer, new ResourceLocation("data_gen_test", "contains_nbt_ingredient_single_item")); - - // contains NBT match - should match a wood, stone, or iron pickaxe that was named "Diamond Pickaxe", regardless of damage - ShapelessRecipeBuilder.shapeless(RecipeCategory.TOOLS, Items.DIAMOND_PICKAXE) - .requires(PartialNBTIngredient.of(Util.make(() -> - { - CompoundTag nbt = new CompoundTag(); - CompoundTag display = new CompoundTag(); - display.putString(ItemStack.TAG_DISPLAY_NAME, "{\"text\":\"Diamond Pickaxe\"}"); - nbt.put(ItemStack.TAG_DISPLAY, display); - return nbt; - }), Items.WOODEN_PICKAXE, Items.STONE_PICKAXE, Items.IRON_PICKAXE)) - .unlockedBy("has_pickaxe", has(Items.WOODEN_PICKAXE)) - .save(consumer, new ResourceLocation("data_gen_test", "contains_nbt_ingredient_item_set")); - - // intersection - should match all non-flammable planks - ShapedRecipeBuilder.shaped(RecipeCategory.BUILDING_BLOCKS, Blocks.NETHERRACK) - .pattern("###") - .pattern("###") - .pattern(" # ") - .define('#', IntersectionIngredient.of(Ingredient.of(ItemTags.PLANKS), Ingredient.of(ItemTags.NON_FLAMMABLE_WOOD))) - .unlockedBy("has_planks", has(Items.CRIMSON_PLANKS)) - .save(consumer, new ResourceLocation("data_gen_test", "intersection_ingredient")); - - // difference - should match all flammable fences - ShapedRecipeBuilder.shaped(RecipeCategory.TOOLS, Items.FLINT_AND_STEEL) - .pattern(" # ") - .pattern("###") - .pattern(" # ") - .define('#', DifferenceIngredient.of(Ingredient.of(ItemTags.FENCES), Ingredient.of(ItemTags.NON_FLAMMABLE_WOOD))) - .unlockedBy("has_fence", has(Items.CRIMSON_FENCE)) - .save(consumer, new ResourceLocation("data_gen_test", "difference_ingredient")); - - // compound - should match planks, logs, or bedrock - ShapedRecipeBuilder.shaped(RecipeCategory.BUILDING_BLOCKS, Blocks.DIRT) - .pattern("###") - .pattern(" # ") - .define('#', CompoundIngredient.of(Ingredient.of(ItemTags.PLANKS), Ingredient.of(ItemTags.LOGS), Ingredient.of(Blocks.BEDROCK))) - .unlockedBy("has_planks", has(Items.CRIMSON_PLANKS)) - .save(consumer, new ResourceLocation("data_gen_test", "compound_ingredient_only_vanilla")); - - // compound - should match planks, logs, or a stone pickaxe with 3 damage - ShapedRecipeBuilder.shaped(RecipeCategory.BUILDING_BLOCKS, Blocks.GOLD_BLOCK) - .pattern("#") - .pattern("#") - .define('#', CompoundIngredient.of(Ingredient.of(ItemTags.PLANKS), Ingredient.of(ItemTags.LOGS), nbtIngredient)) - .unlockedBy("has_planks", has(Items.CRIMSON_PLANKS)) - .save(consumer, new ResourceLocation("data_gen_test", "compound_ingredient_custom_types")); - */ } } diff --git a/src/test/java/net/minecraftforge/debug/gameplay/crafting/CustomIngredientsTest.java b/src/test/java/net/minecraftforge/debug/gameplay/crafting/CustomIngredientsTest.java new file mode 100644 index 0000000000..e08bef7037 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/gameplay/crafting/CustomIngredientsTest.java @@ -0,0 +1,320 @@ +/* + * Copyright (c) Forge Development LLC and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.minecraftforge.debug.gameplay.crafting; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Function; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.core.HolderLookup; +import net.minecraft.data.PackOutput; +import net.minecraft.data.recipes.RecipeCategory; +import net.minecraft.data.recipes.RecipeOutput; +import net.minecraft.data.recipes.RecipeProvider; +import net.minecraft.data.recipes.ShapedRecipeBuilder; +import net.minecraft.data.tags.ItemTagsProvider; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.ItemTags; +import net.minecraft.tags.TagKey; +import net.minecraft.world.Container; +import net.minecraft.world.inventory.CraftingContainer; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.ItemLike; +import net.minecraftforge.common.crafting.SimpleCraftingContainer; +import net.minecraftforge.common.crafting.conditions.IConditionBuilder; +import net.minecraftforge.common.crafting.ingredients.IIngredientBuilder; +import net.minecraftforge.common.data.BlockTagsProvider; +import net.minecraftforge.common.data.ExistingFileHelper; +import net.minecraftforge.common.util.INBTBuilder; +import net.minecraftforge.data.event.GatherDataEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.gametest.GameTestHolder; +import net.minecraftforge.test.BaseTestMod; + +@GameTestHolder("forge." + CustomIngredientsTest.MODID) +@Mod(CustomIngredientsTest.MODID) +public class CustomIngredientsTest extends BaseTestMod implements INBTBuilder { + static final String MODID = "custom_ingredients"; + private static final int TEST_DAMAGE = 3; + private static final String TEST_DISPLAY_NAME = "{\"text\":\"Test Item\"}"; + private static final TagKey LEFT = tag("left"); + private static final TagKey RIGHT = tag("right"); + + @SubscribeEvent + public void gatherData(GatherDataEvent event) { + var gen = event.getGenerator(); + var out = gen.getPackOutput(); + var look = event.getLookupProvider(); + var exist = event.getExistingFileHelper(); + + var blockTags = new BlockTagsGen(out, look, exist); + gen.addProvider(event.includeServer(), blockTags); + gen.addProvider(event.includeServer(), new ItemTagsGen(out, look, blockTags, exist)); + gen.addProvider(event.includeServer(), new Recipes(out)); + } + + private static ResourceLocation rl(String path) { + return new ResourceLocation(MODID, path); + } + + private static TagKey tag(String name) { + return ItemTags.create(rl(name)); + } + + private static > void assertRecipeMiss(GameTestHelper helper, RecipeType type, C container) { + var recipe = helper.getLevel().getRecipeManager().getRecipeFor(type, container, helper.getLevel()); + helper.assertTrue(recipe.isEmpty(), () -> "Found crafting recipe when unexpected: " + recipe.get().id()); + helper.succeed(); + } + + private static > void assertRecipeMatch(GameTestHelper helper, RecipeType type, C container, String name) { + var recipe = helper.getLevel().getRecipeManager().getRecipeFor(type, container, helper.getLevel()); + helper.assertTrue(recipe.isPresent(), "Did not find crafting recipe when expected!"); + helper.assertTrue(recipe.get().id().getNamespace().equals(MODID), "It wasn't our recipe: " + recipe.get().id()); + helper.assertTrue(recipe.get().id().getPath().equals(name), "It wasn't the correct recipe expected: " + name + " got: " + recipe.get().id()); + helper.succeed(); + } + + + private static CompoundTag named() { + return CompoundTag.builder() + .put(ItemStack.TAG_DISPLAY_NAME, TEST_DISPLAY_NAME) + .build(); + } + + private static CompoundTag damaged() { + return CompoundTag.builder() + .putInt(ItemStack.TAG_DAMAGE, TEST_DAMAGE) + .build(); + } + + private static ItemStack damaged(ItemLike item) { + return tagged(item, damaged()); + } + + private static ItemStack named(ItemLike item) { + return tagged(item, named()); + } + + public static ItemStack stack(ItemLike item) { + return new ItemStack(item); + } + + private static ItemStack tagged(ItemLike item, CompoundTag tag) { + var ret = stack(item); + ret.setTag(tag.copy()); + return ret; + } + + @GameTest(template = "forge:empty3x3x3") + public static void partial_nbt(GameTestHelper helper) { + var both = named().merge(damaged()); + var wood = stack(Items.WOODEN_PICKAXE); + var wood_tag = tagged(Items.WOODEN_PICKAXE, both); + var stone = stack(Items.STONE_PICKAXE); + var stone_tag = tagged(Items.STONE_PICKAXE, both); + var iron = stack(Items.IRON_PICKAXE); + var iron_tag = tagged(Items.IRON_PICKAXE, both); + var diamond = stack(Items.DIAMOND_PICKAXE); + var diamond_tag = tagged(Items.DIAMOND_PICKAXE, both); + Function container = (stack) -> + SimpleCraftingContainer.builder() + .pattern("XXX") + .pattern("XXX") + .define('X', stack) + .build(); + + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(wood)); + assertRecipeMatch(helper, RecipeType.CRAFTING, container.apply(wood_tag), "partial_nbt_damage_only"); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(stone)); + assertRecipeMatch(helper, RecipeType.CRAFTING, container.apply(stone_tag), "partial_nbt_damage_only"); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(iron)); + assertRecipeMatch(helper, RecipeType.CRAFTING, container.apply(iron_tag), "partial_nbt_damage_only"); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(diamond)); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(diamond_tag)); + + container = (stack) -> + SimpleCraftingContainer.builder() + .pattern("XXX") + .define('X', stack) + .build(); + + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(wood)); + assertRecipeMatch(helper, RecipeType.CRAFTING, container.apply(wood_tag), "partial_nbt_name_only"); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(stone)); + assertRecipeMatch(helper, RecipeType.CRAFTING, container.apply(stone_tag), "partial_nbt_name_only"); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(iron)); + assertRecipeMatch(helper, RecipeType.CRAFTING, container.apply(iron_tag), "partial_nbt_name_only"); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(diamond)); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(diamond_tag)); + } + + @GameTest(template = "forge:empty3x3x3") + public static void compound(GameTestHelper helper) { + Function container = (stack) -> + SimpleCraftingContainer.builder() + .pattern("XXX") + .pattern(" X ") + .define('X', stack) + .build(); + + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(stack(Items.DIRT))); + assertRecipeMatch(helper, RecipeType.CRAFTING, container.apply(named(Items.DIRT)), "compound_ingredient"); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(stack(Items.OAK_LOG))); + assertRecipeMatch(helper, RecipeType.CRAFTING, container.apply(named(Items.OAK_LOG)), "compound_ingredient"); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(stack(Items.REDSTONE))); + } + + @GameTest(template = "forge:empty3x3x3") + public static void strict(GameTestHelper helper) { + Function container = (stack) -> + SimpleCraftingContainer.builder() + .pattern("XXX") + .define('X', stack) + .build(); + + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(stack(Items.IRON_PICKAXE))); + assertRecipeMatch(helper, RecipeType.CRAFTING, container.apply(damaged(Items.IRON_PICKAXE)), "strict_nbt_ingredient"); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(damaged(Items.STONE_PICKAXE))); + } + + @GameTest(template = "forge:empty3x3x3") + public static void intersection(GameTestHelper helper) { + Function container = (stack) -> + SimpleCraftingContainer.builder() + .pattern("XXX") + .pattern("XXX") + .pattern(" X ") + .define('X', stack) + .build(); + + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(stack(Items.DIRT))); + assertRecipeMatch(helper, RecipeType.CRAFTING, container.apply(stack(Items.STONE)), "intersection_ingredient"); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(stack(Items.GRAVEL))); + } + + + @GameTest(template = "forge:empty3x3x3") + public static void difference(GameTestHelper helper) { + Function container = (stack) -> + SimpleCraftingContainer.builder() + .pattern(" X ") + .pattern("XXX") + .pattern(" X ") + .define('X', stack) + .build(); + + assertRecipeMatch(helper, RecipeType.CRAFTING, container.apply(stack(Items.DIRT)), "difference_ingredient"); + assertRecipeMiss(helper, RecipeType.CRAFTING, container.apply(stack(Items.STONE))); + } + + private static class BlockTagsGen extends BlockTagsProvider { + public BlockTagsGen(PackOutput out, CompletableFuture look, @Nullable ExistingFileHelper exist) { + super(out, look, MODID, exist); + } + + @Override + public void addTags(HolderLookup.Provider lookup) { } + } + + private static class ItemTagsGen extends ItemTagsProvider { + public ItemTagsGen(PackOutput out, CompletableFuture lookup, BlockTagsProvider blocks, ExistingFileHelper existing) { + super(out, lookup, blocks.contentsGetter(), MODID, existing); + } + + @Override + public void addTags(HolderLookup.Provider lookup) { + tag(LEFT).add(Items.DIRT, Items.STONE); + tag(RIGHT).add(Items.STONE, Items.GRAVEL); + } + } + + private static class Recipes extends RecipeProvider implements IConditionBuilder, IIngredientBuilder, INBTBuilder{ + public Recipes(PackOutput gen) { + super(gen); + } + + private static ShapedRecipeBuilder shaped() { + return ShapedRecipeBuilder.shaped(RecipeCategory.MISC, Items.DIRT); + } + + @Override + protected void buildRecipes(RecipeOutput out) { + var hasName = getHasName(Items.DIRT); + var has = has(Items.DIRT); + + // contains NBT match - should match a stone pickaxe that lost 3 durability, regardless of setting its name + shaped() + .pattern("XXX") + .pattern("XXX") + .define('X', + partialNbt() + .nbt(damaged()) + .items(Items.WOODEN_PICKAXE, Items.STONE_PICKAXE, Items.IRON_PICKAXE) + .build() + ) + .unlockedBy(hasName, has) + .save(out, rl("partial_nbt_damage_only")); + + // contains NBT match - should match a named wood, stone, or iron pickaxe, regardless of damage + shaped() + .pattern("XXX") + .define('X', + partialNbt() + .nbt(named()) + .items(Items.WOODEN_PICKAXE, Items.STONE_PICKAXE, Items.IRON_PICKAXE) + .build() + ) + .unlockedBy(hasName, has) + .save(out, rl("partial_nbt_name_only")); + + // compound - should match named dirt or named oak logs + shaped() + .pattern("XXX") + .pattern(" X ") + .define('X', compound( + strictNbt(named(Items.DIRT)), + strictNbt(named(Items.OAK_LOG)) + )) + .unlockedBy(hasName, has) + .save(out, rl("compound_ingredient")); + + // strict NBT match - should match an unnamed pickaxe that lost 3 durability + shaped() + .pattern("XXX") + .define('X', strictNbt(damaged(Items.IRON_PICKAXE))) + .unlockedBy(hasName, has) + .save(out, rl("strict_nbt_ingredient")); + + // intersection - should match STONE only, [dirt, stone] + [stone, gravel] + shaped() + .pattern("XXX") + .pattern("XXX") + .pattern(" X ") + .define('X', intersection(LEFT, RIGHT)) + .unlockedBy(hasName, has) + .save(out, rl("intersection_ingredient")); + + // difference - should match DIRT only, [dirt, stone] - [stone, gravel] + shaped() + .pattern(" X ") + .pattern("XXX") + .pattern(" X ") + .define('X', difference(LEFT, RIGHT)) + .unlockedBy(hasName, has) + .save(out, rl("difference_ingredient")); + } + } +}