<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.mutigeraffe.com/index.php?action=history&amp;feed=atom&amp;title=UtilityBiome%3ABuild-Time_Tools</id>
	<title>UtilityBiome:Build-Time Tools - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.mutigeraffe.com/index.php?action=history&amp;feed=atom&amp;title=UtilityBiome%3ABuild-Time_Tools"/>
	<link rel="alternate" type="text/html" href="https://wiki.mutigeraffe.com/index.php?title=UtilityBiome:Build-Time_Tools&amp;action=history"/>
	<updated>2026-10-20T03:36:02Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.11</generator>
	<entry>
		<id>https://wiki.mutigeraffe.com/index.php?title=UtilityBiome:Build-Time_Tools&amp;diff=493&amp;oldid=prev</id>
		<title>Maintenance script: UtilityBiome wiki</title>
		<link rel="alternate" type="text/html" href="https://wiki.mutigeraffe.com/index.php?title=UtilityBiome:Build-Time_Tools&amp;diff=493&amp;oldid=prev"/>
		<updated>2026-10-19T07:06:58Z</updated>

		<summary type="html">&lt;p&gt;UtilityBiome wiki&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;The &amp;lt;code&amp;gt;tools&amp;lt;/code&amp;gt; packages generate Hytale world-generation files (biomes, caves, ores, resources, spawns) from Java code and check every reference before a file reaches a server. They run in your Gradle build and tests, never in the game, and contain no Hytale imports. Gson comes from the server jar.&lt;br /&gt;
&lt;br /&gt;
Generated files only refer to vanilla and your own files. A mod that uses nothing else from Utility Biome does not need it at runtime, see [[UtilityBiome:Modding API#Build time only|Modding API]].&lt;br /&gt;
&lt;br /&gt;
== Gradle plugin ==&lt;br /&gt;
The plugin &amp;lt;code&amp;gt;utilitybiome.content&amp;lt;/code&amp;gt; wires everything up. It is included through the submodule (see [[UtilityBiome:Modding API|Modding API]]).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// build.gradle.kts&lt;br /&gt;
plugins { id(&amp;quot;utilitybiome.content&amp;quot;) }&lt;br /&gt;
&lt;br /&gt;
utilityBiome {&lt;br /&gt;
    prefix = &amp;quot;MyMod_&amp;quot;                                    // every generated file and ID carries it&lt;br /&gt;
    content = &amp;quot;com.example.mymod.content.MyModContent&amp;quot;    // your ContentPack&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Setting !! Default !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;prefix&amp;lt;/code&amp;gt; || required for &amp;lt;code&amp;gt;validateAssets&amp;lt;/code&amp;gt; || Mod prefix with underscore, for example &amp;lt;code&amp;gt;BetterBiomes_&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;content&amp;lt;/code&amp;gt; || required for generation || Fully qualified &amp;lt;code&amp;gt;ContentPack&amp;lt;/code&amp;gt; class in the &amp;lt;code&amp;gt;biomes&amp;lt;/code&amp;gt; source set.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;resources&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;src/main/resources&amp;lt;/code&amp;gt; || Where files are generated to and what is validated.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;devResources&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;src/dev/resources&amp;lt;/code&amp;gt; || Files only for the dev server, never in the jar.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;vanillaIndex&amp;lt;/code&amp;gt; || &amp;lt;code&amp;gt;modules/utilitybiome/vanilla-index/&amp;amp;lt;server_version&amp;amp;gt;.txt&amp;lt;/code&amp;gt; || Vanilla index for the validator.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dependencyPacks&amp;lt;/code&amp;gt; || Utility Biome&amp;#039;s own pack in the submodule || Asset packs your files may refer to.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The plugin adds the source set &amp;lt;code&amp;gt;biomes&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;src/biomes/java&amp;lt;/code&amp;gt;). It sees the server API and Utility Biome and is never packed into the jar. The tasks:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Task !! Does&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;generateBiomes&amp;lt;/code&amp;gt; || Writes the files of your &amp;lt;code&amp;gt;ContentPack&amp;lt;/code&amp;gt; into &amp;lt;code&amp;gt;resources&amp;lt;/code&amp;gt;. Check them in.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;checkGeneratedAssets&amp;lt;/code&amp;gt; || Fails if the checked-in files differ from what the content generates (hand edits or a Hytale update). Part of &amp;lt;code&amp;gt;check&amp;lt;/code&amp;gt;. Skipped when &amp;lt;code&amp;gt;Assets.zip&amp;lt;/code&amp;gt; is not available, for example in CI.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;validateAssets&amp;lt;/code&amp;gt; || Runs the asset validator over your packs. Part of &amp;lt;code&amp;gt;check&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;devAssetPack&amp;lt;/code&amp;gt; || Zips &amp;lt;code&amp;gt;resources&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;devResources&amp;lt;/code&amp;gt; for the dev server. &amp;lt;code&amp;gt;runServer&amp;lt;/code&amp;gt; depends on it.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;freshWorld&amp;lt;/code&amp;gt; || Deletes the dev world &amp;lt;code&amp;gt;run/universe/worlds/default&amp;lt;/code&amp;gt; but keeps its seed. With &amp;lt;code&amp;gt;-PnewSeed&amp;lt;/code&amp;gt; the seed goes too.&lt;br /&gt;
|}&lt;br /&gt;
Generation reads vanilla files from your local Hytale installation. Set &amp;lt;code&amp;gt;hytaleHomeOverride&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;~/.gradle/gradle.properties&amp;lt;/code&amp;gt; to the Hytale home folder.&lt;br /&gt;
&lt;br /&gt;
== ContentPack and Mod ==&lt;br /&gt;
A &amp;lt;code&amp;gt;ContentPack&amp;lt;/code&amp;gt; (package &amp;lt;code&amp;gt;tools.content&amp;lt;/code&amp;gt;) returns everything your mod generates. It needs a public constructor without parameters.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public final class MyModContent implements ContentPack {&lt;br /&gt;
    private static final Mod MOD = Mod.of(&amp;quot;MyMod&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
    @Override&lt;br /&gt;
    public List&amp;lt;GeneratedAsset&amp;gt; assets(VanillaAssets vanilla) {&lt;br /&gt;
        List&amp;lt;GeneratedAsset&amp;gt; out = new ArrayList&amp;lt;&amp;gt;();&lt;br /&gt;
        out.addAll(OreSpec.named(&amp;quot;MyMod_Tin&amp;quot;)&lt;br /&gt;
                .rarity(Rarity.UNCOMMON).vein(VeinShape.MEDIUM).heights(20, 90)&lt;br /&gt;
                .zone(&amp;quot;Zone1&amp;quot;, &amp;quot;MyMod_Ore_Tin_Stone&amp;quot;)&lt;br /&gt;
                .zone(&amp;quot;Zone3&amp;quot;, &amp;quot;MyMod_Ore_Tin_Shale&amp;quot;)&lt;br /&gt;
                .build());&lt;br /&gt;
        out.add(BiomeSpec.tile(&amp;quot;Zone1_Tier1&amp;quot;, MOD.id(&amp;quot;Birch_Glade&amp;quot;),&lt;br /&gt;
                        vanilla.biome(&amp;quot;Zone1_Tier1&amp;quot;, &amp;quot;Tile.Forest_Birch&amp;quot;))&lt;br /&gt;
                .covers(c -&amp;gt; c.remove(Select.placing(&amp;quot;Plant_Flower_*&amp;quot;))));&lt;br /&gt;
        return out;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;Mod&amp;lt;/code&amp;gt; builds names, seeds and paths from one place, so no name without the prefix slips through. The mod name may not contain &amp;lt;code&amp;gt;_&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;.&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mod mod = Mod.of(&amp;quot;FrostMod&amp;quot;);&lt;br /&gt;
mod.id(&amp;quot;Ice_Forest&amp;quot;)                         // FrostMod_Ice_Forest&lt;br /&gt;
mod.seed(&amp;quot;Icicles&amp;quot;)                          // FrostMod-Icicles&lt;br /&gt;
mod.path(&amp;quot;PrefabPatterns&amp;quot;, &amp;quot;Ice&amp;quot;)            // Zones.FrostMod.PrefabPatterns.Ice&lt;br /&gt;
Mod.file(mod.path(&amp;quot;PrefabPatterns&amp;quot;, &amp;quot;Ice&amp;quot;))  // Server/World/Default/Zones/FrostMod/PrefabPatterns/Ice.json&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Never rename a seed afterwards: a changed seed moves things in existing worlds.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;VanillaAssets&amp;lt;/code&amp;gt; reads vanilla files from &amp;lt;code&amp;gt;Assets.zip&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;biome(zone, fileName)&amp;lt;/code&amp;gt; for a biome, &amp;lt;code&amp;gt;file(path)&amp;lt;/code&amp;gt; for any JSON file and &amp;lt;code&amp;gt;files(prefix)&amp;lt;/code&amp;gt; to list JSON files under a path.&lt;br /&gt;
&lt;br /&gt;
== Biomes ==&lt;br /&gt;
Package &amp;lt;code&amp;gt;tools.biome&amp;lt;/code&amp;gt;. Hytale has no inheritance between biomes, so &amp;lt;code&amp;gt;BiomeSpec&amp;lt;/code&amp;gt; copies a vanilla biome and changes it step by step. What you do not touch (relief, water, environment, monuments) stays as in the original, so the biome behaves like its model at the edges.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
BiomeSpec.tile(&amp;quot;Zone1_Tier1&amp;quot;, &amp;quot;MyMod_Forest_Dead&amp;quot;, vanilla.biome(&amp;quot;Zone1_Tier1&amp;quot;, &amp;quot;Tile.Forest_Birch&amp;quot;))&lt;br /&gt;
        .replaceBlocks(Map.of(&amp;quot;Soil_Grass&amp;quot;, &amp;quot;Soil_Grass_Dry&amp;quot;))&lt;br /&gt;
        .covers(c -&amp;gt; c.remove(Select.placing(&amp;quot;Plant_Flower_*&amp;quot;)))&lt;br /&gt;
        .mapColor(&amp;quot;#8f8456&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tile(zone, name, source)&amp;lt;/code&amp;gt; || A main biome of the zone (&amp;lt;code&amp;gt;Tile.&amp;amp;lt;name&amp;amp;gt;.json&amp;lt;/code&amp;gt;), spread by weight.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;custom(zone, name, source)&amp;lt;/code&amp;gt; || A biome placed on top of others (&amp;lt;code&amp;gt;Custom.&amp;amp;lt;name&amp;amp;gt;.json&amp;lt;/code&amp;gt;). Needs &amp;lt;code&amp;gt;customGenerator&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;customGenerator(noiseMask, priority, biomes...)&amp;lt;/code&amp;gt; || Where a Custom biome appears: inside which biomes, at which spots, and above which other Custom biomes.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;replaceBlocks(map)&amp;lt;/code&amp;gt; || Swaps blocks in covers and layers, all at once (no chains).&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;covers(edit)&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;layers(group, edit)&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;prefabs(edit)&amp;lt;/code&amp;gt; || Edit lists of entries with &amp;lt;code&amp;gt;EntryList&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;monumentsOf(source)&amp;lt;/code&amp;gt; || Takes the monuments (dungeons, outposts) of another biome.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;environmentOf(source)&amp;lt;/code&amp;gt; || Takes the environment (enemies, weather) of another biome.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;tint(edit)&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mapColor(hex)&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;weight(n)&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;comment(text)&amp;lt;/code&amp;gt; || Tint, map colour, weight (Tile only), comment.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;set(path, value)&amp;lt;/code&amp;gt; || Anything else, by JSON path.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Helpers:&lt;br /&gt;
* &amp;lt;code&amp;gt;Select.placing(globs)&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Select.placingOnly(globs)&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Select.pattern(globs)&amp;lt;/code&amp;gt; pick entries by content instead of position. A selection that matches nothing stops generation, since it is almost always a typo or a changed vanilla file.&lt;br /&gt;
* &amp;lt;code&amp;gt;EntryList&amp;lt;/code&amp;gt;: &amp;lt;code&amp;gt;edit&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;remove&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;keep&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;clear&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;add&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;insertAfter&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;Cover&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;PrefabEntry&amp;lt;/code&amp;gt; build new cover and prefab entries.&lt;br /&gt;
* &amp;lt;code&amp;gt;Palettes.DRY_GRASSLAND&amp;lt;/code&amp;gt; is a ready-made block swap for dry grassland.&lt;br /&gt;
* &amp;lt;code&amp;gt;RegionGrid&amp;lt;/code&amp;gt; lays a cell grid for large Custom biomes. Its &amp;lt;code&amp;gt;allocator()&amp;lt;/code&amp;gt; hands out shares of the cells, so biomes on the same grid never overlap. &amp;lt;code&amp;gt;natural()&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Warp&amp;lt;/code&amp;gt; distort the round areas into natural shapes.&lt;br /&gt;
* &amp;lt;code&amp;gt;VanillaTiers&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;VanillaPriorities&amp;lt;/code&amp;gt; hold vanilla facts per zone and tier and the priorities of vanilla&amp;#039;s Custom biomes.&lt;br /&gt;
&lt;br /&gt;
Every generated file carries a &amp;lt;code&amp;gt;$Comment&amp;lt;/code&amp;gt; that names its source. Do not edit generated files by hand.&lt;br /&gt;
&lt;br /&gt;
Utility Biome&amp;#039;s own pack contains one shared noise, &amp;lt;code&amp;gt;Zones.UtilityBiome.Noise.Tint.Forest_Patchy&amp;lt;/code&amp;gt;, a patchy tint noise for forests. Mods that refer to it need Utility Biome at runtime.&lt;br /&gt;
&lt;br /&gt;
== Caves ==&lt;br /&gt;
Package &amp;lt;code&amp;gt;tools.cave&amp;lt;/code&amp;gt;. &amp;lt;code&amp;gt;CaveSpec&amp;lt;/code&amp;gt; describes a cave for several tiers and produces the nodes, the cave type and one modifier per tier.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CaveSpec hollow = CaveSpec.of(mod, &amp;quot;Frozen_Hollow&amp;quot;)&lt;br /&gt;
        .inTiers(CaveTier.ZONE3_TIER1, CaveTier.ZONE3_TIER2)&lt;br /&gt;
        .entryTo(&amp;quot;Chamber&amp;quot;)&lt;br /&gt;
        .node(&amp;quot;Chamber&amp;quot;, &amp;quot;Ice chamber&amp;quot;, tier -&amp;gt; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
                {&amp;quot;Type&amp;quot;: &amp;quot;ELLIPSOID&amp;quot;, &amp;quot;RadiusX&amp;quot;: [8, 12], &amp;quot;RadiusY&amp;quot;: [5, 7], &amp;quot;RadiusZ&amp;quot;: [8, 12]}&amp;quot;&amp;quot;&amp;quot;)&lt;br /&gt;
        .type(&amp;quot;Frozen cave&amp;quot;, tier -&amp;gt; &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
                &amp;quot;EntryPoints&amp;quot;: {&amp;quot;Seed&amp;quot;: &amp;quot;%s&amp;quot;, &amp;quot;Density&amp;quot;: [0, 0.3], &amp;quot;Scale&amp;quot;: 0.005, &amp;quot;Jitter&amp;quot;: 0.5},&lt;br /&gt;
                &amp;quot;FixedEntryHeight&amp;quot;: [70, 90], &amp;quot;Pitch&amp;quot;: 90&amp;quot;&amp;quot;&amp;quot;.formatted(mod.seed(&amp;quot;Frozen-Hollow&amp;quot;)));&lt;br /&gt;
List&amp;lt;GeneratedAsset&amp;gt; assets = CaveSpec.build(mod, List.of(hollow));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;entryTo(child)&amp;lt;/code&amp;gt; creates a safe, empty entry node. The entry node must never be an &amp;lt;code&amp;gt;ELLIPSOID&amp;lt;/code&amp;gt;: that crashes chunk generation and kicks players. The validator checks this.&lt;br /&gt;
* &amp;lt;code&amp;gt;CaveTier&amp;lt;/code&amp;gt; has constants for every tier with its cave environment, for example &amp;lt;code&amp;gt;ZONE1_TIER1&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;Env_Zone1_Caves_Plains&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;CaveKit&amp;lt;/code&amp;gt; is the lower-level version with the default block and prefab masks.&lt;br /&gt;
&lt;br /&gt;
== Ores ==&lt;br /&gt;
Package &amp;lt;code&amp;gt;tools.ore&amp;lt;/code&amp;gt;. &amp;lt;code&amp;gt;OreSpec&amp;lt;/code&amp;gt; creates an ore the way vanilla does: per zone a vein node, a placement and a modifier that adds it to all tiers of the zone.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
List&amp;lt;GeneratedAsset&amp;gt; tin = OreSpec.named(&amp;quot;MyMod_Tin&amp;quot;)&lt;br /&gt;
        .rarity(Rarity.UNCOMMON)&lt;br /&gt;
        .vein(VeinShape.MEDIUM)&lt;br /&gt;
        .heights(20, 90)&lt;br /&gt;
        .zone(&amp;quot;Zone1&amp;quot;, &amp;quot;MyMod_Ore_Tin_Stone&amp;quot;)&lt;br /&gt;
        .zone(&amp;quot;Zone3&amp;quot;, &amp;quot;MyMod_Ore_Tin_Shale&amp;quot;)&lt;br /&gt;
        .build();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Rarity !! Like in vanilla !! About&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;COMMON&amp;lt;/code&amp;gt; || copper in zone 1 || 40 veins per chunk&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;UNCOMMON&amp;lt;/code&amp;gt; || iron in zone 1 || 10 per chunk&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;RARE&amp;lt;/code&amp;gt; || cobalt || 2–3 per chunk&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;EPIC&amp;lt;/code&amp;gt; || gold || one per 4 chunks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;LEGENDARY&amp;lt;/code&amp;gt; || silver || one per 25 chunks&lt;br /&gt;
|}&lt;br /&gt;
The ore block per zone should match the rock of that zone and must exist as a block, either in vanilla or in your own assets. Vein shapes: &amp;lt;code&amp;gt;SMALL&amp;lt;/code&amp;gt; (a lump like silver), &amp;lt;code&amp;gt;MEDIUM&amp;lt;/code&amp;gt; (core with optional casing like iron, see &amp;lt;code&amp;gt;casing(block)&amp;lt;/code&amp;gt;), &amp;lt;code&amp;gt;LARGE&amp;lt;/code&amp;gt; (mostly thin veins, rarely a large one, very rarely a motherlode, like copper). Defaults: &amp;lt;code&amp;gt;UNCOMMON&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MEDIUM&amp;lt;/code&amp;gt;, heights 20–100. &amp;lt;code&amp;gt;hostRock(blocks...)&amp;lt;/code&amp;gt; changes which blocks the ore may replace. Because the steps come from vanilla, an ore &amp;quot;as rare as gold&amp;quot; is equally rare in every mod that uses Utility Biome.&lt;br /&gt;
&lt;br /&gt;
== Resources ==&lt;br /&gt;
Package &amp;lt;code&amp;gt;tools.resource&amp;lt;/code&amp;gt;. &amp;lt;code&amp;gt;ResourceSpec&amp;lt;/code&amp;gt; adds covers (plants, stones) or prefabs (buildings, rocks, skeletons) to existing biomes through a modifier, without touching vanilla files.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ResourceSpec.prefabs(&amp;quot;MyMod_Watchtowers&amp;quot;)&lt;br /&gt;
        .into(&amp;quot;Zones.Zone1_Tier*.Tile.Plains_*&amp;quot;)&lt;br /&gt;
        .prefab(&amp;quot;Monuments.Encounter.City_Ruins.Towers.*&amp;quot;, 1)&lt;br /&gt;
        .grid(0.01, 0.05)               // cells of ~100 blocks, 5 % of them&lt;br /&gt;
        .parents(&amp;quot;Soil_Grass&amp;quot;, &amp;quot;Soil_Grass_Full&amp;quot;)&lt;br /&gt;
        .build();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;into&amp;lt;/code&amp;gt; takes globs on the dotted path of the biome. Further options: &amp;lt;code&amp;gt;fitHeightmap()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submerge()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;sink(blocks)&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mask(json)&amp;lt;/code&amp;gt;. For covers use &amp;lt;code&amp;gt;ResourceSpec.cover(name, coverJson)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Spawns ==&lt;br /&gt;
Package &amp;lt;code&amp;gt;tools.spawn&amp;lt;/code&amp;gt;. &amp;lt;code&amp;gt;SpawnSpec&amp;lt;/code&amp;gt; writes a world NPC spawn file. Spawns have no modifiers, a new file is enough, and the prefix keeps it from replacing a vanilla file.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SpawnSpec.named(&amp;quot;MyMod_Spawns_Forest_Boars&amp;quot;)&lt;br /&gt;
        .environments(&amp;quot;Env_Zone1_Forests&amp;quot;, &amp;quot;Env_Zone1_Autumn&amp;quot;)&lt;br /&gt;
        .npc(&amp;quot;Boar&amp;quot;, 10, &amp;quot;Group_Small&amp;quot;)&lt;br /&gt;
        .onBlocks(&amp;quot;Soil&amp;quot;)&lt;br /&gt;
        .dayTime(6, 18)&lt;br /&gt;
        .build();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;light(min, max)&amp;lt;/code&amp;gt; limits the light level (0–100).&lt;br /&gt;
&lt;br /&gt;
== Asset validator ==&lt;br /&gt;
&amp;lt;code&amp;gt;AssetValidator&amp;lt;/code&amp;gt; (package &amp;lt;code&amp;gt;tools&amp;lt;/code&amp;gt;) checks world-generation and modifier files before they reach a server. The task &amp;lt;code&amp;gt;validateAssets&amp;lt;/code&amp;gt; runs it, or call it from a JUnit test:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
AssetValidator.Report report = AssetValidator.forPrefix(&amp;quot;MyMod_&amp;quot;)&lt;br /&gt;
        .vanilla(VanillaIndex.load(Path.of(&amp;quot;modules/utilitybiome/vanilla-index/0.7.0.txt&amp;quot;)))&lt;br /&gt;
        .pack(Path.of(&amp;quot;src/main/resources&amp;quot;))&lt;br /&gt;
        .dependency(Path.of(&amp;quot;modules/utilitybiome/src/main/resources&amp;quot;))&lt;br /&gt;
        .validate();&lt;br /&gt;
assertTrue(report.isOk(), report.format());&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
It reports:&lt;br /&gt;
* JSON that cannot be read,&lt;br /&gt;
* &amp;lt;code&amp;gt;&amp;quot;File&amp;quot;&amp;lt;/code&amp;gt; references that point nowhere (the world would not load),&lt;br /&gt;
* prefab globs that match no prefab,&lt;br /&gt;
* unknown block, item and environment IDs,&lt;br /&gt;
* biome and modifier files without the mod prefix, and files that hide or disable a vanilla file,&lt;br /&gt;
* cave types whose entry node is an &amp;lt;code&amp;gt;ELLIPSOID&amp;lt;/code&amp;gt;,&lt;br /&gt;
* biomes whose tint or environment has a different number of entries than weights, or two or more entries without a noise,&lt;br /&gt;
* biome masks that name no Tile biome of the zone,&lt;br /&gt;
* modifiers with wrong enum values (a wrong value stops the whole server), no rules or no operations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;VanillaIndex&amp;lt;/code&amp;gt; lists everything that exists in vanilla, as one text file per Hytale version (&amp;lt;code&amp;gt;vanilla-index/&amp;amp;lt;version&amp;amp;gt;.txt&amp;lt;/code&amp;gt;). References can then be checked without &amp;lt;code&amp;gt;Assets.zip&amp;lt;/code&amp;gt;, for example in CI, and a Hytale update shows up as a diff.&lt;br /&gt;
&lt;br /&gt;
{{UtilityBiome Nav}}&lt;/div&gt;</summary>
		<author><name>Maintenance script</name></author>
	</entry>
</feed>