> For the complete documentation index, see [llms.txt](https://plugins.elitefantasy.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://plugins.elitefantasy.net/masky-plugins/maskycore/datapack-integration/modular-world-generation.md).

# Modular World Generation

## Introduction

MaskyCore extends Minecraft datapacks with portable CraftEngine structures and configurable world generation modules.

Datapacks retain Minecraft’s standard resource format. Additional JSON definitions allow MaskyCore to apply generation logic supplied by registered modules.

The system is extensible: the volcanic caldera is one implementation, and future implementations can use the same loading, registration, and generation interfaces.

### Requirements

* Paper 26.2.
* Java 25.
* A MaskyCore build containing the modular world generation system.
* CraftEngine for portable structure conversion and custom block integration.

For developers building the plugin:

* JDK 25.
* Gradle 9.1 through the project’s Gradle Wrapper.
* Kotlin 2.3.0.
* The dependencies declared by the project.

The current project references a local `libs/Adapter-1.0.6.4.jar`. This file must be available when compiling.

### How It Works

The datapack and the plugin have separate responsibilities:

| Component                   | Responsibility                                                                                 |
| --------------------------- | ---------------------------------------------------------------------------------------------- |
| Standard datapack resources | Define biomes, configured features, placed features, structures, and other Minecraft resources |
| MaskyCore JSON definitions  | Select and configure registered generation modules                                             |
| Generation modules          | Implement terrain, surface, cave, and biome selection logic                                    |
| Portable structure compiler | Convert stable CraftEngine block references into server-specific states                        |

A datapack can configure multiple instances of an existing module type.

A new generation algorithm requires a new module implementation. Adding an unknown value to `type` does not automatically create that algorithm.

### Datapack Folder Structure

Place each extracted source datapack inside:

```
plugins/MaskyCore/datapacks/<pack>/
```

The pack must contain `pack.mcmeta` directly inside its folder.

Example:

```yaml
plugins/
└── MaskyCore/
    └── datapacks/
        └── example_pack/
            ├── pack.mcmeta
            └── data/
                └── example_pack/
                    ├── worldgen/
                    │   ├── biome/
                    │   ├── configured_feature/
                    │   ├── placed_feature/
                    │   └── structure/
                    ├── structure/
                    │   └── example.nbt
                    └── maskycore/
                        └── worldgen/
                            └── definitions/
                                └── example.json
```

Use the `pack.mcmeta` format required by your Minecraft version.

> Extract the datapack before placing it in the source directory. The module loader reads JSON files from directories, not from ZIP archives.

#### Source, Cache, and Installed Copies

| Location                                   | Purpose                      |
| ------------------------------------------ | ---------------------------- |
| `plugins/MaskyCore/datapacks/<pack>/`      | Editable source              |
| `plugins/MaskyCore/datapack-cache/<pack>/` | Compiled cache               |
| `<world>/datapacks/maskycore-<pack>/`      | Installed Minecraft datapack |

Always edit the source. Generated copies may be overwritten when the pack is rebuilt.

### Installation

#### 1. Add the source datapack

Place the extracted datapack in:

```
plugins/MaskyCore/datapacks/<pack>/
```

#### 2. Configure the destination world

In `plugins/MaskyCore/config.yml`:

```
datapacks:
  auto-build: true
  fallback-block: minecraft:stone
  register-structures-live: true
  install-to-world: true
  world-folder: world
  reload-after-install: true
```

Replace `world` with the destination world folder.

When `world-folder` is empty, MaskyCore reads `level-name` from `server.properties`.

#### 3. Build and inspect the datapack

After CraftEngine and MaskyCore have loaded:

```
/maskycore datapack build
/maskycore datapack status
```

| Command                      | Purpose                              |
| ---------------------------- | ------------------------------------ |
| `/maskycore datapack build`  | Compile and install source datapacks |
| `/maskycore datapack status` | Inspect datapack status and paths    |

#### 4. Enable modular generation

For a world using MaskyCore generation modules, configure `bukkit.yml`:

```yaml
worlds:
  world:
    generator: MaskyCore:worldgen
```

Replace `world` with the actual world name.

`MaskyCore:biomes` is also accepted as an alias.

#### 5. Restart the server

Restart the server completely after installing or updating world generation resources.

> During the first installation, MaskyCore may copy the datapack after Minecraft has already loaded its world generation registries. Another full startup is required before those resources become available.

Test generation in new chunks. Existing terrain is not rebuilt automatically.

### Reload Behavior

`/maskycore datapack build` compiles and installs resources. It does not replace the generator of an already loaded world.

A full restart is required when changing:

* World generation registries.
* Custom biomes.
* Generation module definitions.
* Generator configuration.

The `reload-after-install` option applies to resources that can safely reload. It does not make world generation registries reloadable.

### Portable CraftEngine Structures

CraftEngine assigns server-specific registry states to custom blocks. These identifiers may change between installations.

To keep structures portable, their NBT palettes can reference stable CraftEngine states:

```
craftengine:elitefantasy:termite_nest[stage=empty]
```

When MaskyCore compiles the datapack, it resolves that reference through CraftEngine and replaces it with the current server’s registry state, for example:

```
craftengine:custom_71101
```

The numeric identifier is only an example. Keep stable references in the source datapack.

#### Supported Structure Paths

Conversion applies to structure NBT files under:

```
data/<namespace>/structure/**/*.nbt
data/<namespace>/structures/**/*.nbt
```

Other resources are copied without this conversion.

Missing CraftEngine states use `datapacks.fallback-block` and are listed in the compilation report.

### Module Definition Format

Module definitions are stored in:

```
data/<namespace>/maskycore/worldgen/definitions/<name>.json
```

Common fields:

```
{
  "id": "example_pack:custom_region",
  "type": "example_plugin:registered_type",
  "enabled": true,
  "priority": 50
}
```

This example describes the format. `example_plugin:registered_type` must have an actual registered factory.

| Field             | Required       | Description                                                                |
| ----------------- | -------------- | -------------------------------------------------------------------------- |
| `id`              | No             | Instance identifier. Derived from the namespace and file path when omitted |
| `type`            | Yes            | Registered factory responsible for interpreting the definition             |
| `enabled`         | No             | Defaults to `true`. Set to `false` to exclude the instance                 |
| `priority`        | No             | Defaults to `0`. Controls module ordering                                  |
| Additional fields | Type-dependent | Parsed and validated by the selected factory                               |

Use lowercase namespaced identifiers without spaces or accented characters.

Multiple definitions can use the same `type`, provided they have distinct instance IDs.

#### Definition Precedence

MaskyCore reads cached definitions first, followed by source definitions.

A valid source definition replaces a cached definition with the same `id`.

Avoid duplicate IDs across packs.

> To disable a module, keep its definition and set `enabled` to `false`. Deleting only the source file may leave an older definition in the cache.

### Module Priority

Higher values take precedence, with different execution rules for biome selection and block generation:

* **Biome selection:** modules are consulted from highest to lowest priority. The first resolvable biome result is used.
* **Terrain, surface, and cave generation:** lower-priority modules execute first within each phase; higher-priority modules write afterward.

Priority does not automatically merge geometries or prevent overlapping modifications.

A later generation phase may also modify blocks written during an earlier phase.

Use distinct priorities when ordering matters.

### Included Type: Volcanic Caldera

The built-in volcanic implementation uses:

```
maskycore:volcanic_caldera
```

Example definition using the expansion datapack’s biome IDs:

```json
{
  "id": "masky_biomes_expansion:sulfur_caldera",
  "type": "maskycore:volcanic_caldera",
  "enabled": true,
  "priority": 100,
  "biomes": {
    "crater": "masky_biomes_expansion:sulfur_caldera/caldera",
    "fumarolic_fields": "masky_biomes_expansion:sulfur_caldera/fumarolic_fields",
    "ash_slopes": "masky_biomes_expansion:sulfur_caldera/ash_slopes",
    "lava_tubes": "masky_biomes_expansion:sulfur_caldera/lava_tubes",
    "allowed_vanilla_biomes": [
      "minecraft:desert",
      "minecraft:badlands",
      "minecraft:eroded_badlands",
      "minecraft:savanna",
      "minecraft:savanna_plateau"
    ]
  },
  "placement": {
    "spacing": 3072,
    "separation": 1100,
    "salt": 26100262,
    "rarity": 0.38,
    "sample_radius": 128,
    "required_allowed_samples": 3
  },
  "terrain": {
    "radius_min": 280.0,
    "radius_max": 440.0,
    "base_height": 72,
    "rim_height": 92.0,
    "crater_depth": 32.0,
    "crater_radius_ratio": 0.34,
    "blend_width_ratio": 0.24,
    "lava_level": 58
  }
}
```

The referenced biome resources must exist in the datapack and be registered during startup.

#### Configuration Sections

| Section     | Purpose                                                               |
| ----------- | --------------------------------------------------------------------- |
| `biomes`    | Select biome IDs and allowed placement biomes                         |
| `placement` | Configure candidate spacing, rarity, sampling, and seed variation     |
| `terrain`   | Configure size, elevation, crater shape, deformation, and transitions |
| `surface`   | Configure foundation blocks, palettes, and materials                  |
| `tubes`     | Configure lava tube generation                                        |
| `fumaroles` | Configure vent generation                                             |

`rarity` is a probability per candidate grid cell. It is not the percentage of the world occupied by the biome.

Placement eligibility samples nine positions around the candidate center. `required_allowed_samples` specifies how many must match the allowed biome list.

An empty `allowed_vanilla_biomes` list removes that restriction.

These fields belong to the volcanic implementation. Other module types may define different settings.

### Generator Blocks and Palettes

The volcanic module accepts vanilla materials as strings:

```
{
  "foundation": "minecraft:basalt"
}
```

Custom materials can specify a fallback:

```json
{
  "foundation": {
    "id": "elitefantasy:custom_rock",
    "fallback": "minecraft:basalt"
  }
}
```

Palettes support positive relative weights:

```json
{
  "ash": [
    {
      "block": {
        "id": "elitefantasy:ashes_block",
        "fallback": "minecraft:tuff"
      },
      "weight": 7
    },
    {
      "block": "minecraft:basalt",
      "weight": 3
    }
  ]
}
```

Place these fields inside the definition’s `surface` object.

Weights control relative selection. They do not guarantee an exact block percentage in each chunk.

#### Generator IDs Versus Structure States

The generator resolves a CraftEngine block by its ID and uses its **default state**:

```
elitefantasy:custom_rock
```

The portable NBT syntax is different:

```
craftengine:elitefantasy:termite_nest[stage=empty]
```

The generator’s current block resolver does not interpret that portable state syntax.

Per-block worldgen fallbacks are separate from `datapacks.fallback-block`, which belongs to structure compilation.

Use a consistent fallback for each custom ID because generator block resolution is cached by ID.

### Developer API

A new implementation supplies a `WorldgenModuleFactory`.

The factory receives:

* `WorldgenDefinition`: common fields, complete JSON configuration, and source path.
* `WorldgenFactoryContext`: block resolver and logger.

It parses and validates its configuration before returning a module.

Example integration, assuming `CanyonModule` has been implemented:

```java
class CanyonModuleFactory : WorldgenModuleFactory {
    override val type = "myplugin:canyon"

    override fun create(
        definition: WorldgenDefinition,
        context: WorldgenFactoryContext
    ): WorldgenModule {
        return CanyonModule(definition, context)
    }
}
```

This example shows the extension point. It does not implement a canyon algorithm.

#### Available Interfaces

| Interface                  | Responsibility                                          |
| -------------------------- | ------------------------------------------------------- |
| `WorldgenModule`           | Identity, priority, managed biomes, and biome selection |
| `NoiseWorldgenModule`      | Base terrain modifications                              |
| `SurfaceWorldgenModule`    | Surface modifications                                   |
| `CaveWorldgenModule`       | Cave modifications                                      |
| `BlockStateResolver`       | Material resolution                                     |
| `WorldgenDefinitionSource` | Definition loading                                      |

Modules implement only the generation phases they need.

`biomeIdAt` should return `null` outside the module’s area of influence. A module that does not assign biomes can return `null` everywhere and declare an empty `managedBiomeIds` set.

`legacyBiomeFallbackId` optionally replaces managed biome results from the underlying distribution outside the module’s area. Use `null` when this behavior is unnecessary.

Generation callbacks receive a `WorldgenChunkContext` containing world information, chunk coordinates, `ChunkData`, and the block resolver.

Implementations should:

* Restrict writes to the supplied chunk and world height limits.
* Produce consistent results for the same seed and coordinates.
* Handle concurrent generation calls safely.
* Avoid depending on chunk generation order.

#### Registering an Included Module

Add the factory’s fully qualified class name to:

```
src/main/resources/META-INF/services/io.elitefantasy.maskyCore.worldgen.api.WorldgenModuleFactory
```

Example entry:

```
my.package.CanyonModuleFactory
```

Keep existing provider entries. Factories discovered through Java SPI must be
