Background

WorldGenOverlayLib

ModsWorldGenOverlayLib
WorldGenOverlayLib

WorldGenOverlayLib

CurseForge
LibraryWorld Gen

A library mod for Hytale that provides shared worldgen overlay functionality, allowing multiple mods to coexist and merge their ore generation without conflicts.

WorldGenOverlayLib

A library mod for Hytale that provides shared worldgen overlay functionality, allowing multiple mods to coexist and merge their ore generation without conflicts.

Supports Hytale's new ore system - Add ores using the Ores/ZoneX/ structure with automatic Caves.json merging. No Java code required!

Overview

WorldGenOverlayLib solves a critical problem in Hytale modding: how to add custom ores to world generation without conflicts between multiple mods. Instead of each mod trying to replace the entire worldgen provider, this library provides a shared registry system where multiple mods can register their ore overlays, and they all get merged together automatically.

Features

  • Shared Overlay Registry: Multiple mods can register their worldgen overlays in a centralized registry
  • Automatic Merging: All registered overlays are automatically merged when worldgen loads
  • Automatic Caves.json Merging: Automatically adds ore placement references to Caves.json files for all relevant zones
  • Provider Management: Smart provider registration ensures only one provider instance is needed, even with multiple mods
  • Mod Compatibility: Multiple ore mods can coexist without conflicts or overwriting each other
  • Data-Driven: Add ores using only JSON files - no Java code required! Perfect for resource packs
  • New Ore System Support: Fully supports Hytale's new ore system with Ores/ZoneX/ structure

How It Works

Architecture

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   CoalMod   │     │   IronMod    │     │   GoldMod   │
└──────┬──────┘     └──────┬──────┘     └──────┬──────┘
       │                   │                   │
       │  registerOverlay()│  registerOverlay()│  registerOverlay()
       └───────────────────┴───────────────────┘
                          │
                          ▼
              ┌───────────────────────┐
              │ WorldGenOverlayHelper │
              │   OVERLAY_REGISTRY    │
              └───────────┬───────────┘
                          │
                          ▼
              ┌───────────────────────┐
              │ModOresWorldGenProvider│
              │   (extends Hytale)    │
              └───────────┬───────────┘
                          │
                          ▼
              ┌───────────────────────┐
              │   Merged WorldGen     │
              │  (Base + All Mods)    │
              └───────────────────────┘

Workflow

  1. Registration Phase: During mod setup, each mod calls WorldGenOverlayHelper.registerOverlay() to register their overlay resources
  2. Provider Registration: The first mod to load registers ModOresWorldGenProvider to replace HytaleWorldGenProvider
  3. Worldgen Loading: When a world is created/loaded, ModOresWorldGenProvider.getGenerator() is called
  4. Overlay Preparation: The provider:
    • Copies the base worldgen folder to a temporary directory
    • Applies all registered overlays from the registry
    • Copies ore files (OreName.json and OreNamePlacement.json) to Ores/ZoneX/ folders
    • Automatically merges ore placement references into Caves.json files for all relevant zones
    • Loads the merged worldgen configuration
  5. Result: A worldgen configuration that includes both base ores and all mod-added ores

Ore Placement Merging

The library automatically merges ore placement references into Caves.json files:

  • For each ore zone (e.g., Zone1), finds all zone folders that use that zone's ores
  • Checks each zone folder's Cave/Caves.json file
  • If the file references ores from that zone (e.g., contains "Ores.Zone1.CopperPlacement"), adds the new ore placement reference
  • Adds {"File": "Ores.Zone1.YourOrePlacement"} to the Types array in Caves.json
  • Ensures no duplicate entries (skips if already present)

This ensures that:

  • Base ores are preserved
  • Mod ores are automatically registered in all relevant zones
  • Multiple mods can add ores to the same zone without conflicts
  • No manual editing of Caves.json files required

Usage

WorldGenOverlayLib supports two ways to add ores:

  1. Data-Driven (Recommended for Resource Packs): Just JSON files - no Java code needed!
  2. Programmatic (For Java Mods): Register overlays from Java code

Data-Driven Approach (No Java Code Required!)

Perfect for resource packs! You can add ores to worldgen using only JSON configuration files.

Step 1: Create Overlay Configuration

Create a file at Server/WorldGenOverlays/overlays.json in your resource pack:

{
  "Overlays": [
    {
      "Name": "My Ores Overlay",
      "Generator": "Default",
      "Ores": ["Coal", "Diamond", "Ruby"],
      "Zones": ["Zone0", "Zone1", "Zone2", "Zone3", "Zone4"]
    }
  ]
}

Configuration Fields:

  • Name (optional): Display name for the overlay
  • Generator (required): World generator name (usually "Default")
  • Ores (required): Array of ore names (e.g., ["Coal", "Diamond"]) - all ores share the same zones
  • Zones (required): Array of ore zone numbers ("Zone0" through "Zone4") - applies to all ores

Alternative Format (Per-Ore Zones):

If you need different zones for different ores, use the object format. In this format, each ore specifies its own zones (top-level Zones is optional):

{
  "Overlays": [
    {
      "Name": "My Ores Overlay",
      "Generator": "Default",
      "Ores": [
        {
          "Name": "Coal",
          "Zones": ["Zone0", "Zone1", "Zone2"]
        },
        {
          "Name": "Diamond",
          "Zones": ["Zone3", "Zone4"]
        }
      ]
    }
  ]
}

Note:

  • When using the simple array format ("Ores": ["Coal", "Diamond"]), the top-level Zones field is required
  • When using the object format, each ore must have its own Zones array (top-level Zones is optional and used as fallback)

Step 2: Create Your Ore Files

Create two JSON files for each ore and each zone you want your ore to spawn in:

  1. OreName.json: Defines the ore node structure (veins, spreads, etc.)
  2. OreNamePlacement.json: Defines placement configuration (block masks, density, height, etc.)

Place them in the Ores/ZoneX/ structure:

src/main/resources/
└── Server/
    ├── WorldGenOverlays/
    │   └── overlays.json          (overlay configuration)
    └── World/
        └── Default/
            └── Ores/
                ├── Zone0/
                │   ├── Coal.json
                │   ├── CoalPlacement.json
                │   ├── Diamond.json
                │   └── DiamondPlacement.json
                ├── Zone1/
                │   ├── Coal.json
                │   ├── CoalPlacement.json
                │   ├── Diamond.json
                │   └── DiamondPlacement.json
                └── ...

Step 3: Install WorldGenOverlayLib

Add WorldGenOverlayLib as a dependency in your manifest.json:

{
  "Dependencies": {
    "Hexvane:WorldGenOverlayLib": "1.0.0",
    "Hytale:WorldGen": "*"
  }
}

That's it! The AutoOverlayPlugin will automatically discover and register your overlay when the server starts.

No Java code needed! Just JSON files.

Note: For the data-driven approach to work, your resource pack needs to be loaded as a mod (with a manifest.json that depends on WorldGenOverlayLib). Pure resource packs (without a manifest) cannot be discovered automatically. If you're creating a pure resource pack, you'll need to either:

  • Add a minimal manifest.json to make it a mod, or
  • Use the programmatic approach from a Java mod

Example: Multiple Ores in One Overlay

You can define multiple ores in a single overlay configuration - they'll all share the same zones:

{
  "Overlays": [
    {
      "Name": "Common Ores Overlay",
      "Generator": "Default",
      "Ores": ["Coal", "Iron", "Copper"],
      "Zones": ["Zone0", "Zone1", "Zone2"]
    }
  ]
}

Or use per-ore zones for more control:

{
  "Overlays": [
    {
      "Name": "All Ores Overlay",
      "Generator": "Default",
      "Ores": [
        {
          "Name": "Coal",
          "Zones": ["Zone0", "Zone1", "Zone2"]
        },
        {
          "Name": "Diamond",
          "Zones": ["Zone3", "Zone4"]
        }
      ]
    }
  ]
}

Each overlay will automatically:

  • Copy all ore files to the appropriate Ores/ZoneX/ folders
  • Merge placement references into all relevant Caves.json files for each ore

Programmatic Approach (For Advanced Java Mods)

Note: The data-driven approach is recommended for most use cases. Use the programmatic approach only if you need dynamic ore generation or other advanced features.

If you're writing a Java mod and want to register overlays programmatically:

Step 1: Add as Dependency

Add WorldGenOverlayLib as a dependency in your mod's build.gradle.kts:

Option A: Using local file dependency (for development)

dependencies {
    val worldGenLibPath = file("${rootProject.projectDir}/../WorldGenOverlayLib/build/libs")
    if (worldGenLibPath.exists()) {
        implementation(files(worldGenLibPath.listFiles().filter { 
            it.name.endsWith(".jar") && !it.name.contains("sources") && !it.name.contains("javadoc") 
        }))
    }
}

Option B: Using published Maven version (recommended for release)

repositories {
    maven {
        name = "local"
        url = uri("${rootProject.projectDir}/../maven-repo")
    }
}

dependencies {
    implementation("com.hexvane:WorldGenOverlayLib:1.0.0")
}

Step 2: Add to Manifest

Add the library as a dependency in your manifest.json:

{
  "Dependencies": {
    "Hexvane:WorldGenOverlayLib": "1.0.0",
    "Hytale:WorldGen": "*"
  }
}

Step 3: Register Your Overlay

In your mod's setup() method, register your overlay:

import com.hexvane.worldgenoverlay.worldgen.WorldGenOverlayHelper;

public class YourOreMod extends JavaPlugin {
    private static final String RESOURCE_PREFIX = "Server/World/";
    
    @Override
    protected void setup() {
        // Build list of overlay resource paths for new ore system
        List<String> overlayPaths = buildOverlayPaths("Default", "YourOre");
        
        // Register your overlay in the shared registry
        WorldGenOverlayHelper.registerOverlay(
                getClass().getClassLoader(),
                overlayPaths,
                Collections.emptyList() // No merge prefixes needed for new system
        );
    }
    
    private static List<String> buildOverlayPaths(String generatorName, String oreName) {
        String base = RESOURCE_PREFIX + generatorName + "/Ores/";
        List<String> paths = new ArrayList<>();
        
        // Add ore files for each zone
        String[] zones = {"Zone0", "Zone1", "Zone2", "Zone3", "Zone4"};
        
        for (String zone : zones) {
            // Add both OreName.json and OreNamePlacement.json
            paths.add(base + zone + "/" + oreName + ".json");
            paths.add(base + zone + "/" + oreName + "Placement.json");
        }
        
        return paths;
    }
}

Step 4: Create Your Ore Files

Place your ore files in the new structure:

src/main/resources/
└── Server/
    └── World/
        └── Default/
            └── Ores/
                ├── Zone0/
                │   ├── YourOre.json
                │   └── YourOrePlacement.json
                ├── Zone1/
                │   ├── YourOre.json
                │   └── YourOrePlacement.json
                └── ...

The library will automatically merge placement references into Caves.json files for all relevant zones.

API Reference

WorldGenOverlayHelper

registerOverlay(ClassLoader, List<String>, List<String>)

Registers an overlay from a mod. This should be called during mod setup.

Parameters:

  • classLoader: The classloader to load overlay resources from (typically getClass().getClassLoader())
  • overlayPaths: List of full resource paths (e.g., "Server/World/Default/Zones/Zone1_Tier1/Cave/Ores/Entry.node.json")
  • mergeEntryPrefixes: List of prefixes for entries to merge (e.g., List.of("Ores.Coal."))

Example:

WorldGenOverlayHelper.registerOverlay(
    getClass().getClassLoader(),
    List.of(
        "Server/World/Default/Zones/Zone1_Tier1/Cave/Ores/Entry.node.json",
        "Server/World/Default/Zones/Zone1_Tier1/Cave/Ores/Coal/CoalSpread.node.json"
    ),
    List.of("Ores.Coal.")
);

prepareFromRegistry(Path, String, Path)

Prepares a working worldgen directory by copying base worldgen and applying all registered overlays. This is called internally by ModOresWorldGenProvider.

Parameters:

  • basePath: Base worldgen folder to copy (e.g., .../Default)
  • generatorName: Generator name used in resource paths (e.g., "Default")
  • workDir: Temporary directory to create the merged worldgen in

Returns: The workDir path (for convenience)

Throws: IOException if file operations fail

ModOresWorldGenProvider

Extends HytaleWorldGenProvider and overrides getGenerator() to apply overlays before loading worldgen.

CODEC:

  • Uses the same shape as HytaleWorldGenProvider for compatibility
  • Fields: Name (String), Path (String, optional)

Methods:

  • getGenerator(): Returns an IWorldGen instance with all overlays applied

Benefits

For Mod Developers

  • No Conflicts: Multiple ore mods can coexist without overwriting each other
  • Simple API: Just register your overlay and the library handles the rest
  • No Code Duplication: Shared library eliminates need to copy provider classes
  • Easy Integration: Minimal code changes required
  • Data-Driven Option: Add ores using only JSON files - perfect for resource packs!

For Resource Pack Creators

  • No Java Required: Add custom ores using only JSON configuration files
  • Easy to Share: Just distribute your resource pack - no compilation needed
  • Multiple Ores: Define multiple overlays in a single config file
  • Auto-Discovery: The library automatically finds and registers your overlays

For Server Owners

  • Mod Compatibility: Install multiple ore mods without conflicts
  • Flexible: Each mod can add ores to different zones independently
  • Maintainable: Library updates benefit all mods using it

Building

This is a library mod with no main entry point. Build it using:

./gradlew build

To publish to Maven repositories, see PUBLISHING.md for detailed instructions.

Example: CoalMod

See CoalMod for a complete working example of the data-driven approach:

  • Defines coal ore overlay in Server/WorldGenOverlays/overlays.json
  • Creates Coal.json and CoalPlacement.json files for all zones
  • Places files in Ores/ZoneX/ structure
  • No Java code required - completely JSON-based
  • The library automatically merges placement references into Caves.json files

Troubleshooting

My ores aren't spawning

  1. Check ore files exist: Verify OreName.json and OreNamePlacement.json are in Ores/ZoneX/ folders
  2. Verify Entry.File reference: In OreNamePlacement.json, ensure Entry.File matches the pattern "Ores.ZoneX.OreName" (e.g., "Ores.Zone1.Coal")
  3. Check Caves.json merging: Look for log messages like "Merged 'Ores.Zone1.CoalPlacement' into Caves.json" - if you see "0 Caves.json files", the zone detection isn't working
  4. Verify zone numbers: Ensure you're using ore zone numbers (Zone0, Zone1, etc.) not zone folder names (Zone1_Tier1) in your overlay config
  5. Check logs: Look for warnings about missing resources, merge failures, or zone detection issues
  6. Verify provider registration: Check that ModOresWorldGenProvider is registered (check logs)
  7. Data-driven config: If using the data-driven approach, verify:
    • Server/WorldGenOverlays/overlays.json exists in your resource pack
    • The JSON is valid (check for syntax errors)
    • Ores array contains your ore names (must match your JSON file names exactly)
    • Zone numbers are correct (Zone0-Zone4)
    • Ore files are in the correct location (Ores/ZoneX/OreName.json and Ores/ZoneX/OreNamePlacement.json)

Multiple mods conflict

  • Ensure all mods are using WorldGenOverlayLib and calling registerOverlay()
  • Only one mod should register the provider (the library handles this automatically)
  • Check that all mods have the library as a dependency in their manifest

Build errors

  • Ensure WorldGenOverlayLib is built before building mods that depend on it
  • Check that the library JAR is in the expected location
  • Verify dependency configuration in build.gradle.kts

Compatibility

  • Hytale Version: Compatible with latest Hytale server (uses new ore system with Ores/ZoneX/ structure)
  • WorldGen V1: Designed for WorldGen V1 (not V2/HytaleGenerator)
  • Java: Requires Java 25 (as per Hytale modding requirements)
  • Ore System: Uses Hytale's new ore system (as of latest update) - ores are defined in Ores/ZoneX/ folders and referenced in Caves.json

License

See LICENSE file for details.

Contributing

When contributing, please ensure:

  • Code follows the existing style
  • All API methods are properly documented
  • Tests are added for new functionality
  • README is updated for API changes
WorldGenOverlayLib - Hytale Mod | Hytale Wiki