Codex CLI Claude Skill

minecraft-world-generation

Create and debug Minecraft 26.x and legacy 1.21.x world generation for datapacks, NeoForge, or Fabric, including biomes, dimensions, features, structures, and biome modifiers. Use for worldgen data or registration, not general gameplay systems.

LLM Mart · 0 points · 11 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download Jahrome907-minecraft-agent-skills-.codex_skills_minecraft-world-generation-dd57c5a.zip · 11 KB
Part of jahrome907/minecraft-agent-skills — 52 skills

Install

skills CLI npx skills add https://github.com/Jahrome907/minecraft-agent-skills/tree/main/.codex/skills/minecraft-world-generation
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install jahrome907-minecraft-agent-skills@llmmart
Git git clone https://github.com/Jahrome907/minecraft-agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole jahrome907/minecraft-agent-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Minecraft World Generation

Use this skill for biome, dimension, feature, or structure data and their registration. Use minecraft-datapack for non-worldgen data and minecraft-modding for non-worldgen gameplay code.

Routing Boundaries

  • Use when: the task changes worldgen data, registration, or injection.
  • Do not use when: the task is non-worldgen datapack work (minecraft-datapack).
  • Do not use when: the task is non-worldgen mod systems (minecraft-modding).

Choose the delivery path

Approach Best When Platform
Datapack JSON Change data supplied by a pack Vanilla, any server
Mod + Datagen Registering new biomes/dimensions, code-driven NeoForge / Fabric
Biome Modifier (NeoForge) Adding features/spawns to existing biomes NeoForge
BiomeModification API (Fabric) Adding features/spawns to existing biomes Fabric

Worldgen registries are datapack registries: their files load at world load and their registry path determines the data path. Read the NeoForge registry guide before choosing a mod-specific registry path.

Version boundary

Use Minecraft 26.x for new work. Use Java 25 and start each JSON schema from the exact target's vanilla data or generated output. Do not copy a 1.21 shape into a 26.x pack merely because it parses as JSON.

Preserve an established 1.21.x project on Java 21 and its matching schema unless the task explicitly includes an upgrade. Keep examples matched to the project version.

The 26.1 migration primer removes minecraft:random_patch and minecraft:no_bonemeal_flower. It replaces the random-patch pattern with a separate minecraft:simple_block configured feature and placements for count, random offset, and block-predicate filtering. Inspect the relevant primer section before migrating code or data.

Read legacy 1.21 JSON patterns only when the project targets that version. Those examples are not release artifacts for 26.x.


Data layout and reference graph

data/<namespace>/
├── worldgen/
│   ├── biome/
│   │   └── my_biome.json
│   ├── configured_feature/
│   │   └── my_ore.json
│   ├── placed_feature/
│   │   └── my_ore_placed.json
│   ├── noise_settings/
│   │   └── my_dimension_noise.json
│   ├── structure/
│   │   └── my_structure.json
│   ├── structure_set/
│   │   └── my_structures.json
│   ├── processor_list/
│   │   └── my_processors.json
│   ├── template_pool/
│   │   └── my_pool.json
│   └── carver/
│       └── my_carver.json
├── dimension/
│   └── my_dimension.json
├── dimension_type/
│   └── my_type.json
├── tags/
│   └── worldgen/
│       └── biome/
│           └── is_forest.json
└── neoforge/
    └── biome_modifier/      (NeoForge mod only)
        └── add_ores.json

Build and review the graph from its leaves upward:

  1. Define a configured feature, then its placed feature.
  2. Reference placed features from a biome or biome modifier at the intended decoration step.
  3. Define a structure, then its structure set; a jigsaw structure also needs a template pool, processor list, and structure template.
  4. Define a dimension type and noise settings before a dimension that references them.

Use fully qualified identifiers across namespaces. An external minecraft: or dependency reference is valid when that dependency supplies the registry entry; do not create a local copy merely to satisfy static checking. If the same pack contains that external namespace and registry directory, treat it as local and verify the target exists.


Biomes and dimensions

For 26.x biome and dimension data, use the exact target's vanilla data or datagen output as the schema source. The older effects and dimension-type fields do not model newer environment behavior. The 11 decoration steps still organize placed features; choose the semantically appropriate step and keep ore placement in underground_ores.

The version-labeled 1.21.5 biome and dimension examples are in legacy 1.21 JSON patterns.

26.x feature pattern

For the 26.1 replacement for a simple random patch, the migration primer shows a simple_block configured feature and a placed feature with count, random offset, and a block-predicate filter. Adapt the exact values and block state to the target release's generated data.

At data/<namespace>/worldgen/configured_feature/my_plant.json:

{
  "type": "minecraft:simple_block",
  "config": {
    "to_place": {
      "type": "minecraft:simple_state_provider",
      "state": { "Name": "minecraft:sweet_berry_bush", "Properties": { "age": "3" } }
    }
  }
}

At data/<namespace>/worldgen/placed_feature/my_plant.json:

{
  "feature": "<namespace>:my_plant",
  "placement": [
    { "type": "minecraft:count", "count": 96 },
    {
      "type": "minecraft:random_offset",
      "xz_spread": { "type": "minecraft:trapezoid", "min": -7, "max": 7, "plateau": 0 },
      "y_spread": { "type": "minecraft:trapezoid", "min": -3, "max": 3, "plateau": 0 }
    },
    {
      "type": "minecraft:block_predicate_filter",
      "predicate": {
        "type": "minecraft:all_of",
        "predicates": [
          { "type": "minecraft:matching_block_tag", "tag": "minecraft:air" },
          { "type": "minecraft:matching_blocks", "blocks": "minecraft:grass_block", "offset": [0, -1, 0] }
        ]
      }
    }
  ]
}

NeoForge biome modifiers

Biome modifiers load from data/<modid>/neoforge/biome_modifier/<path>.json. They can target a biome id or tag and add or remove placed features, among other changes. The current Biome Modifiers guide documents their schemas, decoration steps, and datagen.

For neoforge:add_features, features accepts a placed-feature id, list, or tag. Vanilla placed features may be referenced in biome JSON or added with a modifier, but NeoForge cautions against doing both because feature-order cycles can crash world loading. Prefer a copy under the mod namespace when an injected vanilla feature would create that risk.

When targeting a biome from an optional dependency, put the target in a biome tag entry with required: false, then use that tag in the modifier. This lets the pack load when the dependency is absent.


Structures and dimensions

For any current release, derive structure, template-pool, dimension, and dimension-type JSON from that release's vanilla data or datagen output. Confirm the reference graph before launching a test world:

  • structure_set references structure.
  • Jigsaw start_pool references template_pool; each single-pool element references its structure template and processor list.
  • dimension.type references dimension_type; a noise generator's string settings references worldgen/noise_settings.

For Fabric registration or mod datagen, use the exact loader and API version's documentation rather than copying 1.21 code into a 26.x project.

The detailed 1.21 structure and dimension examples are in legacy 1.21 JSON patterns.


Development Workflow

  1. Create or edit worldgen JSON files in data/<namespace>/worldgen/ (or equivalent mod resources path).
  2. Run the bundled validator to catch JSON and cross-reference errors before loading:
    ./scripts/validate-worldgen-json.sh --root /path/to/datapack-or-mod-resources
    # Strict mode treats warnings as failures:
    ./scripts/validate-worldgen-json.sh --root /path/to/datapack-or-mod-resources --strict
    
  3. Fix any reported errors and re-validate until clean. The validator checks:
    • JSON validity for worldgen/** and neoforge/biome_modifier/**
    • Cross-reference integrity for placed_feature -> configured_feature
    • Cross-reference integrity for structure_set -> structure and biome/biome_modifier feature targets
    • Cross-reference integrity for jigsaw structure -> start_pool and template_pool -> structure template / processor_list
  4. Compare biome and dimension-type JSON against the exact target's vanilla registry shape before in-game testing. The helper does not run Mojang codecs: valid JSON and local references do not prove that fields such as carvers, effects, or dimension settings match that release's schema.
  5. In-game biome and structure testing:
    /locate structure <namespace>:my_structure
    /locate biome <namespace>:my_biome
    /place feature <namespace>:my_ore
    
    place feature takes a configured-feature ID, not its placed-feature wrapper. See Mojang's place command reference in the 1.19 release notes.
  6. For dimension testing, use /execute in (dimension must exist at world load, not added via /reload):
    execute in <namespace>:my_dimension run tp @s 0 100 0
    
  7. Check latest.log for worldgen errors (missing biome references, malformed noise settings).
  8. Note: /reload refreshes datapack JSON but does not re-generate already-generated chunks. Test new worldgen in a fresh world or newly generated chunks. For existing test worlds, use a disposable copy and a purpose-built chunk reset/regeneration workflow; /fill only replaces blocks and is not a substitute for world generation.

References

Files (minecraft-agent-skills)
  • references
    • legacy-1.21-worldgen-json.md 4.6 KB
      # Legacy Minecraft 1.21.5 Worldgen JSON
      
      These examples target Minecraft 1.21.5. For another 1.21.x release, compare each
      registry with that exact release's vanilla data or datagen output before adapting
      it. Minecraft 1.21.11 and 26.x change worldgen and environment data; the broad
      label "1.21.x" does not establish JSON schema compatibility.
      
      ## Biome and feature chain
      
      Biome `features` has 11 arrays, one for each `GenerationStep.Decoration` index.
      Ores belong at index 6, `underground_ores`. Each entry is a placed-feature id.
      
      At `data/<namespace>/worldgen/biome/my_biome.json`:
      
      ```json
      {
        "has_precipitation": true,
        "temperature": 0.7,
        "downfall": 0.8,
        "effects": {
          "sky_color": 7907327,
          "fog_color": 12638463,
          "water_color": 4159204,
          "water_fog_color": 329011
        },
        "spawners": {},
        "spawn_costs": {},
        "carvers": [],
        "features": [
          [], [], [], [], [], [],
          ["<namespace>:my_ore_placed"],
          [], [], [], []
        ]
      }
      ```
      
      At `data/<namespace>/worldgen/configured_feature/my_ore.json`:
      
      ```json
      {
        "type": "minecraft:ore",
        "config": {
          "targets": [
            {
              "target": {
                "predicate_type": "minecraft:tag_match",
                "tag": "minecraft:stone_ore_replaceables"
              },
              "state": { "Name": "minecraft:emerald_ore" }
            }
          ],
          "size": 4,
          "discard_chance_on_air_exposure": 0.0
        }
      }
      ```
      
      At `data/<namespace>/worldgen/placed_feature/my_ore_placed.json`:
      
      ```json
      {
        "feature": "<namespace>:my_ore",
        "placement": [
          { "type": "minecraft:count", "count": 8 },
          { "type": "minecraft:in_square" },
          {
            "type": "minecraft:height_range",
            "height": {
              "type": "minecraft:trapezoid",
              "min_inclusive": { "above_bottom": 0 },
              "max_inclusive": { "absolute": 64 }
            }
          },
          { "type": "minecraft:biome" }
        ]
      }
      ```
      
      ## Dimension and dimension type
      
      At `data/<namespace>/dimension_type/my_type.json`:
      
      ```json
      {
        "ultrawarm": false,
        "natural": true,
        "coordinate_scale": 1.0,
        "has_skylight": true,
        "has_ceiling": false,
        "ambient_light": 0.0,
        "monster_spawn_light_level": {
          "type": "minecraft:uniform",
          "min_inclusive": 0,
          "max_inclusive": 7
        },
        "monster_spawn_block_light_limit": 0,
        "piglin_safe": false,
        "bed_works": true,
        "respawn_anchor_works": false,
        "has_raids": true,
        "logical_height": 384,
        "height": 384,
        "min_y": -64,
        "infiniburn": "#minecraft:infiniburn_overworld",
        "effects": "minecraft:overworld"
      }
      ```
      
      At `data/<namespace>/dimension/my_dimension.json`:
      
      ```json
      {
        "type": "<namespace>:my_type",
        "generator": {
          "type": "minecraft:noise",
          "biome_source": { "type": "minecraft:fixed", "biome": "<namespace>:my_biome" },
          "settings": "minecraft:overworld"
        }
      }
      ```
      
      Use `minecraft:multi_noise` only with a complete set of climate parameters from
      a known-good 1.21 source. Keep the dimension type, noise settings, and every
      referenced biome in the same Minecraft version.
      
      ## NeoForge biome modifier
      
      At `data/<namespace>/neoforge/biome_modifier/add_ores.json`:
      
      ```json
      {
        "type": "neoforge:add_features",
        "biomes": "#minecraft:is_overworld",
        "features": "<namespace>:my_ore_placed",
        "step": "underground_ores"
      }
      ```
      
      For a legacy `remove_features` modifier, `steps` is an array; for
      `add_features`, use the singular `step`. Verify the exact minor version's
      NeoForge documentation before hand-authoring other modifier types.
      
      ## Jigsaw structures
      
      At `data/<namespace>/worldgen/structure/my_structure.json`:
      
      ```json
      {
        "type": "minecraft:jigsaw",
        "biomes": "#<namespace>:my_biome_tag",
        "step": "surface_structures",
        "terrain_adaptation": "beard_thin",
        "start_pool": "<namespace>:my_pool/start",
        "size": 6,
        "max_distance_from_center": 80,
        "use_expansion_hack": false,
        "spawn_overrides": {}
      }
      ```
      
      At `data/<namespace>/worldgen/template_pool/my_pool/start.json`:
      
      ```json
      {
        "fallback": "minecraft:empty",
        "elements": [
          {
            "weight": 1,
            "element": {
              "element_type": "minecraft:single_pool_element",
              "location": "<namespace>:my_structure/start",
              "projection": "rigid",
              "processors": "minecraft:empty"
            }
          }
        ]
      }
      ```
      
      At `data/<namespace>/worldgen/structure_set/my_structures.json`:
      
      ```json
      {
        "structures": [
          { "structure": "<namespace>:my_structure", "weight": 1 }
        ],
        "placement": {
          "type": "minecraft:random_spread",
          "spacing": 32,
          "separation": 8,
          "salt": 12345678
        }
      }
      ```
      
      The pool's `location` needs a matching
      `data/<namespace>/structure/my_structure/start.nbt`; use a processor list other
      than `minecraft:empty` only when its JSON exists and has been tested.
      
  • scripts
    • jq-shim.mjs 7.3 KB · in bundle
    • validate-worldgen-json.sh 18 KB
      #!/usr/bin/env bash
      set -euo pipefail
      
      PASS='[PASS]'
      WARN='[WARN]'
      FAIL='[FAIL]'
      
      ROOT='.'
      STRICT=0
      
      while [[ $# -gt 0 ]]; do
        case "$1" in
          --root)
            ROOT="${2:-}"
            shift 2
            ;;
          --strict)
            STRICT=1
            shift
            ;;
          --help|-h)
            cat <<'USAGE'
      Usage: validate-worldgen-json.sh [--root <path>] [--strict]
      
      Checks worldgen JSON integrity:
      - validates JSON under data/**/worldgen, data/**/dimension, data/**/dimension_type, data/**/tags/worldgen, and data/**/neoforge/biome_modifier
      - validates key directory conventions
      - validates local cross-references:
        dimension -> dimension_type + noise_settings
        placed_feature -> configured_feature
        structure_set -> structure
        jigsaw structure -> template_pool
        template_pool single_pool_element -> structure template + processor_list
        biome and biome_modifier feature references -> placed_feature
      USAGE
            exit 0
            ;;
          *)
            echo "$FAIL unknown arg: $1" >&2
            exit 1
            ;;
        esac
      done
      
      if ! command -v jq >/dev/null 2>&1; then
        SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
        JQ_SHIM="$SCRIPT_DIR/jq-shim.mjs"
        if command -v node >/dev/null 2>&1 && [[ -f "$JQ_SHIM" ]]; then
          jq() {
            node "$JQ_SHIM" "$@"
          }
        else
          echo "$FAIL jq is required"
          exit 1
        fi
      fi
      
      if [[ ! -d "$ROOT" ]]; then
        echo "$FAIL root path does not exist: $ROOT"
        exit 1
      fi
      
      FAILURES=0
      WARNINGS=0
      
      pass() { echo "$PASS $*"; }
      warn() { echo "$WARN $*"; WARNINGS=$((WARNINGS + 1)); }
      fail() { echo "$FAIL $*"; FAILURES=$((FAILURES + 1)); }
      strip_cr() { printf '%s' "${1%$'\r'}"; }
      json_query_raw() {
        local filter="$1"
        local file="$2"
      
        if command -v jq >/dev/null 2>&1; then
          jq -r "$filter" "$file"
        else
          node "$JQ_SHIM" -r "$filter" "$file"
        fi
      }
      
      TOTAL_SUPPORTED_FILES=0
      
      declare -A VALIDATED_JSON_FILES=()
      declare -A INVALID_JSON_FILES=()
      declare -A CONFIGURED_FEATURES=()
      declare -A PLACED_FEATURES=()
      declare -A STRUCTURES=()
      declare -A TEMPLATE_POOLS=()
      declare -A PROCESSOR_LISTS=()
      declare -A STRUCTURE_TEMPLATES=()
      declare -A DIMENSION_TYPES=()
      declare -A NOISE_SETTINGS=()
      declare -A VANILLA_TEMPLATE_POOLS=(
        ["minecraft:empty"]=1
      )
      declare -A VANILLA_PROCESSOR_LISTS=(
        ["minecraft:empty"]=1
      )
      declare -A VANILLA_DIMENSION_TYPES=(
        ["minecraft:overworld"]=1
        ["minecraft:overworld_caves"]=1
        ["minecraft:the_nether"]=1
        ["minecraft:the_end"]=1
      )
      declare -A VANILLA_NOISE_SETTINGS=(
        ["minecraft:overworld"]=1
        ["minecraft:large_biomes"]=1
        ["minecraft:amplified"]=1
        ["minecraft:nether"]=1
        ["minecraft:end"]=1
        ["minecraft:caves"]=1
        ["minecraft:floating_islands"]=1
      )
      
      DATA_ROOT="$ROOT/data"
      
      BIOME_FILES=0
      BIOME_MODIFIER_FILES=0
      
      to_id() {
        local file="$1"
        local rel ns path noext
        rel="${file#"$ROOT/data/"}"
        ns="${rel%%/*}"
        path="${rel#*/}"
        path="${path#worldgen/}"
        noext="${path%.json}"
        noext="${noext%.nbt}"
        echo "$ns:${noext#*/}"
      }
      
      split_ref() {
        local default_ns="$1"
        local ref="$2"
        if [[ "$ref" == *:* ]]; then
          echo "$ref"
        else
          echo "$default_ns:$ref"
        fi
      }
      
      check_json() {
        local file="$1"
        if jq empty "$file" >/dev/null 2>&1; then
          unset 'INVALID_JSON_FILES[$file]'
          pass "valid JSON: ${file#$ROOT/}"
        else
          INVALID_JSON_FILES["$file"]=1
          fail "invalid JSON: ${file#$ROOT/}"
        fi
      }
      
      check_json_once() {
        local file="$1"
        if [[ -n "${VALIDATED_JSON_FILES["$file"]+x}" ]]; then
          return
        fi
      
        VALIDATED_JSON_FILES["$file"]=1
        check_json "$file"
        TOTAL_SUPPORTED_FILES=$((TOTAL_SUPPORTED_FILES + 1))
      }
      
      should_validate_dimension_type_ref() {
        local source_ns="$1"
        local id="$2"
        local target_ns="${id%%:*}"
      
        if [[ "$target_ns" == "$source_ns" ]]; then
          return 0
        fi
      
        if [[ "$target_ns" == "minecraft" && -n "${VANILLA_DIMENSION_TYPES[$id]:-}" ]]; then
          return 1
        fi
      
        [[ -d "$DATA_ROOT/$target_ns/dimension_type" ]]
      }
      
      should_validate_noise_settings_ref() {
        local source_ns="$1"
        local id="$2"
        local target_ns="${id%%:*}"
      
        if [[ "$target_ns" == "$source_ns" ]]; then
          return 0
        fi
      
        if [[ "$target_ns" == "minecraft" && -n "${VANILLA_NOISE_SETTINGS[$id]:-}" ]]; then
          return 1
        fi
      
        [[ -d "$DATA_ROOT/$target_ns/worldgen/noise_settings" ]]
      }
      
      find_worldgen_jsons() {
        local category="${1:-}"
        while IFS= read -r -d '' worldgen_dir; do
          if [[ -n "$category" ]]; then
            [[ -d "$worldgen_dir/$category" ]] || continue
            find "$worldgen_dir/$category" -mindepth 1 -type f -name '*.json' -print0 2>/dev/null
          else
            find "$worldgen_dir" -mindepth 2 -type f -name '*.json' -print0 2>/dev/null
          fi
        done < <(find "$DATA_ROOT" -mindepth 2 -maxdepth 2 -type d -name worldgen -print0 2>/dev/null)
      }
      
      find_namespace_jsons() {
        local dir_name="$1"
        while IFS= read -r -d '' dir; do
          find "$dir" -mindepth 1 -type f -name '*.json' -print0 2>/dev/null
        done < <(find "$DATA_ROOT" -mindepth 2 -maxdepth 2 -type d -name "$dir_name" -print0 2>/dev/null)
      }
      
      find_neoforge_jsons() {
        local dir_name="$1"
        while IFS= read -r -d '' dir; do
          find "$dir" -mindepth 1 -type f -name '*.json' -print0 2>/dev/null
        done < <(find "$DATA_ROOT" -mindepth 3 -maxdepth 3 -type d -path "$DATA_ROOT/*/neoforge/$dir_name" -print0 2>/dev/null)
      }
      
      find_structure_templates() {
        while IFS= read -r -d '' dir; do
          find "$dir" -mindepth 1 -type f -name '*.nbt' -print0 2>/dev/null
        done < <(find "$DATA_ROOT" -mindepth 2 -maxdepth 2 -type d -name structure -print0 2>/dev/null)
      }
      
      find_tags_worldgen_jsons() {
        while IFS= read -r -d '' dir; do
          find "$dir" -mindepth 2 -type f -name '*.json' -print0 2>/dev/null
        done < <(find "$DATA_ROOT" -mindepth 3 -maxdepth 3 -type d -path "$DATA_ROOT/*/tags/worldgen" -print0 2>/dev/null)
      }
      
      find_invalid_tags_worldgen_jsons() {
        while IFS= read -r -d '' dir; do
          find "$dir" -mindepth 1 -maxdepth 1 -type f -name '*.json' -print0 2>/dev/null
        done < <(find "$DATA_ROOT" -mindepth 3 -maxdepth 3 -type d -path "$DATA_ROOT/*/tags/worldgen" -print0 2>/dev/null)
      }
      
      should_validate_template_pool_ref() {
        local source_ns="$1"
        local id="$2"
        local target_ns="${id%%:*}"
      
        if [[ "$target_ns" == "$source_ns" ]]; then
          return 0
        fi
      
        if [[ "$target_ns" == "minecraft" && -n "${VANILLA_TEMPLATE_POOLS[$id]:-}" ]]; then
          return 1
        fi
      
        [[ -d "$DATA_ROOT/$target_ns/worldgen/template_pool" ]]
      }
      
      should_validate_processor_list_ref() {
        local source_ns="$1"
        local id="$2"
        local target_ns="${id%%:*}"
      
        if [[ "$target_ns" == "$source_ns" ]]; then
          return 0
        fi
      
        if [[ "$target_ns" == "minecraft" && -n "${VANILLA_PROCESSOR_LISTS[$id]:-}" ]]; then
          return 1
        fi
      
        [[ -d "$DATA_ROOT/$target_ns/worldgen/processor_list" ]]
      }
      
      should_validate_structure_template_ref() {
        local source_ns="$1"
        local id="$2"
        local target_ns="${id%%:*}"
      
        if [[ "$target_ns" == "$source_ns" ]]; then
          return 0
        fi
      
        [[ -d "$DATA_ROOT/$target_ns/structure" ]]
      }
      
      # A pack can reference vanilla or dependency-owned registry entries that are not
      # present in this source tree. Check own-namespace targets and any external
      # namespace that the supplied pack actually defines; leave the runtime to
      # resolve all other external entries.
      should_validate_worldgen_ref() {
        local source_ns="$1"
        local id="$2"
        local registry_dir="$3"
        local target_ns="${id%%:*}"
      
        if [[ "$target_ns" == "$source_ns" ]]; then
          return 0
        fi
      
        [[ -d "$DATA_ROOT/$target_ns/worldgen/$registry_dir" ]]
      }
      
      echo "=== Worldgen Validator ==="
      
      if [[ ! -d "$DATA_ROOT" ]]; then
        fail "missing data/ directory"
      fi
      
      echo "Checking for legacy paths..."
      while IFS= read -r -d '' legacy_path; do
        fail "legacy path detected: ${legacy_path#$ROOT/}"
      done < <(find_neoforge_jsons 'biome_modifiers')
      
      while IFS= read -r -d '' invalid_tag_path; do
        fail "invalid worldgen tag path: ${invalid_tag_path#$ROOT/} (expected tags/worldgen/<registry>/...)"
      done < <(find_invalid_tags_worldgen_jsons)
      
      while IFS= read -r -d '' f; do
        check_json_once "$f"
      done < <(
        {
          find_worldgen_jsons
          find_namespace_jsons 'dimension'
          find_namespace_jsons 'dimension_type'
          find_tags_worldgen_jsons
          find_neoforge_jsons 'biome_modifier'
        }
      )
      
      while IFS= read -r -d '' f; do
        id="$(to_id "$f")"
        CONFIGURED_FEATURES["$id"]=1
      done < <(find_worldgen_jsons 'configured_feature')
      
      while IFS= read -r -d '' f; do
        id="$(to_id "$f")"
        PLACED_FEATURES["$id"]=1
      done < <(find_worldgen_jsons 'placed_feature')
      
      while IFS= read -r -d '' f; do
        id="$(to_id "$f")"
        STRUCTURES["$id"]=1
      done < <(find_worldgen_jsons 'structure')
      
      while IFS= read -r -d '' f; do
        id="$(to_id "$f")"
        TEMPLATE_POOLS["$id"]=1
      done < <(find_worldgen_jsons 'template_pool')
      
      while IFS= read -r -d '' f; do
        id="$(to_id "$f")"
        PROCESSOR_LISTS["$id"]=1
      done < <(find_worldgen_jsons 'processor_list')
      
      while IFS= read -r -d '' f; do
        id="$(to_id "$f")"
        STRUCTURE_TEMPLATES["$id"]=1
      done < <(find_structure_templates)
      
      while IFS= read -r -d '' f; do
        id="$(to_id "$f")"
        DIMENSION_TYPES["$id"]=1
      done < <(find_namespace_jsons 'dimension_type')
      
      while IFS= read -r -d '' f; do
        id="$(to_id "$f")"
        NOISE_SETTINGS["$id"]=1
      done < <(find_worldgen_jsons 'noise_settings')
      
      while IFS= read -r -d '' _; do
        BIOME_FILES=$((BIOME_FILES + 1))
      done < <(find_worldgen_jsons 'biome')
      
      while IFS= read -r -d '' _; do
        BIOME_MODIFIER_FILES=$((BIOME_MODIFIER_FILES + 1))
      done < <(find_neoforge_jsons 'biome_modifier')
      
      if [[ "$TOTAL_SUPPORTED_FILES" -eq 0 ]]; then
        fail "no supported worldgen JSON files found under data/**/worldgen, data/**/dimension, data/**/dimension_type, data/**/tags/worldgen, or data/**/neoforge/biome_modifier"
      fi
      
      if (( ${#PLACED_FEATURES[@]} == 0 && (BIOME_FILES > 0 || BIOME_MODIFIER_FILES > 0) )); then
        warn "no placed_feature JSON files found"
      fi
      
      if (( ${#CONFIGURED_FEATURES[@]} == 0 && ${#PLACED_FEATURES[@]} > 0 )); then
        warn "no configured_feature JSON files found"
      fi
      
      echo "Checking dimension references..."
      while IFS= read -r -d '' dimension_file; do
        if [[ -n "${INVALID_JSON_FILES["$dimension_file"]:-}" ]]; then
          continue
        fi
      
        rel="${dimension_file#"$ROOT/data/"}"
        ns="${rel%%/*}"
        type_ref="$(jq -r '.type? // empty' "$dimension_file")"
        type_ref="$(strip_cr "$type_ref")"
      
        if [[ -z "$type_ref" ]]; then
          fail "dimension missing .type: ${dimension_file#$ROOT/}"
        else
          type_id="$(split_ref "$ns" "$type_ref")"
          if should_validate_dimension_type_ref "$ns" "$type_id"; then
            if [[ -n "${DIMENSION_TYPES[$type_id]:-}" ]]; then
              pass "dimension type target exists: $type_id"
            else
              fail "dimension references missing dimension_type: $type_id"
            fi
          fi
        fi
      
        settings_ref="$(jq -r 'if (.generator?.type? // empty) == "minecraft:noise" and (.generator?.settings? | type) == "string" then .generator.settings else empty end' "$dimension_file")"
        settings_ref="$(strip_cr "$settings_ref")"
        if [[ -z "$settings_ref" ]]; then
          continue
        fi
      
        settings_id="$(split_ref "$ns" "$settings_ref")"
        if ! should_validate_noise_settings_ref "$ns" "$settings_id"; then
          continue
        fi
      
        if [[ -n "${NOISE_SETTINGS[$settings_id]:-}" ]]; then
          pass "dimension noise settings target exists: $settings_id"
        else
          fail "dimension references missing noise_settings: $settings_id"
        fi
      done < <(find_namespace_jsons 'dimension')
      
      echo "Checking placed_feature -> configured_feature references..."
      while IFS= read -r -d '' pf_file; do
        rel="${pf_file#"$ROOT/data/"}"
        ns="${rel%%/*}"
        feature_ref="$(jq -r '.feature? // empty' "$pf_file")"
        feature_ref="$(strip_cr "$feature_ref")"
      
        if [[ -z "$feature_ref" ]]; then
          fail "placed_feature missing .feature: ${pf_file#$ROOT/}"
          continue
        fi
      
        if [[ "$feature_ref" == \#* ]]; then
          warn "tag reference not resolved in placed_feature: ${pf_file#$ROOT/} -> $feature_ref"
          continue
        fi
      
        feature_id="$(split_ref "$ns" "$feature_ref")"
        if ! should_validate_worldgen_ref "$ns" "$feature_id" 'configured_feature'; then
          continue
        fi
      
        if [[ -n "${CONFIGURED_FEATURES[$feature_id]:-}" ]]; then
          pass "placed_feature target exists: $feature_id"
        else
          fail "placed_feature references missing configured_feature: $feature_id"
        fi
      done < <(find_worldgen_jsons 'placed_feature')
      
      echo "Checking structure_set -> structure references..."
      while IFS= read -r -d '' ss_file; do
        if [[ -n "${INVALID_JSON_FILES["$ss_file"]:-}" ]]; then
          continue
        fi
      
        rel="${ss_file#"$ROOT/data/"}"
        ns="${rel%%/*}"
        while IFS= read -r sref; do
          sref="$(strip_cr "$sref")"
          [[ -z "$sref" ]] && continue
          sid="$(split_ref "$ns" "$sref")"
          if ! should_validate_worldgen_ref "$ns" "$sid" 'structure'; then
            continue
          fi
      
          if [[ -n "${STRUCTURES[$sid]:-}" ]]; then
            pass "structure_set target exists: $sid"
          else
            fail "structure_set references missing structure: $sid"
          fi
        done < <(jq -r '.structures[]?.structure? // empty' "$ss_file")
      done < <(find_worldgen_jsons 'structure_set')
      
      echo "Checking jigsaw structure and template_pool references..."
      while IFS= read -r -d '' structure_file; do
        if [[ -n "${INVALID_JSON_FILES["$structure_file"]:-}" ]]; then
          continue
        fi
      
        rel="${structure_file#"$ROOT/data/"}"
        ns="${rel%%/*}"
        structure_type="$(json_query_raw '.type? // empty' "$structure_file")"
        structure_type="$(strip_cr "$structure_type")"
      
        if [[ "$structure_type" != "minecraft:jigsaw" ]]; then
          continue
        fi
      
        start_pool_ref="$(json_query_raw '.start_pool? // empty' "$structure_file")"
        start_pool_ref="$(strip_cr "$start_pool_ref")"
      
        if [[ -z "$start_pool_ref" ]]; then
          fail "jigsaw structure missing .start_pool: ${structure_file#$ROOT/}"
          continue
        fi
      
        start_pool_id="$(split_ref "$ns" "$start_pool_ref")"
        if should_validate_template_pool_ref "$ns" "$start_pool_id"; then
          if [[ -n "${TEMPLATE_POOLS[$start_pool_id]:-}" ]]; then
            pass "jigsaw start_pool target exists: $start_pool_id"
          else
            fail "jigsaw structure references missing template_pool: $start_pool_id"
          fi
        fi
      done < <(find_worldgen_jsons 'structure')
      
      while IFS= read -r -d '' pool_file; do
        if [[ -n "${INVALID_JSON_FILES["$pool_file"]:-}" ]]; then
          continue
        fi
      
        rel="${pool_file#"$ROOT/data/"}"
        ns="${rel%%/*}"
        while IFS=$'\t' read -r location_ref processors_ref; do
          location_ref="$(strip_cr "$location_ref")"
          processors_ref="$(strip_cr "$processors_ref")"
      
          if [[ -z "$location_ref" ]]; then
            fail "template_pool single_pool_element missing .location: ${pool_file#$ROOT/}"
          else
            location_id="$(split_ref "$ns" "$location_ref")"
            if should_validate_structure_template_ref "$ns" "$location_id"; then
              if [[ -n "${STRUCTURE_TEMPLATES[$location_id]:-}" ]]; then
                pass "template_pool structure template target exists: $location_id"
              else
                fail "template_pool element references missing structure template: $location_id"
              fi
            fi
          fi
      
          if [[ -z "$processors_ref" ]]; then
            fail "template_pool single_pool_element missing .processors: ${pool_file#$ROOT/}"
          else
            processors_id="$(split_ref "$ns" "$processors_ref")"
            if should_validate_processor_list_ref "$ns" "$processors_id"; then
              if [[ -n "${PROCESSOR_LISTS[$processors_id]:-}" ]]; then
                pass "template_pool processor_list target exists: $processors_id"
              else
                fail "template_pool element references missing processor_list: $processors_id"
              fi
            fi
          fi
        done < <(json_query_raw '
          .. | objects
          | select(.element_type? == "minecraft:single_pool_element" or .element_type? == "minecraft:legacy_single_pool_element")
          | [(.location? // ""), (.processors? // "")]
          | @tsv
        ' "$pool_file")
      done < <(find_worldgen_jsons 'template_pool')
      
      echo "Checking biome feature references..."
      while IFS= read -r -d '' biome_file; do
        if [[ -n "${INVALID_JSON_FILES["$biome_file"]:-}" ]]; then
          continue
        fi
      
        rel="${biome_file#"$ROOT/data/"}"
        ns="${rel%%/*}"
        while IFS= read -r fref; do
          fref="$(strip_cr "$fref")"
          [[ -z "$fref" ]] && continue
          if [[ "$fref" == \#* ]]; then
            warn "tag reference not resolved in biome file: ${biome_file#$ROOT/} -> $fref"
            continue
          fi
      
          fid="$(split_ref "$ns" "$fref")"
          if ! should_validate_worldgen_ref "$ns" "$fid" 'placed_feature'; then
            continue
          fi
      
          if [[ -n "${PLACED_FEATURES[$fid]:-}" ]]; then
            pass "biome feature target exists: $fid"
          else
            fail "biome references missing placed_feature: $fid"
          fi
        done < <(jq -r '.features[][]? // empty' "$biome_file")
      done < <(find_worldgen_jsons 'biome')
      
      echo "Checking biome_modifier feature/structure references..."
      while IFS= read -r -d '' mod_file; do
        if [[ -n "${INVALID_JSON_FILES["$mod_file"]:-}" ]]; then
          continue
        fi
      
        rel="${mod_file#"$ROOT/data/"}"
        ns="${rel%%/*}"
      
        while IFS= read -r ref; do
          ref="$(strip_cr "$ref")"
          [[ -z "$ref" ]] && continue
          if [[ "$ref" == \#* ]]; then
            warn "tag reference not resolved in biome_modifier: ${mod_file#$ROOT/} -> $ref"
            continue
          fi
      
          rid="$(split_ref "$ns" "$ref")"
          if ! should_validate_worldgen_ref "$ns" "$rid" 'placed_feature'; then
            continue
          fi
      
          if [[ -n "${PLACED_FEATURES[$rid]:-}" ]]; then
            pass "biome_modifier feature target exists: $rid"
          else
            fail "biome_modifier references missing placed_feature: $rid"
          fi
        done < <(jq -r 'if (.features? | type) == "array" then .features[]? else .features? // empty end' "$mod_file")
      
        while IFS= read -r ref; do
          ref="$(strip_cr "$ref")"
          [[ -z "$ref" ]] && continue
          rid="$(split_ref "$ns" "$ref")"
          if ! should_validate_worldgen_ref "$ns" "$rid" 'structure'; then
            continue
          fi
      
          if [[ -n "${STRUCTURES[$rid]:-}" ]]; then
            pass "biome_modifier structure target exists: $rid"
          else
            fail "biome_modifier references missing structure: $rid"
          fi
        done < <(jq -r 'if (.structures? | type) == "array" then .structures[]? else .structures? // empty end' "$mod_file")
      done < <(find_neoforge_jsons 'biome_modifier')
      
      echo ""
      if [[ "$FAILURES" -gt 0 ]]; then
        echo "$FAIL worldgen validation failed with $FAILURES error(s) and $WARNINGS warning(s)"
        exit 1
      fi
      
      if [[ "$STRICT" -eq 1 && "$WARNINGS" -gt 0 ]]; then
        echo "$FAIL worldgen validation strict mode failed on $WARNINGS warning(s)"
        exit 1
      fi
      
      echo "$PASS worldgen validation passed with $WARNINGS warning(s)"
      
  • SKILL.md 10.3 KB
    ---
    name: minecraft-world-generation
    description: "Create and debug Minecraft 26.x and legacy 1.21.x world generation for datapacks, NeoForge, or Fabric, including biomes, dimensions, features, structures, and biome modifiers. Use for worldgen data or registration, not general gameplay systems."
    ---
    
    # Minecraft World Generation
    
    Use this skill for biome, dimension, feature, or structure data and their
    registration. Use `minecraft-datapack` for non-worldgen data and
    `minecraft-modding` for non-worldgen gameplay code.
    
    ## Routing Boundaries
    
    - `Use when`: the task changes worldgen data, registration, or injection.
    - `Do not use when`: the task is non-worldgen datapack work (`minecraft-datapack`).
    - `Do not use when`: the task is non-worldgen mod systems (`minecraft-modding`).
    
    ## Choose the delivery path
    
    | Approach | Best When | Platform |
    |----------|-----------|----------|
    | Datapack JSON | Change data supplied by a pack | Vanilla, any server |
    | **Mod + Datagen** | Registering new biomes/dimensions, code-driven | NeoForge / Fabric |
    | **Biome Modifier (NeoForge)** | Adding features/spawns to existing biomes | NeoForge |
    | **BiomeModification API (Fabric)** | Adding features/spawns to existing biomes | Fabric |
    
    Worldgen registries are datapack registries: their files load at world load and
    their registry path determines the data path. Read the [NeoForge registry
    guide](https://docs.neoforged.net/docs/concepts/registries/) before choosing a
    mod-specific registry path.
    
    ## Version boundary
    
    Use Minecraft 26.x for new work. Use Java 25 and start
    each JSON schema from the exact target's vanilla data or generated output. Do
    not copy a 1.21 shape into a 26.x pack merely because it parses as JSON.
    
    Preserve an established 1.21.x project on Java 21 and its matching schema unless
    the task explicitly includes an upgrade. Keep examples matched to the project version.
    
    The [26.1 migration primer](https://docs.neoforged.net/primer/docs/26.1/)
    removes `minecraft:random_patch` and `minecraft:no_bonemeal_flower`. It replaces
    the random-patch pattern with a separate `minecraft:simple_block` configured
    feature and placements for count, random offset, and block-predicate filtering.
    Inspect the relevant primer section before migrating code or data.
    
    Read [legacy 1.21 JSON patterns](references/legacy-1.21-worldgen-json.md) only
    when the project targets that version. Those examples are not
    release artifacts for 26.x.
    
    ---
    
    ## Data layout and reference graph
    
    ```
    data/<namespace>/
    ├── worldgen/
    │   ├── biome/
    │   │   └── my_biome.json
    │   ├── configured_feature/
    │   │   └── my_ore.json
    │   ├── placed_feature/
    │   │   └── my_ore_placed.json
    │   ├── noise_settings/
    │   │   └── my_dimension_noise.json
    │   ├── structure/
    │   │   └── my_structure.json
    │   ├── structure_set/
    │   │   └── my_structures.json
    │   ├── processor_list/
    │   │   └── my_processors.json
    │   ├── template_pool/
    │   │   └── my_pool.json
    │   └── carver/
    │       └── my_carver.json
    ├── dimension/
    │   └── my_dimension.json
    ├── dimension_type/
    │   └── my_type.json
    ├── tags/
    │   └── worldgen/
    │       └── biome/
    │           └── is_forest.json
    └── neoforge/
        └── biome_modifier/      (NeoForge mod only)
            └── add_ores.json
    ```
    
    Build and review the graph from its leaves upward:
    
    1. Define a configured feature, then its placed feature.
    2. Reference placed features from a biome or biome modifier at the intended
       decoration step.
    3. Define a structure, then its structure set; a jigsaw structure also needs a
       template pool, processor list, and structure template.
    4. Define a dimension type and noise settings before a dimension that references
       them.
    
    Use fully qualified identifiers across namespaces. An external `minecraft:` or
    dependency reference is valid when that dependency supplies the registry entry;
    do not create a local copy merely to satisfy static checking. If the same pack
    contains that external namespace and registry directory, treat it as local and
    verify the target exists.
    
    ---
    
    ## Biomes and dimensions
    
    For 26.x biome and dimension data, use the exact target's vanilla data or
    datagen output as the schema source. The older `effects` and dimension-type
    fields do not model newer environment behavior. The 11 decoration steps still
    organize placed features; choose the semantically appropriate step and keep ore
    placement in `underground_ores`.
    
    The version-labeled 1.21.5 biome and dimension examples are in
    [legacy 1.21 JSON patterns](references/legacy-1.21-worldgen-json.md).
    
    ## 26.x feature pattern
    
    For the 26.1 replacement for a simple random patch, the migration primer shows
    a `simple_block` configured feature and a placed feature with count, random
    offset, and a block-predicate filter. Adapt the exact values and block state to
    the target release's generated data.
    
    At `data/<namespace>/worldgen/configured_feature/my_plant.json`:
    
    ```json
    {
      "type": "minecraft:simple_block",
      "config": {
        "to_place": {
          "type": "minecraft:simple_state_provider",
          "state": { "Name": "minecraft:sweet_berry_bush", "Properties": { "age": "3" } }
        }
      }
    }
    ```
    
    At `data/<namespace>/worldgen/placed_feature/my_plant.json`:
    
    ```json
    {
      "feature": "<namespace>:my_plant",
      "placement": [
        { "type": "minecraft:count", "count": 96 },
        {
          "type": "minecraft:random_offset",
          "xz_spread": { "type": "minecraft:trapezoid", "min": -7, "max": 7, "plateau": 0 },
          "y_spread": { "type": "minecraft:trapezoid", "min": -3, "max": 3, "plateau": 0 }
        },
        {
          "type": "minecraft:block_predicate_filter",
          "predicate": {
            "type": "minecraft:all_of",
            "predicates": [
              { "type": "minecraft:matching_block_tag", "tag": "minecraft:air" },
              { "type": "minecraft:matching_blocks", "blocks": "minecraft:grass_block", "offset": [0, -1, 0] }
            ]
          }
        }
      ]
    }
    ```
    
    ---
    
    ## NeoForge biome modifiers
    
    Biome modifiers load from
    `data/<modid>/neoforge/biome_modifier/<path>.json`. They can target a biome id
    or tag and add or remove placed features, among other changes. The current
    [Biome Modifiers guide](https://docs.neoforged.net/docs/worldgen/biomemodifier/)
    documents their schemas, decoration steps, and datagen.
    
    For `neoforge:add_features`, `features` accepts a placed-feature id, list, or
    tag. Vanilla placed features may be referenced in biome JSON or added with a
    modifier, but NeoForge cautions against doing both because feature-order cycles
    can crash world loading. Prefer a copy under the mod namespace when an injected
    vanilla feature would create that risk.
    
    When targeting a biome from an optional dependency, put the target in a biome
    tag entry with `required: false`, then use that tag in the modifier. This lets
    the pack load when the dependency is absent.
    
    ---
    
    ## Structures and dimensions
    
    For any current release, derive structure, template-pool, dimension, and
    dimension-type JSON from that release's vanilla data or datagen output. Confirm
    the reference graph before launching a test world:
    
    - `structure_set` references `structure`.
    - Jigsaw `start_pool` references `template_pool`; each single-pool element
      references its structure template and processor list.
    - `dimension.type` references `dimension_type`; a noise generator's string
      `settings` references `worldgen/noise_settings`.
    
    For Fabric registration or mod datagen, use the exact loader and API version's
    documentation rather than copying 1.21 code into a 26.x project.
    
    The detailed 1.21 structure and dimension examples are in
    [legacy 1.21 JSON patterns](references/legacy-1.21-worldgen-json.md).
    
    ---
    
    ## Development Workflow
    
    1. Create or edit worldgen JSON files in `data/<namespace>/worldgen/` (or equivalent mod resources path).
    2. Run the bundled validator to catch JSON and cross-reference errors before loading:
       ```bash
       ./scripts/validate-worldgen-json.sh --root /path/to/datapack-or-mod-resources
       # Strict mode treats warnings as failures:
       ./scripts/validate-worldgen-json.sh --root /path/to/datapack-or-mod-resources --strict
       ```
    3. Fix any reported errors and re-validate until clean. The validator checks:
       - JSON validity for `worldgen/**` and `neoforge/biome_modifier/**`
       - Cross-reference integrity for `placed_feature -> configured_feature`
       - Cross-reference integrity for `structure_set -> structure` and biome/biome_modifier feature targets
       - Cross-reference integrity for `jigsaw structure -> start_pool` and `template_pool -> structure template / processor_list`
    4. Compare biome and dimension-type JSON against the exact target's vanilla
       registry shape before in-game testing. The helper does not run Mojang codecs:
       valid JSON and local references do not prove that fields such as `carvers`,
       `effects`, or dimension settings match that release's schema.
    5. In-game biome and structure testing:
       ```mcfunction
       /locate structure <namespace>:my_structure
       /locate biome <namespace>:my_biome
       /place feature <namespace>:my_ore
       ```
       `place feature` takes a configured-feature ID, not its placed-feature wrapper.
       See Mojang's [place command reference in the 1.19 release notes](https://www.minecraft.net/en-us/article/the-wild-update-out-today-java).
    6. For dimension testing, use `/execute in` (dimension must exist at world load, not added via `/reload`):
       ```mcfunction
       execute in <namespace>:my_dimension run tp @s 0 100 0
       ```
    7. Check `latest.log` for worldgen errors (missing biome references, malformed noise settings).
    8. Note: `/reload` refreshes datapack JSON but does **not** re-generate already-generated chunks. Test new worldgen in a fresh world or newly generated chunks. For existing test worlds, use a disposable copy and a purpose-built chunk reset/regeneration workflow; `/fill` only replaces blocks and is not a substitute for world generation.
    
    ---
    
    ## References
    
    - Minecraft Wiki — World generation: https://minecraft.wiki/w/Custom_world_generation
    - Minecraft Wiki — Biome: https://minecraft.wiki/w/Biome/JSON_format
    - Minecraft Wiki — Features: https://minecraft.wiki/w/World_generation/Configured_feature
    - NeoForge Biome Modifiers: https://docs.neoforged.net/docs/worldgen/biomemodifier/
    - Fabric BiomeModifications: https://wiki.fabricmc.net/tutorial:biomemodification
    - misode's data pack generator (worldgen UI): https://misode.github.io/worldgen/
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related