Placed Features
A placed feature determines where and how a configured feature will be placed. Placed features work via placement modifiers that can be applied to a configured feature.
You can create placed feature files in the data/worldgen/placed_feature
directory or register them in the FeatureRegistry
in code.
Structure
feature
-
The configured feature's id
placement
-
A list of placement modifiers. See Placement Modifiers for more information.
Here's an example of Minecraft's large diamond ore placed feature:
val ORE_DIAMOND_LARGE_PLACEMENT = FeatureRegistry.registerPlacedFeature(
Machines,
"ore_diamond_large",
ORE_DIAMOND_LARGE_CONFIG, // (1)!
listOf(
RarityFilter.onAverageOnceEvery(9), // (2)!
InSquarePlacement.spread(), // (3)!
HeightRangePlacement.triangle(VerticalAnchor.aboveBottom(-80), VerticalAnchor.aboveBottom(80)), // (4)!
BiomeFilter.biome() // (5)!
)
)
- The configured feature to place
- Only give the feature a chance of \({{}^{1}\!/_{9}}\) to generate
- Adds a random integer in the range \([0;15]\) to the x- and z-coordinates of the initial position
- Sets the y-coordinate to a value provided by the trapezoid height provider.
triangle
is a shortcut for the trapezoid height provider with a plateau of width 0. This provider provides a y-coordinate in the range \([-80;80]\) below/above the bedrock layer via an isosceles trapezoidal distribution. Since blocks can't be placed under the bedrock layer, this again halves the chance of the feature generating. - Only generates the feature in biomes that contain this feature (the
in_square
placement modifiers might have generated a position in a different biome).
{
"feature": "minecraft:ore_diamond_large",
"placement": [
{
"type": "minecraft:rarity_filter", // (1)!
"chance": 9
},
{
"type": "minecraft:in_square" // (2)!
},
{
"type": "minecraft:height_range", // (3)!
"height": {
"type": "minecraft:trapezoid", // (4)!
"max_inclusive": {
"above_bottom": 80
},
"min_inclusive": {
"above_bottom": -80
}
}
},
{
"type": "minecraft:biome" // (5)!
}
]
}
- Only give the feature a chance of \({{}^{1}\!/_{9}}\) to generate
- Adds a random integer in the range \([0;15]\) to the x- and z-coordinates of the initial position
- Sets the y-coordinate to a value provided by the trapezoid height provider
- Provides a y-coordinate in the range \([-80;80]\) below/above the bedrock layer via an isosceles trapezoidal distribution. Since blocks can't be placed under the bedrock layer, this again halves the chance of the feature generating.
- Only generates the feature in biomes that contain this feature (the
in_square
placement modifiers might have generated a position in a different biome).
Placement Modifiers
A Placement modifier takes an initial position and returns empty, one or more block positions. These modifiers are chained,
and pretty much act like a lot of flatMap
calls. In fact, that's exactly what Minecraft does internally:
private boolean placeWithContext(PlacementContext ctx, RandomSource random, BlockPos pos) {
Stream<BlockPos> stream = Stream.of(pos);
for (PlacementModifier placementmodifier : this.placement) {
stream = stream.flatMap((blockPos) -> {
return placementmodifier.getPositions(ctx, random, blockPos);
});
}
// ...
}
So you can also think of these positions as attempts to place the configured feature. A list of vanilla placement modifiers can be found below.
minecraft:biome
Returns the position if the configured feature is registered in the biome's feature
list at the given position. Empty
otherwise.
minecraft:block_predicate_filter
Returns the position if the block predicate matches the block at the given position. Empty otherwise.
Name | Description |
---|---|
predicate |
The block predicate |
minecraft:carving_mask
Returns all positions in the given position's chunk that were carved out by a carver.
minecraft:count
Returns the given position count
times.
Name | Description |
---|---|
count |
Either an int value in the range \([0;256]\) or an int provider. The provided value is the number of times the position is returned |
minecraft:count_on_every_layer
Deprecated. For more information, check out the Minecraft Wiki
minecraft:environment_scan
Scans for blocks matching the given block predicate up/down until it finds a matching block or the max number of steps is reached. If no matching block is found, empty is returned.
Name | Description |
---|---|
direction_of_search |
The direction of the scan. Can be up or down |
max_steps |
Max number of steps to scan. An int value in the range \([1;32]\) |
target_condition |
The block predicate to match |
allowed_search_condition (optional) |
A block predicate that each scanned block must match to allow further scanning. If not provided, no condition is applied |
minecraft:height_range
Takes the input position and sets the y coordinate to a value provided by the given height provider.
Name | Description |
---|---|
height |
The height provider providing the y-coordinate |
minecraft:heightmap
Takes the input position and sets the y coordinate to one block above the heightmap at the given position. Check out the heightmap gist page for image examples.
minecraft:in_square
Adds a random integer in the range \([0;15]\) to the x- and z-coordinates of the given position.
minecraft:noise_based_count
Gets the noise value at the given position and, if the value is positive, returns the given position multiple times. The amount of times the position is returned is determined by the following code:
double noise = Biome.BIOME_INFO_NOISE.getValue((double)pos.getX() / noiseFactor, (double)pos.getZ() / noiseFactor, false);
int n = (int)Math.ceil((noise + noiseOffset) * noiseToCountRatio);
Name | Description |
---|---|
noise_factor |
A double value that scales the noise horizontally. The higher the value, the wider the peaks. |
noise_offset (optional) |
A double value that offsets the noise vertically. |
noise_to_count_ratio |
An int value that defines the ratio of noise to count. |
minecraft:noise_threshold_count
Returns the given position multiple times. If the noise value at the given position is below the given threshold, the
position is returned below_noise
times. Otherwise, it is returned above_noise
times. Or, in code:
Name | Description |
---|---|
noise_level |
A double value of the threshold that determines whether the position is returned below_noise or above_noise times. |
above_noise |
An int value that determines how often the position is returned if the noise value is above/equal to the threshold. |
below_noise |
An int value that determines how often the position is returned if the noise value is below the threshold. |
minecraft:random_offset
Offsets the given position by the provided int provider's values.
Name | Description |
---|---|
xz_spread |
Either a fixed int value in the range \([-16;16]\) or an int provider. x and z are sampled separately! |
y_spread |
Either a fixed int value in the range \([-16;16]\) or an int provider. |
minecraft:rarity_filter
Either returns the given position or empty. The chance of returning the position is determined by the given chance and
calculated via 1 / chance
.
minecraft:surface_relative_threshold_filter
Returns the given position if the surface height at the given position is inside the specified range. Otherwise, returns empty. Check out the heightmap gist page for image examples.
Name | Description |
---|---|
heightmap |
The heightmap to use. Can be WORLD_SURFACE_WG , WORLD_SURFACE , OCEAN_FLOOR_WG , OCEAN_FLOOR , MOTION_BLOCKING or MOTION_BLOCKING_NO_LEAVES . |
max_inclusive |
A double value that defines the maximum surface level. Defaults to \(2^{31} - 1\). |
min_inclusive |
A double value that defines the minimum surface level. Defaults to \(-2^{31}\). |
minecraft:surface_water_depth_filter
If the amount of motion-blocking blocks under the surface is less than/equal to max_water_depth
, returns the given position. Otherwise, returns empty.
Custom PlacementModifiers
You can also implement your own custom PlacementModifiers
by extending Minecraft's PlacementModifier
class. You can
then register your custom PlacementModifier
via the FeatureRegistry
either by creating a PlacementModifierType
or
by providing the Codec
directly and thus creating an inline PlacementModifierType
. Check out the Codecs
page for more information on Mojang's serialization system.
Here's how you'd implement the minecraft:count
PlacementModifier
as an example:
val COUNT_PLACEMENT = FeatureRegistry.registerPlacementModifierType(Machines, "count", CountPlacement.CODEC)
class CountPlacement(val count: IntProvider) : PlacementModifier() {
override fun getPositions(ctx: PlacementContext, random: RandomSource, pos: BlockPos): Stream<BlockPos> =
Stream.generate { pos }.limit(count.sample(random).toLong())
override fun type(): PlacementModifierType<*> = PlacementModifiers.COUNT_PLACEMENT
companion object {
@JvmField // (1)!
val CODEC: Codec<CountPlacement> = IntProvider
.codec(0, 256)
.fieldOf("count")
.xmap(::CountPlacement, CountPlacement::count)
.codec()
@JvmStatic
fun of(count: Int) = CountPlacement(ConstantInt.of(count))
@JvmStatic
fun of(count: IntProvider) = CountPlacement(count)
}
}
- This allows
CODEC
to be accessed as a field from Java code instead of having to callgetCODEC()
object CountPlacementType : PlacementModifierType<CountPlacement> {
private val CODEC: Codec<CountPlacement> = IntProvider
.codec(0, 256)
.fieldOf("count")
.xmap(::CountPlacement, CountPlacement::count)
.codec()
override fun codec() = CODEC
}
val COUNT_PLACEMENT = FeatureRegistry.registerPlacementModifierType(Machines, "count", CountPlacementType)
class CountPlacement(val count: IntProvider) : PlacementModifier() {
override fun getPositions(ctx: PlacementContext, random: RandomSource, pos: BlockPos): Stream<BlockPos> =
Stream.generate { pos }.limit(count.sample(random).toLong())
override fun type(): PlacementModifierType<*> = CountPlacementType
companion object {
@JvmStatic
fun of(count: Int) = CountPlacement(ConstantInt.of(count))
@JvmStatic
fun of(count: IntProvider) = CountPlacement(count)
}
}
Minecraft also offers further abstraction via the RepeatingPlacement
and PlacementFilter
classes. They both override
the getPositions
method and provide the count
and shouldPlace
methods respectively.