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.jsonfiles 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
- Registration Phase: During mod setup, each mod calls
WorldGenOverlayHelper.registerOverlay()to register their overlay resources - Provider Registration: The first mod to load registers
ModOresWorldGenProviderto replaceHytaleWorldGenProvider - Worldgen Loading: When a world is created/loaded,
ModOresWorldGenProvider.getGenerator()is called - Overlay Preparation: The provider:
- Copies the base worldgen folder to a temporary directory
- Applies all registered overlays from the registry
- Copies ore files (
OreName.jsonandOreNamePlacement.json) toOres/ZoneX/folders - Automatically merges ore placement references into
Caves.jsonfiles for all relevant zones - Loads the merged worldgen configuration
- 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.jsonfile - 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 theTypesarray inCaves.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.jsonfiles required
Usage
WorldGenOverlayLib supports two ways to add ores:
- Data-Driven (Recommended for Resource Packs): Just JSON files - no Java code needed!
- 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 overlayGenerator(required): World generator name (usually"Default")Ores(required): Array of ore names (e.g.,["Coal", "Diamond"]) - all ores share the same zonesZones(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-levelZonesfield is required - When using the object format, each ore must have its own
Zonesarray (top-levelZonesis 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:
OreName.json: Defines the ore node structure (veins, spreads, etc.)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.jsonto 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.jsonfiles 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 (typicallygetClass().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
HytaleWorldGenProviderfor compatibility - Fields:
Name(String),Path(String, optional)
Methods:
getGenerator(): Returns anIWorldGeninstance 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.jsonandCoalPlacement.jsonfiles for all zones - Places files in
Ores/ZoneX/structure - No Java code required - completely JSON-based
- The library automatically merges placement references into
Caves.jsonfiles
Troubleshooting
My ores aren't spawning
- Check ore files exist: Verify
OreName.jsonandOreNamePlacement.jsonare inOres/ZoneX/folders - Verify Entry.File reference: In
OreNamePlacement.json, ensureEntry.Filematches the pattern"Ores.ZoneX.OreName"(e.g.,"Ores.Zone1.Coal") - 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
- Verify zone numbers: Ensure you're using ore zone numbers (
Zone0,Zone1, etc.) not zone folder names (Zone1_Tier1) in your overlay config - Check logs: Look for warnings about missing resources, merge failures, or zone detection issues
- Verify provider registration: Check that
ModOresWorldGenProvideris registered (check logs) - Data-driven config: If using the data-driven approach, verify:
Server/WorldGenOverlays/overlays.jsonexists in your resource pack- The JSON is valid (check for syntax errors)
Oresarray 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.jsonandOres/ZoneX/OreNamePlacement.json)
Multiple mods conflict
- Ensure all mods are using
WorldGenOverlayLiband callingregisterOverlay() - 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
WorldGenOverlayLibis 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 inCaves.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
