World generation
Data pack registries
Registry data that you put in data/ in your mod jar works in the vanilla 26.2 format.
- Supported registries:
worldgen/configured_feature,worldgen/placed_feature,dimension,dimension_type,damage_type,enchantment, and others - Mod packs are marked as known packs (
KnownPack,poly:<mod id>), so these entries do not make a world "experimental". - A client that has the same mod reads the data from its own jar instead of receiving it from the server.
- Minecraft 26.2 treats every non-vanilla dimension as experimental. Poly Mod Loader treats only dimensions in the namespace of a loaded mod as stable (
poly:mod_dimension_stability). - You can also define structures with data packs. To define them in code, see Code structures.
Add features to existing biomes
mod.worldgen().addFeature(BiomeSelector.tag(ResourceId.minecraft("is_overworld")),
GenerationStepName.UNDERGROUND_ORES, ResourceId.of("my_mod", "glow_ore")); // placed feature in data/
- Select biomes with
BiomeSelector.ids(...)(a list of IDs) orBiomeSelector.tag(...)(a biome tag). - Generation steps (
GenerationStepName):RAW_GENERATION,LAKES,LOCAL_MODIFICATIONS,UNDERGROUND_STRUCTURES,SURFACE_STRUCTURES,STRONGHOLDS,UNDERGROUND_ORES,UNDERGROUND_DECORATION,FLUID_SPRINGS,VEGETAL_DECORATION,TOP_LAYER_MODIFICATION - This applies every time the server creates a world, and affects chunks generated afterward.
- If the placed feature does not exist, startup stops with
POLY-WORLDGEN-001.
Code biomes
mod.worldgen().biome("glow_meadow", BiomeDefinition.builder()
.climate(0.7f, 0.6f, true) // temperature, downfall, has precipitation
.skyColor(0x9ad0ff).grassColor(0x7fd16b)
.feature(GenerationStepName.VEGETAL_DECORATION, ResourceId.minecraft("flower_plains"))
.spawn(MobCategory.CREATURE, ResourceId.minecraft("sheep"), 8, 2, 4)
.build());
- Colors:
skyColor,fogColor,waterColor,grassColor,foliageColor - Poly Mod Loader writes the definition to the generated pack (
.poly/generated-pack) in the game's biome file format. - Like data pack biomes, you can use it in dimensions, biome tags,
/locate biome, and/fillbiome. - The generated pack is a known pack, and its version comes from the contents of the generated files. So a client that has the same mod does not receive biomes from the server.
Code terrain generators
mod.worldgen().chunkGenerator("terraces", (x, z, column) -> { // fills one column at a time
column.fill(column.minY(), 63, ResourceId.minecraft("stone"));
column.set(64, ResourceId.minecraft("grass_block"));
});
TerrainGenerator.fillColumn(x, z, column) fills the block columns of a new chunk one at a time. ColumnBuilder has minY(), maxY(), seed(), set(y, block), and fill(fromY, toY, block), and you can override seaLevel().
- It is registered as generator type
<mod>:<path>, and you use it in a data pack dimension together withbiome_source. - The game adds biome features (ores, trees), structures, and initial mob spawns exactly as it does for other generators.
Several columns are called at the same time on world generation threads. A generator must be thread-safe and must produce the same result for the same input.
Code placed features
You write placed features (boulders, plant clusters, ore veins with special shapes) in code. Poly Mod Loader registers them as game placed features with the format <mod id>:<path>, and writes a configured feature and a placed feature with the same ID to the generated pack.
ResourceId boulder = mod.worldgen().feature("boulder", FeaturePlacement.onSurface(1).rarity(4), context -> {
BlockPos origin = context.origin();
int radius = 1 + context.random().nextInt(2);
for (int dx = -radius; dx <= radius; dx++)
for (int dy = -radius; dy <= radius; dy++)
for (int dz = -radius; dz <= radius; dz++)
if (dx * dx + dy * dy + dz * dz <= radius * radius + 1)
context.setBlock(origin.offset(dx, dy, dz), ResourceId.minecraft("mossy_cobblestone"));
return true;
});
mod.worldgen().addFeature(BiomeSelector.ids(ResourceId.minecraft("plains")), GenerationStepName.VEGETAL_DECORATION, boulder);
FeaturePlacement | Meaning |
|---|---|
onSurface(count) | count times per chunk, on the surface |
atHeight(count, minY, maxY) | count times per chunk, at a random height between minY and maxY |
.rarity(n) | Only in 1 of every n chunks |
- Use
FeatureContextto read and write blocks:origin(),random(),blockAt,isAir,setBlock,surfaceY(x, z),minY,maxY. - It is called for several chunks at the same time on world generation threads. Keep state in local variables only, and take random values only from
random(). Write blocks within 16 blocks horizontally of the origin. - You can place it directly with
/place feature <mod id>:<path>. - If your code throws an exception, Poly Mod Loader logs
POLY-WORLDGEN-005once and skips that location.
Add code stages to noise terrain
You keep the game's noise terrain generator and change only the shape of the terrain in code. The game still handles caves, aquifers, ore veins, surface rules, and biomes. Your code function is registered as a game density function with the format <mod id>:<path>, and you use it in the noise settings of a data pack.
// Solid ground below 64 + 6·sin(x / 6): positive is block, negative is air
mod.worldgen().densityFunction("waves", -1, 1,
(x, y, z) -> Math.max(-1, Math.min(1, (64 + 6 * Math.sin(x / 6.0) - y) / 4.0)));
"noise_router": {
"final_density": { "type": "my_mod:waves" },
"barrier": 0.0, "continents": 0.0, "depth": 0.0, "erosion": 0.0, "...": 0.0
}
To add to the existing terrain, wrap it like {"type": "minecraft:add", "argument1": <original value>, "argument2": {"type": "my_mod:waves"}}. In the dimension file, use "generator": {"type": "minecraft:noise", "settings": "my_mod:waves", ...}.
minValueandmaxValueare the range of values the function returns. The game uses this range to skip computation, so give it exactly. If the range is reversed, you getPOLY-WORLDGEN-004.- The function is called very often and on several threads at the same time. It must be fast, stateless, and always return the same value for the same position.
- The game computes terrain in cells. A cell is
size_horizontal × 4blocks wide andsize_vertical × 4blocks tall, and values are interpolated inside a cell. So shapes smaller than a cell are smoothed out, and the surface can be off by one block.
Code structures
You place one box on the surface at the center of a selected chunk and fill it with code. Poly Mod Loader writes the structure and structure set to the generated pack. So you can find and place it with /locate structure and /place structure.
mod.worldgen().structure("shrine", StructureDefinition.builder(7, 4, 7, shrine -> {
shrine.fill(0, 0, 0, 6, 0, 6, ResourceId.minecraft("stone_bricks")); // floor
for (int x : new int[] {0, 6})
for (int z : new int[] {0, 6})
shrine.fill(x, 1, z, x, 3, z, ResourceId.minecraft("stone_bricks")); // pillars
shrine.setBlock(3, 1, 3, GLOW_BLOCK);
})
.biomes(BiomeSelector.tag(ResourceId.minecraft("is_overworld")))
.spacing(40, 12) // 40 chunks apart on average, at least 12 chunks apart
.build());
| Builder | Meaning |
|---|---|
builder(sizeX, sizeY, sizeZ, builder) | Box size (up to 128 horizontally, up to 256 in height) and the code that fills it |
yOffset(n) | Raises the floor height above the surface (or buries it, if negative) |
biomes(selector) | Biomes where it generates, default #minecraft:is_overworld |
spacing(spacing, separation) | Average and minimum distance between structures (in chunks), default 32 and 8 |
step(step) | Generation step, default SURFACE_STRUCTURES |
adaptTerrain() | Smooths the surrounding terrain, like a village |
- Coordinates count from the lowest corner of the box. Anything outside the box is ignored.
- A structure larger than one chunk is built chunk by chunk. Your code is called once for each chunk the box overlaps, with the same
random()sequence, and only blocks inside that chunk are written. It must build the same structure every time. This is the same approach as the game's structure pieces. blockAtoutside that chunk reads as air.
Mod dimensions
{
"type": "minecraft:overworld",
"generator": {
"type": "my_mod:terraces",
"biome_source": {"type": "minecraft:fixed", "biome": "my_mod:glow_meadow"}
}
}
world.otherDimension(ResourceId.of("my_mod", "terraces")); // WorldView of another dimension
entity.changeDimension(ResourceId.minecraft("the_nether"), x, y, z); // move to a dimension
The game test server creates dimensions only from the flat preset, so it has no data dimensions. Check mod dimensions in a regular world (single player or server).
Error codes
| Code | Meaning |
|---|---|
POLY-WORLDGEN-001 | Unknown placed feature |
POLY-WORLDGEN-002 | Declared after registration closed |
POLY-WORLDGEN-003 | Cannot access the biome feature or spawn list |
POLY-WORLDGEN-004 | Invalid or duplicate generator, biome, placed feature, density function, or structure ID, or a reversed density function range |
POLY-WORLDGEN-005 | A code placed feature, density function, or structure threw an exception (logged once, that location is skipped) |