Skip to content

Instantly share code, notes, and snippets.

@TelepathicGrunt
Last active January 4, 2024 18:31
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TelepathicGrunt/c02333993a1c35dea26fdb98fead5074 to your computer and use it in GitHub Desktop.
Save TelepathicGrunt/c02333993a1c35dea26fdb98fead5074 to your computer and use it in GitHub Desktop.
how to add to village farms in a mod/datapack compatible way that can stack without overwriting each other.
// This gist code is public domain. Take it! Steal it! AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!!!!!!!!!!!!
// 1.18.2+
public ExampleMod() {
MinecraftForge.EVENT_BUS.addListener(this::addNewVillageCrop);
}
public void addNewVillageCrop(final ServerAboutToStartEvent event) {
// Dynamic registry of processor lists. We will edit the processor lists from here as this is what the world will actually use.
Registry<StructureProcessorList> processorListRegistry = event.getServer().registryAccess().registry(Registry.PROCESSOR_LIST_REGISTRY).orElseThrow();
// Create a new processor to add to the processor lists
StructureProcessor newCropProcessor = new RuleProcessor(List.of(new ProcessorRule(
// We replace the vanilla Wheat block with Sweet Berry Bush 50% of the time.
// Note, Potatoes and Beetroot will also be avaliable to be replaced too by our processor.
new RandomBlockMatchTest(Blocks.WHEAT, 0.5F),
// Location predicate. Keep this as always true for most use-cases.
AlwaysTrueTest.INSTANCE,
// The modded block to use.
Blocks.SWEET_BERRY_BUSH.defaultBlockState()
)));
// Now add new rule processor to vanilla crop processors
addNewRuleToProcessorList(new ResourceLocation("minecraft:farm_plains"), newCropProcessor, processorListRegistry);
addNewRuleToProcessorList(new ResourceLocation("minecraft:farm_savanna"), newCropProcessor, processorListRegistry);
addNewRuleToProcessorList(new ResourceLocation("minecraft:farm_snowy"), newCropProcessor, processorListRegistry);
addNewRuleToProcessorList(new ResourceLocation("minecraft:farm_taiga"), newCropProcessor, processorListRegistry);
addNewRuleToProcessorList(new ResourceLocation("minecraft:farm_desert"), newCropProcessor, processorListRegistry);
// Can target other mod's processor lists
addNewRuleToProcessorList(new ResourceLocation("repurposed_structures:villages/badlands/crop_replacement"), newCropProcessor, processorListRegistry);
addNewRuleToProcessorList(new ResourceLocation("repurposed_structures:villages/birch/crop_randomizer"), newCropProcessor, processorListRegistry);
addNewRuleToProcessorList(new ResourceLocation("repurposed_structures:villages/dark_forest/crop_randomizer"), newCropProcessor, processorListRegistry);
addNewRuleToProcessorList(new ResourceLocation("repurposed_structures:villages/giant_taiga/crop_randomizer"), newCropProcessor, processorListRegistry);
addNewRuleToProcessorList(new ResourceLocation("repurposed_structures:villages/jungle/crop_randomizer"), newCropProcessor, processorListRegistry);
addNewRuleToProcessorList(new ResourceLocation("repurposed_structures:villages/mountains/crop_randomizer"), newCropProcessor, processorListRegistry);
addNewRuleToProcessorList(new ResourceLocation("repurposed_structures:villages/oak/crop_randomizer"), newCropProcessor, processorListRegistry);
addNewRuleToProcessorList(new ResourceLocation("repurposed_structures:villages/swamp/crop_randomizer"), newCropProcessor, processorListRegistry);
}
private static void addNewRuleToProcessorList(ResourceLocation targetProcessorList, StructureProcessor processorToAdd, Registry<StructureProcessorList> processorListRegistry) {
processorListRegistry.getOptional(targetProcessorList)
.ifPresent(processorList -> {
// The list of processor is an immutable list.
// So we will create a new list, add to it, and set the field to that new list.
List<StructureProcessor> newSafeList = new ArrayList<>(processorList.list());
newSafeList.add(processorToAdd);
processorList.list = newSafeList; // Use an Access Transformer or Accessor Mixin to set the private list field.
});
}
////////////////////////////////////////////////////////
// 1.16.5 - (this old way is outdated and probably entirely unnecessary. Try the method done above instead and see if that works in 1.16.5)
public ExampleMod() {
MinecraftForge.EVENT_BUS.addListener(this::addNewVillageCrop);
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::createProcessors);
}
// We will create the processor in FMLCommonSetupEvent as we know our
// block will be created and registered by the time this event fires.
public static RuleStructureProcessor MODDED_CROP_PROCESSOR;
public void createProcessors(final FMLCommonSetupEvent event) {
MODDED_CROP_PROCESSOR = new RuleStructureProcessor(ImmutableList.of(
new RuleEntry(
// We replace the vanilla Wheat block with Sweet Berry Bush 50% of the time.
// Note, Potatoes and Beetroot will also be avaliable to be replaced too by our processor.
new RandomBlockMatchRuleTest(Blocks.WHEAT, 0.5F),
// Location predicate. Keep this as always true for most use-cases.
AlwaysTrueRuleTest.INSTANCE,
// The modded block to use.
Blocks.SWEET_BERRY_BUSH.defaultBlockState()
)
));
}
/**
* Appends the given processor to the end of the targeted pieces in the targeted pools.
* We will call this in addNewVillageCrop method further down to add to every village farm pool entry.
*
* Note: This is an additive operation which means multiple mods can do this and they stack with each other safely.
*/
private static void addCropToPoolElement(MutableRegistry<JigsawPattern> templatePoolRegistry, ResourceLocation poolRL, List<ResourceLocation> nbtPieceRLList) {
JigsawPattern pool = templatePoolRegistry.get(poolRL);
if (pool == null) return;
// AccessTransformer to make JigsawPattern's templates field public for us to see.
// public net.minecraft.world.gen.feature.jigsaw.JigsawPattern field_214953_e #templates
pool.templates.forEach(entry -> {
if (entry instanceof SingleJigsawPiece) {
SingleJigsawPiece jigsawPiece = (SingleJigsawPiece) entry;
// AccessTransformer to make SingleJigsawPiece's template field public for us to see.
// public net.minecraft.world.gen.feature.jigsaw.SingleJigsawPiece field_236839_c_ #template
if (jigsawPiece.template.left().isPresent()) {
// Found the piece entry we want to modify.
ResourceLocation currentNBTPiece = jigsawPiece.template.left().get();
if (nbtPieceRLList.stream().anyMatch(currentNBTPiece::equals)) {
// AccessTransformer to make SingleJigsawPiece's processors field public and non-final for us to see and swap out.
// public-f net.minecraft.world.gen.feature.jigsaw.SingleJigsawPiece field_214862_b #processors
StructureProcessorList originalStructureProcessorList = jigsawPiece.processors.get();
// Make a mutable list as this is an immutable list originally
List<StructureProcessor> mutableProcessorList = new ArrayList<>(originalStructureProcessorList.list());
// Add our processor to the end so it runs after everything else
mutableProcessorList.add(MODDED_CROP_PROCESSOR);
StructureProcessorList newStructureProcessorList = new StructureProcessorList(mutableProcessorList);
// Override the original field with our new instance. This is an additive operation and safe to stack with other mods
jigsawPiece.processors = () -> newStructureProcessorList;
}
}
}
});
}
/**
* We use FMLServerAboutToStartEvent as the dynamic registry exists now and all JSON worldgen files were parsed.
* Mod compat is best done here.
*/
public void addNewVillageCrop(final FMLServerAboutToStartEvent event) {
MutableRegistry<JigsawPattern> templatePoolRegistry = event.getServer().registryAccess().registry(Registry.TEMPLATE_POOL_REGISTRY).get();
// Adds the crop processor to all vanilla farm village pieces
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("minecraft:village/plains/houses"), ImmutableList.of(
new ResourceLocation("minecraft:village/plains/houses/plains_large_farm_1"),
new ResourceLocation("minecraft:village/plains/houses/plains_small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("minecraft:village/plains/zombie/houses"), ImmutableList.of(
new ResourceLocation("minecraft:village/plains/houses/plains_large_farm_1"),
new ResourceLocation("minecraft:village/plains/houses/plains_small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("minecraft:village/snowy/houses"), ImmutableList.of(
new ResourceLocation("minecraft:village/snowy/houses/snowy_farm_1"),
new ResourceLocation("minecraft:village/snowy/houses/snowy_farm_2")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("minecraft:village/snowy/zombie/houses"), ImmutableList.of(
new ResourceLocation("minecraft:village/snowy/houses/snowy_farm_1"),
new ResourceLocation("minecraft:village/snowy/houses/snowy_farm_2")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("minecraft:village/savanna/houses"), ImmutableList.of(
new ResourceLocation("minecraft:village/savanna/houses/savanna_large_farm_1"),
new ResourceLocation("minecraft:village/savanna/houses/savanna_large_farm_2"),
new ResourceLocation("minecraft:village/savanna/houses/savanna_small_farm")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("minecraft:village/savanna/zombie/houses"), ImmutableList.of(
new ResourceLocation("minecraft:village/savanna/houses/savanna_large_farm_1"),
new ResourceLocation("minecraft:village/savanna/zombie/houses/savanna_large_farm_2"),
new ResourceLocation("minecraft:village/savanna/houses/savanna_small_farm")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("minecraft:village/taiga/houses"), ImmutableList.of(
new ResourceLocation("minecraft:village/taiga/houses/taiga_large_farm_1"),
new ResourceLocation("minecraft:village/taiga/houses/taiga_large_farm_2"),
new ResourceLocation("minecraft:village/taiga/houses/taiga_small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("minecraft:village/taiga/zombie/houses"), ImmutableList.of(
new ResourceLocation("minecraft:village/taiga/houses/taiga_large_farm_1"),
new ResourceLocation("minecraft:village/taiga/zombie/houses/taiga_large_farm_2"),
new ResourceLocation("minecraft:village/taiga/houses/taiga_small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("minecraft:village/desert/houses"), ImmutableList.of(
new ResourceLocation("minecraft:village/desert/houses/desert_large_farm_1"),
new ResourceLocation("minecraft:village/desert/houses/desert_farm_1"),
new ResourceLocation("minecraft:village/desert/houses/desert_farm_2")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("minecraft:village/desert/zombie/houses"), ImmutableList.of(
new ResourceLocation("minecraft:village/desert/houses/desert_large_farm_1"),
new ResourceLocation("minecraft:village/desert/houses/desert_farm_1"),
new ResourceLocation("minecraft:village/desert/houses/desert_farm_2")
));
// Since we only use Resourcelocations to target where to add our processor, there's no hard dependency with other mod's code.
// A simple check to see if the targeted mod exists is sufficient before actaully attempting to add.
if (ModList.get().isLoaded("repurposed_structures")) {
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/badlands/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/badlands/houses/farm_1"),
new ResourceLocation("repurposed_structures:villages/badlands/houses/farm_2")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/badlands/zombie/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/badlands/houses/farm_1"),
new ResourceLocation("repurposed_structures:villages/badlands/houses/farm_2")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/birch/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/birch/houses/large_farm_1"),
new ResourceLocation("repurposed_structures:villages/birch/houses/small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/birch/zombie/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/birch/houses/large_farm_1"),
new ResourceLocation("repurposed_structures:villages/birch/houses/small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/dark_forest/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/dark_forest/houses/large_farm_1"),
new ResourceLocation("repurposed_structures:villages/dark_forest/houses/small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/dark_forest/zombie/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/dark_forest/houses/large_farm_1"),
new ResourceLocation("repurposed_structures:villages/dark_forest/houses/small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/giant_tree_taiga/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/giant_taiga/houses/farm_1"),
new ResourceLocation("repurposed_structures:villages/giant_taiga/houses/farm_2")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/giant_tree_taiga/zombie/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/giant_taiga/houses/farm_1"),
new ResourceLocation("repurposed_structures:villages/giant_taiga/houses/farm_2")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/jungle/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/jungle/houses/large_farm_1"),
new ResourceLocation("repurposed_structures:villages/jungle/houses/large_farm_2"),
new ResourceLocation("repurposed_structures:villages/jungle/houses/small_farm")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/jungle/zombie/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/jungle/houses/large_farm_1"),
new ResourceLocation("repurposed_structures:villages/jungle/zombie/houses/large_farm_2"),
new ResourceLocation("repurposed_structures:villages/jungle/houses/small_farm")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/mountains/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/mountains/houses/large_farm_1"),
new ResourceLocation("repurposed_structures:villages/mountains/houses/large_farm_2"),
new ResourceLocation("repurposed_structures:villages/mountains/houses/small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/mountains/zombie/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/mountains/houses/large_farm_1"),
new ResourceLocation("repurposed_structures:villages/mountains/zombie/houses/large_farm_2"),
new ResourceLocation("repurposed_structures:villages/mountains/houses/small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/mushroom/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/mushroom/houses/large_farm_1"),
new ResourceLocation("repurposed_structures:villages/mushroom/houses/small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/oak/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/oak/houses/large_farm_1"),
new ResourceLocation("repurposed_structures:villages/oak/houses/small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/oak/zombie/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/oak/houses/large_farm_1"),
new ResourceLocation("repurposed_structures:villages/oak/houses/small_farm_1")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:village/swamp/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/swamp/houses/farm_1"),
new ResourceLocation("repurposed_structures:villages/swamp/houses/farm_2")
));
addCropToPoolElement(templatePoolRegistry, new ResourceLocation("repurposed_structures:villages/swamp/zombie/houses"), ImmutableList.of(
new ResourceLocation("repurposed_structures:villages/swamp/houses/farm_1"),
new ResourceLocation("repurposed_structures:villages/swamp/houses/farm_2")
));
}
}
@TelepathicGrunt
Copy link
Author

Fixed again and thanks! I’m guessing you looked at my StructureTutorialMod for creating a new structure. But yeah, doing a reverse pillager outpost placing is smart for having a nearby building to villages without actually being connected. I’m imagining a village barn and stuff lol. Good luck with your mod!

@BladeAndFeather
Copy link

I just checked, and yep I got inspiration from the StructureTutorialMod, I did not even realize that was also yours.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment