Skip to content

Configured Features

A ConfiguredFeature is a feature paired with a configuration. The feature is the algorithm (an ore vein, a tree, a geode) and the configuration would be its parameters such as which ore, how big, etc.

Configured features are not placed in the world on their own. A configured feature describes what to generate and a placed feature formally wraps it with the placement rules that decide where and/or how often it should appear.

To use a configured feature in a biome, wrap it in a placed feature.

The ConfiguredFeatures catalog holds typed references to every configured feature vanilla registrar.

ExamplePlugin.java Java
import dev.wyck.worldgen.feature.ConfiguredFeature;
import dev.wyck.worldgen.feature.ConfiguredFeatures;

// a reference straight from the catalog
ConfiguredFeature largeDiamonds = ConfiguredFeatures.ORE_DIAMOND_LARGE;

// or by key
ConfiguredFeature byKey = ConfiguredFeature.reference(ResourceKey.minecraft("ore_diamond_large"));

Wyck includes wrappers for all of Minecraft’s built-in configured features, so you can easily modify their configuration without having to create your own custom feature.

All wrappers extend the FeatureConfiguration interface.

ExamplePlugin.java Java
import dev.wyck.keys.ResourceKey;
import dev.wyck.biome.Biome;
import dev.wyck.biome.BiomeGenerationSettings;
import dev.wyck.worldgen.blockpredicates.BlockPredicate;
import dev.wyck.worldgen.Decoration;
import dev.wyck.worldgen.HeightmapType;
import dev.wyck.worldgen.feature.ConfiguredFeature;
import dev.wyck.worldgen.feature.FeatureType;
import dev.wyck.worldgen.feature.configurations.FeatureConfiguration;
import dev.wyck.worldgen.feature.configurations.TreeConfiguration;
import dev.wyck.worldgen.feature.featuresize.TwoLayersFeatureSize;
import dev.wyck.worldgen.feature.foliageplacers.PineFoliagePlacer;
import dev.wyck.worldgen.feature.trunkplacers.StraightTrunkPlacer;
import dev.wyck.worldgen.placement.PlacedFeature;
import dev.wyck.worldgen.placement.PlacementModifier;
import dev.wyck.worldgen.stateproviders.BlockStateProvider;
import dev.wyck.worldgen.valueproviders.IntProvider;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.plugin.java.JavaPlugin;

public class ExamplePlugin extends JavaPlugin {
  @Override
  public void onEnable() {
      // Making a tall spruce tree
      TreeConfiguration treeConfig = FeatureConfiguration.tree()
          .foliageProvider(BlockStateProvider.simple(Material.SPRUCE_LEAVES))
          .trunkProvider(BlockStateProvider.simple(Material.SPRUCE_LOG))
          .belowTrunkProvider(
              BlockStateProvider.ruleBased()
                  .rule(
                      BlockPredicate.not(BlockPredicate.matchesTag(Tag.CANNOT_REPLACE_BELOW_TREE_TRUNK)),
                      BlockStateProvider.simple(Material.DIRT)
                  )
                  .build()
          )
          .foliagePlacer(PineFoliagePlacer.builder()
              .height(IntProvider.constant(4))
              .offset(IntProvider.constant(1))
              .radius(IntProvider.uniform(4, 6))
              .build()
          )
          .minimumSize(TwoLayersFeatureSize.builder()
              .limit(1)
              .lowerSize(0)
              .minClippedHeight(0)
              .upperSize(0)
              .build()
          )
          .trunkPlacer(StraightTrunkPlacer.builder()
              .baseHeight(10)
              .heightRandA(4)
              .heightRandB(8)
              .build()
          )
          .ignoreVines()
          .build();

      // Using it in a biome
      Biome.builder()
          .resourceKey(ResourceKey.of("test:biome"))
          .generationSettings(
              BiomeGenerationSettings.builder()
                  .addFeature(Decoration.VEGETAL_DECORATION, PlacedFeature.builder()
                      .feature(ConfiguredFeature.of(FeatureType.TREE, treeConfig))
                      .modifier(PlacementModifier.rarityFilter(1))
                      .modifier(PlacementModifier.inSquare())
                      .modifier(PlacementModifier.surfaceWaterDepthFilter(0))
                      .modifier(PlacementModifier.heightmap(HeightmapType.OCEAN_FLOOR))
                      .modifier(PlacementModifier.biomeFilter())
                      .build())
                  .build()
          )
          .register();
  }
}

To author your own configured features, you can extend the CustomFeature class. And pair it with your own configuration context.

Java
import dev.wyck.worldgen.feature.custom.CustomFeature;
import dev.wyck.worldgen.feature.custom.PlacementContext;
import org.bukkit.Material;
import org.bukkit.util.BlockVector;
import org.jspecify.annotations.NonNull;

import java.util.Random;

public class PillarFeature extends CustomFeature<@NotNull PillarFeature.PillarConfig> {

  public PillarFeature() {
      super(PillarConfig::defaults);
  }

  @Override
  public boolean place(PlacementContext<@NotNull PillarConfig> context) {
      PillarConfig config = context.config();
      Random random = context.random();
      BlockVector origin = context.origin();

      int height = config.minHeight() + random.nextInt(config.maxHeight() - config.minHeight() + 1);

      for (int y = 0; y < height; y++) {
          BlockVector pos = new BlockVector(origin.getBlockX(), origin.getBlockY() + y, origin.getBlockZ());
          context.setBlock(pos, config.pillarBlock().createBlockData());
      }

      BlockVector cap = new BlockVector(origin.getBlockX(), origin.getBlockY() + height, origin.getBlockZ());
      context.setBlock(cap, config.capBlock().createBlockData());

      return true;
  }

  public record PillarConfig(Material pillarBlock, Material capBlock, int minHeight, int maxHeight) {
      public static PillarConfig defaults() {
          return new PillarConfig(Material.OBSIDIAN, Material.GLOWSTONE, 4, 9);
      }
  }
}