Last active
September 12, 2024 13:05
-
-
Save TelepathicGrunt/4fdbc445ebcbcbeb43ac748f4b18f342 to your computer and use it in GitHub Desktop.
How to add new buildings to villages and other jigsaw structures
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Note: Lithostitch mod allows you to add to template pools easily! No code needed. | |
// https://github.com/Apollounknowndev/lithostitched/wiki/Worldgen-Modifiers#add_template_pool_elements | |
// This gist code is public domain. Take it! Steal it! AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA!!!!!!!!!!!! | |
// 1.18.2 code at top. 1.16.5 version farther down | |
private static final ResourceKey<StructureProcessorList> EMPTY_PROCESSOR_LIST_KEY = ResourceKey.create( | |
Registry.PROCESSOR_LIST_REGISTRY, new ResourceLocation("minecraft", "empty")); | |
public ExampleMod() { | |
MinecraftForge.EVENT_BUS.addListener(this::addNewVillageBuilding); | |
} | |
/** | |
* Adds the building to the targeted pool. | |
* We will call this in addNewVillageBuilding method further down to add to every village. | |
* | |
* Note: This is an additive operation which means multiple mods can do this and they stack with each other safely. | |
*/ | |
private static void addBuildingToPool(Registry<StructureTemplatePool> templatePoolRegistry, | |
Registry<StructureProcessorList> processorListRegistry, | |
ResourceLocation poolRL, | |
String nbtPieceRL, | |
int weight) { | |
// Grabs the processor list we want to use along with our piece. | |
// This is a requirement as using the ProcessorLists.EMPTY field will cause the game to throw errors. | |
// The reason why is the empty processor list in the world's registry is not the same instance as in that field once the world is started up. | |
Holder<StructureProcessorList> emptyProcessorList = processorListRegistry.getHolderOrThrow(EMPTY_PROCESSOR_LIST_KEY); | |
// Grab the pool we want to add to | |
StructureTemplatePool pool = templatePoolRegistry.get(poolRL); | |
if (pool == null) return; | |
// Grabs the nbt piece and creates a SinglePoolElement of it that we can add to a structure's pool. | |
// Use .legacy( for villages/outposts and .single( for everything else | |
SinglePoolElement piece = SinglePoolElement.legacy(nbtPieceRL, emptyProcessorList).apply(StructureTemplatePool.Projection.RIGID); | |
// Use AccessTransformer or Accessor Mixin to make StructureTemplatePool's templates field public for us to see. | |
// Weight is handled by how many times the entry appears in this list. | |
// We do not need to worry about immutability as this field is created using Lists.newArrayList(); which makes a mutable list. | |
for (int i = 0; i < weight; i++) { | |
pool.templates.add(piece); | |
} | |
// Use AccessTransformer or Accessor Mixin to make StructureTemplatePool's rawTemplates field public for us to see. | |
// This list of pairs of pieces and weights is not used by vanilla by default but another mod may need it for efficiency. | |
// So lets add to this list for completeness. We need to make a copy of the array as it can be an immutable list. | |
// NOTE: This is a com.mojang.datafixers.util.Pair. It is NOT a fastUtil pair class. Use the mojang class. | |
List<Pair<StructurePoolElement, Integer>> listOfPieceEntries = new ArrayList<>(pool.rawTemplates); | |
listOfPieceEntries.add(new Pair<>(piece, weight)); | |
pool.rawTemplates = listOfPieceEntries; | |
} | |
/** | |
* We use FMLServerAboutToStartEvent as the dynamic registry exists now and all JSON worldgen files were parsed. | |
* Mod compat is best done here. | |
*/ | |
public void addNewVillageBuilding(final ServerAboutToStartEvent event) { | |
Registry<StructureTemplatePool> templatePoolRegistry = event.getServer().registryAccess().registry(Registry.TEMPLATE_POOL_REGISTRY).orElseThrow(); | |
Registry<StructureProcessorList> processorListRegistry = event.getServer().registryAccess().registry(Registry.PROCESSOR_LIST_REGISTRY).orElseThrow(); | |
// Adds our piece to all village houses pool | |
// Note, the resourcelocation is getting the pool files from the data folder. Not assets folder. | |
addBuildingToPool(templatePoolRegistry, processorListRegistry, | |
new ResourceLocation("minecraft:village/plains/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
addBuildingToPool(templatePoolRegistry, processorListRegistry, | |
new ResourceLocation("minecraft:village/snowy/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
addBuildingToPool(templatePoolRegistry, processorListRegistry, | |
new ResourceLocation("minecraft:village/savanna/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
addBuildingToPool(templatePoolRegistry, processorListRegistry, | |
new ResourceLocation("minecraft:village/taiga/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
addBuildingToPool(templatePoolRegistry, processorListRegistry, | |
new ResourceLocation("minecraft:village/desert/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
} | |
//////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// 1.16.5 code | |
public ExampleMod() { | |
MinecraftForge.EVENT_BUS.addListener(this::addNewVillageBuilding); | |
} | |
/** | |
* Adds the building to the targeted pool. | |
* We will call this in addNewVillageBuilding method further down to add to every village. | |
* | |
* Note: This is an additive operation which means multiple mods can do this and they stack with each other safely. | |
*/ | |
private static void addBuildingToPool(MutableRegistry<JigsawPattern> templatePoolRegistry, ResourceLocation poolRL, String nbtPieceRL, int weight) { | |
// Grab the pool we want to add to | |
JigsawPattern pool = templatePoolRegistry.get(poolRL); | |
if (pool == null) return; | |
// Grabs the nbt piece and creates a SingleJigsawPiece of it that we can add to a structure's pool. | |
SingleJigsawPiece piece = SingleJigsawPiece.single(nbtPieceRL).apply(JigsawPattern.PlacementBehaviour.RIGID); | |
// AccessTransformer to make JigsawPattern's templates field public for us to see. | |
// public net.minecraft.world.gen.feature.jigsaw.JigsawPattern field_214953_e #templates | |
// Weight is handled by how many times the entry appears in this list. | |
// We do not need to worry about immutability as this field is created using Lists.newArrayList(); which makes a mutable list. | |
for (int i = 0; i < weight; i++) { | |
pool.templates.add(piece); | |
} | |
// AccessTransformer to make JigsawPattern's rawTemplates field public for us to see. | |
// net.minecraft.world.gen.feature.jigsaw.JigsawPattern field_214952_d #rawTemplates | |
// This list of pairs of pieces and weights is not used by vanilla by default but another mod may need it for efficiency. | |
// So lets add to this list for completeness. We need to make a copy of the array as it can be an immutable list. | |
List<Pair<JigsawPiece, Integer>> listOfPieceEntries = new ArrayList<>(pool.rawTemplates); | |
listOfPieceEntries.add(new Pair<>(piece, weight)); | |
pool.rawTemplates = listOfPieceEntries; | |
} | |
/** | |
* We use FMLServerAboutToStartEvent as the dynamic registry exists now and all JSON worldgen files were parsed. | |
* Mod compat is best done here. | |
*/ | |
public void addNewVillageBuilding(final FMLServerAboutToStartEvent event) { | |
MutableRegistry<JigsawPattern> templatePoolRegistry = event.getServer().registryAccess().registry(Registry.TEMPLATE_POOL_REGISTRY).get(); | |
// Adds our piece to all village houses pool | |
// Note, the resourcelocation is getting the pool files from the data folder. Not assets folder. | |
addBuildingToPool(templatePoolRegistry, new ResourceLocation("minecraft:village/plains/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
addBuildingToPool(templatePoolRegistry, new ResourceLocation("minecraft:village/snowy/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
addBuildingToPool(templatePoolRegistry, new ResourceLocation("minecraft:village/savanna/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
addBuildingToPool(templatePoolRegistry, new ResourceLocation("minecraft:village/taiga/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
addBuildingToPool(templatePoolRegistry, new ResourceLocation("minecraft:village/desert/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Note: Lithostitch mod allows you to add to template pools easily! No code needed. | |
// https://github.com/Apollounknowndev/lithostitched/wiki/Worldgen-Modifiers#add_template_pool_elements | |
@Override | |
public void onInitialize() { | |
ServerLifecycleEvents.SERVER_STARTING.register(this::addNewVillageBuilding); | |
} | |
private static final RegistryKey<StructureProcessorList> EMPTY_PROCESSOR_LIST_KEY = RegistryKey.of( | |
RegistryKeys.PROCESSOR_LIST, new Identifier("minecraft", "empty")); | |
/** | |
* Adds the building to the targeted pool. | |
* We will call this in addNewVillageBuilding method further down to add to every village. | |
* | |
* Note: This is an additive operation which means multiple mods can do this and they stack with each other safely. | |
*/ | |
private static void addBuildingToPool(Registry<StructurePool> templatePoolRegistry, | |
Registry<StructureProcessorList> processorListRegistry, | |
Identifier poolRL, | |
String nbtPieceRL, | |
int weight) { | |
// Grabs the processor list we want to use along with our piece. | |
// This is a requirement as using the ProcessorLists.EMPTY field will cause the game to throw errors. | |
// The reason why is the empty processor list in the world's registry is not the same instance as in that field once the world is started up. | |
RegistryEntry<StructureProcessorList> emptyProcessorList = processorListRegistry.getEntry(EMPTY_PROCESSOR_LIST_KEY).get(); | |
// Grab the pool we want to add to | |
StructurePool pool = templatePoolRegistry.get(poolRL); | |
if (pool == null) return; | |
// Grabs the nbt piece and creates a SinglePoolElement of it that we can add to a structure's pool. | |
// Use .ofProcessedLegacySingle( for villages/outposts and .ofProcessedSingle( for everything else | |
SinglePoolElement piece = SinglePoolElement.ofProcessedLegacySingle(nbtPieceRL, emptyProcessorList).apply(StructurePool.Projection.RIGID); | |
// Use AccessWideners or Accessor Mixin to make StructurePool's templates field public for us to see. | |
// Weight is handled by how many times the entry appears in this list. | |
// We do not need to worry about immutability as this field is created using Lists.newArrayList(); which makes a mutable list. | |
for (int i = 0; i < weight; i++) { | |
((StructurePoolAccessor)pool).getElements().add(piece); | |
} | |
// Use AccessWideners or Accessor Mixin to make StructurePool's elementCounts field public for us to see. | |
// This list of pairs of pieces and weights is not used by vanilla by default but another mod may need it for efficiency. | |
// So lets add to this list for completeness. We need to make a copy of the array as it can be an immutable list. | |
// NOTE: This is a com.mojang.datafixers.util.Pair. It is NOT a fastUtil pair class. Use the mojang class. | |
List<Pair<StructurePoolElement, Integer>> listOfPieceEntries = new ArrayList<>(((StructurePoolAccessor)pool).getElementCounts()); | |
listOfPieceEntries.add(new Pair<>(piece, weight)); | |
((StructurePoolAccessor)pool).setElementCounts(listOfPieceEntries); | |
} | |
/** | |
* We use ServerLifecycleEvents.SERVER_STARTING as the dynamic registry exists now and all JSON worldgen files were parsed. | |
* Mod compat is best done here. | |
*/ | |
public void addNewVillageBuilding(final MinecraftServer event) { | |
Registry<StructurePool> templatePoolRegistry = event.getRegistryManager().get(RegistryKeys.TEMPLATE_POOL); | |
Registry<StructureProcessorList> processorListRegistry = event.getRegistryManager().get(RegistryKeys.PROCESSOR_LIST); | |
// Adds our piece to all village houses pool | |
// Note, the resourcelocation is getting the pool files from the data folder. Not assets folder. | |
addBuildingToPool(templatePoolRegistry, processorListRegistry, | |
new Identifier("minecraft:village/plains/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
addBuildingToPool(templatePoolRegistry, processorListRegistry, | |
new Identifier("minecraft:village/snowy/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
addBuildingToPool(templatePoolRegistry, processorListRegistry, | |
new Identifier("minecraft:village/savanna/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
addBuildingToPool(templatePoolRegistry, processorListRegistry, | |
new Identifier("minecraft:village/taiga/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
addBuildingToPool(templatePoolRegistry, processorListRegistry, | |
new Identifier("minecraft:village/desert/houses"), | |
"modid:structure_nbt_resourcelocation", 5); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For any confused chaps like me in 1.19 having problems related to private variables:
Happy modding!