Generate Minecraft worlds by map images!

Block Layers define what Block Placer should be used for which block position (e.g. -100, 0, 234). It’s recommended to have layers with the following names: deepslate, stone, dirt, surface, sea.. Those layer names are also used in the implemented Zones. By default, CTGen ships for different layer types, explained below.

Height Layer

This layer type depends on the absolute y value of the block, e.g. -64 to 256.

{
  "type": "ctgen:height_layer",
  "min": -65,
  "max": 0,
  "limit_to_surface": true,
  "shift": true,
  "name": "deepslate",
  "has_caves": true,
  "fallback": "minecraft:deepslate"
}
  • min - min y where the layer is applied
  • max - max y where the layer is applied
  • limit_to_surface - whether the layer should stop on the surface level, if the surface is below “max”
  • shift - whether the layer transition should be “shifted” for each block. adds a bit more beautiful noise to it
  • name - the name of the layer so Zones can modify it
  • has_caves - whether carvers should be applied within this layer
  • fallback - a fallback block player in case the current zone doesn’t have a custom one

The exact same layer will look like this if coded in Java:

BlockLayer layer = new HeightLayer(-65, 0, true, true, "deepslate", true, new BasicPlacer(Blocks.DEEPSLATE));

Weight Layer

This layer type depends on the relative y - y in percent between minY and surface height. If the minimum y is -64 and the surface is at 70, then y = -64 will be 0, y = 70 will be 1.

{
  "type": "ctgen:weight_layer",
  "min_percentage": 0,
  "max_percentage": 0.96,
  "shift": true,
  "name": "stone",
  "has_caves": true,
  "fallback": "minecraft:stone"
}
  • min_percentage - min y in percent between the minimum y and the surface
  • max_percentage - max y in percent between the minimum y and the surface
  • shift - whether the layer transition should be “shifted” for each block. adds a bit more beautiful noise to it
  • name - the name of the layer so Zones can modify it
  • has_caves - whether carvers should be applied within this layer
  • fallback - a fallback block player in case the current zone doesn’t have a custom one

The exact same layer will look like this if coded in Java:

BlockLayer layer = new WeightLayer(0D, 0.96D, true, "stone", true, new BasicPlacer(Blocks.STONE));

Sea Layer

This layer type fills every block between the surface and the sea level, if the surface is below the sea level.

{
  "type": "ctgen:sea_layer",
  "name": "sea",
  "fallback": "minecraft:water"
}
  • name - the name of the layer so Zones can modify it
  • fallback - a fallback block player in case the current zone doesn’t have a custom one

The exact same layer will look like this if coded in Java:

BlockLayer layer = new SeaLayer("sea", new BasicPlacer(Blocks.WATER));

Surface Layer

This layer type only applies on the surface block.

{
  "type": "ctgen:surface_layer",
  "name": "surface",
  "fallback": "minecraft:grass_block"
}
  • name - the name of the layer so Zones can modify it
  • fallback - a fallback block player in case the current zone doesn’t have a custom one

The exact same layer will look like this if coded in Java:

BlockLayer layer = new SurfaceLayer("surface", new BasicPlacer(Blocks.GRASS_BLOCK));

Registering Block Layer Types

You can also register a custom block layer using code similar to this one:

public class HeightLayer extends BlockLayer {
    private final int min;
    private final int max;
    private final boolean limitedToSurface;
    private final boolean hasShift;

    public HeightLayer(int minY, int maxY, boolean limitedToSurface, boolean hasShift, String name, boolean hasCaves, BlockPlacer fallback) {
        super(name, hasCaves, fallback);
        this.min = minY;
        this.max = maxY;
        this.hasShift = hasShift;
        this.limitedToSurface = limitedToSurface;
    }

    @Override
    public boolean is(SimplexNoise noise, int x, int y, int z, Zone zone, int minY, int seaLevel, double surfaceHeight, int genHeight, int shift) {
        int y2 = hasShift ? y + shift : y;
        return y2 >= min - 1 && y2 <= max && (!limitedToSurface || y < surfaceHeight);
    }

    public static final Codec<HeightLayer> CODEC = RecordCodecBuilder.create(instance -> instance.group(
            Codec.INT.fieldOf("min").forGetter(o -> o.min),
            Codec.INT.fieldOf("max").forGetter(o -> o.max),
            Codec.BOOL.optionalFieldOf("limit_to_surface", true).forGetter(o -> o.limitedToSurface),
            Codec.BOOL.optionalFieldOf("shift", true).forGetter(o -> o.hasShift),
            Codec.STRING.fieldOf("name").forGetter(BlockLayer::getName),
            Codec.BOOL.optionalFieldOf("has_caves", true).forGetter(BlockLayer::hasCaves),
            BlockPlacer.CODEC.fieldOf("fallback").forGetter(BlockLayer::getFallback)
    ).apply(instance, instance.stable(HeightLayer::new)));

    public static final ResourceLocation ID = new ResourceLocation(YourMod.MODID, "height_layer");

    @Override
    protected Codec<HeightLayer> codec() {
        return CODEC;
    }
}

To register the layer, just use this snippet:

CTRegistries.BLOCK_LAYER.register(HeightLayer.ID, HeightLayer.CODEC);