Skip to content

proposal: Custom world generation API #22

Description

@PyXiion

Summary

Add a core lib to define custom world generation from Lua — procedural chunk
generation callbacks plus declarative presets (flat layers, noise settings,
biome sources). Registered generators are consumed by the dimensions API.

local worldgen = require "core:worldgen"

worldgen.register("mypack:custom", {
  generator = "lua",
  biome = "minecraft:plains",
  generate = function(ctx)
    ctx:fill(ctx.x0, ctx.minY, ctx.z0, ctx.x1, 63, ctx.z1, "minecraft:stone")
  end,
})

local dimension = require "core:dimension"
local arena = dimension.register("mypack:arena", { type = "mypack:custom" })

Related: dimension proposal, #7, #5.

API

Core lib require "core:worldgen".

Registration

worldgen.register(id, config)

Same idempotent, never-auto-deleted semantics as the dimension registry.
First registration wins; re-registering the same id with different config
logs a warning and keeps the original. worldgen.get(id) and
worldgen.list() provide introspection.

Generator modes

Mode Description
"lua" Full control via a generate(ctx) callback
"flat" Superflat with declarative layers
"noise" Noise-based terrain with declarative settings

Default is "noise". Only one mode per registration.

Lua generator (generator = "lua")

worldgen.register("mypack:custom", {
  generator = "lua",
  biome = "minecraft:plains",
  generate = function(ctx)
    -- ctx:  chunk generation context — writes only (no reads guaranteed)
    --   ctx.x, ctx.z          — chunk coordinates
    --   ctx.x0 .. ctx.z1      — world-coordinate bounds of the chunk
    --   ctx.minY, ctx.maxY    — vertical bounds

    -- Block placement (world coordinates):
    ctx:setBlock(x, y, z, "minecraft:diamond_block")
    ctx:fill(x1, y1, z1, x2, y2, z2, "minecraft:stone")

    -- Place a vanilla placed feature at a location:
    ctx:placeFeature("minecraft:oak", x, y, z)
  end,
})

The callback runs once per chunk during worldgen, synchronously on the
server thread. Additional context helpers (getBiome, setBiome, getNoise,
decorate) can be added iteratively.

Flat generator (generator = "flat")

worldgen.register("mypack:flat", {
  generator = "flat",
  biome = "minecraft:plains",
  layers = {
    { block = "minecraft:bedrock",    height = 1 },
    { block = "minecraft:stone",      height = 59 },
    { block = "minecraft:grass_block", height = 1 },
  },
})

block resolves with the usual minecraft: default if un-namespaced.

Noise generator (generator = "noise")

worldgen.register("mypack:noise", {
  generator = "noise",
  biome = { plains = 1, desert = 0.5 },     -- weighted multi-biome
  -- or biome = "minecraft:plains"           -- fixed single biome

  noise = {
    sea_level     = 63,
    default_block = "minecraft:stone",
    default_fluid = "minecraft:water",
    min_y = -64,  height = 384,
    aquifers = true,  ore_veins = true,
  },

  density    = "minecraft:overworld",       -- noise router preset
  surface    = "minecraft:overworld",       -- surface rule preset
  features   = { "minecraft:village_plains" },
  structures = { "minecraft:mineshaft", "minecraft:stronghold" },
})

biome accepts a string (single biome) or a table of
<biome_id> = <weight> for weighted multi-biome selection. density and
surface reference existing presets; a builder API for customising them is
deferred. features and structures reference vanilla or datapack placed-
feature / structure ids.

Discovery

worldgen.get("mypack:custom")  -> table | nil
worldgen.list()                -> { id, ... }

Registration order

Worldgen types must be declared before any dimension that references
them. If both registries are deferred to end-of-load this ordering
constraint is enforced automatically; if they execute at module level,
require "core:worldgen" must appear before require "core:dimension"
in load order.

Non-goals (v1)

  • Multi-threaded or async chunk generation
  • Exposing raw NBT or codec APIs for density functions / surface rules
  • Custom block-entity generation from the callback
  • Client-side custom rendering

Acceptance criteria

  • Lua generate callback fires per chunk and produces visible blocks
  • ctx:fill and ctx:setBlock place blocks correctly
  • Flat layers produce the requested stack with the declared biome
  • Noise generator respects sea level, default block/fluid, height, and
    toggles
  • Weighted multi-biome sources select biomes correctly
  • Referenced vanilla features and structures spawn in the generated world
  • worldgen.get / worldgen.list return correct data
  • Reloading a script that re-registers the same type is a no-op

Open questions

  • Should generate return before the chunk is fully populated (yield /
    async)?
  • How to handle ctx:placeFeature crossing chunk boundaries — should it be
    deferred to a post-generate decorate phase or handled when neighbour chunks
    exist?
  • For density / surface: keep as preset references in v1 or provide a
    builder API?
  • Should generate have access to the world wrapper (for reads / neighbor
    chunk queries) or a restricted generation-only view?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions