Skip to content

Contexts and State

Zaldaryon edited this page Aug 28, 2026 · 1 revision

Contexts and state

WorldgenLib makes state explicit so a consumer can extend world generation without copying private engine fields or sharing mutable buffers across parallel columns.

ChunkContext

ChunkContext is a class created for a terrain request. It contains:

Group Members
Coordinates ChunkX, ChunkZ, RegionX, RegionZ, ChunkSize, ChunkPixelSize, BaseX, BaseZ
Map samples Four corner samples for climate, ocean, and upheaval maps
World config SeaLevel, MapSizeY, OceanicityFactor, TaperThreshold, GeoUpheavalAmplitude
Resolved blocks RockBlockId, FreshWaterBlockId, SaltWaterBlockId, LakeIceBlockId
Terrain data TerrainYThresholds, Landforms, LandLerpMap, LandformMap, TaperMap
Runtime objects Chunks, MapChunk, MapRegion, and the original Request
Noise Distort2dX, Distort2dZ, TerrainNoise, and GeoUpheavalNoise

TaperMap is writable at the border-taper step. The other properties are the canonical inputs for the current request.

Request-local custom data

Use CustomData for communication between hooks in one terrain request. Keys should be namespaced by the owning mod:

var values = chunk.GetOrCreateCustomData(
    "my-mod:erosion-values",
    () => new float[chunk.ChunkSize * chunk.ChunkSize]);

if (chunk.TryGetCustomData<float[]>("my-mod:erosion-values", out var existing))
{
    Consume(existing!);
}

GetOrCreateCustomData protects initialization with a lock and rejects reuse of a key with a different type. The collection is safe to initialize from parallel hooks. The object stored in it is not automatically thread-safe. Partition or synchronize it yourself.

Moddata helpers

ChunkContext.SetChunkModdata writes generic data to the first generated chunk. SetMapChunkModdata writes generic data to the generated map chunk. The carving context provides the same operations after placement, including byte-array overloads.

Use moddata only for bounded, intentional persistence. Do not write one record per block. For region-scale numeric fields, use Registries-and-Sampling and its bounded RegionMapSlot format.

ColumnContext

ColumnContext is a ref struct created inside the terrain loop. It exposes:

  • coordinates: WorldX, WorldZ, LocalX, LocalZ
  • mutable spans: LandformWeights, OctaveAmplitudes, OctaveThresholds
  • derived values: UpheavalStrength, Oceanicity
  • mutable values: DistY, WaterBlockId
  • noise bounds: NoiseBoundMin, NoiseBoundMax
  • prepared ColumnNoise
  • ColumnBlockSolidities

It is valid only during the callback. Do not store the context, its spans, its BitArray, or its column noise for later use. RecalculateOctaves() is the supported way to rebuild octave arrays after changing landform weights.

ColumnCarvingContext

The Step 10 context is also a ref struct. It provides all vertical chunks and a global-Y accessor:

private static void FillBelowSeaLevel(
    ChunkContext chunk,
    ref ColumnCarvingContext column)
{
    for (int y = 1; y < column.SeaLevel; y++)
    {
        if (!column.ColumnBlockSolidities[y])
            column.SetFluid(column.LocalX, y, column.LocalZ, column.WaterBlockId);
    }
}

GetBlockDataAtY validates the global Y range and selects the vertical chunk. SetFluid validates local X/Z and converts global Y to the selected chunk's local Y. This avoids the common error of writing every Y level into the bottom chunk.

RegionContext

Map hooks receive RegionContext. It contains the region coordinates, IMapRegion, server API, current map, map noise sizes, custom-map access, generator access, and the force-request methods described in Map-Hooks. CurrentMap can be replaced by a compatible map before the host assigns it back to the region.

Threading rules

The terrain column loop is parallel. A safe consumer follows these rules:

  1. Keep per-column values in ColumnContext or local variables.
  2. Keep per-request values in ChunkContext.CustomData with consumer-owned synchronization.
  3. Keep persistent region values in a registered RegionMapSlot.
  4. Move expensive work to a region or chunk boundary.
  5. Never rely on callback arrival order across different columns.

These rules preserve deterministic output and keep the hot path close to vanilla allocation behavior.

Clone this wiki locally