Hytale World Generation System
Comprehensive reference for Hytale's procedural world generation systems used to create environments, biomes, and structures dynamically as players explore the world.
Related skills: For ECS fundamentals, see
hytale-ecs. For persistent data/Codec patterns, seehytale-persistent-data. For entity effects, seehytale-entity-effects.
Official worldgen docs recently removed dedicated directionality coverage. Any remaining directionality notes in this skill are preserved as legacy references and should be re-verified against current server source before relying on them.
Quick Reference
| Task | Approach |
|---|---|
| Get zone at position | zoneGen.generate(seed, x, z) returns ZoneGeneratorResult |
| Get zone from result | zoneResult.getZone() returns Zone |
| Get biome in zone | zone.biomePatternGenerator().generateBiomeAt(zoneResult, seed, x, z) |
| Get cave generator | zone.caveGenerator() returns CaveGenerator (nullable) |
| Get cave types | caveGen.getCaveTypes() returns CaveType[] |
| Check biome cave mask | CaveBiomeMaskFlags.canGenerate(flags) |
| Get border distance | zoneResult.getBorderDistance() |
| Fade near border | customBiome.getFadeContainer().getMaskFactor(result) |
| Create zone discovery | new ZoneDiscoveryConfig(...) |
| Create biome pattern | new BiomePatternGenerator(points, tileBiomes, customBiomes) |
| Create cave config | new CaveGenerator(caveTypes) |
| Create custom zone | new Zone(id, name, discovery, caveGen, biomeGen, prefabs) |
Overview
Hytale's world generation is organized into three interconnected systems:
World Generation
├── Zones — Large-scale regions defining overall world structure
├── Biomes — Terrain characteristics and environment within zones
└── Caves — Underground structures and networks within zones
These systems are hierarchical: Zones contain Biomes, and both Zones and Biomes influence Cave generation. They work together to produce smooth transitions and coherent regional themes.
Zones
Zones are the largest-scale division in world generation. Each zone defines its own biome patterns, cave configurations, and unique structures (prefabs).
Zone Lookup and Generation
// Get zone at a world position
ZonePatternGenerator zoneGen = /* from world generator */;
ZoneGeneratorResult zoneResult = zoneGen.generate(seed, x, z);
Zone zone = zoneResult.getZone();
ZoneGeneratorResult
The result object provides:
| Method | Returns | Description |
|---|---|---|
getZone() |
Zone |
The zone at the queried position |
getBorderDistance() |
double |
Distance to the nearest zone border |
Zone Class
A Zone encapsulates all generation data for a region:
| Property | Type | Description |
|---|---|---|
| ID | int |
Unique numeric identifier |
| Name | String |
Internal name (e.g., "new_custom_zone") |
| Discovery | ZoneDiscoveryConfig |
Player notification on zone entry |
| Cave Generator | CaveGenerator |
Cave configuration (nullable) |
| Biome Pattern | BiomePatternGenerator |
Biome layout within the zone |
| Unique Prefabs | — | Unique structures placed in the zone |
Creating a Custom Zone
// 1. Configure zone discovery (player notification on entry)
ZoneDiscoveryConfig discovery = new ZoneDiscoveryConfig(
true, // Show notification
"Custom Zone", // Display name
"zone.forest.discover", // Sound event
"icons/forest.png", // Icon
true, // Major zone
5.0f, 2.0f, 1.5f // Duration, fade in, fade out
);
// 2. Create biome pattern
IPointGenerator biomePoints;
IWeightedMap<TileBiome> tileBiomes;
CustomBiome[] customBiomes;
BiomePatternGenerator biomeGen = new BiomePatternGenerator(
biomePoints,
tileBiomes,
customBiomes
);
// 3. Create cave configuration
CaveType[] caveTypes; // cave definitions
CaveGenerator caveGen = new CaveGenerator(caveTypes);
// 4. Assemble the zone
Zone customZone = new Zone(
100, // Unique ID
"new_custom_zone", // Internal name
discovery, // Discovery config
caveGen, // Cave generator
biomeGen, // Biome pattern
uniquePrefabs // Unique structures
);
ZoneDiscoveryConfig
Controls what the player sees when entering a zone:
| Parameter | Type | Description |
|---|---|---|
showNotification |
boolean |
Whether to display the zone entry notification |
displayName |
String |
Name shown to the player |
soundEvent |
String |
Sound played on zone entry |
icon |
String |
Icon path for the notification |
majorZone |
boolean |
Whether this is a major zone (affects display) |
duration |
float |
How long the notification is visible (seconds) |
fadeIn |
float |
Fade-in duration (seconds) |
fadeOut |
float |
Fade-out duration (seconds) |
Biomes
Biomes define terrain characteristics, vegetation, and environmental properties within a zone. Each zone has its own BiomePatternGenerator that determines biome layout.
Biome Lookup
// Get the biome at a specific position within a zone
Zone zone = zoneResult.getZone();
BiomePatternGenerator biomeGen = zone.biomePatternGenerator();
Biome biome = biomeGen.generateBiomeAt(zoneResult, seed, x, z);
BiomePatternGenerator
Constructs the biome layout from three inputs:
| Parameter | Type | Description |
|---|---|---|
biomePoints |
IPointGenerator |
Point distribution controlling biome placement |
tileBiomes |
IWeightedMap<TileBiome> |
Weighted map of tile-level biome data |
customBiomes |
CustomBiome[] |
Custom biome definitions with fade/transition settings |
Biome Properties
Each Biome has at minimum:
| Method | Returns | Description |
|---|---|---|
getId() |
int |
Unique biome identifier |
CustomBiome Fading
Custom biomes support smooth transitions near zone borders via a fade container:
CustomBiome customBiome = /* ... */;
FadeContainer fade = customBiome.getFadeContainer();
double maskFadeSum = fade.getMaskFadeSum();
double factor = fade.getMaskFactor(zoneResult);
Caves
Caves are underground structures generated within zones. Each zone optionally has a CaveGenerator with one or more CaveType definitions.
Cave Lookup
Zone zone = /* ... */;
CaveGenerator caveGen = zone.caveGenerator();
if (caveGen != null) {
// This zone has caves
CaveType[] types = caveGen.getCaveTypes();
}
CaveType and Biome Masks
Cave types use biome masks to control where caves can generate:
// Cave type with biome restrictions
Int2FlagsCondition biomeMask = caveType.getBiomeMask();
int biomeId = biome.getId();
int flags = biomeMask.eval(biomeId);
// Check if cave can generate in this biome
if (CaveBiomeMaskFlags.canGenerate(flags)) {
// Generate cave
}
| Class | Purpose |
|---|---|
CaveGenerator |
Holds cave type definitions for a zone |
CaveType |
Individual cave definition with shape, biome mask, etc. |
Int2FlagsCondition |
Evaluates biome ID → flags for cave placement |
CaveBiomeMaskFlags |
Utility to interpret biome mask flags (e.g., canGenerate) |
System Integration
Zone → Biome Integration
Each zone defines its own biome pattern. The biome generator requires the zone result (for border distance and zone context):
Zone zone = zoneGen.generate(seed, x, z).getZone();
BiomePatternGenerator biomeGen = zone.biomePatternGenerator();
Biome biome = biomeGen.generateBiomeAt(zoneResult, seed, x, z);
Zone → Cave Integration
Each zone defines its own cave patterns. Not all zones have caves:
Zone zone = /* ... */;
CaveGenerator caveGen = zone.caveGenerator();
if (caveGen != null) {
CaveType[] types = caveGen.getCaveTypes();
}
Biome → Cave Integration
Caves use biome masks to restrict generation to specific biomes:
Int2FlagsCondition biomeMask = caveType.getBiomeMask();
int biomeId = biome.getId();
int flags = biomeMask.eval(biomeId);
if (CaveBiomeMaskFlags.canGenerate(flags)) {
// Cave can generate in this biome
}
Border Transitions
All systems respect zone boundaries. Custom biomes fade near borders for smooth transitions:
ZoneGeneratorResult result = zoneGen.generate(seed, x, z);
double borderDistance = result.getBorderDistance();
// Fade custom biomes near borders
if (borderDistance < customBiome.getFadeContainer().getMaskFadeSum()) {
double factor = customBiome.getFadeContainer().getMaskFactor(result);
// Apply fading based on factor (0.0 = fully faded, 1.0 = full strength)
}
Key Classes Summary
| Class | Package Area | Purpose |
|---|---|---|
ZonePatternGenerator |
worldgen | Generates zones at world positions |
ZoneGeneratorResult |
worldgen | Result of zone lookup (zone + border distance) |
Zone |
worldgen | Zone definition (biomes, caves, prefabs, discovery) |
ZoneDiscoveryConfig |
worldgen | Player notification on zone entry |
BiomePatternGenerator |
worldgen | Biome layout within a zone |
Biome |
worldgen | Biome data (ID, properties) |
CustomBiome |
worldgen | Custom biome with fade/transition support |
TileBiome |
worldgen | Tile-level biome data |
IPointGenerator |
worldgen | Point distribution for biome placement |
IWeightedMap<T> |
worldgen | Weighted map for biome selection |
CaveGenerator |
worldgen | Cave configuration for a zone |
CaveType |
worldgen | Individual cave type definition |
Int2FlagsCondition |
worldgen | Biome mask evaluator for caves |
CaveBiomeMaskFlags |
worldgen | Flag utility for cave biome masks |
FadeContainer |
worldgen | Border fade/transition controller |
Common Patterns
Full World Position → Zone + Biome + Cave Pipeline
// Complete lookup pipeline
ZonePatternGenerator zoneGen = /* from world generator */;
long seed = /* world seed */;
int x = /* world x */;
int z = /* world z */;
// 1. Determine zone
ZoneGeneratorResult zoneResult = zoneGen.generate(seed, x, z);
Zone zone = zoneResult.getZone();
// 2. Determine biome within zone
BiomePatternGenerator biomeGen = zone.biomePatternGenerator();
Biome biome = biomeGen.generateBiomeAt(zoneResult, seed, x, z);
// 3. Check for caves
CaveGenerator caveGen = zone.caveGenerator();
if (caveGen != null) {
for (CaveType caveType : caveGen.getCaveTypes()) {
Int2FlagsCondition biomeMask = caveType.getBiomeMask();
int flags = biomeMask.eval(biome.getId());
if (CaveBiomeMaskFlags.canGenerate(flags)) {
// This cave type can generate here
}
}
}
// 4. Handle border transitions
double borderDistance = zoneResult.getBorderDistance();
// Use borderDistance for custom blending/fading logic
Zone Entry Notification Setup
// Minimal zone discovery config
ZoneDiscoveryConfig discovery = new ZoneDiscoveryConfig(
true, // Show notification
"Enchanted Forest", // Display name
"zone.forest.discover", // Sound event
"icons/forest.png", // Icon
true, // Major zone
5.0f, 2.0f, 1.5f // Duration, fade in, fade out
);
Data-Driven World Generation (Node/JSON System)
Beyond the Java plugin API, Hytale's world generation is primarily data-driven through JSON asset files and an in-game Node Editor. Biomes, terrain shapes, materials, and content placement are all defined declaratively.
Detailed references for every node type are in the
references/subdirectory of this skill.
Asset Directory Structure
Server/HytaleGenerator/
├── WorldStructure/ # Generator files defining which biomes spawn together
├── Biomes/ # Biome assets with content configurations
├── Density/ # Reusable density assets referenced by other generation assets
└── Assignments/ # Reusable prop assignment assets referenced by biomes
World Instance Configuration
Instances are separate worlds that players can join. Each instance has an Instance.bson that specifies which generator to use:
{
"WorldGen": {
"Type": "HytaleGenerator",
"WorldStructure": "Basic",
"playerSpawn": {
"X": 123, "Y": 480, "Z": 10000,
"Pitch": 0, "Yaw": 0, "Roll": 0
}
}
}
Instance configs are stored at Server/Instances/. Create custom instances by duplicating the basic config and changing WorldStructure to your asset name.
Previewing World Generation
| Command | Purpose |
|---|---|
/viewport --radius 5 |
Live-reload area around player as you edit |
/instances spawn <instance> |
Create a new world with latest changes |
| Fly south | New chunks generate with latest changes |
Biome Asset Structure
Every biome has 5 root components:
| Component | Purpose |
|---|---|
| Terrain | Density function nodes defining the physical terrain shape |
| Material Provider | Logic nodes determining which block types make up terrain |
| Props | Object nodes placing prefabs, trees, POIs, grass, etc. |
| Environment Provider | Logic nodes determining weather, NPC spawns, sounds per coordinate |
| Tint Provider | Function nodes determining color codes for grasses, soils, etc. |
Each biome is self-contained — it controls everything within its boundaries except Base Heights.
Node System Overview
World generation uses a node-based system of composable JSON assets. Each node type has parameters and inputs:
| Node Category | Purpose | Key Types |
|---|---|---|
| Density | 3D decimal value fields for terrain shape | SimplexNoise2D/3D, PositionsCellNoise, Distance, Ellipsoid, Cube, Cylinder, Sum, Multiplier, Mix, CurveMapper, Scale, Rotator, Warp nodes |
| Curves | f(x)=y mappings for value transformation | Manual, DistanceExponential, DistanceS, Ceiling/Floor, SmoothClamp, Clamp, Inverter, Min/Max |
| Patterns | Validate world positions by material/structure | BlockType, BlockSet, Floor, Ceiling, Wall, Surface, Gap, Cuboid, And/Or/Not, FieldFunction |
| Material Providers | Determine block types at positions | Constant, Solidity, Queue, SimpleHorizontal, Striped, Weighted, FieldFunction, SpaceAndDepth (with Layers and Conditions) |
| Positions Provider | Define infinite 3D position fields | Mesh2D, Mesh3D, List, FieldFunction, Occurrence, BaseHeight, Cache |
| Scanners | Scan local world areas for valid positions | Origin, ColumnLinear, ColumnRandom, Area |
| Props | Localized content placed in the world | Box, Density, Prefab, Column, Cluster, Union, Weighted, Queue, PondFiller |
| Assignments | Assign props to positions | Constant, FieldFunction, Sandwich, Weighted |
| Legacy Directionality | Older prop-facing nodes preserved as reference only | Static, Random, Pattern-based |
| Vector Provider | Define 3D vectors procedurally | Constant, DensityGradient, Cache |
| Block Mask | Control which materials can replace others | DontPlace, DontReplace, Advanced rules |
Core Concept: Density Fields
Terrain generation is built on density fields — maps of decimal values (typically -1 to 1) defining terrain shape:
- Positive values → solid terrain
- Negative values → empty space (air)
- Density is calculated from the sum of all nodes:
f(x) = z + y
Common terrain formula: Simplex 2D noise + Y-Curve (height gradient) = basic terrain shape.
Function nodes manipulate density fields: Absolute (ridged shapes), Normalization (rescaling range), Scale (stretch/contract), Rotator (orientation), Warp (distortion), and many more.
Core Concept: Props Pipeline
Props place procedural content (trees, prefabs, grass) using three connected systems:
Positions → Scanner → Pattern → Prop Placement
- Positions provide candidate locations (e.g., Mesh2D grid, BaseHeight offsets)
- Scanner searches around each position (e.g., ColumnLinear scans Y range)
- Pattern validates positions (e.g., Floor pattern checks for solid ground below)
- Prop places content at validated positions (e.g., Prefab placement; older docs also referenced Directionality for facing)
Asset Packs
Asset Packs override or add assets to the base game. Required for world gen customization:
{
"Group": "My Group",
"Name": "Pack Example",
"Version": "1.0.0",
"Description": "An Example Asset Pack",
"Authors": [{"Name": "Me", "Email": "", "Url": ""}],
"Dependencies": {},
"DisabledByDefault": false,
"IncludesAssetPack": false,
"SubPlugins": []
}
- Stored in
C:\Users\<user>\AppData\Roaming\Hytale\UserData\Mods - Per-world packs stored in
UserData\Saves\<world>\mods - Asset Packs exist entirely on the server — only the host needs them
- Assets inside packs override base game assets
Import/Export System
Most node types support Imported and Exported nodes for reusability:
- Exported: Makes a node tree available by name, with optional
SingleInstancefor shared caching - Imported: References an exported asset by name
- This enables modular, reusable density fields, curves, patterns, etc.
Notes
- The
worldgenmodule is a built-in Hytale module. Plugins interact with it through the public API classes listed above. - Zone IDs must be unique across the world generation configuration.
CaveGeneratoris nullable — not all zones have caves.- Biome masks use a flags-based system (
Int2FlagsCondition) for efficient cave-biome filtering. - Border transitions use
FadeContainerwithgetMaskFadeSum()andgetMaskFactor()for smooth blending between zones. - The
--validate-world-genserver launch flag can be used to validate world generation assets. - World gen examples can be found in
HytaleGenerator/Biomes/Examples/. - The Hytale Node Editor is accessible in-game from the Content Creation menu (Tab key).
Performance Optimization
World-gen prints a performance report to server logs at configurable sample-size intervals. Open it: right-click world in world selection → open world folder → logs/latest. Use reports with Sample Count ≥ 500 for accuracy.
Key metrics to watch: Content Generation: # ms — the time you have the most control over. Each stage (BiomeStage, TerrainStage, PropStages, TintStage, EnvironmentStage) is listed separately.
Density / Terrain
| Technique | Detail |
|---|---|
| Use Mix to mask expensive Density | Limit costly density fields to only the regions that need them. Replace areas above max terrain height with Constant(-1) and all-underground with Constant(1). |
| Use YSampled | Reduces vertical sample count for density fields where full resolution isn't visually needed. Recommended SampleDistance: 8 for best performance, 2 for high-detail areas. Mix sampled and non-sampled versions for precision only where needed. |
| Cache nodes | Add Cache on the right side of YOverride nodes, and at the root of ChoiceDensity/CellValue Density. SimplexNoise2d and CellNoise2d already cache automatically. |
| Use Mix over Sum/Min/Max | Mix disables fields where they aren't needed; Sum/Min/Max always evaluate all inputs. |
| Avoid unnecessary Density gradients | Gradients resolve the child 4x per position. Mask them out. Inside Material Providers, use the TerrainDensity node (reuses existing buffers) rather than importing the terrain root density. |
| 2D where possible | Use 3D density only when required. 3D domain warping invalidates 2D caches from all child nodes. |
| WorldStructure tuning | Don't set DefaultTransitionDistance and MaxBiomeEdgeDistance higher than necessary — they affect BiomeStage and TerrainStage output sizes. |
Materials
- Put expensive Material Providers as far right in the tree as possible; cheap limiters go left.
SimpleHorizontalreduces the height range for its children.SpaceAndDepthreduces the depth range for its children.- Use
FieldFunctionwith a cheap density mask to limit an expensive Material Provider to a specific world region. - Use
TerrainDensitynode inside Material Providers when referencing the terrain's density for gradients.
Props
- Keep each Runtime's horizontal size as small as possible — it's determined by the largest Prop in that Runtime.
- Put Props with the largest horizontal size at the lowest Runtime index.
- Pack as many Props into one Runtime as possible. Only create new Runtimes when Props need context from other Props.
- All Biomes in a WorldStructure should use the same Runtime indices to avoid multiplying Runtimes.
- Break large Props into smaller modular pieces assembled at the Prop Distribution level.
Quick Checklist
- Try 2D over 3D everywhere possible.
- Place
Cacheon the right side of allYOverridenodes. - Add
YSampledat the root of each Biome's Density as a first pass. - Use
Mix(notSum) to cap terrain above max height and below min height. - Ensure all Biomes share the same Runtime indices.
- Check
DefaultTransitionDistanceandMaxBiomeEdgeDistancein WorldStructure — keep them minimal.
Source: hytalemodding.dev — World Generation System Source: HytaleModding/site — Official World Generation Documentation