From c768f956ed52ddcc56593d7019fe462c6f309ef1 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Mon, 20 Jan 2025 12:00:58 +0200 Subject: [PATCH] Add a biome villagers data map to replace `VillagerType#BY_BIOME` This PR adds a `neoforge:biome_villagers` biome data map to replace `VillagerType#BY_BIOME`, allowing mods to change the type of the villagers spawned in their own biomes. To avoid a sudden breaking change, the map will still be used for now, but the fallback to it will be removed in 1.22. --- .../world/entity/npc/VillagerType.java.patch | 21 +++++ .../worldgen/biome/biome_villagers.json | 91 +++++++++++++++++++ .../internal/NeoForgeDataMapsProvider.java | 7 ++ .../datamaps/builtin/BiomeVillager.java | 25 +++++ .../datamaps/builtin/NeoForgeDataMaps.java | 16 ++++ 5 files changed, 160 insertions(+) create mode 100644 patches/net/minecraft/world/entity/npc/VillagerType.java.patch create mode 100644 src/generated/resources/data/neoforge/data_maps/worldgen/biome/biome_villagers.json create mode 100644 src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/BiomeVillager.java diff --git a/patches/net/minecraft/world/entity/npc/VillagerType.java.patch b/patches/net/minecraft/world/entity/npc/VillagerType.java.patch new file mode 100644 index 00000000000..6b09c51c7e3 --- /dev/null +++ b/patches/net/minecraft/world/entity/npc/VillagerType.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/world/entity/npc/VillagerType.java ++++ b/net/minecraft/world/entity/npc/VillagerType.java +@@ -21,6 +_,8 @@ + public static final VillagerType SWAMP = register("swamp"); + public static final VillagerType TAIGA = register("taiga"); + private final String name; ++ /** Neo: use the {@link net.neoforged.neoforge.registries.datamaps.builtin.NeoForgeDataMaps.BIOME_VILLAGERS data map} instead as this field will be ignored in future versions */ ++ @Deprecated + private static final Map, VillagerType> BY_BIOME = Util.make(Maps.newHashMap(), p_35834_ -> { + p_35834_.put(Biomes.BADLANDS, DESERT); + p_35834_.put(Biomes.DESERT, DESERT); +@@ -67,6 +_,9 @@ + } + + public static VillagerType byBiome(Holder p_204074_) { ++ var fromDataMap = p_204074_.getData(net.neoforged.neoforge.registries.datamaps.builtin.NeoForgeDataMaps.BIOME_VILLAGERS); ++ if (fromDataMap != null) return fromDataMap.type(); ++ // TODO - 1.22: remove fallback + return p_204074_.unwrapKey().map(BY_BIOME::get).orElse(PLAINS); + } + } diff --git a/src/generated/resources/data/neoforge/data_maps/worldgen/biome/biome_villagers.json b/src/generated/resources/data/neoforge/data_maps/worldgen/biome/biome_villagers.json new file mode 100644 index 00000000000..0dd3b7ff2ef --- /dev/null +++ b/src/generated/resources/data/neoforge/data_maps/worldgen/biome/biome_villagers.json @@ -0,0 +1,91 @@ +{ + "values": { + "minecraft:badlands": { + "villager_type": "minecraft:desert" + }, + "minecraft:bamboo_jungle": { + "villager_type": "minecraft:jungle" + }, + "minecraft:deep_frozen_ocean": { + "villager_type": "minecraft:snow" + }, + "minecraft:desert": { + "villager_type": "minecraft:desert" + }, + "minecraft:eroded_badlands": { + "villager_type": "minecraft:desert" + }, + "minecraft:frozen_ocean": { + "villager_type": "minecraft:snow" + }, + "minecraft:frozen_peaks": { + "villager_type": "minecraft:snow" + }, + "minecraft:frozen_river": { + "villager_type": "minecraft:snow" + }, + "minecraft:grove": { + "villager_type": "minecraft:snow" + }, + "minecraft:ice_spikes": { + "villager_type": "minecraft:snow" + }, + "minecraft:jagged_peaks": { + "villager_type": "minecraft:snow" + }, + "minecraft:jungle": { + "villager_type": "minecraft:jungle" + }, + "minecraft:mangrove_swamp": { + "villager_type": "minecraft:swamp" + }, + "minecraft:old_growth_pine_taiga": { + "villager_type": "minecraft:taiga" + }, + "minecraft:old_growth_spruce_taiga": { + "villager_type": "minecraft:taiga" + }, + "minecraft:savanna": { + "villager_type": "minecraft:savanna" + }, + "minecraft:savanna_plateau": { + "villager_type": "minecraft:savanna" + }, + "minecraft:snowy_beach": { + "villager_type": "minecraft:snow" + }, + "minecraft:snowy_plains": { + "villager_type": "minecraft:snow" + }, + "minecraft:snowy_slopes": { + "villager_type": "minecraft:snow" + }, + "minecraft:snowy_taiga": { + "villager_type": "minecraft:snow" + }, + "minecraft:sparse_jungle": { + "villager_type": "minecraft:jungle" + }, + "minecraft:swamp": { + "villager_type": "minecraft:swamp" + }, + "minecraft:taiga": { + "villager_type": "minecraft:taiga" + }, + "minecraft:windswept_forest": { + "villager_type": "minecraft:taiga" + }, + "minecraft:windswept_gravelly_hills": { + "villager_type": "minecraft:taiga" + }, + "minecraft:windswept_hills": { + "villager_type": "minecraft:taiga" + }, + "minecraft:windswept_savanna": { + "villager_type": "minecraft:savanna" + }, + "minecraft:wooded_badlands": { + "villager_type": "minecraft:desert" + } + } +} \ No newline at end of file diff --git a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeDataMapsProvider.java b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeDataMapsProvider.java index 1dd3b1d0a8e..13f8d001717 100644 --- a/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeDataMapsProvider.java +++ b/src/main/java/net/neoforged/neoforge/common/data/internal/NeoForgeDataMapsProvider.java @@ -24,10 +24,12 @@ import net.minecraft.world.entity.ai.behavior.WorkAtComposter; import net.minecraft.world.entity.animal.Parrot; import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerType; import net.minecraft.world.flag.FeatureFlags; import net.minecraft.world.item.HoneycombItem; import net.minecraft.world.item.Item; import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.ComposterBlock; import net.minecraft.world.level.block.WeatheringCopper; import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity; @@ -38,6 +40,7 @@ import net.minecraft.world.level.storage.loot.LootTable; import net.neoforged.fml.util.ObfuscationReflectionHelper; import net.neoforged.neoforge.common.data.DataMapProvider; +import net.neoforged.neoforge.registries.datamaps.builtin.BiomeVillager; import net.neoforged.neoforge.registries.datamaps.builtin.Compostable; import net.neoforged.neoforge.registries.datamaps.builtin.FurnaceFuel; import net.neoforged.neoforge.registries.datamaps.builtin.MonsterRoomMob; @@ -55,6 +58,10 @@ public NeoForgeDataMapsProvider(PackOutput packOutput, CompletableFuture, VillagerType>, VillagerType>getPrivateValue(VillagerType.class, null, "BY_BIOME") + .forEach((biome, type) -> biomeVillagers.add(biome, new BiomeVillager(type), false)); + final var compostables = builder(NeoForgeDataMaps.COMPOSTABLES); final List villagerCompostables = ObfuscationReflectionHelper.getPrivateValue(WorkAtComposter.class, null, "COMPOSTABLE_ITEMS"); ComposterBlock.COMPOSTABLES.forEach((item, chance) -> compostables.add(item.asItem().builtInRegistryHolder(), new Compostable(chance, villagerCompostables.contains(item.asItem())), false)); diff --git a/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/BiomeVillager.java b/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/BiomeVillager.java new file mode 100644 index 00000000000..559315e9780 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/BiomeVillager.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.registries.datamaps.builtin; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.entity.npc.VillagerType; + +/** + * Data map value for {@linkplain NeoForgeDataMaps#BIOME_VILLAGERS biome villagers}. + * + * @param type the type of the villagers present in this biome + */ +public record BiomeVillager(VillagerType type) { + public static final Codec TYPE_CODEC = BuiltInRegistries.VILLAGER_TYPE.byNameCodec() + .xmap(BiomeVillager::new, BiomeVillager::type); + public static final Codec CODEC = Codec.withAlternative( + RecordCodecBuilder.create(in -> in.group( + BuiltInRegistries.VILLAGER_TYPE.byNameCodec().fieldOf("villager_type").forGetter(BiomeVillager::type)).apply(in, BiomeVillager::new)), + TYPE_CODEC); +} diff --git a/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/NeoForgeDataMaps.java b/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/NeoForgeDataMaps.java index 330857d5412..b0ae6a2b1e8 100644 --- a/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/NeoForgeDataMaps.java +++ b/src/main/java/net/neoforged/neoforge/registries/datamaps/builtin/NeoForgeDataMaps.java @@ -11,10 +11,12 @@ import net.minecraft.world.entity.ai.behavior.GiveGiftToHero; import net.minecraft.world.entity.animal.Parrot; import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerType; import net.minecraft.world.item.HoneycombItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.ComposterBlock; import net.minecraft.world.level.block.WeatheringCopper; @@ -36,6 +38,19 @@ * synced so that mods can use them on the client side. */ public class NeoForgeDataMaps { + /** + * The {@linkplain Biome} data map that replaces {@link VillagerType#BY_BIOME}. + *

+ * The location of this data map is {@code neoforge/data_maps/biome/biome_villagers.json}, and the values are objects with 1 field: + *

    + *
  • {@code villager_type}, villager type ID - the type of the villagers present in the biome
  • + *
+ * + * The use of a string as the value is also possible, though discouraged in case more options are added in the future. + */ + public static final DataMapType BIOME_VILLAGERS = DataMapType.builder( + id("biome_villagers"), Registries.BIOME, BiomeVillager.CODEC).synced(BiomeVillager.TYPE_CODEC, false).build(); + /** * The {@linkplain Item} data map that replaces {@link ComposterBlock#COMPOSTABLES}. *

@@ -147,6 +162,7 @@ private static ResourceLocation id(final String name) { @SubscribeEvent private static void register(final RegisterDataMapTypesEvent event) { + event.register(BIOME_VILLAGERS); event.register(COMPOSTABLES); event.register(FURNACE_FUELS); event.register(MONSTER_ROOM_MOBS);