Skip to content

Commit

Permalink
Add a biome villagers data map to replace VillagerType#BY_BIOME
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Matyrobbrt committed Jan 20, 2025
1 parent 1fabdf8 commit c768f95
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 0 deletions.
21 changes: 21 additions & 0 deletions patches/net/minecraft/world/entity/npc/VillagerType.java.patch
Original file line number Diff line number Diff line change
@@ -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<ResourceKey<Biome>, 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<Biome> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -55,6 +58,10 @@ public NeoForgeDataMapsProvider(PackOutput packOutput, CompletableFuture<HolderL

@Override
protected void gather(HolderLookup.Provider provider) {
final var biomeVillagers = builder(NeoForgeDataMaps.BIOME_VILLAGERS);
ObfuscationReflectionHelper.<Map<ResourceKey<Biome>, 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<Item> 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));
Expand Down
Original file line number Diff line number Diff line change
@@ -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<BiomeVillager> TYPE_CODEC = BuiltInRegistries.VILLAGER_TYPE.byNameCodec()
.xmap(BiomeVillager::new, BiomeVillager::type);
public static final Codec<BiomeVillager> CODEC = Codec.withAlternative(
RecordCodecBuilder.create(in -> in.group(
BuiltInRegistries.VILLAGER_TYPE.byNameCodec().fieldOf("villager_type").forGetter(BiomeVillager::type)).apply(in, BiomeVillager::new)),
TYPE_CODEC);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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}.
* <p>
* The location of this data map is {@code neoforge/data_maps/biome/biome_villagers.json}, and the values are objects with 1 field:
* <ul>
* <li>{@code villager_type}, villager type ID - the type of the villagers present in the biome</li>
* </ul>
*
* 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, BiomeVillager> 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}.
* <p>
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit c768f95

Please sign in to comment.